summaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
Diffstat (limited to 'backend')
-rw-r--r--backend/.gitignore3
-rw-r--r--backend/Makefile.am154
-rw-r--r--backend/agfafocus.c1
-rw-r--r--backend/apple.c3
-rw-r--r--backend/artec.c8
-rw-r--r--backend/artec_eplus48u.c2
-rw-r--r--backend/as6e.c28
-rw-r--r--backend/avision.c2
-rw-r--r--backend/avision.h2
-rw-r--r--backend/canon-sane.c4
-rw-r--r--backend/canon.c2
-rw-r--r--backend/canon630u-common.c4
-rw-r--r--backend/canon_dr.c35
-rw-r--r--backend/dc25.c2
-rw-r--r--backend/dll.c90
-rw-r--r--backend/dll.conf.in15
-rw-r--r--backend/epjitsu.c13
-rw-r--r--backend/epson2.c5
-rw-r--r--backend/epsonds.c5
-rw-r--r--backend/escl.conf.in17
-rw-r--r--backend/escl/escl.c777
-rw-r--r--backend/escl/escl.h171
-rw-r--r--backend/escl/escl_capabilities.c377
-rw-r--r--backend/escl/escl_devices.c185
-rw-r--r--backend/escl/escl_jpeg.c230
-rw-r--r--backend/escl/escl_newjob.c241
-rw-r--r--backend/escl/escl_png.c193
-rw-r--r--backend/escl/escl_reset.c75
-rw-r--r--backend/escl/escl_scan.c99
-rw-r--r--backend/escl/escl_status.c176
-rw-r--r--backend/escl/escl_tiff.c119
-rw-r--r--backend/fujitsu.c10
-rw-r--r--backend/fujitsu.conf.in3
-rw-r--r--backend/genesys.conf.in19
-rw-r--r--backend/genesys/buffer.cpp102
-rw-r--r--backend/genesys/buffer.h89
-rw-r--r--backend/genesys/calibration.h108
-rw-r--r--backend/genesys/command_set.h166
-rw-r--r--backend/genesys/conv.cpp238
-rw-r--r--backend/genesys/conv.h69
-rw-r--r--backend/genesys/device.cpp272
-rw-r--r--backend/genesys/device.h387
-rw-r--r--backend/genesys/enums.cpp131
-rw-r--r--backend/genesys/enums.h530
-rw-r--r--backend/genesys/error.cpp (renamed from backend/genesys_error.cc)106
-rw-r--r--backend/genesys/error.h (renamed from backend/genesys_error.h)95
-rw-r--r--backend/genesys/fwd.h132
-rw-r--r--backend/genesys/genesys.cpp (renamed from backend/genesys.cc)5798
-rw-r--r--backend/genesys/genesys.h (renamed from backend/genesys.h)32
-rw-r--r--backend/genesys/gl124.cpp2269
-rw-r--r--backend/genesys/gl124.h205
-rw-r--r--backend/genesys/gl124_registers.h316
-rw-r--r--backend/genesys/gl646.cpp3436
-rw-r--r--backend/genesys/gl646.h521
-rw-r--r--backend/genesys/gl646_registers.h176
-rw-r--r--backend/genesys/gl841.cpp4010
-rw-r--r--backend/genesys/gl841.h130
-rw-r--r--backend/genesys/gl841_registers.h269
-rw-r--r--backend/genesys/gl843.cpp3060
-rw-r--r--backend/genesys/gl843.h139
-rw-r--r--backend/genesys/gl843_registers.h382
-rw-r--r--backend/genesys/gl846.cpp2098
-rw-r--r--backend/genesys/gl846.h218
-rw-r--r--backend/genesys/gl846_registers.h351
-rw-r--r--backend/genesys/gl847.cpp2140
-rw-r--r--backend/genesys/gl847.h206
-rw-r--r--backend/genesys/gl847_registers.h333
-rw-r--r--backend/genesys/image.cpp204
-rw-r--r--backend/genesys/image.h87
-rw-r--r--backend/genesys/image_buffer.cpp203
-rw-r--r--backend/genesys/image_buffer.h129
-rw-r--r--backend/genesys/image_pipeline.cpp839
-rw-r--r--backend/genesys/image_pipeline.h572
-rw-r--r--backend/genesys/image_pixel.cpp509
-rw-r--r--backend/genesys/image_pixel.h144
-rw-r--r--backend/genesys/low.cpp1994
-rw-r--r--backend/genesys/low.h525
-rw-r--r--backend/genesys/motor.cpp180
-rw-r--r--backend/genesys/motor.h177
-rw-r--r--backend/genesys/register.h537
-rw-r--r--backend/genesys/register_cache.h92
-rw-r--r--backend/genesys/row_buffer.h214
-rw-r--r--backend/genesys/scanner_interface.cpp52
-rw-r--r--backend/genesys/scanner_interface.h112
-rw-r--r--backend/genesys/scanner_interface_usb.cpp515
-rw-r--r--backend/genesys/scanner_interface_usb.h98
-rw-r--r--backend/genesys/sensor.cpp160
-rw-r--r--backend/genesys/sensor.h470
-rw-r--r--backend/genesys/serialize.cpp (renamed from backend/genesys_serialize.cc)0
-rw-r--r--backend/genesys/serialize.h (renamed from backend/genesys_serialize.h)8
-rw-r--r--backend/genesys/settings.cpp142
-rw-r--r--backend/genesys/settings.h328
-rw-r--r--backend/genesys/static_init.cpp70
-rw-r--r--backend/genesys/static_init.h88
-rw-r--r--backend/genesys/status.cpp66
-rw-r--r--backend/genesys/status.h68
-rw-r--r--backend/genesys/tables_frontend.cpp653
-rw-r--r--backend/genesys/tables_gpo.cpp415
-rw-r--r--backend/genesys/tables_model.cpp2958
-rw-r--r--backend/genesys/tables_motor.cpp325
-rw-r--r--backend/genesys/tables_motor_profile.cpp380
-rw-r--r--backend/genesys/tables_sensor.cpp3668
-rw-r--r--backend/genesys/test_scanner_interface.cpp229
-rw-r--r--backend/genesys/test_scanner_interface.h122
-rw-r--r--backend/genesys/test_settings.cpp106
-rw-r--r--backend/genesys/test_settings.h70
-rw-r--r--backend/genesys/test_usb_device.cpp141
-rw-r--r--backend/genesys/test_usb_device.h (renamed from backend/genesys_sanei.h)60
-rw-r--r--backend/genesys/usb_device.cpp (renamed from backend/genesys_sanei.cc)15
-rw-r--r--backend/genesys/usb_device.h118
-rw-r--r--backend/genesys/utilities.h180
-rw-r--r--backend/genesys_conv.cc474
-rw-r--r--backend/genesys_conv_hlp.cc345
-rw-r--r--backend/genesys_devices.cc5165
-rw-r--r--backend/genesys_gl124.cc3592
-rw-r--r--backend/genesys_gl124.h489
-rw-r--r--backend/genesys_gl646.cc4911
-rw-r--r--backend/genesys_gl646.h594
-rw-r--r--backend/genesys_gl841.cc5624
-rw-r--r--backend/genesys_gl841.h265
-rw-r--r--backend/genesys_gl843.cc4415
-rw-r--r--backend/genesys_gl843.h472
-rw-r--r--backend/genesys_gl846.cc3393
-rw-r--r--backend/genesys_gl846.h498
-rw-r--r--backend/genesys_gl847.cc3517
-rw-r--r--backend/genesys_gl847.h510
-rw-r--r--backend/genesys_low.cc2059
-rw-r--r--backend/genesys_low.h2042
-rw-r--r--backend/gt68xx.c32
-rw-r--r--backend/hp-option.h2
-rw-r--r--backend/hp-scl.c4
-rw-r--r--backend/hp3900_config.c2
-rw-r--r--backend/hp3900_debug.c6
-rw-r--r--backend/hp3900_rts8822.c92
-rw-r--r--backend/hp3900_sane.c5
-rw-r--r--backend/hp3900_usb.c6
-rw-r--r--backend/hpsj5s.c9
-rw-r--r--backend/ibm.c10
-rw-r--r--backend/kodakaio.c5
-rw-r--r--backend/kvs1025_opt.c2
-rw-r--r--backend/kvs20xx_opt.c2
-rw-r--r--backend/kvs40xx.c9
-rw-r--r--backend/kvs40xx_opt.c18
-rw-r--r--backend/magicolor.c5
-rw-r--r--backend/microtek.c1
-rw-r--r--backend/mustek_pp.c40
-rw-r--r--backend/mustek_usb2_transparent.c2
-rw-r--r--backend/nec.c4
-rw-r--r--backend/niash.c1
-rw-r--r--backend/pieusb_buffer.h4
-rw-r--r--backend/pieusb_specific.c6
-rw-r--r--backend/pixma.conf.in9
-rw-r--r--backend/pixma/pixma.c (renamed from backend/pixma.c)10
-rw-r--r--backend/pixma/pixma.h (renamed from backend/pixma.h)10
-rw-r--r--backend/pixma/pixma_bjnp.c (renamed from backend/pixma_bjnp.c)301
-rw-r--r--backend/pixma/pixma_bjnp.h (renamed from backend/pixma_bjnp.h)10
-rw-r--r--backend/pixma/pixma_bjnp_private.h (renamed from backend/pixma_bjnp_private.h)6
-rw-r--r--backend/pixma/pixma_common.c (renamed from backend/pixma_common.c)10
-rw-r--r--backend/pixma/pixma_common.h (renamed from backend/pixma_common.h)0
-rw-r--r--backend/pixma/pixma_imageclass.c (renamed from backend/pixma_imageclass.c)10
-rw-r--r--backend/pixma/pixma_io.h (renamed from backend/pixma_io.h)2
-rw-r--r--backend/pixma/pixma_io_sanei.c (renamed from backend/pixma_io_sanei.c)67
-rw-r--r--backend/pixma/pixma_mp150.c (renamed from backend/pixma_mp150.c)890
-rw-r--r--backend/pixma/pixma_mp730.c (renamed from backend/pixma_mp730.c)6
-rw-r--r--backend/pixma/pixma_mp750.c (renamed from backend/pixma_mp750.c)10
-rw-r--r--backend/pixma/pixma_mp800.c (renamed from backend/pixma_mp810.c)78
-rw-r--r--backend/pixma/pixma_rename.h (renamed from backend/pixma_rename.h)2
-rw-r--r--backend/pixma/pixma_sane_options.c (renamed from backend/pixma_sane_options.c)2
-rw-r--r--backend/pixma/pixma_sane_options.h (renamed from backend/pixma_sane_options.h)0
-rwxr-xr-xbackend/pixma/scripts/pixma_gen_options.py (renamed from backend/scripts/pixma_gen_options.py)0
-rw-r--r--backend/plustek-pp_motor.c1
-rw-r--r--backend/plustek-usb.c14
-rw-r--r--backend/plustek-usbcal.c2
-rw-r--r--backend/plustek.c13
-rw-r--r--backend/plustek_pp.c2
-rw-r--r--backend/ricoh.c10
-rw-r--r--backend/ricoh2.c9
-rw-r--r--backend/ricoh2_buffer.c5
-rw-r--r--backend/s9036.c1
-rw-r--r--backend/sane_strstatus.c2
-rw-r--r--backend/sharp.c1
-rw-r--r--backend/sm3600.c8
-rw-r--r--backend/snapscan-options.c4
-rw-r--r--backend/stv680.c12
-rw-r--r--backend/umax_pp.c47
-rw-r--r--backend/umax_pp_low.c31
-rw-r--r--backend/umax_pp_low.h2
-rw-r--r--backend/umax_pp_mid.c2
-rw-r--r--backend/umax_pp_mid.h2
-rw-r--r--backend/xerox_mfp.c42
-rw-r--r--backend/xerox_mfp.h4
191 files changed, 46698 insertions, 43178 deletions
diff --git a/backend/.gitignore b/backend/.gitignore
index 6ebfbd5..6276e61 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -1,5 +1,6 @@
*-s.c
-*-s.cc
+*-s.cpp
*.conf
+.dirstamp
dll-preload.c
dll-preload.h
diff --git a/backend/Makefile.am b/backend/Makefile.am
index 44d1e17..1dc1a58 100644
--- a/backend/Makefile.am
+++ b/backend/Makefile.am
@@ -17,7 +17,12 @@ DIST_LIBS_LDFLAGS = $(AM_LDFLAGS) -rpath '$(libdir)' -version-number $(V_MAJOR):
LIBTOOL += --silent
FIRMWARE_DIRS = artec_eplus48u gt68xx snapscan epjitsu
+# Needed by most backends as they add sane_strstatus.lo to their list
+# of libraries to link with via libsane_${BACKEND}_la_LIBADD. Due to
+# the implicit dependency, automake does not notice the need to clean
+# up the dependency tracking file.
EXTRA_DIST = sane_strstatus.c
+CLEANFILES = $(DEPDIR)/sane_strstatus.Plo
all: becfg
@@ -29,7 +34,7 @@ EXTRA_DIST += stubs.c
$(AM_V_at)rm -f $@
$(AM_V_at)$(LN_S) $(srcdir)/stubs.c $@
-%-s.cc: $(srcdir)/stubs.c
+%-s.cpp: $(srcdir)/stubs.c
$(AM_V_at)rm -f $@
$(AM_V_at)$(LN_S) $(srcdir)/stubs.c $@
@@ -63,8 +68,8 @@ BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \
canon_pp.conf cardscan.conf coolscan2.conf coolscan3.conf \
coolscan.conf dc210.conf dc240.conf dc25.conf \
dell1600n_net.conf dmc.conf epjitsu.conf epson2.conf \
- epson.conf epsonds.conf fujitsu.conf genesys.conf gphoto2.conf \
- gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
+ epson.conf epsonds.conf escl.conf fujitsu.conf genesys.conf \
+ gphoto2.conf gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
hp.conf hpsj5s.conf hs2p.conf ibm.conf kodak.conf kodakaio.conf\
kvs1025.conf \
leo.conf lexmark.conf ma1509.conf magicolor.conf \
@@ -133,7 +138,7 @@ uninstall-hook:
rmdir $(DESTDIR)$(datadir)/sane/$${dir} ; \
done
-CLEANFILES = $(BACKEND_CONFS) $(be_convenience_libs)
+CLEANFILES += $(BACKEND_CONFS) $(be_convenience_libs)
clean-local:
find . -type l -name \*-s.c | xargs rm -f
@@ -156,7 +161,7 @@ be_convenience_libs = libabaton.la libagfafocus.la \
libcoolscan2.la libcoolscan3.la libdc25.la \
libdc210.la libdc240.la libdell1600n_net.la \
libdmc.la libdll.la libdll_preload.la libepjitsu.la libepson.la \
- libepson2.la libepsonds.la libfujitsu.la libgenesys.la \
+ libepson2.la libepsonds.la libescl.la libfujitsu.la libgenesys.la \
libgphoto2_i.la libgt68xx.la libhp.la \
libhp3500.la libhp3900.la libhp4200.la \
libhp5400.la libhp5590.la libhpljm1005.la \
@@ -189,8 +194,8 @@ be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \
libsane-coolscan2.la libsane-coolscan3.la libsane-dc25.la \
libsane-dc210.la libsane-dc240.la libsane-dell1600n_net.la \
libsane-dmc.la libsane-epjitsu.la libsane-epson.la \
- libsane-epson2.la libsane-epsonds.la libsane-fujitsu.la libsane-genesys.la \
- libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
+ libsane-epson2.la libsane-epsonds.la libsane-escl.la libsane-fujitsu.la \
+ libsane-genesys.la libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
libsane-hp3500.la libsane-hp3900.la libsane-hp4200.la \
libsane-hp5400.la libsane-hp5590.la libsane-hpljm1005.la \
libsane-hpsj5s.la libsane-hs2p.la libsane-ibm.la libsane-kodak.la libsane-kodakaio.la\
@@ -219,7 +224,7 @@ lib_LTLIBRARIES = libsane.la
sanelibdir = $(libdir)/sane
sanelib_LTLIBRARIES = $(BACKEND_LIBS_ENABLED) libsane-dll.la
-COMMON_LIBS = ../lib/liblib.la
+COMMON_LIBS = ../lib/liblib.la $(XML_LIBS)
# Each backend should define a convenience library that compiles
# all related files within backend directory. General guideline
@@ -252,7 +257,7 @@ libagfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus
nodist_libsane_agfafocus_la_SOURCES = agfafocus-s.c
libsane_agfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus
libsane_agfafocus_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_agfafocus_la_LIBADD = $(COMMON_LIBS) libagfafocus.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_agfafocus_la_LIBADD = $(COMMON_LIBS) libagfafocus.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += agfafocus.conf.in
libapple_la_SOURCES = apple.c apple.h
@@ -279,7 +284,7 @@ libartec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u
nodist_libsane_artec_eplus48u_la_SOURCES = artec_eplus48u-s.c
libsane_artec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u
libsane_artec_eplus48u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_artec_eplus48u_la_LIBADD = $(COMMON_LIBS) libartec_eplus48u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMEG_LIBS)
+libsane_artec_eplus48u_la_LIBADD = $(COMMON_LIBS) libartec_eplus48u.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMEG_LIBS)
EXTRA_DIST += artec_eplus48u.conf.in
libas6e_la_SOURCES = as6e.c as6e.h
@@ -296,7 +301,7 @@ libavision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision
nodist_libsane_avision_la_SOURCES = avision-s.c
libsane_avision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision
libsane_avision_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_avision_la_LIBADD = $(COMMON_LIBS) libavision.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_avision_la_LIBADD = $(COMMON_LIBS) libavision.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += avision.conf.in
libbh_la_SOURCES = bh.c bh.h
@@ -363,7 +368,7 @@ libcoolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan
nodist_libsane_coolscan_la_SOURCES = coolscan-s.c
libsane_coolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan
libsane_coolscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_coolscan_la_LIBADD = $(COMMON_LIBS) libcoolscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_coolscan_la_LIBADD = $(COMMON_LIBS) libcoolscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += coolscan.conf.in
libcoolscan2_la_SOURCES = coolscan2.c
@@ -429,6 +434,21 @@ libsane_dmc_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_dmc_la_LIBADD = $(COMMON_LIBS) libdmc.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += dmc.conf.in
+if have_libavahi
+if have_libcurl
+if have_libxml2
+libescl_la_SOURCES = escl/escl.c escl/escl_capabilities.c escl/escl_devices.c escl/escl.h escl/escl_newjob.c escl/escl_reset.c escl/escl_scan.c escl/escl_status.c escl/escl_jpeg.c escl/escl_png.c escl/escl_tiff.c
+libescl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl
+
+nodist_libsane_escl_la_SOURCES = escl-s.c
+libsane_escl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl
+libsane_escl_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_escl_la_LIBADD = $(COMMON_LIBS) libescl.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS) $(XML_LIBS) $(libcurl_LIBS) $(AVAHI_LIBS)
+endif
+endif
+endif
+EXTRA_DIST += escl.conf.in
+
libepjitsu_la_SOURCES = epjitsu.c epjitsu.h epjitsu-cmd.h
libepjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu
@@ -480,22 +500,56 @@ libsane_fujitsu_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_fujitsu_la_LIBADD = $(COMMON_LIBS) libfujitsu.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_magic.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += fujitsu.conf.in
-libgenesys_la_SOURCES = genesys.cc genesys.h genesys_sanei.h genesys_sanei.cc genesys_error.h genesys_error.cc \
- genesys_serialize.h \
- genesys_gl646.cc genesys_gl646.h genesys_gl841.cc genesys_gl841.h \
- genesys_gl843.cc genesys_gl843.h genesys_gl846.cc genesys_gl846.h \
- genesys_gl847.cc genesys_gl847.h genesys_gl124.cc genesys_gl124.h \
- genesys_low.cc genesys_low.h
+libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \
+ genesys/buffer.h genesys/buffer.cpp \
+ genesys/calibration.h \
+ genesys/command_set.h \
+ genesys/conv.h genesys/conv.cpp \
+ genesys/device.h genesys/device.cpp \
+ genesys/enums.h genesys/enums.cpp \
+ genesys/error.h genesys/error.cpp \
+ genesys/fwd.h \
+ genesys/gl646.cpp genesys/gl646.h genesys/gl646_registers.h \
+ genesys/gl124.cpp genesys/gl124.h genesys/gl124_registers.h \
+ genesys/gl841.cpp genesys/gl841.h genesys/gl841_registers.h \
+ genesys/gl843.cpp genesys/gl843.h genesys/gl843_registers.h \
+ genesys/gl846.cpp genesys/gl846.h genesys/gl846_registers.h \
+ genesys/gl847.cpp genesys/gl847.h genesys/gl847_registers.h \
+ genesys/row_buffer.h \
+ genesys/image_buffer.h genesys/image_buffer.cpp \
+ genesys/image_pipeline.h genesys/image_pipeline.cpp \
+ genesys/image_pixel.h genesys/image_pixel.cpp \
+ genesys/image.h genesys/image.cpp \
+ genesys/motor.h genesys/motor.cpp \
+ genesys/register.h \
+ genesys/register_cache.h \
+ genesys/scanner_interface.h genesys/scanner_interface.cpp \
+ genesys/scanner_interface_usb.h genesys/scanner_interface_usb.cpp \
+ genesys/sensor.h genesys/sensor.cpp \
+ genesys/settings.h genesys/settings.cpp \
+ genesys/serialize.h \
+ genesys/static_init.h genesys/static_init.cpp \
+ genesys/status.h genesys/status.cpp \
+ genesys/tables_frontend.cpp \
+ genesys/tables_gpo.cpp \
+ genesys/tables_model.cpp \
+ genesys/tables_motor.cpp \
+ genesys/tables_motor_profile.cpp \
+ genesys/tables_sensor.cpp \
+ genesys/test_scanner_interface.h genesys/test_scanner_interface.cpp \
+ genesys/test_settings.h genesys/test_settings.cpp \
+ genesys/test_usb_device.h genesys/test_usb_device.cpp \
+ genesys/usb_device.h genesys/usb_device.cpp \
+ genesys/low.cpp genesys/low.h \
+ genesys/utilities.h
libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys
-nodist_libsane_genesys_la_SOURCES = genesys-s.cc
+nodist_libsane_genesys_la_SOURCES = genesys-s.cpp
libsane_genesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys
libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la ../sanei/sanei_magic.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += genesys.conf.in
-# TODO: Why are this distributed but not compiled?
-EXTRA_DIST += genesys_conv.cc genesys_conv_hlp.cc genesys_devices.cc
libgphoto2_i_la_SOURCES = gphoto2.c gphoto2.h
libgphoto2_i_la_CPPFLAGS = $(AM_CPPFLAGS) $(GPHOTO2_CPPFLAGS) -DBACKEND_NAME=gphoto2
@@ -523,7 +577,7 @@ libhp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp
nodist_libsane_hp_la_SOURCES = hp-s.c
libsane_hp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp
libsane_hp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_hp_la_LIBADD = $(COMMON_LIBS) libhp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_hp_la_LIBADD = $(COMMON_LIBS) libhp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pio.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += hp.conf.in
# TODO: These should be moved to ../docs/hp; don't belong here.
EXTRA_DIST += hp.README hp.TODO
@@ -534,7 +588,7 @@ libhp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500
nodist_libsane_hp3500_la_SOURCES = hp3500-s.c
libsane_hp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500
libsane_hp3500_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_hp3500_la_LIBADD = $(COMMON_LIBS) libhp3500.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_hp3500_la_LIBADD = $(COMMON_LIBS) libhp3500.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
libhp3900_la_SOURCES = hp3900.c
libhp3900_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3900
@@ -726,7 +780,7 @@ libmicrotek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2
nodist_libsane_microtek2_la_SOURCES = microtek2-s.c
libsane_microtek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2
libsane_microtek2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_microtek2_la_LIBADD = $(COMMON_LIBS) libmicrotek2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_microtek2_la_LIBADD = $(COMMON_LIBS) libmicrotek2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += microtek2.conf.in
libmustek_la_SOURCES = mustek.c mustek.h
@@ -735,7 +789,7 @@ libmustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek
nodist_libsane_mustek_la_SOURCES = mustek-s.c
libsane_mustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek
libsane_mustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_mustek_la_LIBADD = $(COMMON_LIBS) libmustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pa4s2.lo $(IEEE1284_LIBS) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_mustek_la_LIBADD = $(COMMON_LIBS) libmustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pa4s2.lo $(IEEE1284_LIBS) $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += mustek.conf.in
# TODO: Why are these distributed but not compiled?
EXTRA_DIST += mustek_scsi_pp.c mustek_scsi_pp.h
@@ -768,7 +822,7 @@ libmustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2
nodist_libsane_mustek_usb2_la_SOURCES = mustek_usb2-s.c
libsane_mustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2
libsane_mustek_usb2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_mustek_usb2_la_LIBADD = $(COMMON_LIBS) libmustek_usb2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(PTHREAD_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_mustek_usb2_la_LIBADD = $(COMMON_LIBS) libmustek_usb2.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(MATH_LIB) $(PTHREAD_LIBS) $(USB_LIBS) $(RESMGR_LIBS)
# TODO: Why are these distributed but not compiled?
EXTRA_DIST += mustek_usb2_asic.c mustek_usb2_asic.h mustek_usb2_high.c mustek_usb2_high.h mustek_usb2_reflective.c mustek_usb2_transparent.c
@@ -806,7 +860,7 @@ libpie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie
nodist_libsane_pie_la_SOURCES = pie-s.c
libsane_pie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie
libsane_pie_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_pie_la_LIBADD = $(COMMON_LIBS) libpie.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_pie_la_LIBADD = $(COMMON_LIBS) libpie.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += pie.conf.in
libpieusb_la_SOURCES = pieusb.h pieusb_buffer.c pieusb_buffer.h pieusb_scancmd.c pieusb_scancmd.h pieusb_specific.c pieusb_specific.h pieusb_usb.c pieusb_usb.h pieusb.c
@@ -815,7 +869,7 @@ libpieusb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pieusb
nodist_libsane_pieusb_la_SOURCES = pieusb-s.c
libsane_pieusb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pieusb
libsane_pieusb_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_pieusb_la_LIBADD = $(COMMON_LIBS) libpieusb.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo ../sanei/sanei_usb.lo ../sanei/sanei_ir.lo ../sanei/sanei_magic.lo $(PTHREAD_LIBS) $(RESMGR_LIBS) $(USB_LIBS) $(MATH_LIB)
+libsane_pieusb_la_LIBADD = $(COMMON_LIBS) libpieusb.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo ../sanei/sanei_thread.lo ../sanei/sanei_usb.lo ../sanei/sanei_ir.lo ../sanei/sanei_magic.lo $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) $(USB_LIBS) $(MATH_LIB)
EXTRA_DIST += pieusb.conf.in
libp5_la_SOURCES = p5.c p5.h p5_device.h
@@ -835,16 +889,30 @@ libsane_pint_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pint
libsane_pint_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_pint_la_LIBADD = $(COMMON_LIBS) libpint.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo
-libpixma_la_SOURCES = pixma.c pixma.h pixma_io_sanei.c pixma_io.h pixma_common.c pixma_common.h pixma_mp150.c pixma_mp730.c pixma_mp750.c pixma_mp810.c pixma_imageclass.c pixma_bjnp.c pixma_bjnp.h pixma_bjnp_private.h pixma_rename.h
+libpixma_la_SOURCES = pixma/pixma.c \
+ pixma/pixma.h \
+ pixma/pixma_io_sanei.c \
+ pixma/pixma_io.h \
+ pixma/pixma_common.c \
+ pixma/pixma_common.h \
+ pixma/pixma_mp150.c \
+ pixma/pixma_mp730.c \
+ pixma/pixma_mp750.c \
+ pixma/pixma_mp800.c \
+ pixma/pixma_imageclass.c \
+ pixma/pixma_bjnp.c \
+ pixma/pixma_bjnp.h \
+ pixma/pixma_bjnp_private.h \
+ pixma/pixma_rename.h
libpixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma
nodist_libsane_pixma_la_SOURCES = pixma-s.c
libsane_pixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma
libsane_pixma_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_pixma_la_LIBADD = $(COMMON_LIBS) libpixma.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(SANEI_SANEI_JPEG_LO) $(JPEG_LIBS) $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_pixma_la_LIBADD = $(COMMON_LIBS) libpixma.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(SANEI_SANEI_JPEG_LO) $(JPEG_LIBS) $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += pixma.conf.in
-# TODO: Why are these distributed but not compiled?
-EXTRA_DIST += pixma_sane_options.c pixma_sane_options.h
+# included in pixma.c
+EXTRA_DIST += pixma/pixma_sane_options.c pixma/pixma_sane_options.h
libplustek_la_SOURCES = plustek.c plustek.h
libplustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek
@@ -852,7 +920,7 @@ libplustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek
nodist_libsane_plustek_la_SOURCES = plustek-s.c
libsane_plustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek
libsane_plustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_plustek_la_LIBADD = $(COMMON_LIBS) libplustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_plustek_la_LIBADD = $(COMMON_LIBS) libplustek.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += plustek.conf.in
EXTRA_DIST += plustek-usb.c plustek-usb.h plustek-usbcal.c plustek-usbcalfile.c plustek-usbdevs.c plustek-usbhw.c plustek-usbimg.c plustek-usbio.c plustek-usbmap.c plustek-usbscan.c plustek-usbshading.c
@@ -862,7 +930,7 @@ libplustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp
nodist_libsane_plustek_pp_la_SOURCES = plustek_pp-s.c
libsane_plustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp
libsane_plustek_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_plustek_pp_la_LIBADD = $(COMMON_LIBS) libplustek_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(IEEE1284_LIBS) $(PTHREAD_LIBS)
+libsane_plustek_pp_la_LIBADD = $(COMMON_LIBS) libplustek_pp.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(IEEE1284_LIBS) $(SANEI_THREAD_LIBS)
EXTRA_DIST += plustek_pp.conf.in
# TODO: Why are these distributed but not compiled?
EXTRA_DIST += plustek-pp_dac.c plustek-pp_dbg.h plustek-pp_detect.c plustek-pp_genericio.c plustek-pp_hwdefs.h plustek-pp_image.c plustek-pp_io.c plustek-pp_map.c plustek-pp_misc.c plustek-pp_models.c plustek-pp_motor.c plustek-pp_p12.c plustek-pp_p12ccd.c plustek-pp_p48xx.c plustek-pp_p9636.c plustek-pp_procfs.c plustek-pp_procs.h plustek-pp_ptdrv.c plustek-pp_scale.c plustek-pp_scan.h plustek-pp_scandata.h plustek-pp_sysdep.h plustek-pp_tpa.c plustek-pp_types.h plustek-pp_wrapper.c
@@ -969,7 +1037,7 @@ libsnapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan
nodist_libsane_snapscan_la_SOURCES = snapscan-s.c
libsane_snapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan
libsane_snapscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_snapscan_la_LIBADD = $(COMMON_LIBS) libsnapscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_snapscan_la_LIBADD = $(COMMON_LIBS) libsnapscan.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += snapscan.conf.in
# TODO: Why are these distributed but not compiled?
EXTRA_DIST += snapscan-data.c snapscan-mutex.c snapscan-options.c snapscan-scsi.c snapscan-sources.c snapscan-sources.h snapscan-usb.c snapscan-usb.h
@@ -980,7 +1048,7 @@ libsp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c
nodist_libsane_sp15c_la_SOURCES = sp15c-s.c
libsane_sp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c
libsane_sp15c_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_sp15c_la_LIBADD = $(COMMON_LIBS) libsp15c.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_sp15c_la_LIBADD = $(COMMON_LIBS) libsp15c.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += sp15c.conf.in
libst400_la_SOURCES = st400.c st400.h
@@ -1007,7 +1075,7 @@ libtamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack
nodist_libsane_tamarack_la_SOURCES = tamarack-s.c
libsane_tamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack
libsane_tamarack_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_tamarack_la_LIBADD = $(COMMON_LIBS) libtamarack.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_tamarack_la_LIBADD = $(COMMON_LIBS) libtamarack.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += tamarack.conf.in
libtest_la_SOURCES = test.c test.h
@@ -1016,7 +1084,7 @@ libtest_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test
nodist_libsane_test_la_SOURCES = test-s.c
libsane_test_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test
libsane_test_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_test_la_LIBADD = $(COMMON_LIBS) libtest.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_thread.lo $(PTHREAD_LIBS)
+libsane_test_la_LIBADD = $(COMMON_LIBS) libtest.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_thread.lo $(SANEI_THREAD_LIBS)
EXTRA_DIST += test.conf.in
# TODO: Why are these distributed but not compiled?
EXTRA_DIST += test-picture.c
@@ -1054,7 +1122,7 @@ libu12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12
nodist_libsane_u12_la_SOURCES = u12-s.c
libsane_u12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12
libsane_u12_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_u12_la_LIBADD = $(COMMON_LIBS) libu12.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_u12_la_LIBADD = $(COMMON_LIBS) libu12.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += u12.conf.in
# TODO: Why are these distributed but not compiled?
EXTRA_DIST += u12-ccd.c u12-hw.c u12-hwdef.h u12-if.c u12-image.c u12-io.c u12-map.c u12-motor.c u12-scanner.h u12-shading.c u12-tpa.c
@@ -1065,7 +1133,7 @@ libumax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax
nodist_libsane_umax_la_SOURCES = umax-s.c
libsane_umax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax
libsane_umax_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
-libsane_umax_la_LIBADD = $(COMMON_LIBS) libumax.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+libsane_umax_la_LIBADD = $(COMMON_LIBS) libumax.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_thread.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += umax.conf.in
# TODO: Why are these distributed but not compiled?
EXTRA_DIST += umax-scanner.c umax-scanner.h umax-scsidef.h umax-uc1200s.c umax-uc1200se.c umax-uc1260.c umax-uc630.c umax-uc840.c umax-ug630.c umax-ug80.c umax-usb.c
@@ -1110,8 +1178,10 @@ EXTRA_DIST += xerox_mfp.conf.in
libdll_preload_la_SOURCES = dll.c
libdll_preload_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll -DENABLE_PRELOAD
+libdll_preload_la_LIBADD = ../sanei/sanei_usb.lo $(USB_LIBS) $(XML_LIBS)
libdll_la_SOURCES = dll.c
libdll_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
+libdll_la_LIBADD = ../sanei/sanei_usb.lo $(USB_LIBS) $(XML_LIBS)
BUILT_SOURCES = dll-preload.h
CLEANFILES += dll-preload.h
@@ -1142,13 +1212,13 @@ EXTRA_DIST += dll.aliases
# what backends are preloaded. It should include what is needed by
# those backends that are actually preloaded.
if preloadable_backends_enabled
-PRELOADABLE_BACKENDS_LIBS = ../sanei/sanei_config2.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo ../sanei/sanei_pa4s2.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo ../sanei/sanei_magic.lo $(LIBV4L_LIBS) $(MATH_LIB) $(IEEE1284_LIBS) $(TIFF_LIBS) $(JPEG_LIBS) $(GPHOTO2_LIBS) $(SOCKET_LIBS) $(USB_LIBS) $(AVAHI_LIBS) $(SCSI_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS)
+PRELOADABLE_BACKENDS_LIBS = ../sanei/sanei_config2.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo ../sanei/sanei_pa4s2.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo ../sanei/sanei_magic.lo $(LIBV4L_LIBS) $(MATH_LIB) $(IEEE1284_LIBS) $(TIFF_LIBS) $(JPEG_LIBS) $(GPHOTO2_LIBS) $(SOCKET_LIBS) $(USB_LIBS) $(AVAHI_LIBS) $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) $(XML_LIBS)
PRELOADABLE_BACKENDS_DEPS = ../sanei/sanei_config2.lo ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo ../sanei/sanei_pv8630.lo ../sanei/sanei_pp.lo ../sanei/sanei_thread.lo ../sanei/sanei_lm983x.lo ../sanei/sanei_access.lo ../sanei/sanei_net.lo ../sanei/sanei_wire.lo ../sanei/sanei_codec_bin.lo ../sanei/sanei_pa4s2.lo ../sanei/sanei_ab306.lo ../sanei/sanei_pio.lo ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo ../sanei/sanei_magic.lo $(SANEI_SANEI_JPEG_LO)
endif
nodist_libsane_la_SOURCES = dll-s.c
libsane_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll
libsane_la_LDFLAGS = $(DIST_LIBS_LDFLAGS)
-libsane_la_LIBADD = $(COMMON_LIBS) $(PRELOADABLE_BACKENDS_ENABLED) libdll_preload.la sane_strstatus.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo $(PRELOADABLE_BACKENDS_LIBS) $(DL_LIBS)
+libsane_la_LIBADD = $(COMMON_LIBS) $(PRELOADABLE_BACKENDS_ENABLED) libdll_preload.la sane_strstatus.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo $(PRELOADABLE_BACKENDS_LIBS) $(DL_LIBS) $(XML_LIBS)
# WARNING: Automake is getting this wrong so have to do it ourselves.
libsane_la_DEPENDENCIES = $(COMMON_LIBS) $(PRELOADABLE_BACKENDS_ENABLED) libdll_preload.la sane_strstatus.lo ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo $(PRELOADABLE_BACKENDS_DEPS)
diff --git a/backend/agfafocus.c b/backend/agfafocus.c
index 0b59d2d..8177f38 100644
--- a/backend/agfafocus.c
+++ b/backend/agfafocus.c
@@ -1485,6 +1485,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
case OPT_BR_Y:
if (info)
*info |= SANE_INFO_RELOAD_PARAMS;
+ // fall through
case OPT_SHARPEN:
case OPT_EXPOSURE:
case OPT_ATTENUATION_RED:
diff --git a/backend/apple.c b/backend/apple.c
index 167f6ea..980edb4 100644
--- a/backend/apple.c
+++ b/backend/apple.c
@@ -2064,7 +2064,8 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
v2 = SANE_UNFIX (f);
DBG (FLOW_CONTROL, "Value %g (Fixed)\n",
(action == SANE_ACTION_GET_VALUE) ? v1 : v2);
- }
+ break;
+ }
default:
DBG (FLOW_CONTROL, "Value %u (Int).\n",
(action == SANE_ACTION_GET_VALUE)
diff --git a/backend/artec.c b/backend/artec.c
index 2ba8d6d..395b4f1 100644
--- a/backend/artec.c
+++ b/backend/artec.c
@@ -1999,8 +1999,8 @@ attach (const char *devname, ARTEC_Device ** devp)
DBG (6, "Found BlackWidow BW4800SP scanner, setting up like AT3\n");
/* setup the vendor and product to mimic the Artec/Ultima AT3 */
- strncpy (result + 8, "ULTIMA", 6);
- strncpy (result + 16, "AT3 ", 16);
+ memcpy (result + 8, "ULTIMA", 6);
+ memcpy (result + 16, "AT3 ", 16);
}
/*
@@ -2013,8 +2013,8 @@ attach (const char *devname, ARTEC_Device ** devp)
DBG (6, "Found Plustek 19200S scanner, setting up like AM12S\n");
/* setup the vendor and product to mimic the Artec/Ultima AM12S */
- strncpy (result + 8, "ULTIMA", 6);
- strncpy (result + 16, "AM12S ", 16);
+ memcpy (result + 8, "ULTIMA", 6);
+ memcpy (result + 16, "AM12S ", 16);
}
/*
diff --git a/backend/artec_eplus48u.c b/backend/artec_eplus48u.c
index 0e81b06..c5eb225 100644
--- a/backend/artec_eplus48u.c
+++ b/backend/artec_eplus48u.c
@@ -2956,7 +2956,7 @@ init_options (Artec48U_Scanner * s)
SANE_I18N ("If enabled, only the shading correction is "
"performed during calibration. The default values "
"for gain, offset and exposure time, "
- "either build-in or from the configuration file, "
+ "either built-in or from the configuration file, "
"are used.");
s->opt[OPT_CALIBRATE_SHADING].type = SANE_TYPE_BOOL;
s->opt[OPT_CALIBRATE_SHADING].unit = SANE_UNIT_NONE;
diff --git a/backend/as6e.c b/backend/as6e.c
index 37a6d3b..47d8c90 100644
--- a/backend/as6e.c
+++ b/backend/as6e.c
@@ -797,7 +797,6 @@ check_for_driver (const char *devname)
struct stat statbuf;
mode_t modes;
char *path;
- char fullname[NAMESIZE];
char dir[NAMESIZE];
int count = 0, offset = 0, valid;
@@ -806,7 +805,6 @@ check_for_driver (const char *devname)
return 0;
while (path[count] != '\0')
{
- memset (fullname, '\0', sizeof (fullname));
memset (dir, '\0', sizeof (dir));
valid = 1;
while ((path[count] != ':') && (path[count] != '\0'))
@@ -819,19 +817,19 @@ check_for_driver (const char *devname)
count++;
}
if (valid == 1)
- {
- /* use sizeof(fullname)-1 to make sure there is at least one padded null byte */
- strncpy (fullname, dir, sizeof(fullname)-1);
- /* take into account that fullname already contains non-null bytes */
- strncat (fullname, "/", sizeof(fullname)-strlen(fullname)-1);
- strncat (fullname, devname, sizeof(fullname)-strlen(fullname)-1);
- if (!stat (fullname, &statbuf))
- {
- modes = statbuf.st_mode;
- if (S_ISREG (modes))
- return (1); /* found as6edriver */
- }
- }
+ {
+ char fullname[NAMESIZE];
+ int len = snprintf(fullname, sizeof(fullname), "%s/%s", dir, devname);
+ if ((len > 0) && (len <= (int)sizeof(fullname)))
+ {
+ if (!stat (fullname, &statbuf))
+ {
+ modes = statbuf.st_mode;
+ if (S_ISREG (modes))
+ return (1); /* found as6edriver */
+ }
+ }
+ }
if (path[count] == '\0')
return (0); /* end of path --no driver found */
count++;
diff --git a/backend/avision.c b/backend/avision.c
index e9f0145..55b5f4f 100644
--- a/backend/avision.c
+++ b/backend/avision.c
@@ -7921,7 +7921,7 @@ sane_open (SANE_String_Const devicename, SANE_Handle *handle)
However, I was told Cygwin (et al.) takes care of it. */
strncpy(s->duplex_rear_fname, "/tmp/avision-rear-XXXXXX", PATH_MAX);
- if (! mktemp(s->duplex_rear_fname) ) {
+ if (! mkstemp(s->duplex_rear_fname) ) {
DBG (1, "sane_open: failed to generate temporary fname for duplex scans\n");
return SANE_STATUS_NO_MEM;
}
diff --git a/backend/avision.h b/backend/avision.h
index 3f42ff6..58552c0 100644
--- a/backend/avision.h
+++ b/backend/avision.h
@@ -780,7 +780,7 @@ typedef struct acceleration_info
#define SANE_NAME_DUPLEX "duplex"
#define SANE_TITLE_DUPLEX SANE_I18N("Duplex scan")
-#define SANE_DESC_DUPLEX SANE_I18N("Duplex scan provide a scan of the front and back side of the document")
+#define SANE_DESC_DUPLEX SANE_I18N("Duplex scan provides a scan of the front and back side of the document")
#ifdef AVISION_ENHANCED_SANE
#warning "Compiled Avision backend will violate the SANE standard"
diff --git a/backend/canon-sane.c b/backend/canon-sane.c
index 5d9ec99..cd75719 100644
--- a/backend/canon-sane.c
+++ b/backend/canon-sane.c
@@ -1125,9 +1125,9 @@ sane_start (SANE_Handle handle)
if (thistmpfile != NULL)
{
- if (mktemp(thistmpfile) == 0)
+ if (!mkstemp(thistmpfile))
{
- DBG(1, "mktemp(thistmpfile) is failed\n");
+ DBG(1, "mkstemp(thistmpfile) is failed\n");
return (SANE_STATUS_INVAL);
}
}
diff --git a/backend/canon.c b/backend/canon.c
index ad738e8..4a5a2a2 100644
--- a/backend/canon.c
+++ b/backend/canon.c
@@ -457,7 +457,7 @@ sense_handler (int scsi_fd, u_char * result, void *arg)
status = SANE_STATUS_UNSUPPORTED;
break;
case 0x8002:
- sense_str = SANE_I18N("option not connect");
+ sense_str = SANE_I18N("option not correct");
status = SANE_STATUS_UNSUPPORTED;
break;
default:
diff --git a/backend/canon630u-common.c b/backend/canon630u-common.c
index 27c34ff..5cd0fcf 100644
--- a/backend/canon630u-common.c
+++ b/backend/canon630u-common.c
@@ -939,7 +939,7 @@ plugin_cal (CANON_Handle * s)
{
DBG (1, "No temp filename!\n");
s->fname = strdup ("/tmp/cal.XXXXXX");
- mktemp (s->fname);
+ mkstemp (s->fname);
}
s->width = 2551;
s->height = 75;
@@ -1583,7 +1583,7 @@ CANON_start_scan (CANON_Handle * scanner)
/* choose a temp file name for scan data */
scanner->fname = strdup ("/tmp/scan.XXXXXX");
- if (!mktemp (scanner->fname))
+ if (!mkstemp (scanner->fname))
return SANE_STATUS_IO_ERROR;
/* calibrate if needed */
diff --git a/backend/canon_dr.c b/backend/canon_dr.c
index 64aec31..f6cd5d4 100644
--- a/backend/canon_dr.c
+++ b/backend/canon_dr.c
@@ -3,7 +3,7 @@
This file is part of the SANE package, and implements a SANE backend
for various Canon DR-series scanners.
- Copyright (C) 2008-2016 m. allan noah
+ Copyright (C) 2008-2019 m. allan noah
Yabarana Corp. www.yabarana.com provided significant funding
EvriChart, Inc. www.evrichart.com provided funding and loaned equipment
@@ -338,6 +338,8 @@
- initial support for P-150
v57 2019-02-24, manuarg
- complete support for X-10, including hardware cropping
+ v58 2019-11-10, MAN
+ - adjust wait_scanner to set runRS only as a last resort, bug #154
SANE FLOW DIAGRAM
@@ -388,7 +390,7 @@
#include "canon_dr.h"
#define DEBUG 1
-#define BUILD 57
+#define BUILD 58
/* values for SANE_DEBUG_CANON_DR env var:
- errors 5
@@ -7584,6 +7586,24 @@ wait_scanner(struct scanner *s)
NULL, NULL
);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick.\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again.\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
// some scanners (such as DR-F120) are OK but will not respond to commands
// when in sleep mode. By checking the sense it wakes them up.
if (ret != SANE_STATUS_GOOD) {
@@ -7596,7 +7616,16 @@ wait_scanner(struct scanner *s)
);
}
if (ret != SANE_STATUS_GOOD) {
- DBG(5,"WARNING: Brain-dead scanner. Hitting with stick instead.\n");
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick a third time.\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick a fourth time.\n");
ret = do_cmd (
s, 0, 1,
cmd, cmdLen,
diff --git a/backend/dc25.c b/backend/dc25.c
index 3bb81f5..6188608 100644
--- a/backend/dc25.c
+++ b/backend/dc25.c
@@ -2030,7 +2030,7 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle)
if (tmpname == NULL)
{
tmpname = tmpnamebuf;
- if (mktemp (tmpname) == NULL)
+ if (!mkstemp (tmpname))
{
DBG (1, "Unable to make temp file %s\n", tmpname);
return SANE_STATUS_INVAL;
diff --git a/backend/dll.c b/backend/dll.c
index 8f0983d..73ffde4 100644
--- a/backend/dll.c
+++ b/backend/dll.c
@@ -89,6 +89,20 @@ posix_dlsym (void *handle, const char *func)
}
# pragma GCC diagnostic pop
+ /* Similar to the above, GCC also warns about conversion between
+ pointers to functions. The ISO C standard says that invoking a
+ converted pointer to a function whose type is not compatible with
+ the pointed-to type, the behavior is undefined. Although GCC is
+ correct to warn about this, the dll backend has been using these
+ conversions without issues for a very long time already.
+
+ Rather than push/pop around every use, which would get very ugly
+ real fast, ignore this particular warning for the remainder of
+ the file.
+ */
+# pragma GCC diagnostic ignored "-Wpragmas" /* backward compatibility */
+# pragma GCC diagnostic ignored "-Wcast-function-type"
+
/* Older versions of dlopen() don't define RTLD_NOW and RTLD_LAZY.
They all seem to use a mode of 1 to indicate RTLD_NOW and some do
not support RTLD_LAZY at all. Hence, unless defined, we define
@@ -139,6 +153,8 @@ posix_dlsym (void *handle, const char *func)
#define DLL_CONFIG_FILE "dll.conf"
#define DLL_ALIASES_FILE "dll.aliases"
+#include "../include/sane/sanei_usb.h"
+
enum SANE_Ops
{
OP_INIT = 0,
@@ -797,7 +813,8 @@ read_dlld (void)
DIR *dlld;
struct dirent *dllconf;
struct stat st;
- char conffile[PATH_MAX], dlldir[PATH_MAX];
+ char dlldir[PATH_MAX];
+ char conffile[PATH_MAX + strlen("/") + NAME_MAX];
size_t len, plen;
const char *dir_list;
char *copy, *next, *dir;
@@ -849,7 +866,7 @@ read_dlld (void)
|| (dllconf->d_name[len-1] == '#'))
continue;
- snprintf (conffile, PATH_MAX, "%s/%s", dlldir, dllconf->d_name);
+ snprintf (conffile, sizeof(conffile), "%s/%s", dlldir, dllconf->d_name);
DBG (5, "sane_init/read_dlld: considering %s\n", conffile);
@@ -1177,18 +1194,73 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
}
dev_name = strchr (full_name, ':');
+
+ int is_fakeusb = 0, is_fakeusbdev = 0, is_fakeusbout = 0;
+
if (dev_name)
{
- be_name = strndup(full_name, dev_name - full_name);
- ++dev_name; /* skip colon */
+ is_fakeusb = strncmp(full_name, "fakeusb", dev_name - full_name) == 0 &&
+ dev_name - full_name == 7;
+ is_fakeusbdev = strncmp(full_name, "fakeusbdev", dev_name - full_name) == 0 &&
+ dev_name - full_name == 10;
+ is_fakeusbout = strncmp(full_name, "fakeusbout", dev_name - full_name) == 0 &&
+ dev_name - full_name == 10;
+ }
+
+ if (is_fakeusb || is_fakeusbdev)
+ {
+ ++dev_name; // skip colon
+ status = sanei_usb_testing_enable_replay(dev_name, is_fakeusbdev);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ be_name = sanei_usb_testing_get_backend();
+ if (be_name == NULL)
+ {
+ DBG (0, "%s: unknown backend for testing\n", __func__);
+ return SANE_STATUS_ACCESS_DENIED;
+ }
}
else
{
- /* if no colon interpret full_name as the backend name; an empty
- backend device name will cause us to open the first device of
- that backend. */
- be_name = strdup(full_name);
- dev_name = "";
+ char* fakeusbout_path = NULL;
+ if (is_fakeusbout)
+ {
+ ++dev_name; // skip colon
+
+ const char* path_end = strchr(dev_name, ':');
+ if (path_end == NULL)
+ {
+ DBG (0, "%s: the device name does not contain path\n", __func__);
+ return SANE_STATUS_INVAL;
+ }
+ fakeusbout_path = strndup(dev_name, path_end - dev_name);
+
+ full_name = path_end + 1; // skip colon
+ dev_name = strchr(full_name, ':');
+ }
+
+ if (dev_name)
+ {
+ be_name = strndup(full_name, dev_name - full_name);
+ ++dev_name; /* skip colon */
+ }
+ else
+ {
+ /* if no colon interpret full_name as the backend name; an empty
+ backend device name will cause us to open the first device of
+ that backend. */
+ be_name = strdup(full_name);
+ dev_name = "";
+ }
+
+ if (is_fakeusbout)
+ {
+ status = sanei_usb_testing_enable_record(fakeusbout_path, be_name);
+ free(fakeusbout_path);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
}
if (!be_name)
diff --git a/backend/dll.conf.in b/backend/dll.conf.in
index 8d459ab..92091cb 100644
--- a/backend/dll.conf.in
+++ b/backend/dll.conf.in
@@ -11,10 +11,10 @@ net
abaton
agfafocus
apple
-avision
artec
artec_eplus48u
as6e
+avision
bh
canon
canon630u
@@ -24,33 +24,35 @@ cardscan
coolscan
#coolscan2
coolscan3
-#dc25
#dc210
#dc240
+#dc25
dell1600n_net
dmc
epjitsu
#epson
epson2
epsonds
+escl
fujitsu
-#gphoto2
genesys
+#gphoto2
gt68xx
hp
-hp3900
-hpsj5s
hp3500
+hp3900
hp4200
hp5400
hp5590
hpljm1005
+hpsj5s
hs2p
ibm
kodak
kodakaio
kvs1025
kvs20xx
+kvs40xx
leo
lexmark
ma1509
@@ -66,6 +68,7 @@ nec
niash
#p5
pie
+pieusb
pint
pixma
plustek
@@ -91,7 +94,7 @@ teco3
#test
u12
umax
-#umax_pp
umax1220u
+#umax_pp
v4l
xerox_mfp
diff --git a/backend/epjitsu.c b/backend/epjitsu.c
index bee4310..714bc0b 100644
--- a/backend/epjitsu.c
+++ b/backend/epjitsu.c
@@ -349,10 +349,21 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
continue;
if ((strncmp ("firmware", lp, 8) == 0) && isspace (lp[8])) {
+ size_t firmware_len;
+
lp += 8;
lp = sanei_config_skip_whitespace (lp);
DBG (15, "sane_get_devices: firmware '%s'\n", lp);
- strncpy((char *)global_firmware_filename,lp,PATH_MAX);
+
+ firmware_len = strlen(lp);
+ if (firmware_len > sizeof(global_firmware_filename) - 1)
+ {
+ DBG (5, "sane_get_devices: firmware file too long. ignoring '%s'\n", lp);
+ }
+ else
+ {
+ strcpy((char *)global_firmware_filename, lp);
+ }
}
else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) {
DBG (15, "sane_get_devices: looking for '%s'\n", lp);
diff --git a/backend/epson2.c b/backend/epson2.c
index f119018..e6f6786 100644
--- a/backend/epson2.c
+++ b/backend/epson2.c
@@ -1889,12 +1889,11 @@ setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
case OPT_BR_X:
case OPT_BR_Y:
- sval->w = *((SANE_Word *) value);
- if (SANE_UNFIX(sval->w) == 0) {
+ if (SANE_UNFIX(*((SANE_Word *) value)) == 0) {
DBG(17, "invalid br-x or br-y\n");
return SANE_STATUS_INVAL;
}
- /* passthru */
+ // fall through
case OPT_TL_X:
case OPT_TL_Y:
sval->w = *((SANE_Word *) value);
diff --git a/backend/epsonds.c b/backend/epsonds.c
index d402f58..ff5d681 100644
--- a/backend/epsonds.c
+++ b/backend/epsonds.c
@@ -1050,12 +1050,11 @@ setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
case OPT_BR_X:
case OPT_BR_Y:
- sval->w = *((SANE_Word *) value);
- if (SANE_UNFIX(sval->w) == 0) {
+ if (SANE_UNFIX(*((SANE_Word *) value)) == 0) {
DBG(17, " invalid br-x or br-y\n");
return SANE_STATUS_INVAL;
}
- /* passthru */
+ // fall through
case OPT_TL_X:
case OPT_TL_Y:
sval->w = *((SANE_Word *) value);
diff --git a/backend/escl.conf.in b/backend/escl.conf.in
new file mode 100644
index 0000000..2aa6257
--- /dev/null
+++ b/backend/escl.conf.in
@@ -0,0 +1,17 @@
+# escl.conf -- ESCL configuration
+# Lines starting with a # or a ; are comments. Comments must be on a
+# line of their own. End-of-line comments are not supported.
+# Explanation : if you can't detect your device but it's an eSCL device, modify this escl conf' file to use your device.
+# -> uncomment the lines below, from '[device]' to 'port'.
+# -> put your device name instead of 'EPSON X'.
+# -> put your type of protocol instead of 'https' : http or https.
+# -> put your device ip instead of '123.456.789.10'.
+# -> put the port that you use instead of '88'.
+# For example, the lines below are for one device, but if you have several devices to use, you can duplicate the lines below as many times as you have devices.
+
+#[device]
+
+#model EPSON X
+#type https
+#ip 123.456.789.10
+#port 88
diff --git a/backend/escl/escl.c b/backend/escl/escl.c
new file mode 100644
index 0000000..8df6c5c
--- /dev/null
+++ b/backend/escl/escl.c
@@ -0,0 +1,777 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#include "escl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <setjmp.h>
+
+#include <curl/curl.h>
+
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#define min(A,B) (((A)<(B)) ? (A) : (B))
+#define max(A,B) (((A)>(B)) ? (A) : (B))
+#define INPUT_BUFFER_SIZE 4096
+
+static const SANE_Device **devlist = NULL;
+static ESCL_Device *list_devices_primary = NULL;
+static int num_devices = 0;
+
+typedef struct Handled {
+ struct Handled *next;
+ SANE_String_Const name;
+ char *result;
+ ESCL_ScanParam param;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ capabilities_t *scanner;
+ SANE_Range x_range;
+ SANE_Range y_range;
+ SANE_Bool cancel;
+ SANE_Bool write_scan_data;
+ SANE_Bool decompress_scan_data;
+ SANE_Bool end_read;
+ SANE_Parameters ps;
+} escl_sane_t;
+
+/**
+ * \fn static SANE_Status escl_add_in_list(ESCL_Device *current)
+ * \brief Function that adds all the element needed to my list :
+ * the port number, the model name, the ip address, and the type of url (http/https).
+ * Moreover, this function counts the number of devices found.
+ *
+ * \return SANE_STATUS_GOOD if everything is OK.
+ */
+static SANE_Status
+escl_add_in_list(ESCL_Device *current)
+{
+ ++num_devices;
+ current->next = list_devices_primary;
+ list_devices_primary = current;
+ return (SANE_STATUS_GOOD);
+}
+
+/**
+ * \fn SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type)
+ * \brief Function that browses my list ('for' loop) and returns the "escl_add_in_list" function to
+ * adds all the element needed to my list :
+ * the port number, the model name, the ip address and the type of the url (http / https).
+ *
+ * \return escl_add_in_list(current)
+ */
+SANE_Status
+escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type)
+{
+ ESCL_Device *current = NULL;
+ DBG (10, "escl_device_add\n");
+ for (current = list_devices_primary; current; current = current->next) {
+ if (strcmp(current->ip_address, ip_address) == 0 && current->port_nb == port_nb
+ && strcmp(current->type, type) == 0)
+ return (SANE_STATUS_GOOD);
+ }
+ current = malloc(sizeof(*current));
+ if (current == NULL)
+ return (SANE_STATUS_NO_MEM);
+ memset(current, 0, sizeof(*current));
+ current->port_nb = port_nb;
+ current->model_name = strdup(model_name);
+ current->ip_address = strdup(ip_address);
+ current->type = strdup(type);
+ return escl_add_in_list(current);
+}
+
+/**
+ * \fn static inline size_t max_string_size(const SANE_String_Const strings[])
+ * \brief Function that browses the string ('for' loop) and counts the number of character in the string.
+ * --> this allows to know the maximum size of the string.
+ *
+ * \return max_size + 1 (the size max)
+ */
+static inline size_t
+max_string_size(const SANE_String_Const strings[])
+{
+ size_t max_size = 0;
+ int i = 0;
+
+ for (i = 0; strings[i]; ++i) {
+ size_t size = strlen (strings[i]);
+ if (size > max_size)
+ max_size = size;
+ }
+ return (max_size + 1);
+}
+
+/**
+ * \fn static SANE_Device *convertFromESCLDev(ESCL_Device *cdev)
+ * \brief Function that checks if the url of the received scanner is secured or not (http / https).
+ * --> if the url is not secured, our own url will be composed like "http://'ip':'port'".
+ * --> else, our own url will be composed like "https://'ip':'port'".
+ * AND, it's in this function that we gather all the informations of the url (that were in our list) :
+ * the model_name, the port, the ip, and the type of url.
+ * SO, leaving this function, we have in memory the complete url.
+ *
+ * \return sdev (structure that contains the elements of the url)
+ */
+static SANE_Device *
+convertFromESCLDev(ESCL_Device *cdev)
+{
+ SANE_Device *sdev = (SANE_Device*) calloc(1, sizeof(SANE_Device));
+ char tmp[PATH_MAX] = { 0 };
+
+ if (strcmp(cdev->type, "_uscan._tcp") == 0 || strcmp(cdev->type, "http") == 0)
+ snprintf(tmp, sizeof(tmp), "http://%s:%d", cdev->ip_address, cdev->port_nb);
+ else
+ snprintf(tmp, sizeof(tmp), "https://%s:%d", cdev->ip_address, cdev->port_nb);
+ DBG( 1, "Escl add device : %s\n", tmp);
+ sdev->name = strdup(tmp);
+ sdev->model = strdup(cdev->model_name);
+ sdev->vendor = strdup("ESCL");
+ sdev->type = strdup("flatbed scanner");
+ return (sdev);
+}
+
+/**
+ * \fn SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+ * \brief Function that's called before any other SANE function ; it's the first SANE function called.
+ * --> this function checks the SANE config. and can check the authentication of the user if
+ * 'authorize' value is more than SANE_TRUE.
+ * In this case, it will be necessary to define an authentication method.
+ *
+ * \return SANE_STATUS_GOOD (everything is OK)
+ */
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
+{
+ DBG_INIT();
+ DBG (10, "escl sane_init\n");
+ SANE_Status status = SANE_STATUS_GOOD;
+ curl_global_init(CURL_GLOBAL_ALL);
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE(1, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ return (SANE_STATUS_GOOD);
+}
+
+/**
+ * \fn void sane_exit(void)
+ * \brief Function that must be called to terminate use of a backend.
+ * This function will first close all device handles that still might be open.
+ * --> by freeing all the elements of my list.
+ * After this function, no function other than 'sane_init' may be called.
+ */
+void
+sane_exit(void)
+{
+ DBG (10, "escl sane_exit\n");
+ ESCL_Device *next = NULL;
+
+ while (list_devices_primary != NULL) {
+ next = list_devices_primary->next;
+ free(list_devices_primary);
+ list_devices_primary = next;
+ }
+ if (devlist)
+ free (devlist);
+ list_devices_primary = NULL;
+ devlist = NULL;
+ curl_global_cleanup();
+}
+
+/**
+ * \fn static SANE_Status attach_one_config(SANEI_Config *config, const char *line)
+ * \brief Function that implements a configuration file to the user :
+ * if the user can't detect some devices, he will be able to force their detection with this config' file to use them.
+ * Thus, this function parses the config' file to use the device of the user with the information below :
+ * the type of protocol (http/https), the ip, the port number, and the model name.
+ *
+ * \return escl_add_in_list(escl_device) if the parsing worked, SANE_STATUS_GOOD otherwise.
+ */
+static SANE_Status
+attach_one_config(SANEI_Config __sane_unused__ *config, const char *line)
+{
+ int port = 0;
+ static int count = 0;
+ static ESCL_Device *escl_device = NULL;
+
+ if (strncmp(line, "[device]", 8) == 0) {
+ count = 0;
+ escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device));
+ }
+ if (strncmp(line, "ip", 2) == 0) {
+ const char *ip_space = sanei_config_skip_whitespace(line + 2);
+ if (escl_device != NULL && ip_space != NULL) {
+ count++;
+ escl_device->ip_address = strdup(ip_space);
+ }
+ }
+ if (sscanf(line, "port %i", &port) == 1 && port != 0) {
+ const char *port_space = sanei_config_skip_whitespace(line + 4);
+ if (escl_device != NULL && port_space != NULL) {
+ count++;
+ escl_device->port_nb = port;
+ }
+ }
+ if (strncmp(line, "model", 5) == 0) {
+ const char *model_space = sanei_config_skip_whitespace(line + 5);
+ if (escl_device != NULL && model_space != NULL) {
+ count++;
+ escl_device->model_name = strdup(model_space);
+ }
+ }
+ if (strncmp(line, "type", 4) == 0) {
+ const char *type_space = sanei_config_skip_whitespace(line + 4);
+ if (escl_device != NULL && type_space != NULL) {
+ count++;
+ escl_device->type = strdup(type_space);
+ }
+ }
+ if (count == 4)
+ return (escl_add_in_list(escl_device));
+ return (SANE_STATUS_GOOD);
+}
+
+/**
+ * \fn SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
+ * \brief Function that searches for connected devices and places them in our 'device_list'. ('for' loop)
+ * If the attribute 'local_only' is worth SANE_FALSE, we only returns the connected devices locally.
+ *
+ * \return SANE_STATUS_GOOD if devlist != NULL ; SANE_STATUS_NO_MEM otherwise.
+ */
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ if (local_only) /* eSCL is a network-only protocol */
+ return (device_list ? SANE_STATUS_GOOD : SANE_STATUS_INVAL);
+
+ DBG (10, "escl sane_get_devices\n");
+ ESCL_Device *dev = NULL;
+ static const SANE_Device **devlist = 0;
+ SANE_Status status;
+
+ if (device_list == NULL)
+ return (SANE_STATUS_INVAL);
+ status = sanei_configure_attach(ESCL_CONFIG_FILE, NULL, attach_one_config);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ escl_devices(&status);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ if (devlist)
+ free(devlist);
+ devlist = (const SANE_Device **) calloc (num_devices + 1, sizeof (devlist[0]));
+ if (devlist == NULL)
+ return (SANE_STATUS_NO_MEM);
+ int i = 0;
+ for (dev = list_devices_primary; i < num_devices; dev = dev->next) {
+ SANE_Device *s_dev = convertFromESCLDev(dev);
+ devlist[i] = s_dev;
+ i++;
+ }
+ devlist[i] = 0;
+ *device_list = devlist;
+ return (devlist) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
+}
+
+/**
+ * \fn static SANE_Status init_options(SANE_String_Const name, escl_sane_t *s)
+ * \brief Function thzt initializes all the needed options of the received scanner
+ * (the resolution / the color / the margins) thanks to the informations received with
+ * the 'escl_capabilities' function, called just before.
+ *
+ * \return status (if everything is OK, status = SANE_STATUS_GOOD)
+ */
+static SANE_Status
+init_options(SANE_String_Const name, escl_sane_t *s)
+{
+ DBG (10, "escl init_options\n");
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i = 0;
+
+ if (name == NULL)
+ return (SANE_STATUS_INVAL);
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ s->x_range.min = 0;
+ s->x_range.max = s->scanner->MaxWidth - s->scanner->MinWidth;
+ s->x_range.quant = 1;
+ s->y_range.min = 0;
+ s->y_range.max = s->scanner->MaxHeight - s->scanner->MinHeight;
+ s->y_range.quant = 1;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = s->scanner->ColorModes;
+ s->val[OPT_MODE].s = (char *)strdup(s->scanner->ColorModes[0]);
+ s->opt[OPT_MODE].size = max_string_size(s->scanner->ColorModes);
+ s->scanner->default_color = (char *)strdup(s->scanner->ColorModes[0]);
+
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->scanner->SupportedResolutions;
+ s->val[OPT_RESOLUTION].w = s->scanner->SupportedResolutions[1];
+ s->scanner->default_resolution = s->scanner->SupportedResolutions[1];
+
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW;
+ s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
+
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY;
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->x_range;
+ s->val[OPT_TL_X].w = s->scanner->RiskyLeftMargin;
+
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->y_range;
+ s->val[OPT_TL_Y].w = s->scanner->RiskyTopMargin;
+
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->x_range;
+ s->val[OPT_BR_X].w = s->scanner->MaxWidth;
+
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->y_range;
+ s->val[OPT_BR_Y].w = s->scanner->MaxHeight;
+ return (status);
+}
+
+/**
+ * \fn SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h)
+ * \brief Function that establishes a connection with the device named by 'name',
+ * and returns a 'handler' using 'SANE_Handle *h', representing it.
+ * Thus, it's this function that calls the 'escl_status' function firstly,
+ * then the 'escl_capabilities' function, and, after, the 'init_options' function.
+ *
+ * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle *h)
+{
+ DBG (10, "escl sane_open\n");
+ SANE_Status status;
+ escl_sane_t *handler = NULL;
+
+ if (name == NULL)
+ return (SANE_STATUS_INVAL);
+ status = escl_status(name);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ handler = (escl_sane_t *)calloc(1, sizeof(escl_sane_t));
+ if (handler == NULL)
+ return (SANE_STATUS_NO_MEM);
+ handler->name = strdup(name);
+ handler->scanner = escl_capabilities(name, &status);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ status = init_options(name, handler);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ handler->ps.depth = 8;
+ handler->ps.last_frame = SANE_TRUE;
+ handler->ps.format = SANE_FRAME_RGB;
+ handler->ps.pixels_per_line = handler->val[OPT_BR_X].w;
+ handler->ps.lines = handler->val[OPT_BR_Y].w;
+ handler->ps.bytes_per_line = handler->ps.pixels_per_line * 3;
+ status = sane_get_parameters(handler, 0);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ handler->cancel = SANE_FALSE;
+ handler->write_scan_data = SANE_FALSE;
+ handler->decompress_scan_data = SANE_FALSE;
+ handler->end_read = SANE_FALSE;
+ *h = handler;
+ return (status);
+}
+
+/**
+ * \fn void sane_cancel(SANE_Handle h)
+ * \brief Function that's used to, immediately or as quickly as possible, cancel the currently
+ * pending operation of the device represented by 'SANE_Handle h'.
+ * This functions calls the 'escl_scanner' functions, that resets the scan operations.
+ */
+void
+sane_cancel(SANE_Handle h)
+{
+ DBG (10, "escl sane_cancel\n");
+ escl_sane_t *handler = h;
+ if (handler->scanner->tmp)
+ {
+ fclose(handler->scanner->tmp);
+ handler->scanner->tmp = NULL;
+ }
+ handler->cancel = SANE_TRUE;
+ escl_scanner(handler->name, handler->result);
+}
+
+/**
+ * \fn void sane_close(SANE_Handle h)
+ * \brief Function that closes the communication with the device represented by 'SANE_Handle h'.
+ * This function must release the resources that were allocated to the opening of 'h'.
+ */
+void
+sane_close(SANE_Handle h)
+{
+ DBG (10, "escl sane_close\n");
+ if (h != NULL) {
+ free(h);
+ h = NULL;
+ }
+}
+
+/**
+ * \fn const SANE_Option_Descriptor *sane_get_option_descriptor(SANE_Handle h, SANE_Int n)
+ * \brief Function that retrieves a descriptor from the n number option of the scanner
+ * represented by 'h'.
+ * The descriptor remains valid until the machine is closed.
+ *
+ * \return s->opt + n
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor(SANE_Handle h, SANE_Int n)
+{
+ DBG (10, "escl sane_get_option_descriptor\n");
+ escl_sane_t *s = h;
+
+ if ((unsigned) n >= NUM_OPTIONS || n < 0)
+ return (0);
+ return (s->opt + n);
+}
+
+/**
+ * \fn SANE_Status sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i)
+ * \brief Function that defines the actions to perform for the 'n' option of the machine,
+ * represented by 'h', if the action is 'a'.
+ * There are 3 types of possible actions :
+ * --> SANE_ACTION_GET_VALUE: 'v' must be used to provide the value of the option.
+ * --> SANE_ACTION_SET_VALUE: The option must take the 'v' value.
+ * --> SANE_ACTION_SET_AUTO: The backend or machine must affect the option with an appropriate value.
+ * Moreover, the parameter 'i' is used to provide additional information about the state of
+ * 'n' option if SANE_ACTION_SET_VALUE has been performed.
+ *
+ * \return SANE_STATUS_GOOD if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL
+ */
+SANE_Status
+sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i)
+{
+ DBG (10, "escl sane_control_option\n");
+ escl_sane_t *handler = h;
+
+ if (i)
+ *i = 0;
+ if (n >= NUM_OPTIONS || n < 0)
+ return (SANE_STATUS_INVAL);
+ if (a == SANE_ACTION_GET_VALUE) {
+ switch (n) {
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ *(SANE_Word *) v = handler->val[n].w;
+ break;
+ case OPT_MODE:
+ strcpy (v, handler->val[n].s);
+ break;
+ case OPT_MODE_GROUP:
+ default:
+ break;
+ }
+ return (SANE_STATUS_GOOD);
+ }
+ if (a == SANE_ACTION_SET_VALUE) {
+ switch (n) {
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ handler->val[n].w = *(SANE_Word *) v;
+ if (i && handler->val[n].w != *(SANE_Word *) v)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ handler->val[n].w = *(SANE_Word *) v;
+ break;
+ case OPT_RESOLUTION:
+ handler->val[n].w = *(SANE_Word *) v;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ break;
+ case OPT_MODE:
+ if (handler->val[n].s)
+ free (handler->val[n].s);
+ handler->val[n].s = strdup (v);
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ break;
+ default:
+ break;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+}
+
+/**
+ * \fn SANE_Status sane_start(SANE_Handle h)
+ * \brief Function that initiates aquisition of an image from the device represented by handle 'h'.
+ * This function calls the "escl_newjob" function and the "escl_scan" function.
+ *
+ * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+sane_start(SANE_Handle h)
+{
+ DBG (10, "escl sane_start\n");
+ SANE_Status status = SANE_STATUS_GOOD;
+ escl_sane_t *handler = h;
+ int w = 0;
+ int he = 0;
+ int bps = 0;
+
+ if (handler->name == NULL)
+ return (SANE_STATUS_INVAL);
+ handler->cancel = SANE_FALSE;
+ handler->write_scan_data = SANE_FALSE;
+ handler->decompress_scan_data = SANE_FALSE;
+ handler->end_read = SANE_FALSE;
+ handler->scanner->height = handler->val[OPT_BR_Y].w;
+ handler->scanner->width = handler->val[OPT_BR_X].w;
+ handler->scanner->pos_x = handler->val[OPT_TL_X].w;
+ handler->scanner->pos_y = handler->val[OPT_TL_Y].w;
+ if(handler->scanner->default_color)
+ free(handler->scanner->default_color);
+ if (handler->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ int i = 0, val = 9999;;
+ if (handler->val[OPT_GRAY_PREVIEW].w == SANE_TRUE ||
+ !strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3))
+ handler->scanner->default_color = strdup("Grayscale8");
+ else
+ handler->scanner->default_color = strdup("RGB24");
+ for (i = 1; i < handler->scanner->SupportedResolutionsSize; i++)
+ {
+ if (val > handler->scanner->SupportedResolutions[i])
+ val = handler->scanner->SupportedResolutions[i];
+ }
+ handler->scanner->default_resolution = val;
+ }
+ else
+ {
+ handler->scanner->default_resolution = handler->val[OPT_RESOLUTION].w;
+ if (!strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3))
+ handler->scanner->default_color = strdup("Grayscale8");
+ else
+ handler->scanner->default_color = strdup("RGB24");
+ }
+ handler->result = escl_newjob(handler->scanner, handler->name, &status);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ status = escl_scan(handler->scanner, handler->name, handler->result);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ if (!strcmp(handler->scanner->default_format, "image/jpeg"))
+ {
+ status = get_JPEG_data(handler->scanner, &w, &he, &bps);
+ }
+ else if (!strcmp(handler->scanner->default_format, "image/png"))
+ {
+ status = get_PNG_data(handler->scanner, &w, &he, &bps);
+ }
+ else if (!strcmp(handler->scanner->default_format, "image/tiff"))
+ {
+ status = get_TIFF_data(handler->scanner, &w, &he, &bps);
+ }
+ else
+ return SANE_STATUS_INVAL;
+
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ handler->ps.depth = 8;
+ handler->ps.pixels_per_line = w;
+ handler->ps.lines = he;
+ handler->ps.bytes_per_line = w * bps;
+ handler->ps.last_frame = SANE_TRUE;
+ handler->ps.format = SANE_FRAME_RGB;
+ return (status);
+}
+
+/**
+ * \fn SANE_Status sane_get_parameters(SANE_Handle h, SANE_Parameters *p)
+ * \brief Function that retrieves the device parameters represented by 'h' and stores them in 'p'.
+ * This function is normally used after "sane_start".
+ * It's in this function that we choose to assign the default color. (Color or Monochrome)
+ *
+ * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+sane_get_parameters(SANE_Handle h, SANE_Parameters *p)
+{
+ DBG (10, "escl sane_get_parameters\n");
+ SANE_Status status = SANE_STATUS_GOOD;
+ escl_sane_t *handler = h;
+
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ if (p != NULL) {
+ p->depth = 8;
+ p->last_frame = SANE_TRUE;
+ p->format = SANE_FRAME_RGB;
+ p->pixels_per_line = handler->ps.pixels_per_line;
+ p->lines = handler->ps.lines;
+ p->bytes_per_line = handler->ps.bytes_per_line;
+ }
+ return (status);
+}
+
+
+/**
+ * \fn SANE_Status sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
+ * \brief Function that's used to read image data from the device represented by handle 'h'.
+ * The argument 'buf' is a pointer to a memory area that is at least 'maxlen' bytes long.
+ * The number of bytes returned is stored in '*len'.
+ * --> When the call succeeds, the number of bytes returned can be anywhere in the range from 0 to 'maxlen' bytes.
+ *
+ * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
+{
+ DBG (10, "escl sane_read\n");
+ escl_sane_t *handler = h;
+ SANE_Status status = SANE_STATUS_GOOD;
+ long readbyte;
+
+ if (!handler | !buf | !len)
+ return (SANE_STATUS_INVAL);
+ if (handler->cancel)
+ return (SANE_STATUS_CANCELLED);
+ if (!handler->write_scan_data)
+ handler->write_scan_data = SANE_TRUE;
+ if (!handler->decompress_scan_data) {
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ handler->decompress_scan_data = SANE_TRUE;
+ }
+ if (handler->scanner->img_data == NULL)
+ return (SANE_STATUS_INVAL);
+ if (!handler->end_read) {
+ readbyte = min((handler->scanner->img_size - handler->scanner->img_read), maxlen);
+ memcpy(buf, handler->scanner->img_data + handler->scanner->img_read, readbyte);
+ handler->scanner->img_read = handler->scanner->img_read + readbyte;
+ *len = readbyte;
+ if (handler->scanner->img_read == handler->scanner->img_size)
+ handler->end_read = SANE_TRUE;
+ else if (handler->scanner->img_read > handler->scanner->img_size) {
+ *len = 0;
+ handler->end_read = SANE_TRUE;
+ free(handler->scanner->img_data);
+ handler->scanner->img_data = NULL;
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else {
+ *len = 0;
+ free(handler->scanner->img_data);
+ handler->scanner->img_data = NULL;
+ return (SANE_STATUS_EOF);
+ }
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_get_select_fd(SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ *fd)
+{
+ return (SANE_STATUS_UNSUPPORTED);
+}
+
+SANE_Status
+sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking)
+{
+ return (SANE_STATUS_UNSUPPORTED);
+}
diff --git a/backend/escl/escl.h b/backend/escl/escl.h
new file mode 100644
index 0000000..82910bd
--- /dev/null
+++ b/backend/escl/escl.h
@@ -0,0 +1,171 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+
+#ifndef __ESCL_H__
+#define __ESCL_H__
+
+#include "../include/sane/config.h"
+
+#if !(HAVE_LIBCURL && defined(WITH_AVAHI) && defined(HAVE_LIBXML2))
+#error "The escl backend requires libcurl, libavahi and libxml2"
+#endif
+
+#ifndef HAVE_LIBJPEG
+/* FIXME: Make JPEG support optional.
+ Support for PNG and PDF is to be added later but currently only
+ JPEG is supported. Absence of JPEG support makes the backend a
+ no-op at present.
+ */
+#error "The escl backend currently requires libjpeg"
+#endif
+
+#include "../include/sane/sane.h"
+
+#include <stdio.h>
+
+#ifndef BACKEND_NAME
+#define BACKEND_NAME escl
+#endif
+
+#define DEBUG_NOT_STATIC
+#include "../include/sane/sanei_debug.h"
+
+#ifndef DBG_LEVEL
+#define DBG_LEVEL PASTE(sanei_debug_, BACKEND_NAME)
+#endif
+#ifndef NDEBUG
+# define DBGDUMP(level, buf, size) \
+ do { if (DBG_LEVEL >= (level)) sanei_escl_dbgdump(buf, size); } while (0)
+#else
+# define DBGDUMP(level, buf, size)
+#endif
+
+#define ESCL_CONFIG_FILE "escl.conf"
+
+typedef struct {
+ int p1_0;
+ int p2_0;
+ int p3_3;
+ int DocumentType;
+ int p4_0;
+ int p5_0;
+ int p6_1;
+ int reserve[11];
+} ESCL_SCANOPTS;
+
+
+typedef struct ESCL_Device {
+ struct ESCL_Device *next;
+
+ char *model_name;
+ int port_nb;
+ char *ip_address;
+ char *type;
+} ESCL_Device;
+
+typedef struct capabilities
+{
+ int height;
+ int width;
+ int pos_x;
+ int pos_y;
+ SANE_String default_color;
+ SANE_String default_format;
+ SANE_Int default_resolution;
+ int MinWidth;
+ int MaxWidth;
+ int MinHeight;
+ int MaxHeight;
+ int MaxScanRegions;
+ SANE_String_Const *ColorModes;
+ int ColorModesSize;
+ SANE_String_Const *ContentTypes;
+ int ContentTypesSize;
+ SANE_String_Const *DocumentFormats;
+ int DocumentFormatsSize;
+ SANE_Int *SupportedResolutions;
+ int SupportedResolutionsSize;
+ SANE_String_Const *SupportedIntents;
+ int SupportedIntentsSize;
+ SANE_String_Const SupportedIntentDefault;
+ int MaxOpticalXResolution;
+ int RiskyLeftMargin;
+ int RiskyRightMargin;
+ int RiskyTopMargin;
+ int RiskyBottomMargin;
+ FILE *tmp;
+ unsigned char *img_data;
+ long img_size;
+ long img_read;
+ int format_ext;
+} capabilities_t;
+
+typedef struct {
+ int XRes;
+ int YRes;
+ int Left;
+ int Top;
+ int Right;
+ int Bottom;
+ int ScanMode;
+ int ScanMethod;
+ ESCL_SCANOPTS opts;
+} ESCL_ScanParam;
+
+
+enum
+{
+ OPT_NUM_OPTS = 0,
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_RESOLUTION,
+ OPT_PREVIEW,
+ OPT_GRAY_PREVIEW,
+
+ OPT_GEOMETRY_GROUP,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ NUM_OPTIONS
+};
+
+ESCL_Device *escl_devices(SANE_Status *status);
+SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type);
+SANE_Status escl_status(SANE_String_Const name);
+capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status);
+char *escl_newjob(capabilities_t *scanner, SANE_String_Const name, SANE_Status *status);
+SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result);
+void escl_scanner(SANE_String_Const name, char *result);
+
+// JPEG
+SANE_Status get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps);
+
+// PNG
+SANE_Status get_PNG_data(capabilities_t *scanner, int *w, int *h, int *bps);
+
+// TIFF
+SANE_Status get_TIFF_data(capabilities_t *scanner, int *w, int *h, int *bps);
+
+#endif
diff --git a/backend/escl/escl_capabilities.c b/backend/escl/escl_capabilities.c
new file mode 100644
index 0000000..690ff1e
--- /dev/null
+++ b/backend/escl/escl_capabilities.c
@@ -0,0 +1,377 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <libxml/parser.h>
+
+#include "../include/sane/saneopts.h"
+
+struct cap
+{
+ char *memory;
+ size_t size;
+};
+
+/**
+ * \fn static SANE_String_Const convert_elements(SANE_String_Const str)
+ * \brief Function that converts the 'color modes' of the scanner (color/gray) to be understood by SANE.
+ *
+ * \return SANE_VALUE_SCAN_MODE_GRAY / SANE_VALUE_SCAN_MODE_COLOR ; NULL otherwise
+ */
+static SANE_String_Const
+convert_elements(SANE_String_Const str)
+{
+ if (strcmp(str, "Grayscale8") == 0)
+ return (SANE_VALUE_SCAN_MODE_GRAY);
+ else if (strcmp(str, "RGB24") == 0)
+ return (SANE_VALUE_SCAN_MODE_COLOR);
+ return (NULL);
+}
+
+/**
+ * \fn static SANE_String_Const *char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
+ * \brief Function that creates the character arrays to put inside :
+ * the 'color modes', the 'content types', the 'document formats' and the 'supported intents'.
+ *
+ * \return board (the allocated array)
+ */
+static SANE_String_Const *
+char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
+{
+ SANE_String_Const *board = NULL;
+ int i = 0;
+ SANE_String_Const convert = NULL;
+
+ if (mode == NULL)
+ return (tab);
+ if (good_array != 0) {
+ convert = convert_elements(mode);
+ if (convert == NULL)
+ return (tab);
+ }
+ else
+ convert = mode;
+ for (i = 0; i < (*tabsize); i++) {
+ if (strcmp(tab[i], convert) == 0)
+ return (tab);
+ }
+ (*tabsize)++;
+ if (*tabsize == 1)
+ board = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * (*tabsize) + 1);
+ else
+ board = (SANE_String_Const *)realloc(tab, sizeof(SANE_String_Const) * (*tabsize) + 1);
+ board[*tabsize - 1] = (SANE_String_Const)strdup(convert);
+ board[*tabsize] = NULL;
+ return (board);
+}
+
+/**
+ * \fn static SANE_Int *int_to_array(SANE_Int *tab, int *tabsize, int cont)
+ * \brief Function that creates the integer array to put inside the 'supported resolutions'.
+ *
+ * \return board (the allocated array)
+ */
+static SANE_Int *
+int_to_array(SANE_Int *tab, int *tabsize, int cont)
+{
+ SANE_Int *board = NULL;
+ int i = 0;
+
+ for (i = 0; i < (*tabsize); i++) {
+ if (tab[i] == cont)
+ return (tab);
+ }
+ (*tabsize)++;
+ if (*tabsize == 1) {
+ (*tabsize)++;
+ board = malloc(sizeof(SANE_Int *) * (*tabsize) + 1);
+ }
+ else
+ board = realloc(tab, sizeof(SANE_Int *) * (*tabsize) + 1);
+ board[0] = *tabsize - 1;
+ board[*tabsize - 1] = cont;
+ board[*tabsize] = -1;
+ return (board);
+}
+
+/**
+ * \fn static size_t memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
+ * \brief Callback function that stocks in memory the content of the scanner capabilities.
+ *
+ * \return realsize (size of the content needed -> the scanner capabilities)
+ */
+static size_t
+memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ size_t realsize = size * nmemb;
+ struct cap *mem = (struct cap *)userp;
+
+ char *str = realloc(mem->memory, mem->size + realsize + 1);
+ if (str == NULL) {
+ fprintf(stderr, "not enough memory (realloc returned NULL)\n");
+ return (0);
+ }
+ mem->memory = str;
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size = mem->size + realsize;
+ mem->memory[mem->size] = 0;
+ return (realsize);
+}
+
+/**
+ * \fn static int find_nodes_c(xmlNode *node)
+ * \brief Function that browses the xml file and parses it, to find the xml children node.
+ * --> to recover the scanner capabilities.
+ *
+ * \return 0 if a xml child node is found, 1 otherwise
+ */
+static int
+find_nodes_c(xmlNode *node)
+{
+ xmlNode *child = node->children;
+
+ while (child) {
+ if (child->type == XML_ELEMENT_NODE)
+ return (0);
+ child = child->next;
+ }
+ return (1);
+}
+
+/**
+ * \fn static int find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
+ * \brief Function that searchs in the xml file if a scanner capabilitie stocked
+ * in one of the created array (character/integer array) is found.
+ *
+ * \return 0
+ */
+static int
+find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
+{
+ const char *name = (const char *)node->name;
+ if (strcmp(name, "ColorMode") == 0)
+ scanner->ColorModes = char_to_array(scanner->ColorModes, &scanner->ColorModesSize, (SANE_String_Const)xmlNodeGetContent(node), 1);
+ else if (strcmp(name, "ContentType") == 0)
+ scanner->ContentTypes = char_to_array(scanner->ContentTypes, &scanner->ContentTypesSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
+ else if (strcmp(name, "DocumentFormat") == 0)
+ {
+ int i = 0;
+ scanner->DocumentFormats = char_to_array(scanner->DocumentFormats, &scanner->DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
+ for(; i < scanner->DocumentFormatsSize; i++)
+ {
+ if (scanner->default_format == NULL && !strcmp(scanner->DocumentFormats[i], "image/jpeg"))
+ {
+ scanner->default_format = strdup("image/jpeg");
+ }
+#if(defined HAVE_LIBPNG)
+ else if(!strcmp(scanner->DocumentFormats[i], "image/png") && (scanner->default_format == NULL || strcmp(scanner->default_format, "image/tiff")))
+ {
+ if (scanner->default_format)
+ free(scanner->default_format);
+ scanner->default_format = strdup("image/png");
+ }
+#endif
+#if(defined HAVE_TIFFIO_H)
+ else if(!strcmp(scanner->DocumentFormats[i], "image/tiff"))
+ {
+ if (scanner->default_format)
+ free(scanner->default_format);
+ scanner->default_format = strdup("image/tiff");
+ }
+#endif
+ }
+ fprintf(stderr, "Capability : [%s]\n", scanner->default_format);
+ }
+ else if (strcmp(name, "DocumentFormatExt") == 0)
+ scanner->format_ext = 1;
+ else if (strcmp(name, "Intent") == 0)
+ scanner->SupportedIntents = char_to_array(scanner->SupportedIntents, &scanner->SupportedIntentsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
+ else if (strcmp(name, "XResolution") == 0)
+ scanner->SupportedResolutions = int_to_array(scanner->SupportedResolutions, &scanner->SupportedResolutionsSize, atoi((const char *)xmlNodeGetContent(node)));
+ return (0);
+}
+
+/**
+ * \fn static int find_value_of_int_variables(xmlNode *node, capabilities_t *scanner)
+ * \brief Function that searchs in the xml file if a integer scanner capabilitie is found.
+ * The integer scanner capabilities that are interesting are :
+ * MinWidth, MaxWidth, MaxHeight, MinHeight, MaxScanRegions, MaxOpticalXResolution,
+ * RiskyLeftMargin, RiskyRightMargin, RiskyTopMargin, RiskyBottomMargin.
+ *
+ * \return 0
+ */
+static int
+find_value_of_int_variables(xmlNode *node, capabilities_t *scanner)
+{
+ int MaxWidth = 0;
+ int MaxHeight = 0;
+ const char *name = (const char *)node->name;
+
+ if (strcmp(name, "MinWidth") == 0)
+ scanner->MinWidth = atoi((const char*)xmlNodeGetContent(node));
+ else if (strcmp(name, "MaxWidth") == 0) {
+ MaxWidth = atoi((const char*)xmlNodeGetContent(node));
+ if (scanner->MaxWidth == 0 || MaxWidth < scanner->MaxWidth)
+ scanner->MaxWidth = atoi((const char *)xmlNodeGetContent(node));
+ }
+ else if (strcmp(name, "MinHeight") == 0)
+ scanner->MinHeight = atoi((const char*)xmlNodeGetContent(node));
+ else if (strcmp(name, "MaxHeight") == 0) {
+ MaxHeight = atoi((const char*)xmlNodeGetContent(node));
+ if (scanner->MaxHeight == 0 || MaxHeight < scanner->MaxHeight)
+ scanner->MaxHeight = atoi((const char *)xmlNodeGetContent(node));
+ }
+ else if (strcmp(name, "MaxScanRegions") == 0)
+ scanner->MaxScanRegions = atoi((const char *)xmlNodeGetContent(node));
+ else if (strcmp(name, "MaxOpticalXResolution") == 0)
+ scanner->MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node));
+ else if (strcmp(name, "RiskyLeftMargin") == 0)
+ scanner->RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node));
+ else if (strcmp(name, "RiskyRightMargin") == 0)
+ scanner->RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node));
+ else if (strcmp(name, "RiskyTopMargin") == 0)
+ scanner->RiskyTopMargin = atoi((const char *)xmlNodeGetContent(node));
+ else if (strcmp(name, "RiskyBottomMargin") == 0)
+ scanner->RiskyBottomMargin = atoi((const char *)xmlNodeGetContent(node));
+ find_valor_of_array_variables(node, scanner);
+ return (0);
+}
+
+/**
+ * \fn static int find_true_variables(xmlNode *node, capabilities_t *scanner)
+ * \brief Function that searchs in the xml file if we find a scanner capabilitie stocked
+ * in one of the created array (character/integer array),
+ * or, if we find a integer scanner capabilitie.
+ *
+ * \return 0
+ */
+static int
+find_true_variables(xmlNode *node, capabilities_t *scanner)
+{
+ const char *name = (const char *)node->name;
+ if (strcmp(name, "MinWidth") == 0 ||
+ strcmp(name, "MaxWidth") == 0 ||
+ strcmp(name, "MinHeight") == 0 ||
+ strcmp(name, "MaxHeight") == 0 ||
+ strcmp(name, "MaxScanRegions") == 0 ||
+ strcmp(name, "ColorMode") == 0 ||
+ strcmp(name, "ContentType") == 0 ||
+ strcmp(name, "DocumentFormat") == 0 ||
+ strcmp(name, "XResolution") == 0 ||
+ strcmp(name, "Intent") == 0 ||
+ strcmp(name, "MaxOpticalXResolution") == 0 ||
+ strcmp(name, "RiskyLeftMargin") == 0 ||
+ strcmp(name, "RiskyRightMargin") == 0 ||
+ strcmp(name, "RiskyTopMargin") == 0 ||
+ strcmp(name, "RiskyBottomMargin") == 0 ||
+ strcmp(name, "DocumentFormatExt") == 0)
+ find_value_of_int_variables(node, scanner);
+ return (0);
+}
+
+/**
+ * \fn static int print_xml_c(xmlNode *node, capabilities_t *scanner)
+ * \brief Function that browses the xml file, node by node.
+ *
+ * \return 0
+ */
+static int
+print_xml_c(xmlNode *node, capabilities_t *scanner)
+{
+ while (node) {
+ if (node->type == XML_ELEMENT_NODE) {
+ if (find_nodes_c(node))
+ find_true_variables(node, scanner);
+ }
+ print_xml_c(node->children, scanner);
+ node = node->next;
+ }
+ return (0);
+}
+
+/**
+ * \fn capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status)
+ * \brief Function that finally recovers all the capabilities of the scanner, using curl.
+ * This function is called in the 'sane_open' function and it's the equivalent of
+ * the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerCapabilities".
+ *
+ * \return scanner (the structure that stocks all the capabilities elements)
+ */
+capabilities_t *
+escl_capabilities(SANE_String_Const name, SANE_Status *status)
+{
+ capabilities_t *scanner = (capabilities_t*)calloc(1, sizeof(capabilities_t));
+ CURL *curl_handle = NULL;
+ struct cap *var = NULL;
+ xmlDoc *data = NULL;
+ xmlNode *node = NULL;
+ const char *scanner_capabilities = "/eSCL/ScannerCapabilities";
+ char tmp[PATH_MAX] = { 0 };
+
+ *status = SANE_STATUS_GOOD;
+ if (name == NULL)
+ *status = SANE_STATUS_NO_MEM;
+ var = (struct cap *)calloc(1, sizeof(struct cap));
+ if (var == NULL)
+ *status = SANE_STATUS_NO_MEM;
+ var->memory = malloc(1);
+ var->size = 0;
+ curl_handle = curl_easy_init();
+ strcpy(tmp, name);
+ strcat(tmp, scanner_capabilities);
+ DBG( 1, "Get Capabilities : %s\n", tmp);
+ curl_easy_setopt(curl_handle, CURLOPT_URL, tmp);
+ if (strncmp(name, "https", 5) == 0) {
+ DBG( 1, "Ignoring safety certificates, use https\n");
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
+ }
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_c);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
+ if (curl_easy_perform(curl_handle) != CURLE_OK) {
+ DBG( 1, "The scanner didn't respond.\n");
+ *status = SANE_STATUS_INVAL;
+ }
+ data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
+ if (data == NULL)
+ *status = SANE_STATUS_NO_MEM;
+ node = xmlDocGetRootElement(data);
+ if (node == NULL)
+ *status = SANE_STATUS_NO_MEM;
+ print_xml_c(node, scanner);
+ xmlFreeDoc(data);
+ xmlCleanupParser();
+ xmlMemoryDump();
+ curl_easy_cleanup(curl_handle);
+ free(var->memory);
+ return (scanner);
+}
diff --git a/backend/escl/escl_devices.c b/backend/escl/escl_devices.c
new file mode 100644
index 0000000..7ecbe31
--- /dev/null
+++ b/backend/escl/escl_devices.c
@@ -0,0 +1,185 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <avahi-client/lookup.h>
+#include <avahi-common/error.h>
+#include <avahi-common/simple-watch.h>
+
+#include "../include/sane/sanei.h"
+
+static AvahiSimplePoll *simple_poll = NULL;
+
+/**
+ * \fn static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED
+ * AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ * AvahiResolverEvent event, const char *name,
+ * const char *type, const char *domain, const char *host_name,
+ * const AvahiAddress *address, uint16_t port,
+ * AvahiStringList *txt, AvahiLookupResultFlags flags,
+ * void *userdata)
+ * \brief Callback function that will check if the selected scanner follows the escl
+ * protocol or not.
+ */
+static void
+resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event, const char *name,
+ const char __sane_unused__ *type,
+ const char __sane_unused__ *domain,
+ const char __sane_unused__ *host_name,
+ const AvahiAddress *address, uint16_t port, AvahiStringList *txt,
+ AvahiLookupResultFlags __sane_unused__ flags,
+ void __sane_unused__ *userdata)
+{
+ char a[AVAHI_ADDRESS_STR_MAX], *t;
+ assert(r);
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ break;
+ case AVAHI_RESOLVER_FOUND:
+ avahi_address_snprint(a, sizeof(a), address);
+ t = avahi_string_list_to_string(txt);
+ if (strstr(t, "\"rs=eSCL\"") || strstr(t, "\"rs=/eSCL\""))
+ escl_device_add(port, name, a, (char*)type);
+ }
+}
+
+/**
+ * \fn static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
+ * AvahiProtocol protocol, AvahiBrowserEvent event, const char *name,
+ * const char *type, const char *domain,
+ * AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata)
+ * \brief Callback function that will browse tanks to 'avahi' the scanners
+ * connected in network.
+ */
+static void
+browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
+ AvahiProtocol protocol, AvahiBrowserEvent event,
+ const char *name, const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+{
+ AvahiClient *c = userdata;
+ assert(b);
+ switch (event) {
+ case AVAHI_BROWSER_FAILURE:
+ avahi_simple_poll_quit(simple_poll);
+ return;
+ case AVAHI_BROWSER_NEW:
+ if (!(avahi_service_resolver_new(c, interface, protocol, name,
+ type, domain,
+ AVAHI_PROTO_UNSPEC, 0,
+ resolve_callback, c)))
+ break;
+ case AVAHI_BROWSER_REMOVE:
+ break;
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ if (event != AVAHI_BROWSER_CACHE_EXHAUSTED)
+ avahi_simple_poll_quit(simple_poll);
+ break;
+ }
+}
+
+/**
+ * \fn static void client_callback(AvahiClient *c, AvahiClientState state,
+ * AVAHI_GCC_UNUSED void *userdata)
+ * \brief Callback Function that quit if it doesn't find a connected scanner,
+ * possible thanks the "Hello Protocol".
+ * --> Waiting for a answer by the scanner to continue the avahi process.
+ */
+static void
+client_callback(AvahiClient *c, AvahiClientState state,
+ AVAHI_GCC_UNUSED void *userdata)
+{
+ assert(c);
+ if (state == AVAHI_CLIENT_FAILURE)
+ avahi_simple_poll_quit(simple_poll);
+}
+
+/**
+ * \fn ESCL_Device *escl_devices(SANE_Status *status)
+ * \brief Function that calls all the avahi functions and then, recovers the
+ * connected eSCL devices.
+ * This function is called in the 'sane_get_devices' function.
+ *
+ * \return NULL (the eSCL devices found)
+ */
+ESCL_Device *
+escl_devices(SANE_Status *status)
+{
+ AvahiClient *client = NULL;
+ AvahiServiceBrowser *sb = NULL;
+ int error;
+
+ *status = SANE_STATUS_GOOD;
+ if (!(simple_poll = avahi_simple_poll_new())) {
+ DBG( 1, "Failed to create simple poll object.\n");
+ *status = SANE_STATUS_INVAL;
+ goto fail;
+ }
+ client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0,
+ client_callback, NULL, &error);
+ if (!client) {
+ DBG( 1, "Failed to create client: %s\n", avahi_strerror(error));
+ *status = SANE_STATUS_INVAL;
+ goto fail;
+ }
+ if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC, "_uscan._tcp",
+ NULL, 0, browse_callback, client))) {
+ DBG( 1, "Failed to create service browser: %s\n",
+ avahi_strerror(avahi_client_errno(client)));
+ *status = SANE_STATUS_INVAL;
+ goto fail;
+ }
+ if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ "_uscans._tcp", NULL, 0,
+ browse_callback, client))) {
+ DBG( 1, "Failed to create service browser: %s\n",
+ avahi_strerror(avahi_client_errno(client)));
+ *status = SANE_STATUS_INVAL;
+ goto fail;
+ }
+ avahi_simple_poll_loop(simple_poll);
+fail:
+ if (sb)
+ avahi_service_browser_free(sb);
+ if (client)
+ avahi_client_free(client);
+ if (simple_poll)
+ avahi_simple_poll_free(simple_poll);
+ return (NULL);
+}
diff --git a/backend/escl/escl_jpeg.c b/backend/escl/escl_jpeg.c
new file mode 100644
index 0000000..d6287ef
--- /dev/null
+++ b/backend/escl/escl_jpeg.c
@@ -0,0 +1,230 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include "../include/sane/sanei.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if(defined HAVE_LIBJPEG)
+# include <jpeglib.h>
+#endif
+
+#include <setjmp.h>
+
+#define INPUT_BUFFER_SIZE 4096
+
+#if(defined HAVE_LIBJPEG)
+struct my_error_mgr
+{
+ struct jpeg_error_mgr errmgr;
+ jmp_buf escape;
+};
+
+typedef struct
+{
+ struct jpeg_source_mgr pub;
+ FILE *ctx;
+ unsigned char buffer[INPUT_BUFFER_SIZE];
+} my_source_mgr;
+
+/**
+ * \fn static boolean fill_input_buffer(j_decompress_ptr cinfo)
+ * \brief Called in the "skip_input_data" function.
+ *
+ * \return TRUE (everything is OK)
+ */
+static boolean
+fill_input_buffer(j_decompress_ptr cinfo)
+{
+ my_source_mgr *src = (my_source_mgr *) cinfo->src;
+ int nbytes = 0;
+
+ nbytes = fread(src->buffer, 1, INPUT_BUFFER_SIZE, src->ctx);
+ if (nbytes <= 0) {
+ src->buffer[0] = (unsigned char) 0xFF;
+ src->buffer[1] = (unsigned char) JPEG_EOI;
+ nbytes = 2;
+ }
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = nbytes;
+ return (TRUE);
+}
+
+/**
+ * \fn static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+ * \brief Called in the "jpeg_RW_src" function.
+ */
+static void
+skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ my_source_mgr *src = (my_source_mgr *) cinfo->src;
+
+ if (num_bytes > 0) {
+ while (num_bytes > (long) src->pub.bytes_in_buffer) {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ (void) src->pub.fill_input_buffer(cinfo);
+ }
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+static void
+term_source(j_decompress_ptr __sane_unused__ cinfo)
+{
+ return;
+}
+
+static void
+init_source(j_decompress_ptr __sane_unused__ cinfo)
+{
+ return;
+}
+
+/**
+ * \fn static void jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx)
+ * \brief Called in the "escl_sane_decompressor" function.
+ */
+static void
+jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx)
+{
+ my_source_mgr *src;
+
+ if (cinfo->src == NULL) {
+ cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)
+ ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr));
+ src = (my_source_mgr *) cinfo->src;
+ }
+ src = (my_source_mgr *) cinfo->src;
+ src->pub.init_source = init_source;
+ src->pub.fill_input_buffer = fill_input_buffer;
+ src->pub.skip_input_data = skip_input_data;
+ src->pub.resync_to_restart = jpeg_resync_to_restart;
+ src->pub.term_source = term_source;
+ src->ctx = ctx;
+ src->pub.bytes_in_buffer = 0;
+ src->pub.next_input_byte = NULL;
+}
+
+static void
+my_error_exit(j_common_ptr cinfo)
+{
+ struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
+
+ longjmp(err->escape, 1);
+}
+
+static void
+output_no_message(j_common_ptr __sane_unused__ cinfo)
+{
+}
+
+/**
+ * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler)
+ * \brief Function that aims to decompress the jpeg image to SANE be able to read the image.
+ * This function is called in the "sane_read" function.
+ *
+ * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps)
+{
+ int start = 0;
+ struct jpeg_decompress_struct cinfo;
+ JSAMPROW rowptr[1];
+ unsigned char *surface = NULL;
+ struct my_error_mgr jerr;
+ int lineSize = 0;
+
+ if (scanner->tmp == NULL)
+ return (SANE_STATUS_INVAL);
+ fseek(scanner->tmp, SEEK_SET, 0);
+ start = ftell(scanner->tmp);
+ cinfo.err = jpeg_std_error(&jerr.errmgr);
+ jerr.errmgr.error_exit = my_error_exit;
+ jerr.errmgr.output_message = output_no_message;
+ if (setjmp(jerr.escape)) {
+ jpeg_destroy_decompress(&cinfo);
+ if (surface != NULL)
+ free(surface);
+ DBG( 1, "Escl Jpeg : Error reading jpeg\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+ jpeg_create_decompress(&cinfo);
+ jpeg_RW_src(&cinfo, scanner->tmp);
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.out_color_space = JCS_RGB;
+ cinfo.quantize_colors = FALSE;
+ jpeg_calc_output_dimensions(&cinfo);
+ surface = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
+ if (surface == NULL) {
+ jpeg_destroy_decompress(&cinfo);
+ fseek(scanner->tmp, start, SEEK_SET);
+ DBG( 1, "Escl Jpeg : Memory allocation problem\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_NO_MEM);
+ }
+ lineSize = cinfo.output_width * cinfo.output_components;
+ jpeg_start_decompress(&cinfo);
+ while (cinfo.output_scanline < cinfo.output_height) {
+ rowptr[0] = (JSAMPROW)surface + (lineSize * cinfo.output_scanline);
+ jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
+ }
+ scanner->img_data = surface;
+ scanner->img_size = lineSize * cinfo.output_height;
+ scanner->img_read = 0;
+ *w = cinfo.output_width;
+ *h = cinfo.output_height;
+ *bps = cinfo.output_components;
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ return (SANE_STATUS_GOOD);
+}
+#else
+
+SANE_Status
+get_JPEG_data(capabilities_t __sane_unused__ *scanner,
+ int __sane_unused__ *w,
+ int __sane_unused__ *h,
+ int __sane_unused__ *bps)
+{
+ return (SANE_STATUS_INVAL);
+}
+
+#endif
diff --git a/backend/escl/escl_newjob.c b/backend/escl/escl_newjob.c
new file mode 100644
index 0000000..279b9df
--- /dev/null
+++ b/backend/escl/escl_newjob.c
@@ -0,0 +1,241 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+
+#ifdef PATH_MAX
+# undef PATH_MAX
+#endif
+
+#define PATH_MAX 4096
+
+struct uploading
+{
+ const char *read_data;
+ size_t size;
+};
+
+struct downloading
+{
+ char *memory;
+ size_t size;
+};
+
+static const char settings[] =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
+ "<scan:ScanSettings xmlns:pwg=\"http://www.pwg.org/schemas/2010/12/sm\" xmlns:scan=\"http://schemas.hp.com/imaging/escl/2011/05/03\">" \
+ " <pwg:Version>2.0</pwg:Version>" \
+ " <pwg:ScanRegions>" \
+ " <pwg:ScanRegion>" \
+ " <pwg:ContentRegionUnits>escl:ThreeHundredthsOfInches</pwg:ContentRegionUnits>" \
+ " <pwg:Height>%d</pwg:Height>" \
+ " <pwg:Width>%d</pwg:Width>" \
+ " <pwg:XOffset>%d</pwg:XOffset>" \
+ " <pwg:YOffset>%d</pwg:YOffset>" \
+ " </pwg:ScanRegion>" \
+ " </pwg:ScanRegions>" \
+ " <pwg:DocumentFormat>%s</pwg:DocumentFormat>" \
+ "%s" \
+ " <scan:ColorMode>%s</scan:ColorMode>" \
+ " <scan:XResolution>%d</scan:XResolution>" \
+ " <scan:YResolution>%d</scan:YResolution>" \
+ " <pwg:InputSource>Platen</pwg:InputSource>" \
+ "</scan:ScanSettings>";
+
+static char formatExtJPEG[] =
+ " <scan:DocumentFormatExt>image/jpeg</scan:DocumentFormatExt>";
+
+static char formatExtPNG[] =
+ " <scan:DocumentFormatExt>image/png</scan:DocumentFormatExt>";
+
+static char formatExtTIFF[] =
+ " <scan:DocumentFormatExt>image/tiff</scan:DocumentFormatExt>";
+
+/**
+ * \fn static size_t download_callback(void *str, size_t size, size_t nmemb, void *userp)
+ * \brief Callback function that stocks in memory the content of the 'job'. Example below :
+ * "Trying 192.168.14.150...
+ * TCP_NODELAY set
+ * Connected to 192.168.14.150 (192.168.14.150) port 80
+ * POST /eSCL/ScanJobs HTTP/1.1
+ * Host: 192.168.14.150
+ * User-Agent: curl/7.55.1
+ * Accept: /
+ * Content-Length: 605
+ * Content-Type: application/x-www-form-urlencoded
+ * upload completely sent off: 605 out of 605 bytes
+ * < HTTP/1.1 201 Created
+ * < MIME-Version: 1.0
+ * < Location: http://192.168.14.150/eSCL/ScanJobs/22b54fd0-027b-1000-9bd0-f4a99726e2fa
+ * < Content-Length: 0
+ * < Connection: close
+ * <
+ * Closing connection 0"
+ *
+ * \return realsize (size of the content needed -> the 'job')
+ */
+static size_t
+download_callback(void *str, size_t size, size_t nmemb, void *userp)
+{
+ struct downloading *download = (struct downloading *)userp;
+ size_t realsize = size * nmemb;
+ char *content = realloc(download->memory, download->size + realsize + 1);
+
+ if (content == NULL) {
+ DBG( 1, "Not enough memory (realloc returned NULL)\n");
+ return (0);
+ }
+ download->memory = content;
+ memcpy(&(download->memory[download->size]), str, realsize);
+ download->size = download->size + realsize;
+ download->memory[download->size] = 0;
+ return (realsize);
+}
+
+/**
+ * \fn char *escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status)
+ * \brief Function that, using curl, uploads the data (composed by the scanner capabilities) to the
+ * server to download the 'job' and recover the 'new job' (char *result), in LOCATION.
+ * This function is called in the 'sane_start' function and it's the equivalent of the
+ * following curl command : "curl -v POST -d cap.xml http(s)://'ip':'port'/eSCL/ScanJobs".
+ *
+ * \return result (the 'new job', situated in LOCATION)
+ */
+char *
+escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status)
+{
+ CURL *curl_handle = NULL;
+ struct uploading *upload = NULL;
+ struct downloading *download = NULL;
+ const char *scan_jobs = "/eSCL/ScanJobs";
+ char cap_data[PATH_MAX] = { 0 };
+ char job_cmd[PATH_MAX] = { 0 };
+ char *location = NULL;
+ char *result = NULL;
+ char *temporary = NULL;
+ char *f_ext = "";
+ char *format_ext = NULL;
+
+ *status = SANE_STATUS_GOOD;
+ if (name == NULL || scanner == NULL) {
+ *status = SANE_STATUS_NO_MEM;
+ DBG( 1, "Create NewJob : the name or the scan are invalid.\n");
+ return (NULL);
+ }
+ upload = (struct uploading *)calloc(1, sizeof(struct uploading));
+ if (upload == NULL) {
+ *status = SANE_STATUS_NO_MEM;
+ DBG( 1, "Create NewJob : memory allocation failure\n");
+ return (NULL);
+ }
+ download = (struct downloading *)calloc(1, sizeof(struct downloading));
+ if (download == NULL) {
+ free(upload);
+ DBG( 1, "Create NewJob : memory allocation failure\n");
+ *status = SANE_STATUS_NO_MEM;
+ return (NULL);
+ }
+ curl_handle = curl_easy_init();
+ if (scanner->format_ext == 1)
+ {
+ if (!strcmp(scanner->default_format, "image/jpeg"))
+ format_ext = formatExtJPEG;
+ else if (!strcmp(scanner->default_format, "image/png"))
+ format_ext = formatExtPNG;
+ else if (!strcmp(scanner->default_format, "image/tiff"))
+ format_ext = formatExtTIFF;
+ else
+ format_ext = f_ext;
+ }
+ else
+ format_ext = f_ext;
+ DBG( 1, "Create NewJob : %s\n", scanner->default_format);
+ if (curl_handle != NULL) {
+ snprintf(cap_data, sizeof(cap_data), settings, scanner->height, scanner->width, 0, 0, scanner->default_format,
+ format_ext,
+ scanner->default_color, scanner->default_resolution, scanner->default_resolution);
+ DBG( 1, "Create NewJob : %s\n", cap_data);
+ upload->read_data = strdup(cap_data);
+ upload->size = strlen(cap_data);
+ download->memory = malloc(1);
+ download->size = 0;
+ strcpy(job_cmd, name);
+ strcat(job_cmd, scan_jobs);
+ curl_easy_setopt(curl_handle, CURLOPT_URL, job_cmd);
+ if (strncmp(name, "https", 5) == 0) {
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
+ }
+ curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
+ curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, upload->read_data);
+ curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, upload->size);
+ curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, download_callback);
+ curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)download);
+ if (curl_easy_perform(curl_handle) != CURLE_OK) {
+ DBG( 1, "Create NewJob : the scanner responded incorrectly.\n");
+ *status = SANE_STATUS_INVAL;
+ }
+ else {
+ if (download->memory != NULL) {
+ if (strstr(download->memory, "Location:")) {
+ temporary = strrchr(download->memory, '/');
+ if (temporary != NULL) {
+ location = strchr(temporary, '\r');
+ if (location == NULL)
+ location = strchr(temporary, '\n');
+ else {
+ *location = '\0';
+ result = strdup(temporary);
+ }
+ DBG( 1, "Create NewJob : %s\n", result);
+ }
+ free(download->memory);
+ }
+ else {
+ DBG( 1, "Create NewJob : The creation of the failed job\n");
+ *status = SANE_STATUS_INVAL;
+ }
+ }
+ else {
+ *status = SANE_STATUS_NO_MEM;
+ DBG( 1, "Create NewJob : The creation of the failed job\n");
+ return (NULL);
+ }
+ }
+ curl_easy_cleanup(curl_handle);
+ }
+ if (upload != NULL)
+ free(upload);
+ if (download != NULL)
+ free(download);
+ return (result);
+}
diff --git a/backend/escl/escl_png.c b/backend/escl/escl_png.c
new file mode 100644
index 0000000..18f6f35
--- /dev/null
+++ b/backend/escl/escl_png.c
@@ -0,0 +1,193 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include "../include/sane/sanei.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if(defined HAVE_LIBPNG)
+#include <png.h>
+#endif
+
+#include <setjmp.h>
+
+
+#if(defined HAVE_LIBPNG)
+
+/**
+ * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler)
+ * \brief Function that aims to decompress the png image to SANE be able to read the image.
+ * This function is called in the "sane_read" function.
+ *
+ * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components)
+{
+ unsigned int width = 0; /* largeur */
+ unsigned int height = 0; /* hauteur */
+ int bps = 3; /* composantes d'un texel */
+ unsigned char *texels = NULL; /* données de l'image */
+ unsigned int i = 0;
+ png_byte magic[8];
+
+ // read magic number
+ fread (magic, 1, sizeof (magic), scanner->tmp);
+ // check for valid magic number
+ if (!png_check_sig (magic, sizeof (magic)))
+ {
+ DBG( 1, "Escl Png : PNG error is not a valid PNG image!\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+ // create a png read struct
+ png_structp png_ptr = png_create_read_struct
+ (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ DBG( 1, "Escl Png : PNG error create a png read struct\n");
+ if (scanner->tmp)
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+ // create a png info struct
+ png_infop info_ptr = png_create_info_struct (png_ptr);
+ if (!info_ptr)
+ {
+ DBG( 1, "Escl Png : PNG error create a png info struct\n");
+ png_destroy_read_struct (&png_ptr, NULL, NULL);
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+ // initialize the setjmp for returning properly after a libpng
+ // error occured
+ if (setjmp (png_jmpbuf (png_ptr)))
+ {
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+ if (texels)
+ free (texels);
+ fprintf(stderr,"PNG read error.\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ DBG( 1, "Escl Png : PNG read error.\n");
+ return (SANE_STATUS_INVAL);
+ }
+ // setup libpng for using standard C fread() function
+ // with our FILE pointer
+ png_init_io (png_ptr, scanner->tmp);
+ // tell libpng that we have already read the magic number
+ png_set_sig_bytes (png_ptr, sizeof (magic));
+
+ // read png info
+ png_read_info (png_ptr, info_ptr);
+
+ int bit_depth, color_type;
+ // get some usefull information from header
+ bit_depth = png_get_bit_depth (png_ptr, info_ptr);
+ color_type = png_get_color_type (png_ptr, info_ptr);
+ // convert index color images to RGB images
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb (png_ptr);
+ else if (color_type != PNG_COLOR_TYPE_RGB && color_type != PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ fprintf(stderr,"PNG format not supported.\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ bps = 4;
+ else
+ bps = 3;
+ if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha (png_ptr);
+ if (bit_depth == 16)
+ png_set_strip_16 (png_ptr);
+ else if (bit_depth < 8)
+ png_set_packing (png_ptr);
+ // update info structure to apply transformations
+ png_read_update_info (png_ptr, info_ptr);
+ // retrieve updated information
+ png_get_IHDR (png_ptr, info_ptr,
+ (png_uint_32*)(&width),
+ (png_uint_32*)(&height),
+ &bit_depth, &color_type,
+ NULL, NULL, NULL);
+
+ *w = (int)width;
+ *h = (int)height;
+ *components = bps;
+ // we can now allocate memory for storing pixel data
+ texels = (unsigned char *)malloc (sizeof (unsigned char) * width
+ * height * bps);
+ png_bytep *row_pointers;
+ // setup a pointer array. Each one points at the begening of a row.
+ row_pointers = (png_bytep *)malloc (sizeof (png_bytep) * height);
+ for (i = 0; i < height; ++i)
+ {
+ row_pointers[i] = (png_bytep)(texels +
+ ((height - (i + 1)) * width * bps));
+ }
+ // read pixel data using row pointers
+ png_read_image (png_ptr, row_pointers);
+ // we don't need row pointers anymore
+ scanner->img_data = texels;
+ scanner->img_size = (int)(width * height * bps);
+ scanner->img_read = 0;
+ free (row_pointers);
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ return (SANE_STATUS_GOOD);
+}
+#else
+
+SANE_Status
+get_PNG_data(capabilities_t __sane_unused__ *scanner,
+ int __sane_unused__ *w,
+ int __sane_unused__ *h,
+ int __sane_unused__ *bps)
+{
+ return (SANE_STATUS_INVAL);
+}
+
+#endif
diff --git a/backend/escl/escl_reset.c b/backend/escl/escl_reset.c
new file mode 100644
index 0000000..7722d89
--- /dev/null
+++ b/backend/escl/escl_reset.c
@@ -0,0 +1,75 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+
+/**
+ * \fn void escl_scanner(SANE_String_Const name, char *result)
+ * \brief Function that resets the scanner after each scan, using curl.
+ * This function is called in the 'sane_cancel' function.
+ */
+void
+escl_scanner(SANE_String_Const name, char *result)
+{
+ CURL *curl_handle = NULL;
+ const char *scan_jobs = "/eSCL/ScanJobs";
+ const char *scanner_start = "/NextDocument";
+ char scan_cmd[PATH_MAX] = { 0 };
+ int i = 0;
+ long answer = 0;
+
+ if (name == NULL || result == NULL)
+ return;
+CURL_CALL:
+ curl_handle = curl_easy_init();
+ if (curl_handle != NULL) {
+ strcpy(scan_cmd, name);
+ strcat(scan_cmd, scan_jobs);
+ strcat(scan_cmd, result);
+ strcat(scan_cmd, scanner_start);
+ curl_easy_setopt(curl_handle, CURLOPT_URL, scan_cmd);
+ DBG( 1, "Reset Job : %s.\n", scan_cmd);
+ if (strncmp(name, "https", 5) == 0) {
+ DBG( 1, "Ignoring safety certificates, use https\n");
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
+ }
+ if (curl_easy_perform(curl_handle) == CURLE_OK) {
+ curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &answer);
+ if (i < 3 && answer == 503) {
+ curl_easy_cleanup(curl_handle);
+ i++;
+ goto CURL_CALL;
+ }
+ }
+ curl_easy_cleanup(curl_handle);
+ }
+}
diff --git a/backend/escl/escl_scan.c b/backend/escl/escl_scan.c
new file mode 100644
index 0000000..8f077a1
--- /dev/null
+++ b/backend/escl/escl_scan.c
@@ -0,0 +1,99 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+
+#include "../include/sane/sanei.h"
+
+/**
+ * \fn static size_t write_callback(void *str, size_t size, size_t nmemb, void *userp)
+ * \brief Callback function that writes the image scanned into the temporary file.
+ *
+ * \return to_write (the result of the fwrite fonction)
+ */
+static size_t
+write_callback(void *str, size_t size, size_t nmemb, void *userp)
+{
+ size_t to_write = fwrite(str, size, nmemb, (FILE *)userp);
+
+ return (to_write);
+}
+
+/**
+ * \fn SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result)
+ * \brief Function that, after recovering the 'new job', scans the image writed in the
+ * temporary file, using curl.
+ * This function is called in the 'sane_start' function and it's the equivalent of
+ * the following curl command : "curl -s http(s)://'ip:'port'/eSCL/ScanJobs/'new job'/NextDocument > image.jpg".
+ *
+ * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+escl_scan(capabilities_t __sane_unused__ *scanner, SANE_String_Const name, char *result)
+{
+ CURL *curl_handle = NULL;
+ const char *scan_jobs = "/eSCL/ScanJobs";
+ const char *scanner_start = "/NextDocument";
+ char scan_cmd[PATH_MAX] = { 0 };
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (name == NULL)
+ return (SANE_STATUS_NO_MEM);
+ curl_handle = curl_easy_init();
+ if (curl_handle != NULL) {
+ strcpy(scan_cmd, name);
+ strcat(scan_cmd, scan_jobs);
+ strcat(scan_cmd, result);
+ strcat(scan_cmd, scanner_start);
+ curl_easy_setopt(curl_handle, CURLOPT_URL, scan_cmd);
+ DBG( 1, "Scan : %s.\n", scan_cmd);
+ if (strncmp(name, "https", 5) == 0) {
+ DBG( 1, "Ignoring safety certificates, use https\n");
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
+ }
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback);
+ scanner->tmp = tmpfile();
+ if (scanner->tmp != NULL) {
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner->tmp);
+ if (curl_easy_perform(curl_handle) != CURLE_OK) {
+ status = SANE_STATUS_INVAL;
+ }
+ else
+ curl_easy_cleanup(curl_handle);
+ fseek(scanner->tmp, 0, SEEK_SET);
+ }
+ else
+ status = SANE_STATUS_NO_MEM;
+ }
+ return (status);
+}
diff --git a/backend/escl/escl_status.c b/backend/escl/escl_status.c
new file mode 100644
index 0000000..68b51dc
--- /dev/null
+++ b/backend/escl/escl_status.c
@@ -0,0 +1,176 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <libxml/parser.h>
+
+struct idle
+{
+ char *memory;
+ size_t size;
+};
+
+/**
+ * \fn static size_t memory_callback_s(void *contents, size_t size, size_t nmemb, void *userp)
+ * \brief Callback function that stocks in memory the content of the scanner status.
+ *
+ * \return realsize (size of the content needed -> the scanner status)
+ */
+static size_t
+memory_callback_s(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ size_t realsize = size * nmemb;
+ struct idle *mem = (struct idle *)userp;
+
+ char *str = realloc(mem->memory, mem->size + realsize + 1);
+ if (str == NULL) {
+ DBG(1, "not enough memory (realloc returned NULL)\n");
+ return (0);
+ }
+ mem->memory = str;
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size = mem->size + realsize;
+ mem->memory[mem->size] = 0;
+ return (realsize);
+}
+
+/**
+ * \fn static int find_nodes_s(xmlNode *node)
+ * \brief Function that browses the xml file and parses it, to find the xml children node.
+ * --> to recover the scanner status.
+ *
+ * \return 0 if a xml child node is found, 1 otherwise
+ */
+static int
+find_nodes_s(xmlNode *node)
+{
+ xmlNode *child = node->children;
+
+ while (child) {
+ if (child->type == XML_ELEMENT_NODE)
+ return (0);
+ child = child->next;
+ }
+ return (1);
+}
+
+/**
+ * \fn static void print_xml_s(xmlNode *node, SANE_Status *status)
+ * \brief Function that browses the xml file, node by node.
+ * If the node 'State' is found, we are expecting to found in this node the 'Idle'
+ * content (if the scanner is ready to use) and then 'status' = SANE_STATUS_GOOD.
+ * Otherwise, this means that the scanner isn't ready to use.
+ */
+static void
+print_xml_s(xmlNode *node, SANE_Status *status)
+{
+ int x = 0;
+
+ while (node) {
+ if (node->type == XML_ELEMENT_NODE) {
+ if (find_nodes_s(node)) {
+ if (strcmp((const char *)node->name, "State") == 0)
+ x = 1;
+ }
+ if (x == 1 && strcmp((const char *)xmlNodeGetContent(node), "Idle") == 0)
+ *status = SANE_STATUS_GOOD;
+ }
+ print_xml_s(node->children, status);
+ node = node->next;
+ }
+}
+
+/**
+ * \fn SANE_Status escl_status(SANE_String_Const name)
+ * \brief Function that finally recovers the scanner status ('Idle', or not), using curl.
+ * This function is called in the 'sane_open' function and it's the equivalent of
+ * the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerStatus".
+ *
+ * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+escl_status(SANE_String_Const name)
+{
+ SANE_Status status;
+ CURL *curl_handle = NULL;
+ struct idle *var = NULL;
+ xmlDoc *data = NULL;
+ xmlNode *node = NULL;
+ const char *scanner_status = "/eSCL/ScannerStatus";
+ char tmp[PATH_MAX] = { 0 };
+
+ if (name == NULL)
+ return (SANE_STATUS_NO_MEM);
+ var = (struct idle*)calloc(1, sizeof(struct idle));
+ if (var == NULL)
+ return (SANE_STATUS_NO_MEM);
+ var->memory = malloc(1);
+ var->size = 0;
+ curl_handle = curl_easy_init();
+ strcpy(tmp, name);
+ strcat(tmp, scanner_status);
+ curl_easy_setopt(curl_handle, CURLOPT_URL, tmp);
+ DBG( 1, "Get Status : %s.\n", tmp);
+ if (strncmp(name, "https", 5) == 0) {
+ DBG( 1, "Ignoring safety certificates, use https\n");
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
+ }
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_s);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
+ if (curl_easy_perform(curl_handle) != CURLE_OK) {
+ DBG( 1, "The scanner didn't respond.\n");
+ status = SANE_STATUS_INVAL;
+ goto clean_data;
+ }
+ data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
+ if (data == NULL) {
+ status = SANE_STATUS_NO_MEM;
+ goto clean_data;
+ }
+ node = xmlDocGetRootElement(data);
+ if (node == NULL) {
+ status = SANE_STATUS_NO_MEM;
+ goto clean;
+ }
+ status = SANE_STATUS_DEVICE_BUSY;
+ print_xml_s(node, &status);
+clean:
+ xmlFreeDoc(data);
+clean_data:
+ xmlCleanupParser();
+ xmlMemoryDump();
+ curl_easy_cleanup(curl_handle);
+ free(var->memory);
+ free(var);
+ return (status);
+}
diff --git a/backend/escl/escl_tiff.c b/backend/escl/escl_tiff.c
new file mode 100644
index 0000000..52aec20
--- /dev/null
+++ b/backend/escl/escl_tiff.c
@@ -0,0 +1,119 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Touboul Nathane
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include "../include/sane/sanei.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if(defined HAVE_TIFFIO_H)
+#include <tiffio.h>
+#endif
+
+#include <setjmp.h>
+
+
+#if(defined HAVE_TIFFIO_H)
+
+/**
+ * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler)
+ * \brief Function that aims to decompress the png image to SANE be able to read the image.
+ * This function is called in the "sane_read" function.
+ *
+ * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+get_TIFF_data(capabilities_t *scanner, int *w, int *h, int *components)
+{
+ TIFF* tif = NULL;
+ uint32 width = 0; /* largeur */
+ uint32 height = 0; /* hauteur */
+ unsigned char *raster = NULL; /* données de l'image */
+ int bps = 4;
+ uint32 npixels = 0;
+
+ lseek(fileno(scanner->tmp), 0, SEEK_SET);
+ tif = TIFFFdOpen(fileno(scanner->tmp), "temp", "r");
+ if (!tif) {
+ DBG( 1, "Escl Tiff : Can not open, or not a TIFF file.\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+
+ TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
+ TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
+ npixels = width * height;
+ raster = (unsigned char*) malloc(npixels * sizeof (uint32));
+ if (raster != NULL)
+ {
+ DBG( 1, "Escl Tiff : Memory allocation problem.\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+
+ if (!TIFFReadRGBAImage(tif, width, height, (uint32 *)raster, 0))
+ {
+ DBG( 1, "Escl Tiff : Problem reading image data.\n");
+ if (scanner->tmp) {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ }
+ return (SANE_STATUS_INVAL);
+ }
+ *w = (int)width;
+ *h = (int)height;
+ *components = bps;
+ // we don't need row pointers anymore
+ scanner->img_data = raster;
+ scanner->img_size = (int)(width * height * bps);
+ scanner->img_read = 0;
+ TIFFClose(tif);
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ return (SANE_STATUS_GOOD);
+}
+#else
+
+SANE_Status
+get_TIFF_data(capabilities_t __sane_unused__ *scanner,
+ int __sane_unused__ *w,
+ int __sane_unused__ *h,
+ int __sane_unused__ *bps)
+{
+ return (SANE_STATUS_INVAL);
+}
+
+#endif
diff --git a/backend/fujitsu.c b/backend/fujitsu.c
index 3ac4c8e..5dc466c 100644
--- a/backend/fujitsu.c
+++ b/backend/fujitsu.c
@@ -6,7 +6,7 @@
Copyright (C) 2000 Randolph Bentson
Copyright (C) 2001 Frederik Ramm
Copyright (C) 2001-2004 Oliver Schirrmeister
- Copyright (C) 2003-2016 m. allan noah
+ Copyright (C) 2003-2019 m. allan noah
JPEG output and low memory usage support funded by:
Archivista GmbH, www.archivista.ch
@@ -603,6 +603,8 @@
v134 2019-02-23, MAN
- rewrite init_vpd for scanners which fail to report
overscan correctly
+ v135 2019-11-10, MAN
+ - set has_MS_lamp=0 for fi-72x0, bug #134
SANE FLOW DIAGRAM
@@ -2404,6 +2406,8 @@ init_model (struct fujitsu *s)
else if (strstr (s->model_name,"fi-7280")
|| strstr (s->model_name,"fi-7260")){
+ /* locks up scanner if we try to auto detect */
+ s->has_MS_lamp = 0;
/* weirdness */
/* these machines have longer max paper at lower res */
@@ -4377,7 +4381,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
if(option==OPT_TOP){
opt->name = "top-edge";
opt->title = SANE_I18N ("Top edge");
- opt->desc = SANE_I18N ("Paper is pulled partly into adf");
+ opt->desc = SANE_I18N ("Paper is pulled partly into ADF");
opt->type = SANE_TYPE_BOOL;
opt->unit = SANE_UNIT_NONE;
if (s->has_cmd_hw_status || s->ghs_in_rs)
@@ -6935,7 +6939,7 @@ sane_start (SANE_Handle handle)
else{
ret = scanner_control(s, SC_function_adf);
if (ret != SANE_STATUS_GOOD) {
- DBG (5, "sane_start: ERROR: cannot control adf, ignoring\n");
+ DBG (5, "sane_start: ERROR: cannot control ADF, ignoring\n");
}
}
diff --git a/backend/fujitsu.conf.in b/backend/fujitsu.conf.in
index cb33f15..4f2b1a9 100644
--- a/backend/fujitsu.conf.in
+++ b/backend/fujitsu.conf.in
@@ -252,3 +252,6 @@ usb 0x04c5 0x1521
#fi-7700S
usb 0x04c5 0x1522
+
+#ScanSnap iX1500
+usb 0x04c5 0x159f
diff --git a/backend/genesys.conf.in b/backend/genesys.conf.in
index b1a0861..786ccd5 100644
--- a/backend/genesys.conf.in
+++ b/backend/genesys.conf.in
@@ -11,7 +11,7 @@
# Hewlett Packard ScanJet 2400c
usb 0x03f0 0x0a01
-# Hewlett Packard ScanJet 3670c/3690c
+# Hewlett Packard ScanJet 3670/3690c
usb 0x03f0 0x1405
# Plustek OpticPro ST24
@@ -51,9 +51,6 @@ usb 0x04a9 0x1909
# Canon LiDE 200
usb 0x04a9 0x1905
-# Canon 5600F
-usb 0x04a9 0x1906
-
# Canon LiDE 700F
usb 0x04a9 0x1907
@@ -66,9 +63,12 @@ usb 0x04a9 0x190e
# Canon LiDE 220
usb 0x04a9 0x190f
-# Canon 5600f
+# Canon 5600F
usb 0x04a9 0x1906
+# Canon 8400F
+usb 0x04a9 0x221e
+
# Canon 8600F
usb 0x04a9 0x2229
@@ -124,6 +124,15 @@ usb 0x03f0 0x4605
# Plustek OpticBook 3600
usb 0x07b3 0x0900
+# Plustek OpticFilm 7200i
+usb 0x07b3 0x0c04
+
+# Plustek OpticFilm 7300
+usb 0x07b3 0x0c12
+
+# Plustek OpticFilm 7500i
+usb 0x07b3 0x0c13
+
# Primax Electronics, Ltd Xerox 2400 Onetouch
usb 0x0461 0x038b
diff --git a/backend/genesys/buffer.cpp b/backend/genesys/buffer.cpp
new file mode 100644
index 0000000..f17e361
--- /dev/null
+++ b/backend/genesys/buffer.cpp
@@ -0,0 +1,102 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "buffer.h"
+#include <cstring>
+#include <stdexcept>
+
+namespace genesys {
+
+void Genesys_Buffer::alloc(std::size_t size)
+{
+ buffer_.resize(size);
+ avail_ = 0;
+ pos_ = 0;
+}
+
+void Genesys_Buffer::clear()
+{
+ buffer_.clear();
+ avail_ = 0;
+ pos_ = 0;
+}
+
+void Genesys_Buffer::reset()
+{
+ avail_ = 0;
+ pos_ = 0;
+}
+
+std::uint8_t* Genesys_Buffer::get_write_pos(std::size_t size)
+{
+ if (avail_ + size > buffer_.size())
+ return nullptr;
+ if (pos_ + avail_ + size > buffer_.size())
+ {
+ std::memmove(buffer_.data(), buffer_.data() + pos_, avail_);
+ pos_ = 0;
+ }
+ return buffer_.data() + pos_ + avail_;
+}
+
+std::uint8_t* Genesys_Buffer::get_read_pos()
+{
+ return buffer_.data() + pos_;
+}
+
+void Genesys_Buffer::produce(std::size_t size)
+{
+ if (size > buffer_.size() - avail_)
+ throw std::runtime_error("buffer size exceeded");
+ avail_ += size;
+}
+
+void Genesys_Buffer::consume(std::size_t size)
+{
+ if (size > avail_)
+ throw std::runtime_error("no more data in buffer");
+ avail_ -= size;
+ pos_ += size;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/buffer.h b/backend/genesys/buffer.h
new file mode 100644
index 0000000..e9c889b
--- /dev/null
+++ b/backend/genesys/buffer.h
@@ -0,0 +1,89 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_BUFFER_H
+#define BACKEND_GENESYS_BUFFER_H
+
+#include <vector>
+#include <cstddef>
+#include <cstdint>
+
+namespace genesys {
+
+/* A FIFO buffer. Note, that this is _not_ a ringbuffer.
+ if we need a block which does not fit at the end of our available data,
+ we move the available data to the beginning.
+*/
+struct Genesys_Buffer
+{
+ Genesys_Buffer() = default;
+
+ std::size_t size() const { return buffer_.size(); }
+ std::size_t avail() const { return avail_; }
+ std::size_t pos() const { return pos_; }
+
+ // TODO: refactor code that uses this function to no longer use it
+ void set_pos(std::size_t pos) { pos_ = pos; }
+
+ void alloc(std::size_t size);
+ void clear();
+
+ void reset();
+
+ std::uint8_t* get_write_pos(std::size_t size);
+ std::uint8_t* get_read_pos(); // TODO: mark as const
+
+ void produce(std::size_t size);
+ void consume(std::size_t size);
+
+private:
+ std::vector<std::uint8_t> buffer_;
+ // current position in read buffer
+ std::size_t pos_ = 0;
+ // data bytes currently in buffer
+ std::size_t avail_ = 0;
+};
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_BUFFER_H
diff --git a/backend/genesys/calibration.h b/backend/genesys/calibration.h
new file mode 100644
index 0000000..f14aaa3
--- /dev/null
+++ b/backend/genesys/calibration.h
@@ -0,0 +1,108 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_CALIBRATION_H
+#define BACKEND_GENESYS_CALIBRATION_H
+
+#include "sensor.h"
+#include "settings.h"
+#include <ctime>
+
+namespace genesys {
+
+struct Genesys_Calibration_Cache
+{
+ Genesys_Calibration_Cache() = default;
+ ~Genesys_Calibration_Cache() = default;
+
+ // used to check if entry is compatible
+ SetupParams params;
+
+ std::time_t last_calibration = 0;
+
+ Genesys_Frontend frontend;
+ Genesys_Sensor sensor;
+
+ size_t calib_pixels = 0;
+ size_t calib_channels = 0;
+ size_t average_size = 0;
+ std::vector<std::uint16_t> white_average_data;
+ std::vector<std::uint16_t> dark_average_data;
+
+ bool operator==(const Genesys_Calibration_Cache& other) const
+ {
+ return params == other.params &&
+ last_calibration == other.last_calibration &&
+ frontend == other.frontend &&
+ sensor == other.sensor &&
+ calib_pixels == other.calib_pixels &&
+ calib_channels == other.calib_channels &&
+ average_size == other.average_size &&
+ white_average_data == other.white_average_data &&
+ dark_average_data == other.dark_average_data;
+ }
+};
+
+template<class Stream>
+void serialize(Stream& str, Genesys_Calibration_Cache& x)
+{
+ serialize(str, x.params);
+ serialize_newline(str);
+ serialize(str, x.last_calibration);
+ serialize_newline(str);
+ serialize(str, x.frontend);
+ serialize_newline(str);
+ serialize(str, x.sensor);
+ serialize_newline(str);
+ serialize(str, x.calib_pixels);
+ serialize(str, x.calib_channels);
+ serialize(str, x.average_size);
+ serialize_newline(str);
+ serialize(str, x.white_average_data);
+ serialize_newline(str);
+ serialize(str, x.dark_average_data);
+}
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_CALIBRATION_H
diff --git a/backend/genesys/command_set.h b/backend/genesys/command_set.h
new file mode 100644
index 0000000..ab3a4b6
--- /dev/null
+++ b/backend/genesys/command_set.h
@@ -0,0 +1,166 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_COMMAND_SET_H
+#define BACKEND_GENESYS_COMMAND_SET_H
+
+#include "device.h"
+#include "fwd.h"
+#include <cstdint>
+
+namespace genesys {
+
+
+/** Scanner command set description.
+
+ This description contains parts which are common to all scanners with the
+ same command set, but may have different optical resolution and other
+ parameters.
+ */
+class CommandSet
+{
+public:
+ virtual ~CommandSet() = default;
+
+ virtual bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const = 0;
+
+ virtual void init(Genesys_Device* dev) const = 0;
+
+ virtual void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const = 0;
+
+ virtual void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const = 0;
+ virtual void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const = 0;
+ virtual void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const = 0;
+
+ /** Set up registers for a scan. Similar to init_regs_for_scan except that the session is
+ already computed from the session
+ */
+ virtual void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const= 0;
+
+ virtual void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const = 0;
+ virtual void set_powersaving(Genesys_Device* dev, int delay) const = 0;
+ virtual void save_power(Genesys_Device* dev, bool enable) const = 0;
+
+ virtual void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, bool start_motor) const = 0;
+ virtual void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs,
+ bool check_stop) const = 0;
+
+
+ /**
+ * Send gamma tables to ASIC
+ */
+ virtual void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const = 0;
+
+ virtual void search_start_position(Genesys_Device* dev) const = 0;
+ virtual void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const = 0;
+ virtual void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const = 0;
+ virtual SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const = 0;
+
+ virtual void wait_for_motor_stop(Genesys_Device* dev) const = 0;
+ virtual void move_back_home(Genesys_Device* dev, bool wait_until_home) const = 0;
+
+ // Updates hardware sensor information in Genesys_Scanner.val[].
+ virtual void update_hardware_sensors(struct Genesys_Scanner* s) const = 0;
+
+ /** Whether the scanner needs to call update_home_sensor_gpio before reading the status of the
+ home sensor. On some chipsets this is unreliable until update_home_sensor_gpio() is called.
+ */
+ virtual bool needs_update_home_sensor_gpio() const { return false; }
+
+ /** Needed on some chipsets before reading the status of the home sensor to make this operation
+ reliable.
+ */
+ virtual void update_home_sensor_gpio(Genesys_Device& dev) const { (void) dev; }
+
+ // functions for sheetfed scanners
+
+ // load document into scanner
+ virtual void load_document(Genesys_Device* dev) const = 0;
+
+ /** Detects is the scanned document has left scanner. In this case it updates the amount of
+ data to read and set up flags in the dev struct
+ */
+ virtual void detect_document_end(Genesys_Device* dev) const = 0;
+
+ /// eject document from scanner
+ virtual void eject_document(Genesys_Device* dev) const = 0;
+ /**
+ * search for an black or white area in forward or reverse
+ * direction */
+ virtual void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const = 0;
+
+ /// move scanning head to transparency adapter
+ virtual void move_to_ta(Genesys_Device* dev) const = 0;
+
+ /// write shading data calibration to ASIC
+ virtual void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ std::uint8_t* data, int size) const = 0;
+
+ virtual bool has_send_shading_data() const
+ {
+ return true;
+ }
+
+ /// calculate an instance of ScanSession for scanning with the given settings
+ virtual ScanSession calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const = 0;
+
+ /// cold boot init function
+ virtual void asic_boot(Genesys_Device* dev, bool cold) const = 0;
+};
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_COMMAND_SET_H
diff --git a/backend/genesys/conv.cpp b/backend/genesys/conv.cpp
new file mode 100644
index 0000000..a87c463
--- /dev/null
+++ b/backend/genesys/conv.cpp
@@ -0,0 +1,238 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2005, 2006 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "conv.h"
+#include "sane/sanei_magic.h"
+
+namespace genesys {
+
+/**
+ * uses the threshold/threshold_curve to control software binarization
+ * This code was taken from the epjistsu backend by m. allan noah
+ * @param dev device set up for the scan
+ * @param src pointer to raw data
+ * @param dst pointer where to store result
+ * @param width width of the processed line
+ * */
+void binarize_line(Genesys_Device* dev, std::uint8_t* src, std::uint8_t* dst, int width)
+{
+ DBG_HELPER(dbg);
+ int j, windowX, sum = 0;
+ int thresh;
+ int offset, addCol, dropCol;
+ unsigned char mask;
+
+ int x;
+ std::uint8_t min, max;
+
+ /* normalize line */
+ min = 255;
+ max = 0;
+ for (x = 0; x < width; x++)
+ {
+ if (src[x] > max)
+ {
+ max = src[x];
+ }
+ if (src[x] < min)
+ {
+ min = src[x];
+ }
+ }
+
+ /* safeguard against dark or white areas */
+ if(min>80)
+ min=0;
+ if(max<80)
+ max=255;
+ for (x = 0; x < width; x++)
+ {
+ src[x] = ((src[x] - min) * 255) / (max - min);
+ }
+
+ /* ~1mm works best, but the window needs to have odd # of pixels */
+ windowX = (6 * dev->settings.xres) / 150;
+ if (!(windowX % 2))
+ windowX++;
+
+ /* second, prefill the sliding sum */
+ for (j = 0; j < windowX; j++)
+ sum += src[j];
+
+ /* third, walk the input buffer, update the sliding sum, */
+ /* determine threshold, output bits */
+ for (j = 0; j < width; j++)
+ {
+ /* output image location */
+ offset = j % 8;
+ mask = 0x80 >> offset;
+ thresh = dev->settings.threshold;
+
+ /* move sum/update threshold only if there is a curve */
+ if (dev->settings.threshold_curve)
+ {
+ addCol = j + windowX / 2;
+ dropCol = addCol - windowX;
+
+ if (dropCol >= 0 && addCol < width)
+ {
+ sum -= src[dropCol];
+ sum += src[addCol];
+ }
+ thresh = dev->lineart_lut[sum / windowX];
+ }
+
+ /* use average to lookup threshold */
+ if (src[j] > thresh)
+ *dst &= ~mask; /* white */
+ else
+ *dst |= mask; /* black */
+
+ if (offset == 7)
+ dst++;
+ }
+}
+
+/**
+ * software lineart using data from a 8 bit gray scan. We assume true gray
+ * or monochrome scan as input.
+ */
+void genesys_gray_lineart(Genesys_Device* dev,
+ std::uint8_t* src_data, std::uint8_t* dst_data,
+ std::size_t pixels, std::size_t lines, std::uint8_t threshold)
+{
+ DBG_HELPER(dbg);
+ std::size_t y;
+
+ DBG(DBG_io2, "%s: converting %zu lines of %zu pixels\n", __func__, lines, pixels);
+ DBG(DBG_io2, "%s: threshold=%d\n", __func__, threshold);
+
+ for (y = 0; y < lines; y++)
+ {
+ binarize_line (dev, src_data + y * pixels, dst_data, pixels);
+ dst_data += pixels / 8;
+ }
+}
+
+/** Look in image for likely left/right/bottom paper edges, then crop image.
+ */
+void genesys_crop(Genesys_Scanner* s)
+{
+ DBG_HELPER(dbg);
+ Genesys_Device *dev = s->dev;
+ int top = 0;
+ int bottom = 0;
+ int left = 0;
+ int right = 0;
+
+ // first find edges if any
+ TIE(sanei_magic_findEdges(&s->params, dev->img_buffer.data(),
+ dev->settings.xres, dev->settings.yres,
+ &top, &bottom, &left, &right));
+
+ DBG (DBG_io, "%s: t:%d b:%d l:%d r:%d\n", __func__, top, bottom, left,
+ right);
+
+ // now crop the image
+ TIE(sanei_magic_crop (&(s->params), dev->img_buffer.data(), top, bottom, left, right));
+
+ /* update counters to new image size */
+ dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines;
+}
+
+/** Look in image for likely upper and left paper edges, then rotate
+ * image so that upper left corner of paper is upper left of image.
+ */
+void genesys_deskew(Genesys_Scanner *s, const Genesys_Sensor& sensor)
+{
+ DBG_HELPER(dbg);
+ Genesys_Device *dev = s->dev;
+
+ int x = 0, y = 0, bg;
+ double slope = 0;
+
+ bg=0;
+ if(s->params.format==SANE_FRAME_GRAY && s->params.depth == 1)
+ {
+ bg=0xff;
+ }
+ TIE(sanei_magic_findSkew(&s->params, dev->img_buffer.data(),
+ sensor.optical_res, sensor.optical_res,
+ &x, &y, &slope));
+
+ DBG(DBG_info, "%s: slope=%f => %f\n", __func__, slope, slope * 180 / M_PI);
+
+ // rotate image slope is in [-PI/2,PI/2]. Positive values rotate trigonometric direction wise
+ TIE(sanei_magic_rotate(&s->params, dev->img_buffer.data(),
+ x, y, slope, bg));
+}
+
+/** remove lone dots
+ */
+void genesys_despeck(Genesys_Scanner* s)
+{
+ DBG_HELPER(dbg);
+ TIE(sanei_magic_despeck(&s->params, s->dev->img_buffer.data(), s->despeck));
+}
+
+/** Look if image needs rotation and apply it
+ * */
+void genesys_derotate(Genesys_Scanner* s)
+{
+ DBG_HELPER(dbg);
+ int angle = 0;
+
+ TIE(sanei_magic_findTurn(&s->params, s->dev->img_buffer.data(),
+ s->resolution, s->resolution, &angle));
+
+ // apply rotation angle found
+ TIE(sanei_magic_turn(&s->params, s->dev->img_buffer.data(), angle));
+
+ // update counters to new image size
+ s->dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/conv.h b/backend/genesys/conv.h
new file mode 100644
index 0000000..446a80d
--- /dev/null
+++ b/backend/genesys/conv.h
@@ -0,0 +1,69 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_CONV_H
+#define BACKEND_GENESYS_CONV_H
+
+#include "device.h"
+#include "sensor.h"
+#include "genesys.h"
+
+namespace genesys {
+
+void binarize_line(Genesys_Device* dev, std::uint8_t* src, std::uint8_t* dst, int width);
+
+void genesys_gray_lineart(Genesys_Device* dev,
+ std::uint8_t* src_data, std::uint8_t* dst_data,
+ std::size_t pixels, size_t lines, std::uint8_t threshold);
+
+void genesys_crop(Genesys_Scanner* s);
+
+void genesys_deskew(Genesys_Scanner *s, const Genesys_Sensor& sensor);
+
+void genesys_despeck(Genesys_Scanner* s);
+
+void genesys_derotate(Genesys_Scanner* s);
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_CONV_H
diff --git a/backend/genesys/device.cpp b/backend/genesys/device.cpp
new file mode 100644
index 0000000..ba035fd
--- /dev/null
+++ b/backend/genesys/device.cpp
@@ -0,0 +1,272 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "device.h"
+#include "command_set.h"
+#include "low.h"
+#include "utilities.h"
+
+namespace genesys {
+
+std::vector<unsigned> MethodResolutions::get_resolutions() const
+{
+ std::vector<unsigned> ret;
+ std::copy(resolutions_x.begin(), resolutions_x.end(), std::back_inserter(ret));
+ std::copy(resolutions_y.begin(), resolutions_y.end(), std::back_inserter(ret));
+ // sort in decreasing order
+
+ std::sort(ret.begin(), ret.end(), std::greater<unsigned>());
+ ret.erase(std::unique(ret.begin(), ret.end()), ret.end());
+ return ret;
+}
+
+const MethodResolutions& Genesys_Model::get_resolution_settings(ScanMethod method) const
+{
+ for (const auto& res_for_method : resolutions) {
+ for (auto res_method : res_for_method.methods) {
+ if (res_method == method) {
+ return res_for_method;
+ }
+ }
+ }
+ throw SaneException("Could not find resolution settings for method %d",
+ static_cast<unsigned>(method));
+}
+
+std::vector<unsigned> Genesys_Model::get_resolutions(ScanMethod method) const
+{
+ return get_resolution_settings(method).get_resolutions();
+}
+
+Genesys_Device::~Genesys_Device()
+{
+ clear();
+}
+
+void Genesys_Device::clear()
+{
+ read_buffer.clear();
+ binarize_buffer.clear();
+ local_buffer.clear();
+
+ calib_file.clear();
+
+ calibration_cache.clear();
+
+ white_average_data.clear();
+ dark_average_data.clear();
+}
+
+ImagePipelineNodeBytesSource& Genesys_Device::get_pipeline_source()
+{
+ return static_cast<ImagePipelineNodeBytesSource&>(pipeline.front());
+}
+
+bool Genesys_Device::is_head_pos_known(ScanHeadId scan_head) const
+{
+ switch (scan_head) {
+ case ScanHeadId::PRIMARY: return is_head_pos_primary_known_;
+ case ScanHeadId::SECONDARY: return is_head_pos_secondary_known_;
+ case ScanHeadId::ALL: return is_head_pos_primary_known_ && is_head_pos_secondary_known_;
+ default:
+ throw SaneException("Unknown scan head ID");
+ }
+}
+unsigned Genesys_Device::head_pos(ScanHeadId scan_head) const
+{
+ switch (scan_head) {
+ case ScanHeadId::PRIMARY: return head_pos_primary_;
+ case ScanHeadId::SECONDARY: return head_pos_secondary_;
+ default:
+ throw SaneException("Unknown scan head ID");
+ }
+}
+
+void Genesys_Device::set_head_pos_unknown()
+{
+ is_head_pos_primary_known_ = false;
+ is_head_pos_secondary_known_ = false;
+}
+
+void Genesys_Device::set_head_pos_zero(ScanHeadId scan_head)
+{
+ if ((scan_head & ScanHeadId::PRIMARY) != ScanHeadId::NONE) {
+ head_pos_primary_ = 0;
+ is_head_pos_primary_known_ = true;
+ }
+ if ((scan_head & ScanHeadId::SECONDARY) != ScanHeadId::NONE) {
+ head_pos_secondary_ = 0;
+ is_head_pos_secondary_known_ = true;
+ }
+}
+
+void Genesys_Device::advance_head_pos_by_session(ScanHeadId scan_head)
+{
+ int motor_steps = session.params.starty +
+ (session.params.lines * motor.base_ydpi) / session.params.yres;
+ auto direction = has_flag(session.params.flags, ScanFlag::REVERSE) ? Direction::BACKWARD
+ : Direction::FORWARD;
+ advance_head_pos_by_steps(scan_head, direction, motor_steps);
+}
+
+static void advance_pos(unsigned& pos, Direction direction, unsigned offset)
+{
+ if (direction == Direction::FORWARD) {
+ pos += offset;
+ } else {
+ if (pos < offset) {
+ throw SaneException("Trying to advance head behind the home sensor");
+ }
+ pos -= offset;
+ }
+}
+
+void Genesys_Device::advance_head_pos_by_steps(ScanHeadId scan_head, Direction direction,
+ unsigned steps)
+{
+ if ((scan_head & ScanHeadId::PRIMARY) != ScanHeadId::NONE) {
+ if (!is_head_pos_primary_known_) {
+ throw SaneException("Trying to advance head while scanhead position is not known");
+ }
+ advance_pos(head_pos_primary_, direction, steps);
+ }
+ if ((scan_head & ScanHeadId::SECONDARY) != ScanHeadId::NONE) {
+ if (!is_head_pos_secondary_known_) {
+ throw SaneException("Trying to advance head while scanhead position is not known");
+ }
+ advance_pos(head_pos_secondary_, direction, steps);
+ }
+}
+
+void print_scan_position(std::ostream& out, const Genesys_Device& dev, ScanHeadId scan_head)
+{
+ if (dev.is_head_pos_known(scan_head)) {
+ out << dev.head_pos(scan_head);
+ } else {
+ out <<"(unknown)";
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)
+{
+ StreamStateSaver state_saver{out};
+
+ out << "Genesys_Device{\n"
+ << std::hex
+ << " vendorId: 0x" << dev.vendorId << '\n'
+ << " productId: 0x" << dev.productId << '\n'
+ << std::dec
+ << " usb_mode: " << dev.usb_mode << '\n'
+ << " file_name: " << dev.file_name << '\n'
+ << " calib_file: " << dev.calib_file << '\n'
+ << " force_calibration: " << dev.force_calibration << '\n'
+ << " ignore_offsets: " << dev.ignore_offsets << '\n'
+ << " model: (not printed)\n"
+ << " reg: " << format_indent_braced_list(4, dev.reg) << '\n'
+ << " calib_reg: " << format_indent_braced_list(4, dev.calib_reg) << '\n'
+ << " settings: " << format_indent_braced_list(4, dev.settings) << '\n'
+ << " frontend: " << format_indent_braced_list(4, dev.frontend) << '\n'
+ << " frontend_initial: " << format_indent_braced_list(4, dev.frontend_initial) << '\n'
+ << " frontend_is_init: " << dev.frontend_is_init << '\n'
+ << " gpo.regs: " << format_indent_braced_list(4, dev.gpo.regs) << '\n'
+ << " motor: " << format_indent_braced_list(4, dev.motor) << '\n'
+ << " control[0..6]: " << std::hex
+ << static_cast<unsigned>(dev.control[0]) << ' '
+ << static_cast<unsigned>(dev.control[1]) << ' '
+ << static_cast<unsigned>(dev.control[2]) << ' '
+ << static_cast<unsigned>(dev.control[3]) << ' '
+ << static_cast<unsigned>(dev.control[4]) << ' '
+ << static_cast<unsigned>(dev.control[5]) << '\n' << std::dec
+ << " average_size: " << dev.average_size << '\n'
+ << " calib_pixels: " << dev.calib_pixels << '\n'
+ << " calib_lines: " << dev.calib_lines << '\n'
+ << " calib_channels: " << dev.calib_channels << '\n'
+ << " calib_resolution: " << dev.calib_resolution << '\n'
+ << " calib_total_bytes_to_read: " << dev.calib_total_bytes_to_read << '\n'
+ << " calib_session: " << format_indent_braced_list(4, dev.calib_session) << '\n'
+ << " calib_pixels_offset: " << dev.calib_pixels_offset << '\n'
+ << " gamma_override_tables[0].size(): " << dev.gamma_override_tables[0].size() << '\n'
+ << " gamma_override_tables[1].size(): " << dev.gamma_override_tables[1].size() << '\n'
+ << " gamma_override_tables[2].size(): " << dev.gamma_override_tables[2].size() << '\n'
+ << " white_average_data.size(): " << dev.white_average_data.size() << '\n'
+ << " dark_average_data.size(): " << dev.dark_average_data.size() << '\n'
+ << " already_initialized: " << dev.already_initialized << '\n'
+ << " scanhead_position[PRIMARY]: ";
+ print_scan_position(out, dev, ScanHeadId::PRIMARY);
+ out << '\n'
+ << " scanhead_position[SECONDARY]: ";
+ print_scan_position(out, dev, ScanHeadId::SECONDARY);
+ out << '\n'
+ << " read_active: " << dev.read_active << '\n'
+ << " parking: " << dev.parking << '\n'
+ << " document: " << dev.document << '\n'
+ << " read_buffer.size(): " << dev.read_buffer.size() << '\n'
+ << " binarize_buffer.size(): " << dev.binarize_buffer.size() << '\n'
+ << " local_buffer.size(): " << dev.local_buffer.size() << '\n'
+ << " oe_buffer.size(): " << dev.oe_buffer.size() << '\n'
+ << " total_bytes_read: " << dev.total_bytes_read << '\n'
+ << " total_bytes_to_read: " << dev.total_bytes_to_read << '\n'
+ << " session: " << format_indent_braced_list(4, dev.session) << '\n'
+ << " lineart_lut: (not printed)\n"
+ << " calibration_cache: (not printed)\n"
+ << " line_count: " << dev.line_count << '\n'
+ << " segment_order: "
+ << format_indent_braced_list(4, format_vector_unsigned(4, dev.segment_order)) << '\n'
+ << " buffer_image: " << dev.buffer_image << '\n'
+ << " img_buffer.size(): " << dev.img_buffer.size() << '\n'
+ << '}';
+ return out;
+}
+
+void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs)
+{
+ for (const auto& reg : regs) {
+ uint8_t val = dev.interface->read_register(reg.address);
+ val = (val & ~reg.mask) | (reg.value & reg.mask);
+ dev.interface->write_register(reg.address, val);
+ }
+}
+
+} // namespace genesys
diff --git a/backend/genesys/device.h b/backend/genesys/device.h
new file mode 100644
index 0000000..6c744c9
--- /dev/null
+++ b/backend/genesys/device.h
@@ -0,0 +1,387 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_DEVICE_H
+#define BACKEND_GENESYS_DEVICE_H
+
+#include "calibration.h"
+#include "command_set.h"
+#include "buffer.h"
+#include "enums.h"
+#include "image_pipeline.h"
+#include "motor.h"
+#include "settings.h"
+#include "sensor.h"
+#include "register.h"
+#include "usb_device.h"
+#include "scanner_interface.h"
+#include <vector>
+
+namespace genesys {
+
+struct Genesys_Gpo
+{
+ Genesys_Gpo() = default;
+
+ // Genesys_Gpo
+ GpioId id = GpioId::UNKNOWN;
+
+ /* GL646 and possibly others:
+ - have the value registers at 0x66 and 0x67
+ - have the enable registers at 0x68 and 0x69
+
+ GL841, GL842, GL843, GL846, GL848 and possibly others:
+ - have the value registers at 0x6c and 0x6d.
+ - have the enable registers at 0x6e and 0x6f.
+ */
+ GenesysRegisterSettingSet regs;
+};
+
+/// Stores a SANE_Fixed value which is automatically converted from and to floating-point values
+class FixedFloat
+{
+public:
+ FixedFloat() = default;
+ FixedFloat(const FixedFloat&) = default;
+ FixedFloat(double number) : value_{SANE_FIX(number)} {}
+ FixedFloat& operator=(const FixedFloat&) = default;
+ FixedFloat& operator=(double number) { value_ = SANE_FIX(number); return *this; }
+
+ operator double() const { return value(); }
+
+ double value() const { return SANE_UNFIX(value_); }
+
+private:
+ SANE_Fixed value_ = 0;
+};
+
+struct MethodResolutions
+{
+ std::vector<ScanMethod> methods;
+ std::vector<unsigned> resolutions_x;
+ std::vector<unsigned> resolutions_y;
+
+ unsigned get_min_resolution_x() const
+ {
+ return *std::min_element(resolutions_x.begin(), resolutions_x.end());
+ }
+
+ unsigned get_min_resolution_y() const
+ {
+ return *std::min_element(resolutions_y.begin(), resolutions_y.end());
+ }
+
+ std::vector<unsigned> get_resolutions() const;
+};
+
+/** @brief structure to describe a scanner model
+ * This structure describes a model. It is composed of information on the
+ * sensor, the motor, scanner geometry and flags to drive operation.
+ */
+struct Genesys_Model
+{
+ Genesys_Model() = default;
+
+ const char* name = nullptr;
+ const char* vendor = nullptr;
+ const char* model = nullptr;
+ ModelId model_id = ModelId::UNKNOWN;
+
+ AsicType asic_type = AsicType::UNKNOWN;
+
+ // possible x and y resolutions for each method supported by the scanner
+ std::vector<MethodResolutions> resolutions;
+
+ // possible depths in gray mode
+ std::vector<unsigned> bpp_gray_values;
+ // possible depths in color mode
+ std::vector<unsigned> bpp_color_values;
+
+ // the default scanning method. This is used when moving the head for example
+ ScanMethod default_method = ScanMethod::FLATBED;
+
+ // All offsets below are with respect to the sensor home position
+
+ // Start of scan area in mm
+ FixedFloat x_offset = 0;
+
+ // Start of scan area in mm (Amount of feeding needed to get to the medium)
+ FixedFloat y_offset = 0;
+
+ // Size of scan area in mm
+ FixedFloat x_size = 0;
+
+ // Size of scan area in mm
+ FixedFloat y_size = 0;
+
+ // Start of white strip in mm
+ FixedFloat y_offset_calib_white = 0;
+
+ // Start of black mark in mm
+ FixedFloat x_offset_calib_black = 0;
+
+ // Start of scan area in transparency mode in mm
+ FixedFloat x_offset_ta = 0;
+
+ // Start of scan area in transparency mode in mm
+ FixedFloat y_offset_ta = 0;
+
+ // Size of scan area in transparency mode in mm
+ FixedFloat x_size_ta = 0;
+
+ // Size of scan area in transparency mode in mm
+ FixedFloat y_size_ta = 0;
+
+ // The position of the sensor when it's aligned with the lamp for transparency scanning
+ FixedFloat y_offset_sensor_to_ta = 0;
+
+ // Start of white strip in transparency mode in mm
+ FixedFloat y_offset_calib_white_ta = 0;
+
+ // Start of black strip in transparency mode in mm
+ FixedFloat y_offset_calib_black_ta = 0;
+
+ // Size of scan area after paper sensor stop sensing document in mm
+ FixedFloat post_scan = 0;
+
+ // Amount of feeding needed to eject document after finishing scanning in mm
+ FixedFloat eject_feed = 0;
+
+ // Line-distance correction (in pixel at optical_ydpi) for CCD scanners
+ SANE_Int ld_shift_r = 0;
+ SANE_Int ld_shift_g = 0;
+ SANE_Int ld_shift_b = 0;
+
+ // Order of the CCD/CIS colors
+ ColorOrder line_mode_color_order = ColorOrder::RGB;
+
+ // Is this a CIS or CCD scanner?
+ bool is_cis = false;
+
+ // Is this sheetfed scanner?
+ bool is_sheetfed = false;
+
+ // sensor type
+ SensorId sensor_id = SensorId::UNKNOWN;
+ // Analog-Digital converter type
+ AdcId adc_id = AdcId::UNKNOWN;
+ // General purpose output type
+ GpioId gpio_id = GpioId::UNKNOWN;
+ // stepper motor type
+ MotorId motor_id = MotorId::UNKNOWN;
+
+ // Which hacks are needed for this scanner?
+ SANE_Word flags = 0;
+
+ // Button flags, described existing buttons for the model
+ SANE_Word buttons = 0;
+
+ // how many lines are used for shading calibration
+ SANE_Int shading_lines = 0;
+ // how many lines are used for shading calibration in TA mode
+ SANE_Int shading_ta_lines = 0;
+ // how many lines are used to search start position
+ SANE_Int search_lines = 0;
+
+ const MethodResolutions& get_resolution_settings(ScanMethod method) const;
+
+ std::vector<unsigned> get_resolutions(ScanMethod method) const;
+};
+
+/**
+ * Describes the current device status for the backend
+ * session. This should be more accurately called
+ * Genesys_Session .
+ */
+struct Genesys_Device
+{
+ Genesys_Device() = default;
+ ~Genesys_Device();
+
+ using Calibration = std::vector<Genesys_Calibration_Cache>;
+
+ // frees commonly used data
+ void clear();
+
+ SANE_Word vendorId = 0; /**< USB vendor identifier */
+ SANE_Word productId = 0; /**< USB product identifier */
+
+ // USB mode:
+ // 0: not set
+ // 1: USB 1.1
+ // 2: USB 2.0
+ SANE_Int usb_mode = 0;
+
+ std::string file_name;
+ std::string calib_file;
+
+ // if enabled, no calibration data will be loaded or saved to files
+ SANE_Int force_calibration = 0;
+ // if enabled, will ignore the scan offsets and start scanning at true origin. This allows
+ // acquiring the positions of the black and white strips and the actual scan area
+ bool ignore_offsets = false;
+
+ Genesys_Model *model = nullptr;
+
+ // pointers to low level functions
+ std::unique_ptr<CommandSet> cmd_set;
+
+ Genesys_Register_Set reg;
+ Genesys_Register_Set calib_reg;
+ Genesys_Settings settings;
+ Genesys_Frontend frontend, frontend_initial;
+
+ // whether the frontend is initialized. This is currently used just to preserve historical
+ // behavior
+ bool frontend_is_init = false;
+
+ Genesys_Gpo gpo;
+ Genesys_Motor motor;
+ std::uint8_t control[6] = {};
+
+ size_t average_size = 0;
+ // number of pixels used during shading calibration
+ size_t calib_pixels = 0;
+ // number of lines used during shading calibration
+ size_t calib_lines = 0;
+ size_t calib_channels = 0;
+ size_t calib_resolution = 0;
+ // bytes to read from USB when calibrating. If 0, this is not set
+ size_t calib_total_bytes_to_read = 0;
+
+ // the session that was configured for calibration
+ ScanSession calib_session;
+
+ // certain scanners support much higher resolution when scanning transparency, but we can't
+ // read whole width of the scanner as a single line at that resolution. Thus for stuff like
+ // calibration we want to read only the possible calibration area.
+ size_t calib_pixels_offset = 0;
+
+ // gamma overrides. If a respective array is not empty then it means that the gamma for that
+ // color is overridden.
+ std::vector<std::uint16_t> gamma_override_tables[3];
+
+ std::vector<std::uint16_t> white_average_data;
+ std::vector<std::uint16_t> dark_average_data;
+
+ bool already_initialized = false;
+
+ bool read_active = false;
+ // signal wether the park command has been issued
+ bool parking = false;
+
+ // for sheetfed scanner's, is TRUE when there is a document in the scanner
+ bool document = false;
+
+ Genesys_Buffer read_buffer;
+
+ // buffer for digital lineart from gray data
+ Genesys_Buffer binarize_buffer;
+ // local buffer for gray data during dynamix lineart
+ Genesys_Buffer local_buffer;
+
+ // total bytes read sent to frontend
+ size_t total_bytes_read = 0;
+ // total bytes read to be sent to frontend
+ size_t total_bytes_to_read = 0;
+
+ // contains computed data for the current setup
+ ScanSession session;
+
+ // look up table used in dynamic rasterization
+ unsigned char lineart_lut[256] = {};
+
+ Calibration calibration_cache;
+
+ // number of scan lines used during scan
+ int line_count = 0;
+
+ // array describing the order of the sub-segments of the sensor
+ std::vector<unsigned> segment_order;
+
+ // buffer to handle even/odd data
+ Genesys_Buffer oe_buffer = {};
+
+ // stores information about how the input image should be processed
+ ImagePipelineStack pipeline;
+
+ // an buffer that allows reading from `pipeline` in chunks of any size
+ ImageBuffer pipeline_buffer;
+
+ // when true the scanned picture is first buffered to allow software image enhancements
+ bool buffer_image = false;
+
+ // image buffer where the scanned picture is stored
+ std::vector<std::uint8_t> img_buffer;
+
+ ImagePipelineNodeBytesSource& get_pipeline_source();
+
+ std::unique_ptr<ScannerInterface> interface;
+
+ bool is_head_pos_known(ScanHeadId scan_head) const;
+ unsigned head_pos(ScanHeadId scan_head) const;
+ void set_head_pos_unknown();
+ void set_head_pos_zero(ScanHeadId scan_head);
+ void advance_head_pos_by_session(ScanHeadId scan_head);
+ void advance_head_pos_by_steps(ScanHeadId scan_head, Direction direction, unsigned steps);
+
+private:
+ // the position of the primary scan head in motor->base_dpi units
+ unsigned head_pos_primary_ = 0;
+ bool is_head_pos_primary_known_ = true;
+
+ // the position of the secondary scan head in motor->base_dpi units. Only certain scanners
+ // have a secondary scan head.
+ unsigned head_pos_secondary_ = 0;
+ bool is_head_pos_secondary_known_ = true;
+
+ friend class ScannerInterfaceUsb;
+};
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev);
+
+void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs);
+
+} // namespace genesys
+
+#endif
diff --git a/backend/genesys/enums.cpp b/backend/genesys/enums.cpp
new file mode 100644
index 0000000..f515cfd
--- /dev/null
+++ b/backend/genesys/enums.cpp
@@ -0,0 +1,131 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "enums.h"
+#include "genesys.h"
+#include <iomanip>
+
+namespace genesys {
+
+const char* scan_method_to_option_string(ScanMethod method)
+{
+ switch (method) {
+ case ScanMethod::FLATBED: return STR_FLATBED;
+ case ScanMethod::TRANSPARENCY: return STR_TRANSPARENCY_ADAPTER;
+ case ScanMethod::TRANSPARENCY_INFRARED: return STR_TRANSPARENCY_ADAPTER_INFRARED;
+ }
+ throw SaneException("Unknown scan method %d", static_cast<unsigned>(method));
+}
+
+ScanMethod option_string_to_scan_method(const std::string& str)
+{
+ if (str == STR_FLATBED) {
+ return ScanMethod::FLATBED;
+ } else if (str == STR_TRANSPARENCY_ADAPTER) {
+ return ScanMethod::TRANSPARENCY;
+ } else if (str == STR_TRANSPARENCY_ADAPTER_INFRARED) {
+ return ScanMethod::TRANSPARENCY_INFRARED;
+ }
+ throw SaneException("Unknown scan method option %s", str.c_str());
+}
+
+const char* scan_color_mode_to_option_string(ScanColorMode mode)
+{
+ switch (mode) {
+ case ScanColorMode::COLOR_SINGLE_PASS: return SANE_VALUE_SCAN_MODE_COLOR;
+ case ScanColorMode::GRAY: return SANE_VALUE_SCAN_MODE_GRAY;
+ case ScanColorMode::HALFTONE: return SANE_VALUE_SCAN_MODE_HALFTONE;
+ case ScanColorMode::LINEART: return SANE_VALUE_SCAN_MODE_LINEART;
+ }
+ throw SaneException("Unknown scan mode %d", static_cast<unsigned>(mode));
+}
+
+ScanColorMode option_string_to_scan_color_mode(const std::string& str)
+{
+ if (str == SANE_VALUE_SCAN_MODE_COLOR) {
+ return ScanColorMode::COLOR_SINGLE_PASS;
+ } else if (str == SANE_VALUE_SCAN_MODE_GRAY) {
+ return ScanColorMode::GRAY;
+ } else if (str == SANE_VALUE_SCAN_MODE_HALFTONE) {
+ return ScanColorMode::HALFTONE;
+ } else if (str == SANE_VALUE_SCAN_MODE_LINEART) {
+ return ScanColorMode::LINEART;
+ }
+ throw SaneException("Unknown scan color mode %s", str.c_str());
+}
+
+
+std::ostream& operator<<(std::ostream& out, ColorFilter mode)
+{
+ switch (mode) {
+ case ColorFilter::RED: out << "RED"; break;
+ case ColorFilter::GREEN: out << "GREEN"; break;
+ case ColorFilter::BLUE: out << "BLUE"; break;
+ case ColorFilter::NONE: out << "NONE"; break;
+ default: out << static_cast<unsigned>(mode); break;
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, StepType type)
+{
+ switch (type) {
+ case StepType::FULL: out << "1/1"; break;
+ case StepType::HALF: out << "1/2"; break;
+ case StepType::QUARTER: out << "1/4"; break;
+ case StepType::EIGHTH: out << "1/8"; break;
+ default: out << static_cast<unsigned>(type); break;
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, ScanFlag flags)
+{
+ StreamStateSaver state_saver{out};
+ out << "0x" << std::hex << static_cast<unsigned>(flags);
+ return out;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/enums.h b/backend/genesys/enums.h
new file mode 100644
index 0000000..810c4ca
--- /dev/null
+++ b/backend/genesys/enums.h
@@ -0,0 +1,530 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_ENUMS_H
+#define BACKEND_GENESYS_ENUMS_H
+
+#include <iostream>
+#include "serialize.h"
+
+namespace genesys {
+
+enum class ScanMethod : unsigned {
+ // normal scan method
+ FLATBED = 0,
+ // scan using transparency adaptor
+ TRANSPARENCY = 1,
+ // scan using transparency adaptor via infrared channel
+ TRANSPARENCY_INFRARED = 2
+};
+
+inline std::ostream& operator<<(std::ostream& out, ScanMethod mode)
+{
+ switch (mode) {
+ case ScanMethod::FLATBED: out << "FLATBED"; return out;
+ case ScanMethod::TRANSPARENCY: out << "TRANSPARENCY"; return out;
+ case ScanMethod::TRANSPARENCY_INFRARED: out << "TRANSPARENCY_INFRARED"; return out;
+ }
+ return out;
+}
+
+inline void serialize(std::istream& str, ScanMethod& x)
+{
+ unsigned value;
+ serialize(str, value);
+ x = static_cast<ScanMethod>(value);
+}
+
+inline void serialize(std::ostream& str, ScanMethod& x)
+{
+ unsigned value = static_cast<unsigned>(x);
+ serialize(str, value);
+}
+
+const char* scan_method_to_option_string(ScanMethod method);
+ScanMethod option_string_to_scan_method(const std::string& str);
+
+enum class ScanColorMode : unsigned {
+ LINEART = 0,
+ HALFTONE,
+ GRAY,
+ COLOR_SINGLE_PASS
+};
+
+inline std::ostream& operator<<(std::ostream& out, ScanColorMode mode)
+{
+ switch (mode) {
+ case ScanColorMode::LINEART: out << "LINEART"; return out;
+ case ScanColorMode::HALFTONE: out << "HALFTONE"; return out;
+ case ScanColorMode::GRAY: out << "GRAY"; return out;
+ case ScanColorMode::COLOR_SINGLE_PASS: out << "COLOR_SINGLE_PASS"; return out;
+ }
+ return out;
+}
+
+inline void serialize(std::istream& str, ScanColorMode& x)
+{
+ unsigned value;
+ serialize(str, value);
+ x = static_cast<ScanColorMode>(value);
+}
+
+inline void serialize(std::ostream& str, ScanColorMode& x)
+{
+ unsigned value = static_cast<unsigned>(x);
+ serialize(str, value);
+}
+
+const char* scan_color_mode_to_option_string(ScanColorMode mode);
+ScanColorMode option_string_to_scan_color_mode(const std::string& str);
+
+
+enum class ScanHeadId : unsigned {
+ NONE = 0,
+ PRIMARY = 1 << 0,
+ SECONDARY = 1 << 1,
+ ALL = PRIMARY | SECONDARY,
+};
+
+inline ScanHeadId operator|(ScanHeadId left, ScanHeadId right)
+{
+ return static_cast<ScanHeadId>(static_cast<unsigned>(left) | static_cast<unsigned>(right));
+}
+
+inline ScanHeadId operator&(ScanHeadId left, ScanHeadId right)
+{
+ return static_cast<ScanHeadId>(static_cast<unsigned>(left) & static_cast<unsigned>(right));
+}
+
+
+enum class ColorFilter : unsigned {
+ RED = 0,
+ GREEN,
+ BLUE,
+ NONE
+};
+
+std::ostream& operator<<(std::ostream& out, ColorFilter mode);
+
+inline void serialize(std::istream& str, ColorFilter& x)
+{
+ unsigned value;
+ serialize(str, value);
+ x = static_cast<ColorFilter>(value);
+}
+
+inline void serialize(std::ostream& str, ColorFilter& x)
+{
+ unsigned value = static_cast<unsigned>(x);
+ serialize(str, value);
+}
+
+enum class ColorOrder
+{
+ RGB,
+ GBR,
+ BGR,
+};
+
+/* Enum value naming conventions:
+ Full name must be included with the following exceptions:
+
+ Canon scanners omit "Canoscan" if present
+*/
+enum class ModelId : unsigned
+{
+ UNKNOWN = 0,
+ CANON_4400F,
+ CANON_5600F,
+ CANON_8400F,
+ CANON_8600F,
+ CANON_IMAGE_FORMULA_101,
+ CANON_LIDE_50,
+ CANON_LIDE_60,
+ CANON_LIDE_80,
+ CANON_LIDE_100,
+ CANON_LIDE_110,
+ CANON_LIDE_120,
+ CANON_LIDE_200,
+ CANON_LIDE_210,
+ CANON_LIDE_220,
+ CANON_LIDE_700F,
+ DCT_DOCKETPORT_487,
+ HP_SCANJET_2300C,
+ HP_SCANJET_2400C,
+ HP_SCANJET_3670,
+ HP_SCANJET_4850C,
+ HP_SCANJET_G4010,
+ HP_SCANJET_G4050,
+ HP_SCANJET_N6310,
+ MEDION_MD5345,
+ PANASONIC_KV_SS080,
+ PENTAX_DSMOBILE_600,
+ PLUSTEK_OPTICBOOK_3800,
+ PLUSTEK_OPTICFILM_7200I,
+ PLUSTEK_OPTICFILM_7300,
+ PLUSTEK_OPTICFILM_7500I,
+ PLUSTEK_OPTICPRO_3600,
+ PLUSTEK_OPTICPRO_ST12,
+ PLUSTEK_OPTICPRO_ST24,
+ SYSCAN_DOCKETPORT_465,
+ SYSCAN_DOCKETPORT_467,
+ SYSCAN_DOCKETPORT_485,
+ SYSCAN_DOCKETPORT_665,
+ SYSCAN_DOCKETPORT_685,
+ UMAX_ASTRA_4500,
+ VISIONEER_7100,
+ VISIONEER_ROADWARRIOR,
+ VISIONEER_STROBE_XP100_REVISION3,
+ VISIONEER_STROBE_XP200,
+ VISIONEER_STROBE_XP300,
+ XEROX_2400,
+ XEROX_TRAVELSCANNER_100,
+};
+
+enum class SensorId : unsigned
+{
+ UNKNOWN = 0,
+ CCD_5345,
+ CCD_CANON_4400F,
+ CCD_CANON_8400F,
+ CCD_CANON_8600F,
+ CCD_DP665,
+ CCD_DP685,
+ CCD_DSMOBILE600,
+ CCD_G4050,
+ CCD_HP2300,
+ CCD_HP2400,
+ CCD_HP3670,
+ CCD_HP_N6310,
+ CCD_HP_4850C,
+ CCD_IMG101,
+ CCD_KVSS080,
+ CCD_PLUSTEK_OPTICBOOK_3800,
+ CCD_PLUSTEK_OPTICFILM_7200I,
+ CCD_PLUSTEK_OPTICFILM_7300,
+ CCD_PLUSTEK_OPTICFILM_7500I,
+ CCD_PLUSTEK_OPTICPRO_3600,
+ CCD_ROADWARRIOR,
+ CCD_ST12, // SONY ILX548: 5340 Pixel ???
+ CCD_ST24, // SONY ILX569: 10680 Pixel ???
+ CCD_UMAX,
+ CCD_XP300,
+ CIS_CANON_LIDE_35,
+ CIS_CANON_LIDE_80,
+ CIS_CANON_LIDE_100,
+ CIS_CANON_LIDE_110,
+ CIS_CANON_LIDE_120,
+ CIS_CANON_LIDE_200,
+ CIS_CANON_LIDE_210,
+ CIS_CANON_LIDE_220,
+ CIS_CANON_LIDE_700F,
+ CIS_XP200,
+};
+
+inline void serialize(std::istream& str, SensorId& x)
+{
+ unsigned value;
+ serialize(str, value);
+ x = static_cast<SensorId>(value);
+}
+
+inline void serialize(std::ostream& str, SensorId& x)
+{
+ unsigned value = static_cast<unsigned>(x);
+ serialize(str, value);
+}
+
+
+enum class AdcId : unsigned
+{
+ UNKNOWN = 0,
+ AD_XP200,
+ CANON_LIDE_35,
+ CANON_LIDE_80,
+ CANON_LIDE_110,
+ CANON_LIDE_120,
+ CANON_LIDE_200,
+ CANON_LIDE_700F,
+ CANON_4400F,
+ CANON_8400F,
+ CANON_8600F,
+ G4050,
+ IMG101,
+ KVSS080,
+ PLUSTEK_OPTICBOOK_3800,
+ PLUSTEK_OPTICFILM_7200I,
+ PLUSTEK_OPTICFILM_7300,
+ PLUSTEK_OPTICFILM_7500I,
+ PLUSTEK_OPTICPRO_3600,
+ WOLFSON_5345,
+ WOLFSON_DSM600,
+ WOLFSON_HP2300,
+ WOLFSON_HP2400,
+ WOLFSON_HP3670,
+ WOLFSON_ST12,
+ WOLFSON_ST24,
+ WOLFSON_UMAX,
+ WOLFSON_XP300,
+};
+
+inline void serialize(std::istream& str, AdcId& x)
+{
+ unsigned value;
+ serialize(str, value);
+ x = static_cast<AdcId>(value);
+}
+
+inline void serialize(std::ostream& str, AdcId& x)
+{
+ unsigned value = static_cast<unsigned>(x);
+ serialize(str, value);
+}
+
+enum class GpioId : unsigned
+{
+ UNKNOWN = 0,
+ CANON_LIDE_35,
+ CANON_LIDE_80,
+ CANON_LIDE_110,
+ CANON_LIDE_120,
+ CANON_LIDE_200,
+ CANON_LIDE_210,
+ CANON_LIDE_700F,
+ CANON_4400F,
+ CANON_8400F,
+ CANON_8600F,
+ DP665,
+ DP685,
+ G4050,
+ HP2300,
+ HP2400,
+ HP3670,
+ HP_N6310,
+ IMG101,
+ KVSS080,
+ MD_5345,
+ PLUSTEK_OPTICBOOK_3800,
+ PLUSTEK_OPTICFILM_7200I,
+ PLUSTEK_OPTICFILM_7300,
+ PLUSTEK_OPTICFILM_7500I,
+ PLUSTEK_OPTICPRO_3600,
+ ST12,
+ ST24,
+ UMAX,
+ XP200,
+ XP300,
+};
+
+enum class MotorId : unsigned
+{
+ UNKNOWN = 0,
+ CANON_LIDE_100,
+ CANON_LIDE_110,
+ CANON_LIDE_120,
+ CANON_LIDE_200,
+ CANON_LIDE_210,
+ CANON_LIDE_35,
+ CANON_LIDE_700,
+ CANON_LIDE_80,
+ CANON_4400F,
+ CANON_8400F,
+ CANON_8600F,
+ DP665,
+ DSMOBILE_600,
+ G4050,
+ HP2300,
+ HP2400,
+ HP3670,
+ IMG101,
+ KVSS080,
+ MD_5345,
+ PLUSTEK_OPTICBOOK_3800,
+ PLUSTEK_OPTICFILM_7200I,
+ PLUSTEK_OPTICFILM_7300,
+ PLUSTEK_OPTICFILM_7500I,
+ PLUSTEK_OPTICPRO_3600,
+ ROADWARRIOR,
+ ST24,
+ UMAX,
+ XP200,
+ XP300,
+};
+
+enum class StepType : unsigned
+{
+ FULL = 0,
+ HALF = 1,
+ QUARTER = 2,
+ EIGHTH = 3,
+};
+
+std::ostream& operator<<(std::ostream& out, StepType type);
+
+inline bool operator<(StepType lhs, StepType rhs)
+{
+ return static_cast<unsigned>(lhs) < static_cast<unsigned>(rhs);
+}
+inline bool operator<=(StepType lhs, StepType rhs)
+{
+ return static_cast<unsigned>(lhs) <= static_cast<unsigned>(rhs);
+}
+inline bool operator>(StepType lhs, StepType rhs)
+{
+ return static_cast<unsigned>(lhs) > static_cast<unsigned>(rhs);
+}
+inline bool operator>=(StepType lhs, StepType rhs)
+{
+ return static_cast<unsigned>(lhs) >= static_cast<unsigned>(rhs);
+}
+
+enum class AsicType : unsigned
+{
+ UNKNOWN = 0,
+ GL646,
+ GL841,
+ GL843,
+ GL845,
+ GL846,
+ GL847,
+ GL124,
+};
+
+
+enum class ScanFlag : unsigned
+{
+ NONE = 0,
+ SINGLE_LINE = 1 << 0,
+ DISABLE_SHADING = 1 << 1,
+ DISABLE_GAMMA = 1 << 2,
+ DISABLE_BUFFER_FULL_MOVE = 1 << 3,
+ IGNORE_LINE_DISTANCE = 1 << 4,
+ DISABLE_LAMP = 1 << 5,
+ CALIBRATION = 1 << 6,
+ FEEDING = 1 << 7,
+ USE_XPA = 1 << 8,
+ ENABLE_LEDADD = 1 << 9,
+ USE_XCORRECTION = 1 << 10,
+ REVERSE = 1 << 11,
+};
+
+inline ScanFlag operator|(ScanFlag left, ScanFlag right)
+{
+ return static_cast<ScanFlag>(static_cast<unsigned>(left) | static_cast<unsigned>(right));
+}
+
+inline ScanFlag& operator|=(ScanFlag& left, ScanFlag right)
+{
+ left = left | right;
+ return left;
+}
+
+inline ScanFlag operator&(ScanFlag left, ScanFlag right)
+{
+ return static_cast<ScanFlag>(static_cast<unsigned>(left) & static_cast<unsigned>(right));
+}
+
+inline bool has_flag(ScanFlag flags, ScanFlag which)
+{
+ return (flags & which) == which;
+}
+
+inline void serialize(std::istream& str, ScanFlag& x)
+{
+ unsigned value;
+ serialize(str, value);
+ x = static_cast<ScanFlag>(value);
+}
+
+inline void serialize(std::ostream& str, ScanFlag& x)
+{
+ unsigned value = static_cast<unsigned>(x);
+ serialize(str, value);
+}
+
+std::ostream& operator<<(std::ostream& out, ScanFlag flags);
+
+
+
+enum class MotorFlag : unsigned
+{
+ NONE = 0,
+ AUTO_GO_HOME = 1 << 0,
+ DISABLE_BUFFER_FULL_MOVE = 1 << 2,
+ FEED = 1 << 3,
+ USE_XPA = 1 << 4,
+ REVERSE = 1 << 5,
+};
+
+inline MotorFlag operator|(MotorFlag left, MotorFlag right)
+{
+ return static_cast<MotorFlag>(static_cast<unsigned>(left) | static_cast<unsigned>(right));
+}
+
+inline MotorFlag& operator|=(MotorFlag& left, MotorFlag right)
+{
+ left = left | right;
+ return left;
+}
+
+inline MotorFlag operator&(MotorFlag left, MotorFlag right)
+{
+ return static_cast<MotorFlag>(static_cast<unsigned>(left) & static_cast<unsigned>(right));
+}
+
+inline bool has_flag(MotorFlag flags, MotorFlag which)
+{
+ return (flags & which) == which;
+}
+
+
+enum class Direction : unsigned
+{
+ FORWARD = 0,
+ BACKWARD = 1
+};
+
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_ENUMS_H
diff --git a/backend/genesys_error.cc b/backend/genesys/error.cpp
index c98a490..6c921c1 100644
--- a/backend/genesys_error.cc
+++ b/backend/genesys/error.cpp
@@ -43,12 +43,13 @@
#define DEBUG_DECLARE_ONLY
-#include "genesys_error.h"
+#include "error.h"
#include <cstdarg>
-#include <cstdio>
+
+namespace genesys {
extern "C" void sanei_debug_msg(int level, int max_level, const char *be, const char *fmt,
- va_list ap);
+ std::va_list ap);
#if (defined(__GNUC__) || defined(__CLANG__)) && (defined(__linux__) || defined(__APPLE__))
extern "C" char* __cxa_get_globals();
@@ -69,6 +70,73 @@ static unsigned num_uncaught_exceptions()
#endif
}
+SaneException::SaneException(SANE_Status status) : status_(status)
+{
+ set_msg();
+}
+
+SaneException::SaneException(SANE_Status status, const char* format, ...) : status_(status)
+{
+ std::va_list args;
+ va_start(args, format);
+ set_msg(format, args);
+ va_end(args);
+}
+
+SaneException::SaneException(const char* format, ...) : status_(SANE_STATUS_INVAL)
+{
+ std::va_list args;
+ va_start(args, format);
+ set_msg(format, args);
+ va_end(args);
+}
+
+SANE_Status SaneException::status() const
+{
+ return status_;
+}
+
+const char* SaneException::what() const noexcept
+{
+ return msg_.c_str();
+}
+
+void SaneException::set_msg()
+{
+ const char* status_msg = sane_strstatus(status_);
+ std::size_t status_msg_len = std::strlen(status_msg);
+ msg_.reserve(status_msg_len);
+ msg_ = status_msg;
+}
+
+void SaneException::set_msg(const char* format, std::va_list vlist)
+{
+ const char* status_msg = sane_strstatus(status_);
+ std::size_t status_msg_len = std::strlen(status_msg);
+
+ std::va_list vlist2;
+ va_copy(vlist2, vlist);
+ int msg_len = std::vsnprintf(nullptr, 0, format, vlist2);
+ va_end(vlist2);
+
+ if (msg_len < 0) {
+ const char* formatting_error_msg = "(error formatting arguments)";
+ msg_.reserve(std::strlen(formatting_error_msg) + 3 + status_msg_len);
+ msg_ = formatting_error_msg;
+ msg_ += " : ";
+ msg_ += status_msg;
+ return;
+ }
+
+ msg_.reserve(msg_len + status_msg_len + 3);
+ msg_.resize(msg_len + 1, ' ');
+ std::vsnprintf(&msg_[0], msg_len + 1, format, vlist);
+ msg_.resize(msg_len, ' ');
+
+ msg_ += " : ";
+ msg_ += status_msg;
+}
+
DebugMessageHelper::DebugMessageHelper(const char* func)
{
func_ = func;
@@ -113,3 +181,35 @@ void DebugMessageHelper::vstatus(const char* format, ...)
std::vsnprintf(msg_, MAX_BUF_SIZE, format, args);
va_end(args);
}
+
+void DebugMessageHelper::log(unsigned level, const char* msg)
+{
+ DBG(level, "%s: %s\n", func_, msg);
+}
+
+void DebugMessageHelper::vlog(unsigned level, const char* format, ...)
+{
+ std::string msg;
+
+ std::va_list args;
+
+ va_start(args, format);
+ int msg_len = std::vsnprintf(nullptr, 0, format, args);
+ va_end(args);
+
+ if (msg_len < 0) {
+ DBG(level, "%s: error formatting error message: %s\n", func_, format);
+ return;
+ }
+ msg.resize(msg_len + 1, ' ');
+
+ va_start(args, format);
+ std::vsnprintf(&msg.front(), msg.size(), format, args);
+ va_end(args);
+
+ msg.resize(msg_len, ' '); // strip the null character
+
+ DBG(level, "%s: %s\n", func_, msg.c_str());
+}
+
+} // namespace genesys
diff --git a/backend/genesys_error.h b/backend/genesys/error.h
index d456581..5aba8cf 100644
--- a/backend/genesys_error.h
+++ b/backend/genesys/error.h
@@ -49,8 +49,10 @@
#include "../include/sane/sanei_backend.h"
#include <stdexcept>
+#include <cstdarg>
#include <cstring>
#include <string>
+#include <new>
#define DBG_error0 0 /* errors/warnings printed even with devuglevel 0 */
#define DBG_error 1 /* fatal errors */
@@ -62,71 +64,44 @@
#define DBG_io2 7 /* io functions that are called very often */
#define DBG_data 8 /* log image data */
-class SaneException : std::exception {
-public:
- SaneException(SANE_Status status) : status_(status)
- {
- set_msg(nullptr);
- }
+namespace genesys {
- SaneException(SANE_Status status, const char* msg) : status_(status)
- {
- set_msg(msg);
- }
+class SaneException : public std::exception {
+public:
+ SaneException(SANE_Status status);
+ SaneException(SANE_Status status, const char* format, ...)
+ #ifdef __GNUC__
+ __attribute__((format(printf, 3, 4)))
+ #endif
+ ;
- SaneException(const char* msg) : SaneException(SANE_STATUS_INVAL, msg) {}
+ SaneException(const char* format, ...)
+ #ifdef __GNUC__
+ __attribute__((format(printf, 2, 3)))
+ #endif
+ ;
- SANE_Status status() const { return status_; }
- virtual const char* what() const noexcept override { return msg_.c_str(); }
+ SANE_Status status() const;
+ const char* what() const noexcept override;
private:
- void set_msg(const char* msg)
- {
- const char* status_msg = sane_strstatus(status_);
- std::size_t status_msg_len = std::strlen(status_msg);
-
- if (msg) {
- std::size_t msg_len = std::strlen(msg);
- msg_.reserve(msg_len + status_msg_len + 3);
- msg_ = msg;
- msg_ += " : ";
- msg_ += status_msg;
- return;
- }
-
- msg_.reserve(status_msg_len);
- msg_ = status_msg;
- }
+ void set_msg();
+ void set_msg(const char* format, std::va_list vlist);
std::string msg_;
SANE_Status status_;
};
-/**
- * call a function and return on error
- */
-#define RIE(function) \
- do { status = function; \
- if (status != SANE_STATUS_GOOD) \
- { \
- DBG(DBG_error, "%s: %s\n", __func__, sane_strstatus (status)); \
- return status; \
- } \
- } while (SANE_FALSE)
-
// call a function and throw an exception on error
#define TIE(function) \
do { \
SANE_Status tmp_status = function; \
if (tmp_status != SANE_STATUS_GOOD) { \
- throw SaneException(tmp_status); \
+ throw ::genesys::SaneException(tmp_status); \
} \
} while (false)
-#define DBGSTART DBG (DBG_proc, "%s start\n", __func__);
-#define DBGCOMPLETED DBG (DBG_proc, "%s completed\n", __func__);
-
class DebugMessageHelper {
public:
static constexpr unsigned MAX_BUF_SIZE = 120;
@@ -149,23 +124,43 @@ public:
void clear() { msg_[0] = '\n'; }
+ void log(unsigned level, const char* msg);
+ void vlog(unsigned level, const char* format, ...)
+ #ifdef __GNUC__
+ __attribute__((format(printf, 3, 4)))
+ #endif
+ ;
+
private:
const char* func_ = nullptr;
char msg_[MAX_BUF_SIZE];
unsigned num_exceptions_on_enter_ = 0;
};
-#define DBG_HELPER(var) DebugMessageHelper var(__func__)
-#define DBG_HELPER_ARGS(var, ...) DebugMessageHelper var(__func__, __VA_ARGS__)
+
+#if defined(__GNUC__) || defined(__clang__)
+#define GENESYS_CURRENT_FUNCTION __PRETTY_FUNCTION__
+#elif defined(__FUNCSIG__)
+#define GENESYS_CURRENT_FUNCTION __FUNCSIG__
+#else
+#define GENESYS_CURRENT_FUNCTION __func__
+#endif
+
+#define DBG_HELPER(var) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION)
+#define DBG_HELPER_ARGS(var, ...) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION, __VA_ARGS__)
template<class F>
SANE_Status wrap_exceptions_to_status_code(const char* func, F&& function)
{
try {
- return function();
+ function();
+ return SANE_STATUS_GOOD;
} catch (const SaneException& exc) {
+ DBG(DBG_error, "%s: got error: %s\n", func, exc.what());
return exc.status();
} catch (const std::bad_alloc& exc) {
+ (void) exc;
+ DBG(DBG_error, "%s: failed to allocate memory\n", func);
return SANE_STATUS_NO_MEM;
} catch (const std::exception& exc) {
DBG(DBG_error, "%s: got uncaught exception: %s\n", func, exc.what());
@@ -199,4 +194,6 @@ inline void wrap_status_code_to_exception(SANE_Status status)
throw SaneException(status);
}
+} // namespace genesys
+
#endif // BACKEND_GENESYS_ERROR_H
diff --git a/backend/genesys/fwd.h b/backend/genesys/fwd.h
new file mode 100644
index 0000000..2d55f98
--- /dev/null
+++ b/backend/genesys/fwd.h
@@ -0,0 +1,132 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_FWD_H
+#define BACKEND_GENESYS_FWD_H
+
+namespace genesys {
+
+// buffer.h
+struct Genesys_Buffer;
+
+// calibration.h
+struct Genesys_Calibration_Cache;
+
+// command_set.h
+class CommandSet;
+
+// device.h
+class FixedFloat;
+struct Genesys_Gpo;
+struct MethodResolutions;
+struct Genesys_Model;
+struct Genesys_Device;
+
+// error.h
+class DebugMessageHelper;
+class SaneException;
+
+// genesys.h
+class GenesysButton;
+struct Genesys_Scanner;
+
+// image.h
+class Image;
+
+// image_buffer.h
+class ImageBuffer;
+class FakeBufferModel;
+class ImageBufferGenesysUsb;
+
+// image_pipeline.h
+class ImagePipelineNode;
+// ImagePipelineNode* skipped
+class ImagePipelineStack;
+
+// image_pixel.h
+struct Pixel;
+struct RawPixel;
+
+// low.h
+struct Genesys_USB_Device_Entry;
+struct Motor_Profile;
+
+// motor.h
+struct Genesys_Motor;
+struct MotorSlope;
+struct MotorSlopeTable;
+
+// register.h
+class Genesys_Register_Set;
+struct GenesysRegisterSetState;
+
+// row_buffer.h
+class RowBuffer;
+
+// usb_device.h
+class IUsbDevice;
+class UsbDevice;
+
+// scanner_interface.h
+class ScannerInterface;
+class ScannerInterfaceUsb;
+class TestScannerInterface;
+
+// sensor.h
+class ResolutionFilter;
+struct GenesysFrontendLayout;
+struct Genesys_Frontend;
+struct SensorExposure;
+struct Genesys_Sensor;
+
+// settings.h
+struct Genesys_Settings;
+struct SetupParams;
+struct ScanSession;
+
+// test_usb_device.h
+class TestUsbDevice;
+
+} // namespace genesys
+
+#endif
diff --git a/backend/genesys.cc b/backend/genesys/genesys.cpp
index 0368e21..7c25168 100644
--- a/backend/genesys.cc
+++ b/backend/genesys/genesys.cpp
@@ -61,35 +61,66 @@
#define DEBUG_NOT_STATIC
#include "genesys.h"
-#include "genesys_sanei.h"
+#include "conv.h"
+#include "gl124_registers.h"
+#include "gl841_registers.h"
+#include "gl843_registers.h"
+#include "gl846_registers.h"
+#include "gl847_registers.h"
+#include "usb_device.h"
+#include "utilities.h"
+#include "scanner_interface_usb.h"
+#include "test_scanner_interface.h"
+#include "test_settings.h"
#include "../include/sane/sanei_config.h"
#include "../include/sane/sanei_magic.h"
-#include "genesys_devices.cc"
+#include <array>
+#include <cmath>
#include <cstring>
#include <fstream>
+#include <iterator>
#include <list>
+#include <numeric>
#include <exception>
#include <vector>
-StaticInit<std::list<Genesys_Scanner>> s_scanners;
-StaticInit<std::vector<SANE_Device>> s_sane_devices;
-StaticInit<std::vector<SANE_Device*>> s_sane_devices_ptrs;
-StaticInit<std::list<Genesys_Device>> s_devices;
+#ifndef SANE_GENESYS_API_LINKAGE
+#define SANE_GENESYS_API_LINKAGE extern "C"
+#endif
+
+namespace genesys {
+
+// Data that we allocate to back SANE_Device objects in s_sane_devices
+struct SANE_Device_Data
+{
+ std::string name;
+};
+
+namespace {
+ StaticInit<std::list<Genesys_Scanner>> s_scanners;
+ StaticInit<std::vector<SANE_Device>> s_sane_devices;
+ StaticInit<std::vector<SANE_Device_Data>> s_sane_devices_data;
+ StaticInit<std::vector<SANE_Device*>> s_sane_devices_ptrs;
+ StaticInit<std::list<Genesys_Device>> s_devices;
+
+ // Maximum time for lamp warm-up
+ constexpr unsigned WARMUP_TIME = 65;
+} // namespace
static SANE_String_Const mode_list[] = {
SANE_VALUE_SCAN_MODE_COLOR,
SANE_VALUE_SCAN_MODE_GRAY,
/* SANE_TITLE_HALFTONE, currently unused */
SANE_VALUE_SCAN_MODE_LINEART,
- 0
+ nullptr
};
static SANE_String_Const color_filter_list[] = {
SANE_I18N ("Red"),
SANE_I18N ("Green"),
SANE_I18N ("Blue"),
- 0
+ nullptr
};
static SANE_String_Const cis_color_filter_list[] = {
@@ -97,20 +128,7 @@ static SANE_String_Const cis_color_filter_list[] = {
SANE_I18N ("Green"),
SANE_I18N ("Blue"),
SANE_I18N ("None"),
- 0
-};
-
-static SANE_String_Const source_list[] = {
- SANE_I18N (STR_FLATBED),
- SANE_I18N (STR_TRANSPARENCY_ADAPTER),
- 0
-};
-
-static const char* source_list_infrared[] = {
- SANE_I18N(STR_FLATBED),
- SANE_I18N(STR_TRANSPARENCY_ADAPTER),
- SANE_I18N(STR_TRANSPARENCY_ADAPTER_INFRARED),
- 0
+ nullptr
};
static SANE_Range swdespeck_range = {
@@ -173,565 +191,156 @@ static const SANE_Range expiration_range = {
1 /* quantization */
};
-Genesys_Sensor& sanei_genesys_find_sensor_any_for_write(Genesys_Device* dev)
+const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev)
{
- for (auto& sensor : *s_sensors) {
- if (dev->model->ccd_type == sensor.sensor_id) {
+ DBG_HELPER(dbg);
+ for (const auto& sensor : *s_sensors) {
+ if (dev->model->sensor_id == sensor.sensor_id) {
return sensor;
}
}
throw std::runtime_error("Given device does not have sensor defined");
}
-const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev)
+Genesys_Sensor* find_sensor_impl(Genesys_Device* dev, unsigned dpi, unsigned channels,
+ ScanMethod scan_method)
{
- for (const auto& sensor : *s_sensors) {
- if (dev->model->ccd_type == sensor.sensor_id) {
- return sensor;
+ DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
+ static_cast<unsigned>(scan_method));
+ for (auto& sensor : *s_sensors) {
+ if (dev->model->sensor_id == sensor.sensor_id && sensor.resolutions.matches(dpi) &&
+ sensor.matches_channel_count(channels) && sensor.method == scan_method)
+ {
+ return &sensor;
}
}
- throw std::runtime_error("Given device does not have sensor defined");
+ return nullptr;
+}
+
+bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels,
+ ScanMethod scan_method)
+{
+ DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
+ static_cast<unsigned>(scan_method));
+ return find_sensor_impl(dev, dpi, channels, scan_method) != nullptr;
}
-const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, int dpi,
+const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels,
ScanMethod scan_method)
{
- for (const auto& sensor : *s_sensors) {
- if (dev->model->ccd_type == sensor.sensor_id &&
- (sensor.min_resolution == -1 || dpi >= sensor.min_resolution) &&
- (sensor.max_resolution == -1 || dpi <= sensor.max_resolution) &&
- sensor.method == scan_method) {
- return sensor;
- }
- }
+ DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
+ static_cast<unsigned>(scan_method));
+ const auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method);
+ if (sensor)
+ return *sensor;
throw std::runtime_error("Given device does not have sensor defined");
}
-Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, int dpi,
+Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigned dpi,
+ unsigned channels,
ScanMethod scan_method)
{
+ DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
+ static_cast<unsigned>(scan_method));
+ auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method);
+ if (sensor)
+ return *sensor;
+ throw std::runtime_error("Given device does not have sensor defined");
+}
+
+
+std::vector<std::reference_wrapper<const Genesys_Sensor>>
+ sanei_genesys_find_sensors_all(Genesys_Device* dev, ScanMethod scan_method)
+{
+ DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));
+ std::vector<std::reference_wrapper<const Genesys_Sensor>> ret;
+ for (const Genesys_Sensor& sensor : sanei_genesys_find_sensors_all_for_write(dev, scan_method)) {
+ ret.push_back(sensor);
+ }
+ return ret;
+}
+
+std::vector<std::reference_wrapper<Genesys_Sensor>>
+ sanei_genesys_find_sensors_all_for_write(Genesys_Device* dev, ScanMethod scan_method)
+{
+ DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));
+ std::vector<std::reference_wrapper<Genesys_Sensor>> ret;
for (auto& sensor : *s_sensors) {
- if (dev->model->ccd_type == sensor.sensor_id &&
- (sensor.min_resolution == -1 || dpi >= sensor.min_resolution) &&
- (sensor.max_resolution == -1 || dpi <= sensor.max_resolution) &&
- sensor.method == scan_method) {
- return sensor;
+ if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) {
+ ret.push_back(sensor);
}
}
- throw std::runtime_error("Given device does not have sensor defined");
+ return ret;
}
-
-void
-sanei_genesys_init_structs (Genesys_Device * dev)
+void sanei_genesys_init_structs (Genesys_Device * dev)
{
- unsigned int i, gpo_ok = 0, motor_ok = 0;
+ DBG_HELPER(dbg);
+
+ bool gpo_ok = false;
+ bool motor_ok = false;
bool fe_ok = false;
/* initialize the GPO data stuff */
- for (i = 0; i < sizeof (Gpo) / sizeof (Genesys_Gpo); i++)
- {
- if (dev->model->gpo_type == Gpo[i].gpo_id)
- {
- dev->gpo = Gpo[i];
- gpo_ok = 1;
- }
+ for (const auto& gpo : *s_gpo) {
+ if (dev->model->gpio_id == gpo.id) {
+ dev->gpo = gpo;
+ gpo_ok = true;
+ break;
+ }
}
- /* initialize the motor data stuff */
- for (i = 0; i < sizeof (Motor) / sizeof (Genesys_Motor); i++)
- {
- if (dev->model->motor_type == Motor[i].motor_id)
- {
- dev->motor = Motor[i];
- motor_ok = 1;
- }
+ // initialize the motor data stuff
+ for (const auto& motor : *s_motors) {
+ if (dev->model->motor_id == motor.id) {
+ dev->motor = motor;
+ motor_ok = true;
+ break;
+ }
}
for (const auto& frontend : *s_frontends) {
- if (dev->model->dac_type == frontend.fe_id) {
+ if (dev->model->adc_id == frontend.id) {
dev->frontend_initial = frontend;
+ dev->frontend = frontend;
fe_ok = true;
break;
}
}
- /* sanity check */
- if (motor_ok == 0 || gpo_ok == 0 || !fe_ok)
- {
- DBG(DBG_error0, "%s: bad description(s) for fe/gpo/motor=%d/%d/%d\n", __func__,
- dev->model->ccd_type, dev->model->gpo_type, dev->model->motor_type);
+ if (!motor_ok || !gpo_ok || !fe_ok) {
+ throw SaneException("bad description(s) for fe/gpo/motor=%d/%d/%d\n",
+ static_cast<unsigned>(dev->model->sensor_id),
+ static_cast<unsigned>(dev->model->gpio_id),
+ static_cast<unsigned>(dev->model->motor_id));
}
-
- /* set up initial line distance shift */
- dev->ld_shift_r = dev->model->ld_shift_r;
- dev->ld_shift_g = dev->model->ld_shift_g;
- dev->ld_shift_b = dev->model->ld_shift_b;
-}
-
-/* main function for slope creation */
-/**
- * This function generates a slope table using the given slope
- * truncated at the given exposure time or step count, whichever comes first.
- * The reached step time is then stored in final_exposure and used for the rest
- * of the table. The summed time of the acceleration steps is returned, and the
- * number of accerelation steps is put into used_steps.
- *
- * @param slope_table Table to write to
- * @param max_steps Size of slope_table in steps
- * @param use_steps Maximum number of steps to use for acceleration
- * @param stop_at Minimum step time to use
- * @param vstart Start step time of default slope
- * @param vend End step time of default slope
- * @param steps Step count of default slope
- * @param g Power for default slope
- * @param used_steps Final number of steps is stored here
- * @param vfinal Final step time is stored here
- * @return Time for acceleration
- * @note All times in pixel time. Correction for other motor timings is not
- * done.
- */
-SANE_Int
-sanei_genesys_generate_slope_table (uint16_t * slope_table,
- unsigned int max_steps,
- unsigned int use_steps,
- uint16_t stop_at,
- uint16_t vstart,
- uint16_t vend,
- unsigned int steps,
- double g,
- unsigned int *used_steps,
- unsigned int *vfinal)
-{
- double t;
- SANE_Int sum = 0;
- unsigned int i;
- unsigned int c = 0;
- uint16_t t2;
- unsigned int dummy;
- unsigned int _vfinal;
- if (!used_steps)
- used_steps = &dummy;
- if (!vfinal)
- vfinal = &_vfinal;
-
- DBG(DBG_proc, "%s: table size: %d\n", __func__, max_steps);
-
- DBG(DBG_proc, "%s: stop at time: %d, use %d steps max\n", __func__, stop_at, use_steps);
-
- DBG(DBG_proc, "%s: target slope: vstart: %d, vend: %d, steps: %d, g: %g\n", __func__, vstart,
- vend, steps, g);
-
- sum = 0;
- c = 0;
- *used_steps = 0;
-
- if (use_steps < 1)
- use_steps = 1;
-
- if (stop_at < vstart)
- {
- t2 = vstart;
- for (i = 0; i < steps && i < use_steps - 1 && i < max_steps; i++, c++)
- {
- t = pow (((double) i) / ((double) (steps - 1)), g);
- t2 = vstart * (1 - t) + t * vend;
- if (t2 < stop_at)
- break;
- *slope_table++ = t2;
- /* DBG (DBG_io, "slope_table[%3d] = %5d\n", c, t2); */
- sum += t2;
- }
- if (t2 > stop_at)
- {
- DBG(DBG_warn, "Can not reach target speed(%d) in %d steps.\n", stop_at, use_steps);
- DBG(DBG_warn, "Expect image to be distorted. Ignore this if only feeding.\n");
- }
- *vfinal = t2;
- *used_steps += i;
- max_steps -= i;
- }
- else
- *vfinal = stop_at;
-
- for (i = 0; i < max_steps; i++, c++)
- {
- *slope_table++ = *vfinal;
- /* DBG (DBG_io, "slope_table[%3d] = %5d\n", c, *vfinal); */
- }
-
- (*used_steps)++;
- sum += *vfinal;
-
- DBG(DBG_proc, "%s: returns sum=%d, used %d steps, completed\n", __func__, sum, *used_steps);
-
- return sum;
}
/* Generate slope table for motor movement */
/**
* This function generates a slope table using the slope from the motor struct
* truncated at the given exposure time or step count, whichever comes first.
- * The reached step time is then stored in final_exposure and used for the rest
- * of the table. The summed time of the acceleration steps is returned, and the
+ * The summed time of the acceleration steps is returned, and the
* number of accerelation steps is put into used_steps.
*
* @param dev Device struct
* @param slope_table Table to write to
- * @param max_step Size of slope_table in steps
- * @param use_steps Maximum number of steps to use for acceleration
* @param step_type Generate table for this step_type. 0=>full, 1=>half,
* 2=>quarter
* @param exposure_time Minimum exposure time of a scan line
* @param yres Resolution of a scan line
* @param used_steps Final number of steps is stored here
- * @param final_exposure Final step time is stored here
- * @param power_mode Power mode (related to the Vref used) of the motor
- * @return Time for acceleration
+ * @return Motor slope table
* @note all times in pixel time
*/
-SANE_Int
-sanei_genesys_create_slope_table3 (Genesys_Device * dev,
- uint16_t * slope_table,
- int max_step,
- unsigned int use_steps,
- int step_type,
- int exposure_time,
- double yres,
- unsigned int *used_steps,
- unsigned int *final_exposure,
- int power_mode)
-{
- unsigned int sum_time = 0;
- unsigned int vtarget;
- unsigned int vend;
- unsigned int vstart;
- unsigned int vfinal;
-
- DBG(DBG_proc, "%s: step_type = %d, exposure_time = %d, yres = %g, power_mode = %d\n", __func__,
- step_type, exposure_time, yres, power_mode);
-
- /* final speed */
- vtarget = (exposure_time * yres) / dev->motor.base_ydpi;
-
- vstart = dev->motor.slopes[power_mode][step_type].maximum_start_speed;
- vend = dev->motor.slopes[power_mode][step_type].maximum_speed;
-
- vtarget >>= step_type;
- if (vtarget > 65535)
- vtarget = 65535;
-
- vstart >>= step_type;
- if (vstart > 65535)
- vstart = 65535;
-
- vend >>= step_type;
- if (vend > 65535)
- vend = 65535;
-
- sum_time = sanei_genesys_generate_slope_table (slope_table,
- max_step,
- use_steps,
- vtarget,
- vstart,
- vend,
- dev->motor.slopes[power_mode][step_type].minimum_steps << step_type,
- dev->motor.slopes[power_mode][step_type].g,
- used_steps,
- &vfinal);
-
- if (final_exposure)
- *final_exposure = (vfinal * dev->motor.base_ydpi) / yres;
-
- DBG(DBG_proc, "%s: returns sum_time=%d, completed\n", __func__, sum_time);
-
- return sum_time;
-}
-
-
-/* alternate slope table creation function */
-/* the hardcoded values (g and vstart) will go in a motor struct */
-static SANE_Int
-genesys_create_slope_table2 (Genesys_Device * dev,
- uint16_t * slope_table, int steps,
- int step_type, int exposure_time,
- SANE_Bool same_speed, double yres,
- int power_mode)
-{
- double t, g;
- SANE_Int sum = 0;
- int vstart, vend;
- int i;
-
- DBG(DBG_proc, "%s: %d steps, step_type = %d, "
- "exposure_time = %d, same_speed = %d, yres = %.2f, power_mode = %d\n", __func__, steps,
- step_type, exposure_time, same_speed, yres, power_mode);
-
- /* start speed */
- if (dev->model->motor_type == MOTOR_5345)
- {
- if (yres < dev->motor.base_ydpi / 6)
- vstart = 2500;
- else
- vstart = 2000;
- }
- else
- {
- if (steps == 2)
- vstart = exposure_time;
- else if (steps == 3)
- vstart = 2 * exposure_time;
- else if (steps == 4)
- vstart = 1.5 * exposure_time;
- else if (steps == 120)
- vstart = 1.81674 * exposure_time;
- else
- vstart = exposure_time;
- }
-
- /* final speed */
- vend = (exposure_time * yres) / (dev->motor.base_ydpi * (1 << step_type));
-
- /*
- type=1 : full
- type=2 : half
- type=4 : quarter
- vend * type * base_ydpi / exposure = yres
- */
-
- /* acceleration */
- switch (steps)
- {
- case 255:
- /* test for special case: fast moving slope */
- /* todo: a 'fast' boolean parameter should be better */
- if (vstart == 2000)
- g = 0.2013;
- else
- g = 0.1677;
- break;
- case 120:
- g = 0.5;
- break;
- case 67:
- g = 0.5;
- break;
- case 64:
- g = 0.2555;
- break;
- case 44:
- g = 0.5;
- break;
- case 4:
- g = 0.5;
- break;
- case 3:
- g = 1;
- break;
- case 2:
- vstart = vend;
- g = 1;
- break;
- default:
- g = 0.2635;
- }
-
- /* if same speed, no 'g' */
- sum = 0;
- if (same_speed)
- {
- for (i = 0; i < 255; i++)
- {
- slope_table[i] = vend;
- sum += slope_table[i];
- DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]);
- }
- }
- else
- {
- for (i = 0; i < steps; i++)
- {
- t = pow (((double) i) / ((double) (steps - 1)), g);
- slope_table[i] = vstart * (1 - t) + t * vend;
- DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]);
- sum += slope_table[i];
- }
- for (i = steps; i < 255; i++)
- {
- slope_table[i] = vend;
- DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]);
- sum += slope_table[i];
- }
- }
-
- DBG(DBG_proc, "%s: returns sum=%d, completed\n", __func__, sum);
-
- return sum;
-}
-
-/* Generate slope table for motor movement */
-/* todo: check details */
-SANE_Int
-sanei_genesys_create_slope_table (Genesys_Device * dev,
- uint16_t * slope_table, int steps,
- int step_type, int exposure_time,
- SANE_Bool same_speed, double yres,
- int power_mode)
+MotorSlopeTable sanei_genesys_create_slope_table3(AsicType asic_type, const Genesys_Motor& motor,
+ StepType step_type, int exposure_time,
+ unsigned yres)
{
- double t;
- double start_speed;
- double g;
- uint32_t time_period;
- int sum_time = 0;
- int i, divider;
- int same_step;
-
- if (dev->model->motor_type == MOTOR_5345
- || dev->model->motor_type == MOTOR_HP2300
- || dev->model->motor_type == MOTOR_HP2400)
- return genesys_create_slope_table2 (dev, slope_table, steps,
- step_type, exposure_time,
- same_speed, yres, power_mode);
-
- DBG(DBG_proc, "%s: %d steps, step_type = %d, exposure_time = %d, same_speed =%d\n", __func__,
- steps, step_type, exposure_time, same_speed);
- DBG(DBG_proc, "%s: yres = %.2f\n", __func__, yres);
-
- g = 0.6;
- start_speed = 0.01;
- same_step = 4;
- divider = 1 << step_type;
-
- time_period =
- (uint32_t) (yres * exposure_time / dev->motor.base_ydpi /*MOTOR_GEAR */ );
- if ((time_period < 2000) && (same_speed))
- same_speed = SANE_FALSE;
-
- time_period = time_period / divider;
-
- if (same_speed)
- {
- for (i = 0; i < steps; i++)
- {
- slope_table[i] = (uint16_t) time_period;
- sum_time += time_period;
+ unsigned target_speed_w = (exposure_time * yres) / motor.base_ydpi;
- DBG (DBG_io, "slope_table[%d] = %d\n", i, time_period);
- }
- DBG(DBG_info, "%s: returns sum_time=%d, completed\n", __func__, sum_time);
- return sum_time;
- }
-
- if (time_period > MOTOR_SPEED_MAX * 5)
- {
- g = 1.0;
- start_speed = 0.05;
- same_step = 2;
- }
- else if (time_period > MOTOR_SPEED_MAX * 4)
- {
- g = 0.8;
- start_speed = 0.04;
- same_step = 2;
- }
- else if (time_period > MOTOR_SPEED_MAX * 3)
- {
- g = 0.7;
- start_speed = 0.03;
- same_step = 2;
- }
- else if (time_period > MOTOR_SPEED_MAX * 2)
- {
- g = 0.6;
- start_speed = 0.02;
- same_step = 3;
- }
-
- if (dev->model->motor_type == MOTOR_ST24)
- {
- steps = 255;
- switch ((int) yres)
- {
- case 2400:
- g = 0.1672;
- start_speed = 1.09;
- break;
- case 1200:
- g = 1;
- start_speed = 6.4;
- break;
- case 600:
- g = 0.1672;
- start_speed = 1.09;
- break;
- case 400:
- g = 0.2005;
- start_speed = 20.0 / 3.0 /*7.5 */ ;
- break;
- case 300:
- g = 0.253;
- start_speed = 2.182;
- break;
- case 150:
- g = 0.253;
- start_speed = 4.367;
- break;
- default:
- g = 0.262;
- start_speed = 7.29;
- }
- same_step = 1;
- }
-
- if (steps <= same_step)
- {
- time_period =
- (uint32_t) (yres * exposure_time /
- dev->motor.base_ydpi /*MOTOR_GEAR */ );
- time_period = time_period / divider;
-
- if (time_period > 65535)
- time_period = 65535;
-
- for (i = 0; i < same_step; i++)
- {
- slope_table[i] = (uint16_t) time_period;
- sum_time += time_period;
-
- DBG (DBG_io, "slope_table[%d] = %d\n", i, time_period);
- }
-
- DBG(DBG_proc, "%s: returns sum_time=%d, completed\n", __func__, sum_time);
- return sum_time;
- }
-
- for (i = 0; i < steps; i++)
- {
- double j = ((double) i) - same_step + 1; /* start from 1/16 speed */
-
- if (j <= 0)
- t = 0;
- else
- t = pow (j / (steps - same_step), g);
-
- time_period = /* time required for full steps */
- (uint32_t) (yres * exposure_time /
- dev->motor.base_ydpi /*MOTOR_GEAR */ *
- (start_speed + (1 - start_speed) * t));
-
- time_period = time_period / divider;
- if (time_period > 65535)
- time_period = 65535;
-
- slope_table[i] = (uint16_t) time_period;
- sum_time += time_period;
-
- DBG (DBG_io, "slope_table[%d] = %d\n", i, slope_table[i]);
- }
-
- DBG(DBG_proc, "%s: returns sum_time=%d, completed\n", __func__, sum_time);
-
- return sum_time;
+ return create_slope_table(motor.get_slope(step_type), target_speed_w, step_type, 1, 1,
+ get_slope_table_max_size(asic_type));
}
/** @brief computes gamma table
@@ -758,10 +367,11 @@ sanei_genesys_create_gamma_table (std::vector<uint16_t>& gamma_table, int size,
maximum, gamma_max, gamma);
for (i = 0; i < size; i++)
{
- value = gamma_max * pow ((float) i / size, 1.0 / gamma);
- if (value > maximum)
- value = maximum;
- gamma_table[i] = value;
+ value = static_cast<float>(gamma_max * std::pow(static_cast<double>(i) / size, 1.0 / gamma));
+ if (value > maximum) {
+ value = maximum;
+ }
+ gamma_table[i] = static_cast<std::uint16_t>(value);
}
DBG(DBG_proc, "%s: completed\n", __func__);
}
@@ -771,13 +381,18 @@ void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
{
int size = 0;
int max = 0;
- if (dev->model->asic_type == GENESYS_GL646) {
+ if (dev->model->asic_type == AsicType::GL646) {
if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) {
size = 16384;
} else {
size = 4096;
}
max = size - 1;
+ } else if (dev->model->asic_type == AsicType::GL124 ||
+ dev->model->asic_type == AsicType::GL846 ||
+ dev->model->asic_type == AsicType::GL847) {
+ size = 257;
+ max = 65535;
} else {
size = 256;
max = 65535;
@@ -796,15 +411,12 @@ void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
Note: The enhance option of the scanners does _not_ help. It only halves
the amount of pixels transfered.
*/
-SANE_Int
-sanei_genesys_exposure_time2 (Genesys_Device * dev, float ydpi,
- int step_type, int endpixel,
- int exposure_by_led, int power_mode)
+SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, float ydpi,
+ StepType step_type, int endpixel, int exposure_by_led)
{
int exposure_by_ccd = endpixel + 32;
- int exposure_by_motor =
- (dev->motor.slopes[power_mode][step_type].maximum_speed
- * dev->motor.base_ydpi) / ydpi;
+ unsigned max_speed_motor_w = dev->motor.get_slope(step_type).max_speed_w;
+ int exposure_by_motor = static_cast<int>((max_speed_motor_w * dev->motor.base_ydpi) / ydpi);
int exposure = exposure_by_ccd;
@@ -814,122 +426,12 @@ sanei_genesys_exposure_time2 (Genesys_Device * dev, float ydpi,
if (exposure < exposure_by_led && dev->model->is_cis)
exposure = exposure_by_led;
- DBG(DBG_info, "%s: ydpi=%d, step=%d, endpixel=%d led=%d, power=%d => exposure=%d\n", __func__,
- (int)ydpi, step_type, endpixel, exposure_by_led, power_mode, exposure);
+ DBG(DBG_info, "%s: ydpi=%d, step=%d, endpixel=%d led=%d => exposure=%d\n", __func__,
+ static_cast<int>(ydpi), static_cast<unsigned>(step_type), endpixel,
+ exposure_by_led, exposure);
return exposure;
}
-/* computes the exposure_time on the basis of the given horizontal dpi */
-/* we will clean/simplify it by using constants from a future motor struct */
-SANE_Int
-sanei_genesys_exposure_time (Genesys_Device * dev, Genesys_Register_Set * reg,
- int xdpi)
-{
- if (dev->model->motor_type == MOTOR_5345)
- {
- if (dev->model->cmd_set->get_filter_bit (reg))
- {
- /* monochrome */
- switch (xdpi)
- {
- case 600:
- return 8500;
- case 500:
- case 400:
- case 300:
- case 250:
- case 200:
- case 150:
- return 5500;
- case 100:
- return 6500;
- case 50:
- return 12000;
- default:
- return 11000;
- }
- }
- else
- {
- /* color scan */
- switch (xdpi)
- {
- case 300:
- case 250:
- case 200:
- return 5500;
- case 50:
- return 12000;
- default:
- return 11000;
- }
- }
- }
- else if (dev->model->motor_type == MOTOR_HP2400)
- {
- if (dev->model->cmd_set->get_filter_bit (reg))
- {
- /* monochrome */
- switch (xdpi)
- {
- case 200:
- return 7210;
- default:
- return 11111;
- }
- }
- else
- {
- /* color scan */
- switch (xdpi)
- {
- case 600:
- return 8751; /*11902; 19200 */
- default:
- return 11111;
- }
- }
- }
- else if (dev->model->motor_type == MOTOR_HP2300)
- {
- if (dev->model->cmd_set->get_filter_bit (reg))
- {
- /* monochrome */
- switch (xdpi)
- {
- case 600:
- return 8699; /* 3200; */
- case 300:
- return 3200; /*10000;, 3200 -> too dark */
- case 150:
- return 4480; /* 3200 ???, warmup needs 4480 */
- case 75:
- return 5500;
- default:
- return 11111;
- }
- }
- else
- {
- /* color scan */
- switch (xdpi)
- {
- case 600:
- return 8699;
- case 300:
- return 4349;
- case 150:
- case 75:
- return 4480;
- default:
- return 11111;
- }
- }
- }
- return 11000;
-}
-
-
/* Sends a block of shading information to the scanner.
The data is placed at address 0x0000 for color mode, gray mode and
@@ -940,25 +442,19 @@ sanei_genesys_exposure_time (Genesys_Device * dev, Genesys_Register_Set * reg,
The data needs to be of size "size", and in little endian byte order.
*/
-static SANE_Status
-genesys_send_offset_and_shading (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data,
- int size)
+static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ uint8_t* data, int size)
{
+ DBG_HELPER_ARGS(dbg, "(size = %d)", size);
int dpihw;
int start_address;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s: (size = %d)\n", __func__, size);
/* ASIC higher than gl843 doesn't have register 2A/2B, so we route to
* a per ASIC shading data loading function if available.
* It is also used for scanners using SHDAREA */
- if(dev->model->cmd_set->send_shading_data!=NULL)
- {
- status=dev->model->cmd_set->send_shading_data(dev, sensor, data, size);
- DBGCOMPLETED;
- return status;
+ if (dev->cmd_set->has_send_shading_data()) {
+ dev->cmd_set->send_shading_data(dev, sensor, data, size);
+ return;
}
/* gl646, gl84[123] case */
@@ -969,71 +465,57 @@ genesys_send_offset_and_shading (Genesys_Device * dev, const Genesys_Sensor& sen
/* many scanners send coefficient for lineart/gray like in color mode */
if ((dev->settings.scan_mode == ScanColorMode::LINEART ||
dev->settings.scan_mode == ScanColorMode::HALFTONE)
- && dev->model->ccd_type != CCD_PLUSTEK3800
- && dev->model->ccd_type != CCD_KVSS080
- && dev->model->ccd_type != CCD_G4050
- && dev->model->ccd_type != CCD_CS4400F
- && dev->model->ccd_type != CCD_CS8400F
- && dev->model->ccd_type != CCD_CS8600F
- && dev->model->ccd_type != CCD_DSMOBILE600
- && dev->model->ccd_type != CCD_XP300
- && dev->model->ccd_type != CCD_DP665
- && dev->model->ccd_type != CCD_DP685
- && dev->model->ccd_type != CIS_CANONLIDE80
- && dev->model->ccd_type != CCD_ROADWARRIOR
- && dev->model->ccd_type != CCD_HP2300
- && dev->model->ccd_type != CCD_HP2400
- && dev->model->ccd_type != CCD_HP3670
- && dev->model->ccd_type != CCD_5345) /* lineart, halftone */
- {
- if (dpihw == 0) /* 600 dpi */
- start_address = 0x02a00;
- else if (dpihw == 1) /* 1200 dpi */
- start_address = 0x05500;
- else if (dpihw == 2) /* 2400 dpi */
- start_address = 0x0a800;
- else /* reserved */
- return SANE_STATUS_INVAL;
- }
- else /* color */
- start_address = 0x00;
-
- status = sanei_genesys_set_buffer_address (dev, start_address);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
+ && dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICBOOK_3800
+ && dev->model->sensor_id != SensorId::CCD_KVSS080
+ && dev->model->sensor_id != SensorId::CCD_G4050
+ && dev->model->sensor_id != SensorId::CCD_HP_4850C
+ && dev->model->sensor_id != SensorId::CCD_CANON_4400F
+ && dev->model->sensor_id != SensorId::CCD_CANON_8400F
+ && dev->model->sensor_id != SensorId::CCD_CANON_8600F
+ && dev->model->sensor_id != SensorId::CCD_DSMOBILE600
+ && dev->model->sensor_id != SensorId::CCD_XP300
+ && dev->model->sensor_id != SensorId::CCD_DP665
+ && dev->model->sensor_id != SensorId::CCD_DP685
+ && dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80
+ && dev->model->sensor_id != SensorId::CCD_ROADWARRIOR
+ && dev->model->sensor_id != SensorId::CCD_HP2300
+ && dev->model->sensor_id != SensorId::CCD_HP2400
+ && dev->model->sensor_id != SensorId::CCD_HP3670
+ && dev->model->sensor_id != SensorId::CCD_5345) /* lineart, halftone */
+ {
+ if (dpihw == 0) { /* 600 dpi */
+ start_address = 0x02a00;
+ } else if (dpihw == 1) { /* 1200 dpi */
+ start_address = 0x05500;
+ } else if (dpihw == 2) { /* 2400 dpi */
+ start_address = 0x0a800;
+ } else { /* reserved */
+ throw SaneException("unknown dpihw");
+ }
}
-
- status = dev->model->cmd_set->bulk_write_data (dev, 0x3c, data, size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading table: %s\n", __func__, sane_strstatus(status));
- return status;
+ else { // color
+ start_address = 0x00;
}
- DBGCOMPLETED;
-
- return SANE_STATUS_GOOD;
+ dev->interface->write_buffer(0x3c, start_address, data, size);
}
-/* ? */
-SANE_Status
-sanei_genesys_init_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- int pixels_per_line)
+// ?
+void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ int pixels_per_line)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER_ARGS(dbg, "pixels_per_line: %d", pixels_per_line);
+
+ if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) {
+ return;
+ }
+
int channels;
int i;
- /* these models don't need to init shading data due to the use of specific send shading data
- function */
- if (dev->model->ccd_type==CCD_KVSS080
- || dev->model->ccd_type==CCD_G4050
- || dev->model->ccd_type==CCD_CS4400F
- || dev->model->ccd_type==CCD_CS8400F
- || dev->model->cmd_set->send_shading_data!=NULL)
- return SANE_STATUS_GOOD;
+ if (dev->cmd_set->has_send_shading_data()) {
+ return;
+ }
DBG(DBG_proc, "%s (pixels_per_line = %d)\n", __func__, pixels_per_line);
@@ -1059,56 +541,49 @@ sanei_genesys_init_shading_data (Genesys_Device * dev, const Genesys_Sensor& sen
*shading_data_ptr++ = 0x40; /* white hi -> 0x4000 */
}
- status = genesys_send_offset_and_shading (dev, sensor,
- shading_data.data(),
- pixels_per_line * 4 * channels);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading data: %s\n", __func__,
- sane_strstatus (status));
- }
-
- DBGCOMPLETED;
- return status;
+ genesys_send_offset_and_shading(dev, sensor, shading_data.data(),
+ pixels_per_line * 4 * channels);
}
-/* Find the position of the reference point:
- takes gray level 8 bits data and find
- first CCD usable pixel and top of scanning area */
-SANE_Status
-sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sensor,
- uint8_t * data,
- int start_pixel, int dpi, int width,
- int height)
+// Find the position of the reference point: takes gray level 8 bits data and find
+// first CCD usable pixel and top of scanning area
+void sanei_genesys_search_reference_point(Genesys_Device* dev, Genesys_Sensor& sensor,
+ const uint8_t* src_data, int start_pixel, int dpi,
+ int width, int height)
{
+ DBG_HELPER(dbg);
int x, y;
int current, left, top = 0;
int size, count;
int level = 80; /* edge threshold level */
- /*sanity check */
- if ((width < 3) || (height < 3))
- return SANE_STATUS_INVAL;
+ // sanity check
+ if ((width < 3) || (height < 3)) {
+ throw SaneException("invalid width or height");
+ }
/* transformed image data */
size = width * height;
+ std::vector<uint8_t> image2(size, 0);
std::vector<uint8_t> image(size, 0);
/* laplace filter to denoise picture */
- memcpy(image.data(), data, size); // to initialize unprocessed part of the image buffer
- for (y = 1; y < height - 1; y++)
- for (x = 1; x < width - 1; x++)
- {
- image[y * width + x] =
- (data[(y - 1) * width + x + 1] + 2 * data[(y - 1) * width + x] +
- data[(y - 1) * width + x - 1] + 2 * data[y * width + x + 1] +
- 4 * data[y * width + x] + 2 * data[y * width + x - 1] +
- data[(y + 1) * width + x + 1] + 2 * data[(y + 1) * width + x] +
- data[(y + 1) * width + x - 1]) / 16;
- }
+ std::memcpy(image2.data(), src_data, size);
+ std::memcpy(image.data(), src_data, size); // to initialize unprocessed part of the image buffer
+
+ for (y = 1; y < height - 1; y++) {
+ for (x = 1; x < width - 1; x++) {
+ image[y * width + x] =
+ (image2[(y - 1) * width + x + 1] + 2 * image2[(y - 1) * width + x] +
+ image2[(y - 1) * width + x - 1] + 2 * image2[y * width + x + 1] +
+ 4 * image2[y * width + x] + 2 * image2[y * width + x - 1] +
+ image2[(y + 1) * width + x + 1] + 2 * image2[(y + 1) * width + x] +
+ image2[(y + 1) * width + x - 1]) / 16;
+ }
+ }
- memcpy (data, image.data(), size);
+ image2 = image;
if (DBG_LEVEL >= DBG_data)
sanei_genesys_write_pnm_file("gl_laplace.pnm", image.data(), 8, 1, width, height);
@@ -1119,13 +594,11 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
and finds threshold level
*/
level = 0;
- for (y = 2; y < height - 2; y++)
- for (x = 2; x < width - 2; x++)
- {
- current =
- data[(y - 1) * width + x + 1] - data[(y - 1) * width + x - 1] +
- 2 * data[y * width + x + 1] - 2 * data[y * width + x - 1] +
- data[(y + 1) * width + x + 1] - data[(y + 1) * width + x - 1];
+ for (y = 2; y < height - 2; y++) {
+ for (x = 2; x < width - 2; x++) {
+ current = image2[(y - 1) * width + x + 1] - image2[(y - 1) * width + x - 1] +
+ 2 * image2[y * width + x + 1] - 2 * image2[y * width + x - 1] +
+ image2[(y + 1) * width + x + 1] - image2[(y + 1) * width + x - 1];
if (current < 0)
current = -current;
if (current > 255)
@@ -1133,7 +606,8 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
image[y * width + x] = current;
if (current > level)
level = current;
- }
+ }
+ }
if (DBG_LEVEL >= DBG_data)
sanei_genesys_write_pnm_file("gl_xsobel.pnm", image.data(), 8, 1, width, height);
@@ -1160,8 +634,8 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
sanei_genesys_write_pnm_file("gl_detected-xsobel.pnm", image.data(), 8, 1, width, height);
left = left / count;
- /* turn it in CCD pixel at full sensor optical resolution */
- sensor.CCD_start_xoffset = start_pixel + (left * sensor.optical_res) / dpi;
+ // turn it in CCD pixel at full sensor optical resolution
+ sensor.ccd_start_xoffset = start_pixel + (left * sensor.optical_res) / dpi;
/* find top edge by detecting black strip */
/* apply Y direction sobel filter
@@ -1170,13 +644,11 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
1 2 1
*/
level = 0;
- for (y = 2; y < height - 2; y++)
- for (x = 2; x < width - 2; x++)
- {
- current =
- -data[(y - 1) * width + x + 1] - 2 * data[(y - 1) * width + x] -
- data[(y - 1) * width + x - 1] + data[(y + 1) * width + x + 1] +
- 2 * data[(y + 1) * width + x] + data[(y + 1) * width + x - 1];
+ for (y = 2; y < height - 2; y++) {
+ for (x = 2; x < width - 2; x++) {
+ current = -image2[(y - 1) * width + x + 1] - 2 * image2[(y - 1) * width + x] -
+ image2[(y - 1) * width + x - 1] + image2[(y + 1) * width + x + 1] +
+ 2 * image2[(y + 1) * width + x] + image2[(y + 1) * width + x - 1];
if (current < 0)
current = -current;
if (current > 255)
@@ -1185,6 +657,7 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
if (current > level)
level = current;
}
+ }
if (DBG_LEVEL >= DBG_data)
sanei_genesys_write_pnm_file("gl_ysobel.pnm", image.data(), 8, 1, width, height);
@@ -1192,8 +665,8 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
level = level / 3;
/* search top of horizontal black stripe : TODO yet another flag */
- if (dev->model->ccd_type == CCD_5345
- && dev->model->motor_type == MOTOR_5345)
+ if (dev->model->sensor_id == SensorId::CCD_5345
+ && dev->model->motor_id == MotorId::MD_5345)
{
top = 0;
count = 0;
@@ -1215,18 +688,15 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
/* bottom of black stripe is of fixed witdh, this hardcoded value
* will be moved into device struct if more such values are needed */
top += 10;
- dev->model->y_offset_calib = SANE_FIX ((top * MM_PER_INCH) / dpi);
- DBG(DBG_info, "%s: black stripe y_offset = %f mm \n", __func__,
- SANE_UNFIX (dev->model->y_offset_calib));
+ dev->model->y_offset_calib_white = (top * MM_PER_INCH) / dpi;
+ DBG(DBG_info, "%s: black stripe y_offset = %f mm \n", __func__,
+ dev->model->y_offset_calib_white.value());
}
/* find white corner in dark area : TODO yet another flag */
- if ((dev->model->ccd_type == CCD_HP2300
- && dev->model->motor_type == MOTOR_HP2300)
- || (dev->model->ccd_type == CCD_HP2400
- && dev->model->motor_type == MOTOR_HP2400)
- || (dev->model->ccd_type == CCD_HP3670
- && dev->model->motor_type == MOTOR_HP3670))
+ if ((dev->model->sensor_id == SensorId::CCD_HP2300 && dev->model->motor_id == MotorId::HP2300) ||
+ (dev->model->sensor_id == SensorId::CCD_HP2400 && dev->model->motor_id == MotorId::HP2400) ||
+ (dev->model->sensor_id == SensorId::CCD_HP3670 && dev->model->motor_id == MotorId::HP3670))
{
top = 0;
count = 0;
@@ -1239,92 +709,617 @@ sanei_genesys_search_reference_point (Genesys_Device * dev, Genesys_Sensor& sens
count++;
}
top = top / count;
- dev->model->y_offset_calib = SANE_FIX ((top * MM_PER_INCH) / dpi);
- DBG(DBG_info, "%s: white corner y_offset = %f mm\n", __func__,
- SANE_UNFIX (dev->model->y_offset_calib));
+ dev->model->y_offset_calib_white = (top * MM_PER_INCH) / dpi;
+ DBG(DBG_info, "%s: white corner y_offset = %f mm\n", __func__,
+ dev->model->y_offset_calib_white.value());
}
- DBG(DBG_proc, "%s: CCD_start_xoffset = %d, left = %d, top = %d\n", __func__,
- sensor.CCD_start_xoffset, left, top);
+ DBG(DBG_proc, "%s: ccd_start_xoffset = %d, left = %d, top = %d\n", __func__,
+ sensor.ccd_start_xoffset, left, top);
+}
- return SANE_STATUS_GOOD;
+namespace gl843 {
+ void gl843_park_xpa_lamp(Genesys_Device* dev);
+ void gl843_set_xpa_motor_power(Genesys_Device* dev, Genesys_Register_Set& regs, bool set);
+} // namespace gl843
+
+namespace gl124 {
+ void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution);
+} // namespace gl124
+
+void scanner_clear_scan_and_feed_counts(Genesys_Device& dev)
+{
+ switch (dev.model->asic_type) {
+ case AsicType::GL843: {
+ dev.interface->write_register(gl843::REG_0x0D,
+ gl843::REG_0x0D_CLRLNCNT | gl843::REG_0x0D_CLRMCNT);
+ break;
+ }
+ case AsicType::GL845:
+ case AsicType::GL846: {
+ dev.interface->write_register(gl846::REG_0x0D,
+ gl846::REG_0x0D_CLRLNCNT | gl846::REG_0x0D_CLRMCNT);
+ break;
+ }
+ case AsicType::GL847:{
+ dev.interface->write_register(gl847::REG_0x0D,
+ gl847::REG_0x0D_CLRLNCNT | gl847::REG_0x0D_CLRMCNT);
+ break;
+ }
+ case AsicType::GL124:{
+ dev.interface->write_register(gl124::REG_0x0D,
+ gl124::REG_0x0D_CLRLNCNT | gl124::REG_0x0D_CLRMCNT);
+ break;
+ }
+ default:
+ throw SaneException("Unsupported asic type");
+ }
}
+void scanner_clear_scan_and_feed_counts2(Genesys_Device& dev)
+{
+ // FIXME: switch to scanner_clear_scan_and_feed_counts when updating tests
+ switch (dev.model->asic_type) {
+ case AsicType::GL843: {
+ dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRLNCNT);
+ dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRMCNT);
+ break;
+ }
+ case AsicType::GL845:
+ case AsicType::GL846: {
+ dev.interface->write_register(gl846::REG_0x0D, gl846::REG_0x0D_CLRLNCNT);
+ dev.interface->write_register(gl846::REG_0x0D, gl846::REG_0x0D_CLRMCNT);
+ break;
+ }
+ case AsicType::GL847: {
+ dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRLNCNT);
+ dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRMCNT);
+ break;
+ }
+ case AsicType::GL124: {
+ dev.interface->write_register(gl124::REG_0x0D, gl124::REG_0x0D_CLRLNCNT);
+ dev.interface->write_register(gl124::REG_0x0D, gl124::REG_0x0D_CLRMCNT);
+ break;
+ }
+ default:
+ throw SaneException("Unsupported asic type");
+ }
+}
-void
-sanei_genesys_calculate_zmode2 (SANE_Bool two_table,
- uint32_t exposure_time,
- uint16_t * slope_table,
- int reg21,
- int move, int reg22, uint32_t * z1,
- uint32_t * z2)
+bool scanner_is_motor_stopped(Genesys_Device& dev)
{
- int i;
- int sum;
- DBG(DBG_info, "%s: two_table=%d\n", __func__, two_table);
+ switch (dev.model->asic_type) {
+ case AsicType::GL646: {
+ auto status = scanner_read_status(dev);
+ return !status.is_motor_enabled && status.is_feeding_finished;
+ }
+ case AsicType::GL841: {
+ auto reg = dev.interface->read_register(gl841::REG_0x40);
- /* acceleration total time */
- sum = 0;
- for (i = 0; i < reg21; i++)
- sum += slope_table[i];
-
- /* compute Z1MOD */
- /* c=sum(slope_table;reg21)
- d=reg22*cruising speed
- Z1MOD=(c+d) % exposure_time */
- *z1 = (sum + reg22 * slope_table[reg21 - 1]) % exposure_time;
-
- /* compute Z2MOD */
- /* a=sum(slope_table;reg21), b=move or 1 if 2 tables */
- /* Z2MOD=(a+b) % exposure_time */
- if (!two_table)
- sum = sum + (move * slope_table[reg21 - 1]);
- else
- sum = sum + slope_table[reg21 - 1];
- *z2 = sum % exposure_time;
+ return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG));
+ }
+ case AsicType::GL843: {
+ auto status = scanner_read_status(dev);
+ auto reg = dev.interface->read_register(gl843::REG_0x40);
+
+ return (!(reg & gl843::REG_0x40_DATAENB) && !(reg & gl843::REG_0x40_MOTMFLG) &&
+ !status.is_motor_enabled);
+ }
+ case AsicType::GL845:
+ case AsicType::GL846: {
+ auto status = scanner_read_status(dev);
+ auto reg = dev.interface->read_register(gl846::REG_0x40);
+
+ return (!(reg & gl846::REG_0x40_DATAENB) && !(reg & gl846::REG_0x40_MOTMFLG) &&
+ !status.is_motor_enabled);
+ }
+ case AsicType::GL847: {
+ auto status = scanner_read_status(dev);
+ auto reg = dev.interface->read_register(gl847::REG_0x40);
+
+ return (!(reg & gl847::REG_0x40_DATAENB) && !(reg & gl847::REG_0x40_MOTMFLG) &&
+ !status.is_motor_enabled);
+ }
+ case AsicType::GL124: {
+ auto status = scanner_read_status(dev);
+ auto reg = dev.interface->read_register(gl124::REG_0x100);
+
+ return (!(reg & gl124::REG_0x100_DATAENB) && !(reg & gl124::REG_0x100_MOTMFLG) &&
+ !status.is_motor_enabled);
+ }
+ default:
+ throw SaneException("Unsupported asic type");
+ }
}
+void scanner_stop_action(Genesys_Device& dev)
+{
+ DBG_HELPER(dbg);
+
+ switch (dev.model->asic_type) {
+ case AsicType::GL843:
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847:
+ case AsicType::GL124:
+ break;
+ default:
+ throw SaneException("Unsupported asic type");
+ }
-/* huh? */
-/* todo: double check */
-/* Z1 and Z2 seem to be a time to synchronize with clock or a phase correction */
-/* steps_sum is the result of create_slope_table */
-/* last_speed is the last entry of the slope_table */
-/* feedl is registers 3d,3e,3f */
-/* fastfed is register 02 bit 3 */
-/* scanfed is register 1f */
-/* fwdstep is register 22 */
-/* tgtime is register 6c bit 6+7 >> 6 */
+ if (dev.cmd_set->needs_update_home_sensor_gpio()) {
+ dev.cmd_set->update_home_sensor_gpio(dev);
+ }
-void
-sanei_genesys_calculate_zmode (uint32_t exposure_time,
- uint32_t steps_sum, uint16_t last_speed,
- uint32_t feedl, uint8_t fastfed,
- uint8_t scanfed, uint8_t fwdstep,
- uint8_t tgtime, uint32_t * z1, uint32_t * z2)
+ if (scanner_is_motor_stopped(dev)) {
+ DBG(DBG_info, "%s: already stopped\n", __func__);
+ return;
+ }
+
+ scanner_stop_action_no_move(dev, dev.reg);
+
+ if (is_testing_mode()) {
+ return;
+ }
+
+ for (unsigned i = 0; i < 10; ++i) {
+ if (scanner_is_motor_stopped(dev)) {
+ return;
+ }
+
+ dev.interface->sleep_ms(100);
+ }
+
+ throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor");
+}
+
+void scanner_stop_action_no_move(Genesys_Device& dev, genesys::Genesys_Register_Set& regs)
+{
+ switch (dev.model->asic_type) {
+ case AsicType::GL646:
+ case AsicType::GL841:
+ case AsicType::GL843:
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847:
+ case AsicType::GL124:
+ break;
+ default:
+ throw SaneException("Unsupported asic type");
+ }
+
+ regs_set_optical_off(dev.model->asic_type, regs);
+ // same across all supported ASICs
+ dev.interface->write_register(0x01, regs.get8(0x01));
+
+ // looks like certain scanners lock up if we try to scan immediately after stopping previous
+ // action.
+ dev.interface->sleep_ms(100);
+}
+
+void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, Direction direction)
{
- uint8_t exposure_factor;
+ DBG_HELPER_ARGS(dbg, "steps=%d direction=%d", steps, static_cast<unsigned>(direction));
- exposure_factor = pow (2, tgtime); /* todo: originally, this is always 2^0 ! */
+ auto local_reg = dev.reg;
- /* Z1 is for buffer-full backward forward moving */
- *z1 =
- exposure_factor * ((steps_sum + fwdstep * last_speed) % exposure_time);
+ unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y();
- /* Z2 is for acceleration before scan */
- if (fastfed) /* two curve mode */
+ const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 3, scan_method);
+
+ bool uses_secondary_head = (scan_method == ScanMethod::TRANSPARENCY ||
+ scan_method == ScanMethod::TRANSPARENCY_INFRARED);
+ bool uses_secondary_pos = uses_secondary_head &&
+ dev.model->default_method == ScanMethod::FLATBED;
+
+ if (!dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
+ throw SaneException("Unknown head position");
+ }
+ if (uses_secondary_pos && !dev.is_head_pos_known(ScanHeadId::SECONDARY)) {
+ throw SaneException("Unknown head position");
+ }
+ if (direction == Direction::BACKWARD && steps > dev.head_pos(ScanHeadId::PRIMARY)) {
+ throw SaneException("Trying to feed behind the home position %d %d",
+ steps, dev.head_pos(ScanHeadId::PRIMARY));
+ }
+ if (uses_secondary_pos && direction == Direction::BACKWARD &&
+ steps > dev.head_pos(ScanHeadId::SECONDARY))
{
- *z2 =
- exposure_factor * ((steps_sum + scanfed * last_speed) %
- exposure_time);
+ throw SaneException("Trying to feed behind the home position %d %d",
+ steps, dev.head_pos(ScanHeadId::SECONDARY));
+ }
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 0;
+ session.params.starty = steps;
+ session.params.pixels = 100;
+ session.params.lines = 3;
+ session.params.depth = 8;
+ session.params.channels = 3;
+ session.params.scan_method = scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ if (dev.model->asic_type == AsicType::GL843) {
+ session.params.color_filter = ColorFilter::RED;
+ } else {
+ session.params.color_filter = dev.settings.color_filter;
+ }
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::FEEDING |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+
+ if (dev.model->asic_type == AsicType::GL124) {
+ session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
+ }
+
+ if (direction == Direction::BACKWARD) {
+ session.params.flags |= ScanFlag::REVERSE;
+ }
+
+ compute_session(&dev, session, sensor);
+
+ dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
+
+ if (dev.model->asic_type != AsicType::GL843) {
+ regs_set_exposure(dev.model->asic_type, local_reg, {0, 0, 0});
+ }
+ scanner_clear_scan_and_feed_counts2(dev);
+
+ dev.interface->write_registers(local_reg);
+ if (uses_secondary_head) {
+ gl843::gl843_set_xpa_motor_power(&dev, local_reg, true);
}
- else /* one curve mode */
+
+ try {
+ scanner_start_action(dev, true);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&]() {
+ gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ });
+ catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
+ // restore original registers
+ catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); });
+ throw;
+ }
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint("feed");
+
+ dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps);
+ if (uses_secondary_pos) {
+ dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);
+ }
+
+ // FIXME: why don't we stop the scanner like on other ASICs
+ if (dev.model->asic_type != AsicType::GL843) {
+ scanner_stop_action(dev);
+ }
+ if (uses_secondary_head) {
+ gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ }
+ return;
+ }
+
+ // wait until feed count reaches the required value
+ // FIXME: should porbably wait for some timeout
+ Status status;
+ for (unsigned i = 0;; ++i) {
+ status = scanner_read_status(dev);
+ if (status.is_feeding_finished || (
+ direction == Direction::BACKWARD && status.is_at_home))
+ {
+ break;
+ }
+ dev.interface->sleep_ms(10);
+ }
+
+ // FIXME: why don't we stop the scanner like on other ASICs
+ if (dev.model->asic_type != AsicType::GL843) {
+ scanner_stop_action(dev);
+ }
+ if (uses_secondary_head) {
+ gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ }
+
+ dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps);
+ if (uses_secondary_pos) {
+ dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);
+ }
+
+ // looks like certain scanners lock up if we scan immediately after feeding
+ dev.interface->sleep_ms(100);
+}
+
+void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
+{
+ DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home);
+
+ switch (dev.model->asic_type) {
+ case AsicType::GL843:
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847:
+ case AsicType::GL124:
+ break;
+ default:
+ throw SaneException("Unsupported asic type");
+ }
+
+ // FIXME: also check whether the scanner actually has a secondary head
+ if (!dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
+ dev.head_pos(ScanHeadId::SECONDARY) > 0 ||
+ dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ scanner_move_back_home_ta(dev);
+ }
+
+ if (dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
+ dev.head_pos(ScanHeadId::PRIMARY) > 1000)
{
- *z2 =
- exposure_factor * ((steps_sum + feedl * last_speed) % exposure_time);
+ // leave 500 steps for regular slow back home
+ scanner_move(dev, dev.model->default_method, dev.head_pos(ScanHeadId::PRIMARY) - 500,
+ Direction::BACKWARD);
+ }
+
+ if (dev.cmd_set->needs_update_home_sensor_gpio()) {
+ dev.cmd_set->update_home_sensor_gpio(dev);
}
+
+ auto status = scanner_read_reliable_status(dev);
+
+ if (status.is_at_home) {
+ dbg.log(DBG_info, "already at home");
+ dev.set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+
+ if (dev.model->model_id == ModelId::CANON_LIDE_210) {
+ // move the head back a little first
+ if (dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
+ dev.head_pos(ScanHeadId::PRIMARY) > 30)
+ {
+ scanner_move(dev, dev.model->default_method, 20, Direction::BACKWARD);
+ }
+ }
+
+ Genesys_Register_Set local_reg = dev.reg;
+ unsigned resolution = sanei_genesys_get_lowest_ydpi(&dev);
+
+ const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, dev.model->default_method);
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 100;
+ if (dev.model->asic_type == AsicType::GL843) {
+ session.params.starty = 40000;
+ } else {
+ session.params.starty = 30000;
+ }
+ session.params.pixels = 100;
+ session.params.lines = 100;
+ session.params.depth = 8;
+ session.params.channels = 1;
+ session.params.scan_method = dev.settings.scan_method;
+ if (dev.model->asic_type == AsicType::GL843) {
+ session.params.scan_mode = ScanColorMode::LINEART;
+ session.params.color_filter = dev.settings.color_filter;
+ } else {
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::RED;
+ }
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::REVERSE;
+ if (dev.model->asic_type == AsicType::GL843) {
+ session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
+ }
+
+ compute_session(&dev, session, sensor);
+
+ dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
+
+ scanner_clear_scan_and_feed_counts(dev);
+
+ dev.interface->write_registers(local_reg);
+
+ if (dev.model->asic_type == AsicType::GL124) {
+ gl124::gl124_setup_scan_gpio(&dev, resolution);
+ }
+
+ try {
+ scanner_start_action(dev, true);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
+ // restore original registers
+ catch_all_exceptions(__func__, [&]()
+ {
+ dev.interface->write_registers(dev.reg);
+ });
+ throw;
+ }
+
+ if (dev.cmd_set->needs_update_home_sensor_gpio()) {
+ dev.cmd_set->update_home_sensor_gpio(dev);
+ }
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint("move_back_home");
+ dev.set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+
+ if (wait_until_home) {
+ for (unsigned i = 0; i < 300; ++i) {
+ auto status = scanner_read_status(dev);
+
+ if (status.is_at_home) {
+ dbg.log(DBG_info, "reached home position");
+ if (dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847)
+ {
+ scanner_stop_action(dev);
+ }
+ dev.set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+
+ dev.interface->sleep_ms(100);
+ }
+
+ // when we come here then the scanner needed too much time for this, so we better stop
+ // the motor
+ catch_all_exceptions(__func__, [&](){ scanner_stop_action(dev); });
+ dev.set_head_pos_unknown();
+ throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
+ }
+ dbg.log(DBG_info, "scanhead is still moving");
}
+void scanner_move_back_home_ta(Genesys_Device& dev)
+{
+ DBG_HELPER(dbg);
+
+ switch (dev.model->asic_type) {
+ case AsicType::GL843:
+ break;
+ default:
+ throw SaneException("Unsupported asic type");
+ }
+
+ Genesys_Register_Set local_reg = dev.reg;
+
+ auto scan_method = ScanMethod::TRANSPARENCY;
+ unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y();
+
+ const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, scan_method);
+
+ if (dev.is_head_pos_known(ScanHeadId::SECONDARY) &&
+ dev.head_pos(ScanHeadId::SECONDARY) > 1000)
+ {
+ // leave 500 steps for regular slow back home
+ scanner_move(dev, scan_method, dev.head_pos(ScanHeadId::SECONDARY) - 500,
+ Direction::BACKWARD);
+ }
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 100;
+ session.params.starty = 30000;
+ session.params.pixels = 100;
+ session.params.lines = 100;
+ session.params.depth = 8;
+ session.params.channels = 1;
+ session.params.scan_method = scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::REVERSE;
+
+ compute_session(&dev, session, sensor);
+
+ dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
+
+ scanner_clear_scan_and_feed_counts(dev);
+
+ dev.interface->write_registers(local_reg);
+ gl843::gl843_set_xpa_motor_power(&dev, local_reg, true);
+
+ try {
+ scanner_start_action(dev, true);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
+ // restore original registers
+ catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); });
+ throw;
+ }
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint("move_back_home_ta");
+
+ if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
+ if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) {
+ dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD,
+ dev.head_pos(ScanHeadId::SECONDARY));
+ } else {
+ dev.set_head_pos_zero(ScanHeadId::PRIMARY);
+ }
+ dev.set_head_pos_zero(ScanHeadId::SECONDARY);
+ }
+
+ scanner_stop_action(dev);
+ gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ return;
+ }
+
+ for (unsigned i = 0; i < 1200; ++i) {
+
+ auto status = scanner_read_status(dev);
+
+ if (status.is_at_home) {
+ dbg.log(DBG_info, "TA reached home position");
+
+ if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
+ if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) {
+ dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD,
+ dev.head_pos(ScanHeadId::SECONDARY));
+ } else {
+ dev.set_head_pos_zero(ScanHeadId::PRIMARY);
+ }
+ dev.set_head_pos_zero(ScanHeadId::SECONDARY);
+ }
+
+ scanner_stop_action(dev);
+ gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ return;
+ }
+
+ dev.interface->sleep_ms(100);
+ }
+
+ throw SaneException("Timeout waiting for XPA lamp to park");
+}
+
+void sanei_genesys_calculate_zmod(bool two_table,
+ uint32_t exposure_time,
+ const std::vector<uint16_t>& slope_table,
+ unsigned acceleration_steps,
+ unsigned move_steps,
+ unsigned buffer_acceleration_steps,
+ uint32_t* out_z1, uint32_t* out_z2)
+{
+ DBG(DBG_info, "%s: two_table=%d\n", __func__, two_table);
+
+ // acceleration total time
+ unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps,
+ 0, std::plus<unsigned>());
+
+ /* Z1MOD:
+ c = sum(slope_table; reg_stepno)
+ d = reg_fwdstep * <cruising speed>
+ Z1MOD = (c+d) % exposure_time
+ */
+ *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time;
+
+ /* Z2MOD:
+ a = sum(slope_table; reg_stepno)
+ b = move_steps or 1 if 2 tables
+ Z1MOD = (a+b) % exposure_time
+ */
+ if (!two_table) {
+ sum = sum + (move_steps * slope_table[acceleration_steps - 1]);
+ } else {
+ sum = sum + slope_table[acceleration_steps - 1];
+ }
+ *out_z2 = sum % exposure_time;
+}
static uint8_t genesys_adjust_gain(double* applied_multi, double multi, uint8_t gain)
{
@@ -1338,7 +1333,7 @@ static uint8_t genesys_adjust_gain(double* applied_multi, double multi, uint8_t
voltage *= multi;
- new_gain = (uint8_t) ((voltage - 0.5) * 4);
+ new_gain = static_cast<std::uint8_t>((voltage - 0.5) * 4);
if (new_gain > 0x0e)
new_gain = 0x0e;
@@ -1353,23 +1348,25 @@ static uint8_t genesys_adjust_gain(double* applied_multi, double multi, uint8_t
}
-/* todo: is return status necessary (unchecked?) */
-static SANE_Status
-genesys_average_white (Genesys_Device * dev, Genesys_Sensor& sensor, int channels, int channel,
- uint8_t * data, int size, int *max_average)
+// todo: is return status necessary (unchecked?)
+static void genesys_average_white(Genesys_Device* dev, Genesys_Sensor& sensor, int channels,
+ int channel, uint8_t* data, int size, int *max_average)
{
+
+ DBG_HELPER_ARGS(dbg, "channels=%d, channel=%d, size=%d", channels, channel, size);
int gain_white_ref, sum, range;
int average;
int i;
- DBG(DBG_proc, "%s: channels=%d, channel=%d, size=%d\n", __func__, channels, channel, size);
-
range = size / 50;
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) /* transparency mode */
- gain_white_ref = sensor.fau_gain_white_ref * 256;
- else
- gain_white_ref = sensor.gain_white_ref * 256;
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ gain_white_ref = sensor.fau_gain_white_ref * 256;
+ } else {
+ gain_white_ref = sensor.gain_white_ref * 256;
+ }
if (range < 1)
range = 1;
@@ -1399,9 +1396,7 @@ genesys_average_white (Genesys_Device * dev, Genesys_Sensor& sensor, int channel
gain_white_ref);
if (*max_average >= gain_white_ref)
- return SANE_STATUS_INVAL;
-
- return SANE_STATUS_GOOD;
+ throw SaneException(SANE_STATUS_INVAL);
}
/* todo: understand, values are too high */
@@ -1437,49 +1432,36 @@ genesys_average_black (Genesys_Device * dev, int channel,
DBG(DBG_proc, "%s = %d\n", __func__, sum / pixels);
- return (int) (sum / pixels);
+ return sum / pixels;
}
-/* todo: check; it works but the lines 1, 2, and 3 are too dark even with the
- same offset and gain settings? */
-static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
+// todo: check; it works but the lines 1, 2, and 3 are too dark even with the
+// same offset and gain settings?
+static void genesys_coarse_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
{
- int size;
+ DBG_HELPER_ARGS(dbg, "scan_mode = %d", static_cast<unsigned>(dev->settings.scan_mode));
int black_pixels;
int white_average;
- int channels;
- SANE_Status status = SANE_STATUS_GOOD;
uint8_t offset[4] = { 0xa0, 0x00, 0xa0, 0x40 }; /* first value isn't used */
uint16_t white[12], dark[12];
int i, j;
- DBG(DBG_info, "%s (scan_mode = %d)\n", __func__, static_cast<unsigned>(dev->settings.scan_mode));
-
black_pixels = sensor.black_pixels
* dev->settings.xres / sensor.optical_res;
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
+ unsigned channels = dev->settings.get_channels();
- DBG(DBG_info, "channels %d y_size %d xres %d\n", channels, dev->model->y_size,
+ DBG(DBG_info, "channels %d y_size %f xres %d\n", channels, dev->model->y_size.value(),
dev->settings.xres);
- size =
- channels * 2 * SANE_UNFIX (dev->model->y_size) * dev->settings.xres /
- 25.4;
+ unsigned size = static_cast<unsigned>(channels * 2 * dev->model->y_size * dev->settings.xres /
+ MM_PER_INCH);
/* 1 1 mm 1/inch inch/mm */
std::vector<uint8_t> calibration_data(size);
std::vector<uint8_t> all_data(size * 4, 1);
- status = dev->model->cmd_set->set_fe(dev, sensor, AFE_INIT);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->set_fe(dev, sensor, AFE_INIT);
dev->frontend.set_gain(0, 2);
dev->frontend.set_gain(1, 2);
@@ -1502,11 +1484,15 @@ static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sens
double applied_multi;
double gain_white_ref;
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) /* Transparency */
- gain_white_ref = sensor.fau_gain_white_ref * 256;
- else
- gain_white_ref = sensor.gain_white_ref * 256;
- /* white and black are defined downwards */
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ gain_white_ref = sensor.fau_gain_white_ref * 256;
+ } else {
+ gain_white_ref = sensor.gain_white_ref * 256;
+ }
+
+ // white and black are defined downwards
uint8_t gain0 = genesys_adjust_gain(&applied_multi,
gain_white_ref / (white[0] - dark[0]),
@@ -1526,29 +1512,9 @@ static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sens
dev->frontend.set_gain(1, 2);
dev->frontend.set_gain(2, 2);
- status =
- sanei_genesys_fe_write_data(dev, 0x28, dev->frontend.get_gain(0));
- if (status != SANE_STATUS_GOOD) /* todo: this was 0x28 + 3 ? */
- {
- DBG(DBG_error, "%s: Failed to write gain[0]: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x29, dev->frontend.get_gain(1));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to write gain[1]: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x2a, dev->frontend.get_gain(2));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to write gain[2]: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->interface->write_fe_register(0x28, dev->frontend.get_gain(0));
+ dev->interface->write_fe_register(0x29, dev->frontend.get_gain(1));
+ dev->interface->write_fe_register(0x2a, dev->frontend.get_gain(2));
}
if (i == 3) /* last line */
@@ -1558,8 +1524,7 @@ static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sens
for (j = 0; j < 3; j++)
{
- x =
- (double) (dark[(i - 2) * 3 + j] -
+ x = static_cast<double>(dark[(i - 2) * 3 + j] -
dark[(i - 1) * 3 + j]) * 254 / (offset[i - 1] / 2 -
offset[i - 2] / 2);
y = x - x * (offset[i - 1] / 2) / 254 - dark[(i - 1) * 3 + j];
@@ -1574,29 +1539,9 @@ static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sens
dev->frontend.set_offset(j, curr_offset);
}
}
- status =
- sanei_genesys_fe_write_data(dev, 0x20, dev->frontend.get_offset(0));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to write offset[0]: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x21, dev->frontend.get_offset(1));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to write offset[1]: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x22, dev->frontend.get_offset(2));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to write offset[2]: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->interface->write_fe_register(0x20, dev->frontend.get_offset(0));
+ dev->interface->write_fe_register(0x21, dev->frontend.get_offset(1));
+ dev->interface->write_fe_register(0x22, dev->frontend.get_offset(2));
DBG(DBG_info,
"%s: doing scan: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__,
@@ -1607,51 +1552,35 @@ static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sens
dev->frontend.get_offset(1),
dev->frontend.get_offset(2));
- status =
- dev->model->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status =
- sanei_genesys_read_data_from_scanner (dev, calibration_data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("coarse_calibration");
+ dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ return;
+ }
+ sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size);
std::memcpy(all_data.data() + i * size, calibration_data.data(), size);
if (i == 3) /* last line */
{
std::vector<uint8_t> all_data_8(size * 4 / 2);
unsigned int count;
- for (count = 0; count < (unsigned int) (size * 4 / 2); count++)
- all_data_8[count] = all_data[count * 2 + 1];
- status =
- sanei_genesys_write_pnm_file("gl_coarse.pnm", all_data_8.data(), 8, channels, size / 6, 4);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ for (count = 0; count < static_cast<unsigned>(size * 4 / 2); count++) {
+ all_data_8[count] = all_data[count * 2 + 1];
+ }
+ sanei_genesys_write_pnm_file("gl_coarse.pnm", all_data_8.data(), 8, channels, size / 6, 4);
}
- status = dev->model->cmd_set->end_scan(dev, &dev->calib_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+
if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
{
for (j = 0; j < 3; j++)
{
- genesys_average_white (dev, sensor, 3, j, calibration_data.data(), size,
- &white_average);
+ genesys_average_white(dev, sensor, 3, j, calibration_data.data(), size, &white_average);
white[i * 3 + j] = white_average;
dark[i * 3 + j] =
genesys_average_black (dev, j, calibration_data.data(),
@@ -1662,54 +1591,12 @@ static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sens
}
else /* one color-component modes */
{
- genesys_average_white (dev, sensor, 1, 0, calibration_data.data(), size,
- &white_average);
+ genesys_average_white(dev, sensor, 1, 0, calibration_data.data(), size, &white_average);
white[i * 3 + 0] = white[i * 3 + 1] = white[i * 3 + 2] =
white_average;
dark[i * 3 + 0] = dark[i * 3 + 1] = dark[i * 3 + 2] =
genesys_average_black (dev, 0, calibration_data.data(), black_pixels);
}
-
- if (i == 3)
- {
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- /* todo: huh? */
- dev->dark[0] =
- (uint16_t) (1.6925 * dark[i * 3 + 0] + 0.1895 * 256);
- dev->dark[1] =
- (uint16_t) (1.4013 * dark[i * 3 + 1] + 0.3147 * 256);
- dev->dark[2] =
- (uint16_t) (1.2931 * dark[i * 3 + 2] + 0.1558 * 256);
- }
- else /* one color-component modes */
- {
- switch (dev->settings.color_filter)
- {
- case ColorFilter::RED:
- default:
- dev->dark[0] =
- (uint16_t) (1.6925 * dark[i * 3 + 0] +
- (1.1895 - 1.0) * 256);
- dev->dark[1] = dev->dark[2] = dev->dark[0];
- break;
-
- case ColorFilter::GREEN:
- dev->dark[1] =
- (uint16_t) (1.4013 * dark[i * 3 + 1] +
- (1.3147 - 1.0) * 256);
- dev->dark[0] = dev->dark[2] = dev->dark[1];
- break;
-
- case ColorFilter::BLUE:
- dev->dark[2] =
- (uint16_t) (1.2931 * dark[i * 3 + 2] +
- (1.1558 - 1.0) * 256);
- dev->dark[0] = dev->dark[1] = dev->dark[2];
- break;
- }
- }
- }
} /* for (i = 0; i < 4; i++) */
DBG(DBG_info, "%s: final: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__,
@@ -1719,150 +1606,130 @@ static SANE_Status genesys_coarse_calibration(Genesys_Device * dev, Genesys_Sens
dev->frontend.get_offset(0),
dev->frontend.get_offset(1),
dev->frontend.get_offset(2));
- DBGCOMPLETED;
-
- return status;
-}
-
-/* Averages image data.
- average_data and calibration_data are little endian 16 bit words.
- */
-static void
-genesys_average_data (uint8_t * average_data,
- uint8_t * calibration_data,
- uint32_t lines,
- uint32_t pixel_components_per_line)
-{
- uint32_t x, y;
- uint32_t sum;
-
- for (x = 0; x < pixel_components_per_line; x++)
- {
- sum = 0;
- for (y = 0; y < lines; y++)
- {
- sum += calibration_data[(x + y * pixel_components_per_line) * 2];
- sum +=
- calibration_data[(x + y * pixel_components_per_line) * 2 +
- 1] * 256;
- }
- sum /= lines;
- *average_data++ = sum & 255;
- *average_data++ = sum / 256;
- }
}
/**
* scans a white area with motor and lamp off to get the per CCD pixel offset
* that will be used to compute shading coefficient
* @param dev scanner's device
- * @return SANE_STATUS_GOOD if OK, else an error
*/
-static SANE_Status
-genesys_dark_shading_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor)
+static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ std::vector<std::uint16_t>& out_average_data,
+ bool is_dark, const std::string& log_filename_prefix)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER(dbg);
+
+ debug_dump(DBG_info, dev->calib_session);
+
size_t size;
uint32_t pixels_per_line;
uint8_t channels;
- SANE_Bool motor;
-
- DBGSTART;
/* end pixel - start pixel */
pixels_per_line = dev->calib_pixels;
channels = dev->calib_channels;
uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset;
- dev->average_size = channels * 2 * out_pixels_per_line;
- dev->dark_average_data.clear();
- dev->dark_average_data.resize(dev->average_size);
+ // FIXME: we set this during both dark and white calibration. A cleaner approach should
+ // probably be used
+ dev->average_size = channels * out_pixels_per_line;
+
+ out_average_data.clear();
+ out_average_data.resize(dev->average_size);
+
+ if (is_dark && dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) {
+ // FIXME: dark shading currently not supported on infrared transparency scans
+ return;
+ }
- // FIXME: the current calculation is likely incorrect on non-GENESYS_GL843 implementations,
+ // FIXME: the current calculation is likely incorrect on non-GL843 implementations,
// but this needs checking
if (dev->calib_total_bytes_to_read > 0) {
size = dev->calib_total_bytes_to_read;
- } else if (dev->model->asic_type == GENESYS_GL843) {
+ } else if (dev->model->asic_type == AsicType::GL843) {
size = channels * 2 * pixels_per_line * dev->calib_lines;
} else {
size = channels * 2 * pixels_per_line * (dev->calib_lines + 1);
}
- std::vector<uint8_t> calibration_data(size);
+ std::vector<uint16_t> calibration_data(size / 2);
- motor=SANE_TRUE;
+ bool motor = true;
if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
{
- motor=SANE_FALSE;
+ motor = false;
}
- /* turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
- * because they have a calibration sheet with a sufficient black strip */
- if (dev->model->is_sheetfed == SANE_FALSE)
- {
+ // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
+ // because they have a calibration sheet with a sufficient black strip
+ if (is_dark && !dev->model->is_sheetfed) {
sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, false);
sanei_genesys_set_motor_power(dev->calib_reg, motor);
- }
- else
- {
+ } else {
sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true);
sanei_genesys_set_motor_power(dev->calib_reg, motor);
}
- status =
- dev->model->cmd_set->bulk_write_register(dev, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
+ dev->interface->write_registers(dev->calib_reg);
+
+ if (is_dark) {
+ // wait some time to let lamp to get dark
+ dev->interface->sleep_ms(200);
+ } else if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) {
+ // make sure lamp is bright again
+ // FIXME: what about scanners that take a long time to warm the lamp?
+ dev->interface->sleep_ms(500);
}
- // wait some time to let lamp to get dark
- sanei_genesys_sleep_ms(200);
+ bool start_motor = !is_dark;
+ dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, start_motor);
- status = dev->model->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_read_data_from_scanner (dev, calibration_data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint(is_dark ? "dark_shading_calibration"
+ : "white_shading_calibration");
+ dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ return;
}
- status = dev->model->cmd_set->end_scan(dev, &dev->calib_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
+ sanei_genesys_read_data_from_scanner(dev, reinterpret_cast<std::uint8_t*>(calibration_data.data()),
+ size);
+
+ dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+
+ if (dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) {
+ for (std::size_t i = 0; i < size / 2; ++i) {
+ auto value = calibration_data[i];
+ value = ((value >> 8) & 0xff) | ((value << 8) & 0xff00);
+ calibration_data[i] = value;
+ }
}
- std::fill(dev->dark_average_data.begin(),
- dev->dark_average_data.begin() + dev->calib_pixels_offset * channels,
- 0x00);
+ std::fill(out_average_data.begin(),
+ out_average_data.begin() + dev->calib_pixels_offset * channels, 0);
- genesys_average_data(dev->dark_average_data.data() + dev->calib_pixels_offset * channels,
- calibration_data.data(),
- dev->calib_lines, pixels_per_line * channels);
+ compute_array_percentile_approx(out_average_data.data() + dev->calib_pixels_offset * channels,
+ calibration_data.data(),
+ dev->calib_lines, pixels_per_line * channels,
+ 0.5f);
- if (DBG_LEVEL >= DBG_data)
- {
- sanei_genesys_write_pnm_file("gl_black_shading.pnm", calibration_data.data(), 16,
- channels, pixels_per_line, dev->calib_lines);
- sanei_genesys_write_pnm_file("gl_black_average.pnm", dev->dark_average_data.data(), 16,
- channels, out_pixels_per_line, 1);
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file16((log_filename_prefix + "_shading.pnm").c_str(),
+ calibration_data.data(),
+ channels, pixels_per_line, dev->calib_lines);
+ sanei_genesys_write_pnm_file16((log_filename_prefix + "_average.pnm").c_str(),
+ out_average_data.data(),
+ channels, out_pixels_per_line, 1);
}
+}
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
+static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
+{
+ DBG_HELPER(dbg);
+ genesys_shading_calibration_impl(dev, sensor, dev->dark_average_data, true, "gl_black_");
}
-
/*
* this function builds dummy dark calibration data so that we can
* compute shading coefficient in a clean way
@@ -1870,22 +1737,20 @@ genesys_dark_shading_calibration(Genesys_Device * dev, const Genesys_Sensor& sen
* can be computed from previous calibration data (when doing offset
* calibration ?)
*/
-static SANE_Status
-genesys_dummy_dark_shading (Genesys_Device * dev, const Genesys_Sensor& sensor)
+static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor& sensor)
{
+ DBG_HELPER(dbg);
uint32_t pixels_per_line;
uint8_t channels;
- uint32_t x, skip, xend;
+ uint32_t skip, xend;
int dummy1, dummy2, dummy3; /* dummy black average per channel */
- DBGSTART;
-
pixels_per_line = dev->calib_pixels;
channels = dev->calib_channels;
uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset;
- dev->average_size = channels * 2 * out_pixels_per_line;
+ dev->average_size = channels * out_pixels_per_line;
dev->dark_average_data.clear();
dev->dark_average_data.resize(dev->average_size, 0);
@@ -1901,10 +1766,11 @@ genesys_dummy_dark_shading (Genesys_Device * dev, const Genesys_Sensor& sensor)
skip = 4;
xend = 68;
}
- if (dev->model->ccd_type==CCD_G4050
- || dev->model->ccd_type==CCD_CS4400F
- || dev->model->ccd_type==CCD_CS8400F
- || dev->model->ccd_type==CCD_KVSS080)
+ if (dev->model->sensor_id==SensorId::CCD_G4050 ||
+ dev->model->sensor_id==SensorId::CCD_HP_4850C
+ || dev->model->sensor_id==SensorId::CCD_CANON_4400F
+ || dev->model->sensor_id==SensorId::CCD_CANON_8400F
+ || dev->model->sensor_id==SensorId::CCD_KVSS080)
{
skip = 2;
xend = sensor.black_pixels;
@@ -1915,20 +1781,12 @@ genesys_dummy_dark_shading (Genesys_Device * dev, const Genesys_Sensor& sensor)
dummy2 = 0;
dummy3 = 0;
- for (x = skip + 1; x <= xend; x++)
- {
- dummy1 +=
- dev->white_average_data[channels * 2 * x] +
- 256 * dev->white_average_data[channels * 2 * x + 1];
- if (channels > 1)
- {
- dummy2 +=
- (dev->white_average_data[channels * 2 * x + 2] +
- 256 * dev->white_average_data[channels * 2 * x + 3]);
- dummy3 +=
- (dev->white_average_data[channels * 2 * x + 4] +
- 256 * dev->white_average_data[channels * 2 * x + 5]);
- }
+ for (unsigned x = skip + 1; x <= xend; x++) {
+ dummy1 += dev->white_average_data[channels * x];
+ if (channels > 1) {
+ dummy2 += dev->white_average_data[channels * x + 1];
+ dummy3 += dev->white_average_data[channels * x + 2];
+ }
}
dummy1 /= (xend - skip);
@@ -1940,176 +1798,63 @@ genesys_dummy_dark_shading (Genesys_Device * dev, const Genesys_Sensor& sensor)
DBG(DBG_proc, "%s: dummy1=%d, dummy2=%d, dummy3=%d \n", __func__, dummy1, dummy2, dummy3);
/* fill dark_average */
- for (x = 0; x < out_pixels_per_line; x++)
- {
- dev->dark_average_data[channels * 2 * x] = dummy1 & 0xff;
- dev->dark_average_data[channels * 2 * x + 1] = dummy1 >> 8;
- if (channels > 1)
- {
- dev->dark_average_data[channels * 2 * x + 2] = dummy2 & 0xff;
- dev->dark_average_data[channels * 2 * x + 3] = dummy2 >> 8;
- dev->dark_average_data[channels * 2 * x + 4] = dummy3 & 0xff;
- dev->dark_average_data[channels * 2 * x + 5] = dummy3 >> 8;
- }
+ for (unsigned x = 0; x < out_pixels_per_line; x++) {
+ dev->dark_average_data[channels * x] = dummy1;
+ if (channels > 1) {
+ dev->dark_average_data[channels * x + 1] = dummy2;
+ dev->dark_average_data[channels * x + 2] = dummy3;
+ }
}
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
}
-static SANE_Status
-genesys_white_shading_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor)
+static void genesys_repark_sensor_before_shading(Genesys_Device* dev)
{
- SANE_Status status = SANE_STATUS_GOOD;
- size_t size;
- uint32_t pixels_per_line;
- uint8_t channels;
- SANE_Bool motor;
-
- DBG(DBG_proc, "%s (lines = %d)\n", __func__, (unsigned int)dev->calib_lines);
-
- pixels_per_line = dev->calib_pixels;
- channels = dev->calib_channels;
-
- uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset;
-
- dev->white_average_data.clear();
- dev->white_average_data.resize(channels * 2 * out_pixels_per_line);
-
- // FIXME: the current calculation is likely incorrect on non-GENESYS_GL843 implementations,
- // but this needs checking
- if (dev->calib_total_bytes_to_read > 0) {
- size = dev->calib_total_bytes_to_read;
- } else if (dev->model->asic_type == GENESYS_GL843) {
- size = channels * 2 * pixels_per_line * dev->calib_lines;
- } else {
- size = channels * 2 * pixels_per_line * (dev->calib_lines + 1);
- }
-
- std::vector<uint8_t> calibration_data(size);
-
- motor=SANE_TRUE;
- if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
- {
- motor=SANE_FALSE;
- }
-
- // turn on motor and lamp power
- sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true);
- sanei_genesys_set_motor_power(dev->calib_reg, motor);
+ DBG_HELPER(dbg);
+ if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) {
+ dev->cmd_set->move_back_home(dev, true);
- /* if needed, go back before doing next scan */
- if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK)
- {
- /* rewind keeps registers and slopes table intact from previous
- scan but is not available on all supported chipsets (or may
- cause scan artifacts, see #7) */
- status = (dev->model->cmd_set->rewind
- ? dev->model->cmd_set->rewind (dev)
- : dev->model->cmd_set->slow_back_home (dev, SANE_TRUE));
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY)
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
- dev->model->cmd_set->move_to_ta(dev);
+ dev->cmd_set->move_to_ta(dev);
}
}
+}
- status =
- dev->model->cmd_set->bulk_write_register(dev, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)
- sanei_genesys_sleep_ms(500); // make sure lamp is bright again
-
- status = dev->model->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_read_data_from_scanner (dev, calibration_data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = dev->model->cmd_set->end_scan(dev, &dev->calib_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl_white_shading.pnm", calibration_data.data(), 16,
- channels, pixels_per_line, dev->calib_lines);
-
- std::fill(dev->dark_average_data.begin(),
- dev->dark_average_data.begin() + dev->calib_pixels_offset * channels,
- 0x00);
-
- genesys_average_data (dev->white_average_data.data() + dev->calib_pixels_offset * channels,
- calibration_data.data(), dev->calib_lines,
- pixels_per_line * channels);
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl_white_average.pnm", dev->white_average_data.data(), 16,
- channels, out_pixels_per_line, 1);
-
- /* in case we haven't done dark calibration, build dummy data from white_average */
- if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION))
- {
- status = genesys_dummy_dark_shading(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do dummy dark shading calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
-
- if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK)
- {
- status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE);
+static void genesys_repark_sensor_after_white_shading(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) {
+ dev->cmd_set->move_back_home(dev, true);
}
+}
- DBGCOMPLETED;
-
- return status;
+static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
+{
+ DBG_HELPER(dbg);
+ genesys_shading_calibration_impl(dev, sensor, dev->white_average_data, false, "gl_white_");
}
-/* This calibration uses a scan over the calibration target, comprising a
- * black and a white strip. (So the motor must be on.)
- */
-static SANE_Status
-genesys_dark_white_shading_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor)
+// This calibration uses a scan over the calibration target, comprising a black and a white strip.
+// (So the motor must be on.)
+static void genesys_dark_white_shading_calibration(Genesys_Device* dev,
+ const Genesys_Sensor& sensor)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER_ARGS(dbg, "lines = %zu", dev->calib_lines);
size_t size;
uint32_t pixels_per_line;
- uint8_t *average_white, *average_dark;
uint8_t channels;
unsigned int x;
- int y;
uint32_t dark, white, dark_sum, white_sum, dark_count, white_count, col,
dif;
- SANE_Bool motor;
-
-
- DBG(DBG_proc, "%s: (lines = %d)\n", __func__, (unsigned int)dev->calib_lines);
pixels_per_line = dev->calib_pixels;
channels = dev->calib_channels;
uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset;
- dev->average_size = channels * 2 * out_pixels_per_line;
+ dev->average_size = channels * out_pixels_per_line;
dev->white_average_data.clear();
dev->white_average_data.resize(dev->average_size);
@@ -2124,45 +1869,29 @@ genesys_dark_white_shading_calibration(Genesys_Device * dev, const Genesys_Senso
std::vector<uint8_t> calibration_data(size);
- motor=SANE_TRUE;
+ bool motor = true;
if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
{
- motor=SANE_FALSE;
+ motor = false;
}
// turn on motor and lamp power
sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true);
sanei_genesys_set_motor_power(dev->calib_reg, motor);
- status =
- dev->model->cmd_set->bulk_write_register(dev, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->interface->write_registers(dev->calib_reg);
- status = dev->model->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, SANE_FALSE);
+ dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("dark_white_shading_calibration");
+ dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ return;
}
- status = sanei_genesys_read_data_from_scanner (dev, calibration_data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size);
- status = dev->model->cmd_set->end_scan(dev, &dev->calib_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
if (DBG_LEVEL >= DBG_data)
{
@@ -2181,22 +1910,20 @@ genesys_dark_white_shading_calibration(Genesys_Device * dev, const Genesys_Senso
}
- std::fill(dev->dark_average_data.begin(),
- dev->dark_average_data.begin() + dev->calib_pixels_offset * channels,
- 0x00);
- std::fill(dev->white_average_data.begin(),
- dev->white_average_data.begin() + dev->calib_pixels_offset * channels,
- 0x00);
+ std::fill(dev->dark_average_data.begin(),
+ dev->dark_average_data.begin() + dev->calib_pixels_offset * channels, 0);
+ std::fill(dev->white_average_data.begin(),
+ dev->white_average_data.begin() + dev->calib_pixels_offset * channels, 0);
- average_white = dev->white_average_data.data() + dev->calib_pixels_offset * channels;
- average_dark = dev->dark_average_data.data() + dev->calib_pixels_offset * channels;
+ uint16_t* average_white = dev->white_average_data.data() + dev->calib_pixels_offset * channels;
+ uint16_t* average_dark = dev->dark_average_data.data() + dev->calib_pixels_offset * channels;
for (x = 0; x < pixels_per_line * channels; x++)
{
dark = 0xffff;
white = 0;
- for (y = 0; y < (int)dev->calib_lines; y++)
+ for (std::size_t y = 0; y < dev->calib_lines; y++)
{
col = calibration_data[(x + y * pixels_per_line * channels) * 2];
col |=
@@ -2220,7 +1947,7 @@ genesys_dark_white_shading_calibration(Genesys_Device * dev, const Genesys_Senso
white_count = 0;
white_sum = 0;
- for (y = 0; y < (int)dev->calib_lines; y++)
+ for (std::size_t y = 0; y < dev->calib_lines; y++)
{
col = calibration_data[(x + y * pixels_per_line * channels) * 2];
col |=
@@ -2243,26 +1970,16 @@ genesys_dark_white_shading_calibration(Genesys_Device * dev, const Genesys_Senso
dark_sum /= dark_count;
white_sum /= white_count;
- *average_dark++ = dark_sum & 255;
- *average_dark++ = dark_sum >> 8;
-
- *average_white++ = white_sum & 255;
- *average_white++ = white_sum >> 8;
+ *average_dark++ = dark_sum;
+ *average_white++ = white_sum;
}
- if (DBG_LEVEL >= DBG_data)
- {
- sanei_genesys_write_pnm_file("gl_white_average.pnm",
- dev->white_average_data.data(), 16, channels,
- out_pixels_per_line, 1);
- sanei_genesys_write_pnm_file("gl_dark_average.pnm",
- dev->dark_average_data.data(), 16, channels,
- out_pixels_per_line, 1);
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file16("gl_white_average.pnm", dev->white_average_data.data(),
+ channels, out_pixels_per_line, 1);
+ sanei_genesys_write_pnm_file16("gl_dark_average.pnm", dev->dark_average_data.data(),
+ channels, out_pixels_per_line, 1);
}
-
- DBGCOMPLETED;
-
- return SANE_STATUS_GOOD;
}
/* computes one coefficient given bright-dark value
@@ -2393,8 +2110,7 @@ compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor,
avgpixels = 15;
/* LiDE80 packs shading data */
- if(dev->model->ccd_type != CIS_CANONLIDE80)
- {
+ if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80) {
factor=1;
fill=avgpixels;
}
@@ -2420,21 +2136,10 @@ compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor,
br = 0;
for (i = 0; i < avgpixels; i++)
{
- /* dark data */
- dk +=
- (dev->dark_average_data[(x + i +
- pixels_per_line * j) *
- 2] |
- (dev->dark_average_data
- [(x + i + pixels_per_line * j) * 2 + 1] << 8));
-
- /* white data */
- br +=
- (dev->white_average_data[(x + i +
- pixels_per_line * j) *
- 2] |
- (dev->white_average_data
- [(x + i + pixels_per_line * j) * 2 + 1] << 8));
+ // dark data
+ dk += dev->dark_average_data[(x + i + pixels_per_line * j)];
+ // white data
+ br += dev->white_average_data[(x + i + pixels_per_line * j)];
}
br /= avgpixels;
@@ -2490,6 +2195,16 @@ compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor,
}
}
+static std::array<unsigned, 3> color_order_to_cmat(ColorOrder color_order)
+{
+ switch (color_order) {
+ case ColorOrder::RGB: return {0, 1, 2};
+ case ColorOrder::GBR: return {2, 0, 1};
+ default:
+ throw std::logic_error("Unknown color order");
+ }
+}
+
/**
* Computes shading coefficient using formula in data sheet. 16bit data values
* manipulated here are little endian. For now we assume deletion scanning type
@@ -2503,12 +2218,11 @@ compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor,
* @param coeff 4000h or 2000h depending on fast scan mode or not
* @param target value of the target code
*/
-static void
-compute_coefficients (Genesys_Device * dev,
+static void compute_coefficients(Genesys_Device * dev,
uint8_t * shading_data,
unsigned int pixels_per_line,
unsigned int channels,
- unsigned int cmat[3],
+ ColorOrder color_order,
int offset,
unsigned int coeff,
unsigned int target)
@@ -2520,6 +2234,8 @@ compute_coefficients (Genesys_Device * dev,
DBG(DBG_io, "%s: pixels_per_line=%d, coeff=0x%04x\n", __func__, pixels_per_line, coeff);
+ auto cmat = color_order_to_cmat(color_order);
+
/* compute start & end values depending of the offset */
if (offset < 0)
{
@@ -2539,13 +2255,11 @@ compute_coefficients (Genesys_Device * dev,
/* TODO if channels=1 , use filter to know the base addr */
ptr = shading_data + 4 * ((x + offset) * channels + cmat[c]);
- /* dark data */
- dk = dev->dark_average_data[x * 2 * channels + c * 2];
- dk += 256 * dev->dark_average_data[x * 2 * channels + c * 2 + 1];
+ // dark data
+ dk = dev->dark_average_data[x * channels + c];
- /* white data */
- br = dev->white_average_data[x * 2 * channels + c * 2];
- br += 256 * dev->white_average_data[x * 2 * channels + c * 2 + 1];
+ // white data
+ br = dev->white_average_data[x * channels + c];
/* compute coeff */
val=compute_coefficient(coeff,target,br-dk);
@@ -2576,14 +2290,13 @@ compute_coefficients (Genesys_Device * dev,
* @param coeff 4000h or 2000h depending on fast scan mode or not
* @param target white target value
*/
-static void
-compute_planar_coefficients (Genesys_Device * dev,
+static void compute_planar_coefficients(Genesys_Device * dev,
uint8_t * shading_data,
unsigned int factor,
unsigned int pixels_per_line,
unsigned int words_per_color,
unsigned int channels,
- unsigned int cmat[3],
+ ColorOrder color_order,
unsigned int offset,
unsigned int coeff,
unsigned int target)
@@ -2592,6 +2305,8 @@ compute_planar_coefficients (Genesys_Device * dev,
uint32_t x, c, i;
uint32_t val, dk, br;
+ auto cmat = color_order_to_cmat(color_order);
+
DBG(DBG_io, "%s: factor=%d, pixels_per_line=%d, words=0x%X, coeff=0x%04x\n", __func__, factor,
pixels_per_line, words_per_color, coeff);
for (c = 0; c < channels; c++)
@@ -2609,12 +2324,8 @@ compute_planar_coefficients (Genesys_Device * dev,
/* average case */
for(i=0;i<factor;i++)
{
- dk +=
- 256 * dev->dark_average_data[((x+i) + pixels_per_line * c) * 2 + 1];
- dk += dev->dark_average_data[((x+i) + pixels_per_line * c) * 2];
- br +=
- 256 * dev->white_average_data[((x+i) + pixels_per_line * c) * 2 + 1];
- br += dev->white_average_data[((x+i) + pixels_per_line * c) * 2];
+ dk += dev->dark_average_data[((x+i) + pixels_per_line * c)];
+ br += dev->white_average_data[((x+i) + pixels_per_line * c)];
}
dk /= factor;
br /= factor;
@@ -2650,7 +2361,7 @@ compute_shifted_coefficients (Genesys_Device * dev,
uint8_t * shading_data,
unsigned int pixels_per_line,
unsigned int channels,
- unsigned int cmat[3],
+ ColorOrder color_order,
int offset,
unsigned int coeff,
unsigned int target_dark,
@@ -2662,6 +2373,8 @@ compute_shifted_coefficients (Genesys_Device * dev,
uint8_t *ptr = shading_data + offset * 3 * 4; /* contain 16bit words in little endian */
unsigned int patch_cnt = offset * 3; /* at start, offset of first patch */
+ auto cmat = color_order_to_cmat(color_order);
+
x = dev->settings.xres;
if (sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres) > 1)
x *= 2; /* scanner is using half-ccd mode */
@@ -2691,10 +2404,8 @@ compute_shifted_coefficients (Genesys_Device * dev,
for (i = 0; i < avgpixels; i++) {
for (j = 0; j < channels; j++) {
- br_tmp[j] += (dev->white_average_data[((x + i) * channels + j) * 2] |
- (dev->white_average_data[((x + i) * channels + j) * 2 + 1] << 8));
- dk_tmp[i] += (dev->dark_average_data[((x + i) * channels + j) * 2] |
- (dev->dark_average_data[((x + i) * channels + j) * 2 + 1] << 8));
+ br_tmp[j] += dev->white_average_data[((x + i) * channels + j)];
+ dk_tmp[i] += dev->dark_average_data[((x + i) * channels + j)];
}
}
for (j = 0; j < channels; j++) {
@@ -2736,20 +2447,21 @@ compute_shifted_coefficients (Genesys_Device * dev,
}
}
-static SANE_Status
-genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sensor)
+static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_Sensor& sensor)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER(dbg);
+
+ if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) {
+ return;
+ }
+
uint32_t pixels_per_line;
uint8_t channels;
int o;
unsigned int length; /**> number of shading calibration data words */
unsigned int factor;
- unsigned int cmat[3]; /**> matrix of color channels */
unsigned int coeff, target_code, words_per_color = 0;
- DBGSTART;
-
pixels_per_line = dev->calib_pixels + dev->calib_pixels_offset;
channels = dev->calib_channels;
@@ -2781,7 +2493,7 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
/* special case, memory is aligned on 0x5400, this has yet to be explained */
/* could be 0xa800 because sensor is truly 2400 dpi, then halved because
* we only set 1200 dpi */
- if(dev->model->ccd_type==CIS_CANONLIDE80)
+ if(dev->model->sensor_id==SensorId::CIS_CANON_LIDE_80)
{
words_per_color = 0x5400;
}
@@ -2797,10 +2509,11 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
Wn = white average for column n
Dn = dark average for column n
*/
- if (dev->model->cmd_set->get_gain4_bit(&dev->calib_reg))
- coeff = 0x4000;
- else
- coeff = 0x2000;
+ if (get_registers_gain4_bit(dev->model->asic_type, dev->calib_reg)) {
+ coeff = 0x4000;
+ } else {
+ coeff = 0x2000;
+ }
/* compute avg factor */
if(dev->settings.xres>sensor.optical_res)
@@ -2812,24 +2525,21 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
factor=sensor.optical_res/dev->settings.xres;
}
- /* for GL646, shading data is planar if REG01_FASTMOD is set and
+ /* for GL646, shading data is planar if REG_0x01_FASTMOD is set and
* chunky if not. For now we rely on the fact that we know that
* each sensor is used only in one mode. Currently only the CIS_XP200
- * sets REG01_FASTMOD.
+ * sets REG_0x01_FASTMOD.
*/
/* TODO setup a struct in genesys_devices that
* will handle these settings instead of having this switch growing up */
- cmat[0] = 0;
- cmat[1] = 1;
- cmat[2] = 2;
- switch (dev->model->ccd_type)
+ switch (dev->model->sensor_id)
{
- case CCD_XP300:
- case CCD_ROADWARRIOR:
- case CCD_DP665:
- case CCD_DP685:
- case CCD_DSMOBILE600:
+ case SensorId::CCD_XP300:
+ case SensorId::CCD_ROADWARRIOR:
+ case SensorId::CCD_DP665:
+ case SensorId::CCD_DP685:
+ case SensorId::CCD_DSMOBILE600:
target_code = 0xdc00;
o = 4;
compute_planar_coefficients (dev,
@@ -2838,29 +2548,26 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
pixels_per_line,
words_per_color,
channels,
- cmat,
+ ColorOrder::RGB,
o,
coeff,
target_code);
break;
- case CIS_XP200:
+ case SensorId::CIS_XP200:
target_code = 0xdc00;
o = 2;
- cmat[0] = 2; /* red is last */
- cmat[1] = 0; /* green is first */
- cmat[2] = 1; /* blue is second */
compute_planar_coefficients (dev,
shading_data.data(),
1,
pixels_per_line,
words_per_color,
channels,
- cmat,
+ ColorOrder::GBR,
o,
coeff,
target_code);
break;
- case CCD_HP2300:
+ case SensorId::CCD_HP2300:
target_code = 0xdc00;
o = 2;
if(dev->settings.xres<=sensor.optical_res/2)
@@ -2871,12 +2578,12 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
shading_data.data(),
pixels_per_line,
3,
- cmat,
+ ColorOrder::RGB,
o,
coeff,
target_code);
break;
- case CCD_5345:
+ case SensorId::CCD_5345:
target_code = 0xe000;
o = 4;
if(dev->settings.xres<=sensor.optical_res/2)
@@ -2887,22 +2594,24 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
shading_data.data(),
pixels_per_line,
3,
- cmat,
+ ColorOrder::RGB,
o,
coeff,
target_code);
break;
- case CCD_HP3670:
- case CCD_HP2400:
+ case SensorId::CCD_HP3670:
+ case SensorId::CCD_HP2400:
target_code = 0xe000;
- /* offset is cksel dependent, but we can't use this in common code */
+ // offset is dependent on ccd_pixels_per_system_pixel(), but we couldn't use this in
+ // common code previously.
+ // FIXME: use sensor.ccd_pixels_per_system_pixel()
if(dev->settings.xres<=300)
{
- o = -10; /* OK for <=300 */
+ o = -10;
}
else if(dev->settings.xres<=600)
{
- o = -6; /* ok at 600 */
+ o = -6;
}
else
{
@@ -2912,47 +2621,49 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
shading_data.data(),
pixels_per_line,
3,
- cmat,
+ ColorOrder::RGB,
o,
coeff,
target_code);
break;
- case CCD_KVSS080:
- case CCD_PLUSTEK3800:
- case CCD_G4050:
- case CCD_CS4400F:
- case CCD_CS8400F:
- case CCD_CS8600F:
+ case SensorId::CCD_KVSS080:
+ case SensorId::CCD_PLUSTEK_OPTICBOOK_3800:
+ case SensorId::CCD_G4050:
+ case SensorId::CCD_HP_4850C:
+ case SensorId::CCD_CANON_4400F:
+ case SensorId::CCD_CANON_8400F:
+ case SensorId::CCD_CANON_8600F:
+ case SensorId::CCD_PLUSTEK_OPTICFILM_7200I:
+ case SensorId::CCD_PLUSTEK_OPTICFILM_7300:
+ case SensorId::CCD_PLUSTEK_OPTICFILM_7500I:
target_code = 0xe000;
o = 0;
compute_coefficients (dev,
shading_data.data(),
pixels_per_line,
3,
- cmat,
+ ColorOrder::RGB,
o,
coeff,
target_code);
break;
- case CIS_CANONLIDE700:
- case CIS_CANONLIDE100:
- case CIS_CANONLIDE200:
- case CIS_CANONLIDE110:
- case CIS_CANONLIDE120:
- case CIS_CANONLIDE210:
- case CIS_CANONLIDE220:
+ case SensorId::CIS_CANON_LIDE_700F:
+ case SensorId::CIS_CANON_LIDE_100:
+ case SensorId::CIS_CANON_LIDE_200:
+ case SensorId::CIS_CANON_LIDE_110:
+ case SensorId::CIS_CANON_LIDE_120:
+ case SensorId::CIS_CANON_LIDE_210:
+ case SensorId::CIS_CANON_LIDE_220:
/* TODO store this in a data struct so we avoid
* growing this switch */
- switch(dev->model->ccd_type)
+ switch(dev->model->sensor_id)
{
- case CIS_CANONLIDE110:
- case CIS_CANONLIDE120:
- case CIS_CANONLIDE210:
- case CIS_CANONLIDE220:
- target_code = 0xf000;
- break;
- case CIS_CANONLIDE700:
- target_code = 0xc000; /* from experimentation */
+ case SensorId::CIS_CANON_LIDE_110:
+ case SensorId::CIS_CANON_LIDE_120:
+ case SensorId::CIS_CANON_LIDE_210:
+ case SensorId::CIS_CANON_LIDE_220:
+ case SensorId::CIS_CANON_LIDE_700F:
+ target_code = 0xc000;
break;
default:
target_code = 0xdc00;
@@ -2967,12 +2678,12 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
pixels_per_line,
words_per_color,
channels,
- cmat,
+ ColorOrder::RGB,
0,
coeff,
target_code);
break;
- case CCD_CANONLIDE35:
+ case SensorId::CIS_CANON_LIDE_35:
compute_averaged_planar (dev, sensor,
shading_data.data(),
pixels_per_line,
@@ -2983,7 +2694,7 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
0xe000,
0x0a00);
break;
- case CIS_CANONLIDE80:
+ case SensorId::CIS_CANON_LIDE_80:
compute_averaged_planar (dev, sensor,
shading_data.data(),
pixels_per_line,
@@ -2994,12 +2705,12 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
0xe000,
0x0800);
break;
- case CCD_PLUSTEK_3600:
+ case SensorId::CCD_PLUSTEK_OPTICPRO_3600:
compute_shifted_coefficients (dev, sensor,
shading_data.data(),
pixels_per_line,
channels,
- cmat,
+ ColorOrder::RGB,
12, /* offset */
coeff,
0x0001, /* target_dark */
@@ -3007,21 +2718,13 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
256); /* patch_size: contigous extent */
break;
default:
- DBG (DBG_error, "%s: sensor %d not supported\n", __func__, dev->model->ccd_type);
- return SANE_STATUS_UNSUPPORTED;
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "sensor %d not supported",
+ static_cast<unsigned>(dev->model->sensor_id));
break;
}
- /* do the actual write of shading calibration data to the scanner */
- status = genesys_send_offset_and_shading (dev, sensor, shading_data.data(), length);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to send shading data: %s\n", __func__,
- sane_strstatus (status));
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
+ // do the actual write of shading calibration data to the scanner
+ genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length);
}
@@ -3035,20 +2738,21 @@ genesys_send_shading_coefficient(Genesys_Device * dev, const Genesys_Sensor& sen
static bool
genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
{
- DBGSTART;
+ DBG_HELPER(dbg);
+
+ // if no cache or no function to evaluate cache entry ther can be no match/
+ if (dev->calibration_cache.empty()) {
+ return false;
+ }
- /* if no cache or no function to evaluate cache entry ther can be no match */
- if (!dev->model->cmd_set->is_compatible_calibration
- || dev->calibration_cache.empty())
- return false;
+ auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings);
/* we walk the link list of calibration cache in search for a
* matching one */
for (auto& cache : dev->calibration_cache)
{
- if (dev->model->cmd_set->is_compatible_calibration(dev, sensor, &cache, SANE_FALSE))
- {
- dev->frontend = cache.frontend;
+ if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) {
+ dev->frontend = cache.frontend;
/* we don't restore the gamma fields */
sensor.exposure = cache.sensor.exposure;
@@ -3059,9 +2763,8 @@ genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
dev->dark_average_data = cache.dark_average_data;
dev->white_average_data = cache.white_average_data;
- if(dev->model->cmd_set->send_shading_data==NULL)
- {
- TIE(genesys_send_shading_coefficient(dev, sensor));
+ if (!dev->cmd_set->has_send_shading_data()) {
+ genesys_send_shading_coefficient(dev, sensor);
}
DBG(DBG_proc, "%s: restored\n", __func__);
@@ -3073,26 +2776,22 @@ genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
}
-static SANE_Status
-genesys_save_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor)
+static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
{
+ DBG_HELPER(dbg);
#ifdef HAVE_SYS_TIME_H
struct timeval time;
#endif
- DBGSTART;
-
- if (!dev->model->cmd_set->is_compatible_calibration)
- return SANE_STATUS_UNSUPPORTED;
+ auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings);
auto found_cache_it = dev->calibration_cache.end();
for (auto cache_it = dev->calibration_cache.begin(); cache_it != dev->calibration_cache.end();
cache_it++)
{
- if (dev->model->cmd_set->is_compatible_calibration(dev, sensor, &*cache_it, SANE_TRUE))
- {
- found_cache_it = cache_it;
- break;
+ if (sanei_genesys_is_compatible_calibration(dev, session, &*cache_it, true)) {
+ found_cache_it = cache_it;
+ break;
}
}
@@ -3109,7 +2808,7 @@ genesys_save_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor)
found_cache_it->dark_average_data = dev->dark_average_data;
found_cache_it->white_average_data = dev->white_average_data;
- found_cache_it->used_setup = dev->current_setup;
+ found_cache_it->params = session.params;
found_cache_it->frontend = dev->frontend;
found_cache_it->sensor = sensor;
@@ -3117,12 +2816,9 @@ genesys_save_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor)
found_cache_it->calib_channels = dev->calib_channels;
#ifdef HAVE_SYS_TIME_H
- gettimeofday(&time,NULL);
+ gettimeofday(&time, nullptr);
found_cache_it->last_calibration = time.tv_sec;
#endif
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
}
/**
@@ -3131,194 +2827,145 @@ genesys_save_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor)
* - gain calibration
* - shading calibration
* @param dev device to calibrate
- * @return SANE_STATUS_GOOD if everything when all right, else the error code.
*/
-static SANE_Status
-genesys_flatbed_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
+static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER(dbg);
uint32_t pixels_per_line;
- int yres;
- DBG(DBG_info, "%s\n", __func__);
+ unsigned coarse_res = sensor.optical_res;
+ if (dev->settings.yres <= sensor.optical_res / 2) {
+ coarse_res /= 2;
+ }
- yres = sensor.optical_res;
- if (dev->settings.yres <= sensor.optical_res / 2)
- yres /= 2;
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ coarse_res = 1600;
+ }
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- yres = 1200;
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ coarse_res = 1200;
+ }
/* do offset calibration if needed */
if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
{
- status = dev->model->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: offset calibration failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->interface->record_progress_message("offset_calibration");
+ dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
/* since all the registers are set up correctly, just use them */
- status = dev->model->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, yres);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: coarse gain calibration: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- }
- else
+ dev->interface->record_progress_message("coarse_gain_calibration");
+ dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, coarse_res);
+ } else {
/* since we have 2 gain calibration proc, skip second if first one was
used. */
- {
- status = dev->model->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send calibration registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- status = genesys_coarse_calibration(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do coarse gain calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ dev->interface->record_progress_message("init_regs_for_coarse_calibration");
+ dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
+ dev->interface->record_progress_message("genesys_coarse_calibration");
+ genesys_coarse_calibration(dev, sensor);
}
if (dev->model->is_cis)
{
/* the afe now sends valid data for doing led calibration */
- status = dev->model->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: led calibration failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->interface->record_progress_message("led_calibration");
+ switch (dev->model->asic_type) {
+ case AsicType::GL124:
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847: {
+ auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
+ for (auto& sensor_update :
+ sanei_genesys_find_sensors_all_for_write(dev, sensor.method)) {
+ sensor_update.get().exposure = calib_exposure;
+ }
+ sensor.exposure = calib_exposure;
+ break;
+ }
+ default: {
+ sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
+ }
+ }
+
/* calibrate afe again to match new exposure */
- if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
- {
- status = dev->model->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: offset calibration failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) {
+ dev->interface->record_progress_message("offset_calibration");
+ dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
- /* since all the registers are set up correctly, just use them */
+ // since all the registers are set up correctly, just use them
- status = dev->model->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, yres);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: coarse gain calibration: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
- else
- /* since we have 2 gain calibration proc, skip second if first one was
- used. */
- {
- status = dev->model->cmd_set->init_regs_for_coarse_calibration(dev, sensor,
- dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send calibration registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ dev->interface->record_progress_message("coarse_gain_calibration");
+ dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, coarse_res);
+ } else {
+ // since we have 2 gain calibration proc, skip second if first one was used
+ dev->interface->record_progress_message("init_regs_for_coarse_calibration");
+ dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
- status = genesys_coarse_calibration(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do static calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
+ dev->interface->record_progress_message("genesys_coarse_calibration");
+ genesys_coarse_calibration(dev, sensor);
+ }
}
/* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */
if (!(dev->model->flags & GENESYS_FLAG_SIS_SENSOR))
{
- pixels_per_line = (SANE_UNFIX (dev->model->x_size) * dev->settings.xres) / MM_PER_INCH;
+ pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size * dev->settings.xres) /
+ MM_PER_INCH);
}
else
{
pixels_per_line = sensor.sensor_pixels;
}
- /* send default shading data */
- status = sanei_genesys_init_shading_data(dev, sensor, pixels_per_line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to init shading process: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ // send default shading data
+ dev->interface->record_progress_message("sanei_genesys_init_shading_data");
+ sanei_genesys_init_shading_data(dev, sensor, pixels_per_line);
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
- RIE(dev->model->cmd_set->move_to_ta(dev));
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ dev->cmd_set->move_to_ta(dev);
}
- /* shading calibration */
- status = dev->model->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ // shading calibration
+ if (dev->model->flags & GENESYS_FLAG_DARK_WHITE_CALIBRATION) {
+ dev->interface->record_progress_message("init_regs_for_shading");
+ dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
- if (dev->model->flags & GENESYS_FLAG_DARK_WHITE_CALIBRATION)
- {
- status = genesys_dark_white_shading_calibration (dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do dark+white shading calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
- else
- {
- if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)
- {
- status = genesys_dark_shading_calibration(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do dark shading calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
+ dev->interface->record_progress_message("genesys_dark_white_shading_calibration");
+ genesys_dark_white_shading_calibration(dev, sensor);
+ } else {
+ DBG(DBG_proc, "%s : genesys_dark_shading_calibration dev->calib_reg ", __func__);
+ debug_dump(DBG_proc, dev->calib_reg);
- status = genesys_white_shading_calibration (dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do white shading calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
+ if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) {
+ dev->interface->record_progress_message("init_regs_for_shading");
+ dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
- if(dev->model->cmd_set->send_shading_data==NULL)
- {
- status = genesys_send_shading_coefficient(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading calibration coefficients: %s\n", __func__,
- sane_strstatus(status));
- return status;
+ dev->interface->record_progress_message("genesys_dark_shading_calibration");
+ genesys_dark_shading_calibration(dev, sensor);
+ genesys_repark_sensor_before_shading(dev);
}
- }
- DBG(DBG_info, "%s: completed\n", __func__);
+ dev->interface->record_progress_message("init_regs_for_shading2");
+ dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
- return SANE_STATUS_GOOD;
+ dev->interface->record_progress_message("genesys_white_shading_calibration");
+ genesys_white_shading_calibration(dev, sensor);
+ genesys_repark_sensor_after_white_shading(dev);
+
+ if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) {
+ genesys_dummy_dark_shading(dev, sensor);
+ }
+ }
+
+ if (!dev->cmd_set->has_send_shading_data()) {
+ dev->interface->record_progress_message("genesys_send_shading_coefficient");
+ genesys_send_shading_coefficient(dev, sensor);
+ }
}
/**
@@ -3329,104 +2976,56 @@ genesys_flatbed_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
* During calibration a predefined calibration sheet with specific black and white
* areas is used.
* @param dev device to calibrate
- * @return SANE_STATUS_GOOD if everything when all right, else the error code.
*/
-static SANE_Status genesys_sheetfed_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
+static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
{
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Bool forward = SANE_TRUE;
- int xres;
-
- DBGSTART;
- if (dev->model->cmd_set->search_strip == NULL)
- {
- DBG(DBG_error, "%s: no strip searching function available\n", __func__);
- return SANE_STATUS_UNSUPPORTED;
- }
-
- /* first step, load document */
- status = dev->model->cmd_set->load_document (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to load document: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
+ DBG_HELPER(dbg);
+ bool forward = true;
- DBG(DBG_info, "%s\n", __func__);
+ // first step, load document
+ dev->cmd_set->load_document(dev);
/* led, offset and gain calibration are influenced by scan
* settings. So we set it to sensor resolution */
- xres = sensor.optical_res;
dev->settings.xres = sensor.optical_res;
/* XP200 needs to calibrate a full and half sensor's resolution */
- if (dev->model->ccd_type == CIS_XP200
- && dev->settings.xres <= sensor.optical_res / 2)
- dev->settings.xres /= 2;
+ if (dev->model->sensor_id == SensorId::CIS_XP200 &&
+ dev->settings.xres <= sensor.optical_res / 2)
+ {
+ dev->settings.xres /= 2;
+ }
/* the afe needs to sends valid data even before calibration */
/* go to a white area */
try {
- status = dev->model->cmd_set->search_strip(dev, sensor, forward, SANE_FALSE);
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: failed to find white strip: %s\n", __func__,
- sane_strstatus(status));
- dev->model->cmd_set->eject_document (dev);
- return status;
- }
+ dev->cmd_set->search_strip(dev, sensor, forward, false);
} catch (...) {
- dev->model->cmd_set->eject_document(dev);
+ catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
}
if (dev->model->is_cis)
{
- status = dev->model->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: led calibration failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
}
/* calibrate afe */
if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
{
- status = dev->model->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: offset calibration failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
/* since all the registers are set up correctly, just use them */
- status = dev->model->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, xres);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: coarse gain calibration: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, sensor.optical_res);
}
else
/* since we have 2 gain calibration proc, skip second if first one was
used. */
{
- status = dev->model->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send calibration registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
- status = genesys_coarse_calibration(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do static calibration: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ genesys_coarse_calibration(dev, sensor);
}
/* search for a full width black strip and then do a 16 bit scan to
@@ -3435,81 +3034,49 @@ static SANE_Status genesys_sheetfed_calibration(Genesys_Device * dev, Genesys_Se
{
/* seek black/white reverse/forward */
try {
- status = dev->model->cmd_set->search_strip(dev, sensor, forward, SANE_TRUE);
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: failed to find black strip: %s\n", __func__,
- sane_strstatus(status));
- dev->model->cmd_set->eject_document(dev);
- return status;
- }
+ dev->cmd_set->search_strip(dev, sensor, forward, true);
} catch (...) {
- dev->model->cmd_set->eject_document(dev);
+ catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
}
- status = dev->model->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do set up registers for shading calibration: %s\n",
- __func__, sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
+
try {
- status = genesys_dark_shading_calibration(dev, sensor);
- if (status != SANE_STATUS_GOOD) {
- dev->model->cmd_set->eject_document(dev);
- DBG(DBG_error, "%s: failed to do dark shading calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ genesys_dark_shading_calibration(dev, sensor);
} catch (...) {
- dev->model->cmd_set->eject_document(dev);
+ catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
}
- forward = SANE_FALSE;
+ forward = false;
}
/* go to a white area */
try {
- status = dev->model->cmd_set->search_strip(dev, sensor, forward, SANE_FALSE);
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: failed to find white strip: %s\n", __func__,
- sane_strstatus(status));
- dev->model->cmd_set->eject_document (dev);
- return status;
- }
+ dev->cmd_set->search_strip(dev, sensor, forward, false);
} catch (...) {
- dev->model->cmd_set->eject_document (dev);
+ catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
}
- status = dev->model->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do set up registers for shading calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ genesys_repark_sensor_before_shading(dev);
+
+ dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
try {
- status = genesys_white_shading_calibration(dev, sensor);
- if (status != SANE_STATUS_GOOD) {
- dev->model->cmd_set->eject_document(dev);
- DBG(DBG_error, "%s: failed eject target: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ genesys_white_shading_calibration(dev, sensor);
+ genesys_repark_sensor_after_white_shading(dev);
} catch (...) {
- dev->model->cmd_set->eject_document (dev);
+ catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
}
- /* in case we haven't black shading data, build it from black pixels
- * of white calibration */
- if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION))
- {
+ // in case we haven't black shading data, build it from black pixels of white calibration
+ // FIXME: shouldn't we use genesys_dummy_dark_shading() ?
+ if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) {
dev->dark_average_data.clear();
- dev->dark_average_data.resize(dev->average_size, 0x0f);
+ dev->dark_average_data.resize(dev->average_size, 0x0f0f);
/* XXX STEF XXX
* with black point in white shading, build an average black
* pixel and use it to fill the dark_average
@@ -3521,78 +3088,33 @@ static SANE_Status genesys_sheetfed_calibration(Genesys_Device * dev, Genesys_Se
/* send the shading coefficient when doing whole line shading
* but not when using SHDAREA like GL124 */
- if(dev->model->cmd_set->send_shading_data==NULL)
- {
- status = genesys_send_shading_coefficient(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading calibration coefficients: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ if (!dev->cmd_set->has_send_shading_data()) {
+ genesys_send_shading_coefficient(dev, sensor);
}
- /* save the calibration data */
- genesys_save_calibration (dev, sensor);
+ // save the calibration data
+ genesys_save_calibration(dev, sensor);
- /* and finally eject calibration sheet */
- status = dev->model->cmd_set->eject_document (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to eject document: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ // and finally eject calibration sheet
+ dev->cmd_set->eject_document(dev);
- /* resotre settings */
- dev->settings.xres = xres;
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
+ // restore settings
+ dev->settings.xres = sensor.optical_res;
}
/**
* does the calibration process for a device
* @param dev device to calibrate
*/
-static SANE_Status
-genesys_scanner_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
+static void genesys_scanner_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
{
- if (dev->model->is_sheetfed == SANE_FALSE)
- {
- return genesys_flatbed_calibration (dev, sensor);
- }
- return genesys_sheetfed_calibration(dev, sensor);
-}
-
-/* unused function kept in case it may be usefull in the futur */
-#if 0
-static SANE_Status
-genesys_wait_not_moving (Genesys_Device * dev, int mseconds)
-{
- uint8_t value;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s: waiting %d mseconds for motor to stop\n", __func__, mseconds);
- while (mseconds > 0)
- {
- RIE (sanei_genesys_get_status (dev, &value));
-
- if (dev->model->cmd_set->test_motor_flag_bit (value))
- {
- sanei_genesys_sleep_ms(100);
- mseconds -= 100;
- DBG(DBG_io, "%s: motor is moving, %d mseconds to go\n", __func__, mseconds);
- }
- else
- {
- DBG(DBG_info, "%s: motor is not moving, exiting\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
+ DBG_HELPER(dbg);
+ if (!dev->model->is_sheetfed) {
+ genesys_flatbed_calibration(dev, sensor);
+ return;
}
- DBG(DBG_error, "%s: motor is still moving, timeout exceeded\n", __func__);
- return SANE_STATUS_DEVICE_BUSY;
+ genesys_sheetfed_calibration(dev, sensor);
}
-#endif
/* ------------------------------------------------------------------------ */
@@ -3603,73 +3125,60 @@ genesys_wait_not_moving (Genesys_Device * dev, int mseconds)
* wait lamp to be warm enough by scanning the same line until
* differences between two scans are below a threshold
*/
-static SANE_Status
-genesys_warmup_lamp (Genesys_Device * dev)
+static void genesys_warmup_lamp(Genesys_Device* dev)
{
- int seconds = 0;
+ DBG_HELPER(dbg);
+ unsigned seconds = 0;
int pixel;
int channels, total_size;
double first_average = 0;
double second_average = 0;
int difference = 255;
- int empty, lines = 3;
- SANE_Status status = SANE_STATUS_IO_ERROR;
-
- DBGSTART;
-
- /* check if the current chipset implements warmup */
- if(dev->model->cmd_set->init_regs_for_warmup==NULL)
- {
- DBG(DBG_error,"%s: init_regs_for_warmup not implemented\n", __func__);
- return status;
- }
+ int lines = 3;
const auto& sensor = sanei_genesys_find_sensor_any(dev);
- dev->model->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg, &channels, &total_size);
+ dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg, &channels, &total_size);
std::vector<uint8_t> first_line(total_size);
std::vector<uint8_t> second_line(total_size);
do
{
DBG(DBG_info, "%s: one more loop\n", __func__);
- RIE(dev->model->cmd_set->begin_scan(dev, sensor, &dev->reg, SANE_FALSE));
- do
- {
- sanei_genesys_test_buffer_empty (dev, &empty);
- }
- while (empty);
+ dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("warmup_lamp");
+ dev->cmd_set->end_scan(dev, &dev->reg, true);
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
try {
- status = sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
- if (status != SANE_STATUS_GOOD) {
- RIE(sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size));
- }
+ sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
} catch (...) {
- RIE(sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size));
+ // FIXME: document why this retry is here
+ sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
}
- RIE(dev->model->cmd_set->end_scan(dev, &dev->reg, SANE_TRUE));
+ dev->cmd_set->end_scan(dev, &dev->reg, true);
- sanei_genesys_sleep_ms(1000);
+ dev->interface->sleep_ms(1000);
seconds++;
- RIE(dev->model->cmd_set->begin_scan(dev, sensor, &dev->reg, SANE_FALSE));
- do
- {
- sanei_genesys_test_buffer_empty (dev, &empty);
- sanei_genesys_sleep_ms(100);
- }
- while (empty);
- RIE(sanei_genesys_read_data_from_scanner (dev, second_line.data(), total_size));
- RIE(dev->model->cmd_set->end_scan(dev, &dev->reg, SANE_TRUE));
+ dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false);
+
+ wait_until_buffer_non_empty(dev);
+
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+ dev->cmd_set->end_scan(dev, &dev->reg, true);
/* compute difference between the two scans */
for (pixel = 0; pixel < total_size; pixel++)
{
- /* 16 bit data */
- if (dev->model->cmd_set->get_bitset_bit(&dev->reg))
- {
+ // 16 bit data
+ if (dev->session.params.depth == 16) {
first_average += (first_line[pixel] + first_line[pixel + 1] * 256);
second_average += (second_line[pixel] + second_line[pixel + 1] * 256);
pixel++;
@@ -3680,11 +3189,10 @@ genesys_warmup_lamp (Genesys_Device * dev)
second_average += second_line[pixel];
}
}
- if (dev->model->cmd_set->get_bitset_bit(&dev->reg))
- {
+ if (dev->session.params.depth == 16) {
first_average /= pixel;
second_average /= pixel;
- difference = fabs (first_average - second_average);
+ difference = static_cast<int>(std::fabs(first_average - second_average));
DBG(DBG_info, "%s: average = %.2f, diff = %.3f\n", __func__,
100 * ((second_average) / (256 * 256)),
100 * (difference / second_average));
@@ -3713,162 +3221,93 @@ genesys_warmup_lamp (Genesys_Device * dev)
}
/* sleep another second before next loop */
- sanei_genesys_sleep_ms(1000);
- seconds++;
- }
- while (seconds < WARMUP_TIME);
+ dev->interface->sleep_ms(1000);
+ seconds++;
+ } while (seconds < WARMUP_TIME);
if (seconds >= WARMUP_TIME)
{
- DBG(DBG_error, "%s: warmup timed out after %d seconds. Lamp defective?\n", __func__, seconds);
- status = SANE_STATUS_IO_ERROR;
+ throw SaneException(SANE_STATUS_IO_ERROR,
+ "warmup timed out after %d seconds. Lamp defective?", seconds);
}
else
{
DBG(DBG_info, "%s: warmup succeeded after %d seconds\n", __func__, seconds);
}
-
- DBGCOMPLETED;
-
- return status;
}
-/* High-level start of scanning */
-static SANE_Status
-genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off)
+// High-level start of scanning
+static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER(dbg);
unsigned int steps, expected;
- SANE_Bool empty;
-
- DBGSTART;
/* since not all scanners are set ot wait for head to park
* we check we are not still parking before starting a new scan */
- if (dev->parking == SANE_TRUE)
- {
- status = sanei_genesys_wait_for_home (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to wait for head to park: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ if (dev->parking) {
+ sanei_genesys_wait_for_home(dev);
}
- /* disable power saving*/
- status = dev->model->cmd_set->save_power (dev, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to disable power saving mode: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ // disable power saving
+ dev->cmd_set->save_power(dev, false);
/* wait for lamp warmup : until a warmup for TRANSPARENCY is designed, skip
* it when scanning from XPA. */
if (!(dev->model->flags & GENESYS_FLAG_SKIP_WARMUP)
&& (dev->settings.scan_method == ScanMethod::FLATBED))
{
- RIE (genesys_warmup_lamp (dev));
+ genesys_warmup_lamp(dev);
}
/* set top left x and y values by scanning the internals if flatbed scanners */
- if (dev->model->is_sheetfed == SANE_FALSE)
- {
+ if (!dev->model->is_sheetfed) {
/* do the geometry detection only once */
if ((dev->model->flags & GENESYS_FLAG_SEARCH_START)
- && (dev->model->y_offset_calib == 0))
+ && (dev->model->y_offset_calib_white == 0))
{
- status = dev->model->cmd_set->search_start_position (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to search start position: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->search_start_position (dev);
- dev->parking = SANE_FALSE;
- status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move scanhead to home position: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- dev->scanhead_position_in_steps = 0;
+ dev->parking = false;
+ dev->cmd_set->move_back_home(dev, true);
}
else
{
/* Go home */
/* TODO: check we can drop this since we cannot have the
scanner's head wandering here */
- dev->parking = SANE_FALSE;
- status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move scanhead to home position: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- dev->scanhead_position_in_steps = 0;
+ dev->parking = false;
+ dev->cmd_set->move_back_home(dev, true);
}
}
/* move to calibration area for transparency adapter */
- if ((dev->settings.scan_method == ScanMethod::TRANSPARENCY)
- && dev->model->cmd_set->move_to_ta != NULL)
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
- status=dev->model->cmd_set->move_to_ta(dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move to start of transparency adapter: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ dev->cmd_set->move_to_ta(dev);
}
/* load document if needed (for sheetfed scanner for instance) */
- if (dev->model->is_sheetfed == SANE_TRUE
- && dev->model->cmd_set->load_document != NULL)
- {
- status = dev->model->cmd_set->load_document (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to load document: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ if (dev->model->is_sheetfed) {
+ dev->cmd_set->load_document(dev);
}
auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres,
+ dev->settings.get_channels(),
dev->settings.scan_method);
- /* send gamma tables. They have been set to device or user value
- * when setting option value */
- status = dev->model->cmd_set->send_gamma_table(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to init gamma table: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ // send gamma tables. They have been set to device or user value
+ // when setting option value */
+ dev->cmd_set->send_gamma_table(dev, sensor);
/* try to use cached calibration first */
if (!genesys_restore_calibration (dev, sensor))
{
/* calibration : sheetfed scanners can't calibrate before each scan */
/* and also those who have the NO_CALIBRATION flag */
- if (!(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)
- &&dev->model->is_sheetfed == SANE_FALSE)
- {
- status = genesys_scanner_calibration(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do scanner calibration: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
+ if (!(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION) && !dev->model->is_sheetfed) {
+ genesys_scanner_calibration(dev, sensor);
genesys_save_calibration (dev, sensor);
}
else
@@ -3878,76 +3317,47 @@ genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off)
}
/* build look up table for dynamic lineart */
- if(dev->settings.dynamic_lineart==SANE_TRUE)
- {
- status = sanei_genesys_load_lut(dev->lineart_lut, 8, 8, 50, 205,
- dev->settings.threshold_curve,
- dev->settings.threshold-127);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to build lut\n", __func__);
- return status;
- }
+ if (dev->settings.scan_mode == ScanColorMode::LINEART) {
+ sanei_genesys_load_lut(dev->lineart_lut, 8, 8, 50, 205, dev->settings.threshold_curve,
+ dev->settings.threshold-127);
}
- if (dev->model->cmd_set->wait_for_motor_stop) {
- dev->model->cmd_set->wait_for_motor_stop(dev);
- }
-
- if (dev->model->cmd_set->needs_home_before_init_regs_for_scan &&
- dev->model->cmd_set->needs_home_before_init_regs_for_scan(dev) &&
- dev->model->cmd_set->slow_back_home)
- {
- RIE(dev->model->cmd_set->slow_back_home(dev, SANE_TRUE));
- }
+ dev->cmd_set->wait_for_motor_stop(dev);
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
- RIE(dev->model->cmd_set->move_to_ta(dev));
+ if (dev->cmd_set->needs_home_before_init_regs_for_scan(dev)) {
+ dev->cmd_set->move_back_home(dev, true);
}
- status = dev->model->cmd_set->init_regs_for_scan(dev, sensor);
- if (status != SANE_STATUS_GOOD)
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
- DBG(DBG_error, "%s: failed to do init registers for scan: %s\n", __func__,
- sane_strstatus(status));
- return status;
+ dev->cmd_set->move_to_ta(dev);
}
+ dev->cmd_set->init_regs_for_scan(dev, sensor);
+
/* no lamp during scan */
- if(lamp_off == SANE_TRUE)
- {
+ if (lamp_off) {
sanei_genesys_set_lamp_power(dev, sensor, dev->reg, false);
}
/* GL124 is using SHDAREA, so we have to wait for scan to be set up before
* sending shading data */
- if( (dev->model->cmd_set->send_shading_data!=NULL)
- && !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ if (dev->cmd_set->has_send_shading_data() &&
+ !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
{
- status = genesys_send_shading_coefficient(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading calibration coefficients: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ genesys_send_shading_coefficient(dev, sensor);
}
- /* now send registers for scan */
- status =
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers, status = %d\n", __func__, status);
- return status;
- }
+ // now send registers for scan
+ dev->interface->write_registers(dev->reg);
- /* start effective scan */
- status = dev->model->cmd_set->begin_scan(dev, sensor, &dev->reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
+ // start effective scan
+ dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("start_scan");
+ return;
}
/*do we really need this? the valid data check should be sufficent -- pierre*/
@@ -3957,348 +3367,36 @@ genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off)
+ dev->reg.get8(0x3f);
do
{
- // wait some time between each test to avoid overloading USB and CPU
- sanei_genesys_sleep_ms(100);
- status = sanei_genesys_read_feed_steps (dev, &steps);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to read feed steps: %s\n", __func__, sane_strstatus(status));
- return status;
- }
+ // wait some time between each test to avoid overloading USB and CPU
+ dev->interface->sleep_ms(100);
+ sanei_genesys_read_feed_steps (dev, &steps);
}
while (steps < expected);
- /* wait for buffers to be filled */
- do
- {
- RIE (sanei_genesys_test_buffer_empty (dev, &empty));
- }
- while (empty);
-
- /* when doing one or two-table movement, let the motor settle to scanning speed */
- /* and scanning start before reading data */
-/* the valid data check already waits until the scanner delivers data. this here leads to unnecessary buffer full conditions in the scanner.
- if (dev->model->cmd_set->get_fast_feed_bit (dev->reg))
- sanei_genesys_sleep_ms(1000);
- else
- sanei_genesys_sleep_ms(500);
-*/
- /* then we wait for at least one word of valid scan data
+ wait_until_buffer_non_empty(dev);
- this is also done in sanei_genesys_read_data_from_scanner -- pierre */
- if (dev->model->is_sheetfed == SANE_FALSE)
- {
- do
- {
- sanei_genesys_sleep_ms(100);
- status = sanei_genesys_read_valid_words (dev, &steps);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read valid words: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
+ // we wait for at least one word of valid scan data
+ // this is also done in sanei_genesys_read_data_from_scanner -- pierre
+ if (!dev->model->is_sheetfed) {
+ do {
+ dev->interface->sleep_ms(100);
+ sanei_genesys_read_valid_words(dev, &steps);
+ }
while (steps < 1);
}
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* this is _not_ a ringbuffer.
- if we need a block which does not fit at the end of our available data,
- we move the available data to the beginning.
- */
-
-void Genesys_Buffer::alloc(size_t size)
-{
- buffer_.resize(size);
- avail_ = 0;
- pos_ = 0;
-}
-
-void Genesys_Buffer::clear()
-{
- buffer_.clear();
- avail_ = 0;
- pos_ = 0;
-}
-
-void Genesys_Buffer::reset()
-{
- avail_ = 0;
- pos_ = 0;
-}
-
-uint8_t* Genesys_Buffer::get_write_pos(size_t size)
-{
- if (avail_ + size > buffer_.size())
- return nullptr;
- if (pos_ + avail_ + size > buffer_.size())
- {
- std::memmove(buffer_.data(), buffer_.data() + pos_, avail_);
- pos_ = 0;
- }
- return buffer_.data() + pos_ + avail_;
-}
-
-uint8_t* Genesys_Buffer::get_read_pos()
-{
- return buffer_.data() + pos_;
}
-void Genesys_Buffer::produce(size_t size)
+static void genesys_fill_read_buffer(Genesys_Device* dev)
{
- if (size > buffer_.size() - avail_)
- throw std::runtime_error("buffer size exceeded");
- avail_ += size;
-}
-
-void Genesys_Buffer::consume(size_t size)
-{
- if (size > avail_)
- throw std::runtime_error("no more data in buffer");
- avail_ -= size;
- pos_ += size;
-}
-
-
-#include "genesys_conv.cc"
-
-static SANE_Status accurate_line_read(Genesys_Device * dev,
- Genesys_Buffer& buffer)
-{
- buffer.reset();
-
- SANE_Status status = SANE_STATUS_GOOD;
- status = dev->model->cmd_set->bulk_read_data(dev, 0x45, buffer.get_write_pos(buffer.size()),
- buffer.size());
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read %lu bytes (%s)\n", __func__, (u_long) buffer.size(),
- sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
-
- buffer.produce(buffer.size());
- return status;
-}
-
-/** @brief fill buffer while reducing vertical resolution
- * This function fills a read buffer with scanned data from a sensor
- * which puts odd and even pixels in 2 different data segment. So a complete
- * must be read and bytes interleaved to get usable by the other stages
- * of the backend
- */
-static SANE_Status
-genesys_fill_line_interp_buffer (Genesys_Device * dev, uint8_t *work_buffer_dst, size_t size)
-{
- size_t count;
- SANE_Status status = SANE_STATUS_GOOD;
-
- /* fill buffer if needed */
- if (dev->oe_buffer.avail() == 0)
- {
- status = accurate_line_read(dev, dev->oe_buffer);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read %lu bytes (%s)\n", __func__,
- (u_long) dev->oe_buffer.size(), sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
- }
-
- /* copy size bytes of data, copying from a line when line count matches */
- count = 0;
- while (count < size)
- {
- /* line counter */
- /* dev->line_interp holds the number of lines scanned for one line of data sent */
- if(((dev->line_count/dev->current_setup.channels) % dev->line_interp)==0)
- {
- /* copy pixel when line matches */
- work_buffer_dst[count] = dev->oe_buffer.get_read_pos()[dev->cur];
- count++;
- }
-
- /* always update pointer so we skip uncopied data */
- dev->cur++;
-
- /* go to next line if needed */
- if (dev->cur == dev->len)
- {
- dev->oe_buffer.set_pos(dev->oe_buffer.pos() + dev->bpl);
- dev->cur = 0;
- dev->line_count++;
- }
-
- /* read a new buffer if needed */
- if (dev->oe_buffer.pos() >= dev->oe_buffer.avail())
- {
- status = accurate_line_read(dev, dev->oe_buffer);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read %lu bytes (%s)\n", __func__,
- (u_long) dev->oe_buffer.size(), sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
- }
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/** @brief fill buffer for segmented sensors
- * This function fills a read buffer with scanned data from a sensor segmented
- * in several parts (multi-lines sensors). Data of the same valid area is read
- * back to back and must be interleaved to get usable by the other stages
- * of the backend
- */
-static SANE_Status
-genesys_fill_segmented_buffer (Genesys_Device * dev, uint8_t *work_buffer_dst, size_t size)
-{
- size_t count;
- SANE_Status status = SANE_STATUS_GOOD;
- int depth,i,n,k;
-
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART && dev->settings.dynamic_lineart==SANE_FALSE)
- depth = 1;
-
- /* fill buffer if needed */
- if (dev->oe_buffer.avail() == 0)
- {
- status = accurate_line_read(dev, dev->oe_buffer);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read %lu bytes (%s)\n", __func__,
- (u_long) dev->oe_buffer.size(), sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
- }
-
- /* copy size bytes of data, copying from a subwindow of each line
- * when last line of buffer is exhausted, read another one */
- count = 0;
- while (count < size)
- {
- if (depth==1) {
- while (dev->cur < dev->len && count < size) {
- for (n=0; n<dev->segnb; n++) {
- work_buffer_dst[count+n] = 0;
- }
- /* interleaving is at bit level */
- for (i=0;i<8;i++) {
- k=count+(i*dev->segnb)/8;
- for (n=0;n<dev->segnb;n++) {
- work_buffer_dst[k] = work_buffer_dst[k] << 1;
- if ((dev->oe_buffer.get_read_pos()[dev->cur + dev->skip + dev->dist*dev->order[n]])&(128>>i)) {
- work_buffer_dst[k] |= 1;
- }
- }
- }
-
- /* update counter and pointer */
- count += dev->segnb;
- dev->cur++;
- }
- }
- if (depth==8) {
- while (dev->cur < dev->len && count < size) {
- for (n=0;n<dev->segnb;n++) {
- work_buffer_dst[count+n] = dev->oe_buffer.get_read_pos()[dev->cur + dev->skip + dev->dist*dev->order[n]];
- }
- /* update counter and pointer */
- count += dev->segnb;
- dev->cur++;
- }
- }
- if (depth==16) {
- while (dev->cur < dev->len && count < size) {
- for (n=0;n<dev->segnb;n++) {
- work_buffer_dst[count+n*2] = dev->oe_buffer.get_read_pos()[dev->cur + dev->skip + dev->dist*dev->order[n]];
- work_buffer_dst[count+n*2+1] = dev->oe_buffer.get_read_pos()[dev->cur + dev->skip + dev->dist*dev->order[n] + 1];
- }
- /* update counter and pointer */
- count += dev->segnb*2;
- dev->cur+=2;
- }
- }
-
- /* go to next line if needed */
- if (dev->cur == dev->len)
- {
- dev->oe_buffer.set_pos(dev->oe_buffer.pos() + dev->bpl);
- dev->cur = 0;
- }
-
- /* read a new buffer if needed */
- if (dev->oe_buffer.pos() >= dev->oe_buffer.avail())
- {
- status = accurate_line_read(dev, dev->oe_buffer);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read %lu bytes (%s)\n", __func__,
- (u_long) dev->oe_buffer.size(), sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
- }
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/**
- *
- */
-static SANE_Status
-genesys_fill_read_buffer (Genesys_Device * dev)
-{
- size_t size;
- size_t space;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t *work_buffer_dst;
-
- DBGSTART;
+ DBG_HELPER(dbg);
/* for sheetfed scanner, we must check is document is shorter than
* the requested scan */
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- status = dev->model->cmd_set->detect_document_end (dev);
- if (status != SANE_STATUS_GOOD)
- return status;
- }
-
- space = dev->read_buffer.size() - dev->read_buffer.avail();
-
- work_buffer_dst = dev->read_buffer.get_write_pos(space);
-
- size = space;
-
- /* never read an odd number. exception: last read
- the chip internal counter does not count half words. */
- size &= ~1;
- /* Some setups need the reads to be multiples of 256 bytes */
- size &= ~0xff;
-
- if (dev->read_bytes_left < size)
- {
- size = dev->read_bytes_left;
- /*round up to a multiple of 256 bytes */
- size += (size & 0xff) ? 0x100 : 0x00;
- size &= ~0xff;
+ if (dev->model->is_sheetfed) {
+ dev->cmd_set->detect_document_end(dev);
}
- /* early out if our remaining buffer capacity is too low */
- if (size == 0)
- return SANE_STATUS_GOOD;
-
- DBG(DBG_io, "%s: reading %lu bytes\n", __func__, (u_long) size);
-
- /* size is already maxed to our needs. for most models bulk_read_data
- will read as much data as requested. */
+ std::size_t size = dev->read_buffer.size() - dev->read_buffer.avail();
/* due to sensors and motors, not all data can be directly used. It
* may have to be read from another intermediate buffer and then processed.
@@ -4310,159 +3408,46 @@ genesys_fill_read_buffer (Genesys_Device * dev)
*
* This is also the place where full duplex data will be handled.
*/
- if (dev->line_interp>0)
- {
- /* line interpolation */
- status = genesys_fill_line_interp_buffer (dev, work_buffer_dst, size);
- }
- else if (dev->segnb>1)
- {
- /* multi-segment sensors processing */
- status = genesys_fill_segmented_buffer (dev, work_buffer_dst, size);
- }
- else /* regular case with no extra copy */
- {
- status = dev->model->cmd_set->bulk_read_data (dev, 0x45, work_buffer_dst, size);
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read %lu bytes (%s)\n", __func__, (u_long) size,
- sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
-
- if (size > dev->read_bytes_left)
- size = dev->read_bytes_left;
-
- dev->read_bytes_left -= size;
+ dev->pipeline_buffer.get_data(size, dev->read_buffer.get_write_pos(size));
- dev->read_buffer.produce(size);
-
- DBGCOMPLETED;
-
- return SANE_STATUS_GOOD;
+ dev->read_buffer.produce(size);
}
/* this function does the effective data read in a manner that suits
the scanner. It does data reordering and resizing if need.
It also manages EOF and I/O errors, and line distance correction.
- */
-static SANE_Status
-genesys_read_ordered_data (Genesys_Device * dev, SANE_Byte * destination,
- size_t * len)
+ Returns true on success, false on end-of-file.
+*/
+static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destination, size_t* len)
{
- SANE_Status status = SANE_STATUS_GOOD;
- size_t bytes, extra;
- unsigned int channels, depth, src_pixels;
- unsigned int ccd_shift[12], shift_count;
+ DBG_HELPER(dbg);
+ size_t bytes = 0;
uint8_t *work_buffer_src;
- uint8_t *work_buffer_dst;
- unsigned int dst_lines;
- unsigned int step_1_mode;
- unsigned int needs_reorder;
- unsigned int needs_ccd;
- unsigned int needs_shrink;
- unsigned int needs_reverse;
Genesys_Buffer *src_buffer;
- Genesys_Buffer *dst_buffer;
- DBGSTART;
- if (dev->read_active != SANE_TRUE)
- {
- DBG(DBG_error, "%s: read not active!\n", __func__);
+ if (!dev->read_active) {
*len = 0;
- return SANE_STATUS_INVAL;
+ throw SaneException("read is not active");
}
- DBG(DBG_info, "%s: ", __func__);
- debug_dump(DBG_info, dev->current_setup);
-
- /* prepare conversion */
- /* current settings */
- channels = dev->current_setup.channels;
- depth = dev->current_setup.depth;
-
- src_pixels = dev->current_setup.pixels;
-
- needs_reorder = 1;
- if (channels != 3 && depth != 16)
- needs_reorder = 0;
-#ifndef WORDS_BIGENDIAN
- if (channels != 3 && depth == 16)
- needs_reorder = 0;
- if (channels == 3 && depth == 16 && !dev->model->is_cis &&
- dev->model->line_mode_color_order == COLOR_ORDER_RGB)
- needs_reorder = 0;
-#endif
- if (channels == 3 && depth == 8 && !dev->model->is_cis &&
- dev->model->line_mode_color_order == COLOR_ORDER_RGB)
- needs_reorder = 0;
+ DBG(DBG_info, "%s: frontend requested %zu bytes\n", __func__, *len);
+ DBG(DBG_info, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__,
+ dev->total_bytes_to_read, dev->total_bytes_read);
- needs_ccd = dev->current_setup.max_shift > 0;
- needs_shrink = dev->settings.pixels != src_pixels;
- needs_reverse = depth == 1;
-
- DBG(DBG_info, "%s: using filters:%s%s%s%s\n", __func__,
- needs_reorder ? " reorder" : "",
- needs_ccd ? " ccd" : "",
- needs_shrink ? " shrink" : "",
- needs_reverse ? " reverse" : "");
-
- DBG(DBG_info, "%s: frontend requested %lu bytes\n", __func__, (u_long) * len);
-
- DBG(DBG_info, "%s: bytes_to_read=%lu, total_bytes_read=%lu\n", __func__,
- (u_long) dev->total_bytes_to_read, (u_long) dev->total_bytes_read);
/* is there data left to scan */
if (dev->total_bytes_read >= dev->total_bytes_to_read)
{
- DBG(DBG_proc, "%s: nothing more to scan: EOF\n", __func__);
- *len = 0;
-
/* issue park command immediatly in case scanner can handle it
* so we save time */
- if (dev->model->is_sheetfed == SANE_FALSE
- && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT)
- && dev->parking == SANE_FALSE)
+ if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) &&
+ !dev->parking)
{
- dev->model->cmd_set->slow_back_home (dev, SANE_FALSE);
- dev->parking = SANE_TRUE;
+ dev->cmd_set->move_back_home(dev, false);
+ dev->parking = true;
}
- return SANE_STATUS_EOF;
- }
-
- DBG(DBG_info, "%s: %lu lines left by output\n", __func__,
- ((dev->total_bytes_to_read - dev->total_bytes_read) * 8UL) /
- (dev->settings.pixels * channels * depth));
- DBG(DBG_info, "%s: %lu lines left by input\n", __func__,
- ((dev->read_bytes_left + dev->read_buffer.avail()) * 8UL) /
- (src_pixels * channels * depth));
-
- if (channels == 1)
- {
- ccd_shift[0] = 0;
- ccd_shift[1] = dev->current_setup.stagger;
- shift_count = 2;
- }
- else
- {
- ccd_shift[0] =
- ((dev->ld_shift_r * dev->settings.yres) /
- dev->motor.base_ydpi);
- ccd_shift[1] =
- ((dev->ld_shift_g * dev->settings.yres) /
- dev->motor.base_ydpi);
- ccd_shift[2] =
- ((dev->ld_shift_b * dev->settings.yres) /
- dev->motor.base_ydpi);
-
- ccd_shift[3] = ccd_shift[0] + dev->current_setup.stagger;
- ccd_shift[4] = ccd_shift[1] + dev->current_setup.stagger;
- ccd_shift[5] = ccd_shift[2] + dev->current_setup.stagger;
-
- shift_count = 6;
+ throw SaneException(SANE_STATUS_EOF, "nothing more to scan: EOF");
}
-
/* convert data */
/*
0. fill_read_buffer
@@ -4490,317 +3475,46 @@ Problems with the first approach:
total_bytes_to_read and total_bytes_read help in that case.
*/
- status = genesys_fill_read_buffer (dev);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: genesys_fill_read_buffer failed\n", __func__);
- return status;
- }
-
- src_buffer = &(dev->read_buffer);
-
-/* maybe reorder components/bytes */
- if (needs_reorder)
- {
-/*not implemented for depth == 1.*/
- if (depth == 1)
- {
- DBG(DBG_error, "Can't reorder single bit data\n");
- return SANE_STATUS_INVAL;
- }
-
- dst_buffer = &(dev->lines_buffer);
-
- work_buffer_src = src_buffer->get_read_pos();
- bytes = src_buffer->avail();
-
-/*how many bytes can be processed here?*/
-/*we are greedy. we work as much as possible*/
- if (bytes > dst_buffer->size() - dst_buffer->avail())
- bytes = dst_buffer->size() - dst_buffer->avail();
-
- dst_lines = (bytes * 8) / (src_pixels * channels * depth);
- bytes = (dst_lines * src_pixels * channels * depth) / 8;
-
- work_buffer_dst = dst_buffer->get_write_pos(bytes);
-
- DBG(DBG_info, "%s: reordering %d lines\n", __func__, dst_lines);
-
- if (dst_lines != 0)
- {
-
- if (channels == 3)
- {
- step_1_mode = 0;
-
- if (depth == 16)
- step_1_mode |= 1;
-
- if (dev->model->is_cis)
- step_1_mode |= 2;
-
- if (dev->model->line_mode_color_order == COLOR_ORDER_BGR)
- step_1_mode |= 4;
-
- switch (step_1_mode)
- {
- case 1: /* RGB, chunky, 16 bit */
-#ifdef WORDS_BIGENDIAN
- status =
- genesys_reorder_components_endian_16 (work_buffer_src,
- work_buffer_dst,
- dst_lines,
- src_pixels, 3);
- break;
-#endif /*WORDS_BIGENDIAN */
- case 0: /* RGB, chunky, 8 bit */
- status = SANE_STATUS_GOOD;
- break;
- case 2: /* RGB, cis, 8 bit */
- status =
- genesys_reorder_components_cis_8 (work_buffer_src,
- work_buffer_dst,
- dst_lines, src_pixels);
- break;
- case 3: /* RGB, cis, 16 bit */
- status =
- genesys_reorder_components_cis_16 (work_buffer_src,
- work_buffer_dst,
- dst_lines, src_pixels);
- break;
- case 4: /* BGR, chunky, 8 bit */
- status =
- genesys_reorder_components_bgr_8 (work_buffer_src,
- work_buffer_dst,
- dst_lines, src_pixels);
- break;
- case 5: /* BGR, chunky, 16 bit */
- status =
- genesys_reorder_components_bgr_16 (work_buffer_src,
- work_buffer_dst,
- dst_lines, src_pixels);
- break;
- case 6: /* BGR, cis, 8 bit */
- status =
- genesys_reorder_components_cis_bgr_8 (work_buffer_src,
- work_buffer_dst,
- dst_lines,
- src_pixels);
- break;
- case 7: /* BGR, cis, 16 bit */
- status =
- genesys_reorder_components_cis_bgr_16 (work_buffer_src,
- work_buffer_dst,
- dst_lines,
- src_pixels);
- break;
- }
- }
- else
- {
-#ifdef WORDS_BIGENDIAN
- if (depth == 16)
- {
- status =
- genesys_reorder_components_endian_16 (work_buffer_src,
- work_buffer_dst,
- dst_lines,
- src_pixels, 1);
- }
- else
- {
- status = SANE_STATUS_GOOD;
- }
-#else /*!WORDS_BIGENDIAN */
- status = SANE_STATUS_GOOD;
-#endif /*WORDS_BIGENDIAN */
- }
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to convert byte ordering(%s)\n", __func__,
- sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
-
- dst_buffer->produce(bytes);
- src_buffer->consume(bytes);
- }
- src_buffer = dst_buffer;
- }
-
-/* maybe reverse effects of ccd layout */
- if (needs_ccd)
- {
-/*should not happen with depth == 1.*/
- if (depth == 1)
- {
- DBG(DBG_error, "Can't reverse ccd for single bit data\n");
- return SANE_STATUS_INVAL;
- }
-
- dst_buffer = &(dev->shrink_buffer);
-
- work_buffer_src = src_buffer->get_read_pos();
- bytes = src_buffer->avail();
-
- extra =
- (dev->current_setup.max_shift * src_pixels * channels * depth) / 8;
-
-/*extra bytes are reserved, and should not be consumed*/
- if (bytes < extra)
- bytes = 0;
- else
- bytes -= extra;
-
-/*how many bytes can be processed here?*/
-/*we are greedy. we work as much as possible*/
- if (bytes > dst_buffer->size() - dst_buffer->avail())
- bytes = dst_buffer->size() - dst_buffer->avail();
-
- dst_lines = (bytes * 8) / (src_pixels * channels * depth);
- bytes = (dst_lines * src_pixels * channels * depth) / 8;
-
- work_buffer_dst = dst_buffer->get_write_pos(bytes);
-
- DBG(DBG_info, "%s: un-ccd-ing %d lines\n", __func__, dst_lines);
-
- if (dst_lines != 0)
- {
-
- if (depth == 8)
- status = genesys_reverse_ccd_8 (work_buffer_src, work_buffer_dst,
- dst_lines,
- src_pixels * channels,
- ccd_shift, shift_count);
- else
- status = genesys_reverse_ccd_16 (work_buffer_src, work_buffer_dst,
- dst_lines,
- src_pixels * channels,
- ccd_shift, shift_count);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to reverse ccd effects(%s)\n", __func__,
- sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
-
- dst_buffer->produce(bytes);
- src_buffer->consume(bytes);
- }
- src_buffer = dst_buffer;
- }
-
-/* maybe shrink(or enlarge) lines */
- if (needs_shrink)
- {
-
- dst_buffer = &(dev->out_buffer);
-
- work_buffer_src = src_buffer->get_read_pos();
- bytes = src_buffer->avail();
-
-/*lines in input*/
- dst_lines = (bytes * 8) / (src_pixels * channels * depth);
-
- /* how many lines can be processed here? */
- /* we are greedy. we work as much as possible */
- bytes = dst_buffer->size() - dst_buffer->avail();
-
- if (dst_lines > (bytes * 8) / (dev->settings.pixels * channels * depth))
- dst_lines = (bytes * 8) / (dev->settings.pixels * channels * depth);
-
- bytes = (dst_lines * dev->settings.pixels * channels * depth) / 8;
-
- work_buffer_dst = dst_buffer->get_write_pos(bytes);
+ if (is_testing_mode()) {
+ if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
+ *len = dev->total_bytes_to_read - dev->total_bytes_read;
+ }
+ dev->total_bytes_read += *len;
+ } else {
+ genesys_fill_read_buffer(dev);
- DBG(DBG_info, "%s: shrinking %d lines\n", __func__, dst_lines);
+ src_buffer = &(dev->read_buffer);
- if (dst_lines != 0)
- {
- if (depth == 1)
- status = genesys_shrink_lines_1 (work_buffer_src,
- work_buffer_dst,
- dst_lines,
- src_pixels,
- dev->settings.pixels,
- channels);
- else if (depth == 8)
- status = genesys_shrink_lines_8 (work_buffer_src,
- work_buffer_dst,
- dst_lines,
- src_pixels,
- dev->settings.pixels, channels);
- else
- status = genesys_shrink_lines_16 (work_buffer_src,
- work_buffer_dst,
- dst_lines,
- src_pixels,
- dev->settings.pixels, channels);
+ /* move data to destination */
+ bytes = std::min(src_buffer->avail(), *len);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to shrink lines(%s)\n", __func__, sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
+ work_buffer_src = src_buffer->get_read_pos();
- /* we just consumed this many bytes*/
- bytes = (dst_lines * src_pixels * channels * depth) / 8;
- src_buffer->consume(bytes);
+ std::memcpy(destination, work_buffer_src, bytes);
+ *len = bytes;
- /* we just created this many bytes*/
- bytes = (dst_lines * dev->settings.pixels * channels * depth) / 8;
- dst_buffer->produce(bytes);
- }
- src_buffer = dst_buffer;
- }
+ /* avoid signaling some extra data because we have treated a full block
+ * on the last block */
+ if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
+ *len = dev->total_bytes_to_read - dev->total_bytes_read;
+ }
- /* move data to destination */
- bytes = src_buffer->avail();
- if (bytes > *len)
- bytes = *len;
- work_buffer_src = src_buffer->get_read_pos();
+ /* count bytes sent to frontend */
+ dev->total_bytes_read += *len;
- if (needs_reverse)
- {
- status = genesys_reverse_bits (work_buffer_src, destination, bytes);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to reverse bits(%s)\n", __func__, sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
- *len = bytes;
+ src_buffer->consume(bytes);
}
- else
- {
- memcpy (destination, work_buffer_src, bytes);
- *len = bytes;
- }
-
- /* avoid signaling some extra data because we have treated a full block
- * on the last block */
- if (dev->total_bytes_read + *len > dev->total_bytes_to_read)
- *len = dev->total_bytes_to_read - dev->total_bytes_read;
-
- /* count bytes sent to frontend */
- dev->total_bytes_read += *len;
-
- src_buffer->consume(bytes);
/* end scan if all needed data have been read */
if(dev->total_bytes_read >= dev->total_bytes_to_read)
{
- dev->model->cmd_set->end_scan(dev, &dev->reg, SANE_TRUE);
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- dev->model->cmd_set->eject_document (dev);
+ dev->cmd_set->end_scan(dev, &dev->reg, true);
+ if (dev->model->is_sheetfed) {
+ dev->cmd_set->eject_document (dev);
}
}
- DBG(DBG_proc, "%s: completed, %lu bytes read\n", __func__, (u_long) bytes);
- return SANE_STATUS_GOOD;
+ DBG(DBG_proc, "%s: completed, %zu bytes read\n", __func__, bytes);
}
@@ -4824,10 +3538,47 @@ max_string_size (const SANE_String_Const strings[])
return max_size;
}
-static SANE_Status
-calc_parameters (Genesys_Scanner * s)
+static std::size_t max_string_size(const std::vector<const char*>& strings)
+{
+ std::size_t max_size = 0;
+ for (const auto& s : strings) {
+ if (!s) {
+ continue;
+ }
+ max_size = std::max(max_size, std::strlen(s));
+ }
+ return max_size;
+}
+
+static unsigned pick_resolution(const std::vector<unsigned>& resolutions, unsigned resolution,
+ const char* direction)
+{
+ DBG_HELPER(dbg);
+
+ if (resolutions.empty())
+ throw SaneException("Empty resolution list");
+
+ unsigned best_res = resolutions.front();
+ unsigned min_diff = abs_diff(best_res, resolution);
+
+ for (auto it = std::next(resolutions.begin()); it != resolutions.end(); ++it) {
+ unsigned curr_diff = abs_diff(*it, resolution);
+ if (curr_diff < min_diff) {
+ min_diff = curr_diff;
+ best_res = *it;
+ }
+ }
+
+ if (best_res != resolution) {
+ DBG(DBG_warn, "%s: using resolution %d that is nearest to %d for direction %s\n",
+ __func__, best_res, resolution, direction);
+ }
+ return best_res;
+}
+
+static void calc_parameters(Genesys_Scanner* s)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER(dbg);
double tl_x = 0, tl_y = 0, br_x = 0, br_y = 0;
tl_x = SANE_UNFIX(s->pos_top_left_x);
@@ -4835,7 +3586,7 @@ calc_parameters (Genesys_Scanner * s)
br_x = SANE_UNFIX(s->pos_bottom_right_x);
br_y = SANE_UNFIX(s->pos_bottom_right_y);
- s->params.last_frame = SANE_TRUE; /* only single pass scanning supported */
+ s->params.last_frame = true; /* only single pass scanning supported */
if (s->mode == SANE_VALUE_SCAN_MODE_GRAY || s->mode == SANE_VALUE_SCAN_MODE_LINEART) {
s->params.format = SANE_FRAME_GRAY;
@@ -4849,6 +3600,9 @@ calc_parameters (Genesys_Scanner * s)
s->params.depth = s->bit_depth;
}
+ s->dev->settings.scan_method = s->scan_method;
+ const auto& resolutions = s->dev->model->get_resolution_settings(s->dev->settings.scan_method);
+
s->dev->settings.depth = s->bit_depth;
/* interpolation */
@@ -4858,86 +3612,93 @@ calc_parameters (Genesys_Scanner * s)
const auto& sensor = sanei_genesys_find_sensor_any(s->dev);
// hardware settings
- if (s->resolution > sensor.optical_res && s->dev->settings.disable_interpolation) {
+ if (static_cast<unsigned>(s->resolution) > sensor.optical_res &&
+ s->dev->settings.disable_interpolation)
+ {
s->dev->settings.xres = sensor.optical_res;
} else {
s->dev->settings.xres = s->resolution;
}
s->dev->settings.yres = s->resolution;
- s->params.lines = ((br_y - tl_y) * s->dev->settings.yres) / MM_PER_INCH;
- s->params.pixels_per_line = ((br_x - tl_x) * s->resolution) / MM_PER_INCH;
+ s->dev->settings.xres = pick_resolution(resolutions.resolutions_x, s->dev->settings.xres, "X");
+ s->dev->settings.yres = pick_resolution(resolutions.resolutions_y, s->dev->settings.yres, "Y");
+
+ s->params.lines = static_cast<unsigned>(((br_y - tl_y) * s->dev->settings.yres) /
+ MM_PER_INCH);
+ unsigned pixels_per_line = static_cast<unsigned>(((br_x - tl_x) * s->dev->settings.xres) /
+ MM_PER_INCH);
/* we need an even pixels number
* TODO invert test logic or generalize behaviour across all ASICs */
- if ((s->dev->model->flags & GENESYS_FLAG_SIS_SENSOR)
- || s->dev->model->asic_type == GENESYS_GL847
- || s->dev->model->asic_type == GENESYS_GL124
- || s->dev->model->asic_type == GENESYS_GL845
- || s->dev->model->asic_type == GENESYS_GL846
- || s->dev->model->asic_type == GENESYS_GL843)
- {
- if (s->dev->settings.xres <= 1200)
- s->params.pixels_per_line = (s->params.pixels_per_line/4)*4;
- else
- s->params.pixels_per_line = (s->params.pixels_per_line/16)*16;
+ if ((s->dev->model->flags & GENESYS_FLAG_SIS_SENSOR) ||
+ s->dev->model->asic_type == AsicType::GL847 ||
+ s->dev->model->asic_type == AsicType::GL124 ||
+ s->dev->model->asic_type == AsicType::GL845 ||
+ s->dev->model->asic_type == AsicType::GL846 ||
+ s->dev->model->asic_type == AsicType::GL843)
+ {
+ if (s->dev->settings.xres <= 1200) {
+ pixels_per_line = (pixels_per_line / 4) * 4;
+ } else if (s->dev->settings.xres < s->dev->settings.yres) {
+ // BUG: this is an artifact of the fact that the resolution was twice as large than
+ // the actual resolution when scanning above the supported scanner X resolution
+ pixels_per_line = (pixels_per_line / 8) * 8;
+ } else {
+ pixels_per_line = (pixels_per_line / 16) * 16;
+ }
}
/* corner case for true lineart for sensor with several segments
* or when xres is doubled to match yres */
- if (s->dev->settings.xres >= 1200
- && ( s->dev->model->asic_type == GENESYS_GL124
- || s->dev->model->asic_type == GENESYS_GL847
- || s->dev->current_setup.xres < s->dev->current_setup.yres
- )
- )
- {
- s->params.pixels_per_line = (s->params.pixels_per_line/16)*16;
+ if (s->dev->settings.xres >= 1200 && (
+ s->dev->model->asic_type == AsicType::GL124 ||
+ s->dev->model->asic_type == AsicType::GL847 ||
+ s->dev->session.params.xres < s->dev->session.params.yres))
+ {
+ if (s->dev->settings.xres < s->dev->settings.yres) {
+ // FIXME: this is an artifact of the fact that the resolution was twice as large than
+ // the actual resolution when scanning above the supported scanner X resolution
+ pixels_per_line = (pixels_per_line / 8) * 8;
+ } else {
+ pixels_per_line = (pixels_per_line / 16) * 16;
+ }
}
- s->params.bytes_per_line = s->params.pixels_per_line;
+ unsigned xres_factor = s->resolution / s->dev->settings.xres;
+
+ unsigned bytes_per_line = 0;
+
if (s->params.depth > 8)
{
s->params.depth = 16;
- s->params.bytes_per_line *= 2;
+ bytes_per_line = 2 * pixels_per_line;
}
else if (s->params.depth == 1)
{
- s->params.bytes_per_line /= 8;
- /* round down pixel number
- really? rounding down means loss of at most 7 pixels! -- pierre */
- s->params.pixels_per_line = 8 * s->params.bytes_per_line;
+ // round down pixel number. This will is lossy operation, at most 7 pixels will be lost
+ pixels_per_line = (pixels_per_line / 8) * 8;
+ bytes_per_line = pixels_per_line / 8;
+ } else {
+ bytes_per_line = pixels_per_line;
}
if (s->params.format == SANE_FRAME_RGB) {
- s->params.bytes_per_line *= 3;
- }
-
- if (s->mode == SANE_VALUE_SCAN_MODE_COLOR) {
- s->dev->settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- } else if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) {
- s->dev->settings.scan_mode = ScanColorMode::GRAY;
- } else if (s->mode == SANE_TITLE_HALFTONE) {
- s->dev->settings.scan_mode = ScanColorMode::HALFTONE;
- } else { /* Lineart */
- s->dev->settings.scan_mode = ScanColorMode::LINEART;
+ bytes_per_line *= 3;
}
- if (s->source == STR_FLATBED) {
- s->dev->settings.scan_method = ScanMethod::FLATBED;
- } else if (s->source == STR_TRANSPARENCY_ADAPTER) {
- s->dev->settings.scan_method = ScanMethod::TRANSPARENCY;
- } else if (s->source == STR_TRANSPARENCY_ADAPTER_INFRARED) {
- s->dev->settings.scan_method = ScanMethod::TRANSPARENCY_INFRARED;
- }
+ s->dev->settings.scan_mode = option_string_to_scan_color_mode(s->mode);
s->dev->settings.lines = s->params.lines;
- s->dev->settings.pixels = s->params.pixels_per_line;
+ s->dev->settings.pixels = pixels_per_line;
+ s->dev->settings.requested_pixels = pixels_per_line * xres_factor;
+ s->params.pixels_per_line = pixels_per_line * xres_factor;
+ s->params.bytes_per_line = bytes_per_line * xres_factor;
s->dev->settings.tl_x = tl_x;
s->dev->settings.tl_y = tl_y;
// threshold setting
- s->dev->settings.threshold = 2.55 * (SANE_UNFIX(s->threshold));
+ s->dev->settings.threshold = static_cast<int>(2.55 * (SANE_UNFIX(s->threshold)));
// color filter
if (s->color_filter == "Red") {
@@ -4957,23 +3718,6 @@ calc_parameters (Genesys_Scanner * s)
s->dev->settings.true_gray = 0;
}
- /* dynamic lineart */
- s->dev->settings.dynamic_lineart = SANE_FALSE;
- s->dev->settings.threshold_curve=0;
- if (!s->disable_dynamic_lineart && s->dev->settings.scan_mode == ScanColorMode::LINEART) {
- s->dev->settings.dynamic_lineart = SANE_TRUE;
- }
-
- /* hardware lineart works only when we don't have interleave data
- * for GL847 scanners, ie up to 600 DPI, then we have to rely on
- * dynamic_lineart */
- if(s->dev->settings.xres > 600
- && s->dev->model->asic_type==GENESYS_GL847
- && s->dev->settings.scan_mode == ScanColorMode::LINEART)
- {
- s->dev->settings.dynamic_lineart = SANE_TRUE;
- }
-
// threshold curve for dynamic rasterization
s->dev->settings.threshold_curve = s->threshold_curve;
@@ -4984,11 +3728,11 @@ calc_parameters (Genesys_Scanner * s)
&& (!s->preview)
&& (s->bit_depth <= 8))
{
- s->dev->buffer_image=SANE_TRUE;
+ s->dev->buffer_image = true;
}
else
{
- s->dev->buffer_image=SANE_FALSE;
+ s->dev->buffer_image = false;
}
/* brigthness and contrast only for for 8 bit scans */
@@ -5005,24 +3749,13 @@ calc_parameters (Genesys_Scanner * s)
/* cache expiration time */
s->dev->settings.expiration_time = s->expiration_time;
-
- return status;
}
-static SANE_Status
-create_bpp_list (Genesys_Scanner * s, SANE_Int * bpp)
+static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& bpp)
{
- int count;
-
- for (count = 0; bpp[count] != 0; count++)
- ;
- s->bpp_list[0] = count;
- for (count = 0; bpp[count] != 0; count++)
- {
- s->bpp_list[s->bpp_list[0] - count] = bpp[count];
- }
- return SANE_STATUS_GOOD;
+ s->bpp_list[0] = bpp.size();
+ std::reverse_copy(bpp.begin(), bpp.end(), s->bpp_list + 1);
}
/** @brief this function initialize a gamma vector based on the ASIC:
@@ -5042,8 +3775,7 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)
scanner->opt[option].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
scanner->opt[option].unit = SANE_UNIT_NONE;
scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE;
- if (scanner->dev->model->asic_type == GENESYS_GL646)
- {
+ if (scanner->dev->model->asic_type == AsicType::GL646) {
if ((scanner->dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) != 0)
{
scanner->opt[option].size = 16384 * sizeof (SANE_Word);
@@ -5065,20 +3797,15 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)
/**
* allocate a geometry range
* @param size maximum size of the range
- * @return a pointer to a valid range or NULL
+ * @return a pointer to a valid range or nullptr
*/
-static SANE_Range *create_range(SANE_Fixed size)
+static SANE_Range create_range(float size)
{
-SANE_Range *range=NULL;
-
- range=(SANE_Range *)malloc(sizeof(SANE_Range));
- if(range!=NULL)
- {
- range->min = SANE_FIX (0.0);
- range->max = size;
- range->quant = SANE_FIX (0.0);
- }
- return range;
+ SANE_Range range;
+ range.min = SANE_FIX(0.0);
+ range.max = SANE_FIX(size);
+ range.quant = SANE_FIX(0.0);
+ return range;
}
/** @brief generate calibration cache file nam
@@ -5091,21 +3818,15 @@ SANE_Range *range=NULL;
* @param currdev current scanner device
* @return an allocated string containing a file name
*/
-static char *calibration_filename(Genesys_Device *currdev)
+static std::string calibration_filename(Genesys_Device *currdev)
{
- char *tmpstr;
- char *ptr;
+ std::string ret;
+ ret.resize(PATH_MAX);
+
char filename[80];
unsigned int count;
unsigned int i;
- /* allocate space for result */
- tmpstr = (char*) malloc(PATH_MAX);
- if(tmpstr==NULL)
- {
- return NULL;
- }
-
/* first compute the DIR where we can store cache:
* 1 - home dir
* 2 - $TMPDIR
@@ -5114,18 +3835,15 @@ static char *calibration_filename(Genesys_Device *currdev)
* 5 - temp dir
* 6 - then resort to current dir
*/
- ptr = getenv ("HOME");
- if(ptr==NULL)
- {
- ptr = getenv ("USERPROFILE");
+ char* ptr = std::getenv("HOME");
+ if (ptr == nullptr) {
+ ptr = std::getenv("USERPROFILE");
}
- if(ptr==NULL)
- {
- ptr = getenv ("TMPDIR");
+ if (ptr == nullptr) {
+ ptr = std::getenv("TMPDIR");
}
- if(ptr==NULL)
- {
- ptr = getenv ("TMP");
+ if (ptr == nullptr) {
+ ptr = std::getenv("TMP");
}
/* now choose filename:
@@ -5144,7 +3862,7 @@ static char *calibration_filename(Genesys_Device *currdev)
}
if(count>1)
{
- snprintf(filename,sizeof(filename),"%s.cal",currdev->file_name);
+ std::snprintf(filename, sizeof(filename), "%s.cal", currdev->file_name.c_str());
for(i=0;i<strlen(filename);i++)
{
if(filename[i]==':'||filename[i]==PATH_SEP)
@@ -5159,36 +3877,75 @@ static char *calibration_filename(Genesys_Device *currdev)
}
/* build final final name : store dir + filename */
- if (NULL == ptr)
- {
- snprintf (tmpstr, PATH_MAX, "%s", filename);
+ if (ptr == nullptr) {
+ int size = std::snprintf(&ret.front(), ret.size(), "%s", filename);
+ ret.resize(size);
}
else
{
+ int size = 0;
#ifdef HAVE_MKDIR
- /* make sure .sane directory exists in existing store dir */
- snprintf (tmpstr, PATH_MAX, "%s%c.sane", ptr, PATH_SEP);
- mkdir(tmpstr,0700);
+ /* make sure .sane directory exists in existing store dir */
+ size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane", ptr, PATH_SEP);
+ ret.resize(size);
+ mkdir(ret.c_str(), 0700);
+
+ ret.resize(PATH_MAX);
#endif
- snprintf (tmpstr, PATH_MAX, "%s%c.sane%c%s", ptr, PATH_SEP, PATH_SEP, filename);
+ size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane%c%s",
+ ptr, PATH_SEP, PATH_SEP, filename);
+ ret.resize(size);
}
- DBG(DBG_info, "%s: calibration filename >%s<\n", __func__, tmpstr);
+ DBG(DBG_info, "%s: calibration filename >%s<\n", __func__, ret.c_str());
- return tmpstr;
+ return ret;
}
+static void set_resolution_option_values(Genesys_Scanner& s, bool reset_resolution_value)
+{
+ auto resolutions = s.dev->model->get_resolutions(s.scan_method);
+
+ s.opt_resolution_values.resize(resolutions.size() + 1, 0);
+ s.opt_resolution_values[0] = resolutions.size();
+ std::copy(resolutions.begin(), resolutions.end(), s.opt_resolution_values.begin() + 1);
-static SANE_Status
-init_options (Genesys_Scanner * s)
+ s.opt[OPT_RESOLUTION].constraint.word_list = s.opt_resolution_values.data();
+
+ if (reset_resolution_value) {
+ s.resolution = *std::min_element(resolutions.begin(), resolutions.end());
+ }
+}
+
+static void set_xy_range_option_values(Genesys_Scanner& s)
{
- SANE_Int option, count, min_dpi;
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Word *dpi_list;
- Genesys_Model *model = s->dev->model;
- SANE_Range *x_range, *y_range;
+ if (s.scan_method == ScanMethod::FLATBED)
+ {
+ s.opt_x_range = create_range(static_cast<float>(s.dev->model->x_size));
+ s.opt_y_range = create_range(static_cast<float>(s.dev->model->y_size));
+ }
+ else
+ {
+ s.opt_x_range = create_range(static_cast<float>(s.dev->model->x_size_ta));
+ s.opt_y_range = create_range(static_cast<float>(s.dev->model->y_size_ta));
+ }
+
+ s.opt[OPT_TL_X].constraint.range = &s.opt_x_range;
+ s.opt[OPT_TL_Y].constraint.range = &s.opt_y_range;
+ s.opt[OPT_BR_X].constraint.range = &s.opt_x_range;
+ s.opt[OPT_BR_Y].constraint.range = &s.opt_y_range;
+
+ s.pos_top_left_x = 0;
+ s.pos_top_left_y = 0;
+ s.pos_bottom_right_x = s.opt_x_range.max;
+ s.pos_bottom_right_y = s.opt_y_range.max;
+}
- DBGSTART;
+static void init_options(Genesys_Scanner* s)
+{
+ DBG_HELPER(dbg);
+ SANE_Int option;
+ Genesys_Model *model = s->dev->model;
memset (s->opt, 0, sizeof (s->opt));
@@ -5204,6 +3961,7 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
/* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].name = "scanmode-group";
s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
s->opt[OPT_MODE_GROUP].desc = "";
s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
@@ -5222,26 +3980,25 @@ init_options (Genesys_Scanner * s)
s->mode = SANE_VALUE_SCAN_MODE_GRAY;
/* scan source */
+ s->opt_source_values.clear();
+ for (const auto& resolution_setting : model->resolutions) {
+ for (auto method : resolution_setting.methods) {
+ s->opt_source_values.push_back(scan_method_to_option_string(method));
+ }
+ }
+ s->opt_source_values.push_back(nullptr);
+
s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
- s->opt[OPT_SOURCE].size = max_string_size (source_list);
- s->opt[OPT_SOURCE].constraint.string_list = source_list;
- s->source = STR_FLATBED;
- if (model->flags & GENESYS_FLAG_HAS_UTA)
- {
- ENABLE (OPT_SOURCE);
- if (model->flags & GENESYS_FLAG_HAS_UTA_INFRARED) {
- s->opt[OPT_SOURCE].size = max_string_size(source_list_infrared);
- s->opt[OPT_SOURCE].constraint.string_list = source_list_infrared;
- }
- }
- else
- {
- DISABLE (OPT_SOURCE);
+ s->opt[OPT_SOURCE].size = max_string_size(s->opt_source_values);
+ s->opt[OPT_SOURCE].constraint.string_list = s->opt_source_values.data();
+ if (s->opt_source_values.size() < 2) {
+ throw SaneException("No scan methods specified for scanner");
}
+ s->scan_method = model->default_method;
/* preview */
s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
@@ -5259,38 +4016,21 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
- s->opt[OPT_BIT_DEPTH].constraint.word_list = 0;
s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list;
create_bpp_list (s, model->bpp_gray_values);
- s->bit_depth = 8;
- if (s->opt[OPT_BIT_DEPTH].constraint.word_list[0] < 2)
- DISABLE (OPT_BIT_DEPTH);
+ s->bit_depth = model->bpp_gray_values[0];
- /* resolution */
- min_dpi=200000;
- for (count = 0; model->xdpi_values[count] != 0; count++)
- {
- if(model->xdpi_values[count]<min_dpi)
- {
- min_dpi=model->xdpi_values[count];
- }
- }
- dpi_list = (SANE_Word*) malloc((count + 1) * sizeof(SANE_Word));
- if (!dpi_list)
- return SANE_STATUS_NO_MEM;
- dpi_list[0] = count;
- for (count = 0; model->xdpi_values[count] != 0; count++)
- dpi_list[count + 1] = model->xdpi_values[count];
+ // resolution
s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
- s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
- s->resolution = min_dpi;
+ set_resolution_option_values(*s, true);
/* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY;
s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
s->opt[OPT_GEOMETRY_GROUP].desc = "";
s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
@@ -5298,59 +4038,42 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_GEOMETRY_GROUP].size = 0;
s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
- x_range=create_range(model->x_size);
- if(x_range==NULL)
- {
- return SANE_STATUS_NO_MEM;
- }
-
- y_range=create_range(model->y_size);
- if(y_range==NULL)
- {
- return SANE_STATUS_NO_MEM;
- }
+ s->opt_x_range = create_range(static_cast<float>(model->x_size));
+ s->opt_y_range = create_range(static_cast<float>(model->y_size));
- /* top-left x */
+ // scan area
s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_TL_X].constraint.range = x_range;
- s->pos_top_left_x = 0;
- /* top-left y */
s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_TL_Y].constraint.range = y_range;
- s->pos_top_left_y = 0;
- /* bottom-right x */
s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_BR_X].constraint.range = x_range;
- s->pos_bottom_right_x = x_range->max;
- /* bottom-right y */
s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_BR_Y].constraint.range = y_range;
- s->pos_bottom_right_y = y_range->max;
+
+ set_xy_range_option_values(*s);
/* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].name = SANE_NAME_ENHANCEMENT;
s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
@@ -5483,6 +4206,7 @@ init_options (Genesys_Scanner * s)
s->contrast = 0; // disable by default
/* "Extras" group: */
+ s->opt[OPT_EXTRAS_GROUP].name = "extras-group";
s->opt[OPT_EXTRAS_GROUP].title = SANE_I18N ("Extras");
s->opt[OPT_EXTRAS_GROUP].desc = "";
s->opt[OPT_EXTRAS_GROUP].type = SANE_TYPE_GROUP;
@@ -5510,23 +4234,6 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range;
s->threshold_curve = 50;
- /* dynamic linart */
- s->opt[OPT_DISABLE_DYNAMIC_LINEART].name = "disable-dynamic-lineart";
- s->opt[OPT_DISABLE_DYNAMIC_LINEART].title = SANE_I18N ("Disable dynamic lineart");
- s->opt[OPT_DISABLE_DYNAMIC_LINEART].desc =
- SANE_I18N ("Disable use of a software adaptive algorithm to generate lineart relying instead on hardware lineart.");
- s->opt[OPT_DISABLE_DYNAMIC_LINEART].type = SANE_TYPE_BOOL;
- s->opt[OPT_DISABLE_DYNAMIC_LINEART].unit = SANE_UNIT_NONE;
- s->opt[OPT_DISABLE_DYNAMIC_LINEART].constraint_type = SANE_CONSTRAINT_NONE;
- s->disable_dynamic_lineart = false;
-
- /* fastmod is required for hw lineart to work */
- if ((s->dev->model->asic_type == GENESYS_GL646)
- &&(s->dev->model->motor_type != MOTOR_XP200))
- {
- s->opt[OPT_DISABLE_DYNAMIC_LINEART].cap = SANE_CAP_INACTIVE;
- }
-
/* disable_interpolation */
s->opt[OPT_DISABLE_INTERPOLATION].name = "disable-interpolation";
s->opt[OPT_DISABLE_INTERPOLATION].title =
@@ -5549,8 +4256,7 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_COLOR_FILTER].type = SANE_TYPE_STRING;
s->opt[OPT_COLOR_FILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
/* true gray not yet supported for GL847 and GL124 scanners */
- if(!model->is_cis || model->asic_type==GENESYS_GL847 || model->asic_type==GENESYS_GL124)
- {
+ if (!model->is_cis || model->asic_type==AsicType::GL847 || model->asic_type==AsicType::GL124) {
s->opt[OPT_COLOR_FILTER].size = max_string_size (color_filter_list);
s->opt[OPT_COLOR_FILTER].constraint.string_list = color_filter_list;
s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[1];
@@ -5563,9 +4269,8 @@ init_options (Genesys_Scanner * s)
s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[3];
}
- /* no support for color filter for cis+gl646 scanners */
- if (model->asic_type == GENESYS_GL646 && model->is_cis)
- {
+ // no support for color filter for cis+gl646 scanners
+ if (model->asic_type == AsicType::GL646 && model->is_cis) {
DISABLE (OPT_COLOR_FILTER);
}
@@ -5620,6 +4325,7 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE;
s->lamp_off = false;
+ s->opt[OPT_SENSOR_GROUP].name = SANE_NAME_SENSORS;
s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS;
s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS;
s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP;
@@ -5721,7 +4427,7 @@ init_options (Genesys_Scanner * s)
/* calibration needed */
s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration";
- s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Need calibration");
+ s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Needs calibration");
s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings");
s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL;
s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE;
@@ -5732,6 +4438,7 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE;
/* button group */
+ s->opt[OPT_BUTTON_GROUP].name = "buttons";
s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons");
s->opt[OPT_BUTTON_GROUP].desc = "";
s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP;
@@ -5775,50 +4482,75 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_FORCE_CALIBRATION].cap =
SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
- RIE (calc_parameters (s));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
+ // ignore offsets option
+ s->opt[OPT_IGNORE_OFFSETS].name = "ignore-internal-offsets";
+ s->opt[OPT_IGNORE_OFFSETS].title = SANE_I18N("Ignore internal offsets");
+ s->opt[OPT_IGNORE_OFFSETS].desc =
+ SANE_I18N("Acquires the image including the internal calibration areas of the scanner");
+ s->opt[OPT_IGNORE_OFFSETS].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_IGNORE_OFFSETS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_IGNORE_OFFSETS].size = 0;
+ s->opt[OPT_IGNORE_OFFSETS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_IGNORE_OFFSETS].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT |
+ SANE_CAP_ADVANCED;
+
+ calc_parameters(s);
}
-static SANE_Bool present;
+static bool present;
// this function is passed to C API, it must not throw
static SANE_Status
check_present (SANE_String_Const devname) noexcept
{
- present=SANE_TRUE;
- DBG(DBG_io, "%s: %s detected.\n", __func__, devname);
+ DBG_HELPER_ARGS(dbg, "%s detected.", devname);
+ present = true;
return SANE_STATUS_GOOD;
}
-static SANE_Status
-attach (SANE_String_Const devname, Genesys_Device ** devp, SANE_Bool may_wait)
+static Genesys_Device* attach_usb_device(const char* devname,
+ std::uint16_t vendor_id, std::uint16_t product_id)
{
- DBG_HELPER(dbg);
-
- Genesys_Device *dev = 0;
- unsigned int i;
+ Genesys_USB_Device_Entry* found_usb_dev = nullptr;
+ for (auto& usb_dev : *s_usb_devices) {
+ if (usb_dev.vendor == vendor_id &&
+ usb_dev.product == product_id)
+ {
+ found_usb_dev = &usb_dev;
+ break;
+ }
+ }
+ if (found_usb_dev == nullptr) {
+ throw SaneException("vendor 0x%xd product 0x%xd is not supported by this backend",
+ vendor_id, product_id);
+ }
- DBG(DBG_proc, "%s: start: devp %s NULL, may_wait = %d\n", __func__, devp ? "!=" : "==", may_wait);
+ s_devices->emplace_back();
+ Genesys_Device* dev = &s_devices->back();
+ dev->file_name = devname;
+
+ dev->model = &found_usb_dev->model;
+ dev->vendorId = found_usb_dev->vendor;
+ dev->productId = found_usb_dev->product;
+ dev->usb_mode = 0; // i.e. unset
+ dev->already_initialized = false;
+ return dev;
+}
- if (devp)
- *devp = 0;
+static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may_wait)
+{
+ DBG_HELPER_ARGS(dbg, " devname: %s, may_wait = %d", devname, may_wait);
- if (!devname)
- {
- DBG(DBG_error, "%s: devname == NULL\n", __func__);
- return SANE_STATUS_INVAL;
+ if (!devname) {
+ throw SaneException("devname must not be nullptr");
}
for (auto& dev : *s_devices) {
- if (strcmp(dev.file_name, devname) == 0) {
- if (devp)
- *devp = &dev;
- DBG(DBG_info, "%s: device `%s' was already in device list\n", __func__, devname);
- return SANE_STATUS_GOOD;
- }
+ if (dev.file_name == devname) {
+ DBG(DBG_info, "%s: device `%s' was already in device list\n", __func__, devname);
+ return &dev;
+ }
}
DBG(DBG_info, "%s: trying to open device `%s'\n", __func__, devname);
@@ -5830,78 +4562,35 @@ attach (SANE_String_Const devname, Genesys_Device ** devp, SANE_Bool may_wait)
int vendor, product;
usb_dev.get_vendor_product(vendor, product);
+ usb_dev.close();
/* KV-SS080 is an auxiliary device which requires a master device to be here */
if(vendor == 0x04da && product == 0x100f)
{
- present=SANE_FALSE;
+ present = false;
sanei_usb_find_devices (vendor, 0x1006, check_present);
sanei_usb_find_devices (vendor, 0x1007, check_present);
sanei_usb_find_devices (vendor, 0x1010, check_present);
- if (present == SANE_FALSE) {
+ if (present == false) {
throw SaneException("master device not present");
}
}
- bool found_dev = false;
- for (i = 0; i < MAX_SCANNERS && genesys_usb_device_list[i].model != 0; i++)
- {
- if (vendor == genesys_usb_device_list[i].vendor &&
- product == genesys_usb_device_list[i].product)
- {
- found_dev = true;
- break;
- }
- }
-
- if (!found_dev) {
- DBG(DBG_error, "%s: vendor 0x%xd product 0x%xd is not supported by this backend\n", __func__,
- vendor, product);
- return SANE_STATUS_INVAL;
- }
-
- char* new_devname = strdup (devname);
- if (!new_devname) {
- return SANE_STATUS_NO_MEM;
- }
-
- s_devices->emplace_back();
- dev = &s_devices->back();
- dev->file_name = new_devname;
-
- dev->model = genesys_usb_device_list[i].model;
- dev->vendorId = genesys_usb_device_list[i].vendor;
- dev->productId = genesys_usb_device_list[i].product;
- dev->usb_mode = 0; /* i.e. unset */
- dev->already_initialized = SANE_FALSE;
+ Genesys_Device* dev = attach_usb_device(devname, vendor, product);
- DBG(DBG_info, "%s: found %s flatbed scanner %s at %s\n", __func__, dev->model->vendor,
- dev->model->model, dev->file_name);
+ DBG(DBG_info, "%s: found %s flatbed scanner %s at %s\n", __func__, dev->model->vendor,
+ dev->model->model, dev->file_name.c_str());
- if (devp) {
- *devp = dev;
- }
-
- usb_dev.close();
- return SANE_STATUS_GOOD;
+ return dev;
}
-static SANE_Status
-attach_one_device_impl(SANE_String_Const devname)
-{
- Genesys_Device *dev;
- SANE_Status status = SANE_STATUS_GOOD;
-
- RIE (attach (devname, &dev, SANE_FALSE));
-
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status attach_one_device(SANE_String_Const devname)
+// this function is passed to C API and must not throw
+static SANE_Status attach_one_device(SANE_String_Const devname) noexcept
{
+ DBG_HELPER(dbg);
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return attach_one_device_impl(devname);
+ attach_device_by_name(devname, false);
});
}
@@ -5920,28 +4609,25 @@ config_attach_genesys(SANEI_Config __sane_unused__ *config, const char *devname)
}
/* probes for scanner to attach to the backend */
-static SANE_Status
-probe_genesys_devices (void)
+static void probe_genesys_devices()
{
- SANEI_Config config;
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER(dbg);
+ if (is_testing_mode()) {
+ attach_usb_device(get_testing_device_name().c_str(),
+ get_testing_vendor_id(), get_testing_product_id());
+ return;
+ }
- DBGSTART;
+ SANEI_Config config;
- /* set configuration options structure : no option for this backend */
- config.descriptors = NULL;
- config.values = NULL;
+ // set configuration options structure : no option for this backend
+ config.descriptors = nullptr;
+ config.values = nullptr;
config.count = 0;
- /* generic configure and attach function */
- status = sanei_configure_attach (GENESYS_CONFIG_FILE, &config,
- config_attach_genesys);
+ TIE(sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys));
- DBG(DBG_info, "%s: %d devices currently attached\n", __func__, (int) s_devices->size());
-
- DBGCOMPLETED;
-
- return status;
+ DBG(DBG_info, "%s: %zu devices currently attached\n", __func__, s_devices->size());
}
/**
@@ -5951,11 +4637,13 @@ probe_genesys_devices (void)
of Genesys_Calibration_Cache as is.
*/
static const char* CALIBRATION_IDENT = "sane_genesys";
-static const int CALIBRATION_VERSION = 2;
+static const int CALIBRATION_VERSION = 21;
bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration,
const std::string& path)
{
+ DBG_HELPER(dbg);
+
std::string ident;
serialize(str, ident);
@@ -6022,10 +4710,9 @@ static void write_calibration(Genesys_Device::Calibration& calibration, const st
* In order to allow digital processing, we must be able to put all the
* scanned picture in a buffer.
*/
-static SANE_Status
-genesys_buffer_image(Genesys_Scanner *s)
+static void genesys_buffer_image(Genesys_Scanner *s)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER(dbg);
size_t maximum; /**> maximum bytes size of the scan */
size_t len; /**> length of scanned data read */
size_t total; /**> total of butes read */
@@ -6041,16 +4728,14 @@ genesys_buffer_image(Genesys_Scanner *s)
}
else
{
- lines =
- (SANE_UNFIX (dev->model->y_size) * dev->settings.yres) / MM_PER_INCH;
+ lines = static_cast<int>((dev->model->y_size * dev->settings.yres) / MM_PER_INCH);
}
DBG(DBG_info, "%s: buffering %d lines of %d bytes\n", __func__, lines,
s->params.bytes_per_line);
/* maximum bytes to read */
maximum = s->params.bytes_per_line * lines;
- if(s->dev->settings.dynamic_lineart==SANE_TRUE)
- {
+ if (s->dev->settings.scan_mode == ScanColorMode::LINEART) {
maximum *= 8;
}
@@ -6064,46 +4749,45 @@ genesys_buffer_image(Genesys_Scanner *s)
dev->img_buffer.resize(size);
/* loop reading data until we reach maximum or EOF */
- total = 0;
- while (total < maximum && status != SANE_STATUS_EOF)
- {
+ total = 0;
+ while (total < maximum) {
len = size - maximum;
if (len > read_size)
{
len = read_size;
}
- status = genesys_read_ordered_data(dev, dev->img_buffer.data() + total, &len);
- if (status != SANE_STATUS_EOF && status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: %s buffering failed\n", __func__, sane_strstatus(status));
- return status;
- }
+ try {
+ genesys_read_ordered_data(dev, dev->img_buffer.data() + total, &len);
+ } catch (const SaneException& e) {
+ if (e.status() == SANE_STATUS_EOF) {
+ // ideally we shouldn't end up here, but because computations are duplicated and
+ // slightly different everywhere in the genesys backend, we have no other choice
+ break;
+ }
+ throw;
+ }
total += len;
- /* do we need to enlarge read buffer ? */
- if (total + read_size > size && status != SANE_STATUS_EOF)
- {
- size += read_size;
- dev->img_buffer.resize(size);
- }
+ // do we need to enlarge read buffer ?
+ if (total + read_size > size) {
+ size += read_size;
+ dev->img_buffer.resize(size);
+ }
}
/* since digital processing is going to take place,
* issue head parking command so that the head move while
* computing so we can save time
*/
- if (dev->model->is_sheetfed == SANE_FALSE &&
- dev->parking == SANE_FALSE)
- {
- dev->model->cmd_set->slow_back_home (dev, dev->model->flags & GENESYS_FLAG_MUST_WAIT);
+ if (!dev->model->is_sheetfed && !dev->parking) {
+ dev->cmd_set->move_back_home(dev, dev->model->flags & GENESYS_FLAG_MUST_WAIT);
dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
}
/* in case of dynamic lineart, we have buffered gray data which
* must be converted to lineart first */
- if(s->dev->settings.dynamic_lineart==SANE_TRUE)
- {
+ if (s->dev->settings.scan_mode == ScanColorMode::LINEART) {
total/=8;
std::vector<uint8_t> lineart(total);
@@ -6128,34 +4812,32 @@ genesys_buffer_image(Genesys_Scanner *s)
s->params.format==SANE_FRAME_RGB ? 3 : 1,
s->params.pixels_per_line, s->params.lines);
}
-
- return SANE_STATUS_GOOD;
}
/* -------------------------- SANE API functions ------------------------- */
-SANE_Status
-sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
+void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
{
- SANE_Status status = SANE_STATUS_GOOD;
-
DBG_INIT ();
- DBG(DBG_init, "SANE Genesys backend version %d.%d from %s\n",
- SANE_CURRENT_MAJOR, V_MINOR, PACKAGE_STRING);
+ DBG_HELPER_ARGS(dbg, "authorize %s null", authorize ? "!=" : "==");
+ DBG(DBG_init, "SANE Genesys backend from %s\n", PACKAGE_STRING);
+
+ if (!is_testing_mode()) {
#ifdef HAVE_LIBUSB
- DBG(DBG_init, "SANE Genesys backend built with libusb-1.0\n");
+ DBG(DBG_init, "SANE Genesys backend built with libusb-1.0\n");
#endif
#ifdef HAVE_LIBUSB_LEGACY
- DBG(DBG_init, "SANE Genesys backend built with libusb\n");
+ DBG(DBG_init, "SANE Genesys backend built with libusb\n");
#endif
+ }
- if (version_code)
- *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
-
- DBG(DBG_proc, "%s: authorize %s null\n", __func__, authorize ? "!=" : "==");
+ if (version_code) {
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
+ }
- /* init usb use */
- sanei_usb_init ();
+ if (!is_testing_mode()) {
+ sanei_usb_init();
+ }
/* init sanei_magic */
sanei_magic_init();
@@ -6163,9 +4845,15 @@ sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
s_scanners.init();
s_devices.init();
s_sane_devices.init();
+ s_sane_devices_data.init();
s_sane_devices_ptrs.init();
genesys_init_sensor_tables();
genesys_init_frontend_tables();
+ genesys_init_gpo_tables();
+ genesys_init_motor_tables();
+ genesys_init_motor_profile_tables();
+ genesys_init_usb_device_tables();
+
DBG(DBG_info, "%s: %s endian machine\n", __func__,
#ifdef WORDS_BIGENDIAN
@@ -6175,62 +4863,71 @@ sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
#endif
);
- /* cold-plug case :detection of allready connected scanners */
- status = probe_genesys_devices ();
-
- DBGCOMPLETED;
-
- return status;
+ // cold-plug case :detection of allready connected scanners
+ probe_genesys_devices();
}
-extern "C" SANE_Status sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize)
+SANE_GENESYS_API_LINKAGE
+SANE_Status sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_init_impl(version_code, authorize);
+ sane_init_impl(version_code, authorize);
});
}
void
sane_exit_impl(void)
{
- DBGSTART;
+ DBG_HELPER(dbg);
- sanei_usb_exit();
+ if (!is_testing_mode()) {
+ sanei_usb_exit();
+ }
run_functions_at_backend_exit();
-
- DBGCOMPLETED;
}
+SANE_GENESYS_API_LINKAGE
void sane_exit()
{
catch_all_exceptions(__func__, [](){ sane_exit_impl(); });
}
-SANE_Status
-sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_only)
+void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_only)
{
- DBG(DBG_proc, "%s: start: local_only = %s\n", __func__,
- local_only == SANE_TRUE ? "true" : "false");
+ DBG_HELPER_ARGS(dbg, "local_only = %s", local_only ? "true" : "false");
- /* hot-plug case : detection of newly connected scanners */
- sanei_usb_scan_devices ();
- probe_genesys_devices ();
+ if (!is_testing_mode()) {
+ // hot-plug case : detection of newly connected scanners */
+ sanei_usb_scan_devices();
+ }
+ probe_genesys_devices();
s_sane_devices->clear();
+ s_sane_devices_data->clear();
s_sane_devices_ptrs->clear();
s_sane_devices->reserve(s_devices->size());
+ s_sane_devices_data->reserve(s_devices->size());
s_sane_devices_ptrs->reserve(s_devices->size() + 1);
for (auto dev_it = s_devices->begin(); dev_it != s_devices->end();) {
- present = SANE_FALSE;
- sanei_usb_find_devices(dev_it->vendorId, dev_it->productId, check_present);
+
+ if (is_testing_mode()) {
+ present = true;
+ } else {
+ present = false;
+ sanei_usb_find_devices(dev_it->vendorId, dev_it->productId, check_present);
+ }
+
if (present) {
s_sane_devices->emplace_back();
+ s_sane_devices_data->emplace_back();
auto& sane_device = s_sane_devices->back();
- sane_device.name = dev_it->file_name;
+ auto& sane_device_data = s_sane_devices_data->back();
+ sane_device_data.name = dev_it->file_name;
+ sane_device.name = sane_device_data.name.c_str();
sane_device.vendor = dev_it->model->vendor;
sane_device.model = dev_it->model->model;
sane_device.type = "flatbed scanner";
@@ -6242,64 +4939,57 @@ sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_only)
}
s_sane_devices_ptrs->push_back(nullptr);
- *((SANE_Device ***)device_list) = s_sane_devices_ptrs->data();
-
- DBGCOMPLETED;
-
- return SANE_STATUS_GOOD;
+ *const_cast<SANE_Device***>(device_list) = s_sane_devices_ptrs->data();
}
+SANE_GENESYS_API_LINKAGE
SANE_Status sane_get_devices(const SANE_Device *** device_list, SANE_Bool local_only)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_get_devices_impl(device_list, local_only);
+ sane_get_devices_impl(device_list, local_only);
});
}
-SANE_Status
-sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
+static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
{
- DBG_HELPER(dbg);
- Genesys_Device *dev = nullptr;
- SANE_Status status = SANE_STATUS_GOOD;
- char *tmpstr;
-
- DBG(DBG_proc, "%s: devicename = `%s')\n", __func__, devicename);
+ DBG_HELPER_ARGS(dbg, "devicename = %s", devicename);
+ Genesys_Device* dev = nullptr;
/* devicename="" or devicename="genesys" are default values that use
* first available device
*/
- if (devicename[0] && strcmp ("genesys", devicename) != 0)
- {
+ if (devicename[0] && strcmp ("genesys", devicename) != 0) {
/* search for the given devicename in the device list */
for (auto& d : *s_devices) {
- if (strcmp(d.file_name, devicename) == 0) {
+ if (d.file_name == devicename) {
dev = &d;
break;
}
}
- if (!dev)
- {
- DBG(DBG_info, "%s: couldn't find `%s' in devlist, trying attach\n", __func__, devicename);
- RIE (attach (devicename, &dev, SANE_TRUE));
- }
- else
- DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->model->name);
- }
- else
- {
+ if (dev) {
+ DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->model->name);
+ } else if (is_testing_mode()) {
+ DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename);
+ } else {
+ DBG(DBG_info, "%s: couldn't find `%s' in devlist, trying attach\n", __func__,
+ devicename);
+ dbg.status("attach_device_by_name");
+ dev = attach_device_by_name(devicename, true);
+ dbg.clear();
+ }
+ } else {
// empty devicename or "genesys" -> use first device
if (!s_devices->empty()) {
dev = &s_devices->front();
- devicename = dev->file_name;
- DBG(DBG_info, "%s: empty devicename, trying `%s'\n", __func__, devicename);
- }
+ DBG(DBG_info, "%s: empty devicename, trying `%s'\n", __func__, dev->file_name.c_str());
+ }
}
- if (!dev)
- return SANE_STATUS_INVAL;
+ if (!dev) {
+ throw SaneException("could not find the device to open: %s", devicename);
+ }
if (dev->model->flags & GENESYS_FLAG_UNTESTED)
{
@@ -6311,77 +5001,74 @@ sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
DBG(DBG_error0, " scanner and what does (not) work.\n");
}
- dbg.vstatus("open device '%s'", dev->file_name);
- dev->usb_dev.open(dev->file_name);
- dbg.clear();
+ dbg.vstatus("open device '%s'", dev->file_name.c_str());
+ if (is_testing_mode()) {
+ auto interface = std::unique_ptr<TestScannerInterface>{new TestScannerInterface{dev}};
+ interface->set_checkpoint_callback(get_testing_checkpoint_callback());
+ dev->interface = std::move(interface);
+ } else {
+ dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}};
+ }
+ dev->interface->get_usb_device().open(dev->file_name.c_str());
+ dbg.clear();
s_scanners->push_back(Genesys_Scanner());
auto* s = &s_scanners->back();
s->dev = dev;
- s->scanning = SANE_FALSE;
- s->dev->parking = SANE_FALSE;
- s->dev->read_active = SANE_FALSE;
+ s->scanning = false;
+ s->dev->parking = false;
+ s->dev->read_active = false;
s->dev->force_calibration = 0;
- s->dev->line_interp = 0;
s->dev->line_count = 0;
- s->dev->segnb = 0;
- s->dev->binary=NULL;
*handle = s;
- if (!dev->already_initialized)
- sanei_genesys_init_structs (dev);
+ if (!dev->already_initialized) {
+ sanei_genesys_init_structs (dev);
+ }
- RIE (init_options (s));
+ init_options(s);
- if (sanei_genesys_init_cmd_set (s->dev) != SANE_STATUS_GOOD)
- {
- DBG(DBG_error0, "This device doesn't have a valid command set!!\n");
- return SANE_STATUS_IO_ERROR;
- }
+ sanei_genesys_init_cmd_set(s->dev);
- // FIXME: we create sensor tables for the sensor, this should happen when we know which sensor
- // we will select
- RIE (dev->model->cmd_set->init(dev));
+ // FIXME: we create sensor tables for the sensor, this should happen when we know which sensor
+ // we will select
+ dev->cmd_set->init(dev);
- /* some hardware capabilities are detected through sensors */
- RIE (s->dev->model->cmd_set->update_hardware_sensors (s));
+ // some hardware capabilities are detected through sensors
+ s->dev->cmd_set->update_hardware_sensors (s);
/* here is the place to fetch a stored calibration cache */
if (s->dev->force_calibration == 0)
{
- tmpstr=calibration_filename(s->dev);
- s->calibration_file = tmpstr;
- s->dev->calib_file = tmpstr;
+ auto path = calibration_filename(s->dev);
+ s->calibration_file = path;
+ s->dev->calib_file = path;
DBG(DBG_info, "%s: Calibration filename set to:\n", __func__);
DBG(DBG_info, "%s: >%s<\n", __func__, s->dev->calib_file.c_str());
- free(tmpstr);
catch_all_exceptions(__func__, [&]()
{
sanei_genesys_read_calibration(s->dev->calibration_cache, s->dev->calib_file);
});
}
-
- return SANE_STATUS_GOOD;
}
+SANE_GENESYS_API_LINKAGE
SANE_Status sane_open(SANE_String_Const devicename, SANE_Handle* handle)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_open_impl(devicename, handle);
+ sane_open_impl(devicename, handle);
});
}
void
sane_close_impl(SANE_Handle handle)
{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
+ DBG_HELPER(dbg);
/* remove handle from list of open handles: */
auto it = s_scanners->end();
@@ -6401,64 +5088,46 @@ sane_close_impl(SANE_Handle handle)
Genesys_Scanner* s = &*it;
/* eject document for sheetfed scanners */
- if (s->dev->model->is_sheetfed == SANE_TRUE)
- {
- s->dev->model->cmd_set->eject_document (s->dev);
+ if (s->dev->model->is_sheetfed) {
+ catch_all_exceptions(__func__, [&](){ s->dev->cmd_set->eject_document(s->dev); });
}
else
{
/* in case scanner is parking, wait for the head
* to reach home position */
- if(s->dev->parking==SANE_TRUE)
- {
- status = sanei_genesys_wait_for_home (s->dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to wait for head to park: %s\n", __func__,
- sane_strstatus(status));
- }
+ if (s->dev->parking) {
+ sanei_genesys_wait_for_home(s->dev);
}
}
- /* enable power saving before leaving */
- status = s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to enable power saving mode: %s\n", __func__,
- sane_strstatus(status));
- }
+ // enable power saving before leaving
+ s->dev->cmd_set->save_power(s->dev, true);
// here is the place to store calibration cache
- if (s->dev->force_calibration == 0) {
+ if (s->dev->force_calibration == 0 && !is_testing_mode()) {
catch_all_exceptions(__func__, [&](){ write_calibration(s->dev->calibration_cache,
s->dev->calib_file); });
}
- s->dev->already_initialized = SANE_FALSE;
-
- /* for an handful of bytes .. */
- free ((void *)(size_t)s->opt[OPT_RESOLUTION].constraint.word_list);
- free ((void *)(size_t)s->opt[OPT_TL_X].constraint.range);
- free ((void *)(size_t)s->opt[OPT_TL_Y].constraint.range);
+ s->dev->already_initialized = false;
s->dev->clear();
- /* LAMP OFF : same register across all the ASICs */
- sanei_genesys_write_register (s->dev, 0x03, 0x00);
+ // LAMP OFF : same register across all the ASICs */
+ s->dev->interface->write_register(0x03, 0x00);
- catch_all_exceptions(__func__, [&](){ s->dev->usb_dev.clear_halt(); });
+ catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().clear_halt(); });
// we need this to avoid these ASIC getting stuck in bulk writes
- catch_all_exceptions(__func__, [&](){ s->dev->usb_dev.reset(); });
+ catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().reset(); });
// not freeing s->dev because it's in the dev list
- catch_all_exceptions(__func__, [&](){ s->dev->usb_dev.close(); });
+ catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().close(); });
s_scanners->erase(it);
-
- DBGCOMPLETED;
}
+SANE_GENESYS_API_LINKAGE
void sane_close(SANE_Handle handle)
{
catch_all_exceptions(__func__, [=]()
@@ -6470,19 +5139,22 @@ void sane_close(SANE_Handle handle)
const SANE_Option_Descriptor *
sane_get_option_descriptor_impl(SANE_Handle handle, SANE_Int option)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
+ DBG_HELPER(dbg);
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
+
+ if (static_cast<unsigned>(option) >= NUM_OPTIONS) {
+ return nullptr;
+ }
- if ((unsigned) option >= NUM_OPTIONS)
- return 0;
DBG(DBG_io2, "%s: option = %s (%d)\n", __func__, s->opt[option].name, option);
return s->opt + option;
}
-const SANE_Option_Descriptor *
-sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+SANE_GENESYS_API_LINKAGE
+const SANE_Option_Descriptor* sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
{
- const SANE_Option_Descriptor* ret = NULL;
+ const SANE_Option_Descriptor* ret = nullptr;
catch_all_exceptions(__func__, [&]()
{
ret = sane_get_option_descriptor_impl(handle, option);
@@ -6490,18 +5162,46 @@ sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
return ret;
}
-/* gets an option , called by sane_control_option */
-static SANE_Status
-get_option_value (Genesys_Scanner * s, int option, void *val)
+static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int option, void* val)
{
+ switch (s.opt[option].type) {
+ case SANE_TYPE_INT: {
+ dbg.vlog(DBG_proc, "value: %d", *reinterpret_cast<SANE_Word*>(val));
+ return;
+ }
+ case SANE_TYPE_BOOL: {
+ dbg.vlog(DBG_proc, "value: %s", *reinterpret_cast<SANE_Bool*>(val) ? "true" : "false");
+ return;
+ }
+ case SANE_TYPE_FIXED: {
+ dbg.vlog(DBG_proc, "value: %f", SANE_UNFIX(*reinterpret_cast<SANE_Word*>(val)));
+ return;
+ }
+ case SANE_TYPE_STRING: {
+ dbg.vlog(DBG_proc, "value: %s", reinterpret_cast<char*>(val));
+ return;
+ }
+ default: break;
+ }
+ dbg.log(DBG_proc, "value: (non-printable)");
+}
+
+static void get_option_value(Genesys_Scanner* s, int option, void* val)
+{
+ DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
unsigned int i;
- SANE_Word* table = nullptr;
+ SANE_Word* table = nullptr;
std::vector<uint16_t> gamma_table;
unsigned option_size = 0;
- SANE_Status status = SANE_STATUS_GOOD;
- // FIXME: we should pick correct sensor here
- const Genesys_Sensor& sensor = sanei_genesys_find_sensor_any(s->dev);
+ const Genesys_Sensor* sensor = nullptr;
+ if (sanei_genesys_has_sensor(s->dev, s->dev->settings.xres, s->dev->settings.get_channels(),
+ s->dev->settings.scan_method))
+ {
+ sensor = &sanei_genesys_find_sensor(s->dev, s->dev->settings.xres,
+ s->dev->settings.get_channels(),
+ s->dev->settings.scan_method);
+ }
switch (option)
{
@@ -6537,9 +5237,6 @@ get_option_value (Genesys_Scanner * s, int option, void *val)
case OPT_THRESHOLD_CURVE:
*reinterpret_cast<SANE_Word*>(val) = s->threshold_curve;
break;
- case OPT_DISABLE_DYNAMIC_LINEART:
- *reinterpret_cast<SANE_Word*>(val) = s->disable_dynamic_lineart;
- break;
case OPT_DISABLE_INTERPOLATION:
*reinterpret_cast<SANE_Word*>(val) = s->disable_interpolation;
break;
@@ -6591,18 +5288,21 @@ get_option_value (Genesys_Scanner * s, int option, void *val)
std::strcpy(reinterpret_cast<char*>(val), s->calibration_file.c_str());
break;
case OPT_SOURCE:
- std::strcpy(reinterpret_cast<char*>(val), s->source.c_str());
+ std::strcpy(reinterpret_cast<char*>(val), scan_method_to_option_string(s->scan_method));
break;
/* word array options */
case OPT_GAMMA_VECTOR:
- table = (SANE_Word *) val;
+ if (!sensor)
+ throw SaneException("Unsupported scanner mode selected");
+
+ table = reinterpret_cast<SANE_Word*>(val);
if (s->color_filter == "Red") {
- gamma_table = get_gamma_table(s->dev, sensor, GENESYS_RED);
+ gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_RED);
} else if (s->color_filter == "Blue") {
- gamma_table = get_gamma_table(s->dev, sensor, GENESYS_BLUE);
+ gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE);
} else {
- gamma_table = get_gamma_table(s->dev, sensor, GENESYS_GREEN);
+ gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN);
}
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
@@ -6613,8 +5313,11 @@ get_option_value (Genesys_Scanner * s, int option, void *val)
}
break;
case OPT_GAMMA_VECTOR_R:
- table = (SANE_Word *) val;
- gamma_table = get_gamma_table(s->dev, sensor, GENESYS_RED);
+ if (!sensor)
+ throw SaneException("Unsupported scanner mode selected");
+
+ table = reinterpret_cast<SANE_Word*>(val);
+ gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_RED);
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
throw std::runtime_error("The size of the gamma tables does not match");
@@ -6624,8 +5327,11 @@ get_option_value (Genesys_Scanner * s, int option, void *val)
}
break;
case OPT_GAMMA_VECTOR_G:
- table = (SANE_Word *) val;
- gamma_table = get_gamma_table(s->dev, sensor, GENESYS_GREEN);
+ if (!sensor)
+ throw SaneException("Unsupported scanner mode selected");
+
+ table = reinterpret_cast<SANE_Word*>(val);
+ gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN);
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
throw std::runtime_error("The size of the gamma tables does not match");
@@ -6635,8 +5341,11 @@ get_option_value (Genesys_Scanner * s, int option, void *val)
}
break;
case OPT_GAMMA_VECTOR_B:
- table = (SANE_Word *) val;
- gamma_table = get_gamma_table(s->dev, sensor, GENESYS_BLUE);
+ if (!sensor)
+ throw SaneException("Unsupported scanner mode selected");
+
+ table = reinterpret_cast<SANE_Word*>(val);
+ gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE);
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
throw std::runtime_error("The size of the gamma tables does not match");
@@ -6654,25 +5363,35 @@ get_option_value (Genesys_Scanner * s, int option, void *val)
case OPT_OCR_SW:
case OPT_POWER_SW:
case OPT_EXTRA_SW:
- RIE (s->dev->model->cmd_set->update_hardware_sensors (s));
- *(SANE_Bool *) val = s->buttons[genesys_option_to_button(option)].read();
- break;
- case OPT_NEED_CALIBRATION_SW:
- /* scanner needs calibration for current mode unless a matching
- * calibration cache is found */
- *(SANE_Bool *) val = SANE_TRUE;
- for (auto& cache : s->dev->calibration_cache)
- {
- if (s->dev->model->cmd_set->is_compatible_calibration(s->dev, sensor, &cache, SANE_FALSE))
- {
- *(SANE_Bool *) val = SANE_FALSE;
- }
- }
- break;
+ s->dev->cmd_set->update_hardware_sensors(s);
+ *reinterpret_cast<SANE_Bool*>(val) = s->buttons[genesys_option_to_button(option)].read();
+ break;
+
+ case OPT_NEED_CALIBRATION_SW: {
+ if (!sensor) {
+ throw SaneException("Unsupported scanner mode selected");
+ }
+
+ // scanner needs calibration for current mode unless a matching calibration cache is
+ // found
+
+ bool result = true;
+
+ auto session = s->dev->cmd_set->calculate_scan_session(s->dev, *sensor,
+ s->dev->settings);
+
+ for (auto& cache : s->dev->calibration_cache) {
+ if (sanei_genesys_is_compatible_calibration(s->dev, session, &cache, false)) {
+ *reinterpret_cast<SANE_Bool*>(val) = SANE_FALSE;
+ }
+ }
+ *reinterpret_cast<SANE_Bool*>(val) = result;
+ break;
+ }
default:
DBG(DBG_warn, "%s: can't get unknown option %d\n", __func__, option);
}
- return status;
+ print_option(dbg, *s, option, val);
}
/** @brief set calibration file value
@@ -6702,109 +5421,100 @@ static void set_calibration_value(Genesys_Scanner* s, const char* val)
}
/* sets an option , called by sane_control_option */
-static SANE_Status
-set_option_value (Genesys_Scanner * s, int option, void *val,
- SANE_Int * myinfo)
+static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int* myinfo)
{
- SANE_Status status = SANE_STATUS_GOOD;
+ DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
+ print_option(dbg, *s, option, val);
+
SANE_Word *table;
unsigned int i;
- SANE_Range *x_range, *y_range;
unsigned option_size = 0;
- // FIXME: we should modify device-specific sensor
- auto& sensor = sanei_genesys_find_sensor_any_for_write(s->dev);
-
switch (option)
{
case OPT_TL_X:
s->pos_top_left_x = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_TL_Y:
s->pos_top_left_y = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_BR_X:
s->pos_bottom_right_x = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_BR_Y:
s->pos_bottom_right_y = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_RESOLUTION:
s->resolution = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_THRESHOLD:
s->threshold = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_THRESHOLD_CURVE:
s->threshold_curve = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_DISABLE_DYNAMIC_LINEART:
- s->disable_dynamic_lineart = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_SWCROP:
s->swcrop = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_SWDESKEW:
s->swdeskew = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_DESPECK:
s->despeck = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_SWDEROTATE:
s->swderotate = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_SWSKIP:
s->swskip = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_DISABLE_INTERPOLATION:
s->disable_interpolation = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_LAMP_OFF:
s->lamp_off = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_PREVIEW:
s->preview = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_BRIGHTNESS:
s->brightness = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_CONTRAST:
s->contrast = *reinterpret_cast<SANE_Word*>(val);
- RIE (calc_parameters(s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
case OPT_SWDESPECK:
@@ -6814,7 +5524,7 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
} else {
DISABLE(OPT_DESPECK);
}
- RIE (calc_parameters (s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
break;
/* software enhancement functions only apply to 8 or 1 bits data */
@@ -6842,45 +5552,21 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
ENABLE(OPT_CONTRAST);
ENABLE(OPT_BRIGHTNESS);
}
- RIE (calc_parameters (s));
+ calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
break;
- case OPT_SOURCE:
- if (s->source != reinterpret_cast<const char*>(val)) {
- s->source = reinterpret_cast<const char*>(val);
+ case OPT_SOURCE: {
+ auto scan_method = option_string_to_scan_method(reinterpret_cast<const char*>(val));
+ if (s->scan_method != scan_method) {
+ s->scan_method = scan_method;
- // change geometry constraint to the new source value
- if (s->source == STR_FLATBED)
- {
- x_range=create_range(s->dev->model->x_size);
- y_range=create_range(s->dev->model->y_size);
- }
- else
- {
- x_range=create_range(s->dev->model->x_size_ta);
- y_range=create_range(s->dev->model->y_size_ta);
- }
- if(x_range==NULL || y_range==NULL)
- {
- return SANE_STATUS_NO_MEM;
- }
+ set_xy_range_option_values(*s);
+ set_resolution_option_values(*s, false);
- /* assign new values */
- free((void *)(size_t)s->opt[OPT_TL_X].constraint.range);
- free((void *)(size_t)s->opt[OPT_TL_Y].constraint.range);
- s->opt[OPT_TL_X].constraint.range = x_range;
- s->pos_top_left_x = 0;
- s->opt[OPT_TL_Y].constraint.range = y_range;
- s->pos_top_left_y = 0;
- s->opt[OPT_BR_X].constraint.range = x_range;
- s->pos_bottom_right_x = x_range->max;
- s->opt[OPT_BR_Y].constraint.range = y_range;
- s->pos_bottom_right_y = y_range->max;
-
- /* signals reload */
- *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
- }
- break;
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+ }
case OPT_MODE:
s->mode = reinterpret_cast<const char*>(val);
@@ -6889,36 +5575,30 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
ENABLE (OPT_THRESHOLD);
ENABLE (OPT_THRESHOLD_CURVE);
DISABLE (OPT_BIT_DEPTH);
- if (s->dev->model->asic_type != GENESYS_GL646 || !s->dev->model->is_cis)
- {
- ENABLE (OPT_COLOR_FILTER);
- }
- ENABLE (OPT_DISABLE_DYNAMIC_LINEART);
+ if (s->dev->model->asic_type != AsicType::GL646 || !s->dev->model->is_cis) {
+ ENABLE(OPT_COLOR_FILTER);
+ }
}
else
{
DISABLE (OPT_THRESHOLD);
DISABLE (OPT_THRESHOLD_CURVE);
- DISABLE (OPT_DISABLE_DYNAMIC_LINEART);
if (s->mode == SANE_VALUE_SCAN_MODE_GRAY)
{
- if (s->dev->model->asic_type != GENESYS_GL646 || !s->dev->model->is_cis)
- {
- ENABLE (OPT_COLOR_FILTER);
- }
+ if (s->dev->model->asic_type != AsicType::GL646 || !s->dev->model->is_cis) {
+ ENABLE(OPT_COLOR_FILTER);
+ }
create_bpp_list (s, s->dev->model->bpp_gray_values);
+ s->bit_depth = s->dev->model->bpp_gray_values[0];
}
else
{
DISABLE (OPT_COLOR_FILTER);
create_bpp_list (s, s->dev->model->bpp_color_values);
+ s->bit_depth = s->dev->model->bpp_color_values[0];
}
- if (s->bpp_list[0] < 2)
- DISABLE (OPT_BIT_DEPTH);
- else
- ENABLE (OPT_BIT_DEPTH);
}
- RIE (calc_parameters (s));
+ calc_parameters(s);
/* if custom gamma, toggle gamma table options according to the mode */
if (s->custom_gamma)
@@ -6943,7 +5623,7 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
break;
case OPT_COLOR_FILTER:
s->color_filter = reinterpret_cast<const char*>(val);
- RIE (calc_parameters (s));
+ calc_parameters(s);
break;
case OPT_CALIBRATION_FILE:
if (s->dev->force_calibration == 0) {
@@ -6953,14 +5633,14 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
case OPT_LAMP_OFF_TIME:
if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) {
s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val);
- RIE(s->dev->model->cmd_set->set_powersaving(s->dev, s->lamp_off_time));
+ s->dev->cmd_set->set_powersaving(s->dev, s->lamp_off_time);
}
break;
case OPT_EXPIRATION_TIME:
if (*reinterpret_cast<SANE_Word*>(val) != s->expiration_time) {
s->expiration_time = *reinterpret_cast<SANE_Word*>(val);
// BUG: this is most likely not intended behavior, found out during refactor
- RIE(s->dev->model->cmd_set->set_powersaving(s->dev, s->expiration_time));
+ s->dev->cmd_set->set_powersaving(s->dev, s->expiration_time);
}
break;
@@ -6997,7 +5677,7 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
break;
case OPT_GAMMA_VECTOR:
- table = (SANE_Word *) val;
+ table = reinterpret_cast<SANE_Word*>(val);
option_size = s->opt[option].size / sizeof (SANE_Word);
s->dev->gamma_override_tables[GENESYS_RED].resize(option_size);
@@ -7010,7 +5690,7 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
}
break;
case OPT_GAMMA_VECTOR_R:
- table = (SANE_Word *) val;
+ table = reinterpret_cast<SANE_Word*>(val);
option_size = s->opt[option].size / sizeof (SANE_Word);
s->dev->gamma_override_tables[GENESYS_RED].resize(option_size);
for (i = 0; i < option_size; i++) {
@@ -7018,7 +5698,7 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
}
break;
case OPT_GAMMA_VECTOR_G:
- table = (SANE_Word *) val;
+ table = reinterpret_cast<SANE_Word*>(val);
option_size = s->opt[option].size / sizeof (SANE_Word);
s->dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
for (i = 0; i < option_size; i++) {
@@ -7026,27 +5706,29 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
}
break;
case OPT_GAMMA_VECTOR_B:
- table = (SANE_Word *) val;
+ table = reinterpret_cast<SANE_Word*>(val);
option_size = s->opt[option].size / sizeof (SANE_Word);
s->dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
for (i = 0; i < option_size; i++) {
s->dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
}
break;
- case OPT_CALIBRATE:
- status = s->dev->model->cmd_set->save_power (s->dev, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to disable power saving mode: %s\n", __func__,
- sane_strstatus(status));
- }
- else
- status = genesys_scanner_calibration(s->dev, sensor);
- /* not critical if this fails*/
- s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE);
- /* signals that sensors will have to be read again */
- *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
- break;
+ case OPT_CALIBRATE: {
+ auto& sensor = sanei_genesys_find_sensor_for_write(s->dev, s->dev->settings.xres,
+ s->dev->settings.get_channels(),
+ s->dev->settings.scan_method);
+ catch_all_exceptions(__func__, [&]()
+ {
+ s->dev->cmd_set->save_power(s->dev, false);
+ genesys_scanner_calibration(s->dev, sensor);
+ });
+ catch_all_exceptions(__func__, [&]()
+ {
+ s->dev->cmd_set->save_power(s->dev, true);
+ });
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ }
case OPT_CLEAR_CALIBRATION:
s->dev->calibration_cache.clear();
@@ -7064,116 +5746,93 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
*myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
break;
+ case OPT_IGNORE_OFFSETS: {
+ s->dev->ignore_offsets = true;
+ break;
+ }
default:
DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option);
}
- return status;
}
/* sets and gets scanner option values */
-SANE_Status
-sane_control_option_impl(SANE_Handle handle, SANE_Int option,
- SANE_Action action, void *val, SANE_Int * info)
+void sane_control_option_impl(SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
- SANE_Status status = SANE_STATUS_GOOD;
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
+ auto action_str = (action == SANE_ACTION_GET_VALUE) ? "get" :
+ (action == SANE_ACTION_SET_VALUE) ? "set" :
+ (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown";
+ DBG_HELPER_ARGS(dbg, "action = %s, option = %s (%d)", action_str,
+ s->opt[option].name, option);
+
SANE_Word cap;
SANE_Int myinfo = 0;
- DBG(DBG_io2, "%s: start: action = %s, option = %s (%d)\n", __func__,
- (action == SANE_ACTION_GET_VALUE) ? "get" : (action == SANE_ACTION_SET_VALUE) ?
- "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
- s->opt[option].name, option);
-
- if (info)
- *info = 0;
-
- if (s->scanning)
- {
- DBG(DBG_warn, "%s: don't call this function while scanning (option = %s (%d))\n", __func__,
- s->opt[option].name, option);
+ if (info) {
+ *info = 0;
+ }
- return SANE_STATUS_DEVICE_BUSY;
+ if (s->scanning) {
+ throw SaneException(SANE_STATUS_DEVICE_BUSY,
+ "don't call this function while scanning (option = %s (%d))",
+ s->opt[option].name, option);
}
- if (option >= NUM_OPTIONS || option < 0)
- {
- DBG(DBG_warn, "%s: option %d >= NUM_OPTIONS || option < 0\n", __func__, option);
- return SANE_STATUS_INVAL;
+ if (option >= NUM_OPTIONS || option < 0) {
+ throw SaneException("option %d >= NUM_OPTIONS || option < 0", option);
}
cap = s->opt[option].cap;
- if (!SANE_OPTION_IS_ACTIVE (cap))
- {
- DBG(DBG_warn, "%s: option %d is inactive\n", __func__, option);
- return SANE_STATUS_INVAL;
+ if (!SANE_OPTION_IS_ACTIVE (cap)) {
+ throw SaneException("option %d is inactive", option);
}
- switch (action)
- {
- case SANE_ACTION_GET_VALUE:
- status = get_option_value (s, option, val);
- break;
-
- case SANE_ACTION_SET_VALUE:
- if (!SANE_OPTION_IS_SETTABLE (cap))
- {
- DBG(DBG_warn, "%s: option %d is not settable\n", __func__, option);
- return SANE_STATUS_INVAL;
- }
+ switch (action) {
+ case SANE_ACTION_GET_VALUE:
+ get_option_value(s, option, val);
+ break;
- status = sanei_constrain_value (s->opt + option, val, &myinfo);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_warn, "%s: sanei_constrain_value returned %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (cap)) {
+ throw SaneException("option %d is not settable", option);
+ }
- status = set_option_value (s, option, val, &myinfo);
- break;
+ TIE(sanei_constrain_value(s->opt + option, val, &myinfo));
- case SANE_ACTION_SET_AUTO:
- DBG(DBG_error,
- "%s: SANE_ACTION_SET_AUTO unsupported since no option has SANE_CAP_AUTOMATIC\n",
- __func__);
- status = SANE_STATUS_INVAL;
- break;
+ set_option_value(s, option, val, &myinfo);
+ break;
- default:
- DBG(DBG_warn, "%s: unknown action %d for option %d\n", __func__, action, option);
- status = SANE_STATUS_INVAL;
- break;
+ case SANE_ACTION_SET_AUTO:
+ throw SaneException("SANE_ACTION_SET_AUTO unsupported since no option "
+ "has SANE_CAP_AUTOMATIC");
+ default:
+ throw SaneException("unknown action %d for option %d", action, option);
}
if (info)
*info = myinfo;
-
- DBG(DBG_io2, "%s: exit\n", __func__);
- return status;
}
+SANE_GENESYS_API_LINKAGE
SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option,
- SANE_Action action, void *val, SANE_Int * info)
+ SANE_Action action, void *val, SANE_Int * info)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_control_option_impl(handle, option, action, val, info);
+ sane_control_option_impl(handle, option, action, val, info);
});
}
-SANE_Status sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)
+void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
+ DBG_HELPER(dbg);
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
/* don't recompute parameters once data reading is active, ie during scan */
- if(s->dev->read_active == SANE_FALSE)
- {
- RIE (calc_parameters (s));
+ if (!s->dev->read_active) {
+ calc_parameters(s);
}
if (params)
{
@@ -7184,56 +5843,46 @@ SANE_Status sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params
* don't know the real document height.
* We don't do that doing buffering image for digital processing
*/
- if (s->dev->model->is_sheetfed == SANE_TRUE
- && s->dev->buffer_image == SANE_FALSE
- && s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max)
- {
+ if (s->dev->model->is_sheetfed && !s->dev->buffer_image &&
+ s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max)
+ {
params->lines = -1;
}
}
-
- DBGCOMPLETED;
-
- return SANE_STATUS_GOOD;
+ debug_dump(DBG_proc, *params);
}
+SANE_GENESYS_API_LINKAGE
SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters* params)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_get_parameters_impl(handle, params);
+ sane_get_parameters_impl(handle, params);
});
}
-SANE_Status sane_start_impl(SANE_Handle handle)
+void sane_start_impl(SANE_Handle handle)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
- SANE_Status status=SANE_STATUS_GOOD;
-
- DBGSTART;
+ DBG_HELPER(dbg);
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
- if (s->pos_top_left_x >= s->pos_bottom_right_x)
- {
- DBG(DBG_error0, "%s: top left x >= bottom right x --- exiting\n", __func__);
- return SANE_STATUS_INVAL;
+ if (s->pos_top_left_x >= s->pos_bottom_right_x) {
+ throw SaneException("top left x >= bottom right x");
}
- if (s->pos_top_left_y >= s->pos_bottom_right_y)
- {
- DBG(DBG_error0, "%s: top left y >= bottom right y --- exiting\n", __func__);
- return SANE_STATUS_INVAL;
+ if (s->pos_top_left_y >= s->pos_bottom_right_y) {
+ throw SaneException("top left y >= bottom right y");
}
/* First make sure we have a current parameter set. Some of the
parameters will be overwritten below, but that's OK. */
- RIE (calc_parameters (s));
- RIE(genesys_start_scan(s->dev, s->lamp_off));
+ calc_parameters(s);
+ genesys_start_scan(s->dev, s->lamp_off);
- s->scanning = SANE_TRUE;
+ s->scanning = true;
/* allocate intermediate buffer when doing dynamic lineart */
- if(s->dev->settings.dynamic_lineart==SANE_TRUE)
- {
+ if (s->dev->settings.scan_mode == ScanColorMode::LINEART) {
s->dev->binarize_buffer.clear();
s->dev->binarize_buffer.alloc(s->dev->settings.pixels);
s->dev->local_buffer.clear();
@@ -7246,101 +5895,90 @@ SANE_Status sane_start_impl(SANE_Handle handle)
* at the end */
if (s->dev->buffer_image)
{
- RIE(genesys_buffer_image(s));
+ genesys_buffer_image(s);
/* check if we need to skip this page, sheetfed scanners
* can go to next doc while flatbed ones can't */
if (s->swskip > 0 && IS_ACTIVE(OPT_SWSKIP)) {
- status = sanei_magic_isBlank(&s->params,
- s->dev->img_buffer.data(),
- SANE_UNFIX(s->swskip));
- if(status == SANE_STATUS_NO_DOCS)
- {
- if (s->dev->model->is_sheetfed == SANE_TRUE)
- {
- DBG(DBG_info, "%s: blank page, recurse\n", __func__);
- return sane_start(handle);
- }
- return status;
+ auto status = sanei_magic_isBlank(&s->params,
+ s->dev->img_buffer.data(),
+ SANE_UNFIX(s->swskip));
+
+ if (status == SANE_STATUS_NO_DOCS && s->dev->model->is_sheetfed) {
+ DBG(DBG_info, "%s: blank page, recurse\n", __func__);
+ sane_start(handle);
+ return;
+ }
+
+ if (status != SANE_STATUS_GOOD) {
+ throw SaneException(status);
}
}
if (s->swdeskew) {
- const auto& sensor = sanei_genesys_find_sensor(s->dev, s->dev->settings.xres,
- s->dev->settings.scan_method);
- RIE(genesys_deskew(s, sensor));
+ const auto& sensor = sanei_genesys_find_sensor(s->dev, s->dev->settings.xres,
+ s->dev->settings.get_channels(),
+ s->dev->settings.scan_method);
+ catch_all_exceptions(__func__, [&](){ genesys_deskew(s, sensor); });
}
if (s->swdespeck) {
- RIE(genesys_despeck(s));
+ catch_all_exceptions(__func__, [&](){ genesys_despeck(s); });
}
if(s->swcrop) {
- RIE(genesys_crop(s));
+ catch_all_exceptions(__func__, [&](){ genesys_crop(s); });
}
if(s->swderotate) {
- RIE(genesys_derotate(s));
+ catch_all_exceptions(__func__, [&](){ genesys_derotate(s); });
}
}
-
- DBGCOMPLETED;
- return status;
}
+SANE_GENESYS_API_LINKAGE
SANE_Status sane_start(SANE_Handle handle)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_start_impl(handle);
+ sane_start_impl(handle);
});
}
-SANE_Status
-sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
+void sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
+ DBG_HELPER(dbg);
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
Genesys_Device *dev;
- SANE_Status status=SANE_STATUS_GOOD;
size_t local_len;
- if (!s)
- {
- DBG(DBG_error, "%s: handle is null!\n", __func__);
- return SANE_STATUS_INVAL;
+ if (!s) {
+ throw SaneException("handle is nullptr");
}
dev=s->dev;
- if (!dev)
- {
- DBG(DBG_error, "%s: dev is null!\n", __func__);
- return SANE_STATUS_INVAL;
+ if (!dev) {
+ throw SaneException("dev is nullptr");
}
- if (!buf)
- {
- DBG(DBG_error, "%s: buf is null!\n", __func__);
- return SANE_STATUS_INVAL;
+ if (!buf) {
+ throw SaneException("buf is nullptr");
}
- if (!len)
- {
- DBG(DBG_error, "%s: len is null!\n", __func__);
- return SANE_STATUS_INVAL;
+ if (!len) {
+ throw SaneException("len is nullptr");
}
*len = 0;
- if (!s->scanning)
- {
- DBG(DBG_warn, "%s: scan was cancelled, is over or has not been initiated yet\n", __func__);
- return SANE_STATUS_CANCELLED;
+ if (!s->scanning) {
+ throw SaneException(SANE_STATUS_CANCELLED,
+ "scan was cancelled, is over or has not been initiated yet");
}
DBG(DBG_proc, "%s: start, %d maximum bytes required\n", __func__, max_len);
- DBG(DBG_io2, "%s: bytes_to_read=%lu, total_bytes_read=%lu\n", __func__,
- (u_long) dev->total_bytes_to_read, (u_long) dev->total_bytes_read);
- DBG(DBG_io2, "%s: physical bytes to read = %lu\n", __func__, (u_long) dev->read_bytes_left);
+ DBG(DBG_io2, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__,
+ dev->total_bytes_to_read, dev->total_bytes_read);
if(dev->total_bytes_read>=dev->total_bytes_to_read)
{
@@ -7348,14 +5986,13 @@ sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int*
/* issue park command immediatly in case scanner can handle it
* so we save time */
- if (dev->model->is_sheetfed == SANE_FALSE
- && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT)
- && dev->parking == SANE_FALSE)
+ if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) &&
+ !dev->parking)
{
- dev->model->cmd_set->slow_back_home (dev, SANE_FALSE);
- dev->parking = SANE_TRUE;
+ dev->cmd_set->move_back_home(dev, false);
+ dev->parking = true;
}
- return SANE_STATUS_EOF;
+ throw SaneException(SANE_STATUS_EOF);
}
local_len = max_len;
@@ -7366,36 +6003,31 @@ sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int*
{
/* dynamic lineart is another kind of digital processing that needs
* another layer of buffering on top of genesys_read_ordered_data */
- if(dev->settings.dynamic_lineart==SANE_TRUE)
- {
+ if (dev->settings.scan_mode == ScanColorMode::LINEART) {
/* if buffer is empty, fill it with genesys_read_ordered_data */
if(dev->binarize_buffer.avail() == 0)
{
/* store gray data */
local_len=dev->local_buffer.size();
dev->local_buffer.reset();
- status = genesys_read_ordered_data (dev, dev->local_buffer.get_write_pos(local_len),
- &local_len);
+ genesys_read_ordered_data(dev, dev->local_buffer.get_write_pos(local_len),
+ &local_len);
dev->local_buffer.produce(local_len);
- /* binarize data is read successful */
- if(status==SANE_STATUS_GOOD)
- {
- dev->binarize_buffer.reset();
- genesys_gray_lineart (dev,
- dev->local_buffer.get_read_pos(),
- dev->binarize_buffer.get_write_pos(local_len / 8),
- dev->settings.pixels,
- local_len/dev->settings.pixels,
- dev->settings.threshold);
- dev->binarize_buffer.produce(local_len / 8);
+ dev->binarize_buffer.reset();
+ if (!is_testing_mode()) {
+ genesys_gray_lineart(dev, dev->local_buffer.get_read_pos(),
+ dev->binarize_buffer.get_write_pos(local_len / 8),
+ dev->settings.pixels,
+ local_len / dev->settings.pixels,
+ dev->settings.threshold);
}
-
+ dev->binarize_buffer.produce(local_len / 8);
}
/* return data from lineart buffer if any, up to the available amount */
local_len = max_len;
- if((size_t)max_len>dev->binarize_buffer.avail())
+ if (static_cast<std::size_t>(max_len) > dev->binarize_buffer.avail())
{
local_len=dev->binarize_buffer.avail();
}
@@ -7407,8 +6039,8 @@ sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int*
}
else
{
- /* most usual case, direct read of data from scanner */
- status = genesys_read_ordered_data (dev, buf, &local_len);
+ // most usual case, direct read of data from scanner */
+ genesys_read_ordered_data(dev, buf, &local_len);
}
}
else /* read data from buffer */
@@ -7422,145 +6054,103 @@ sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int*
}
*len = local_len;
- if(local_len>(size_t)max_len)
- {
+ if (local_len > static_cast<std::size_t>(max_len)) {
fprintf (stderr, "[genesys] sane_read: returning incorrect length!!\n");
}
DBG(DBG_proc, "%s: %d bytes returned\n", __func__, *len);
- return status;
}
+SANE_GENESYS_API_LINKAGE
SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_read_impl(handle, buf, max_len, len);
+ sane_read_impl(handle, buf, max_len, len);
});
}
void sane_cancel_impl(SANE_Handle handle)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
-
- /* end binary logging if needed */
- if (s->dev->binary!=NULL)
- {
- fclose(s->dev->binary);
- s->dev->binary=NULL;
- }
+ DBG_HELPER(dbg);
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
- s->scanning = SANE_FALSE;
- s->dev->read_active = SANE_FALSE;
+ s->scanning = false;
+ s->dev->read_active = false;
s->dev->img_buffer.clear();
/* no need to end scan if we are parking the head */
- if(s->dev->parking==SANE_FALSE)
- {
- status = s->dev->model->cmd_set->end_scan(s->dev, &s->dev->reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return;
- }
+ if (!s->dev->parking) {
+ s->dev->cmd_set->end_scan(s->dev, &s->dev->reg, true);
}
/* park head if flatbed scanner */
- if (s->dev->model->is_sheetfed == SANE_FALSE)
- {
- if(s->dev->parking==SANE_FALSE)
- {
- status = s->dev->model->cmd_set->slow_back_home (s->dev, s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move scanhead to home position: %s\n", __func__,
- sane_strstatus(status));
- return;
- }
+ if (!s->dev->model->is_sheetfed) {
+ if (!s->dev->parking) {
+ s->dev->cmd_set->move_back_home (s->dev, s->dev->model->flags &
+ GENESYS_FLAG_MUST_WAIT);
+
s->dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
}
}
else
{ /* in case of sheetfed scanners, we have to eject the document if still present */
- status = s->dev->model->cmd_set->eject_document (s->dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to eject document: %s\n", __func__, sane_strstatus(status));
- return;
- }
+ s->dev->cmd_set->eject_document(s->dev);
}
/* enable power saving mode unless we are parking .... */
- if(s->dev->parking==SANE_FALSE)
- {
- status = s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to enable power saving mode: %s\n", __func__,
- sane_strstatus(status));
- return;
- }
+ if (!s->dev->parking) {
+ s->dev->cmd_set->save_power(s->dev, true);
}
- DBGCOMPLETED;
return;
}
+SANE_GENESYS_API_LINKAGE
void sane_cancel(SANE_Handle handle)
{
catch_all_exceptions(__func__, [=]() { sane_cancel_impl(handle); });
}
-SANE_Status
-sane_set_io_mode_impl(SANE_Handle handle, SANE_Bool non_blocking)
+void sane_set_io_mode_impl(SANE_Handle handle, SANE_Bool non_blocking)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
-
- DBG(DBG_proc, "%s: handle = %p, non_blocking = %s\n", __func__, handle,
- non_blocking == SANE_TRUE ? "true" : "false");
+ DBG_HELPER_ARGS(dbg, "handle = %p, non_blocking = %s", handle,
+ non_blocking == SANE_TRUE ? "true" : "false");
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
- if (!s->scanning)
- {
- DBG(DBG_error, "%s: not scanning\n", __func__);
- return SANE_STATUS_INVAL;
+ if (!s->scanning) {
+ throw SaneException("not scanning");
+ }
+ if (non_blocking) {
+ throw SaneException(SANE_STATUS_UNSUPPORTED);
}
- if (non_blocking)
- return SANE_STATUS_UNSUPPORTED;
- return SANE_STATUS_GOOD;
}
-SANE_Status
-sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
+SANE_GENESYS_API_LINKAGE
+SANE_Status sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_set_io_mode_impl(handle, non_blocking);
+ sane_set_io_mode_impl(handle, non_blocking);
});
}
-SANE_Status
-sane_get_select_fd_impl(SANE_Handle handle, SANE_Int * fd)
+void sane_get_select_fd_impl(SANE_Handle handle, SANE_Int* fd)
{
- Genesys_Scanner *s = (Genesys_Scanner*) handle;
-
- DBG(DBG_proc, "%s: handle = %p, fd = %p\n", __func__, handle, (void *) fd);
+ DBG_HELPER_ARGS(dbg, "handle = %p, fd = %p", handle, reinterpret_cast<void*>(fd));
+ Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
- if (!s->scanning)
- {
- DBG(DBG_error, "%s: not scanning\n", __func__);
- return SANE_STATUS_INVAL;
+ if (!s->scanning) {
+ throw SaneException("not scanning");
}
- return SANE_STATUS_UNSUPPORTED;
+ throw SaneException(SANE_STATUS_UNSUPPORTED);
}
-SANE_Status
-sane_get_select_fd(SANE_Handle handle, SANE_Int * fd)
+SANE_GENESYS_API_LINKAGE
+SANE_Status sane_get_select_fd(SANE_Handle handle, SANE_Int* fd)
{
return wrap_exceptions_to_status_code(__func__, [=]()
{
- return sane_get_select_fd_impl(handle, fd);
+ sane_get_select_fd_impl(handle, fd);
});
}
@@ -7578,3 +6168,5 @@ GenesysButtonName genesys_option_to_button(int option)
default: throw std::runtime_error("Unknown option to convert to button index");
}
}
+
+} // namespace genesys
diff --git a/backend/genesys.h b/backend/genesys/genesys.h
index 47a684c..255bf76 100644
--- a/backend/genesys.h
+++ b/backend/genesys/genesys.h
@@ -51,7 +51,7 @@
# define BACKEND_NAME genesys
#endif
-#include "genesys_low.h"
+#include "low.h"
#include <queue>
#ifndef PATH_MAX
@@ -71,17 +71,16 @@
#define GENESYS_CONFIG_FILE "genesys.conf"
-/* Maximum time for lamp warm-up */
-#define WARMUP_TIME 65
-
-#define STR_FLATBED "Flatbed"
-#define STR_TRANSPARENCY_ADAPTER "Transparency Adapter"
-#define STR_TRANSPARENCY_ADAPTER_INFRARED "Transparency Adapter Infrared"
-
#ifndef SANE_I18N
#define SANE_I18N(text) text
#endif
+#define STR_FLATBED SANE_I18N("Flatbed")
+#define STR_TRANSPARENCY_ADAPTER SANE_I18N("Transparency Adapter")
+#define STR_TRANSPARENCY_ADAPTER_INFRARED SANE_I18N("Transparency Adapter Infrared")
+
+namespace genesys {
+
/** List of SANE options
*/
enum Genesys_Option
@@ -122,7 +121,6 @@ enum Genesys_Option
OPT_LAMP_OFF,
OPT_THRESHOLD,
OPT_THRESHOLD_CURVE,
- OPT_DISABLE_DYNAMIC_LINEART,
OPT_DISABLE_INTERPOLATION,
OPT_COLOR_FILTER,
OPT_CALIBRATION_FILE,
@@ -142,6 +140,7 @@ enum Genesys_Option
OPT_CALIBRATE,
OPT_CLEAR_CALIBRATION,
OPT_FORCE_CALIBRATION,
+ OPT_IGNORE_OFFSETS,
/* must come last: */
NUM_OPTIONS
@@ -202,17 +201,21 @@ struct Genesys_Scanner
// SANE data
// We are currently scanning
- SANE_Bool scanning;
+ bool scanning;
// Option descriptors
SANE_Option_Descriptor opt[NUM_OPTIONS];
+ std::vector<SANE_Word> opt_resolution_values;
+ SANE_Range opt_x_range = {};
+ SANE_Range opt_y_range = {};
+ std::vector<const char*> opt_source_values;
+
// Option values
SANE_Word bit_depth = 0;
SANE_Word resolution = 0;
bool preview = false;
SANE_Word threshold = 0;
SANE_Word threshold_curve = 0;
- bool disable_dynamic_lineart = false;
bool disable_interpolation = false;
bool lamp_off = false;
SANE_Word lamp_off_time = 0;
@@ -232,7 +235,10 @@ struct Genesys_Scanner
SANE_Word pos_bottom_right_y = 0;
SANE_Word pos_bottom_right_x = 0;
- std::string mode, source, color_filter;
+ std::string mode, color_filter;
+
+ // the value of the source option
+ ScanMethod scan_method = ScanMethod::FLATBED;
std::string calibration_file;
// Button states
@@ -247,4 +253,6 @@ void write_calibration(std::ostream& str, Genesys_Device::Calibration& cache);
bool read_calibration(std::istream& str, Genesys_Device::Calibration& cache,
const std::string& path);
+} // namespace genesys
+
#endif /* not GENESYS_H */
diff --git a/backend/genesys/gl124.cpp b/backend/genesys/gl124.cpp
new file mode 100644
index 0000000..054f1ef
--- /dev/null
+++ b/backend/genesys/gl124.cpp
@@ -0,0 +1,2269 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2016 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "gl124.h"
+#include "gl124_registers.h"
+#include "test_settings.h"
+
+#include <vector>
+
+namespace genesys {
+namespace gl124 {
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl124_init_registers (Genesys_Device * dev)
+{
+ DBG_HELPER(dbg);
+
+ dev->reg.clear();
+
+ // default to LiDE 110
+ dev->reg.init_reg(0x01, 0xa2); // + REG_0x01_SHDAREA
+ dev->reg.init_reg(0x02, 0x90);
+ dev->reg.init_reg(0x03, 0x50);
+ dev->reg.init_reg(0x04, 0x03);
+ dev->reg.init_reg(0x05, 0x00);
+
+ if(dev->model->sensor_id == SensorId::CIS_CANON_LIDE_120) {
+ dev->reg.init_reg(0x06, 0x50);
+ dev->reg.init_reg(0x07, 0x00);
+ } else {
+ dev->reg.init_reg(0x03, 0x50 & ~REG_0x03_AVEENB);
+ dev->reg.init_reg(0x06, 0x50 | REG_0x06_GAIN4);
+ }
+ dev->reg.init_reg(0x09, 0x00);
+ dev->reg.init_reg(0x0a, 0xc0);
+ dev->reg.init_reg(0x0b, 0x2a);
+ dev->reg.init_reg(0x0c, 0x12);
+ dev->reg.init_reg(0x11, 0x00);
+ dev->reg.init_reg(0x12, 0x00);
+ dev->reg.init_reg(0x13, 0x0f);
+ dev->reg.init_reg(0x14, 0x00);
+ dev->reg.init_reg(0x15, 0x80);
+ dev->reg.init_reg(0x16, 0x10); // SENSOR_DEF
+ dev->reg.init_reg(0x17, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x19, 0x01); // SENSOR_DEF
+ dev->reg.init_reg(0x1a, 0x30); // SENSOR_DEF
+ dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x1c, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x1d, 0x01); // SENSOR_DEF
+ dev->reg.init_reg(0x1e, 0x10);
+ dev->reg.init_reg(0x1f, 0x00);
+ dev->reg.init_reg(0x20, 0x15); // SENSOR_DEF
+ dev->reg.init_reg(0x21, 0x00);
+ if(dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) {
+ dev->reg.init_reg(0x22, 0x02);
+ } else {
+ dev->reg.init_reg(0x22, 0x14);
+ }
+ dev->reg.init_reg(0x23, 0x00);
+ dev->reg.init_reg(0x24, 0x00);
+ dev->reg.init_reg(0x25, 0x00);
+ dev->reg.init_reg(0x26, 0x0d);
+ dev->reg.init_reg(0x27, 0x48);
+ dev->reg.init_reg(0x28, 0x00);
+ dev->reg.init_reg(0x29, 0x56);
+ dev->reg.init_reg(0x2a, 0x5e);
+ dev->reg.init_reg(0x2b, 0x02);
+ dev->reg.init_reg(0x2c, 0x02);
+ dev->reg.init_reg(0x2d, 0x58);
+ dev->reg.init_reg(0x3b, 0x00);
+ dev->reg.init_reg(0x3c, 0x00);
+ dev->reg.init_reg(0x3d, 0x00);
+ dev->reg.init_reg(0x3e, 0x00);
+ dev->reg.init_reg(0x3f, 0x02);
+ dev->reg.init_reg(0x40, 0x00);
+ dev->reg.init_reg(0x41, 0x00);
+ dev->reg.init_reg(0x42, 0x00);
+ dev->reg.init_reg(0x43, 0x00);
+ dev->reg.init_reg(0x44, 0x00);
+ dev->reg.init_reg(0x45, 0x00);
+ dev->reg.init_reg(0x46, 0x00);
+ dev->reg.init_reg(0x47, 0x00);
+ dev->reg.init_reg(0x48, 0x00);
+ dev->reg.init_reg(0x49, 0x00);
+ dev->reg.init_reg(0x4f, 0x00);
+ dev->reg.init_reg(0x52, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x53, 0x02); // SENSOR_DEF
+ dev->reg.init_reg(0x54, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x55, 0x06); // SENSOR_DEF
+ dev->reg.init_reg(0x56, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x57, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x58, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x59, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x5a, 0x1a); // SENSOR_DEF
+ dev->reg.init_reg(0x5b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x5c, 0xc0); // SENSOR_DEF
+ dev->reg.init_reg(0x5f, 0x00);
+ dev->reg.init_reg(0x60, 0x02);
+ dev->reg.init_reg(0x61, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x62, 0x00);
+ dev->reg.init_reg(0x63, 0x00);
+ dev->reg.init_reg(0x64, 0x00);
+ dev->reg.init_reg(0x65, 0x00);
+ dev->reg.init_reg(0x66, 0x00);
+ dev->reg.init_reg(0x67, 0x00);
+ dev->reg.init_reg(0x68, 0x00);
+ dev->reg.init_reg(0x69, 0x00);
+ dev->reg.init_reg(0x6a, 0x00);
+ dev->reg.init_reg(0x6b, 0x00);
+ dev->reg.init_reg(0x6c, 0x00);
+ dev->reg.init_reg(0x6e, 0x00);
+ dev->reg.init_reg(0x6f, 0x00);
+
+ if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) {
+ dev->reg.init_reg(0x6d, 0xd0);
+ dev->reg.init_reg(0x71, 0x08);
+ } else {
+ dev->reg.init_reg(0x6d, 0x00);
+ dev->reg.init_reg(0x71, 0x1f);
+ }
+ dev->reg.init_reg(0x70, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x71, 0x08); // SENSOR_DEF
+ dev->reg.init_reg(0x72, 0x08); // SENSOR_DEF
+ dev->reg.init_reg(0x73, 0x0a); // SENSOR_DEF
+
+ // CKxMAP
+ dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF
+ dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF
+ dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF
+
+ dev->reg.init_reg(0x7d, 0x00);
+ dev->reg.init_reg(0x7e, 0x08);
+ dev->reg.init_reg(0x7f, 0x58);
+
+ if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) {
+ dev->reg.init_reg(0x80, 0x00);
+ dev->reg.init_reg(0x81, 0x14);
+ } else {
+ dev->reg.init_reg(0x80, 0x00);
+ dev->reg.init_reg(0x81, 0x10);
+ }
+
+ // STRPIXEL
+ dev->reg.init_reg(0x82, 0x00);
+ dev->reg.init_reg(0x83, 0x00);
+ dev->reg.init_reg(0x84, 0x00);
+
+ // ENDPIXEL
+ dev->reg.init_reg(0x85, 0x00);
+ dev->reg.init_reg(0x86, 0x00);
+ dev->reg.init_reg(0x87, 0x00);
+
+ dev->reg.init_reg(0x88, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x89, 0x65); // SENSOR_DEF
+ dev->reg.init_reg(0x8a, 0x00);
+ dev->reg.init_reg(0x8b, 0x00);
+ dev->reg.init_reg(0x8c, 0x00);
+ dev->reg.init_reg(0x8d, 0x00);
+ dev->reg.init_reg(0x8e, 0x00);
+ dev->reg.init_reg(0x8f, 0x00);
+ dev->reg.init_reg(0x90, 0x00);
+ dev->reg.init_reg(0x91, 0x00);
+ dev->reg.init_reg(0x92, 0x00);
+ dev->reg.init_reg(0x93, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x94, 0x14); // SENSOR_DEF
+ dev->reg.init_reg(0x95, 0x30); // SENSOR_DEF
+ dev->reg.init_reg(0x96, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x97, 0x90); // SENSOR_DEF
+ dev->reg.init_reg(0x98, 0x01); // SENSOR_DEF
+ dev->reg.init_reg(0x99, 0x1f);
+ dev->reg.init_reg(0x9a, 0x00);
+ dev->reg.init_reg(0x9b, 0x80);
+ dev->reg.init_reg(0x9c, 0x80);
+ dev->reg.init_reg(0x9d, 0x3f);
+ dev->reg.init_reg(0x9e, 0x00);
+ dev->reg.init_reg(0x9f, 0x00);
+ dev->reg.init_reg(0xa0, 0x20);
+ dev->reg.init_reg(0xa1, 0x30);
+ dev->reg.init_reg(0xa2, 0x00);
+ dev->reg.init_reg(0xa3, 0x20);
+ dev->reg.init_reg(0xa4, 0x01);
+ dev->reg.init_reg(0xa5, 0x00);
+ dev->reg.init_reg(0xa6, 0x00);
+ dev->reg.init_reg(0xa7, 0x08);
+ dev->reg.init_reg(0xa8, 0x00);
+ dev->reg.init_reg(0xa9, 0x08);
+ dev->reg.init_reg(0xaa, 0x01);
+ dev->reg.init_reg(0xab, 0x00);
+ dev->reg.init_reg(0xac, 0x00);
+ dev->reg.init_reg(0xad, 0x40);
+ dev->reg.init_reg(0xae, 0x01);
+ dev->reg.init_reg(0xaf, 0x00);
+ dev->reg.init_reg(0xb0, 0x00);
+ dev->reg.init_reg(0xb1, 0x40);
+ dev->reg.init_reg(0xb2, 0x00);
+ dev->reg.init_reg(0xb3, 0x09);
+ dev->reg.init_reg(0xb4, 0x5b);
+ dev->reg.init_reg(0xb5, 0x00);
+ dev->reg.init_reg(0xb6, 0x10);
+ dev->reg.init_reg(0xb7, 0x3f);
+ dev->reg.init_reg(0xb8, 0x00);
+ dev->reg.init_reg(0xbb, 0x00);
+ dev->reg.init_reg(0xbc, 0xff);
+ dev->reg.init_reg(0xbd, 0x00);
+ dev->reg.init_reg(0xbe, 0x07);
+ dev->reg.init_reg(0xc3, 0x00);
+ dev->reg.init_reg(0xc4, 0x00);
+
+ /* gamma
+ dev->reg.init_reg(0xc5, 0x00);
+ dev->reg.init_reg(0xc6, 0x00);
+ dev->reg.init_reg(0xc7, 0x00);
+ dev->reg.init_reg(0xc8, 0x00);
+ dev->reg.init_reg(0xc9, 0x00);
+ dev->reg.init_reg(0xca, 0x00);
+ dev->reg.init_reg(0xcb, 0x00);
+ dev->reg.init_reg(0xcc, 0x00);
+ dev->reg.init_reg(0xcd, 0x00);
+ dev->reg.init_reg(0xce, 0x00);
+ */
+
+ if (dev->model->sensor_id == SensorId::CIS_CANON_LIDE_120) {
+ dev->reg.init_reg(0xc5, 0x20);
+ dev->reg.init_reg(0xc6, 0xeb);
+ dev->reg.init_reg(0xc7, 0x20);
+ dev->reg.init_reg(0xc8, 0xeb);
+ dev->reg.init_reg(0xc9, 0x20);
+ dev->reg.init_reg(0xca, 0xeb);
+ }
+
+ // memory layout
+ /*
+ dev->reg.init_reg(0xd0, 0x0a);
+ dev->reg.init_reg(0xd1, 0x1f);
+ dev->reg.init_reg(0xd2, 0x34);
+ */
+ dev->reg.init_reg(0xd3, 0x00);
+ dev->reg.init_reg(0xd4, 0x00);
+ dev->reg.init_reg(0xd5, 0x00);
+ dev->reg.init_reg(0xd6, 0x00);
+ dev->reg.init_reg(0xd7, 0x00);
+ dev->reg.init_reg(0xd8, 0x00);
+ dev->reg.init_reg(0xd9, 0x00);
+
+ // memory layout
+ /*
+ dev->reg.init_reg(0xe0, 0x00);
+ dev->reg.init_reg(0xe1, 0x48);
+ dev->reg.init_reg(0xe2, 0x15);
+ dev->reg.init_reg(0xe3, 0x90);
+ dev->reg.init_reg(0xe4, 0x15);
+ dev->reg.init_reg(0xe5, 0x91);
+ dev->reg.init_reg(0xe6, 0x2a);
+ dev->reg.init_reg(0xe7, 0xd9);
+ dev->reg.init_reg(0xe8, 0x2a);
+ dev->reg.init_reg(0xe9, 0xad);
+ dev->reg.init_reg(0xea, 0x40);
+ dev->reg.init_reg(0xeb, 0x22);
+ dev->reg.init_reg(0xec, 0x40);
+ dev->reg.init_reg(0xed, 0x23);
+ dev->reg.init_reg(0xee, 0x55);
+ dev->reg.init_reg(0xef, 0x6b);
+ dev->reg.init_reg(0xf0, 0x55);
+ dev->reg.init_reg(0xf1, 0x6c);
+ dev->reg.init_reg(0xf2, 0x6a);
+ dev->reg.init_reg(0xf3, 0xb4);
+ dev->reg.init_reg(0xf4, 0x6a);
+ dev->reg.init_reg(0xf5, 0xb5);
+ dev->reg.init_reg(0xf6, 0x7f);
+ dev->reg.init_reg(0xf7, 0xfd);
+ */
+
+ dev->reg.init_reg(0xf8, 0x01); // other value is 0x05
+ dev->reg.init_reg(0xf9, 0x00);
+ dev->reg.init_reg(0xfa, 0x00);
+ dev->reg.init_reg(0xfb, 0x00);
+ dev->reg.init_reg(0xfc, 0x00);
+ dev->reg.init_reg(0xff, 0x00);
+
+ // fine tune upon device description
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+ sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res);
+
+ dev->calib_reg = dev->reg;
+}
+
+/**@brief send slope table for motor movement
+ * Send slope_table in machine byte order
+ * @param dev device to send slope table
+ * @param table_nr index of the slope table in ASIC memory
+ * Must be in the [0-4] range.
+ * @param slope_table pointer to 16 bit values array of the slope table
+ * @param steps number of elemnts in the slope table
+ */
+static void gl124_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table,
+ int steps)
+{
+ DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps);
+ int i;
+ char msg[10000];
+
+ /* sanity check */
+ if(table_nr<0 || table_nr>4)
+ {
+ throw SaneException("invalid table number");
+ }
+
+ std::vector<uint8_t> table(steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ std::sprintf(msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++) {
+ std::sprintf(msg + std::strlen(msg), ",%d", slope_table[i]);
+ }
+ DBG (DBG_io, "%s: %s\n", __func__, msg);
+ }
+
+ if (dev->interface->is_mock()) {
+ dev->interface->record_slope_table(table_nr, slope_table);
+ }
+ // slope table addresses are fixed
+ dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data());
+}
+
+/** @brief * Set register values of 'special' ti type frontend
+ * Registers value are taken from the frontend register data
+ * set.
+ * @param dev device owning the AFE
+ * @param set flag AFE_INIT to specify the AFE must be reset before writing data
+ * */
+static void gl124_set_ti_fe(Genesys_Device* dev, uint8_t set)
+{
+ DBG_HELPER(dbg);
+ int i;
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s: setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+
+ dev->frontend = dev->frontend_initial;
+ }
+
+ // start writing to DAC
+ dev->interface->write_fe_register(0x00, 0x80);
+
+ /* write values to analog frontend */
+ for (uint16_t addr = 0x01; addr < 0x04; addr++)
+ {
+ dev->interface->write_fe_register(addr, dev->frontend.regs.get_value(addr));
+ }
+
+ dev->interface->write_fe_register(0x04, 0x00);
+
+ /* these are not really sign for this AFE */
+ for (i = 0; i < 3; i++)
+ {
+ dev->interface->write_fe_register(0x05 + i, dev->frontend.regs.get_value(0x24 + i));
+ }
+
+ if (dev->model->adc_id == AdcId::CANON_LIDE_120) {
+ dev->interface->write_fe_register(0x00, 0x01);
+ }
+ else
+ {
+ dev->interface->write_fe_register(0x00, 0x11);
+ }
+}
+
+
+// Set values of analog frontend
+void CommandSetGl124::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const
+{
+ DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" :
+ set == AFE_SET ? "set" :
+ set == AFE_POWER_SAVE ? "powersave" : "huh?");
+ (void) sensor;
+ uint8_t val;
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+ dev->frontend = dev->frontend_initial;
+ }
+
+ val = dev->interface->read_register(REG_0x0A);
+
+ /* route to correct analog FE */
+ switch ((val & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) {
+ case 3:
+ gl124_set_ti_fe(dev, set);
+ break;
+ case 0:
+ case 1:
+ case 2:
+ default:
+ throw SaneException("unsupported analog FE 0x%02x", val);
+ }
+}
+
+static void gl124_init_motor_regs_scan(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const Motor_Profile& motor_profile,
+ unsigned int scan_exposure_time,
+ unsigned scan_yres,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ ScanColorMode scan_mode,
+ MotorFlag flags)
+{
+ DBG_HELPER(dbg);
+ int use_fast_fed;
+ unsigned int lincnt, fast_dpi;
+ unsigned int feedl,dist;
+ uint32_t z1, z2;
+ unsigned yres;
+ unsigned min_speed;
+ unsigned int linesel;
+
+ DBG(DBG_info, "%s : scan_exposure_time=%d, scan_yres=%d, step_type=%d, scan_lines=%d, "
+ "scan_dummy=%d, feed_steps=%d, scan_mode=%d, flags=%x\n", __func__, scan_exposure_time,
+ scan_yres, static_cast<unsigned>(motor_profile.step_type), scan_lines, scan_dummy,
+ feed_steps, static_cast<unsigned>(scan_mode),
+ static_cast<unsigned>(flags));
+
+ /* we never use fast fed since we do manual feed for the scans */
+ use_fast_fed=0;
+
+ /* enforce motor minimal scan speed
+ * @TODO extend motor struct for this value */
+ if (scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
+ {
+ min_speed = 900;
+ }
+ else
+ {
+ switch(dev->model->motor_id)
+ {
+ case MotorId::CANON_LIDE_110:
+ min_speed = 600;
+ break;
+ case MotorId::CANON_LIDE_120:
+ min_speed = 900;
+ break;
+ default:
+ min_speed = 900;
+ break;
+ }
+ }
+
+ /* compute min_speed and linesel */
+ if(scan_yres<min_speed)
+ {
+ yres=min_speed;
+ linesel = yres / scan_yres - 1;
+ /* limit case, we need a linesel > 0 */
+ if(linesel==0)
+ {
+ linesel=1;
+ yres=scan_yres*2;
+ }
+ }
+ else
+ {
+ yres=scan_yres;
+ linesel=0;
+ }
+
+ DBG(DBG_io2, "%s: final yres=%d, linesel=%d\n", __func__, yres, linesel);
+
+ lincnt=scan_lines*(linesel+1);
+ reg->set24(REG_LINCNT, lincnt);
+ DBG (DBG_io, "%s: lincnt=%d\n", __func__, lincnt);
+
+ /* compute register 02 value */
+ uint8_t r02 = REG_0x02_NOTHOME;
+
+ if (use_fast_fed) {
+ r02 |= REG_0x02_FASTFED;
+ } else {
+ r02 &= ~REG_0x02_FASTFED;
+ }
+
+ if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
+ r02 |= REG_0x02_AGOHOME;
+ }
+
+ if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) || (yres >= sensor.optical_res))
+ {
+ r02 |= REG_0x02_ACDCDIS;
+ }
+ if (has_flag(flags, MotorFlag::REVERSE)) {
+ r02 |= REG_0x02_MTRREV;
+ }
+
+ reg->set8(REG_0x02, r02);
+ sanei_genesys_set_motor_power(*reg, true);
+
+ reg->set16(REG_SCANFED, 4);
+
+ /* scan and backtracking slope table */
+ auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, yres, scan_exposure_time,
+ dev->motor.base_ydpi, 1,
+ motor_profile);
+ gl124_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
+ gl124_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
+
+ reg->set16(REG_STEPNO, scan_table.steps_count);
+
+ /* fast table */
+ fast_dpi=yres;
+
+ /*
+ if (scan_mode != ScanColorMode::COLOR_SINGLE_PASS)
+ {
+ fast_dpi*=3;
+ }
+ */
+ auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_dpi,
+ scan_exposure_time, dev->motor.base_ydpi,
+ 1, motor_profile);
+ gl124_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count);
+ gl124_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
+
+ reg->set16(REG_FASTNO, fast_table.steps_count);
+ reg->set16(REG_FSHDEC, fast_table.steps_count);
+ reg->set16(REG_FMOVNO, fast_table.steps_count);
+
+ /* substract acceleration distance from feedl */
+ feedl=feed_steps;
+ feedl <<= static_cast<unsigned>(motor_profile.step_type);
+
+ dist = scan_table.steps_count;
+ if (has_flag(flags, MotorFlag::FEED)) {
+ dist *= 2;
+ }
+ if (use_fast_fed) {
+ dist += fast_table.steps_count * 2;
+ }
+ DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
+
+ /* get sure we don't use insane value */
+ if (dist < feedl) {
+ feedl -= dist;
+ } else {
+ feedl = 0;
+ }
+
+ reg->set24(REG_FEEDL, feedl);
+ DBG (DBG_io, "%s: feedl=%d\n", __func__, feedl);
+
+ /* doesn't seem to matter that much */
+ sanei_genesys_calculate_zmod(use_fast_fed,
+ scan_exposure_time,
+ scan_table.table,
+ scan_table.steps_count,
+ feedl,
+ scan_table.steps_count,
+ &z1,
+ &z2);
+
+ reg->set24(REG_Z1MOD, z1);
+ DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
+
+ reg->set24(REG_Z2MOD, z2);
+ DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
+
+ /* LINESEL */
+ reg->set8_mask(REG_0x1D, linesel, REG_0x1D_LINESEL);
+ reg->set8(REG_0xA0, (static_cast<unsigned>(motor_profile.step_type) << REG_0xA0S_STEPSEL) |
+ (static_cast<unsigned>(motor_profile.step_type) << REG_0xA0S_FSTPSEL));
+
+ reg->set16(REG_FMOVDEC, fast_table.steps_count);
+}
+
+
+/** @brief copy sensor specific settings
+ * Set up register set for the given sensor resolution. Values are from the device table
+ * in genesys_devices.c for registers:
+ * [0x16 ... 0x1d]
+ * [0x52 ... 0x5e]
+ * Other come from the specific device sensor table in genesys_gl124.h:
+ * 0x18, 0x20, 0x61, 0x98 and
+ * @param dev device to set up
+ * @param regs register set to modify
+ * @param dpi resolution of the sensor during scan
+ * @param ccd_size_divisor flag for half ccd mode
+ * */
+static void gl124_setup_sensor(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs)
+{
+ DBG_HELPER(dbg);
+
+ for (const auto& reg : sensor.custom_regs) {
+ regs->set8(reg.address, reg.value);
+ }
+
+ regs->set24(REG_EXPR, sensor.exposure.red);
+ regs->set24(REG_EXPG, sensor.exposure.green);
+ regs->set24(REG_EXPB, sensor.exposure.blue);
+
+ dev->segment_order = sensor.segment_order;
+}
+
+/** @brief setup optical related registers
+ * start and pixels are expressed in optical sensor resolution coordinate
+ * space.
+ * @param dev scanner device to use
+ * @param reg registers to set up
+ * @param exposure_time exposure time to use
+ * @param used_res scanning resolution used, may differ from
+ * scan's one
+ * @param start logical start pixel coordinate
+ * @param pixels logical number of pixels to use
+ * @param channels number of color channels (currently 1 or 3)
+ * @param depth bit depth of the scan (1, 8 or 16)
+ * @param ccd_size_divisor whether sensor's timings are such that x coordinates must be halved
+ * @param color_filter color channel to use as gray data
+ * @param flags optical flags (@see )
+ */
+static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, unsigned int exposure_time,
+ const ScanSession& session)
+{
+ DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time);
+ unsigned int dpihw;
+ GenesysRegister *r;
+ uint32_t expmax;
+
+ // resolution is divided according to ccd_pixels_per_system_pixel
+ unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
+ DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel);
+
+ // to manage high resolution device while keeping good low resolution scanning speed, we
+ // make hardware dpi vary
+ dpihw = sensor.get_register_hwdpi(session.output_resolution * ccd_pixels_per_system_pixel);
+ DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw);
+
+ gl124_setup_sensor(dev, sensor, reg);
+
+ dev->cmd_set->set_fe(dev, sensor, AFE_SET);
+
+ /* enable shading */
+ regs_set_optical_off(dev->model->asic_type, *reg);
+ r = sanei_genesys_get_address (reg, REG_0x01);
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ r->value &= ~REG_0x01_DVDSET;
+ } else {
+ r->value |= REG_0x01_DVDSET;
+ }
+
+ r = sanei_genesys_get_address(reg, REG_0x03);
+ if ((dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) && (session.params.xres>=600)) {
+ r->value &= ~REG_0x03_AVEENB;
+ DBG (DBG_io, "%s: disabling AVEENB\n", __func__);
+ }
+ else
+ {
+ r->value |= ~REG_0x03_AVEENB;
+ DBG (DBG_io, "%s: enabling AVEENB\n", __func__);
+ }
+
+ sanei_genesys_set_lamp_power(dev, sensor, *reg,
+ !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));
+
+ // BW threshold
+ dev->interface->write_register(REG_0x114, dev->settings.threshold);
+ dev->interface->write_register(REG_0x115, dev->settings.threshold);
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, REG_0x04);
+ switch (session.params.depth) {
+ case 8:
+ r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG_0x04_LINEART;
+ r->value |= REG_0x04_BITSET;
+ break;
+ }
+
+ r->value &= ~REG_0x04_FILTER;
+ if (session.params.channels == 1)
+ {
+ switch (session.params.color_filter)
+ {
+ case ColorFilter::RED:
+ r->value |= 0x10;
+ break;
+ case ColorFilter::BLUE:
+ r->value |= 0x30;
+ break;
+ case ColorFilter::GREEN:
+ r->value |= 0x20;
+ break;
+ default:
+ break; // should not happen
+ }
+ }
+
+ sanei_genesys_set_dpihw(*reg, sensor, dpihw);
+
+ if (should_enable_gamma(session, sensor)) {
+ reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
+ } else {
+ reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
+ }
+
+ unsigned dpiset_reg = session.output_resolution * ccd_pixels_per_system_pixel *
+ session.ccd_size_divisor;
+ if (sensor.dpiset_override != 0) {
+ dpiset_reg = sensor.dpiset_override;
+ }
+
+ reg->set16(REG_DPISET, dpiset_reg);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset_reg);
+
+ r = sanei_genesys_get_address(reg, REG_0x06);
+ r->value |= REG_0x06_GAIN4;
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ /* we set up LEDADD only when asked */
+ if (dev->model->is_cis) {
+ r = sanei_genesys_get_address (reg, REG_0x60);
+ r->value &= ~REG_0x60_LEDADD;
+ if (session.enable_ledadd) {
+ r->value |= REG_0x60_LEDADD;
+ expmax = reg->get24(REG_EXPR);
+ expmax = std::max(expmax, reg->get24(REG_EXPG));
+ expmax = std::max(expmax, reg->get24(REG_EXPB));
+
+ dev->reg.set24(REG_EXPR, expmax);
+ dev->reg.set24(REG_EXPG, expmax);
+ dev->reg.set24(REG_EXPB, expmax);
+ }
+ /* RGB weighting, REG_TRUER,G and B are to be set */
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value &= ~REG_0x01_TRUEGRAY;
+ if (session.enable_ledadd) {
+ r->value |= REG_0x01_TRUEGRAY;
+ dev->interface->write_register(REG_TRUER, 0x80);
+ dev->interface->write_register(REG_TRUEG, 0x80);
+ dev->interface->write_register(REG_TRUEB, 0x80);
+ }
+ }
+
+ reg->set24(REG_STRPIXEL, session.pixel_startx);
+ reg->set24(REG_ENDPIXEL, session.pixel_endx);
+
+ dev->line_count = 0;
+
+ build_image_pipeline(dev, session);
+
+ // MAXWD is expressed in 2 words unit
+
+ // BUG: we shouldn't multiply by channels here
+ reg->set24(REG_MAXWD, session.output_line_bytes_raw / session.ccd_size_divisor * session.params.channels);
+
+ reg->set24(REG_LPERIOD, exposure_time);
+ DBG (DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time);
+
+ reg->set16(REG_DUMMY, sensor.dummy_pixel);
+}
+
+void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const
+{
+ DBG_HELPER(dbg);
+ session.assert_computed();
+
+ int move;
+ int exposure_time;
+
+ int dummy = 0;
+ int slope_dpi = 0;
+
+ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */
+ if (dev->model->is_cis) {
+ slope_dpi = session.params.yres * session.params.channels;
+ } else {
+ slope_dpi = session.params.yres;
+ }
+
+ if (has_flag(session.params.flags, ScanFlag::FEEDING)) {
+ exposure_time = 2304;
+ } else {
+ exposure_time = sensor.exposure_lperiod;
+ }
+ const auto& motor_profile = sanei_genesys_get_motor_profile(*gl124_motor_profiles,
+ dev->model->motor_id,
+ exposure_time);
+
+ DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
+ DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, static_cast<unsigned>(motor_profile.step_type));
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+
+ // now _LOGICAL_ optical values used are known, setup registers
+ gl124_init_optical_regs_scan(dev, sensor, reg, exposure_time, session);
+
+ /* add tl_y to base movement */
+ move = session.params.starty;
+ DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
+
+ MotorFlag mflags = MotorFlag::NONE;
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) {
+ mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE;
+ }
+ if (has_flag(session.params.flags, ScanFlag::FEEDING)) {
+ mflags |= MotorFlag::FEED;
+ }
+ if (has_flag(session.params.flags, ScanFlag::REVERSE)) {
+ mflags |= MotorFlag::REVERSE;
+ }
+ gl124_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi,
+ dev->model->is_cis ? session.output_line_count * session.params.channels :
+ session.output_line_count,
+ dummy, move, session.params.scan_mode, mflags);
+
+ /*** prepares data reordering ***/
+
+ dev->read_buffer.clear();
+ dev->read_buffer.alloc(session.buffer_size_read);
+
+ dev->read_active = true;
+
+ dev->session = session;
+
+ dev->total_bytes_read = 0;
+ dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines;
+
+ DBG(DBG_info, "%s: total bytes to send to frontend = %zu\n", __func__,
+ dev->total_bytes_to_read);
+}
+
+ScanSession CommandSetGl124::calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const
+{
+ int start;
+
+ DBG(DBG_info, "%s ", __func__);
+ debug_dump(DBG_info, settings);
+
+ /* start */
+ start = static_cast<int>(dev->model->x_offset);
+ start += static_cast<int>(settings.tl_x);
+ start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = settings.xres;
+ session.params.yres = settings.yres;
+ session.params.startx = start;
+ session.params.starty = 0; // not used
+ session.params.pixels = settings.pixels;
+ session.params.requested_pixels = settings.requested_pixels;
+ session.params.lines = settings.lines;
+ session.params.depth = settings.depth;
+ session.params.channels = settings.get_channels();
+ session.params.scan_method = settings.scan_method;
+ session.params.scan_mode = settings.scan_mode;
+ session.params.color_filter = settings.color_filter;
+ session.params.flags = ScanFlag::NONE;
+
+ compute_session(dev, session, sensor);
+
+ return session;
+}
+
+/**
+ * for fast power saving methods only, like disabling certain amplifiers
+ * @param dev device to use
+ * @param enable true to set inot powersaving
+ * */
+void CommandSetGl124::save_power(Genesys_Device* dev, bool enable) const
+{
+ (void) dev;
+ DBG_HELPER_ARGS(dbg, "enable = %d", enable);
+}
+
+void CommandSetGl124::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
+{
+ DBG_HELPER_ARGS(dbg, "delay = %d", delay);
+ GenesysRegister *r;
+
+ r = sanei_genesys_get_address(&dev->reg, REG_0x03);
+ r->value &= ~0xf0;
+ if(delay<15)
+ {
+ r->value |= delay;
+ }
+ else
+ {
+ r->value |= 0x0f;
+ }
+}
+
+/** @brief setup GPIOs for scan
+ * Setup GPIO values to drive motor (or light) needed for the
+ * target resolution
+ * @param *dev device to set up
+ * @param resolution dpi of the target scan
+ */
+void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution)
+{
+ DBG_HELPER(dbg);
+
+ uint8_t val = dev->interface->read_register(REG_0x32);
+
+ /* LiDE 110, 210 and 220 cases */
+ if(dev->model->gpio_id != GpioId::CANON_LIDE_120) {
+ if(resolution>=dev->motor.base_ydpi/2)
+ {
+ val &= 0xf7;
+ }
+ else if(resolution>=dev->motor.base_ydpi/4)
+ {
+ val &= 0xef;
+ }
+ else
+ {
+ val |= 0x10;
+ }
+ }
+ /* 120 : <=300 => 0x53 */
+ else
+ { /* base_ydpi is 4800 */
+ if(resolution<=300)
+ {
+ val &= 0xf7;
+ }
+ else if(resolution<=600)
+ {
+ val |= 0x08;
+ }
+ else if(resolution<=1200)
+ {
+ val &= 0xef;
+ val |= 0x08;
+ }
+ else
+ {
+ val &= 0xf7;
+ }
+ }
+ val |= 0x02;
+ dev->interface->write_register(REG_0x32, val);
+}
+
+// Send the low-level scan command
+// todo: is this that useful ?
+void CommandSetGl124::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, bool start_motor) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+ (void) reg;
+
+ // set up GPIO for scan
+ gl124_setup_scan_gpio(dev,dev->settings.yres);
+
+ // clear scan and feed count
+ dev->interface->write_register(REG_0x0D, REG_0x0D_CLRLNCNT | REG_0x0D_CLRMCNT);
+
+ // enable scan and motor
+ uint8_t val = dev->interface->read_register(REG_0x01);
+ val |= REG_0x01_SCAN;
+ dev->interface->write_register(REG_0x01, val);
+
+ scanner_start_action(*dev, start_motor);
+
+ dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
+}
+
+
+// Send the stop scan command
+void CommandSetGl124::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
+ bool check_stop) const
+{
+ (void) reg;
+ DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop);
+
+ if (!dev->model->is_sheetfed) {
+ scanner_stop_action(*dev);
+ }
+}
+
+
+/** Park head
+ * Moves the slider to the home (top) position slowly
+ * @param dev device to park
+ * @param wait_until_home true to make the function waiting for head
+ * to be home before returning, if fals returne immediately
+ */
+void CommandSetGl124::move_back_home(Genesys_Device* dev, bool wait_until_home) const
+{
+ scanner_move_back_home(*dev, wait_until_home);
+}
+
+// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi
+// from very top of scanner
+void CommandSetGl124::search_start_position(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ int size;
+ Genesys_Register_Set local_reg = dev->reg;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ // FIXME: the current approach of doing search only for one resolution does not work on scanners
+ // whith employ different sensors with potentially different settings.
+ const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, ScanMethod::FLATBED);
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0; /*we should give a small offset here~60 steps */
+ session.params.pixels = 600;
+ session.params.lines = dev->model->search_lines;
+ session.params.depth = 8;
+ session.params.channels = 1;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::GREEN;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::DISABLE_BUFFER_FULL_MOVE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ // send to scanner
+ dev->interface->write_registers(local_reg);
+
+ size = pixels * dev->model->search_lines;
+
+ std::vector<uint8_t> data(size);
+
+ begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_start_position");
+ end_scan(dev, &local_reg, true);
+ dev->reg = local_reg;
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl124_search_position.pnm", data.data(), 8, 1, pixels,
+ dev->model->search_lines);
+ }
+
+ end_scan(dev, &local_reg, true);
+
+ /* update regs to copy ASIC internal state */
+ dev->reg = local_reg;
+
+ for (auto& sensor_update :
+ sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method))
+ {
+ sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels,
+ dev->model->search_lines);
+ }
+}
+
+// sets up register for coarse gain calibration
+// todo: check it for scanners using it
+void CommandSetGl124::init_regs_for_coarse_calibration(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel();
+ session.params.lines = 20;
+ session.params.depth = 16;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::FEEDING |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
+ sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres);
+
+ dev->interface->write_registers(regs);
+}
+
+
+// init registers for shading calibration shading calibration is done at dpihw
+void CommandSetGl124::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int move, resolution, dpihw, factor;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ dev->calib_channels = 3;
+ dev->calib_lines = dev->model->shading_lines;
+ dpihw = sensor.get_register_hwdpi(dev->settings.xres);
+ if(dpihw>=2400)
+ {
+ dev->calib_lines *= 2;
+ }
+ resolution=dpihw;
+
+ unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres);
+
+ resolution /= ccd_size_divisor;
+ dev->calib_lines /= ccd_size_divisor; // reducing just because we reduced the resolution
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution,
+ dev->calib_channels,
+ dev->settings.scan_method);
+ dev->calib_resolution = resolution;
+ dev->calib_total_bytes_to_read = 0;
+ factor = calib_sensor.optical_res / resolution;
+ dev->calib_pixels = calib_sensor.sensor_pixels / factor;
+
+ /* distance to move to reach white target at high resolution */
+ move=0;
+ if (dev->settings.yres >= 1200) {
+ move = static_cast<int>(dev->model->y_offset_calib_white);
+ move = static_cast<int>((move * (dev->motor.base_ydpi/4)) / MM_PER_INCH);
+ }
+ DBG (DBG_io, "%s: move=%d steps\n", __func__, move);
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 0;
+ session.params.starty = move;
+ session.params.pixels = dev->calib_pixels;
+ session.params.lines = dev->calib_lines;
+ session.params.depth = 16;
+ session.params.channels = dev->calib_channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::DISABLE_BUFFER_FULL_MOVE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ try {
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
+ throw;
+ }
+ sanei_genesys_set_motor_power(regs, false);
+
+ dev->interface->write_registers(regs);
+}
+
+void CommandSetGl124::wait_for_motor_stop(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+
+ auto status = scanner_read_status(*dev);
+ uint8_t val40 = dev->interface->read_register(REG_0x100);
+
+ if (!status.is_motor_enabled && (val40 & REG_0x100_MOTMFLG) == 0) {
+ return;
+ }
+
+ do {
+ dev->interface->sleep_ms(10);
+ status = scanner_read_status(*dev);
+ val40 = dev->interface->read_register(REG_0x100);
+ } while (status.is_motor_enabled ||(val40 & REG_0x100_MOTMFLG));
+ dev->interface->sleep_ms(50);
+}
+
+/** @brief set up registers for the actual scan
+ */
+void CommandSetGl124::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ float move;
+ int move_dpi;
+ float start;
+
+ debug_dump(DBG_info, dev->settings);
+
+ /* y (motor) distance to move to reach scanned area */
+ move_dpi = dev->motor.base_ydpi/4;
+ move = static_cast<float>(dev->model->y_offset);
+ move += static_cast<float>(dev->settings.tl_y);
+ move = static_cast<float>((move * move_dpi) / MM_PER_INCH);
+ DBG (DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ if (dev->settings.get_channels() * dev->settings.yres >= 600 && move > 700) {
+ scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move - 500),
+ Direction::FORWARD);
+ move=500;
+ }
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ /* start */
+ start = static_cast<float>(dev->model->x_offset);
+ start += static_cast<float>(dev->settings.tl_x);
+ start /= sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres);
+ start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = static_cast<unsigned>(start);
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = dev->settings.pixels;
+ session.params.requested_pixels = dev->settings.requested_pixels;
+ session.params.lines = dev->settings.lines;
+ session.params.depth = dev->settings.depth;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::NONE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &dev->reg, session);
+}
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ std::uint8_t* data, int size) const
+{
+ DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size);
+ uint32_t addr, length, x, factor, segcnt, pixels, i;
+ uint16_t dpiset,dpihw;
+ uint8_t *ptr, *src;
+
+ /* logical size of a color as seen by generic code of the frontend */
+ length = size / 3;
+ std::uint32_t strpixel = dev->session.pixel_startx;
+ std::uint32_t endpixel = dev->session.pixel_endx;
+ segcnt = dev->reg.get24(REG_SEGCNT);
+ if(endpixel==0)
+ {
+ endpixel=segcnt;
+ }
+
+ /* compute deletion factor */
+ dpiset = dev->reg.get16(REG_DPISET);
+ dpihw = sensor.get_register_hwdpi(dpiset);
+ factor=dpihw/dpiset;
+ DBG( DBG_io2, "%s: factor=%d\n",__func__,factor);
+
+ /* turn pixel value into bytes 2x16 bits words */
+ strpixel*=2*2; /* 2 words of 2 bytes */
+ endpixel*=2*2;
+ segcnt*=2*2;
+ pixels=endpixel-strpixel;
+
+ dev->interface->record_key_value("shading_start_pixel", std::to_string(strpixel));
+ dev->interface->record_key_value("shading_pixels", std::to_string(pixels));
+ dev->interface->record_key_value("shading_length", std::to_string(length));
+ dev->interface->record_key_value("shading_factor", std::to_string(factor));
+ dev->interface->record_key_value("shading_segcnt", std::to_string(segcnt));
+ dev->interface->record_key_value("shading_segment_count",
+ std::to_string(dev->session.segment_count));
+
+ DBG( DBG_io2, "%s: using chunks of %d bytes (%d shading data pixels)\n",__func__,length, length/4);
+ std::vector<uint8_t> buffer(pixels * dev->session.segment_count, 0);
+
+ /* write actual red data */
+ for(i=0;i<3;i++)
+ {
+ /* copy data to work buffer and process it */
+ /* coefficent destination */
+ ptr = buffer.data();
+
+ /* iterate on both sensor segment */
+ for(x=0;x<pixels;x+=4*factor)
+ {
+ /* coefficient source */
+ src=data+x+strpixel+i*length;
+
+ /* iterate over all the segments */
+ switch (dev->session.segment_count) {
+ case 1:
+ ptr[0+pixels*0]=src[0+segcnt*0];
+ ptr[1+pixels*0]=src[1+segcnt*0];
+ ptr[2+pixels*0]=src[2+segcnt*0];
+ ptr[3+pixels*0]=src[3+segcnt*0];
+ break;
+ case 2:
+ ptr[0+pixels*0]=src[0+segcnt*0];
+ ptr[1+pixels*0]=src[1+segcnt*0];
+ ptr[2+pixels*0]=src[2+segcnt*0];
+ ptr[3+pixels*0]=src[3+segcnt*0];
+ ptr[0+pixels*1]=src[0+segcnt*1];
+ ptr[1+pixels*1]=src[1+segcnt*1];
+ ptr[2+pixels*1]=src[2+segcnt*1];
+ ptr[3+pixels*1]=src[3+segcnt*1];
+ break;
+ case 4:
+ ptr[0+pixels*0]=src[0+segcnt*0];
+ ptr[1+pixels*0]=src[1+segcnt*0];
+ ptr[2+pixels*0]=src[2+segcnt*0];
+ ptr[3+pixels*0]=src[3+segcnt*0];
+ ptr[0+pixels*1]=src[0+segcnt*2];
+ ptr[1+pixels*1]=src[1+segcnt*2];
+ ptr[2+pixels*1]=src[2+segcnt*2];
+ ptr[3+pixels*1]=src[3+segcnt*2];
+ ptr[0+pixels*2]=src[0+segcnt*1];
+ ptr[1+pixels*2]=src[1+segcnt*1];
+ ptr[2+pixels*2]=src[2+segcnt*1];
+ ptr[3+pixels*2]=src[3+segcnt*1];
+ ptr[0+pixels*3]=src[0+segcnt*3];
+ ptr[1+pixels*3]=src[1+segcnt*3];
+ ptr[2+pixels*3]=src[2+segcnt*3];
+ ptr[3+pixels*3]=src[3+segcnt*3];
+ break;
+ }
+
+ /* next shading coefficient */
+ ptr+=4;
+ }
+ uint8_t val = dev->interface->read_register(0xd0+i);
+ addr = val * 8192 + 0x10000000;
+ dev->interface->write_ahb(addr, pixels * dev->session.segment_count, buffer.data());
+ }
+}
+
+
+/** @brief move to calibration area
+ * This functions moves scanning head to calibration area
+ * by doing a 600 dpi scan
+ * @param dev scanner device
+ */
+static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs)
+{
+ (void) sensor;
+
+ DBG_HELPER(dbg);
+ int pixels;
+ int size;
+
+ unsigned resolution = 600;
+ unsigned channels = 3;
+ const auto& move_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
+ dev->settings.scan_method);
+ pixels = (move_sensor.sensor_pixels * 600) / move_sensor.optical_res;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = 1;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, move_sensor);
+
+ dev->cmd_set->init_regs_for_scan_session(dev, move_sensor, &regs, session);
+
+ size = pixels * 3;
+ std::vector<uint8_t> line(size);
+
+ // write registers and scan data
+ dev->interface->write_registers(regs);
+
+ DBG (DBG_info, "%s: starting line reading\n", __func__);
+ dev->cmd_set->begin_scan(dev, move_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("move_to_calibration_area");
+ scanner_stop_action(*dev);
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), size);
+
+ // stop scanning
+ scanner_stop_action(*dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_pnm_file("gl124_movetocalarea.pnm", line.data(), 8, 3, pixels, 1);
+ }
+}
+
+/* this function does the led calibration by scanning one line of the calibration
+ area below scanner's top on white strip.
+
+-needs working coarse/gain
+*/
+SensorExposure CommandSetGl124::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int total_size;
+ int resolution;
+ int dpihw;
+ int i, j;
+ int val;
+ int channels;
+ int avg[3];
+ int turn;
+ uint16_t exp[3],target;
+
+ /* move to calibration area */
+ move_to_calibration_area(dev, sensor, regs);
+
+ /* offset calibration is always done in 16 bit depth color mode */
+ channels = 3;
+ dpihw = sensor.get_register_hwdpi(dev->settings.xres);
+ resolution = dpihw;
+ unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres);
+ resolution /= ccd_size_divisor;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
+ dev->settings.scan_method);
+ num_pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ total_size = num_pixels * channels * (session.params.depth / 8) * 1;
+ std::vector<uint8_t> line(total_size);
+
+ // initial loop values and boundaries
+ exp[0] = calib_sensor.exposure.red;
+ exp[1] = calib_sensor.exposure.green;
+ exp[2] = calib_sensor.exposure.blue;
+ target=sensor.gain_white_ref*256;
+
+ turn = 0;
+
+ /* no move during led calibration */
+ sanei_genesys_set_motor_power(regs, false);
+ bool acceptable = false;
+ do
+ {
+ // set up exposure
+ regs.set24(REG_EXPR, exp[0]);
+ regs.set24(REG_EXPG, exp[1]);
+ regs.set24(REG_EXPB, exp[2]);
+
+ // write registers and scan data
+ dev->interface->write_registers(regs);
+
+ DBG(DBG_info, "%s: starting line reading\n", __func__);
+ begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("led_calibration");
+ scanner_stop_action(*dev);
+ return calib_sensor.exposure;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ // stop scanning
+ scanner_stop_action(*dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl124_led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, line.data(), session.params.depth, channels, num_pixels,
+ 1);
+ }
+
+ /* compute average */
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
+
+ /* check if exposure gives average within the boundaries */
+ acceptable = true;
+ for(i=0;i<3;i++)
+ {
+ /* we accept +- 2% delta from target */
+ if(abs(avg[i]-target)>target/50)
+ {
+ float prev_weight = 0.5;
+ exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight);
+ acceptable = false;
+ }
+ }
+
+ turn++;
+ }
+ while (!acceptable && turn < 100);
+
+ DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
+
+ // set these values as final ones for scan
+ dev->reg.set24(REG_EXPR, exp[0]);
+ dev->reg.set24(REG_EXPG, exp[1]);
+ dev->reg.set24(REG_EXPB, exp[2]);
+
+ return { exp[0], exp[1], exp[2] };
+}
+
+/**
+ * average dark pixels of a 8 bits scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG(DBG_info, "%s: average = %d\n", __func__, average);
+ return average;
+}
+
+
+void CommandSetGl124::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ unsigned channels;
+ int pass = 0, avg, total_size;
+ int topavg, bottomavg, lines;
+ int top, bottom, black_pixels, pixels;
+
+ // no gain nor offset for TI AFE
+ uint8_t reg0a = dev->interface->read_register(REG_0x0A);
+ if (((reg0a & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) == 3) {
+ return;
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ dev->calib_pixels = sensor.sensor_pixels;
+ lines=1;
+ pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res;
+ black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
+ DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = sensor.optical_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ /* allocate memory for scans */
+ total_size = pixels * channels * lines * (session.params.depth / 8);
+
+ std::vector<uint8_t> first_line(total_size);
+ std::vector<uint8_t> second_line(total_size);
+
+ /* init gain */
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+
+ /* scan with no move */
+ bottom = 10;
+ dev->frontend.set_offset(0, bottom);
+ dev->frontend.set_offset(1, bottom);
+ dev->frontend.set_offset(2, bottom);
+
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("offset_calibration");
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char title[30];
+ std::snprintf(title, 30, "gl124_offset%03d.pnm", bottom);
+ sanei_genesys_write_pnm_file(title, first_line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
+
+ /* now top value */
+ top = 255;
+ dev->frontend.set_offset(0, top);
+ dev->frontend.set_offset(1, top);
+ dev->frontend.set_offset(2, top);
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+
+ topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.set_offset(0, (top + bottom) / 2);
+ dev->frontend.set_offset(1, (top + bottom) / 2);
+ dev->frontend.set_offset(2, (top + bottom) / 2);
+
+ // scan with no move
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char title[30];
+ std::snprintf(title, 30, "gl124_offset%03d.pnm", dev->frontend.get_offset(1));
+ sanei_genesys_write_pnm_file(title, second_line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.get_offset(1);
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.get_offset(1);
+ }
+ }
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+}
+
+
+/* alternative coarse gain calibration
+ this on uses the settings from offset_calibration and
+ uses only one scanline
+ */
+/*
+ with offset and coarse calibration we only want to get our input range into
+ a reasonable shape. the fine calibration of the upper and lower bounds will
+ be done with shading.
+ */
+void CommandSetGl124::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const
+{
+ DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
+ int pixels;
+ int total_size;
+ int i, j, channels;
+ int max[3];
+ float gain[3],coeff;
+ int val, code, lines;
+
+ // no gain nor offset for TI AFE
+ uint8_t reg0a = dev->interface->read_register(REG_0x0A);
+ if (((reg0a & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) == 3) {
+ return;
+ }
+
+ /* coarse gain calibration is always done in color mode */
+ channels = 3;
+
+ if(dev->settings.xres<sensor.optical_res)
+ {
+ coeff = 0.9f;
+ } else {
+ coeff = 1.0f;
+ }
+ lines=10;
+ pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res;
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = sensor.optical_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ try {
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
+ throw;
+ }
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ dev->interface->write_registers(regs);
+
+ total_size = pixels * channels * (16 / session.params.depth) * lines;
+
+ std::vector<uint8_t> line(total_size);
+
+ set_fe(dev, sensor, AFE_SET);
+ begin_scan(dev, sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("coarse_gain_calibration");
+ scanner_stop_action(*dev);
+ move_back_home(dev, true);
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl124_gain.pnm", line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* average value on each channel */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = pixels/4; i < (pixels*3/4); i++)
+ {
+ if (dev->model->is_cis) {
+ val = line[i + j * pixels];
+ } else {
+ val = line[i * channels + j];
+ }
+
+ max[j] += val;
+ }
+ max[j] = max[j] / (pixels/2);
+
+ gain[j] = (static_cast<float>(sensor.gain_white_ref) * coeff) / max[j];
+
+ /* turn logical gain value into gain code, checking for overflow */
+ code = static_cast<int>(283 - 208 / gain[j]);
+ if (code > 255)
+ code = 255;
+ else if (code < 0)
+ code = 0;
+ dev->frontend.set_gain(j, code);
+
+ DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j],
+ gain[j], dev->frontend.get_gain(j));
+ }
+
+ if (dev->model->is_cis) {
+ uint8_t gain0 = dev->frontend.get_gain(0);
+ if (gain0 > dev->frontend.get_gain(1)) {
+ gain0 = dev->frontend.get_gain(1);
+ }
+ if (gain0 > dev->frontend.get_gain(2)) {
+ gain0 = dev->frontend.get_gain(2);
+ }
+ dev->frontend.set_gain(0, gain0);
+ dev->frontend.set_gain(1, gain0);
+ dev->frontend.set_gain(2, gain0);
+ }
+
+ if (channels == 1) {
+ dev->frontend.set_gain(0, dev->frontend.get_gain(1));
+ dev->frontend.set_gain(2, dev->frontend.get_gain(1));
+ }
+
+ scanner_stop_action(*dev);
+
+ move_back_home(dev, true);
+}
+
+// wait for lamp warmup by scanning the same line until difference
+// between 2 scans is below a threshold
+void CommandSetGl124::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, int* channels,
+ int* total_size) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+
+ *channels=3;
+
+ *reg = dev->reg;
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = dev->motor.base_ydpi;
+ session.params.startx = sensor.sensor_pixels / 4;
+ session.params.starty = 0;
+ session.params.pixels = sensor.sensor_pixels / 2;
+ session.params.lines = 1;
+ session.params.depth = 8;
+ session.params.channels = *channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, reg, session);
+
+ num_pixels = session.output_pixels;
+
+ *total_size = num_pixels * 3 * 1; /* colors * bytes_per_color * scan lines */
+
+ sanei_genesys_set_motor_power(*reg, false);
+ dev->interface->write_registers(*reg);
+}
+
+/** @brief default GPIO values
+ * set up GPIO/GPOE for idle state
+ * @param dev device to set up
+ */
+static void gl124_init_gpio(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ int idx;
+
+ /* per model GPIO layout */
+ if (dev->model->model_id == ModelId::CANON_LIDE_110) {
+ idx = 0;
+ } else if (dev->model->model_id == ModelId::CANON_LIDE_120) {
+ idx = 2;
+ }
+ else
+ { /* canon LiDE 210 and 220 case */
+ idx = 1;
+ }
+
+ dev->interface->write_register(REG_0x31, gpios[idx].r31);
+ dev->interface->write_register(REG_0x32, gpios[idx].r32);
+ dev->interface->write_register(REG_0x33, gpios[idx].r33);
+ dev->interface->write_register(REG_0x34, gpios[idx].r34);
+ dev->interface->write_register(REG_0x35, gpios[idx].r35);
+ dev->interface->write_register(REG_0x36, gpios[idx].r36);
+ dev->interface->write_register(REG_0x38, gpios[idx].r38);
+}
+
+/**
+ * set memory layout by filling values in dedicated registers
+ */
+static void gl124_init_memory_layout(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ int idx = 0;
+
+ /* point to per model memory layout */
+ if (dev->model->model_id == ModelId::CANON_LIDE_110 ||
+ dev->model->model_id == ModelId::CANON_LIDE_120)
+ {
+ idx = 0;
+ }
+ else
+ { /* canon LiDE 210 and 220 case */
+ idx = 1;
+ }
+
+ /* setup base address for shading data. */
+ /* values must be multiplied by 8192=0x4000 to give address on AHB */
+ /* R-Channel shading bank0 address setting for CIS */
+ dev->interface->write_register(0xd0, layouts[idx].rd0);
+ /* G-Channel shading bank0 address setting for CIS */
+ dev->interface->write_register(0xd1, layouts[idx].rd1);
+ /* B-Channel shading bank0 address setting for CIS */
+ dev->interface->write_register(0xd2, layouts[idx].rd2);
+
+ /* setup base address for scanned data. */
+ /* values must be multiplied by 1024*2=0x0800 to give address on AHB */
+ /* R-Channel ODD image buffer 0x0124->0x92000 */
+ /* size for each buffer is 0x16d*1k word */
+ dev->interface->write_register(0xe0, layouts[idx].re0);
+ dev->interface->write_register(0xe1, layouts[idx].re1);
+ /* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
+ dev->interface->write_register(0xe2, layouts[idx].re2);
+ dev->interface->write_register(0xe3, layouts[idx].re3);
+
+ /* R-Channel EVEN image buffer 0x0292 */
+ dev->interface->write_register(0xe4, layouts[idx].re4);
+ dev->interface->write_register(0xe5, layouts[idx].re5);
+ /* R-Channel EVEN image buffer end-address 0x03ff*/
+ dev->interface->write_register(0xe6, layouts[idx].re6);
+ dev->interface->write_register(0xe7, layouts[idx].re7);
+
+ /* same for green, since CIS, same addresses */
+ dev->interface->write_register(0xe8, layouts[idx].re0);
+ dev->interface->write_register(0xe9, layouts[idx].re1);
+ dev->interface->write_register(0xea, layouts[idx].re2);
+ dev->interface->write_register(0xeb, layouts[idx].re3);
+ dev->interface->write_register(0xec, layouts[idx].re4);
+ dev->interface->write_register(0xed, layouts[idx].re5);
+ dev->interface->write_register(0xee, layouts[idx].re6);
+ dev->interface->write_register(0xef, layouts[idx].re7);
+
+/* same for blue, since CIS, same addresses */
+ dev->interface->write_register(0xf0, layouts[idx].re0);
+ dev->interface->write_register(0xf1, layouts[idx].re1);
+ dev->interface->write_register(0xf2, layouts[idx].re2);
+ dev->interface->write_register(0xf3, layouts[idx].re3);
+ dev->interface->write_register(0xf4, layouts[idx].re4);
+ dev->interface->write_register(0xf5, layouts[idx].re5);
+ dev->interface->write_register(0xf6, layouts[idx].re6);
+ dev->interface->write_register(0xf7, layouts[idx].re7);
+}
+
+/**
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+void CommandSetGl124::init(Genesys_Device* dev) const
+{
+ DBG_INIT ();
+ DBG_HELPER(dbg);
+
+ sanei_genesys_asic_init(dev, 0);
+}
+
+
+/* *
+ * initialize ASIC from power on condition
+ */
+void CommandSetGl124::asic_boot(Genesys_Device* dev, bool cold) const
+{
+ DBG_HELPER(dbg);
+
+ // reset ASIC in case of cold boot
+ if (cold) {
+ dev->interface->write_register(0x0e, 0x01);
+ dev->interface->write_register(0x0e, 0x00);
+ }
+
+ // enable GPOE 17
+ dev->interface->write_register(0x36, 0x01);
+
+ // set GPIO 17
+ uint8_t val = dev->interface->read_register(0x33);
+ val |= 0x01;
+ dev->interface->write_register(0x33, val);
+
+ // test CHKVER
+ val = dev->interface->read_register(REG_0x100);
+ if (val & REG_0x100_CHKVER) {
+ val = dev->interface->read_register(0x00);
+ DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
+ }
+
+ /* Set default values for registers */
+ gl124_init_registers (dev);
+
+ // Write initial registers
+ dev->interface->write_registers(dev->reg);
+
+ // tune reg 0B
+ dev->interface->write_register(REG_0x0B, REG_0x0B_30MHZ | REG_0x0B_ENBDRAM | REG_0x0B_64M);
+ dev->reg.remove_reg(0x0b);
+
+ //set up end access
+ dev->interface->write_0x8c(0x10, 0x0b);
+ dev->interface->write_0x8c(0x13, 0x0e);
+
+ /* CIS_LINE */
+ dev->reg.init_reg(0x08, REG_0x08_CIS_LINE);
+ dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value);
+
+ // setup gpio
+ gl124_init_gpio(dev);
+
+ // setup internal memory layout
+ gl124_init_memory_layout(dev);
+}
+
+
+void CommandSetGl124::update_hardware_sensors(Genesys_Scanner* s) const
+{
+ /* do what is needed to get a new set of events, but try to not loose
+ any of them.
+ */
+ DBG_HELPER(dbg);
+ uint8_t val = s->dev->interface->read_register(REG_0x31);
+
+ /* TODO : for the next scanner special case,
+ * add another per scanner button profile struct to avoid growing
+ * hard-coded button mapping here.
+ */
+ if ((s->dev->model->gpio_id == GpioId::CANON_LIDE_110) ||
+ (s->dev->model->gpio_id == GpioId::CANON_LIDE_120))
+ {
+ s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0);
+ s->buttons[BUTTON_FILE_SW].write((val & 0x08) == 0);
+ s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0);
+ s->buttons[BUTTON_COPY_SW].write((val & 0x02) == 0);
+ }
+ else
+ { /* LiDE 210 case */
+ s->buttons[BUTTON_EXTRA_SW].write((val & 0x01) == 0);
+ s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0);
+ s->buttons[BUTTON_COPY_SW].write((val & 0x04) == 0);
+ s->buttons[BUTTON_EMAIL_SW].write((val & 0x08) == 0);
+ s->buttons[BUTTON_FILE_SW].write((val & 0x10) == 0);
+ }
+}
+
+void CommandSetGl124::update_home_sensor_gpio(Genesys_Device& dev) const
+{
+ DBG_HELPER(dbg);
+
+ std::uint8_t val = dev.interface->read_register(REG_0x32);
+ val &= ~REG_0x32_GPIO10;
+ dev.interface->write_register(REG_0x32, val);
+}
+
+bool CommandSetGl124::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
+{
+ (void) dev;
+ return true;
+}
+
+void CommandSetGl124::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ sanei_genesys_send_gamma_table(dev, sensor);
+}
+
+void CommandSetGl124::load_document(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl124::detect_document_end(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl124::eject_document(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl124::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const
+{
+ (void) dev;
+ (void) sensor;
+ (void) forward;
+ (void) black;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl124::move_to_ta(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+std::unique_ptr<CommandSet> create_gl124_cmd_set()
+{
+ return std::unique_ptr<CommandSet>(new CommandSetGl124{});
+}
+
+} // namespace gl124
+} // namespace genesys
diff --git a/backend/genesys/gl124.h b/backend/genesys/gl124.h
new file mode 100644
index 0000000..cdf8faf
--- /dev/null
+++ b/backend/genesys/gl124.h
@@ -0,0 +1,205 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2016 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL124_H
+#define BACKEND_GENESYS_GL124_H
+
+#include "genesys.h"
+#include "command_set.h"
+
+namespace genesys {
+namespace gl124 {
+
+typedef struct
+{
+ uint8_t r31;
+ uint8_t r32;
+ uint8_t r33;
+ uint8_t r34;
+ uint8_t r35;
+ uint8_t r36;
+ uint8_t r38;
+} Gpio_layout;
+
+/** @brief gpio layout
+ * describes initial gpio settings for a given model
+ * registers 0x31 to 0x38
+ */
+static Gpio_layout gpios[]={
+ /* LiDE 110 */
+ { /* 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38 */
+ 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00
+ },
+ /* LiDE 210 */
+ {
+ 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00
+ },
+ /* LiDE 120 */
+ {
+ 0x9f, 0x53, 0x01, 0x80, 0x5f, 0x01, 0x00
+ },
+};
+
+typedef struct
+{
+ uint8_t rd0;
+ uint8_t rd1;
+ uint8_t rd2;
+ uint8_t re0;
+ uint8_t re1;
+ uint8_t re2;
+ uint8_t re3;
+ uint8_t re4;
+ uint8_t re5;
+ uint8_t re6;
+ uint8_t re7;
+} Memory_layout;
+
+static Memory_layout layouts[]={
+ /* LIDE 110, 120 */
+ { /* 0xd0 0xd1 0xd2 */
+ 0x0a, 0x15, 0x20,
+ /* 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 */
+ 0x00, 0xac, 0x08, 0x55, 0x08, 0x56, 0x0f, 0xff
+ },
+ /* LIDE 210, 220 */
+ {
+ 0x0a, 0x1f, 0x34,
+ 0x01, 0x24, 0x08, 0x91, 0x08, 0x92, 0x0f, 0xff
+ }
+};
+
+static void gl124_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table, int steps);
+
+class CommandSetGl124 : public CommandSet
+{
+public:
+ ~CommandSetGl124() override = default;
+
+ bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override;
+
+ void init(Genesys_Device* dev) const override;
+
+ void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const override;
+
+ void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const override;
+
+ void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const override;
+ void set_powersaving(Genesys_Device* dev, int delay) const override;
+ void save_power(Genesys_Device* dev, bool enable) const override;
+
+ void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, bool start_motor) const override;
+
+ void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override;
+
+ void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void search_start_position(Genesys_Device* dev) const override;
+
+ void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const override;
+
+ SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void wait_for_motor_stop(Genesys_Device* dev) const override;
+
+ void move_back_home(Genesys_Device* dev, bool wait_until_home) const override;
+
+ void update_hardware_sensors(struct Genesys_Scanner* s) const override;
+
+ bool needs_update_home_sensor_gpio() const override { return true; }
+
+ void update_home_sensor_gpio(Genesys_Device& dev) const override;
+
+ void load_document(Genesys_Device* dev) const override;
+
+ void detect_document_end(Genesys_Device* dev) const override;
+
+ void eject_document(Genesys_Device* dev) const override;
+
+ void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const override;
+
+ void move_to_ta(Genesys_Device* dev) const override;
+
+ void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,
+ int size) const override;
+
+ ScanSession calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const override;
+
+ void asic_boot(Genesys_Device* dev, bool cold) const override;
+};
+
+enum SlopeTable
+{
+ SCAN_TABLE = 0, // table 1 at 0x4000
+ BACKTRACK_TABLE = 1, // table 2 at 0x4800
+ STOP_TABLE = 2, // table 3 at 0x5000
+ FAST_TABLE = 3, // table 4 at 0x5800
+ HOME_TABLE = 4, // table 5 at 0x6000
+};
+
+} // namespace gl124
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL124_H
diff --git a/backend/genesys/gl124_registers.h b/backend/genesys/gl124_registers.h
new file mode 100644
index 0000000..9b42084
--- /dev/null
+++ b/backend/genesys/gl124_registers.h
@@ -0,0 +1,316 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL124_REGISTERS_H
+#define BACKEND_GENESYS_GL124_REGISTERS_H
+
+#include <cstdint>
+
+namespace genesys {
+namespace gl124 {
+
+using RegAddr = std::uint16_t;
+using RegMask = std::uint8_t;
+using RegShift = unsigned;
+
+static constexpr RegAddr REG_0x01 = 0x01;
+static constexpr RegMask REG_0x01_CISSET = 0x80;
+static constexpr RegMask REG_0x01_DOGENB = 0x40;
+static constexpr RegMask REG_0x01_DVDSET = 0x20;
+static constexpr RegMask REG_0x01_STAGGER = 0x10;
+static constexpr RegMask REG_0x01_COMPENB = 0x08;
+static constexpr RegMask REG_0x01_TRUEGRAY = 0x04;
+static constexpr RegMask REG_0x01_SHDAREA = 0x02;
+static constexpr RegMask REG_0x01_SCAN = 0x01;
+
+static constexpr RegAddr REG_0x02 = 0x02;
+static constexpr RegMask REG_0x02_NOTHOME = 0x80;
+static constexpr RegMask REG_0x02_ACDCDIS = 0x40;
+static constexpr RegMask REG_0x02_AGOHOME = 0x20;
+static constexpr RegMask REG_0x02_MTRPWR = 0x10;
+static constexpr RegMask REG_0x02_FASTFED = 0x08;
+static constexpr RegMask REG_0x02_MTRREV = 0x04;
+static constexpr RegMask REG_0x02_HOMENEG = 0x02;
+static constexpr RegMask REG_0x02_LONGCURV = 0x01;
+
+static constexpr RegAddr REG_0x03 = 0x03;
+static constexpr RegMask REG_0x03_LAMPDOG = 0x80;
+static constexpr RegMask REG_0x03_AVEENB = 0x40;
+static constexpr RegMask REG_0x03_XPASEL = 0x20;
+static constexpr RegMask REG_0x03_LAMPPWR = 0x10;
+static constexpr RegMask REG_0x03_LAMPTIM = 0x0f;
+
+static constexpr RegAddr REG_0x04 = 0x04;
+static constexpr RegMask REG_0x04_LINEART = 0x80;
+static constexpr RegMask REG_0x04_BITSET = 0x40;
+static constexpr RegMask REG_0x04_FILTER = 0x30;
+static constexpr RegMask REG_0x04_AFEMOD = 0x07;
+
+static constexpr RegAddr REG_0x05 = 0x05;
+static constexpr RegMask REG_0x05_DPIHW = 0xc0;
+static constexpr RegMask REG_0x05_DPIHW_600 = 0x00;
+static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40;
+static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80;
+static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0;
+static constexpr RegMask REG_0x05_MTLLAMP = 0x30;
+static constexpr RegMask REG_0x05_GMMENB = 0x08;
+static constexpr RegMask REG_0x05_ENB20M = 0x04;
+static constexpr RegMask REG_0x05_MTLBASE = 0x03;
+
+static constexpr RegAddr REG_0x06 = 0x06;
+static constexpr RegMask REG_0x06_SCANMOD = 0xe0;
+static constexpr RegMask REG_0x06S_SCANMOD = 5;
+static constexpr RegMask REG_0x06_PWRBIT = 0x10;
+static constexpr RegMask REG_0x06_GAIN4 = 0x08;
+static constexpr RegMask REG_0x06_OPTEST = 0x07;
+
+static constexpr RegMask REG_0x07_LAMPSIM = 0x80;
+
+static constexpr RegMask REG_0x08_DRAM2X = 0x80;
+static constexpr RegMask REG_0x08_MPENB = 0x20;
+static constexpr RegMask REG_0x08_CIS_LINE = 0x10;
+static constexpr RegMask REG_0x08_IR2_ENB = 0x08;
+static constexpr RegMask REG_0x08_IR1_ENB = 0x04;
+static constexpr RegMask REG_0x08_ENB24M = 0x01;
+
+static constexpr RegMask REG_0x09_MCNTSET = 0xc0;
+static constexpr RegMask REG_0x09_EVEN1ST = 0x20;
+static constexpr RegMask REG_0x09_BLINE1ST = 0x10;
+static constexpr RegMask REG_0x09_BACKSCAN = 0x08;
+static constexpr RegMask REG_0x09_OUTINV = 0x04;
+static constexpr RegMask REG_0x09_SHORTTG = 0x02;
+
+static constexpr RegShift REG_0x09S_MCNTSET = 6;
+static constexpr RegShift REG_0x09S_CLKSET = 4;
+
+static constexpr RegAddr REG_0x0A = 0x0a;
+static constexpr RegMask REG_0x0A_SIFSEL = 0xc0;
+static constexpr RegShift REG_0x0AS_SIFSEL = 6;
+static constexpr RegMask REG_0x0A_SHEETFED = 0x20;
+static constexpr RegMask REG_0x0A_LPWMEN = 0x10;
+
+static constexpr RegAddr REG_0x0B = 0x0b;
+static constexpr RegMask REG_0x0B_DRAMSEL = 0x07;
+static constexpr RegMask REG_0x0B_16M = 0x01;
+static constexpr RegMask REG_0x0B_64M = 0x02;
+static constexpr RegMask REG_0x0B_128M = 0x03;
+static constexpr RegMask REG_0x0B_256M = 0x04;
+static constexpr RegMask REG_0x0B_512M = 0x05;
+static constexpr RegMask REG_0x0B_1G = 0x06;
+static constexpr RegMask REG_0x0B_ENBDRAM = 0x08;
+static constexpr RegMask REG_0x0B_RFHDIS = 0x10;
+static constexpr RegMask REG_0x0B_CLKSET = 0xe0;
+static constexpr RegMask REG_0x0B_24MHZ = 0x00;
+static constexpr RegMask REG_0x0B_30MHZ = 0x20;
+static constexpr RegMask REG_0x0B_40MHZ = 0x40;
+static constexpr RegMask REG_0x0B_48MHZ = 0x60;
+static constexpr RegMask REG_0x0B_60MHZ = 0x80;
+
+static constexpr RegAddr REG_0x0D = 0x0d;
+static constexpr RegMask REG_0x0D_MTRP_RDY = 0x80;
+static constexpr RegMask REG_0x0D_FULLSTP = 0x10;
+static constexpr RegMask REG_0x0D_CLRMCNT = 0x04;
+static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02;
+static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01;
+
+static constexpr RegAddr REG_0x0F = 0x0f;
+
+static constexpr RegMask REG_0x16_CTRLHI = 0x80;
+static constexpr RegMask REG_0x16_TOSHIBA = 0x40;
+static constexpr RegMask REG_0x16_TGINV = 0x20;
+static constexpr RegMask REG_0x16_CK1INV = 0x10;
+static constexpr RegMask REG_0x16_CK2INV = 0x08;
+static constexpr RegMask REG_0x16_CTRLINV = 0x04;
+static constexpr RegMask REG_0x16_CKDIS = 0x02;
+static constexpr RegMask REG_0x16_CTRLDIS = 0x01;
+
+static constexpr RegMask REG_0x17_TGMODE = 0xc0;
+static constexpr RegMask REG_0x17_SNRSYN = 0x0f;
+
+static constexpr RegAddr REG_0x18 = 0x18;
+static constexpr RegMask REG_0x18_CNSET = 0x80;
+static constexpr RegMask REG_0x18_DCKSEL = 0x60;
+static constexpr RegMask REG_0x18_CKTOGGLE = 0x10;
+static constexpr RegMask REG_0x18_CKDELAY = 0x0c;
+static constexpr RegMask REG_0x18_CKSEL = 0x03;
+
+static constexpr RegMask REG_0x1A_SW2SET = 0x80;
+static constexpr RegMask REG_0x1A_SW1SET = 0x40;
+static constexpr RegMask REG_0x1A_MANUAL3 = 0x02;
+static constexpr RegMask REG_0x1A_MANUAL1 = 0x01;
+static constexpr RegMask REG_0x1A_CK4INV = 0x08;
+static constexpr RegMask REG_0x1A_CK3INV = 0x04;
+static constexpr RegMask REG_0x1A_LINECLP = 0x02;
+
+static constexpr RegMask REG_0x1C_TBTIME = 0x07;
+
+static constexpr RegAddr REG_0x1D = 0x1d;
+static constexpr RegMask REG_0x1D_CK4LOW = 0x80;
+static constexpr RegMask REG_0x1D_CK3LOW = 0x40;
+static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
+static constexpr RegMask REG_0x1D_LINESEL = 0x1f;
+static constexpr RegShift REG_0x1DS_LINESEL = 0;
+
+static constexpr RegAddr REG_0x1E = 0x1e;
+static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
+static constexpr RegShift REG_0x1ES_WDTIME = 4;
+
+static constexpr RegAddr REG_0x30 = 0x30;
+static constexpr RegAddr REG_0x31 = 0x31;
+static constexpr RegAddr REG_0x32 = 0x32;
+static constexpr RegMask REG_0x32_GPIO16 = 0x80;
+static constexpr RegMask REG_0x32_GPIO15 = 0x40;
+static constexpr RegMask REG_0x32_GPIO14 = 0x20;
+static constexpr RegMask REG_0x32_GPIO13 = 0x10;
+static constexpr RegMask REG_0x32_GPIO12 = 0x08;
+static constexpr RegMask REG_0x32_GPIO11 = 0x04;
+static constexpr RegMask REG_0x32_GPIO10 = 0x02;
+static constexpr RegMask REG_0x32_GPIO9 = 0x01;
+static constexpr RegAddr REG_0x33 = 0x33;
+static constexpr RegAddr REG_0x34 = 0x34;
+static constexpr RegAddr REG_0x35 = 0x35;
+static constexpr RegAddr REG_0x36 = 0x36;
+static constexpr RegAddr REG_0x37 = 0x37;
+static constexpr RegAddr REG_0x38 = 0x38;
+static constexpr RegAddr REG_0x39 = 0x39;
+
+static constexpr RegAddr REG_0x60 = 0x60;
+static constexpr RegMask REG_0x60_LED4TG = 0x80;
+static constexpr RegMask REG_0x60_YENB = 0x40;
+static constexpr RegMask REG_0x60_YBIT = 0x20;
+static constexpr RegMask REG_0x60_ACYNCNRLC = 0x10;
+static constexpr RegMask REG_0x60_ENOFFSET = 0x08;
+static constexpr RegMask REG_0x60_LEDADD = 0x04;
+static constexpr RegMask REG_0x60_CK4ADC = 0x02;
+static constexpr RegMask REG_0x60_AUTOCONF = 0x01;
+
+static constexpr RegAddr REG_0x80 = 0x80;
+static constexpr RegAddr REG_0x81 = 0x81;
+
+static constexpr RegAddr REG_0xA0 = 0xa0;
+static constexpr RegMask REG_0xA0_FSTPSEL = 0x38;
+static constexpr RegShift REG_0xA0S_FSTPSEL = 3;
+static constexpr RegMask REG_0xA0_STEPSEL = 0x07;
+static constexpr RegShift REG_0xA0S_STEPSEL = 0;
+
+static constexpr RegAddr REG_0xA1 = 0xa1;
+static constexpr RegAddr REG_0xA2 = 0xa2;
+static constexpr RegAddr REG_0xA3 = 0xa3;
+static constexpr RegAddr REG_0xA4 = 0xa4;
+static constexpr RegAddr REG_0xA5 = 0xa5;
+static constexpr RegAddr REG_0xA6 = 0xa6;
+static constexpr RegAddr REG_0xA7 = 0xa7;
+static constexpr RegAddr REG_0xA8 = 0xa8;
+static constexpr RegAddr REG_0xA9 = 0xa9;
+static constexpr RegAddr REG_0xAA = 0xaa;
+static constexpr RegAddr REG_0xAB = 0xab;
+static constexpr RegAddr REG_0xAC = 0xac;
+static constexpr RegAddr REG_0xAD = 0xad;
+static constexpr RegAddr REG_0xAE = 0xae;
+static constexpr RegAddr REG_0xAF = 0xaf;
+static constexpr RegAddr REG_0xB0 = 0xb0;
+static constexpr RegAddr REG_0xB1 = 0xb1;
+
+static constexpr RegAddr REG_0xB2 = 0xb2;
+static constexpr RegMask REG_0xB2_Z1MOD = 0x1f;
+static constexpr RegAddr REG_0xB3 = 0xb3;
+static constexpr RegMask REG_0xB3_Z1MOD = 0xff;
+static constexpr RegAddr REG_0xB4 = 0xb4;
+static constexpr RegMask REG_0xB4_Z1MOD = 0xff;
+
+static constexpr RegAddr REG_0xB5 = 0xb5;
+static constexpr RegMask REG_0xB5_Z2MOD = 0x1f;
+static constexpr RegAddr REG_0xB6 = 0xb6;
+static constexpr RegMask REG_0xB6_Z2MOD = 0xff;
+static constexpr RegAddr REG_0xB7 = 0xb7;
+static constexpr RegMask REG_0xB7_Z2MOD = 0xff;
+
+static constexpr RegAddr REG_0x100 = 0x100;
+static constexpr RegMask REG_0x100_DOCSNR = 0x80;
+static constexpr RegMask REG_0x100_ADFSNR = 0x40;
+static constexpr RegMask REG_0x100_COVERSNR = 0x20;
+static constexpr RegMask REG_0x100_CHKVER = 0x10;
+static constexpr RegMask REG_0x100_DOCJAM = 0x08;
+static constexpr RegMask REG_0x100_HISPDFLG = 0x04;
+static constexpr RegMask REG_0x100_MOTMFLG = 0x02;
+static constexpr RegMask REG_0x100_DATAENB = 0x01;
+
+static constexpr RegAddr REG_0x114 = 0x114;
+static constexpr RegAddr REG_0x115 = 0x115;
+
+static constexpr RegAddr REG_LINCNT = 0x25;
+static constexpr RegAddr REG_MAXWD = 0x28;
+static constexpr RegAddr REG_DPISET = 0x2c;
+static constexpr RegAddr REG_FEEDL = 0x3d;
+static constexpr RegAddr REG_CK1MAP = 0x74;
+static constexpr RegAddr REG_CK3MAP = 0x77;
+static constexpr RegAddr REG_CK4MAP = 0x7a;
+static constexpr RegAddr REG_LPERIOD = 0x7d;
+static constexpr RegAddr REG_DUMMY = 0x80;
+static constexpr RegAddr REG_STRPIXEL = 0x82;
+static constexpr RegAddr REG_ENDPIXEL = 0x85;
+static constexpr RegAddr REG_EXPDMY = 0x88;
+static constexpr RegAddr REG_EXPR = 0x8a;
+static constexpr RegAddr REG_EXPG = 0x8d;
+static constexpr RegAddr REG_EXPB = 0x90;
+static constexpr RegAddr REG_SEGCNT = 0x93;
+static constexpr RegAddr REG_TG0CNT = 0x96;
+static constexpr RegAddr REG_SCANFED = 0xa2;
+static constexpr RegAddr REG_STEPNO = 0xa4;
+static constexpr RegAddr REG_FWDSTEP = 0xa6;
+static constexpr RegAddr REG_BWDSTEP = 0xa8;
+static constexpr RegAddr REG_FASTNO = 0xaa;
+static constexpr RegAddr REG_FSHDEC = 0xac;
+static constexpr RegAddr REG_FMOVNO = 0xae;
+static constexpr RegAddr REG_FMOVDEC = 0xb0;
+static constexpr RegAddr REG_Z1MOD = 0xb2;
+static constexpr RegAddr REG_Z2MOD = 0xb5;
+
+static constexpr RegAddr REG_TRUER = 0x110;
+static constexpr RegAddr REG_TRUEG = 0x111;
+static constexpr RegAddr REG_TRUEB = 0x112;
+
+} // namespace gl124
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL843_REGISTERS_H
diff --git a/backend/genesys/gl646.cpp b/backend/genesys/gl646.cpp
new file mode 100644
index 0000000..04ee85e
--- /dev/null
+++ b/backend/genesys/gl646.cpp
@@ -0,0 +1,3436 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2007 Luke <iceyfor@gmail.com>
+ Copyright (C) 2011 Alexey Osipov <simba@lerlan.ru> for HP2400 description
+ and tuning
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "gl646.h"
+#include "gl646_registers.h"
+#include "test_settings.h"
+
+#include <vector>
+
+namespace genesys {
+namespace gl646 {
+
+namespace {
+constexpr unsigned CALIBRATION_LINES = 10;
+} // namespace
+
+static void gl646_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table,
+ int steps);
+
+/**
+ * reads value from gpio endpoint
+ */
+static void gl646_gpio_read(IUsbDevice& usb_dev, uint8_t* value)
+{
+ DBG_HELPER(dbg);
+ usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, GPIO_READ, INDEX, 1, value);
+}
+
+/**
+ * writes the given value to gpio endpoint
+ */
+static void gl646_gpio_write(IUsbDevice& usb_dev, uint8_t value)
+{
+ DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
+ usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_WRITE, INDEX, 1, &value);
+}
+
+/**
+ * writes the given value to gpio output enable endpoint
+ */
+static void gl646_gpio_output_enable(IUsbDevice& usb_dev, uint8_t value)
+{
+ DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
+ usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_OUTPUT_ENABLE, INDEX, 1, &value);
+}
+
+/**
+ * stop scanner's motor
+ * @param dev scanner's device
+ */
+static void gl646_stop_motor(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ dev->interface->write_register(0x0f, 0x00);
+}
+
+/**
+ * find the closest match in mode tables for the given resolution and scan mode.
+ * @param sensor id of the sensor
+ * @param required required resolution
+ * @param color true is color mode
+ * @return the closest resolution for the sensor and mode
+ */
+static unsigned get_closest_resolution(SensorId sensor_id, int required, unsigned channels)
+{
+ unsigned best_res = 0;
+ unsigned best_diff = 9600;
+
+ for (const auto& sensor : *s_sensors) {
+ if (sensor_id != sensor.sensor_id)
+ continue;
+
+ // exit on perfect match
+ if (sensor.resolutions.matches(required) && sensor.matches_channel_count(channels)) {
+ DBG(DBG_info, "%s: match found for %d\n", __func__, required);
+ return required;
+ }
+
+ // computes distance and keep mode if it is closer than previous
+ if (sensor.matches_channel_count(channels)) {
+ for (auto res : sensor.resolutions.resolutions()) {
+ unsigned curr_diff = std::abs(static_cast<int>(res) - static_cast<int>(required));
+ if (curr_diff < best_diff) {
+ best_res = res;
+ best_diff = curr_diff;
+ }
+ }
+ }
+ }
+
+ DBG(DBG_info, "%s: closest match for %d is %d\n", __func__, required, best_res);
+ return best_res;
+}
+
+/**
+ * Returns the cksel values used by the required scan mode.
+ * @param sensor id of the sensor
+ * @param required required resolution
+ * @param color true is color mode
+ * @return cksel value for mode
+ */
+static int get_cksel(SensorId sensor_id, int required, unsigned channels)
+{
+ for (const auto& sensor : *s_sensors) {
+ // exit on perfect match
+ if (sensor.sensor_id == sensor_id && sensor.resolutions.matches(required) &&
+ sensor.matches_channel_count(channels))
+ {
+ unsigned cksel = sensor.ccd_pixels_per_system_pixel();
+ DBG(DBG_io, "%s: match found for %d (cksel=%d)\n", __func__, required, cksel);
+ return cksel;
+ }
+ }
+ DBG(DBG_error, "%s: failed to find match for %d dpi\n", __func__, required);
+ /* fail safe fallback */
+ return 1;
+}
+
+void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs,
+ const ScanSession& session) const
+{
+ DBG_HELPER(dbg);
+ session.assert_computed();
+
+ debug_dump(DBG_info, sensor);
+
+ uint32_t move = session.params.starty;
+
+ int i, nb;
+ Motor_Master *motor = nullptr;
+ uint32_t z1, z2;
+ int feedl;
+
+
+ /* for the given resolution, search for master
+ * motor mode setting */
+ i = 0;
+ nb = sizeof (motor_master) / sizeof (Motor_Master);
+ while (i < nb)
+ {
+ if (dev->model->motor_id == motor_master[i].motor_id
+ && motor_master[i].dpi == session.params.yres
+ && motor_master[i].channels == session.params.channels)
+ {
+ motor = &motor_master[i];
+ }
+ i++;
+ }
+ if (motor == nullptr)
+ {
+ throw SaneException("unable to find settings for motor %d at %d dpi, color=%d",
+ static_cast<unsigned>(dev->model->motor_id),
+ session.params.yres, session.params.channels);
+ }
+
+ /* now we can search for the specific sensor settings */
+ i = 0;
+
+ // now apply values from settings to registers
+ regs->set16(REG_EXPR, sensor.exposure.red);
+ regs->set16(REG_EXPG, sensor.exposure.green);
+ regs->set16(REG_EXPB, sensor.exposure.blue);
+
+ for (const auto& reg : sensor.custom_regs) {
+ regs->set8(reg.address, reg.value);
+ }
+
+ /* now generate slope tables : we are not using generate_slope_table3 yet */
+ auto slope_table1 = create_slope_table(motor->slope1, motor->slope1.max_speed_w, StepType::FULL,
+ 1, 4, get_slope_table_max_size(AsicType::GL646));
+ auto slope_table2 = create_slope_table(motor->slope2, motor->slope2.max_speed_w, StepType::FULL,
+ 1, 4, get_slope_table_max_size(AsicType::GL646));
+
+ /* R01 */
+ /* now setup other registers for final scan (ie with shading enabled) */
+ /* watch dog + shading + scan enable */
+ regs->find_reg(0x01).value |= REG_0x01_DOGENB | REG_0x01_DVDSET | REG_0x01_SCAN;
+ if (dev->model->is_cis) {
+ regs->find_reg(0x01).value |= REG_0x01_CISSET;
+ } else {
+ regs->find_reg(0x01).value &= ~REG_0x01_CISSET;
+ }
+
+ /* if device has no calibration, don't enable shading correction */
+ if (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)
+ {
+ regs->find_reg(0x01).value &= ~REG_0x01_DVDSET;
+ }
+
+ regs->find_reg(0x01).value &= ~REG_0x01_FASTMOD;
+ if (motor->fastmod) {
+ regs->find_reg(0x01).value |= REG_0x01_FASTMOD;
+ }
+
+ /* R02 */
+ /* allow moving when buffer full by default */
+ if (!dev->model->is_sheetfed) {
+ dev->reg.find_reg(0x02).value &= ~REG_0x02_ACDCDIS;
+ } else {
+ dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS;
+ }
+
+ /* setup motor power and direction */
+ sanei_genesys_set_motor_power(*regs, true);
+
+ if (has_flag(session.params.flags, ScanFlag::REVERSE)) {
+ regs->find_reg(0x02).value |= REG_0x02_MTRREV;
+ } else {
+ regs->find_reg(0x02).value &= ~REG_0x02_MTRREV;
+ }
+
+ /* fastfed enabled (2 motor slope tables) */
+ if (motor->fastfed) {
+ regs->find_reg(0x02).value |= REG_0x02_FASTFED;
+ } else {
+ regs->find_reg(0x02).value &= ~REG_0x02_FASTFED;
+ }
+
+ /* step type */
+ regs->find_reg(0x02).value &= ~REG_0x02_STEPSEL;
+ switch (motor->steptype)
+ {
+ case StepType::FULL:
+ break;
+ case StepType::HALF:
+ regs->find_reg(0x02).value |= 1;
+ break;
+ case StepType::QUARTER:
+ regs->find_reg(0x02).value |= 2;
+ break;
+ default:
+ regs->find_reg(0x02).value |= 3;
+ break;
+ }
+
+ if (dev->model->is_sheetfed) {
+ regs->find_reg(0x02).value &= ~REG_0x02_AGOHOME;
+ } else {
+ regs->find_reg(0x02).value |= REG_0x02_AGOHOME;
+ }
+
+ /* R03 */
+ regs->find_reg(0x03).value &= ~REG_0x03_AVEENB;
+ // regs->find_reg(0x03).value |= REG_0x03_AVEENB;
+ regs->find_reg(0x03).value &= ~REG_0x03_LAMPDOG;
+
+ /* select XPA */
+ regs->find_reg(0x03).value &= ~REG_0x03_XPASEL;
+ if ((session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE) {
+ regs->find_reg(0x03).value |= REG_0x03_XPASEL;
+ }
+ regs->state.is_xpa_on = (session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE;
+
+ /* R04 */
+ /* monochrome / color scan */
+ switch (session.params.depth) {
+ case 8:
+ regs->find_reg(0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
+ break;
+ case 16:
+ regs->find_reg(0x04).value &= ~REG_0x04_LINEART;
+ regs->find_reg(0x04).value |= REG_0x04_BITSET;
+ break;
+ }
+
+ sanei_genesys_set_dpihw(*regs, sensor, sensor.optical_res);
+
+ /* gamma enable for scans */
+ if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) {
+ regs->find_reg(0x05).value |= REG_0x05_GMM14BIT;
+ }
+
+ regs->find_reg(0x05).value &= ~REG_0x05_GMMENB;
+
+ /* true CIS gray if needed */
+ if (dev->model->is_cis && session.params.channels == 1 && dev->settings.true_gray) {
+ regs->find_reg(0x05).value |= REG_0x05_LEDADD;
+ } else {
+ regs->find_reg(0x05).value &= ~REG_0x05_LEDADD;
+ }
+
+ /* HP2400 1200dpi mode tuning */
+
+ if (dev->model->sensor_id == SensorId::CCD_HP2400) {
+ /* reset count of dummy lines to zero */
+ regs->find_reg(0x1e).value &= ~REG_0x1E_LINESEL;
+ if (session.params.xres >= 1200) {
+ /* there must be one dummy line */
+ regs->find_reg(0x1e).value |= 1 & REG_0x1E_LINESEL;
+
+ /* GPO12 need to be set to zero */
+ regs->find_reg(0x66).value &= ~0x20;
+ }
+ else
+ {
+ /* set GPO12 back to one */
+ regs->find_reg(0x66).value |= 0x20;
+ }
+ }
+
+ /* motor steps used */
+ unsigned forward_steps = motor->fwdbwd;
+ unsigned backward_steps = motor->fwdbwd;
+
+ // the steps count must be different by at most 128, otherwise it's impossible to construct
+ // a proper backtracking curve. We're using slightly lower limit to allow at least a minimum
+ // distance between accelerations (forward_steps, backward_steps)
+ if (slope_table1.steps_count > slope_table2.steps_count + 100) {
+ slope_table2.steps_count += slope_table1.steps_count - 100;
+ }
+ if (slope_table2.steps_count > slope_table1.steps_count + 100) {
+ slope_table1.steps_count += slope_table2.steps_count - 100;
+ }
+
+ if (slope_table1.steps_count >= slope_table2.steps_count) {
+ backward_steps += (slope_table1.steps_count - slope_table2.steps_count) * 2;
+ } else {
+ forward_steps += (slope_table2.steps_count - slope_table1.steps_count) * 2;
+ }
+
+ if (forward_steps > 255) {
+ if (backward_steps < (forward_steps - 255)) {
+ throw SaneException("Can't set backtracking parameters without skipping image");
+ }
+ backward_steps -= forward_steps - 255;
+ }
+ if (backward_steps > 255) {
+ if (forward_steps < (backward_steps - 255)) {
+ throw SaneException("Can't set backtracking parameters without skipping image");
+ }
+ forward_steps -= backward_steps - 255;
+ }
+
+ regs->find_reg(0x21).value = slope_table1.steps_count;
+ regs->find_reg(0x24).value = slope_table2.steps_count;
+ regs->find_reg(0x22).value = forward_steps;
+ regs->find_reg(0x23).value = backward_steps;
+
+ /* CIS scanners read one line per color channel
+ * since gray mode use 'add' we also read 3 channels even not in
+ * color mode */
+ if (dev->model->is_cis) {
+ regs->set24(REG_LINCNT, session.output_line_count * 3);
+ } else {
+ regs->set24(REG_LINCNT, session.output_line_count);
+ }
+
+ regs->set16(REG_STRPIXEL, session.pixel_startx);
+ regs->set16(REG_ENDPIXEL, session.pixel_endx);
+
+ regs->set24(REG_MAXWD, session.output_line_bytes);
+
+ regs->set16(REG_DPISET, session.output_resolution * session.ccd_size_divisor *
+ sensor.ccd_pixels_per_system_pixel());
+ regs->set16(REG_LPERIOD, sensor.exposure_lperiod);
+
+ /* move distance must be adjusted to take into account the extra lines
+ * read to reorder data */
+ feedl = move;
+
+ if (session.num_staggered_lines + session.max_color_shift_lines > 0 && feedl != 0) {
+ int feed_offset = ((session.max_color_shift_lines + session.num_staggered_lines) * dev->motor.optical_ydpi) /
+ motor->dpi;
+ if (feedl > feed_offset) {
+ feedl = feedl - feed_offset;
+ }
+ }
+
+ /* we assume all scans are done with 2 tables */
+ /*
+ feedl = feed_steps - fast_slope_steps*2 -
+ (slow_slope_steps >> scan_step_type); */
+ /* but head has moved due to shading calibration => dev->scanhead_position_primary */
+ if (feedl > 0)
+ {
+ DBG(DBG_info, "%s: initial move=%d\n", __func__, feedl);
+
+ /* TODO clean up this when I'll fully understand.
+ * for now, special casing each motor */
+ switch (dev->model->motor_id) {
+ case MotorId::MD_5345:
+ switch (motor->dpi) {
+ case 200:
+ feedl -= 70;
+ break;
+ case 300:
+ feedl -= 70;
+ break;
+ case 400:
+ feedl += 130;
+ break;
+ case 600:
+ feedl += 160;
+ break;
+ case 1200:
+ feedl += 160;
+ break;
+ case 2400:
+ feedl += 180;
+ break;
+ default:
+ break;
+ }
+ break;
+ case MotorId::HP2300:
+ switch (motor->dpi) {
+ case 75:
+ feedl -= 180;
+ break;
+ case 150:
+ feedl += 0;
+ break;
+ case 300:
+ feedl += 30;
+ break;
+ case 600:
+ feedl += 35;
+ break;
+ case 1200:
+ feedl += 45;
+ break;
+ default:
+ break;
+ }
+ break;
+ case MotorId::HP2400:
+ switch (motor->dpi) {
+ case 150:
+ feedl += 150;
+ break;
+ case 300:
+ feedl += 220;
+ break;
+ case 600:
+ feedl += 260;
+ break;
+ case 1200:
+ feedl += 280; /* 300 */
+ break;
+ case 50:
+ feedl += 0;
+ break;
+ case 100:
+ feedl += 100;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* theorical value */
+ default: {
+ unsigned step_shift = static_cast<unsigned>(motor->steptype);
+
+ if (motor->fastfed)
+ {
+ feedl = feedl - 2 * slope_table2.steps_count -
+ (slope_table1.steps_count >> step_shift);
+ }
+ else
+ {
+ feedl = feedl - (slope_table1.steps_count >> step_shift);
+ }
+ break;
+ }
+ }
+ /* security */
+ if (feedl < 0)
+ feedl = 0;
+ }
+
+ DBG(DBG_info, "%s: final move=%d\n", __func__, feedl);
+ regs->set24(REG_FEEDL, feedl);
+
+ regs->find_reg(0x65).value = motor->mtrpwm;
+
+ sanei_genesys_calculate_zmod(regs->find_reg(0x02).value & REG_0x02_FASTFED,
+ sensor.exposure_lperiod,
+ slope_table1.table,
+ slope_table1.steps_count,
+ move, motor->fwdbwd, &z1, &z2);
+
+ /* no z1/z2 for sheetfed scanners */
+ if (dev->model->is_sheetfed) {
+ z1 = 0;
+ z2 = 0;
+ }
+ regs->set16(REG_Z1MOD, z1);
+ regs->set16(REG_Z2MOD, z2);
+ regs->find_reg(0x6b).value = slope_table2.steps_count;
+ regs->find_reg(0x6c).value =
+ (regs->find_reg(0x6c).value & REG_0x6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16)
+ & 0x07);
+
+ write_control(dev, sensor, session.output_resolution);
+
+ // setup analog frontend
+ gl646_set_fe(dev, sensor, AFE_SET, session.output_resolution);
+
+ dev->read_buffer.clear();
+ dev->read_buffer.alloc(session.buffer_size_read);
+
+ build_image_pipeline(dev, session);
+
+ dev->read_active = true;
+
+ dev->session = session;
+
+ dev->total_bytes_read = 0;
+ dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines;
+
+ /* select color filter based on settings */
+ regs->find_reg(0x04).value &= ~REG_0x04_FILTER;
+ if (session.params.channels == 1) {
+ switch (session.params.color_filter) {
+ case ColorFilter::RED:
+ regs->find_reg(0x04).value |= 0x04;
+ break;
+ case ColorFilter::GREEN:
+ regs->find_reg(0x04).value |= 0x08;
+ break;
+ case ColorFilter::BLUE:
+ regs->find_reg(0x04).value |= 0x0c;
+ break;
+ default:
+ break;
+ }
+ }
+
+ gl646_send_slope_table(dev, 0, slope_table1.table, regs->get8(0x21));
+ gl646_send_slope_table(dev, 1, slope_table2.table, regs->get8(0x6b));
+}
+
+
+/** copy sensor specific settings */
+/* *dev : device infos
+ *regs : regiters to be set
+ extended : do extended set up
+ ccd_size_divisor: set up for half ccd resolution
+ all registers 08-0B, 10-1D, 52-5E are set up. They shouldn't
+ appear anywhere else but in register init
+*/
+static void
+gl646_setup_sensor (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * regs)
+{
+ (void) dev;
+ DBG(DBG_proc, "%s: start\n", __func__);
+
+ for (const auto& reg_setting : sensor.custom_base_regs) {
+ regs->set8(reg_setting.address, reg_setting.value);
+ }
+ // FIXME: all other drivers don't set exposure here
+ regs_set_exposure(AsicType::GL646, *regs, sensor.exposure);
+
+ DBG(DBG_proc, "%s: end\n", __func__);
+}
+
+/**
+ * Set all registers to default values after init
+ * @param dev scannerr's device to set
+ */
+static void
+gl646_init_regs (Genesys_Device * dev)
+{
+ int addr;
+
+ DBG(DBG_proc, "%s\n", __func__);
+
+ dev->reg.clear();
+
+ for (addr = 1; addr <= 0x0b; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x10; addr <= 0x29; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x2c; addr <= 0x39; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x3d; addr <= 0x3f; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x52; addr <= 0x5e; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x60; addr <= 0x6d; addr++)
+ dev->reg.init_reg(addr, 0);
+
+ dev->reg.find_reg(0x01).value = 0x20 /*0x22 */ ; /* enable shading, CCD, color, 1M */
+ dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */
+ if (dev->model->motor_id == MotorId::MD_5345) {
+ dev->reg.find_reg(0x02).value |= 0x01; // half-step
+ }
+ switch (dev->model->motor_id) {
+ case MotorId::MD_5345:
+ dev->reg.find_reg(0x02).value |= 0x01; /* half-step */
+ break;
+ case MotorId::XP200:
+ /* for this sheetfed scanner, no AGOHOME, nor backtracking */
+ dev->reg.find_reg(0x02).value = 0x50;
+ break;
+ default:
+ break;
+ }
+ dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ; /* lamp on */
+ 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? */
+ switch (dev->model->adc_id)
+ {
+ case AdcId::AD_XP200:
+ dev->reg.find_reg(0x04).value = 0x12;
+ break;
+ default:
+ /* Wolfson frontend */
+ dev->reg.find_reg(0x04).value = 0x13;
+ break;
+ }
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ dev->reg.find_reg(0x05).value = 0x00; /* 12 bits gamma, disable gamma, 24 clocks/pixel */
+ sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res);
+
+ if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) {
+ dev->reg.find_reg(0x05).value |= REG_0x05_GMM14BIT;
+ }
+ if (dev->model->adc_id == AdcId::AD_XP200) {
+ dev->reg.find_reg(0x05).value |= 0x01; /* 12 clocks/pixel */
+ }
+
+ if (dev->model->sensor_id == SensorId::CCD_HP2300) {
+ dev->reg.find_reg(0x06).value = 0x00; // PWRBIT off, shading gain=4, normal AFE image capture
+ } else {
+ dev->reg.find_reg(0x06).value = 0x18; // PWRBIT on, shading gain=8, normal AFE image capture
+ }
+
+
+ gl646_setup_sensor(dev, sensor, &dev->reg);
+
+ dev->reg.find_reg(0x1e).value = 0xf0; /* watch-dog time */
+
+ switch (dev->model->sensor_id)
+ {
+ case SensorId::CCD_HP2300:
+ dev->reg.find_reg(0x1e).value = 0xf0;
+ dev->reg.find_reg(0x1f).value = 0x10;
+ dev->reg.find_reg(0x20).value = 0x20;
+ break;
+ case SensorId::CCD_HP2400:
+ dev->reg.find_reg(0x1e).value = 0x80;
+ dev->reg.find_reg(0x1f).value = 0x10;
+ dev->reg.find_reg(0x20).value = 0x20;
+ break;
+ case SensorId::CCD_HP3670:
+ dev->reg.find_reg(0x19).value = 0x2a;
+ dev->reg.find_reg(0x1e).value = 0x80;
+ dev->reg.find_reg(0x1f).value = 0x10;
+ dev->reg.find_reg(0x20).value = 0x20;
+ break;
+ case SensorId::CIS_XP200:
+ dev->reg.find_reg(0x1e).value = 0x10;
+ dev->reg.find_reg(0x1f).value = 0x01;
+ dev->reg.find_reg(0x20).value = 0x50;
+ break;
+ default:
+ dev->reg.find_reg(0x1f).value = 0x01;
+ dev->reg.find_reg(0x20).value = 0x50;
+ break;
+ }
+
+ dev->reg.find_reg(0x21).value = 0x08 /*0x20 */ ; /* table one steps number for forward slope curve of the acc/dec */
+ dev->reg.find_reg(0x22).value = 0x10 /*0x08 */ ; /* steps number of the forward steps for start/stop */
+ dev->reg.find_reg(0x23).value = 0x10 /*0x08 */ ; /* steps number of the backward steps for start/stop */
+ dev->reg.find_reg(0x24).value = 0x08 /*0x20 */ ; /* table one steps number backward slope curve of the acc/dec */
+ dev->reg.find_reg(0x25).value = 0x00; /* scan line numbers (7000) */
+ dev->reg.find_reg(0x26).value = 0x00 /*0x1b */ ;
+ dev->reg.find_reg(0x27).value = 0xd4 /*0x58 */ ;
+ dev->reg.find_reg(0x28).value = 0x01; /* PWM duty for lamp control */
+ dev->reg.find_reg(0x29).value = 0xff;
+
+ dev->reg.find_reg(0x2c).value = 0x02; /* set resolution (600 DPI) */
+ dev->reg.find_reg(0x2d).value = 0x58;
+ dev->reg.find_reg(0x2e).value = 0x78; /* set black&white threshold high level */
+ dev->reg.find_reg(0x2f).value = 0x7f; /* set black&white threshold low level */
+
+ dev->reg.find_reg(0x30).value = 0x00; /* begin pixel position (16) */
+ dev->reg.find_reg(0x31).value = sensor.dummy_pixel /*0x10 */ ; /* TGW + 2*TG_SHLD + x */
+ dev->reg.find_reg(0x32).value = 0x2a /*0x15 */ ; /* end pixel position (5390) */
+ dev->reg.find_reg(0x33).value = 0xf8 /*0x0e */ ; /* TGW + 2*TG_SHLD + y */
+ dev->reg.find_reg(0x34).value = sensor.dummy_pixel;
+ dev->reg.find_reg(0x35).value = 0x01 /*0x00 */ ; /* set maximum word size per line, for buffer full control (10800) */
+ dev->reg.find_reg(0x36).value = 0x00 /*0x2a */ ;
+ dev->reg.find_reg(0x37).value = 0x00 /*0x30 */ ;
+ dev->reg.find_reg(0x38).value = 0x2a; // line period (exposure time = 11000 pixels) */
+ dev->reg.find_reg(0x39).value = 0xf8;
+ dev->reg.find_reg(0x3d).value = 0x00; /* set feed steps number of motor move */
+ dev->reg.find_reg(0x3e).value = 0x00;
+ dev->reg.find_reg(0x3f).value = 0x01 /*0x00 */ ;
+
+ dev->reg.find_reg(0x60).value = 0x00; /* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
+ dev->reg.find_reg(0x61).value = 0x00; /* (21h+22h)/LPeriod */
+ dev->reg.find_reg(0x62).value = 0x00; /* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
+ dev->reg.find_reg(0x63).value = 0x00; /* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
+ dev->reg.find_reg(0x64).value = 0x00; /* motor PWM frequency */
+ dev->reg.find_reg(0x65).value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */
+ if (dev->model->motor_id == MotorId::MD_5345) {
+ // PWM duty cycle for table one motor phase (63 = max)
+ dev->reg.find_reg(0x65).value = 0x02;
+ }
+
+ for (const auto& reg : dev->gpo.regs) {
+ dev->reg.set8(reg.address, reg.value);
+ }
+
+ switch (dev->model->motor_id) {
+ case MotorId::HP2300:
+ case MotorId::HP2400:
+ dev->reg.find_reg(0x6a).value = 0x7f; /* table two steps number for acc/dec */
+ dev->reg.find_reg(0x6b).value = 0x78; /* table two steps number for acc/dec */
+ dev->reg.find_reg(0x6d).value = 0x7f;
+ break;
+ case MotorId::MD_5345:
+ dev->reg.find_reg(0x6a).value = 0x42; /* table two fast moving step type, PWM duty for table two */
+ dev->reg.find_reg(0x6b).value = 0xff; /* table two steps number for acc/dec */
+ dev->reg.find_reg(0x6d).value = 0x41; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
+ break;
+ case MotorId::XP200:
+ dev->reg.find_reg(0x6a).value = 0x7f; /* table two fast moving step type, PWM duty for table two */
+ dev->reg.find_reg(0x6b).value = 0x08; /* table two steps number for acc/dec */
+ dev->reg.find_reg(0x6d).value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
+ break;
+ case MotorId::HP3670:
+ dev->reg.find_reg(0x6a).value = 0x41; /* table two steps number for acc/dec */
+ dev->reg.find_reg(0x6b).value = 0xc8; /* table two steps number for acc/dec */
+ dev->reg.find_reg(0x6d).value = 0x7f;
+ break;
+ default:
+ dev->reg.find_reg(0x6a).value = 0x40; /* table two fast moving step type, PWM duty for table two */
+ dev->reg.find_reg(0x6b).value = 0xff; /* table two steps number for acc/dec */
+ dev->reg.find_reg(0x6d).value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
+ break;
+ }
+ dev->reg.find_reg(0x6c).value = 0x00; /* peroid times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
+}
+
+
+// Send slope table for motor movement slope_table in machine byte order
+static void gl646_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table,
+ int steps)
+{
+ DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d)=%d .. %d", table_nr, steps, slope_table[0],
+ slope_table[steps - 1]);
+ int dpihw;
+ int start_address;
+
+ dpihw = dev->reg.find_reg(0x05).value >> 6;
+
+ if (dpihw == 0) /* 600 dpi */
+ start_address = 0x08000;
+ else if (dpihw == 1) /* 1200 dpi */
+ start_address = 0x10000;
+ else if (dpihw == 2) /* 2400 dpi */
+ start_address = 0x1f800;
+ else {
+ throw SaneException("Unexpected dpihw");
+ }
+
+ std::vector<uint8_t> table(steps * 2);
+ for (int i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (dev->interface->is_mock()) {
+ dev->interface->record_slope_table(table_nr, slope_table);
+ }
+ dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(), steps * 2);
+}
+
+// Set values of Analog Device type frontend
+static void gl646_set_ad_fe(Genesys_Device* dev, uint8_t set)
+{
+ DBG_HELPER(dbg);
+ int i;
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+
+ dev->frontend = dev->frontend_initial;
+
+ // write them to analog frontend
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+ }
+ if (set == AFE_SET)
+ {
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i));
+ }
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i));
+ }
+ }
+ /*
+ if (set == AFE_POWER_SAVE)
+ {
+ dev->interface->write_fe_register(0x00, dev->frontend.reg[0] | 0x04);
+ } */
+}
+
+/** set up analog frontend
+ * set up analog frontend
+ * @param dev device to set up
+ * @param set action from AFE_SET, AFE_INIT and AFE_POWERSAVE
+ * @param dpi resolution of the scan since it affects settings
+ */
+static void gl646_wm_hp3670(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set,
+ unsigned dpi)
+{
+ DBG_HELPER(dbg);
+ int i;
+
+ switch (set)
+ {
+ case AFE_INIT:
+ dev->interface->write_fe_register(0x04, 0x80);
+ dev->interface->sleep_ms(200);
+ dev->interface->write_register(0x50, 0x00);
+ dev->frontend = dev->frontend_initial;
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+ dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02));
+ gl646_gpio_output_enable(dev->interface->get_usb_device(), 0x07);
+ break;
+ case AFE_POWER_SAVE:
+ dev->interface->write_fe_register(0x01, 0x06);
+ dev->interface->write_fe_register(0x06, 0x0f);
+ return;
+ break;
+ default: /* AFE_SET */
+ /* mode setup */
+ i = dev->frontend.regs.get_value(0x03);
+ if (dpi > sensor.optical_res / 2)
+ {
+ /* fe_reg_0x03 must be 0x12 for 1200 dpi in WOLFSON_HP3670.
+ * WOLFSON_HP2400 in 1200 dpi mode works well with
+ * fe_reg_0x03 set to 0x32 or 0x12 but not to 0x02 */
+ i = 0x12;
+ }
+ dev->interface->write_fe_register(0x03, i);
+ /* offset and sign (or msb/lsb ?) */
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
+ dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
+ }
+
+ // gain
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
+ }
+ }
+}
+
+/** Set values of analog frontend
+ * @param dev device to set
+ * @param set action to execute
+ * @param dpi dpi to setup the AFE
+ */
+static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set, int dpi)
+{
+ DBG_HELPER_ARGS(dbg, "%s,%d", set == AFE_INIT ? "init" :
+ set == AFE_SET ? "set" :
+ set == AFE_POWER_SAVE ? "powersave" : "huh?", dpi);
+ int i;
+ uint8_t val;
+
+ /* Analog Device type frontend */
+ uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET;
+ if (frontend_type == 0x02) {
+ gl646_set_ad_fe(dev, set);
+ return;
+ }
+
+ /* Wolfson type frontend */
+ if (frontend_type != 0x03) {
+ throw SaneException("unsupported frontend type %d", frontend_type);
+ }
+
+ /* per frontend function to keep code clean */
+ switch (dev->model->adc_id)
+ {
+ case AdcId::WOLFSON_HP3670:
+ case AdcId::WOLFSON_HP2400:
+ gl646_wm_hp3670(dev, sensor, set, dpi);
+ return;
+ default:
+ DBG(DBG_proc, "%s(): using old method\n", __func__);
+ break;
+ }
+
+ /* initialize analog frontend */
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+ dev->frontend = dev->frontend_initial;
+
+ // reset only done on init
+ dev->interface->write_fe_register(0x04, 0x80);
+
+ /* enable GPIO for some models */
+ if (dev->model->sensor_id == SensorId::CCD_HP2300) {
+ val = 0x07;
+ gl646_gpio_output_enable(dev->interface->get_usb_device(), val);
+ }
+ return;
+ }
+
+ // set fontend to power saving mode
+ if (set == AFE_POWER_SAVE) {
+ dev->interface->write_fe_register(0x01, 0x02);
+ return;
+ }
+
+ /* here starts AFE_SET */
+ /* TODO : base this test on cfg reg3 or a CCD family flag to be created */
+ /* if (dev->model->ccd_type != SensorId::CCD_HP2300
+ && dev->model->ccd_type != SensorId::CCD_HP3670
+ && dev->model->ccd_type != SensorId::CCD_HP2400) */
+ {
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+ dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02));
+ }
+
+ // start with reg3
+ dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x03));
+
+ switch (dev->model->sensor_id)
+ {
+ default:
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
+ dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
+ dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
+ }
+ break;
+ /* just can't have it to work ....
+ case SensorId::CCD_HP2300:
+ case SensorId::CCD_HP2400:
+ case SensorId::CCD_HP3670:
+
+ dev->interface->write_fe_register(0x23, dev->frontend.get_offset(1));
+ dev->interface->write_fe_register(0x28, dev->frontend.get_gain(1));
+ break; */
+ }
+
+ // end with reg1
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+}
+
+/** Set values of analog frontend
+ * this this the public interface, the gl646 as to use one more
+ * parameter to work effectively, hence the redirection
+ * @param dev device to set
+ * @param set action to execute
+ */
+void CommandSetGl646::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const
+{
+ gl646_set_fe(dev, sensor, set, dev->settings.yres);
+}
+
+/**
+ * enters or leaves power saving mode
+ * limited to AFE for now.
+ * @param dev scanner's device
+ * @param enable true to enable power saving, false to leave it
+ */
+void CommandSetGl646::save_power(Genesys_Device* dev, bool enable) const
+{
+ DBG_HELPER_ARGS(dbg, "enable = %d", enable);
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ if (enable)
+ {
+ // gl646_set_fe(dev, sensor, AFE_POWER_SAVE);
+ }
+ else
+ {
+ gl646_set_fe(dev, sensor, AFE_INIT, 0);
+ }
+}
+
+void CommandSetGl646::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
+{
+ DBG_HELPER_ARGS(dbg, "delay = %d", delay);
+ Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
+ int rate, exposure_time, tgtime, time;
+
+ local_reg.init_reg(0x01, dev->reg.get8(0x01)); // disable fastmode
+ local_reg.init_reg(0x03, dev->reg.get8(0x03)); // Lamp power control
+ local_reg.init_reg(0x05, dev->reg.get8(0x05) & ~REG_0x05_BASESEL); // 24 clocks/pixel
+ local_reg.init_reg(0x38, 0x00); // line period low
+ local_reg.init_reg(0x39, 0x00); //line period high
+ local_reg.init_reg(0x6c, 0x00); // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE
+
+ if (!delay)
+ local_reg.find_reg(0x03).value &= 0xf0; /* disable lampdog and set lamptime = 0 */
+ else if (delay < 20)
+ local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */
+ else
+ local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */
+
+ time = delay * 1000 * 60; /* -> msec */
+ exposure_time = static_cast<std::uint32_t>((time * 32000.0 /
+ (24.0 * 64.0 * (local_reg.get8(0x03) & REG_0x03_LAMPTIM) *
+ 1024.0) + 0.5));
+ /* 32000 = system clock, 24 = clocks per pixel */
+ rate = (exposure_time + 65536) / 65536;
+ if (rate > 4)
+ {
+ rate = 8;
+ tgtime = 3;
+ }
+ else if (rate > 2)
+ {
+ rate = 4;
+ tgtime = 2;
+ }
+ else if (rate > 1)
+ {
+ rate = 2;
+ tgtime = 1;
+ }
+ else
+ {
+ rate = 1;
+ tgtime = 0;
+ }
+
+ local_reg.find_reg(0x6c).value |= tgtime << 6;
+ exposure_time /= rate;
+
+ if (exposure_time > 65535)
+ exposure_time = 65535;
+
+ local_reg.find_reg(0x38).value = exposure_time / 256;
+ local_reg.find_reg(0x39).value = exposure_time & 255;
+
+ dev->interface->write_registers(local_reg);
+}
+
+
+/**
+ * loads document into scanner
+ * currently only used by XP200
+ * bit2 (0x04) of gpio is paper event (document in/out) on XP200
+ * HOMESNR is set if no document in front of sensor, the sequence of events is
+ * paper event -> document is in the sheet feeder
+ * HOMESNR becomes 0 -> document reach sensor
+ * HOMESNR becomes 1 ->document left sensor
+ * paper event -> document is out
+ */
+void CommandSetGl646::load_document(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+
+ // FIXME: sequential not really needed in this case
+ Genesys_Register_Set regs(Genesys_Register_Set::SEQUENTIAL);
+ unsigned count;
+
+ /* no need to load document is flatbed scanner */
+ if (!dev->model->is_sheetfed) {
+ DBG(DBG_proc, "%s: nothing to load\n", __func__);
+ DBG(DBG_proc, "%s: end\n", __func__);
+ return;
+ }
+
+ auto status = scanner_read_status(*dev);
+
+ // home sensor is set if a document is inserted
+ if (status.is_at_home) {
+ /* if no document, waits for a paper event to start loading */
+ /* with a 60 seconde minutes timeout */
+ count = 0;
+ std::uint8_t val = 0;
+ do {
+ gl646_gpio_read(dev->interface->get_usb_device(), &val);
+
+ DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, val);
+ if ((val & 0x04) != 0x04)
+ {
+ DBG(DBG_warn, "%s: no paper detected\n", __func__);
+ }
+ dev->interface->sleep_ms(200);
+ count++;
+ }
+ while (((val & 0x04) != 0x04) && (count < 300)); /* 1 min time out */
+ if (count == 300)
+ {
+ throw SaneException(SANE_STATUS_NO_DOCS, "timeout waiting for document");
+ }
+ }
+
+ /* set up to fast move before scan then move until document is detected */
+ regs.init_reg(0x01, 0x90);
+
+ /* AGOME, 2 slopes motor moving */
+ regs.init_reg(0x02, 0x79);
+
+ /* motor feeding steps to 0 */
+ regs.init_reg(0x3d, 0);
+ regs.init_reg(0x3e, 0);
+ regs.init_reg(0x3f, 0);
+
+ /* 50 fast moving steps */
+ regs.init_reg(0x6b, 50);
+
+ /* set GPO */
+ regs.init_reg(0x66, 0x30);
+
+ /* stesp NO */
+ regs.init_reg(0x21, 4);
+ regs.init_reg(0x22, 1);
+ regs.init_reg(0x23, 1);
+ regs.init_reg(0x24, 4);
+
+ /* generate slope table 2 */
+ auto slope_table = create_slope_table(MotorSlope::create_from_steps(6000, 2400, 50), 2400,
+ StepType::FULL, 1, 4,
+ get_slope_table_max_size(AsicType::GL646));
+ // document loading:
+ // send regs
+ // start motor
+ // wait e1 status to become e0
+ gl646_send_slope_table(dev, 1, slope_table.table, slope_table.steps_count);
+
+ dev->interface->write_registers(regs);
+
+ scanner_start_action(*dev, true);
+
+ count = 0;
+ do
+ {
+ status = scanner_read_status(*dev);
+ dev->interface->sleep_ms(200);
+ count++;
+ } while (status.is_motor_enabled && (count < 300));
+
+ if (count == 300)
+ {
+ throw SaneException(SANE_STATUS_JAMMED, "can't load document");
+ }
+
+ /* when loading OK, document is here */
+ dev->document = true;
+
+ /* set up to idle */
+ regs.set8(0x02, 0x71);
+ regs.set8(0x3f, 1);
+ regs.set8(0x6b, 8);
+ dev->interface->write_registers(regs);
+}
+
+/**
+ * detects end of document and adjust current scan
+ * to take it into account
+ * used by sheetfed scanners
+ */
+void CommandSetGl646::detect_document_end(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ std::uint8_t gpio;
+ unsigned int bytes_left;
+
+ // test for document presence
+ scanner_read_print_status(*dev);
+
+ gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
+ DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
+
+ /* detect document event. There one event when the document go in,
+ * then another when it leaves */
+ if (dev->document && (gpio & 0x04) && (dev->total_bytes_read > 0)) {
+ DBG(DBG_info, "%s: no more document\n", __func__);
+ dev->document = false;
+
+ /* adjust number of bytes to read:
+ * total_bytes_to_read is the number of byte to send to frontend
+ * total_bytes_read is the number of bytes sent to frontend
+ * read_bytes_left is the number of bytes to read from the scanner
+ */
+ DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read);
+ DBG(DBG_io, "%s: total_bytes_read =%zu\n", __func__, dev->total_bytes_read);
+
+ // amount of data available from scanner is what to scan
+ sanei_genesys_read_valid_words(dev, &bytes_left);
+
+ unsigned lines_in_buffer = bytes_left / dev->session.output_line_bytes_raw;
+
+ // we add the number of lines needed to read the last part of the document in
+ unsigned lines_offset = static_cast<unsigned>(
+ (dev->model->y_offset * dev->session.params.yres) / MM_PER_INCH);
+
+ unsigned remaining_lines = lines_in_buffer + lines_offset;
+
+ bytes_left = remaining_lines * dev->session.output_line_bytes_raw;
+
+ if (bytes_left < dev->get_pipeline_source().remaining_bytes()) {
+ dev->get_pipeline_source().set_remaining_bytes(bytes_left);
+ dev->total_bytes_to_read = dev->total_bytes_read + bytes_left;
+ }
+ DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read);
+ DBG(DBG_io, "%s: total_bytes_read =%zu\n", __func__, dev->total_bytes_read);
+ }
+}
+
+/**
+ * eject document from the feeder
+ * currently only used by XP200
+ * TODO we currently rely on AGOHOME not being set for sheetfed scanners,
+ * maybe check this flag in eject to let the document being eject automaticaly
+ */
+void CommandSetGl646::eject_document(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+
+ // FIXME: SEQUENTIAL not really needed in this case
+ Genesys_Register_Set regs((Genesys_Register_Set::SEQUENTIAL));
+ unsigned count;
+ std::uint8_t gpio;
+
+ /* at the end there will be noe more document */
+ dev->document = false;
+
+ // first check for document event
+ gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
+
+ DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
+
+ // test status : paper event + HOMESNR -> no more doc ?
+ auto status = scanner_read_status(*dev);
+
+ // home sensor is set when document is inserted
+ if (status.is_at_home) {
+ dev->document = false;
+ DBG(DBG_info, "%s: no more document to eject\n", __func__);
+ DBG(DBG_proc, "%s: end\n", __func__);
+ return;
+ }
+
+ // there is a document inserted, eject it
+ dev->interface->write_register(0x01, 0xb0);
+
+ /* wait for motor to stop */
+ do {
+ dev->interface->sleep_ms(200);
+ status = scanner_read_status(*dev);
+ }
+ while (status.is_motor_enabled);
+
+ /* set up to fast move before scan then move until document is detected */
+ regs.init_reg(0x01, 0xb0);
+
+ /* AGOME, 2 slopes motor moving , eject 'backward' */
+ regs.init_reg(0x02, 0x5d);
+
+ /* motor feeding steps to 119880 */
+ regs.init_reg(0x3d, 1);
+ regs.init_reg(0x3e, 0xd4);
+ regs.init_reg(0x3f, 0x48);
+
+ /* 60 fast moving steps */
+ regs.init_reg(0x6b, 60);
+
+ /* set GPO */
+ regs.init_reg(0x66, 0x30);
+
+ /* stesp NO */
+ regs.init_reg(0x21, 4);
+ regs.init_reg(0x22, 1);
+ regs.init_reg(0x23, 1);
+ regs.init_reg(0x24, 4);
+
+ /* generate slope table 2 */
+ auto slope_table = create_slope_table(MotorSlope::create_from_steps(10000, 1600, 60), 1600,
+ StepType::FULL, 1, 4,
+ get_slope_table_max_size(AsicType::GL646));
+ // document eject:
+ // send regs
+ // start motor
+ // wait c1 status to become c8 : HOMESNR and ~MOTFLAG
+ gl646_send_slope_table(dev, 1, slope_table.table, slope_table.steps_count);
+
+ dev->interface->write_registers(regs);
+
+ scanner_start_action(*dev, true);
+
+ /* loop until paper sensor tells paper is out, and till motor is running */
+ /* use a 30 timeout */
+ count = 0;
+ do {
+ status = scanner_read_status(*dev);
+
+ dev->interface->sleep_ms(200);
+ count++;
+ } while (!status.is_at_home && (count < 150));
+
+ // read GPIO on exit
+ gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
+
+ DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
+}
+
+// Send the low-level scan command
+void CommandSetGl646::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, bool start_motor) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+ // FIXME: SEQUENTIAL not really needed in this case
+ Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
+
+ local_reg.init_reg(0x03, reg->get8(0x03));
+ local_reg.init_reg(0x01, reg->get8(0x01) | REG_0x01_SCAN);
+
+ if (start_motor) {
+ local_reg.init_reg(0x0f, 0x01);
+ } else {
+ local_reg.init_reg(0x0f, 0x00); // do not start motor yet
+ }
+
+ dev->interface->write_registers(local_reg);
+
+ dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
+}
+
+
+// Send the stop scan command
+static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop,
+ bool eject)
+{
+ DBG_HELPER_ARGS(dbg, "check_stop = %d, eject = %d", check_stop, eject);
+
+ scanner_stop_action_no_move(*dev, *reg);
+
+ unsigned wait_limit_seconds = 30;
+
+ /* for sheetfed scanners, we may have to eject document */
+ if (dev->model->is_sheetfed) {
+ if (eject && dev->document) {
+ dev->cmd_set->eject_document(dev);
+ }
+ wait_limit_seconds = 3;
+ }
+
+ if (is_testing_mode()) {
+ return;
+ }
+
+ dev->interface->sleep_ms(100);
+
+ if (check_stop) {
+ for (unsigned i = 0; i < wait_limit_seconds * 10; i++) {
+ if (scanner_is_motor_stopped(*dev)) {
+ return;
+ }
+
+ dev->interface->sleep_ms(100);
+ }
+ throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor");
+ }
+}
+
+// Send the stop scan command
+void CommandSetGl646::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
+ bool check_stop) const
+{
+ end_scan_impl(dev, reg, check_stop, false);
+}
+
+/**
+ * parks head
+ * @param dev scanner's device
+ * @param wait_until_home true if the function waits until head parked
+ */
+void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) const
+{
+ DBG_HELPER_ARGS(dbg, "wait_until_home = %d\n", wait_until_home);
+ int i;
+ int loop = 0;
+
+ auto status = scanner_read_status(*dev);
+
+ if (status.is_at_home) {
+ DBG(DBG_info, "%s: end since already at home\n", __func__);
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+
+ /* stop motor if needed */
+ if (status.is_motor_enabled) {
+ gl646_stop_motor(dev);
+ dev->interface->sleep_ms(200);
+ }
+
+ /* when scanhead is moving then wait until scanhead stops or timeout */
+ DBG(DBG_info, "%s: ensuring that motor is off\n", __func__);
+ for (i = 400; i > 0; i--) {
+ // do not wait longer than 40 seconds, count down to get i = 0 when busy
+
+ status = scanner_read_status(*dev);
+
+ if (!status.is_motor_enabled && status.is_at_home) {
+ DBG(DBG_info, "%s: already at home and not moving\n", __func__);
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+ if (!status.is_motor_enabled) {
+ break;
+ }
+
+ dev->interface->sleep_ms(100);
+ }
+
+ if (!i) /* the loop counted down to 0, scanner still is busy */
+ {
+ dev->set_head_pos_unknown();
+ throw SaneException(SANE_STATUS_DEVICE_BUSY, "motor is still on: device busy");
+ }
+
+ // setup for a backward scan of 65535 steps, with no actual data reading
+ auto resolution = sanei_genesys_get_lowest_dpi(dev);
+
+ const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 3,
+ dev->model->default_method);
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 0;
+ session.params.starty = 65535;
+ session.params.pixels = 600;
+ session.params.requested_pixels = 600;
+ session.params.lines = 1;
+ session.params.depth = 8;
+ session.params.channels = 3;
+ session.params.scan_method = dev->model->default_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::USE_XCORRECTION |
+ ScanFlag::REVERSE;
+ if (dev->model->default_method == ScanMethod::TRANSPARENCY) {
+ session.params.flags |= ScanFlag::USE_XPA;
+ }
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &dev->reg, session);
+
+ /* backward , no actual data scanned TODO more setup flags to avoid this register manipulations ? */
+ regs_set_optical_off(dev->model->asic_type, dev->reg);
+
+ // sets frontend
+ gl646_set_fe(dev, sensor, AFE_SET, resolution);
+
+ /* write scan registers */
+ try {
+ dev->interface->write_registers(dev->reg);
+ } catch (...) {
+ DBG(DBG_error, "%s: failed to bulk write registers\n", __func__);
+ }
+
+ /* registers are restored to an iddl state, give up if no head to park */
+ if (dev->model->is_sheetfed) {
+ DBG(DBG_proc, "%s: end \n", __func__);
+ return;
+ }
+
+ // starts scan
+ {
+ // this is effectively the same as dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true);
+ // except that we don't modify the head position calculations
+
+ // FIXME: SEQUENTIAL not really needed in this case
+ Genesys_Register_Set scan_local_reg(Genesys_Register_Set::SEQUENTIAL);
+
+ scan_local_reg.init_reg(0x03, dev->reg.get8(0x03));
+ scan_local_reg.init_reg(0x01, dev->reg.get8(0x01) | REG_0x01_SCAN);
+ scan_local_reg.init_reg(0x0f, 0x01);
+
+ dev->interface->write_registers(scan_local_reg);
+ }
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("move_back_home");
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+
+ /* loop until head parked */
+ if (wait_until_home)
+ {
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ auto status = scanner_read_status(*dev);
+
+ if (status.is_at_home) {
+ DBG(DBG_info, "%s: reached home position\n", __func__);
+ DBG(DBG_proc, "%s: end\n", __func__);
+ dev->interface->sleep_ms(500);
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+ dev->interface->sleep_ms(100);
+ ++loop;
+ }
+
+ // when we come here then the scanner needed too much time for this, so we better
+ // stop the motor
+ catch_all_exceptions(__func__, [&](){ gl646_stop_motor (dev); });
+ catch_all_exceptions(__func__, [&](){ end_scan_impl(dev, &dev->reg, true, false); });
+ dev->set_head_pos_unknown();
+ throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
+ }
+
+
+ DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
+}
+
+/**
+ * Automatically set top-left edge of the scan area by scanning an
+ * area at 300 dpi from very top of scanner
+ * @param dev device stucture describing the scanner
+ */
+void CommandSetGl646::search_start_position(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ Genesys_Settings settings;
+ unsigned int resolution, x, y;
+
+ /* we scan at 300 dpi */
+ resolution = get_closest_resolution(dev->model->sensor_id, 300, 1);
+
+ // FIXME: the current approach of doing search only for one resolution does not work on scanners
+ // whith employ different sensors with potentially different settings.
+ const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 1,
+ dev->model->default_method);
+
+ /* fill settings for a gray level scan */
+ settings.scan_method = dev->model->default_method;
+ settings.scan_mode = ScanColorMode::GRAY;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = 600;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = dev->model->search_lines;
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ // scan the desired area
+ std::vector<uint8_t> data;
+ simple_scan(dev, sensor, settings, true, true, false, data, "search_start_position");
+
+ // handle stagger case : reorder gray data and thus loose some lines
+ auto staggered_lines = dev->session.num_staggered_lines;
+ if (staggered_lines > 0) {
+ DBG(DBG_proc, "%s: 'un-staggering'\n", __func__);
+ for (y = 0; y < settings.lines - staggered_lines; y++) {
+ /* one point out of 2 is 'unaligned' */
+ for (x = 0; x < settings.pixels; x += 2)
+ {
+ data[y * settings.pixels + x] = data[(y + staggered_lines) * settings.pixels + x];
+ }
+ }
+ /* correct line number */
+ settings.lines -= staggered_lines;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_pnm_file("gl646_search_position.pnm", data.data(), settings.depth, 1,
+ settings.pixels, settings.lines);
+ }
+
+ // now search reference points on the data
+ for (auto& sensor_update :
+ sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method))
+ {
+ sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0,
+ resolution, settings.pixels, settings.lines);
+ }
+}
+
+/**
+ * internally overriden during effective calibration
+ * sets up register for coarse gain calibration
+ */
+void CommandSetGl646::init_regs_for_coarse_calibration(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ (void) dev;
+ (void) sensor;
+ (void) regs;
+}
+
+
+/**
+ * init registers for shading calibration
+ * we assume that scanner's head is on an area suiting shading calibration.
+ * We scan a full scan width area by the shading line number for the device
+ * at either at full sensor's resolution or half depending upon ccd_size_divisor
+ * @param dev scanner's device
+ */
+void CommandSetGl646::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ (void) regs;
+ Genesys_Settings settings;
+ int cksel = 1;
+
+ /* fill settings for scan : always a color scan */
+ int channels = 3;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,
+ dev->settings.scan_method);
+
+ unsigned ccd_size_divisor = calib_sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres);
+
+ settings.scan_method = dev->settings.scan_method;
+ settings.scan_mode = dev->settings.scan_mode;
+ if (!dev->model->is_cis) {
+ // FIXME: always a color scan, but why don't we set scan_mode to COLOR_SINGLE_PASS always?
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ }
+ settings.xres = sensor.optical_res / ccd_size_divisor;
+ cksel = get_cksel(dev->model->sensor_id, dev->settings.xres, channels);
+ settings.xres = settings.xres / cksel;
+ settings.yres = settings.xres;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = (calib_sensor.sensor_pixels * settings.xres) / calib_sensor.optical_res;
+ settings.requested_pixels = settings.pixels;
+ dev->calib_lines = dev->model->shading_lines;
+ settings.lines = dev->calib_lines * (3 - ccd_size_divisor);
+ settings.depth = 16;
+ settings.color_filter = dev->settings.color_filter;
+
+ settings.disable_interpolation = dev->settings.disable_interpolation;
+ settings.threshold = dev->settings.threshold;
+
+ // we don't want top offset, but we need right margin to be the same than the one for the final
+ // scan
+ setup_for_scan(dev, calib_sensor, &dev->reg, settings, true, false, false, false);
+
+ /* used when sending shading calibration data */
+ dev->calib_pixels = settings.pixels;
+ dev->calib_channels = dev->session.params.channels;
+ if (!dev->model->is_cis) {
+ dev->calib_channels = 3;
+ }
+
+ /* no shading */
+ dev->reg.find_reg(0x01).value &= ~REG_0x01_DVDSET;
+ dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS; /* ease backtracking */
+ dev->reg.find_reg(0x02).value &= ~(REG_0x02_FASTFED | REG_0x02_AGOHOME);
+ dev->reg.find_reg(0x05).value &= ~REG_0x05_GMMENB;
+ sanei_genesys_set_motor_power(dev->reg, false);
+
+ /* TODO another flag to setup regs ? */
+ /* enforce needed LINCNT, getting rid of extra lines for color reordering */
+ if (!dev->model->is_cis) {
+ dev->reg.set24(REG_LINCNT, dev->calib_lines);
+ } else {
+ dev->reg.set24(REG_LINCNT, dev->calib_lines * 3);
+ }
+
+ /* copy reg to calib_reg */
+ dev->calib_reg = dev->reg;
+
+ DBG(DBG_info, "%s:\n\tdev->settings.xres=%d\n\tdev->settings.yres=%d\n", __func__,
+ dev->settings.xres, dev->settings.yres);
+}
+
+bool CommandSetGl646::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
+{
+ return dev->is_head_pos_known(ScanHeadId::PRIMARY) &&
+ dev->head_pos(ScanHeadId::PRIMARY) &&
+ dev->settings.scan_method == ScanMethod::FLATBED;
+}
+
+/**
+ * set up registers for the actual scan. The scan's parameters are given
+ * through the device settings. It allocates the scan buffers.
+ */
+void CommandSetGl646::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+
+ debug_dump(DBG_info, dev->settings);
+
+ ScanSession session = calculate_scan_session(dev, sensor, dev->settings);
+
+ init_regs_for_scan_session(dev, sensor, &dev->reg, session);
+
+ /* gamma is only enabled at final scan time */
+ if (dev->settings.depth < 16) {
+ dev->reg.find_reg(0x05).value |= REG_0x05_GMMENB;
+ }
+}
+
+/**
+ * set up registers for the actual scan. The scan's parameters are given
+ * through the device settings. It allocates the scan buffers.
+ * @param dev scanner's device
+ * @param regs registers to set up
+ * @param settings settings of scan
+ * @param split true if move to scan area is split from scan, false is
+ * scan first moves to area
+ * @param xcorrection take x geometry correction into account (fixed and detected offsets)
+ * @param ycorrection take y geometry correction into account
+ */
+static void setup_for_scan(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set*regs,
+ Genesys_Settings settings,
+ bool split,
+ bool xcorrection,
+ bool ycorrection,
+ bool reverse)
+{
+ DBG_HELPER(dbg);
+
+ debug_dump(DBG_info, dev->settings);
+
+ // compute distance to move
+ float move = 0;
+ // XXX STEF XXX MD5345 -> optical_ydpi, other base_ydpi => half/full step ? */
+ if (!split) {
+ if (!dev->model->is_sheetfed) {
+ if (ycorrection) {
+ move = static_cast<float>(dev->model->y_offset);
+ }
+
+ // add tl_y to base movement
+ }
+ move += static_cast<float>(settings.tl_y);
+
+ if (move < 0) {
+ DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move);
+ move = 0;
+ }
+ }
+ move = static_cast<float>((move * dev->motor.optical_ydpi) / MM_PER_INCH);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ float start = static_cast<float>(settings.tl_x);
+ if (xcorrection) {
+ if (settings.scan_method == ScanMethod::FLATBED) {
+ start += static_cast<float>(dev->model->x_offset);
+ } else {
+ start += static_cast<float>(dev->model->x_offset_ta);
+ }
+ }
+ start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = settings.xres;
+ session.params.yres = settings.yres;
+ session.params.startx = static_cast<unsigned>(start);
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = settings.pixels;
+ session.params.requested_pixels = settings.requested_pixels;
+ session.params.lines = settings.lines;
+ session.params.depth = settings.depth;
+ session.params.channels = settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = settings.scan_mode;
+ session.params.color_filter = settings.color_filter;
+ session.params.flags = ScanFlag::NONE;
+ if (settings.scan_method == ScanMethod::TRANSPARENCY) {
+ session.params.flags |= ScanFlag::USE_XPA;
+ }
+ if (xcorrection) {
+ session.params.flags |= ScanFlag::USE_XCORRECTION;
+ }
+ if (reverse) {
+ session.params.flags |= ScanFlag::REVERSE;
+ }
+ compute_session(dev, session, sensor);
+
+ dev->cmd_set->init_regs_for_scan_session(dev, sensor, regs, session);
+}
+
+/**
+ * this function send gamma table to ASIC
+ */
+void CommandSetGl646::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ int size;
+ int address;
+ int bits;
+
+ /* gamma table size */
+ if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA)
+ {
+ size = 16384;
+ bits = 14;
+ }
+ else
+ {
+ size = 4096;
+ bits = 12;
+ }
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ std::vector<uint8_t> gamma(size * 2 * 3);
+
+ sanei_genesys_generate_gamma_buffer(dev, sensor, bits, size-1, size, gamma.data());
+
+ /* table address */
+ switch (dev->reg.find_reg(0x05).value >> 6)
+ {
+ case 0: /* 600 dpi */
+ address = 0x09000;
+ break;
+ case 1: /* 1200 dpi */
+ address = 0x11000;
+ break;
+ case 2: /* 2400 dpi */
+ address = 0x20000;
+ break;
+ default:
+ throw SaneException("invalid dpi");
+ }
+
+ dev->interface->write_buffer(0x3c, address, gamma.data(), size * 2 * 3);
+}
+
+/** @brief this function does the led calibration.
+ * this function does the led calibration by scanning one line of the calibration
+ * area below scanner's top on white strip. The scope of this function is
+ * currently limited to the XP200
+ */
+SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ (void) regs;
+ int total_size;
+ unsigned int i, j;
+ int val;
+ int avg[3], avga, avge;
+ int turn;
+ uint16_t expr, expg, expb;
+ Genesys_Settings settings;
+ SANE_Int resolution;
+
+ unsigned channels = dev->settings.get_channels();
+
+ /* get led calibration resolution */
+ if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
+ {
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ }
+ else
+ {
+ settings.scan_mode = ScanColorMode::GRAY;
+ }
+ resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels);
+
+ /* offset calibration is always done in color mode */
+ settings.scan_method = dev->model->default_method;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = (sensor.sensor_pixels * resolution) / sensor.optical_res;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = 1;
+ settings.depth = 16;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ /* colors * bytes_per_color * scan lines */
+ total_size = settings.pixels * channels * 2 * 1;
+
+ std::vector<uint8_t> line(total_size);
+
+/*
+ we try to get equal bright leds here:
+
+ loop:
+ average per color
+ adjust exposure times
+ */
+ expr = sensor.exposure.red;
+ expg = sensor.exposure.green;
+ expb = sensor.exposure.blue;
+
+ turn = 0;
+
+ auto calib_sensor = sensor;
+
+ bool acceptable = false;
+ do {
+ calib_sensor.exposure.red = expr;
+ calib_sensor.exposure.green = expg;
+ calib_sensor.exposure.blue = expb;
+
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+
+ simple_scan(dev, calib_sensor, settings, false, true, false, line, "led_calibration");
+
+ if (is_testing_mode()) {
+ return calib_sensor.exposure;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl646_led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, line.data(), 16, channels, settings.pixels, 1);
+ }
+
+ acceptable = true;
+
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < settings.pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * settings.pixels + 1] * 256 +
+ line[i * 2 + j * 2 * settings.pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= settings.pixels;
+ }
+
+ DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
+
+ acceptable = true;
+
+ if (!acceptable)
+ {
+ avga = (avg[0] + avg[1] + avg[2]) / 3;
+ expr = (expr * avga) / avg[0];
+ expg = (expg * avga) / avg[1];
+ expb = (expb * avga) / avg[2];
+
+ /* keep exposure time in a working window */
+ avge = (expr + expg + expb) / 3;
+ if (avge > 0x2000)
+ {
+ expr = (expr * 0x2000) / avge;
+ expg = (expg * 0x2000) / avge;
+ expb = (expb * 0x2000) / avge;
+ }
+ if (avge < 0x400)
+ {
+ expr = (expr * 0x400) / avge;
+ expg = (expg * 0x400) / avge;
+ expb = (expb * 0x400) / avge;
+ }
+ }
+
+ turn++;
+
+ }
+ while (!acceptable && turn < 100);
+
+ DBG(DBG_info,"%s: acceptable exposure: 0x%04x,0x%04x,0x%04x\n", __func__, expr, expg, expb);
+ // BUG: we don't store the result of the last iteration to the sensor
+ return calib_sensor.exposure;
+}
+
+/**
+ * average dark pixels of a scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG(DBG_info, "%s: average = %d\n", __func__, average);
+ return average;
+}
+
+
+/** @brief calibration for AD frontend devices
+ * we do simple scan until all black_pixels are higher than 0,
+ * raising offset at each turn.
+ */
+static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+
+ unsigned int channels;
+ int pass = 0;
+ SANE_Int resolution;
+ Genesys_Settings settings;
+ unsigned int x, y, adr, min;
+ unsigned int bottom, black_pixels;
+
+ channels = 3;
+ resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels);
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
+ black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res;
+ DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
+
+ settings.scan_method = dev->model->default_method;
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ /* scan first line of data with no gain */
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+
+ std::vector<uint8_t> line;
+
+ /* scan with no move */
+ bottom = 1;
+ do
+ {
+ pass++;
+ dev->frontend.set_offset(0, bottom);
+ dev->frontend.set_offset(1, bottom);
+ dev->frontend.set_offset(2, bottom);
+ simple_scan(dev, calib_sensor, settings, false, true, false, line,
+ "ad_fe_offset_calibration");
+
+ if (is_testing_mode()) {
+ return;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char title[30];
+ std::snprintf(title, 30, "gl646_offset%03d.pnm", static_cast<int>(bottom));
+ sanei_genesys_write_pnm_file (title, line.data(), 8, channels,
+ settings.pixels, settings.lines);
+ }
+
+ min = 0;
+ for (y = 0; y < settings.lines; y++)
+ {
+ for (x = 0; x < black_pixels; x++)
+ {
+ adr = (x + y * settings.pixels) * channels;
+ if (line[adr] > min)
+ min = line[adr];
+ if (line[adr + 1] > min)
+ min = line[adr + 1];
+ if (line[adr + 2] > min)
+ min = line[adr + 2];
+ }
+ }
+
+ DBG(DBG_io2, "%s: pass=%d, min=%d\n", __func__, pass, min);
+ bottom++;
+ }
+ while (pass < 128 && min == 0);
+ if (pass == 128)
+ {
+ throw SaneException(SANE_STATUS_INVAL, "failed to find correct offset");
+ }
+
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+}
+
+/**
+ * This function does the offset calibration by scanning one line of the calibration
+ * area below scanner's top. There is a black margin and the remaining is white.
+ * genesys_search_start() must have been called so that the offsets and margins
+ * are already known.
+ * @param dev scanner's device
+*/
+void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ (void) regs;
+
+ unsigned int channels;
+ int pass = 0, avg;
+ Genesys_Settings settings;
+ int topavg, bottomavg;
+ int top, bottom, black_pixels;
+
+ if (dev->model->adc_id == AdcId::AD_XP200) {
+ ad_fe_offset_calibration(dev, sensor);
+ return;
+ }
+
+ DBG(DBG_proc, "%s: start\n", __func__); // TODO
+
+ /* setup for a RGB scan, one full sensor's width line */
+ /* resolution is the one from the final scan */
+ channels = 3;
+ int resolution = get_closest_resolution(dev->model->sensor_id, dev->settings.xres, channels);
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
+ black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res;
+
+ DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
+
+ settings.scan_method = dev->model->default_method;
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ /* scan first line of data with no gain, but with offset from
+ * last calibration */
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+
+ /* scan with no move */
+ bottom = 90;
+ dev->frontend.set_offset(0, bottom);
+ dev->frontend.set_offset(1, bottom);
+ dev->frontend.set_offset(2, bottom);
+
+ std::vector<uint8_t> first_line, second_line;
+
+ simple_scan(dev, calib_sensor, settings, false, true, false, first_line,
+ "offset_first_line");
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char title[30];
+ std::snprintf(title, 30, "gl646_offset%03d.pnm", bottom);
+ sanei_genesys_write_pnm_file(title, first_line.data(), 8, channels,
+ settings.pixels, settings.lines);
+ }
+ bottomavg = dark_average(first_line.data(), settings.pixels, settings.lines, channels,
+ black_pixels);
+ DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
+
+ /* now top value */
+ top = 231;
+ dev->frontend.set_offset(0, top);
+ dev->frontend.set_offset(1, top);
+ dev->frontend.set_offset(2, top);
+ simple_scan(dev, calib_sensor, settings, false, true, false, second_line,
+ "offset_second_line");
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char title[30];
+ std::snprintf(title, 30, "gl646_offset%03d.pnm", top);
+ sanei_genesys_write_pnm_file (title, second_line.data(), 8, channels,
+ settings.pixels, settings.lines);
+ }
+ topavg = dark_average(second_line.data(), settings.pixels, settings.lines, channels,
+ black_pixels);
+ DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
+
+ if (is_testing_mode()) {
+ return;
+ }
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.set_offset(0, (top + bottom) / 2);
+ dev->frontend.set_offset(1, (top + bottom) / 2);
+ dev->frontend.set_offset(2, (top + bottom) / 2);
+
+ // scan with no move
+ simple_scan(dev, calib_sensor, settings, false, true, false, second_line,
+ "offset_calibration_i");
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char title[30];
+ std::snprintf(title, 30, "gl646_offset%03d.pnm", dev->frontend.get_offset(1));
+ sanei_genesys_write_pnm_file (title, second_line.data(), 8, channels,
+ settings.pixels, settings.lines);
+ }
+
+ avg =
+ dark_average (second_line.data(), settings.pixels, settings.lines, channels,
+ black_pixels);
+ DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.get_offset(1);
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.get_offset(1);
+ }
+ }
+
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+}
+
+/** @brief gain calibration for Analog Device frontends
+ * Alternative coarse gain calibration
+ */
+static void ad_fe_coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi)
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+ (void) regs;
+
+ unsigned int i, channels, val;
+ unsigned int size, count, resolution, pass;
+ float average;
+ Genesys_Settings settings;
+ char title[32];
+
+ /* setup for a RGB scan, one full sensor's width line */
+ /* resolution is the one from the final scan */
+ channels = 3;
+ resolution = get_closest_resolution(dev->model->sensor_id, dpi, channels);
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
+
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+
+ settings.scan_method = dev->model->default_method;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ size = channels * settings.pixels * settings.lines;
+
+ /* start gain value */
+ dev->frontend.set_gain(0, 1);
+ dev->frontend.set_gain(1, 1);
+ dev->frontend.set_gain(2, 1);
+
+ average = 0;
+ pass = 0;
+
+ std::vector<uint8_t> line;
+
+ // loop until each channel raises to acceptable level
+ while ((average < calib_sensor.gain_white_ref) && (pass < 30)) {
+ // scan with no move
+ simple_scan(dev, calib_sensor, settings, false, true, false, line,
+ "ad_fe_coarse_gain_calibration");
+
+ /* log scanning data */
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl646_alternative_gain%02d.pnm", pass);
+ sanei_genesys_write_pnm_file(title, line.data(), 8, channels, settings.pixels,
+ settings.lines);
+ }
+ pass++;
+
+ /* computes white average */
+ average = 0;
+ count = 0;
+ for (i = 0; i < size; i++)
+ {
+ val = line[i];
+ average += val;
+ count++;
+ }
+ average = average / count;
+
+ uint8_t gain0 = dev->frontend.get_gain(0);
+ // adjusts gain for the channel
+ if (average < calib_sensor.gain_white_ref) {
+ gain0 += 1;
+ }
+
+ dev->frontend.set_gain(0, gain0);
+ dev->frontend.set_gain(1, gain0);
+ dev->frontend.set_gain(2, gain0);
+
+ DBG(DBG_proc, "%s: average = %.2f, gain = %d\n", __func__, average, gain0);
+ }
+
+ DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_gain(0),
+ dev->frontend.get_gain(1),
+ dev->frontend.get_gain(2));
+}
+
+/**
+ * Alternative coarse gain calibration
+ * this on uses the settings from offset_calibration. First scan moves so
+ * we can go to calibration area for XPA.
+ * @param dev device for scan
+ * @param dpi resolutnio to calibrate at
+ */
+void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const
+{
+ DBG_HELPER(dbg);
+ (void) dpi;
+
+ unsigned int i, j, k, channels, val, maximum, idx;
+ unsigned int count, resolution, pass;
+ float average[3];
+ Genesys_Settings settings;
+ char title[32];
+
+ if (dev->model->sensor_id == SensorId::CIS_XP200) {
+ return ad_fe_coarse_gain_calibration(dev, sensor, regs, sensor.optical_res);
+ }
+
+ /* setup for a RGB scan, one full sensor's width line */
+ /* resolution is the one from the final scan */
+ channels = 3;
+
+ /* we are searching a sensor resolution */
+ resolution = get_closest_resolution(dev->model->sensor_id, dev->settings.xres, channels);
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
+ ScanMethod::FLATBED);
+
+ settings.scan_method = dev->settings.scan_method;
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_y = 0;
+ if (settings.scan_method == ScanMethod::FLATBED)
+ {
+ settings.tl_x = 0;
+ settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res;
+ }
+ else
+ {
+ settings.tl_x = dev->model->x_offset_ta;
+ settings.pixels = static_cast<unsigned>((dev->model->x_size_ta * resolution) / MM_PER_INCH);
+ }
+ settings.requested_pixels = settings.pixels;
+ settings.lines = CALIBRATION_LINES;
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ /* start gain value */
+ dev->frontend.set_gain(0, 1);
+ dev->frontend.set_gain(1, 1);
+ dev->frontend.set_gain(2, 1);
+
+ if (channels > 1)
+ {
+ average[0] = 0;
+ average[1] = 0;
+ average[2] = 0;
+ idx = 0;
+ }
+ else
+ {
+ average[0] = 255;
+ average[1] = 255;
+ average[2] = 255;
+ switch (dev->settings.color_filter) {
+ case ColorFilter::RED: idx = 0; break;
+ case ColorFilter::GREEN: idx = 1; break;
+ case ColorFilter::BLUE: idx = 2; break;
+ default: idx = 0; break; // should not happen
+ }
+ average[idx] = 0;
+ }
+ pass = 0;
+
+ std::vector<uint8_t> line;
+
+ /* loop until each channel raises to acceptable level */
+ while (((average[0] < calib_sensor.gain_white_ref) ||
+ (average[1] < calib_sensor.gain_white_ref) ||
+ (average[2] < calib_sensor.gain_white_ref)) && (pass < 30))
+ {
+ // scan with no move
+ simple_scan(dev, calib_sensor, settings, false, true, false, line,
+ "coarse_gain_calibration");
+
+ /* log scanning data */
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl646_gain%02d.pnm", pass);
+ sanei_genesys_write_pnm_file(title, line.data(), 8, channels, settings.pixels,
+ settings.lines);
+ }
+ pass++;
+
+ /* average high level for each channel and compute gain
+ to reach the target code
+ we only use the central half of the CCD data */
+ for (k = idx; k < idx + channels; k++)
+ {
+ /* we find the maximum white value, so we can deduce a threshold
+ to average white values */
+ maximum = 0;
+ for (i = 0; i < settings.lines; i++)
+ {
+ for (j = 0; j < settings.pixels; j++)
+ {
+ val = line[i * channels * settings.pixels + j + k];
+ if (val > maximum)
+ maximum = val;
+ }
+ }
+
+ /* threshold */
+ maximum = static_cast<int>(maximum * 0.9);
+
+ /* computes white average */
+ average[k] = 0;
+ count = 0;
+ for (i = 0; i < settings.lines; i++)
+ {
+ for (j = 0; j < settings.pixels; j++)
+ {
+ /* averaging only white points allow us not to care about dark margins */
+ val = line[i * channels * settings.pixels + j + k];
+ if (val > maximum)
+ {
+ average[k] += val;
+ count++;
+ }
+ }
+ }
+ average[k] = average[k] / count;
+
+ /* adjusts gain for the channel */
+ if (average[k] < calib_sensor.gain_white_ref)
+ dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1);
+
+ DBG(DBG_proc, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k],
+ dev->frontend.get_gain(k));
+ }
+ }
+
+ if (channels < 3) {
+ dev->frontend.set_gain(1, dev->frontend.get_gain(0));
+ dev->frontend.set_gain(2, dev->frontend.get_gain(0));
+ }
+
+ DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_gain(0),
+ dev->frontend.get_gain(1),
+ dev->frontend.get_gain(2));
+}
+
+/**
+ * sets up the scanner's register for warming up. We scan 2 lines without moving.
+ *
+ */
+void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* local_reg, int* channels,
+ int* total_size) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+
+ Genesys_Settings settings;
+ int resolution, lines;
+
+ dev->frontend = dev->frontend_initial;
+
+ resolution = get_closest_resolution(dev->model->sensor_id, 300, 1);
+
+ const auto& local_sensor = sanei_genesys_find_sensor(dev, resolution, 1,
+ dev->settings.scan_method);
+
+ /* set up for a half width 2 lines gray scan without moving */
+ settings.scan_method = dev->model->default_method;
+ settings.scan_mode = ScanColorMode::GRAY;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = (local_sensor.sensor_pixels * resolution) / local_sensor.optical_res;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = 2;
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ // setup for scan
+ setup_for_scan(dev, local_sensor, &dev->reg, settings, true, false, false, false);
+
+ /* we are not going to move, so clear these bits */
+ dev->reg.find_reg(0x02).value &= ~(REG_0x02_FASTFED | REG_0x02_AGOHOME);
+
+ /* don't enable any correction for this scan */
+ dev->reg.find_reg(0x01).value &= ~REG_0x01_DVDSET;
+
+ /* copy to local_reg */
+ *local_reg = dev->reg;
+
+ /* turn off motor during this scan */
+ sanei_genesys_set_motor_power(*local_reg, false);
+
+ /* returned value to higher level warmup function */
+ *channels = 1;
+ lines = local_reg->get24(REG_LINCNT) + 1;
+ *total_size = lines * settings.pixels;
+
+ // now registers are ok, write them to scanner
+ gl646_set_fe(dev, local_sensor, AFE_SET, settings.xres);
+ dev->interface->write_registers(*local_reg);
+}
+
+
+/*
+ * this function moves head without scanning, forward, then backward
+ * so that the head goes to park position.
+ * as a by-product, also check for lock
+ */
+static void gl646_repark_head(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ Genesys_Settings settings;
+ unsigned int expected, steps;
+
+ settings.scan_method = dev->model->default_method;
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ settings.xres = get_closest_resolution(dev->model->sensor_id, 75, 1);
+ settings.yres = settings.xres;
+ settings.tl_x = 0;
+ settings.tl_y = 5;
+ settings.pixels = 600;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = 4;
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, 3,
+ dev->model->default_method);
+
+ setup_for_scan(dev, sensor, &dev->reg, settings, false, false, false, false);
+
+ /* TODO seems wrong ... no effective scan */
+ regs_set_optical_off(dev->model->asic_type, dev->reg);
+
+ dev->interface->write_registers(dev->reg);
+
+ // start scan
+ dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true);
+
+ expected = dev->reg.get24(REG_FEEDL);
+ do
+ {
+ dev->interface->sleep_ms(100);
+ sanei_genesys_read_feed_steps (dev, &steps);
+ }
+ while (steps < expected);
+
+ // toggle motor flag, put an huge step number and redo move backward
+ dev->cmd_set->move_back_home(dev, 1);
+}
+
+/* *
+ * initialize ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ * @param dev device description of the scanner to initailize
+ */
+void CommandSetGl646::init(Genesys_Device* dev) const
+{
+ DBG_INIT();
+ DBG_HELPER(dbg);
+
+ uint8_t val = 0;
+ uint32_t addr = 0xdead;
+ size_t len;
+
+ // to detect real power up condition, we write to REG_0x41 with pwrbit set, then read it back.
+ // When scanner is cold (just replugged) PWRBIT will be set in the returned value
+ auto status = scanner_read_status(*dev);
+ if (status.is_replugged) {
+ DBG(DBG_info, "%s: device is cold\n", __func__);
+ } else {
+ DBG(DBG_info, "%s: device is hot\n", __func__);
+ }
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ /* if scanning session hasn't been initialized, set it up */
+ if (!dev->already_initialized)
+ {
+ dev->dark_average_data.clear();
+ dev->white_average_data.clear();
+
+ dev->settings.color_filter = ColorFilter::GREEN;
+
+ /* Set default values for registers */
+ gl646_init_regs (dev);
+
+ // Init shading data
+ sanei_genesys_init_shading_data(dev, sensor, sensor.sensor_pixels);
+
+ /* initial calibration reg values */
+ dev->calib_reg = dev->reg;
+ }
+
+ // execute physical unit init only if cold
+ if (status.is_replugged)
+ {
+ DBG(DBG_info, "%s: device is cold\n", __func__);
+
+ val = 0x04;
+ dev->interface->get_usb_device().control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_INIT, INDEX, 1, &val);
+
+ // ASIC reset
+ dev->interface->write_register(0x0e, 0x00);
+ dev->interface->sleep_ms(100);
+
+ // Write initial registers
+ dev->interface->write_registers(dev->reg);
+
+ // send gamma tables if needed
+ dev->cmd_set->send_gamma_table(dev, sensor);
+
+ // Set powersaving(default = 15 minutes)
+ dev->cmd_set->set_powersaving(dev, 15);
+ }
+
+ // Set analog frontend
+ gl646_set_fe(dev, sensor, AFE_INIT, 0);
+
+ /* GPO enabling for XP200 */
+ if (dev->model->sensor_id == SensorId::CIS_XP200) {
+ dev->interface->write_register(0x68, dev->gpo.regs.get_value(0x68));
+ dev->interface->write_register(0x69, dev->gpo.regs.get_value(0x69));
+
+ // enable GPIO
+ gl646_gpio_output_enable(dev->interface->get_usb_device(), 6);
+
+ // writes 0 to GPIO
+ gl646_gpio_write(dev->interface->get_usb_device(), 0);
+
+ // clear GPIO enable
+ gl646_gpio_output_enable(dev->interface->get_usb_device(), 0);
+
+ dev->interface->write_register(0x66, 0x10);
+ dev->interface->write_register(0x66, 0x00);
+ dev->interface->write_register(0x66, 0x10);
+ }
+
+ /* MD6471/G2410 and XP200 read/write data from an undocumented memory area which
+ * is after the second slope table */
+ if (dev->model->gpio_id != GpioId::HP3670 &&
+ dev->model->gpio_id != GpioId::HP2400)
+ {
+ switch (sensor.optical_res)
+ {
+ case 600:
+ addr = 0x08200;
+ break;
+ case 1200:
+ addr = 0x10200;
+ break;
+ case 2400:
+ addr = 0x1fa00;
+ break;
+ }
+ sanei_genesys_set_buffer_address(dev, addr);
+
+ sanei_usb_set_timeout (2 * 1000);
+ len = 6;
+ // for some reason, read fails here for MD6471, HP2300 and XP200 one time out of
+ // 2 scanimage launches
+ try {
+ dev->interface->bulk_read_data(0x45, dev->control, len);
+ } catch (...) {
+ dev->interface->bulk_read_data(0x45, dev->control, len);
+ }
+ DBG(DBG_info, "%s: control read=0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__,
+ dev->control[0], dev->control[1], dev->control[2], dev->control[3], dev->control[4],
+ dev->control[5]);
+ sanei_usb_set_timeout (30 * 1000);
+ }
+ else
+ /* HP2400 and HP3670 case */
+ {
+ dev->control[0] = 0x00;
+ dev->control[1] = 0x00;
+ dev->control[2] = 0x01;
+ dev->control[3] = 0x00;
+ dev->control[4] = 0x00;
+ dev->control[5] = 0x00;
+ }
+
+ /* ensure head is correctly parked, and check lock */
+ if (!dev->model->is_sheetfed) {
+ if (dev->model->flags & GENESYS_FLAG_REPARK)
+ {
+ // FIXME: if repark fails, we should print an error message that the scanner is locked and
+ // the user should unlock the lock. We should also rethrow with SANE_STATUS_JAMMED
+ gl646_repark_head(dev);
+ }
+ else
+ {
+ move_back_home(dev, true);
+ }
+ }
+
+ /* here session and device are initialized */
+ dev->already_initialized = true;
+}
+
+void CommandSetGl646::move_to_ta(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+
+ simple_move(dev, static_cast<int>(dev->model->y_offset_sensor_to_ta));
+}
+
+
+/**
+ * Does a simple scan: ie no line reordering and avanced data buffering and
+ * shading correction. Memory for data is allocated in this function
+ * and must be freed by caller.
+ * @param dev device of the scanner
+ * @param settings parameters of the scan
+ * @param move true if moving during scan
+ * @param forward true if moving forward during scan
+ * @param shading true to enable shading correction
+ * @param data pointer for the data
+ */
+static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Settings settings, bool move, bool forward,
+ bool shading, std::vector<uint8_t>& data,
+ const char* scan_identifier)
+{
+ DBG_HELPER_ARGS(dbg, "move=%d, forward=%d, shading=%d", move, forward, shading);
+ unsigned int size, lines, x, y, bpp;
+ bool split;
+
+ /* round up to multiple of 3 in case of CIS scanner */
+ if (dev->model->is_cis) {
+ settings.lines = ((settings.lines + 2) / 3) * 3;
+ }
+
+ /* setup for move then scan */
+ split = !(move && settings.tl_y > 0);
+ setup_for_scan(dev, sensor, &dev->reg, settings, split, false, false, !forward);
+
+ /* allocate memory fo scan : LINCNT may have been adjusted for CCD reordering */
+ if (dev->model->is_cis) {
+ lines = dev->reg.get24(REG_LINCNT) / 3;
+ } else {
+ lines = dev->reg.get24(REG_LINCNT) + 1;
+ }
+ size = lines * settings.pixels;
+ if (settings.depth == 16) {
+ bpp = 2;
+ } else {
+ bpp = 1;
+ }
+ size *= bpp * settings.get_channels();
+ data.clear();
+ data.resize(size);
+
+ DBG(DBG_io, "%s: allocated %d bytes of memory for %d lines\n", __func__, size, lines);
+
+ /* put back real line number in settings */
+ settings.lines = lines;
+
+ // initialize frontend
+ gl646_set_fe(dev, sensor, AFE_SET, settings.xres);
+
+ /* no shading correction and not watch dog for simple scan */
+ dev->reg.find_reg(0x01).value &= ~(REG_0x01_DVDSET | REG_0x01_DOGENB);
+ if (shading) {
+ dev->reg.find_reg(0x01).value |= REG_0x01_DVDSET;
+ }
+
+ /* enable gamma table for the scan */
+ dev->reg.find_reg(0x05).value |= REG_0x05_GMMENB;
+
+ /* one table movement for simple scan */
+ dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;
+
+ if (!move) {
+ sanei_genesys_set_motor_power(dev->reg, false);
+
+ /* no automatic go home if no movement */
+ dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME;
+ }
+
+ /* no automatic go home when using XPA */
+ if (settings.scan_method == ScanMethod::TRANSPARENCY) {
+ dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME;
+ }
+
+ // write scan registers
+ dev->interface->write_registers(dev->reg);
+
+ // starts scan
+ dev->cmd_set->begin_scan(dev, sensor, &dev->reg, move);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint(scan_identifier);
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev, true);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ /* in case of CIS scanner, we must reorder data */
+ if (dev->model->is_cis && settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) {
+ /* alloc one line sized working buffer */
+ std::vector<uint8_t> buffer(settings.pixels * 3 * bpp);
+
+ /* reorder one line of data and put it back to buffer */
+ if (bpp == 1)
+ {
+ for (y = 0; y < lines; y++)
+ {
+ /* reorder line */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ buffer[x * 3] = data[y * settings.pixels * 3 + x];
+ buffer[x * 3 + 1] = data[y * settings.pixels * 3 + settings.pixels + x];
+ buffer[x * 3 + 2] = data[y * settings.pixels * 3 + 2 * settings.pixels + x];
+ }
+ /* copy line back */
+ memcpy (data.data() + settings.pixels * 3 * y, buffer.data(),
+ settings.pixels * 3);
+ }
+ }
+ else
+ {
+ for (y = 0; y < lines; y++)
+ {
+ /* reorder line */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ buffer[x * 6] = data[y * settings.pixels * 6 + x * 2];
+ buffer[x * 6 + 1] = data[y * settings.pixels * 6 + x * 2 + 1];
+ buffer[x * 6 + 2] = data[y * settings.pixels * 6 + 2 * settings.pixels + x * 2];
+ buffer[x * 6 + 3] = data[y * settings.pixels * 6 + 2 * settings.pixels + x * 2 + 1];
+ buffer[x * 6 + 4] = data[y * settings.pixels * 6 + 4 * settings.pixels + x * 2];
+ buffer[x * 6 + 5] = data[y * settings.pixels * 6 + 4 * settings.pixels + x * 2 + 1];
+ }
+ /* copy line back */
+ memcpy (data.data() + settings.pixels * 6 * y, buffer.data(),
+ settings.pixels * 6);
+ }
+ }
+ }
+
+ // end scan , waiting the motor to stop if needed (if moving), but without ejecting doc
+ end_scan_impl(dev, &dev->reg, true, false);
+}
+
+/**
+ * Does a simple move of the given distance by doing a scan at lowest resolution
+ * shading correction. Memory for data is allocated in this function
+ * and must be freed by caller.
+ * @param dev device of the scanner
+ * @param distance distance to move in MM
+ */
+static void simple_move(Genesys_Device* dev, SANE_Int distance)
+{
+ DBG_HELPER_ARGS(dbg, "%d mm", distance);
+ Genesys_Settings settings;
+
+ unsigned resolution = sanei_genesys_get_lowest_dpi(dev);
+
+ const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 3, dev->model->default_method);
+
+ /* TODO give a no AGOHOME flag */
+ settings.scan_method = dev->model->default_method;
+ settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ settings.xres = resolution;
+ settings.yres = resolution;
+ settings.tl_y = 0;
+ settings.tl_x = 0;
+ settings.pixels = (sensor.sensor_pixels * settings.xres) / sensor.optical_res;
+ settings.requested_pixels = settings.pixels;
+ settings.lines = static_cast<unsigned>((distance * settings.xres) / MM_PER_INCH);
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ std::vector<uint8_t> data;
+ simple_scan(dev, sensor, settings, true, true, false, data, "simple_move");
+}
+
+/**
+ * update the status of the required sensor in the scanner session
+ * the button fileds are used to make events 'sticky'
+ */
+void CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const
+{
+ DBG_HELPER(dbg);
+ Genesys_Device *dev = session->dev;
+ uint8_t value;
+
+ // do what is needed to get a new set of events, but try to not loose any of them.
+ gl646_gpio_read(dev->interface->get_usb_device(), &value);
+ DBG(DBG_io, "%s: GPIO=0x%02x\n", __func__, value);
+
+ // scan button
+ if (dev->model->buttons & GENESYS_HAS_SCAN_SW) {
+ switch (dev->model->gpio_id) {
+ case GpioId::XP200:
+ session->buttons[BUTTON_SCAN_SW].write((value & 0x02) != 0);
+ break;
+ case GpioId::MD_5345:
+ session->buttons[BUTTON_SCAN_SW].write(value == 0x16);
+ break;
+ case GpioId::HP2300:
+ session->buttons[BUTTON_SCAN_SW].write(value == 0x6c);
+ break;
+ case GpioId::HP3670:
+ case GpioId::HP2400:
+ session->buttons[BUTTON_SCAN_SW].write((value & 0x20) == 0);
+ break;
+ default:
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
+ }
+ }
+
+ // email button
+ if (dev->model->buttons & GENESYS_HAS_EMAIL_SW) {
+ switch (dev->model->gpio_id) {
+ case GpioId::MD_5345:
+ session->buttons[BUTTON_EMAIL_SW].write(value == 0x12);
+ break;
+ case GpioId::HP3670:
+ case GpioId::HP2400:
+ session->buttons[BUTTON_EMAIL_SW].write((value & 0x08) == 0);
+ break;
+ default:
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
+ }
+ }
+
+ // copy button
+ if (dev->model->buttons & GENESYS_HAS_COPY_SW) {
+ switch (dev->model->gpio_id) {
+ case GpioId::MD_5345:
+ session->buttons[BUTTON_COPY_SW].write(value == 0x11);
+ break;
+ case GpioId::HP2300:
+ session->buttons[BUTTON_COPY_SW].write(value == 0x5c);
+ break;
+ case GpioId::HP3670:
+ case GpioId::HP2400:
+ session->buttons[BUTTON_COPY_SW].write((value & 0x10) == 0);
+ break;
+ default:
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
+ }
+ }
+
+ // power button
+ if (dev->model->buttons & GENESYS_HAS_POWER_SW) {
+ switch (dev->model->gpio_id) {
+ case GpioId::MD_5345:
+ session->buttons[BUTTON_POWER_SW].write(value == 0x14);
+ break;
+ default:
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
+ }
+ }
+
+ // ocr button
+ if (dev->model->buttons & GENESYS_HAS_OCR_SW) {
+ switch (dev->model->gpio_id) {
+ case GpioId::MD_5345:
+ session->buttons[BUTTON_OCR_SW].write(value == 0x13);
+ break;
+ default:
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
+ }
+ }
+
+ // document detection
+ if (dev->model->buttons & GENESYS_HAS_PAGE_LOADED_SW) {
+ switch (dev->model->gpio_id) {
+ case GpioId::XP200:
+ session->buttons[BUTTON_PAGE_LOADED_SW].write((value & 0x04) != 0);
+ break;
+ default:
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
+ }
+ }
+
+ /* XPA detection */
+ if (dev->model->flags & GENESYS_FLAG_XPA)
+ {
+ switch (dev->model->gpio_id) {
+ case GpioId::HP3670:
+ case GpioId::HP2400:
+ /* test if XPA is plugged-in */
+ if ((value & 0x40) == 0)
+ {
+ DBG(DBG_io, "%s: enabling XPA\n", __func__);
+ session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ DBG(DBG_io, "%s: disabling XPA\n", __func__);
+ session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ }
+ break;
+ default:
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
+ }
+ }
+}
+
+
+static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution)
+{
+ DBG_HELPER(dbg);
+ uint8_t control[4];
+ uint32_t addr = 0xdead;
+
+ /* 2300 does not write to 'control' */
+ if (dev->model->motor_id == MotorId::HP2300) {
+ return;
+ }
+
+ /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which
+ * is after the second slope table */
+ switch (sensor.optical_res)
+ {
+ case 600:
+ addr = 0x08200;
+ break;
+ case 1200:
+ addr = 0x10200;
+ break;
+ case 2400:
+ addr = 0x1fa00;
+ break;
+ default:
+ throw SaneException("failed to compute control address");
+ }
+
+ /* XP200 sets dpi, what other scanner put is unknown yet */
+ switch (dev->model->motor_id)
+ {
+ case MotorId::XP200:
+ /* we put scan's dpi, not motor one */
+ control[0] = resolution & 0xff;
+ control[1] = (resolution >> 8) & 0xff;
+ control[2] = dev->control[4];
+ control[3] = dev->control[5];
+ break;
+ case MotorId::HP3670:
+ case MotorId::HP2400:
+ case MotorId::MD_5345:
+ default:
+ control[0] = dev->control[2];
+ control[1] = dev->control[3];
+ control[2] = dev->control[4];
+ control[3] = dev->control[5];
+ break;
+ }
+
+ DBG(DBG_info, "%s: control write=0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, control[0], control[1],
+ control[2], control[3]);
+ dev->interface->write_buffer(0x3c, addr, control, 4);
+}
+
+/**
+ * search for a full width black or white strip.
+ * @param dev scanner device
+ * @param forward true if searching forward, false if searching backward
+ * @param black true if searching for a black strip, false for a white strip
+ */
+void CommandSetGl646::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward,
+ bool black) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+
+ Genesys_Settings settings;
+ int res = get_closest_resolution(dev->model->sensor_id, 75, 1);
+ unsigned int pass, count, found, x, y;
+ char title[80];
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, res, 1, ScanMethod::FLATBED);
+
+ /* we set up for a lowest available resolution color grey scan, full width */
+ settings.scan_method = dev->model->default_method;
+ settings.scan_mode = ScanColorMode::GRAY;
+ settings.xres = res;
+ settings.yres = res;
+ settings.tl_x = 0;
+ settings.tl_y = 0;
+ settings.pixels = static_cast<unsigned>((dev->model->x_size * res) / MM_PER_INCH);
+ settings.pixels /= calib_sensor.get_ccd_size_divisor_for_dpi(res);
+ settings.requested_pixels = settings.pixels;
+
+ /* 15 mm at at time */
+ settings.lines = static_cast<unsigned>((15 * settings.yres) / MM_PER_INCH);
+ settings.depth = 8;
+ settings.color_filter = ColorFilter::RED;
+
+ settings.disable_interpolation = 0;
+ settings.threshold = 0;
+
+ /* signals if a strip of the given color has been found */
+ found = 0;
+
+ /* detection pass done */
+ pass = 0;
+
+ std::vector<uint8_t> data;
+
+ /* loop until strip is found or maximum pass number done */
+ while (pass < 20 && !found)
+ {
+ // scan a full width strip
+ simple_scan(dev, calib_sensor, settings, true, forward, false, data, "search_strip");
+
+ if (is_testing_mode()) {
+ return;
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl646_search_strip_%s%02d.pnm", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file (title, data.data(), settings.depth, 1,
+ settings.pixels, settings.lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < settings.lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * settings.pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * settings.pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / settings.pixels < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
+ pass, y);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d\n", __func__, settings.pixels, count);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < settings.lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < settings.pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * settings.pixels + x] > 60)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * settings.pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (settings.pixels * settings.lines) < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d\n", __func__, settings.pixels, count);
+ }
+ }
+ pass++;
+ }
+ if (found)
+ {
+ DBG(DBG_info, "%s: strip found\n", __func__);
+ }
+ else
+ {
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white");
+ }
+}
+
+void CommandSetGl646::wait_for_motor_stop(Genesys_Device* dev) const
+{
+ (void) dev;
+}
+
+void CommandSetGl646::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ std::uint8_t* data, int size) const
+{
+ (void) dev;
+ (void) sensor;
+ (void) data;
+ (void) size;
+ throw SaneException("not implemented");
+}
+
+ScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const
+{
+ // compute distance to move
+ float move = 0;
+ // XXX STEF XXX MD5345 -> optical_ydpi, other base_ydpi => half/full step ? */
+ if (!dev->model->is_sheetfed) {
+ move = static_cast<float>(dev->model->y_offset);
+ // add tl_y to base movement
+ }
+ move += static_cast<float>(settings.tl_y);
+
+ if (move < 0) {
+ DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move);
+ move = 0;
+ }
+
+ move = static_cast<float>((move * dev->motor.optical_ydpi) / MM_PER_INCH);
+ float start = static_cast<float>(settings.tl_x);
+ if (settings.scan_method == ScanMethod::FLATBED) {
+ start += static_cast<float>(dev->model->x_offset);
+ } else {
+ start += static_cast<float>(dev->model->x_offset_ta);
+ }
+ start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = settings.xres;
+ session.params.yres = settings.yres;
+ session.params.startx = static_cast<unsigned>(start);
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = settings.pixels;
+ session.params.requested_pixels = settings.requested_pixels;
+ session.params.lines = settings.lines;
+ session.params.depth = settings.depth;
+ session.params.channels = settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = settings.scan_mode;
+ session.params.color_filter = settings.color_filter;
+ session.params.flags = ScanFlag::USE_XCORRECTION;
+ if (settings.scan_method == ScanMethod::TRANSPARENCY) {
+ session.params.flags |= ScanFlag::USE_XPA;
+ }
+ compute_session(dev, session, sensor);
+
+ return session;
+}
+
+void CommandSetGl646::asic_boot(Genesys_Device *dev, bool cold) const
+{
+ (void) dev;
+ (void) cold;
+ throw SaneException("not implemented");
+}
+
+std::unique_ptr<CommandSet> create_gl646_cmd_set()
+{
+ return std::unique_ptr<CommandSet>(new CommandSetGl646{});
+}
+
+} // namespace gl646
+} // namespace genesys
diff --git a/backend/genesys/gl646.h b/backend/genesys/gl646.h
new file mode 100644
index 0000000..afcfa05
--- /dev/null
+++ b/backend/genesys/gl646.h
@@ -0,0 +1,521 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003-2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004-2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL646_H
+#define BACKEND_GENESYS_GL646_H
+
+#include "genesys.h"
+#include "command_set.h"
+#include "motor.h"
+
+namespace genesys {
+namespace gl646 {
+
+static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set, int dpi);
+
+/**
+ * sets up the scanner for a scan, registers, gamma tables, shading tables
+ * and slope tables, based on the parameter struct.
+ * @param dev device to set up
+ * @param regs registers to set up
+ * @param settings settings of the scan
+ * @param split true if move before scan has to be done
+ * @param xcorrection true if scanner's X geometry must be taken into account to
+ * compute X, ie add left margins
+ * @param ycorrection true if scanner's Y geometry must be taken into account to
+ * compute Y, ie add top margins
+ */
+static void setup_for_scan(Genesys_Device* device,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set*regs,
+ Genesys_Settings settings,
+ bool split,
+ bool xcorrection,
+ bool ycorrection,
+ bool reverse);
+
+/**
+ * Does a simple move of the given distance by doing a scan at lowest resolution
+ * shading correction. Memory for data is allocated in this function
+ * and must be freed by caller.
+ * @param dev device of the scanner
+ * @param distance distance to move in MM
+ */
+static void simple_move(Genesys_Device* dev, SANE_Int distance);
+
+/**
+ * Does a simple scan of the area given by the settings. Scanned data
+ * it put in an allocated area which must be freed by the caller.
+ * and slope tables, based on the parameter struct. There is no shading
+ * correction while gamma correction is active.
+ * @param dev device to set up
+ * @param settings settings of the scan
+ * @param move flag to enable scanhead to move
+ * @param forward flag to tell movement direction
+ * @param shading flag to tell if shading correction should be done
+ * @param data pointer that will point to the scanned data
+ */
+static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Settings settings, bool move, bool forward,
+ bool shading, std::vector<uint8_t>& data, const char* test_identifier);
+
+/**
+ * Send the stop scan command
+ * */
+static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop,
+ bool eject);
+/**
+ * writes control data to an area behind the last motor table.
+ */
+static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution);
+
+
+/**
+ * initialize scanner's registers at SANE init time
+ */
+static void gl646_init_regs (Genesys_Device * dev);
+
+/**
+ * master motor settings table entry
+ */
+typedef struct
+{
+ /* key */
+ MotorId motor_id;
+ unsigned dpi;
+ unsigned channels;
+
+ /* settings */
+ StepType steptype;
+ bool fastmod; // fast scanning
+ bool fastfed; // fast fed slope tables
+ SANE_Int mtrpwm;
+ MotorSlope slope1;
+ MotorSlope slope2;
+ SANE_Int fwdbwd; /* forward/backward steps */
+} Motor_Master;
+
+/**
+ * master motor settings, for a given motor and dpi,
+ * it gives steps and speed informations
+ */
+static Motor_Master motor_master[] = {
+ /* HP3670 motor settings */
+ {MotorId::HP3670, 50, 3, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(2329, 120, 229),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 75, 3, StepType::FULL, false, true, 1,
+ MotorSlope::create_from_steps(3429, 305, 200),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 100, 3, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(2905, 187, 143),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 150, 3, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(3429, 305, 73),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 300, 3, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(1055, 563, 11),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 600, 3, StepType::FULL, false, true, 0,
+ MotorSlope::create_from_steps(10687, 5126, 3),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670,1200, 3, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(15937, 6375, 3),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 50, 1, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(2329, 120, 229),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 75, 1, StepType::FULL, false, true, 1,
+ MotorSlope::create_from_steps(3429, 305, 200),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 100, 1, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(2905, 187, 143),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 150, 1, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(3429, 305, 73),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 300, 1, StepType::HALF, false, true, 1,
+ MotorSlope::create_from_steps(1055, 563, 11),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670, 600, 1, StepType::FULL, false, true, 0,
+ MotorSlope::create_from_steps(10687, 5126, 3),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ {MotorId::HP3670,1200, 1, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(15937, 6375, 3),
+ MotorSlope::create_from_steps(3399, 337, 192), 192},
+
+ /* HP2400/G2410 motor settings base motor dpi = 600 */
+ {MotorId::HP2400, 50, 3, StepType::FULL, false, true, 63,
+ MotorSlope::create_from_steps(8736, 601, 120),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 100, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(8736, 601, 120),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 150, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(15902, 902, 67),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 300, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(16703, 2188, 32),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 600, 3, StepType::FULL, false, true, 63,
+ MotorSlope::create_from_steps(18761, 18761, 3),
+ MotorSlope::create_from_steps(4905, 627, 192), 192},
+
+ {MotorId::HP2400,1200, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(43501, 43501, 3),
+ MotorSlope::create_from_steps(4905, 627, 192), 192},
+
+ {MotorId::HP2400, 50, 1, StepType::FULL, false, true, 63,
+ MotorSlope::create_from_steps(8736, 601, 120),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 100, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(8736, 601, 120),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 150, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(15902, 902, 67),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 300, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(16703, 2188, 32),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400, 600, 1, StepType::FULL, false, true, 63,
+ MotorSlope::create_from_steps(18761, 18761, 3),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ {MotorId::HP2400,1200, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(43501, 43501, 3),
+ MotorSlope::create_from_steps(4905, 337, 192), 192},
+
+ /* XP 200 motor settings */
+ {MotorId::XP200, 75, 3, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(6000, 2136, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 100, 3, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(6000, 2850, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 200, 3, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(6999, 5700, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 250, 3, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(6999, 6999, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 300, 3, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(13500, 13500, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 600, 3, StepType::HALF, true, true, 0,
+ MotorSlope::create_from_steps(31998, 31998, 4),
+ MotorSlope::create_from_steps(12000, 1200, 2), 1},
+
+ {MotorId::XP200, 75, 1, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(6000, 2000, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 100, 1, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(6000, 1300, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 200, 1, StepType::HALF, true, true, 0,
+ MotorSlope::create_from_steps(6000, 3666, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 300, 1, StepType::HALF, true, false, 0,
+ MotorSlope::create_from_steps(6500, 6500, 4),
+ MotorSlope::create_from_steps(12000, 1200, 8), 1},
+
+ {MotorId::XP200, 600, 1, StepType::HALF, true, true, 0,
+ MotorSlope::create_from_steps(24000, 24000, 4),
+ MotorSlope::create_from_steps(12000, 1200, 2), 1},
+
+ /* HP scanjet 2300c */
+ {MotorId::HP2300, 75, 3, StepType::FULL, false, true, 63,
+ MotorSlope::create_from_steps(8139, 560, 120),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 150, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(7903, 543, 67),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(2175, 1087, 3),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 600, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(8700, 4350, 3),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300,1200, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(17400, 8700, 3),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 75, 1, StepType::FULL, false, true, 63,
+ MotorSlope::create_from_steps(8139, 560, 120),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 150, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(7903, 543, 67),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(2175, 1087, 3),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 600, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(8700, 4350, 3),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300,1200, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(17400, 8700, 3),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ /* non half ccd settings for 300 dpi
+ {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(5386, 2175, 44),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+
+ {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63,
+ MotorSlope::create_from_steps(5386, 2175, 44),
+ MotorSlope::create_from_steps(4905, 337, 120), 16},
+ */
+
+ /* MD5345/6471 motor settings */
+ /* vfinal=(exposure/(1200/dpi))/step_type */
+ {MotorId::MD_5345, 50, 3, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 250, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 75, 3, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 343, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 100, 3, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 458, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 150, 3, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 687, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 200, 3, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 916, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 300, 3, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 1375, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 400, 3, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(2000, 1833, 32),
+ MotorSlope::create_from_steps(2000, 300, 255), 32},
+
+ {MotorId::MD_5345, 500, 3, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(2291, 2291, 32),
+ MotorSlope::create_from_steps(2000, 300, 255), 32},
+
+ {MotorId::MD_5345, 600, 3, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(2750, 2750, 32),
+ MotorSlope::create_from_steps(2000, 300, 255), 32},
+
+ {MotorId::MD_5345, 1200, 3, StepType::QUARTER, false, true, 0,
+ MotorSlope::create_from_steps(2750, 2750, 16),
+ MotorSlope::create_from_steps(2000, 300, 255), 146},
+
+ {MotorId::MD_5345, 2400, 3, StepType::QUARTER, false, true, 0,
+ MotorSlope::create_from_steps(5500, 5500, 16),
+ MotorSlope::create_from_steps(2000, 300, 255), 146},
+
+ {MotorId::MD_5345, 50, 1, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 250, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 75, 1, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 343, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 100, 1, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 458, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 150, 1, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 687, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 200, 1, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 916, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 300, 1, StepType::HALF, false, true, 2,
+ MotorSlope::create_from_steps(2500, 1375, 255),
+ MotorSlope::create_from_steps(2000, 300, 255), 64},
+
+ {MotorId::MD_5345, 400, 1, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(2000, 1833, 32),
+ MotorSlope::create_from_steps(2000, 300, 255), 32},
+
+ {MotorId::MD_5345, 500, 1, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(2291, 2291, 32),
+ MotorSlope::create_from_steps(2000, 300, 255), 32},
+
+ {MotorId::MD_5345, 600, 1, StepType::HALF, false, true, 0,
+ MotorSlope::create_from_steps(2750, 2750, 32),
+ MotorSlope::create_from_steps(2000, 300, 255), 32},
+
+ {MotorId::MD_5345, 1200, 1, StepType::QUARTER, false, true, 0,
+ MotorSlope::create_from_steps(2750, 2750, 16),
+ MotorSlope::create_from_steps(2000, 300, 255), 146},
+
+ {MotorId::MD_5345, 2400, 1, StepType::QUARTER, false, true, 0,
+ MotorSlope::create_from_steps(5500, 5500, 16),
+ MotorSlope::create_from_steps(2000, 300, 255), 146}, /* 5500 guessed */
+};
+
+class CommandSetGl646 : public CommandSet
+{
+public:
+ ~CommandSetGl646() override = default;
+
+ bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override;
+
+ void init(Genesys_Device* dev) const override;
+
+ void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const override;
+
+ void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const override;
+
+ void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const override;
+ void set_powersaving(Genesys_Device* dev, int delay) const override;
+ void save_power(Genesys_Device* dev, bool enable) const override;
+
+ void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, bool start_motor) const override;
+
+ void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override;
+
+ void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void search_start_position(Genesys_Device* dev) const override;
+
+ void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const override;
+
+ SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void wait_for_motor_stop(Genesys_Device* dev) const override;
+
+ void move_back_home(Genesys_Device* dev, bool wait_until_home) const override;
+
+ void update_hardware_sensors(struct Genesys_Scanner* s) const override;
+
+ void load_document(Genesys_Device* dev) const override;
+
+ void detect_document_end(Genesys_Device* dev) const override;
+
+ void eject_document(Genesys_Device* dev) const override;
+
+ void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const override;
+
+ void move_to_ta(Genesys_Device* dev) const override;
+
+ void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,
+ int size) const override;
+
+ bool has_send_shading_data() const override
+ {
+ return false;
+ }
+
+ ScanSession calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const override;
+
+ void asic_boot(Genesys_Device* dev, bool cold) const override;
+};
+
+} // namespace gl646
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL646_H
diff --git a/backend/genesys/gl646_registers.h b/backend/genesys/gl646_registers.h
new file mode 100644
index 0000000..2fe8f19
--- /dev/null
+++ b/backend/genesys/gl646_registers.h
@@ -0,0 +1,176 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL646_REGISTERS_H
+#define BACKEND_GENESYS_GL646_REGISTERS_H
+
+#include <cstdint>
+
+namespace genesys {
+namespace gl646 {
+
+using RegAddr = std::uint16_t;
+using RegMask = std::uint8_t;
+using RegShift = unsigned;
+
+static constexpr RegAddr REG_0x01 = 0x01;
+static constexpr RegMask REG_0x01_CISSET = 0x80;
+static constexpr RegMask REG_0x01_DOGENB = 0x40;
+static constexpr RegMask REG_0x01_DVDSET = 0x20;
+static constexpr RegMask REG_0x01_FASTMOD = 0x10;
+static constexpr RegMask REG_0x01_COMPENB = 0x08;
+static constexpr RegMask REG_0x01_DRAMSEL = 0x04;
+static constexpr RegMask REG_0x01_SHDAREA = 0x02;
+static constexpr RegMask REG_0x01_SCAN = 0x01;
+
+static constexpr RegMask REG_0x02_NOTHOME = 0x80;
+static constexpr RegMask REG_0x02_ACDCDIS = 0x40;
+static constexpr RegMask REG_0x02_AGOHOME = 0x20;
+static constexpr RegMask REG_0x02_MTRPWR = 0x10;
+static constexpr RegMask REG_0x02_FASTFED = 0x08;
+static constexpr RegMask REG_0x02_MTRREV = 0x04;
+static constexpr RegMask REG_0x02_STEPSEL = 0x03;
+
+static constexpr RegMask REG_0x02_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x02_HALFSTEP = 0x01;
+static constexpr RegMask REG_0x02_QUATERSTEP = 0x02;
+
+static constexpr RegMask REG_0x03_TG3 = 0x80;
+static constexpr RegMask REG_0x03_AVEENB = 0x40;
+static constexpr RegMask REG_0x03_XPASEL = 0x20;
+static constexpr RegMask REG_0x03_LAMPPWR = 0x10;
+static constexpr RegMask REG_0x03_LAMPDOG = 0x08;
+static constexpr RegMask REG_0x03_LAMPTIM = 0x07;
+
+static constexpr RegMask REG_0x04_LINEART = 0x80;
+static constexpr RegMask REG_0x04_BITSET = 0x40;
+static constexpr RegMask REG_0x04_ADTYPE = 0x30;
+static constexpr RegMask REG_0x04_FILTER = 0x0c;
+static constexpr RegMask REG_0x04_FESET = 0x03;
+
+static constexpr RegMask REG_0x05_DPIHW = 0xc0;
+static constexpr RegMask REG_0x05_DPIHW_600 = 0x00;
+static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40;
+static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80;
+static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0;
+static constexpr RegMask REG_0x05_GMMTYPE = 0x30;
+static constexpr RegMask REG_0x05_GMM14BIT = 0x10;
+static constexpr RegMask REG_0x05_GMMENB = 0x08;
+static constexpr RegMask REG_0x05_LEDADD = 0x04;
+static constexpr RegMask REG_0x05_BASESEL = 0x03;
+
+static constexpr RegAddr REG_0x06 = 0x06;
+static constexpr RegMask REG_0x06_PWRBIT = 0x10;
+static constexpr RegMask REG_0x06_GAIN4 = 0x08;
+static constexpr RegMask REG_0x06_OPTEST = 0x07;
+
+static constexpr RegMask REG_0x07_DMASEL = 0x02;
+static constexpr RegMask REG_0x07_DMARDWR = 0x01;
+
+static constexpr RegMask REG_0x16_CTRLHI = 0x80;
+static constexpr RegMask REG_0x16_SELINV = 0x40;
+static constexpr RegMask REG_0x16_TGINV = 0x20;
+static constexpr RegMask REG_0x16_CK1INV = 0x10;
+static constexpr RegMask REG_0x16_CK2INV = 0x08;
+static constexpr RegMask REG_0x16_CTRLINV = 0x04;
+static constexpr RegMask REG_0x16_CKDIS = 0x02;
+static constexpr RegMask REG_0x16_CTRLDIS = 0x01;
+
+static constexpr RegMask REG_0x17_TGMODE = 0xc0;
+static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00;
+static constexpr RegMask REG_0x17_TGMODE_REF = 0x40;
+static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80;
+static constexpr RegMask REG_0x17_TGW = 0x3f;
+
+static constexpr RegMask REG_0x18_CNSET = 0x80;
+static constexpr RegMask REG_0x18_DCKSEL = 0x60;
+static constexpr RegMask REG_0x18_CKTOGGLE = 0x10;
+static constexpr RegMask REG_0x18_CKDELAY = 0x0c;
+static constexpr RegMask REG_0x18_CKSEL = 0x03;
+
+static constexpr RegMask REG_0x1D_CKMANUAL = 0x80;
+
+static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
+static constexpr RegMask REG_0x1E_LINESEL = 0x0f;
+
+static constexpr RegMask REG_0x41_PWRBIT = 0x80;
+static constexpr RegMask REG_0x41_BUFEMPTY = 0x40;
+static constexpr RegMask REG_0x41_FEEDFSH = 0x20;
+static constexpr RegMask REG_0x41_SCANFSH = 0x10;
+static constexpr RegMask REG_0x41_HOMESNR = 0x08;
+static constexpr RegMask REG_0x41_LAMPSTS = 0x04;
+static constexpr RegMask REG_0x41_FEBUSY = 0x02;
+static constexpr RegMask REG_0x41_MOTMFLG = 0x01;
+
+static constexpr RegMask REG_0x66_LOW_CURRENT = 0x10;
+
+static constexpr RegMask REG_0x6A_FSTPSEL = 0xc0;
+static constexpr RegMask REG_0x6A_FASTPWM = 0x3f;
+
+static constexpr RegMask REG_0x6C_TGTIME = 0xc0;
+static constexpr RegMask REG_0x6C_Z1MOD = 0x38;
+static constexpr RegMask REG_0x6C_Z2MOD = 0x07;
+
+static constexpr RegAddr REG_EXPR = 0x10;
+static constexpr RegAddr REG_EXPG = 0x12;
+static constexpr RegAddr REG_EXPB = 0x14;
+static constexpr RegAddr REG_SCANFED = 0x1f;
+static constexpr RegAddr REG_BUFSEL = 0x20;
+static constexpr RegAddr REG_LINCNT = 0x25;
+static constexpr RegAddr REG_DPISET = 0x2c;
+static constexpr RegAddr REG_STRPIXEL = 0x30;
+static constexpr RegAddr REG_ENDPIXEL = 0x32;
+static constexpr RegAddr REG_DUMMY = 0x34;
+static constexpr RegAddr REG_MAXWD = 0x35;
+static constexpr RegAddr REG_LPERIOD = 0x38;
+static constexpr RegAddr REG_FEEDL = 0x3d;
+static constexpr RegAddr REG_VALIDWORD = 0x42;
+static constexpr RegAddr REG_FEDCNT = 0x48;
+static constexpr RegAddr REG_SCANCNT = 0x4b;
+static constexpr RegAddr REG_Z1MOD = 0x60;
+static constexpr RegAddr REG_Z2MOD = 0x62;
+
+} // namespace gl646
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL646_REGISTERS_H
diff --git a/backend/genesys/gl841.cpp b/backend/genesys/gl841.cpp
new file mode 100644
index 0000000..470f9ba
--- /dev/null
+++ b/backend/genesys/gl841.cpp
@@ -0,0 +1,4010 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005 Philipp Schmid <philipp8288@web.de>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
+ Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
+ for Plustek Opticbook 3600 support
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "gl841.h"
+#include "gl841_registers.h"
+#include "test_settings.h"
+
+#include <vector>
+
+namespace genesys {
+namespace gl841 {
+
+
+static int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor,
+ float slope_dpi,
+ StepType scan_step_type,
+ int start,
+ int used_pixels);
+
+/** copy sensor specific settings */
+/* *dev : device infos
+ *regs : registers to be set
+ extended : do extended set up
+ ccd_size_divisor: set up for half ccd resolution
+ all registers 08-0B, 10-1D, 52-59 are set up. They shouldn't
+ appear anywhere else but in register_ini
+
+Responsible for signals to CCD/CIS:
+ CCD_CK1X (CK1INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1MTGL(0x1C),CK1LOW(0x1D),CK1MAP(0x74,0x75,0x76),CK1NEG(0x7D))
+ CCD_CK2X (CK2INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1LOW(0x1D),CK1NEG(0x7D))
+ CCD_CK3X (MANUAL3(0x1A),CK3INV(0x1A),CK3MTGL(0x1C),CK3LOW(0x1D),CK3MAP(0x77,0x78,0x79),CK3NEG(0x7D))
+ CCD_CK4X (MANUAL3(0x1A),CK4INV(0x1A),CK4MTGL(0x1C),CK4LOW(0x1D),CK4MAP(0x7A,0x7B,0x7C),CK4NEG(0x7D))
+ CCD_CPX (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),CPH(0x72),CPL(0x73),CPNEG(0x7D))
+ CCD_RSX (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),RSH(0x70),RSL(0x71),RSNEG(0x7D))
+ CCD_TGX (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPR(0x10,0x11),TGSHLD(0x1D))
+ CCD_TGG (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPG(0x12,0x13),TGSHLD(0x1D))
+ CCD_TGB (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPB(0x14,0x15),TGSHLD(0x1D))
+ LAMP_SW (EXPR(0x10,0x11),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29))
+ XPA_SW (EXPG(0x12,0x13),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29))
+ LAMP_B (EXPB(0x14,0x15),LAMP_PWR(0x03))
+
+other registers:
+ CISSET(0x01),CNSET(0x18),DCKSEL(0x18),SCANMOD(0x18),EXPDMY(0x19),LINECLP(0x1A),CKAREA(0x1C),TGTIME(0x1C),LINESEL(0x1E),DUMMY(0x34)
+
+Responsible for signals to AFE:
+ VSMP (VSMP(0x58),VSMPW(0x58))
+ BSMP (BSMP(0x59),BSMPW(0x59))
+
+other register settings depending on this:
+ RHI(0x52),RLOW(0x53),GHI(0x54),GLOW(0x55),BHI(0x56),BLOW(0x57),
+
+*/
+static void sanei_gl841_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set * regs,
+ bool extended, unsigned ccd_size_divisor)
+{
+ DBG(DBG_proc, "%s\n", __func__);
+
+ // that one is tricky at least
+ for (uint16_t addr = 0x08; addr <= 0x0b; ++addr) {
+ regs->set8(0x70 + addr - 0x08, sensor.custom_regs.get_value(addr));
+ }
+
+ // ignore registers in range [0x10..0x16)
+ for (uint16_t addr = 0x16; addr < 0x1e; ++addr) {
+ regs->set8(addr, sensor.custom_regs.get_value(addr));
+ }
+
+ // ignore registers in range [0x5b..0x5e]
+ for (uint16_t addr = 0x52; addr < 0x52 + 9; ++addr) {
+ regs->set8(addr, sensor.custom_regs.get_value(addr));
+ }
+
+ /* don't go any further if no extended setup */
+ if (!extended)
+ return;
+
+ /* todo : add more CCD types if needed */
+ /* we might want to expand the Sensor struct to have these
+ 2 kind of settings */
+ if (dev->model->sensor_id == SensorId::CCD_5345) {
+ if (ccd_size_divisor > 1) {
+ GenesysRegister* r;
+ /* settings for CCD used at half is max resolution */
+ r = sanei_genesys_get_address (regs, 0x70);
+ r->value = 0x00;
+ r = sanei_genesys_get_address (regs, 0x71);
+ r->value = 0x05;
+ r = sanei_genesys_get_address (regs, 0x72);
+ r->value = 0x06;
+ r = sanei_genesys_get_address (regs, 0x73);
+ r->value = 0x08;
+ r = sanei_genesys_get_address (regs, 0x18);
+ r->value = 0x28;
+ r = sanei_genesys_get_address (regs, 0x58);
+ r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
+ }
+ else
+ {
+ GenesysRegister* r;
+ /* swap latch times */
+ r = sanei_genesys_get_address (regs, 0x18);
+ r->value = 0x30;
+ regs->set8(0x52, sensor.custom_regs.get_value(0x55));
+ regs->set8(0x53, sensor.custom_regs.get_value(0x56));
+ regs->set8(0x54, sensor.custom_regs.get_value(0x57));
+ regs->set8(0x55, sensor.custom_regs.get_value(0x52));
+ regs->set8(0x56, sensor.custom_regs.get_value(0x53));
+ regs->set8(0x57, sensor.custom_regs.get_value(0x54));
+ r = sanei_genesys_get_address (regs, 0x58);
+ r->value = 0x20 | (r->value & 0x03); /* VSMP=4 */
+ }
+ return;
+ }
+
+ if (dev->model->sensor_id == SensorId::CCD_HP2300) {
+ /* settings for CCD used at half is max resolution */
+ GenesysRegister* r;
+ if (ccd_size_divisor > 1) {
+ r = sanei_genesys_get_address (regs, 0x70);
+ r->value = 0x16;
+ r = sanei_genesys_get_address (regs, 0x71);
+ r->value = 0x00;
+ r = sanei_genesys_get_address (regs, 0x72);
+ r->value = 0x01;
+ r = sanei_genesys_get_address (regs, 0x73);
+ r->value = 0x03;
+ /* manual clock programming */
+ r = sanei_genesys_get_address (regs, 0x1d);
+ r->value |= 0x80;
+ }
+ else
+ {
+ r = sanei_genesys_get_address (regs, 0x70);
+ r->value = 1;
+ r = sanei_genesys_get_address (regs, 0x71);
+ r->value = 3;
+ r = sanei_genesys_get_address (regs, 0x72);
+ r->value = 4;
+ r = sanei_genesys_get_address (regs, 0x73);
+ r->value = 6;
+ }
+ r = sanei_genesys_get_address (regs, 0x58);
+ r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
+ return;
+ }
+}
+
+/*
+ * Set all registers LiDE 80 to default values
+ * (function called only once at the beginning)
+ * we are doing a special case to ease development
+ */
+static void
+gl841_init_lide80 (Genesys_Device * dev)
+{
+ dev->reg.init_reg(0x01, 0x82); // 0x02 = SHDAREA and no CISSET !
+ dev->reg.init_reg(0x02, 0x10);
+ dev->reg.init_reg(0x03, 0x50);
+ dev->reg.init_reg(0x04, 0x02);
+ dev->reg.init_reg(0x05, 0x4c); // 1200 DPI
+ dev->reg.init_reg(0x06, 0x38); // 0x38 scanmod=1, pwrbit, GAIN4
+ dev->reg.init_reg(0x07, 0x00);
+ dev->reg.init_reg(0x08, 0x00);
+ dev->reg.init_reg(0x09, 0x11);
+ dev->reg.init_reg(0x0a, 0x00);
+
+ dev->reg.init_reg(0x10, 0x40);
+ dev->reg.init_reg(0x11, 0x00);
+ dev->reg.init_reg(0x12, 0x40);
+ dev->reg.init_reg(0x13, 0x00);
+ dev->reg.init_reg(0x14, 0x40);
+ dev->reg.init_reg(0x15, 0x00);
+ dev->reg.init_reg(0x16, 0x00);
+ dev->reg.init_reg(0x17, 0x01);
+ dev->reg.init_reg(0x18, 0x00);
+ dev->reg.init_reg(0x19, 0x06);
+ dev->reg.init_reg(0x1a, 0x00);
+ dev->reg.init_reg(0x1b, 0x00);
+ dev->reg.init_reg(0x1c, 0x00);
+ dev->reg.init_reg(0x1d, 0x04);
+ dev->reg.init_reg(0x1e, 0x10);
+ dev->reg.init_reg(0x1f, 0x04);
+ dev->reg.init_reg(0x20, 0x02);
+ dev->reg.init_reg(0x21, 0x10);
+ dev->reg.init_reg(0x22, 0x20);
+ dev->reg.init_reg(0x23, 0x20);
+ dev->reg.init_reg(0x24, 0x10);
+ dev->reg.init_reg(0x25, 0x00);
+ dev->reg.init_reg(0x26, 0x00);
+ dev->reg.init_reg(0x27, 0x00);
+
+ dev->reg.init_reg(0x29, 0xff);
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+ dev->reg.init_reg(0x2c, sensor.optical_res>>8);
+ dev->reg.init_reg(0x2d, sensor.optical_res & 0xff);
+ dev->reg.init_reg(0x2e, 0x80);
+ dev->reg.init_reg(0x2f, 0x80);
+ dev->reg.init_reg(0x30, 0x00);
+ dev->reg.init_reg(0x31, 0x10);
+ dev->reg.init_reg(0x32, 0x15);
+ dev->reg.init_reg(0x33, 0x0e);
+ dev->reg.init_reg(0x34, 0x40);
+ dev->reg.init_reg(0x35, 0x00);
+ dev->reg.init_reg(0x36, 0x2a);
+ dev->reg.init_reg(0x37, 0x30);
+ dev->reg.init_reg(0x38, 0x2a);
+ dev->reg.init_reg(0x39, 0xf8);
+
+ dev->reg.init_reg(0x3d, 0x00);
+ dev->reg.init_reg(0x3e, 0x00);
+ dev->reg.init_reg(0x3f, 0x00);
+
+ dev->reg.init_reg(0x52, 0x03);
+ dev->reg.init_reg(0x53, 0x07);
+ dev->reg.init_reg(0x54, 0x00);
+ dev->reg.init_reg(0x55, 0x00);
+ dev->reg.init_reg(0x56, 0x00);
+ dev->reg.init_reg(0x57, 0x00);
+ dev->reg.init_reg(0x58, 0x29);
+ dev->reg.init_reg(0x59, 0x69);
+ dev->reg.init_reg(0x5a, 0x55);
+
+ dev->reg.init_reg(0x5d, 0x20);
+ dev->reg.init_reg(0x5e, 0x41);
+ dev->reg.init_reg(0x5f, 0x40);
+ dev->reg.init_reg(0x60, 0x00);
+ dev->reg.init_reg(0x61, 0x00);
+ dev->reg.init_reg(0x62, 0x00);
+ dev->reg.init_reg(0x63, 0x00);
+ dev->reg.init_reg(0x64, 0x00);
+ dev->reg.init_reg(0x65, 0x00);
+ dev->reg.init_reg(0x66, 0x00);
+ dev->reg.init_reg(0x67, 0x40);
+ dev->reg.init_reg(0x68, 0x40);
+ dev->reg.init_reg(0x69, 0x20);
+ dev->reg.init_reg(0x6a, 0x20);
+ dev->reg.init_reg(0x6c, 0x00);
+ dev->reg.init_reg(0x6d, 0x00);
+ dev->reg.init_reg(0x6e, 0x00);
+ dev->reg.init_reg(0x6f, 0x00);
+ dev->reg.init_reg(0x70, 0x00);
+ dev->reg.init_reg(0x71, 0x05);
+ dev->reg.init_reg(0x72, 0x07);
+ dev->reg.init_reg(0x73, 0x09);
+ dev->reg.init_reg(0x74, 0x00);
+ dev->reg.init_reg(0x75, 0x01);
+ dev->reg.init_reg(0x76, 0xff);
+ dev->reg.init_reg(0x77, 0x00);
+ dev->reg.init_reg(0x78, 0x0f);
+ dev->reg.init_reg(0x79, 0xf0);
+ dev->reg.init_reg(0x7a, 0xf0);
+ dev->reg.init_reg(0x7b, 0x00);
+ dev->reg.init_reg(0x7c, 0x1e);
+ dev->reg.init_reg(0x7d, 0x11);
+ dev->reg.init_reg(0x7e, 0x00);
+ dev->reg.init_reg(0x7f, 0x50);
+ dev->reg.init_reg(0x80, 0x00);
+ dev->reg.init_reg(0x81, 0x00);
+ dev->reg.init_reg(0x82, 0x0f);
+ dev->reg.init_reg(0x83, 0x00);
+ dev->reg.init_reg(0x84, 0x0e);
+ dev->reg.init_reg(0x85, 0x00);
+ dev->reg.init_reg(0x86, 0x0d);
+ dev->reg.init_reg(0x87, 0x02);
+ dev->reg.init_reg(0x88, 0x00);
+ dev->reg.init_reg(0x89, 0x00);
+
+ for (const auto& reg : dev->gpo.regs) {
+ dev->reg.set8(reg.address, reg.value);
+ }
+
+ // specific scanner settings, clock and gpio first
+ // FIXME: remove the dummy reads as we don't use the values
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6B);
+ }
+ dev->interface->write_register(REG_0x6B, 0x0c);
+ dev->interface->write_register(0x06, 0x10);
+ dev->interface->write_register(REG_0x6E, 0x6d);
+ dev->interface->write_register(REG_0x6F, 0x80);
+ dev->interface->write_register(REG_0x6B, 0x0e);
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6C);
+ }
+ dev->interface->write_register(REG_0x6C, 0x00);
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6D);
+ }
+ dev->interface->write_register(REG_0x6D, 0x8f);
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6B);
+ }
+ dev->interface->write_register(REG_0x6B, 0x0e);
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6B);
+ }
+ dev->interface->write_register(REG_0x6B, 0x0e);
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6B);
+ }
+ dev->interface->write_register(REG_0x6B, 0x0a);
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6B);
+ }
+ dev->interface->write_register(REG_0x6B, 0x02);
+ if (!is_testing_mode()) {
+ dev->interface->read_register(REG_0x6B);
+ }
+ dev->interface->write_register(REG_0x6B, 0x06);
+
+ dev->interface->write_0x8c(0x10, 0x94);
+ dev->interface->write_register(0x09, 0x10);
+
+ // FIXME: the following code originally changed 0x6b, but due to bug the 0x6c register was
+ // effectively changed. The current behavior matches the old code, but should probably be fixed.
+ dev->reg.find_reg(0x6c).value |= REG_0x6B_GPO18;
+ dev->reg.find_reg(0x6c).value &= ~REG_0x6B_GPO17;
+
+ sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 1);
+}
+
+/*
+ * Set all registers to default values
+ * (function called only once at the beginning)
+ */
+static void
+gl841_init_registers (Genesys_Device * dev)
+{
+ int addr;
+
+ DBG(DBG_proc, "%s\n", __func__);
+
+ dev->reg.clear();
+ if (dev->model->model_id == ModelId::CANON_LIDE_80) {
+ gl841_init_lide80(dev);
+ return ;
+ }
+
+ for (addr = 1; addr <= 0x0a; addr++) {
+ dev->reg.init_reg(addr, 0);
+ }
+ for (addr = 0x10; addr <= 0x27; addr++) {
+ dev->reg.init_reg(addr, 0);
+ }
+ dev->reg.init_reg(0x29, 0);
+ for (addr = 0x2c; addr <= 0x39; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x3d; addr <= 0x3f; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x52; addr <= 0x5a; addr++)
+ dev->reg.init_reg(addr, 0);
+ for (addr = 0x5d; addr <= 0x87; addr++)
+ dev->reg.init_reg(addr, 0);
+
+
+ dev->reg.find_reg(0x01).value = 0x20; /* (enable shading), CCD, color, 1M */
+ if (dev->model->is_cis) {
+ dev->reg.find_reg(0x01).value |= REG_0x01_CISSET;
+ } else {
+ dev->reg.find_reg(0x01).value &= ~REG_0x01_CISSET;
+ }
+
+ dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */
+ dev->reg.find_reg(0x02).value |= REG_0x02_AGOHOME;
+ sanei_genesys_set_motor_power(dev->reg, true);
+ dev->reg.find_reg(0x02).value |= REG_0x02_FASTFED;
+
+ dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ; /* lamp on */
+ dev->reg.find_reg(0x03).value |= REG_0x03_AVEENB;
+
+ if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) {
+ // AD front end
+ dev->reg.find_reg(0x04).value = (2 << REG_0x04S_AFEMOD) | 0x02;
+ }
+ else /* Wolfson front end */
+ {
+ dev->reg.find_reg(0x04).value |= 1 << REG_0x04S_AFEMOD;
+ }
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ dev->reg.find_reg(0x05).value = 0x00; /* disable gamma, 24 clocks/pixel */
+
+ unsigned dpihw = 0;
+ if (sensor.sensor_pixels < 0x1500) {
+ dpihw = 600;
+ } else if (sensor.sensor_pixels < 0x2a80) {
+ dpihw = 1200;
+ } else if (sensor.sensor_pixels < 0x5400) {
+ dpihw = 2400;
+ } else {
+ throw SaneException("Cannot handle sensor pixel count %d", sensor.sensor_pixels);
+ }
+ sanei_genesys_set_dpihw(dev->reg, sensor, dpihw);
+
+ dev->reg.find_reg(0x06).value |= REG_0x06_PWRBIT;
+ dev->reg.find_reg(0x06).value |= REG_0x06_GAIN4;
+
+ /* XP300 CCD needs different clock and clock/pixels values */
+ if (dev->model->sensor_id != SensorId::CCD_XP300 &&
+ dev->model->sensor_id != SensorId::CCD_DP685 &&
+ dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICPRO_3600)
+ {
+ dev->reg.find_reg(0x06).value |= 0 << REG_0x06S_SCANMOD;
+ dev->reg.find_reg(0x09).value |= 1 << REG_0x09S_CLKSET;
+ }
+ else
+ {
+ dev->reg.find_reg(0x06).value |= 0x05 << REG_0x06S_SCANMOD; /* 15 clocks/pixel */
+ dev->reg.find_reg(0x09).value = 0; /* 24 MHz CLKSET */
+ }
+
+ dev->reg.find_reg(0x1e).value = 0xf0; /* watch-dog time */
+
+ dev->reg.find_reg(0x17).value |= 1 << REG_0x17S_TGW;
+
+ dev->reg.find_reg(0x19).value = 0x50;
+
+ dev->reg.find_reg(0x1d).value |= 1 << REG_0x1DS_TGSHLD;
+
+ dev->reg.find_reg(0x1e).value |= 1 << REG_0x1ES_WDTIME;
+
+/*SCANFED*/
+ dev->reg.find_reg(0x1f).value = 0x01;
+
+/*BUFSEL*/
+ dev->reg.find_reg(0x20).value = 0x20;
+
+/*LAMPPWM*/
+ dev->reg.find_reg(0x29).value = 0xff;
+
+/*BWHI*/
+ dev->reg.find_reg(0x2e).value = 0x80;
+
+/*BWLOW*/
+ dev->reg.find_reg(0x2f).value = 0x80;
+
+/*LPERIOD*/
+ dev->reg.find_reg(0x38).value = 0x4f;
+ dev->reg.find_reg(0x39).value = 0xc1;
+
+/*VSMPW*/
+ dev->reg.find_reg(0x58).value |= 3 << REG_0x58S_VSMPW;
+
+/*BSMPW*/
+ dev->reg.find_reg(0x59).value |= 3 << REG_0x59S_BSMPW;
+
+/*RLCSEL*/
+ dev->reg.find_reg(0x5a).value |= REG_0x5A_RLCSEL;
+
+/*STOPTIM*/
+ dev->reg.find_reg(0x5e).value |= 0x2 << REG_0x5ES_STOPTIM;
+
+ sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 1);
+
+ // set up GPIO
+ for (const auto& reg : dev->gpo.regs) {
+ dev->reg.set8(reg.address, reg.value);
+ }
+
+ /* TODO there is a switch calling to be written here */
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_35) {
+ dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO18;
+ dev->reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17;
+ }
+
+ if (dev->model->gpio_id == GpioId::XP300) {
+ dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17;
+ }
+
+ if (dev->model->gpio_id == GpioId::DP685) {
+ /* REG_0x6B_GPO18 lights on green led */
+ dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17|REG_0x6B_GPO18;
+ }
+
+ DBG(DBG_proc, "%s complete\n", __func__);
+}
+
+// Send slope table for motor movement slope_table in machine byte order
+static void gl841_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table,
+ int steps)
+{
+ DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps);
+ int dpihw;
+ int start_address;
+ char msg[4000];
+/*#ifdef WORDS_BIGENDIAN*/
+ int i;
+/*#endif*/
+
+ dpihw = dev->reg.find_reg(0x05).value >> 6;
+
+ if (dpihw == 0) /* 600 dpi */
+ start_address = 0x08000;
+ else if (dpihw == 1) /* 1200 dpi */
+ start_address = 0x10000;
+ else if (dpihw == 2) /* 2400 dpi */
+ start_address = 0x20000;
+ else {
+ throw SaneException("Unexpected dpihw");
+ }
+
+ std::vector<uint8_t> table(steps * 2);
+ for(i = 0; i < steps; i++) {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ std::sprintf(msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++) {
+ std::sprintf (msg+strlen(msg), ",%d", slope_table[i]);
+ }
+ DBG(DBG_io, "%s: %s\n", __func__, msg);
+ }
+
+ if (dev->interface->is_mock()) {
+ dev->interface->record_slope_table(table_nr, slope_table);
+ }
+ dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(), steps * 2);
+}
+
+static void gl841_set_lide80_fe(Genesys_Device* dev, uint8_t set)
+{
+ DBG_HELPER(dbg);
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+
+ dev->frontend = dev->frontend_initial;
+
+ // write them to analog frontend
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+ dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x01));
+ dev->interface->write_fe_register(0x06, dev->frontend.regs.get_value(0x02));
+ }
+
+ if (set == AFE_SET)
+ {
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+ dev->interface->write_fe_register(0x06, dev->frontend.regs.get_value(0x20));
+ dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x28));
+ }
+}
+
+// Set values of Analog Device type frontend
+static void gl841_set_ad_fe(Genesys_Device* dev, uint8_t set)
+{
+ DBG_HELPER(dbg);
+ int i;
+
+ if (dev->model->adc_id==AdcId::CANON_LIDE_80) {
+ gl841_set_lide80_fe(dev, set);
+ return;
+ }
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+
+ dev->frontend = dev->frontend_initial;
+
+ // write them to analog frontend
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+
+ for (i = 0; i < 6; i++) {
+ dev->interface->write_fe_register(0x02 + i, 0x00);
+ }
+ }
+ if (set == AFE_SET)
+ {
+ // write them to analog frontend
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+
+ // Write fe 0x02 (red gain)
+ dev->interface->write_fe_register(0x02, dev->frontend.get_gain(0));
+
+ // Write fe 0x03 (green gain)
+ dev->interface->write_fe_register(0x03, dev->frontend.get_gain(1));
+
+ // Write fe 0x04 (blue gain)
+ dev->interface->write_fe_register(0x04, dev->frontend.get_gain(2));
+
+ // Write fe 0x05 (red offset)
+ dev->interface->write_fe_register(0x05, dev->frontend.get_offset(0));
+
+ // Write fe 0x06 (green offset)
+ dev->interface->write_fe_register(0x06, dev->frontend.get_offset(1));
+
+ // Write fe 0x07 (blue offset)
+ dev->interface->write_fe_register(0x07, dev->frontend.get_offset(2));
+ }
+}
+
+// Set values of analog frontend
+void CommandSetGl841::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const
+{
+ DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" :
+ set == AFE_SET ? "set" :
+ set == AFE_POWER_SAVE ? "powersave" : "huh?");
+ (void) sensor;
+
+ /* Analog Device type frontend */
+ uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET;
+
+ if (frontend_type == 0x02) {
+ gl841_set_ad_fe(dev, set);
+ return;
+ }
+
+ if (frontend_type != 0x00) {
+ throw SaneException("unsupported frontend type %d", frontend_type);
+ }
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+ dev->frontend = dev->frontend_initial;
+
+ // reset only done on init
+ dev->interface->write_fe_register(0x04, 0x80);
+ DBG(DBG_proc, "%s(): frontend reset complete\n", __func__);
+ }
+
+
+ if (set == AFE_POWER_SAVE)
+ {
+ dev->interface->write_fe_register(0x01, 0x02);
+ return;
+ }
+
+ /* todo : base this test on cfg reg3 or a CCD family flag to be created */
+ /*if (dev->model->ccd_type!=SensorId::CCD_HP2300 && dev->model->ccd_type!=SensorId::CCD_HP2400) */
+ {
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+ dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02));
+ }
+
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+ dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x03));
+ dev->interface->write_fe_register(0x06, dev->frontend.reg2[0]);
+ dev->interface->write_fe_register(0x08, dev->frontend.reg2[1]);
+ dev->interface->write_fe_register(0x09, dev->frontend.reg2[2]);
+
+ for (unsigned i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
+ dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
+ dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
+ }
+}
+
+enum MotorAction {
+ MOTOR_ACTION_FEED = 1,
+ MOTOR_ACTION_GO_HOME = 2,
+ MOTOR_ACTION_HOME_FREE = 3
+};
+
+// @brief turn off motor
+static void gl841_init_motor_regs_off(Genesys_Register_Set* reg, unsigned int scan_lines)
+{
+ DBG_HELPER_ARGS(dbg, "scan_lines=%d", scan_lines);
+ unsigned int feedl;
+ GenesysRegister* r;
+
+ feedl = 2;
+
+ r = sanei_genesys_get_address (reg, 0x3d);
+ r->value = (feedl >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x3e);
+ r->value = (feedl >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x3f);
+ r->value = feedl & 0xff;
+ r = sanei_genesys_get_address (reg, 0x5e);
+ r->value &= ~0xe0;
+
+ r = sanei_genesys_get_address (reg, 0x25);
+ r->value = (scan_lines >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x26);
+ r->value = (scan_lines >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x27);
+ r->value = scan_lines & 0xff;
+
+ r = sanei_genesys_get_address (reg, 0x02);
+ r->value &= ~0x01; /*LONGCURV OFF*/
+ r->value &= ~0x80; /*NOT_HOME OFF*/
+
+ r->value &= ~0x10;
+
+ r->value &= ~0x06;
+
+ r->value &= ~0x08;
+
+ r->value &= ~0x20;
+
+ r->value &= ~0x40;
+
+ r = sanei_genesys_get_address (reg, 0x67);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address (reg, 0x68);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address(reg, REG_STEPNO);
+ r->value = 0;
+
+ r = sanei_genesys_get_address(reg, REG_FASTNO);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x69);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x6a);
+ r->value = 0;
+
+ r = sanei_genesys_get_address (reg, 0x5f);
+ r->value = 0;
+}
+
+/** @brief write motor table frequency
+ * Write motor frequency data table.
+ * @param dev device to set up motor
+ * @param ydpi motor target resolution
+ */
+static void gl841_write_freq(Genesys_Device* dev, unsigned int ydpi)
+{
+ DBG_HELPER(dbg);
+/**< fast table */
+uint8_t tdefault[] = {0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76};
+uint8_t t1200[] = {0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20};
+uint8_t t300[] = {0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60};
+uint8_t t150[] = {0x0c,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0x40,0x14,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x0c,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0x11,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x0c,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0x40,0xd4,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x0c,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0x11,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60};
+
+uint8_t *table;
+
+ if(dev->model->motor_id == MotorId::CANON_LIDE_80) {
+ switch(ydpi)
+ {
+ case 3600:
+ case 1200:
+ table=t1200;
+ break;
+ case 900:
+ case 300:
+ table=t300;
+ break;
+ case 450:
+ case 150:
+ table=t150;
+ break;
+ default:
+ table=tdefault;
+ }
+ dev->interface->write_register(0x66, 0x00);
+ dev->interface->write_gamma(0x28, 0xc000, table, 128,
+ ScannerInterface::FLAG_SWAP_REGISTERS);
+ dev->interface->write_register(0x5b, 0x00);
+ dev->interface->write_register(0x5c, 0x00);
+ }
+}
+
+
+static void gl841_init_motor_regs(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, unsigned int feed_steps,/*1/base_ydpi*/
+ /*maybe float for half/quarter step resolution?*/
+ unsigned int action, MotorFlag flags)
+{
+ DBG_HELPER_ARGS(dbg, "feed_steps=%d, action=%d, flags=%x", feed_steps, action,
+ static_cast<unsigned>(flags));
+ unsigned int fast_exposure = 0;
+ int use_fast_fed = 0;
+ unsigned int feedl;
+ GenesysRegister* r;
+/*number of scan lines to add in a scan_lines line*/
+
+ {
+ std::vector<uint16_t> table;
+ table.resize(256, 0xffff);
+
+ gl841_send_slope_table(dev, 0, table, 256);
+ gl841_send_slope_table(dev, 1, table, 256);
+ gl841_send_slope_table(dev, 2, table, 256);
+ gl841_send_slope_table(dev, 3, table, 256);
+ gl841_send_slope_table(dev, 4, table, 256);
+ }
+
+ gl841_write_freq(dev, dev->motor.base_ydpi / 4);
+
+ if (action == MOTOR_ACTION_FEED || action == MOTOR_ACTION_GO_HOME) {
+ /* FEED and GO_HOME can use fastest slopes available */
+ fast_exposure = gl841_exposure_time(dev, sensor,
+ dev->motor.base_ydpi / 4,
+ StepType::FULL,
+ 0,
+ 0);
+ DBG(DBG_info, "%s : fast_exposure=%d pixels\n", __func__, fast_exposure);
+ }
+
+ if (action == MOTOR_ACTION_HOME_FREE) {
+/* HOME_FREE must be able to stop in one step, so do not try to get faster */
+ fast_exposure = dev->motor.get_slope(StepType::FULL).max_speed_w;
+ }
+
+ auto fast_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor,
+ StepType::FULL, fast_exposure,
+ dev->motor.base_ydpi / 4);
+
+ feedl = feed_steps - fast_table.steps_count * 2;
+ use_fast_fed = 1;
+
+/* all needed slopes available. we did even decide which mode to use.
+ what next?
+ - transfer slopes
+SCAN:
+flags \ use_fast_fed ! 0 1
+------------------------\--------------------
+ 0 ! 0,1,2 0,1,2,3
+MotorFlag::AUTO_GO_HOME ! 0,1,2,4 0,1,2,3,4
+OFF: none
+FEED: 3
+GO_HOME: 3
+HOME_FREE: 3
+ - setup registers
+ * slope specific registers (already done)
+ * DECSEL for HOME_FREE/GO_HOME/SCAN
+ * FEEDL
+ * MTRREV
+ * MTRPWR
+ * FASTFED
+ * STEPSEL
+ * MTRPWM
+ * FSTPSEL
+ * FASTPWM
+ * HOMENEG
+ * BWDSTEP
+ * FWDSTEP
+ * Z1
+ * Z2
+ */
+
+ r = sanei_genesys_get_address(reg, 0x3d);
+ r->value = (feedl >> 16) & 0xf;
+ r = sanei_genesys_get_address(reg, 0x3e);
+ r->value = (feedl >> 8) & 0xff;
+ r = sanei_genesys_get_address(reg, 0x3f);
+ r->value = feedl & 0xff;
+ r = sanei_genesys_get_address(reg, 0x5e);
+ r->value &= ~0xe0;
+
+ r = sanei_genesys_get_address(reg, 0x25);
+ r->value = 0;
+ r = sanei_genesys_get_address(reg, 0x26);
+ r->value = 0;
+ r = sanei_genesys_get_address(reg, 0x27);
+ r->value = 0;
+
+ r = sanei_genesys_get_address(reg, 0x02);
+ r->value &= ~0x01; /*LONGCURV OFF*/
+ r->value &= ~0x80; /*NOT_HOME OFF*/
+
+ r->value |= 0x10;
+
+ if (action == MOTOR_ACTION_GO_HOME)
+ r->value |= 0x06;
+ else
+ r->value &= ~0x06;
+
+ if (use_fast_fed)
+ r->value |= 0x08;
+ else
+ r->value &= ~0x08;
+
+ if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
+ r->value |= 0x20;
+ } else {
+ r->value &= ~0x20;
+ }
+
+ r->value &= ~0x40;
+
+ if (has_flag(flags, MotorFlag::REVERSE)) {
+ r->value |= REG_0x02_MTRREV;
+ }
+
+ gl841_send_slope_table(dev, 3, fast_table.table, 256);
+
+ r = sanei_genesys_get_address(reg, 0x67);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address(reg, 0x68);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address(reg, REG_STEPNO);
+ r->value = 0;
+
+ r = sanei_genesys_get_address(reg, REG_FASTNO);
+ r->value = 0;
+
+ r = sanei_genesys_get_address(reg, 0x69);
+ r->value = 0;
+
+ r = sanei_genesys_get_address(reg, 0x6a);
+ r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1);
+
+ r = sanei_genesys_get_address(reg, 0x5f);
+ r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1);
+}
+
+static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ unsigned int scan_exposure_time,/*pixel*/
+ unsigned scan_yres, // dpi, motor resolution
+ StepType scan_step_type,
+ unsigned int scan_lines,/*lines, scan resolution*/
+ unsigned int scan_dummy,
+ // number of scan lines to add in a scan_lines line
+ unsigned int feed_steps,/*1/base_ydpi*/
+ // maybe float for half/quarter step resolution?
+ MotorFlag flags)
+{
+ DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, scan_yres=%d, scan_step_type=%d, scan_lines=%d,"
+ " scan_dummy=%d, feed_steps=%d, flags=%x",
+ scan_exposure_time, scan_yres, static_cast<unsigned>(scan_step_type),
+ scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
+ unsigned int fast_exposure;
+ int use_fast_fed = 0;
+ unsigned int fast_time;
+ unsigned int slow_time;
+ unsigned int feedl;
+ GenesysRegister* r;
+ unsigned int min_restep = 0x20;
+ uint32_t z1, z2;
+
+ fast_exposure = gl841_exposure_time(dev, sensor,
+ dev->motor.base_ydpi / 4,
+ StepType::FULL,
+ 0,
+ 0);
+
+ DBG(DBG_info, "%s : fast_exposure=%d pixels\n", __func__, fast_exposure);
+
+ {
+ std::vector<uint16_t> table;
+ table.resize(256, 0xffff);
+
+ gl841_send_slope_table(dev, 0, table, 256);
+ gl841_send_slope_table(dev, 1, table, 256);
+ gl841_send_slope_table(dev, 2, table, 256);
+ gl841_send_slope_table(dev, 3, table, 256);
+ gl841_send_slope_table(dev, 4, table, 256);
+ }
+
+
+ /* motor frequency table */
+ gl841_write_freq(dev, scan_yres);
+
+/*
+ we calculate both tables for SCAN. the fast slope step count depends on
+ how many steps we need for slow acceleration and how much steps we are
+ allowed to use.
+ */
+
+ auto slow_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor,
+ scan_step_type, scan_exposure_time,
+ scan_yres);
+
+ auto back_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor,
+ scan_step_type, 0, scan_yres);
+
+ if (feed_steps < (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) {
+ /*TODO: what should we do here?? go back to exposure calculation?*/
+ feed_steps = slow_table.steps_count >> static_cast<unsigned>(scan_step_type);
+ }
+
+ auto fast_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor,
+ StepType::FULL, fast_exposure,
+ dev->motor.base_ydpi / 4);
+
+ unsigned max_fast_slope_steps_count = 1;
+ if (feed_steps > (slow_table.steps_count >> static_cast<unsigned>(scan_step_type)) + 2) {
+ max_fast_slope_steps_count = (feed_steps -
+ (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) / 2;
+ }
+
+ if (fast_table.steps_count > max_fast_slope_steps_count) {
+ fast_table.slice_steps(max_fast_slope_steps_count);
+ }
+
+ /* fast fed special cases handling */
+ if (dev->model->gpio_id == GpioId::XP300
+ || dev->model->gpio_id == GpioId::DP685)
+ {
+ /* quirk: looks like at least this scanner is unable to use
+ 2-feed mode */
+ use_fast_fed = 0;
+ }
+ else if (feed_steps < fast_table.steps_count * 2 +
+ (slow_table.steps_count >> static_cast<unsigned>(scan_step_type)))
+ {
+ use_fast_fed = 0;
+ DBG(DBG_info, "%s: feed too short, slow move forced.\n", __func__);
+ } else {
+/* for deciding whether we should use fast mode we need to check how long we
+ need for (fast)accelerating, moving, decelerating, (TODO: stopping?)
+ (slow)accelerating again versus (slow)accelerating and moving. we need
+ fast and slow tables here.
+*/
+/*NOTE: scan_exposure_time is per scan_yres*/
+/*NOTE: fast_exposure is per base_ydpi/4*/
+/*we use full steps as base unit here*/
+ fast_time =
+ fast_exposure / 4 *
+ (feed_steps - fast_table.steps_count*2 -
+ (slow_table.steps_count >> static_cast<unsigned>(scan_step_type)))
+ + fast_table.pixeltime_sum*2 + slow_table.pixeltime_sum;
+ slow_time =
+ (scan_exposure_time * scan_yres) / dev->motor.base_ydpi *
+ (feed_steps - (slow_table.steps_count >> static_cast<unsigned>(scan_step_type)))
+ + slow_table.pixeltime_sum;
+
+ DBG(DBG_info, "%s: Time for slow move: %d\n", __func__, slow_time);
+ DBG(DBG_info, "%s: Time for fast move: %d\n", __func__, fast_time);
+
+ use_fast_fed = fast_time < slow_time;
+ }
+
+ if (use_fast_fed) {
+ feedl = feed_steps - fast_table.steps_count * 2 -
+ (slow_table.steps_count >> static_cast<unsigned>(scan_step_type));
+ } else if ((feed_steps << static_cast<unsigned>(scan_step_type)) < slow_table.steps_count) {
+ feedl = 0;
+ } else {
+ feedl = (feed_steps << static_cast<unsigned>(scan_step_type)) - slow_table.steps_count;
+ }
+ DBG(DBG_info, "%s: Decided to use %s mode\n", __func__, use_fast_fed?"fast feed":"slow feed");
+
+/* all needed slopes available. we did even decide which mode to use.
+ what next?
+ - transfer slopes
+SCAN:
+flags \ use_fast_fed ! 0 1
+------------------------\--------------------
+ 0 ! 0,1,2 0,1,2,3
+MotorFlag::AUTO_GO_HOME ! 0,1,2,4 0,1,2,3,4
+OFF: none
+FEED: 3
+GO_HOME: 3
+HOME_FREE: 3
+ - setup registers
+ * slope specific registers (already done)
+ * DECSEL for HOME_FREE/GO_HOME/SCAN
+ * FEEDL
+ * MTRREV
+ * MTRPWR
+ * FASTFED
+ * STEPSEL
+ * MTRPWM
+ * FSTPSEL
+ * FASTPWM
+ * HOMENEG
+ * BWDSTEP
+ * FWDSTEP
+ * Z1
+ * Z2
+ */
+
+ r = sanei_genesys_get_address (reg, 0x3d);
+ r->value = (feedl >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x3e);
+ r->value = (feedl >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x3f);
+ r->value = feedl & 0xff;
+ r = sanei_genesys_get_address (reg, 0x5e);
+ r->value &= ~0xe0;
+
+ r = sanei_genesys_get_address (reg, 0x25);
+ r->value = (scan_lines >> 16) & 0xf;
+ r = sanei_genesys_get_address (reg, 0x26);
+ r->value = (scan_lines >> 8) & 0xff;
+ r = sanei_genesys_get_address (reg, 0x27);
+ r->value = scan_lines & 0xff;
+
+ r = sanei_genesys_get_address (reg, 0x02);
+ r->value &= ~0x01; /*LONGCURV OFF*/
+ r->value &= ~0x80; /*NOT_HOME OFF*/
+ r->value |= 0x10;
+
+ r->value &= ~0x06;
+
+ if (use_fast_fed)
+ r->value |= 0x08;
+ else
+ r->value &= ~0x08;
+
+ if (has_flag(flags, MotorFlag::AUTO_GO_HOME))
+ r->value |= 0x20;
+ else
+ r->value &= ~0x20;
+
+ if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE)) {
+ r->value |= 0x40;
+ } else {
+ r->value &= ~0x40;
+ }
+
+ gl841_send_slope_table(dev, 0, slow_table.table, 256);
+
+ gl841_send_slope_table(dev, 1, back_table.table, 256);
+
+ gl841_send_slope_table(dev, 2, slow_table.table, 256);
+
+ if (use_fast_fed) {
+ gl841_send_slope_table(dev, 3, fast_table.table, 256);
+ }
+
+ if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
+ gl841_send_slope_table(dev, 4, fast_table.table, 256);
+ }
+
+/* now reg 0x21 and 0x24 are available, we can calculate reg 0x22 and 0x23,
+ reg 0x60-0x62 and reg 0x63-0x65
+ rule:
+ 2*STEPNO+FWDSTEP=2*FASTNO+BWDSTEP
+*/
+/* steps of table 0*/
+ if (min_restep < slow_table.steps_count * 2 + 2) {
+ min_restep = slow_table.steps_count * 2 + 2;
+ }
+/* steps of table 1*/
+ if (min_restep < back_table.steps_count * 2 + 2) {
+ min_restep = back_table.steps_count * 2 + 2;
+ }
+/* steps of table 0*/
+ r = sanei_genesys_get_address(reg, REG_FWDSTEP);
+ r->value = min_restep - slow_table.steps_count*2;
+/* steps of table 1*/
+ r = sanei_genesys_get_address(reg, REG_BWDSTEP);
+ r->value = min_restep - back_table.steps_count*2;
+
+/*
+ for z1/z2:
+ in dokumentation mentioned variables a-d:
+ a = time needed for acceleration, table 1
+ b = time needed for reg 0x1f... wouldn't that be reg0x1f*exposure_time?
+ c = time needed for acceleration, table 1
+ d = time needed for reg 0x22... wouldn't that be reg0x22*exposure_time?
+ z1 = (c+d-1) % exposure_time
+ z2 = (a+b-1) % exposure_time
+*/
+/* i don't see any effect of this. i can only guess that this will enhance
+ sub-pixel accuracy
+ z1 = (slope_0_time-1) % exposure_time;
+ z2 = (slope_0_time-1) % exposure_time;
+*/
+ z1 = z2 = 0;
+
+ DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
+ DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
+ r = sanei_genesys_get_address (reg, 0x60);
+ r->value = ((z1 >> 16) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x61);
+ r->value = ((z1 >> 8) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x62);
+ r->value = (z1 & 0xff);
+ r = sanei_genesys_get_address (reg, 0x63);
+ r->value = ((z2 >> 16) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x64);
+ r->value = ((z2 >> 8) & 0xff);
+ r = sanei_genesys_get_address (reg, 0x65);
+ r->value = (z2 & 0xff);
+
+ r = sanei_genesys_get_address(reg, REG_0x1E);
+ r->value &= REG_0x1E_WDTIME;
+ r->value |= scan_dummy;
+
+ r = sanei_genesys_get_address (reg, 0x67);
+ r->value = 0x3f | (static_cast<unsigned>(scan_step_type) << 6);
+
+ r = sanei_genesys_get_address (reg, 0x68);
+ r->value = 0x3f;
+
+ r = sanei_genesys_get_address(reg, REG_STEPNO);
+ r->value = (slow_table.steps_count >> 1) + (slow_table.steps_count & 1);
+
+ r = sanei_genesys_get_address(reg, REG_FASTNO);
+ r->value = (back_table.steps_count >> 1) + (back_table.steps_count & 1);
+
+ r = sanei_genesys_get_address (reg, 0x69);
+ r->value = (slow_table.steps_count >> 1) + (slow_table.steps_count & 1);
+
+ r = sanei_genesys_get_address (reg, 0x6a);
+ r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1);
+
+ r = sanei_genesys_get_address (reg, 0x5f);
+ r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1);
+}
+
+static int
+gl841_get_dpihw(Genesys_Device * dev)
+{
+ GenesysRegister* r;
+ r = sanei_genesys_get_address(&dev->reg, 0x05);
+ if ((r->value & REG_0x05_DPIHW) == REG_0x05_DPIHW_600) {
+ return 600;
+ }
+ if ((r->value & REG_0x05_DPIHW) == REG_0x05_DPIHW_1200) {
+ return 1200;
+ }
+ if ((r->value & REG_0x05_DPIHW) == REG_0x05_DPIHW_2400) {
+ return 2400;
+ }
+ return 0;
+}
+
+static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, unsigned int exposure_time,
+ const ScanSession& session)
+{
+ DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time);
+ GenesysRegister* r;
+ uint16_t expavg, expr, expb, expg;
+
+ dev->cmd_set->set_fe(dev, sensor, AFE_SET);
+
+ /* gpio part.*/
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_35) {
+ r = sanei_genesys_get_address(reg, REG_0x6C);
+ if (session.ccd_size_divisor > 1) {
+ r->value &= ~0x80;
+ } else {
+ r->value |= 0x80;
+ }
+ }
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_80) {
+ r = sanei_genesys_get_address(reg, REG_0x6C);
+ if (session.ccd_size_divisor > 1) {
+ r->value &= ~0x40;
+ r->value |= 0x20;
+ } else {
+ r->value &= ~0x20;
+ r->value |= 0x40;
+ }
+ }
+
+ /* enable shading */
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value |= REG_0x01_SCAN;
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) {
+ r->value &= ~REG_0x01_DVDSET;
+ } else {
+ r->value |= REG_0x01_DVDSET;
+ }
+
+ /* average looks better than deletion, and we are already set up to
+ use one of the average enabled resolutions
+ */
+ r = sanei_genesys_get_address (reg, 0x03);
+ r->value |= REG_0x03_AVEENB;
+ sanei_genesys_set_lamp_power(dev, sensor, *reg,
+ !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));
+
+ /* BW threshold */
+ r = sanei_genesys_get_address (reg, 0x2e);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address (reg, 0x2f);
+ r->value = dev->settings.threshold;
+
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, 0x04);
+ switch (session.params.depth) {
+ case 8:
+ r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG_0x04_LINEART;
+ r->value |= REG_0x04_BITSET;
+ break;
+ }
+
+ /* AFEMOD should depend on FESET, and we should set these
+ * bits separately */
+ r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);
+ if (has_flag(session.params.flags, ScanFlag::ENABLE_LEDADD)) {
+ r->value |= 0x10; /* no filter */
+ }
+ else if (session.params.channels == 1)
+ {
+ switch (session.params.color_filter)
+ {
+ case ColorFilter::RED:
+ r->value |= 0x14;
+ break;
+ case ColorFilter::GREEN:
+ r->value |= 0x18;
+ break;
+ case ColorFilter::BLUE:
+ r->value |= 0x1c;
+ break;
+ default:
+ r->value |= 0x10;
+ break;
+ }
+ }
+ else
+ {
+ if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) {
+ r->value |= 0x22; /* slow color pixel by pixel */
+ }
+ else
+ {
+ r->value |= 0x10; /* color pixel by pixel */
+ }
+ }
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ r = sanei_genesys_get_address (reg, 0x87);
+ r->value &= ~REG_0x87_LEDADD;
+ if (has_flag(session.params.flags, ScanFlag::ENABLE_LEDADD)) {
+ r->value |= REG_0x87_LEDADD;
+ expr = reg->get16(REG_EXPR);
+ expg = reg->get16(REG_EXPG);
+ expb = reg->get16(REG_EXPB);
+
+ /* use minimal exposure for best image quality */
+ expavg = expg;
+ if (expr < expg)
+ expavg = expr;
+ if (expb < expavg)
+ expavg = expb;
+
+ dev->reg.set16(REG_EXPR, expavg);
+ dev->reg.set16(REG_EXPG, expavg);
+ dev->reg.set16(REG_EXPB, expavg);
+ }
+
+ // enable gamma tables
+ if (should_enable_gamma(session, sensor)) {
+ reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
+ } else {
+ reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
+ }
+
+ /* sensor parameters */
+ sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 1, session.ccd_size_divisor);
+
+ r = sanei_genesys_get_address (reg, 0x29);
+ r->value = 255; /*<<<"magic" number, only suitable for cis*/
+
+ reg->set16(REG_DPISET, gl841_get_dpihw(dev) * session.output_resolution / session.optical_resolution);
+ reg->set16(REG_STRPIXEL, session.pixel_startx);
+ reg->set16(REG_ENDPIXEL, session.pixel_endx);
+
+ reg->set24(REG_MAXWD, session.output_line_bytes);
+
+ reg->set16(REG_LPERIOD, exposure_time);
+
+ r = sanei_genesys_get_address (reg, 0x34);
+ r->value = sensor.dummy_pixel;
+}
+
+static int
+gl841_get_led_exposure(Genesys_Device * dev, const Genesys_Sensor& sensor)
+{
+ int d,r,g,b,m;
+ if (!dev->model->is_cis)
+ return 0;
+ d = dev->reg.find_reg(0x19).value;
+
+ r = sensor.exposure.red;
+ g = sensor.exposure.green;
+ b = sensor.exposure.blue;
+
+ m = r;
+ if (m < g)
+ m = g;
+ if (m < b)
+ m = b;
+
+ return m + d;
+}
+
+/** @brief compute exposure time
+ * Compute exposure time for the device and the given scan resolution
+ */
+static int
+gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor,
+ float slope_dpi,
+ StepType scan_step_type,
+ int start,
+ int used_pixels)
+{
+int exposure_time = 0;
+int led_exposure;
+
+ led_exposure=gl841_get_led_exposure(dev, sensor);
+ exposure_time = sanei_genesys_exposure_time2(
+ dev,
+ slope_dpi,
+ scan_step_type,
+ start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/
+ led_exposure);
+
+ return exposure_time;
+}
+
+/**@brief compute scan_step_type
+ * Try to do at least 4 steps per line. if that is impossible we will have to
+ * live with that.
+ * @param dev device
+ * @param yres motor resolution
+ */
+static StepType gl841_scan_step_type(Genesys_Device *dev, int yres)
+{
+ StepType type = StepType::FULL;
+
+ /* TODO : check if there is a bug around the use of max_step_type */
+ /* should be <=1, need to chek all devices entry in genesys_devices */
+ if (yres * 4 < dev->motor.base_ydpi || dev->motor.max_step_type() == StepType::FULL) {
+ type = StepType::FULL;
+ } else if (yres * 4 < dev->motor.base_ydpi * 2 ||
+ dev->motor.max_step_type() <= StepType::HALF)
+ {
+ type = StepType::HALF;
+ } else {
+ type = StepType::QUARTER;
+ }
+
+ /* this motor behaves differently */
+ if (dev->model->motor_id==MotorId::CANON_LIDE_80) {
+ // driven by 'frequency' tables ?
+ type = StepType::FULL;
+ }
+
+ return type;
+}
+
+void CommandSetGl841::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const
+{
+ DBG_HELPER(dbg);
+ session.assert_computed();
+
+ int move;
+ int exposure_time;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+
+/*
+results:
+
+for scanner:
+start
+end
+dpiset
+exposure_time
+dummy
+z1
+z2
+
+for ordered_read:
+ dev->words_per_line
+ dev->read_factor
+ dev->requested_buffer_size
+ dev->read_buffer_size
+ dev->read_pos
+ dev->read_bytes_in_buffer
+ dev->read_bytes_left
+ dev->max_shift
+ dev->stagger
+
+independent of our calculated values:
+ dev->total_bytes_read
+ dev->bytes_to_read
+ */
+
+/* dummy */
+ /* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1
+ dummy line. Maybe the dummy line adds correctness since the motor runs
+ slower (higher dpi)
+ */
+/* for cis this creates better aligned color lines:
+dummy \ scanned lines
+ 0: R G B R ...
+ 1: R G B - R ...
+ 2: R G B - - R ...
+ 3: R G B - - - R ...
+ 4: R G B - - - - R ...
+ 5: R G B - - - - - R ...
+ 6: R G B - - - - - - R ...
+ 7: R G B - - - - - - - R ...
+ 8: R G B - - - - - - - - R ...
+ 9: R G B - - - - - - - - - R ...
+ 10: R G B - - - - - - - - - - R ...
+ 11: R G B - - - - - - - - - - - R ...
+ 12: R G B - - - - - - - - - - - - R ...
+ 13: R G B - - - - - - - - - - - - - R ...
+ 14: R G B - - - - - - - - - - - - - - R ...
+ 15: R G B - - - - - - - - - - - - - - - R ...
+ -- pierre
+ */
+ dummy = 0;
+
+/* slope_dpi */
+/* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis) {
+ slope_dpi = session.params.yres* session.params.channels;
+ } else {
+ slope_dpi = session.params.yres;
+ }
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ StepType scan_step_type = gl841_scan_step_type(dev, session.params.yres);
+ exposure_time = gl841_exposure_time(dev, sensor,
+ slope_dpi,
+ scan_step_type,
+ session.pixel_startx,
+ session.optical_pixels);
+ DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
+
+ gl841_init_optical_regs_scan(dev, sensor, reg, exposure_time, session);
+
+ move = session.params.starty;
+ DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
+
+ /* subtract current head position */
+ move -= (dev->head_pos(ScanHeadId::PRIMARY) * session.params.yres) / dev->motor.base_ydpi;
+ DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
+
+ if (move < 0)
+ move = 0;
+
+ /* round it */
+/* the move is not affected by dummy -- pierre */
+/* move = ((move + dummy) / (dummy + 1)) * (dummy + 1);
+ DBG(DBG_info, "%s: move=%d steps\n", __func__, move);*/
+
+ if (has_flag(session.params.flags, ScanFlag::SINGLE_LINE)) {
+ gl841_init_motor_regs_off(reg, dev->model->is_cis ? session.output_line_count * session.params.channels
+ : session.output_line_count);
+ } else {
+ auto motor_flag = has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) ?
+ MotorFlag::DISABLE_BUFFER_FULL_MOVE : MotorFlag::NONE;
+
+ gl841_init_motor_regs_scan(dev, sensor, reg, exposure_time, slope_dpi, scan_step_type,
+ dev->model->is_cis ? session.output_line_count * session.params.channels
+ : session.output_line_count,
+ dummy, move, motor_flag);
+ }
+
+ dev->read_buffer.clear();
+ dev->read_buffer.alloc(session.buffer_size_read);
+
+ build_image_pipeline(dev, session);
+
+ dev->read_active = true;
+
+ dev->session = session;
+
+ dev->total_bytes_read = 0;
+ dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines;
+
+ DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read);
+}
+
+ScanSession CommandSetGl841::calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const
+{
+ int start;
+
+ DBG(DBG_info, "%s ", __func__);
+ debug_dump(DBG_info, settings);
+
+/* start */
+ start = static_cast<int>(dev->model->x_offset);
+ start += static_cast<int>(settings.tl_x);
+
+ start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = settings.xres;
+ session.params.yres = settings.yres;
+ session.params.startx = start;
+ session.params.starty = 0; // not used
+ session.params.pixels = settings.pixels;
+ session.params.requested_pixels = settings.requested_pixels;
+ session.params.lines = settings.lines;
+ session.params.depth = settings.depth;
+ session.params.channels = settings.get_channels();
+ session.params.scan_method = settings.scan_method;
+ session.params.scan_mode = settings.scan_mode;
+ session.params.color_filter = settings.color_filter;
+ session.params.flags = ScanFlag::NONE;
+
+ compute_session(dev, session, sensor);
+
+ return session;
+}
+
+// for fast power saving methods only, like disabling certain amplifiers
+void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const
+{
+ DBG_HELPER_ARGS(dbg, "enable = %d", enable);
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ if (enable)
+ {
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_35)
+ {
+/* expect GPIO17 to be enabled, and GPIO9 to be disabled,
+ while GPIO8 is disabled*/
+/* final state: GPIO8 disabled, GPIO9 enabled, GPIO17 disabled,
+ GPIO18 disabled*/
+
+ uint8_t val = dev->interface->read_register(REG_0x6D);
+ dev->interface->write_register(REG_0x6D, val | 0x80);
+
+ dev->interface->sleep_ms(1);
+
+ /*enable GPIO9*/
+ val = dev->interface->read_register(REG_0x6C);
+ dev->interface->write_register(REG_0x6C, val | 0x01);
+
+ /*disable GPO17*/
+ val = dev->interface->read_register(REG_0x6B);
+ dev->interface->write_register(REG_0x6B, val & ~REG_0x6B_GPO17);
+
+ /*disable GPO18*/
+ val = dev->interface->read_register(REG_0x6B);
+ dev->interface->write_register(REG_0x6B, val & ~REG_0x6B_GPO18);
+
+ dev->interface->sleep_ms(1);
+
+ val = dev->interface->read_register(REG_0x6D);
+ dev->interface->write_register(REG_0x6D, val & ~0x80);
+
+ }
+ if (dev->model->gpio_id == GpioId::DP685)
+ {
+ uint8_t val = dev->interface->read_register(REG_0x6B);
+ dev->interface->write_register(REG_0x6B, val & ~REG_0x6B_GPO17);
+ dev->reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17;
+ dev->calib_reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17;
+ }
+
+ set_fe(dev, sensor, AFE_POWER_SAVE);
+
+ }
+ else
+ {
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_35)
+ {
+/* expect GPIO17 to be enabled, and GPIO9 to be disabled,
+ while GPIO8 is disabled*/
+/* final state: GPIO8 enabled, GPIO9 disabled, GPIO17 enabled,
+ GPIO18 enabled*/
+
+ uint8_t val = dev->interface->read_register(REG_0x6D);
+ dev->interface->write_register(REG_0x6D, val | 0x80);
+
+ dev->interface->sleep_ms(10);
+
+ /*disable GPIO9*/
+ val = dev->interface->read_register(REG_0x6C);
+ dev->interface->write_register(REG_0x6C, val & ~0x01);
+
+ /*enable GPIO10*/
+ val = dev->interface->read_register(REG_0x6C);
+ dev->interface->write_register(REG_0x6C, val | 0x02);
+
+ /*enable GPO17*/
+ val = dev->interface->read_register(REG_0x6B);
+ dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO17);
+ dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17;
+ dev->calib_reg.find_reg(0x6b).value |= REG_0x6B_GPO17;
+
+ /*enable GPO18*/
+ val = dev->interface->read_register(REG_0x6B);
+ dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO18);
+ dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO18;
+ dev->calib_reg.find_reg(0x6b).value |= REG_0x6B_GPO18;
+
+ }
+ if (dev->model->gpio_id == GpioId::DP665
+ || dev->model->gpio_id == GpioId::DP685)
+ {
+ uint8_t val = dev->interface->read_register(REG_0x6B);
+ dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO17);
+ dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17;
+ dev->calib_reg.find_reg(0x6b).value |= REG_0x6B_GPO17;
+ }
+
+ }
+}
+
+void CommandSetGl841::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
+{
+ DBG_HELPER_ARGS(dbg, "delay = %d", delay);
+ // FIXME: SEQUENTIAL not really needed in this case
+ Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
+ int rate, exposure_time, tgtime, time;
+
+ local_reg.init_reg(0x01, dev->reg.get8(0x01)); /* disable fastmode */
+ local_reg.init_reg(0x03, dev->reg.get8(0x03)); /* Lamp power control */
+ local_reg.init_reg(0x05, dev->reg.get8(0x05)); /*& ~REG_0x05_BASESEL*/; /* 24 clocks/pixel */
+ local_reg.init_reg(0x18, 0x00); // Set CCD type
+ local_reg.init_reg(0x38, 0x00);
+ local_reg.init_reg(0x39, 0x00);
+
+ // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE
+ local_reg.init_reg(0x1c, dev->reg.get8(0x05) & ~REG_0x1C_TGTIME);
+
+ if (!delay) {
+ local_reg.find_reg(0x03).value = local_reg.find_reg(0x03).value & 0xf0; /* disable lampdog and set lamptime = 0 */
+ } else if (delay < 20) {
+ local_reg.find_reg(0x03).value = (local_reg.find_reg(0x03).value & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */
+ } else {
+ local_reg.find_reg(0x03).value = (local_reg.find_reg(0x03).value & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */
+ }
+
+ time = delay * 1000 * 60; /* -> msec */
+ exposure_time = static_cast<std::uint32_t>(time * 32000.0 /
+ (24.0 * 64.0 * (local_reg.find_reg(0x03).value & REG_0x03_LAMPTIM) *
+ 1024.0) + 0.5);
+ /* 32000 = system clock, 24 = clocks per pixel */
+ rate = (exposure_time + 65536) / 65536;
+ if (rate > 4)
+ {
+ rate = 8;
+ tgtime = 3;
+ }
+ else if (rate > 2)
+ {
+ rate = 4;
+ tgtime = 2;
+ }
+ else if (rate > 1)
+ {
+ rate = 2;
+ tgtime = 1;
+ }
+ else
+ {
+ rate = 1;
+ tgtime = 0;
+ }
+
+ local_reg.find_reg(0x1c).value |= tgtime;
+ exposure_time /= rate;
+
+ if (exposure_time > 65535)
+ exposure_time = 65535;
+
+ local_reg.set8(0x38, exposure_time >> 8);
+ local_reg.set8(0x39, exposure_time & 255); /* lowbyte */
+
+ dev->interface->write_registers(local_reg);
+}
+
+static void gl841_stop_action(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ Genesys_Register_Set local_reg;
+ unsigned int loop;
+
+ scanner_read_print_status(*dev);
+
+ if (scanner_is_motor_stopped(*dev)) {
+ DBG(DBG_info, "%s: already stopped\n", __func__);
+ return;
+ }
+
+ local_reg = dev->reg;
+
+ regs_set_optical_off(dev->model->asic_type, local_reg);
+
+ gl841_init_motor_regs_off(&local_reg,0);
+ dev->interface->write_registers(local_reg);
+
+ if (is_testing_mode()) {
+ return;
+ }
+
+ /* looks like writing the right registers to zero is enough to get the chip
+ out of scan mode into command mode, actually triggering(writing to
+ register 0x0f) seems to be unnecessary */
+
+ loop = 10;
+ while (loop > 0) {
+ if (scanner_is_motor_stopped(*dev)) {
+ return;
+ }
+
+ dev->interface->sleep_ms(100);
+ loop--;
+ }
+
+ throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor");
+}
+
+static bool gl841_get_paper_sensor(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+
+ uint8_t val = dev->interface->read_register(REG_0x6D);
+
+ return (val & 0x1) == 0;
+}
+
+void CommandSetGl841::eject_document(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ Genesys_Register_Set local_reg;
+ unsigned int init_steps;
+ float feed_mm;
+ int loop;
+
+ if (!dev->model->is_sheetfed) {
+ DBG(DBG_proc, "%s: there is no \"eject sheet\"-concept for non sheet fed\n", __func__);
+ DBG(DBG_proc, "%s: finished\n", __func__);
+ return;
+ }
+
+
+ local_reg.clear();
+
+ // FIXME: unused result
+ scanner_read_status(*dev);
+
+ gl841_stop_action(dev);
+
+ local_reg = dev->reg;
+
+ regs_set_optical_off(dev->model->asic_type, local_reg);
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+ gl841_init_motor_regs(dev, sensor, &local_reg, 65536, MOTOR_ACTION_FEED, MotorFlag::NONE);
+
+ dev->interface->write_registers(local_reg);
+
+ try {
+ scanner_start_action(*dev, true);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&]() { gl841_stop_action(dev); });
+ // restore original registers
+ catch_all_exceptions(__func__, [&]()
+ {
+ dev->interface->write_registers(dev->reg);
+ });
+ throw;
+ }
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("eject_document");
+ gl841_stop_action(dev);
+ return;
+ }
+
+ if (gl841_get_paper_sensor(dev)) {
+ DBG(DBG_info, "%s: paper still loaded\n", __func__);
+ /* force document TRUE, because it is definitely present */
+ dev->document = true;
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ loop = 300;
+ while (loop > 0) /* do not wait longer then 30 seconds */
+ {
+
+ if (!gl841_get_paper_sensor(dev)) {
+ DBG(DBG_info, "%s: reached home position\n", __func__);
+ DBG(DBG_proc, "%s: finished\n", __func__);
+ break;
+ }
+ dev->interface->sleep_ms(100);
+ --loop;
+ }
+
+ if (loop == 0)
+ {
+ // when we come here then the scanner needed too much time for this, so we better stop
+ // the motor
+ catch_all_exceptions(__func__, [&](){ gl841_stop_action(dev); });
+ throw SaneException(SANE_STATUS_IO_ERROR,
+ "timeout while waiting for scanhead to go home");
+ }
+ }
+
+ feed_mm = static_cast<float>(dev->model->eject_feed);
+ if (dev->document)
+ {
+ feed_mm += static_cast<float>(dev->model->post_scan);
+ }
+
+ sanei_genesys_read_feed_steps(dev, &init_steps);
+
+ /* now feed for extra <number> steps */
+ loop = 0;
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ unsigned int steps;
+
+ sanei_genesys_read_feed_steps(dev, &steps);
+
+ DBG(DBG_info, "%s: init_steps: %d, steps: %d\n", __func__, init_steps, steps);
+
+ if (steps > init_steps + (feed_mm * dev->motor.base_ydpi) / MM_PER_INCH)
+ {
+ break;
+ }
+
+ dev->interface->sleep_ms(100);
+ ++loop;
+ }
+
+ gl841_stop_action(dev);
+
+ dev->document = false;
+}
+
+
+void CommandSetGl841::load_document(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ int loop = 300;
+ while (loop > 0) /* do not wait longer then 30 seconds */
+ {
+ if (gl841_get_paper_sensor(dev)) {
+ DBG(DBG_info, "%s: document inserted\n", __func__);
+
+ /* when loading OK, document is here */
+ dev->document = true;
+
+ // give user some time to place document correctly
+ dev->interface->sleep_ms(1000);
+ break;
+ }
+ dev->interface->sleep_ms(100);
+ --loop;
+ }
+
+ if (loop == 0)
+ {
+ // when we come here then the user needed to much time for this
+ throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for document");
+ }
+}
+
+/**
+ * detects end of document and adjust current scan
+ * to take it into account
+ * used by sheetfed scanners
+ */
+void CommandSetGl841::detect_document_end(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ bool paper_loaded = gl841_get_paper_sensor(dev);
+
+ /* sheetfed scanner uses home sensor as paper present */
+ if (dev->document && !paper_loaded) {
+ DBG(DBG_info, "%s: no more document\n", __func__);
+ dev->document = false;
+
+ /* we can't rely on total_bytes_to_read since the frontend
+ * might have been slow to read data, so we re-evaluate the
+ * amount of data to scan form the hardware settings
+ */
+ unsigned scanned_lines = 0;
+ try {
+ sanei_genesys_read_scancnt(dev, &scanned_lines);
+ } catch (...) {
+ dev->total_bytes_to_read = dev->total_bytes_read;
+ throw;
+ }
+
+ if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS && dev->model->is_cis) {
+ scanned_lines /= 3;
+ }
+
+ std::size_t output_lines = dev->session.output_line_count;
+
+ std::size_t offset_lines = static_cast<std::size_t>(
+ (dev->model->post_scan / MM_PER_INCH) * dev->settings.yres);
+
+ std::size_t scan_end_lines = scanned_lines + offset_lines;
+
+ std::size_t remaining_lines = dev->get_pipeline_source().remaining_bytes() /
+ dev->session.output_line_bytes_raw;
+
+ DBG(DBG_io, "%s: scanned_lines=%u\n", __func__, scanned_lines);
+ DBG(DBG_io, "%s: scan_end_lines=%zu\n", __func__, scan_end_lines);
+ DBG(DBG_io, "%s: output_lines=%zu\n", __func__, output_lines);
+ DBG(DBG_io, "%s: remaining_lines=%zu\n", __func__, remaining_lines);
+
+ if (scan_end_lines > output_lines) {
+ auto skip_lines = scan_end_lines - output_lines;
+
+ if (remaining_lines > skip_lines) {
+ DBG(DBG_io, "%s: skip_lines=%zu\n", __func__, skip_lines);
+
+ remaining_lines -= skip_lines;
+ dev->get_pipeline_source().set_remaining_bytes(remaining_lines *
+ dev->session.output_line_bytes_raw);
+ dev->total_bytes_to_read -= skip_lines * dev->session.output_line_bytes_requested;
+ }
+ }
+ }
+}
+
+// Send the low-level scan command
+// todo : is this that useful ?
+void CommandSetGl841::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, bool start_motor) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+ // FIXME: SEQUENTIAL not really needed in this case
+ Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
+ uint8_t val;
+
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_80) {
+ val = dev->interface->read_register(REG_0x6B);
+ val = REG_0x6B_GPO18;
+ dev->interface->write_register(REG_0x6B, val);
+ }
+
+ if (dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICPRO_3600) {
+ local_reg.init_reg(0x03, reg->get8(0x03) | REG_0x03_LAMPPWR);
+ } else {
+ // TODO PLUSTEK_3600: why ??
+ local_reg.init_reg(0x03, reg->get8(0x03));
+ }
+
+ local_reg.init_reg(0x01, reg->get8(0x01) | REG_0x01_SCAN);
+ local_reg.init_reg(0x0d, 0x01);
+
+ // scanner_start_action(dev, start_motor)
+ if (start_motor) {
+ local_reg.init_reg(0x0f, 0x01);
+ } else {
+ // do not start motor yet
+ local_reg.init_reg(0x0f, 0x00);
+ }
+
+ dev->interface->write_registers(local_reg);
+
+ dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
+}
+
+
+// Send the stop scan command
+void CommandSetGl841::end_scan(Genesys_Device* dev, Genesys_Register_Set __sane_unused__* reg,
+ bool check_stop) const
+{
+ DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop);
+
+ if (!dev->model->is_sheetfed) {
+ gl841_stop_action(dev);
+ }
+}
+
+// Moves the slider to steps
+static void gl841_feed(Genesys_Device* dev, int steps)
+{
+ DBG_HELPER_ARGS(dbg, "steps = %d", steps);
+ Genesys_Register_Set local_reg;
+ int loop;
+
+ gl841_stop_action(dev);
+
+ // FIXME: we should pick sensor according to the resolution scanner is currently operating on
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ local_reg = dev->reg;
+
+ regs_set_optical_off(dev->model->asic_type, local_reg);
+
+ gl841_init_motor_regs(dev, sensor, &local_reg, steps, MOTOR_ACTION_FEED, MotorFlag::NONE);
+
+ dev->interface->write_registers(local_reg);
+
+ try {
+ scanner_start_action(*dev, true);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&]() { gl841_stop_action (dev); });
+ // restore original registers
+ catch_all_exceptions(__func__, [&]()
+ {
+ dev->interface->write_registers(dev->reg);
+ });
+ throw;
+ }
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("feed");
+ dev->advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::FORWARD, steps);
+ gl841_stop_action(dev);
+ return;
+ }
+
+ loop = 0;
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ auto status = scanner_read_status(*dev);
+
+ if (!status.is_motor_enabled) {
+ DBG(DBG_proc, "%s: finished\n", __func__);
+ dev->advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::FORWARD, steps);
+ return;
+ }
+ dev->interface->sleep_ms(100);
+ ++loop;
+ }
+
+ /* when we come here then the scanner needed too much time for this, so we better stop the motor */
+ gl841_stop_action (dev);
+
+ dev->set_head_pos_unknown();
+
+ throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
+}
+
+// Moves the slider to the home (top) position slowly
+void CommandSetGl841::move_back_home(Genesys_Device* dev, bool wait_until_home) const
+{
+ DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home);
+ Genesys_Register_Set local_reg;
+ int loop = 0;
+
+ if (dev->model->is_sheetfed) {
+ DBG(DBG_proc, "%s: there is no \"home\"-concept for sheet fed\n", __func__);
+ DBG(DBG_proc, "%s: finished\n", __func__);
+ return;
+ }
+
+ // reset gpio pin
+ uint8_t val;
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_35) {
+ val = dev->interface->read_register(REG_0x6C);
+ val = dev->gpo.regs.get_value(0x6c);
+ dev->interface->write_register(REG_0x6C, val);
+ }
+ if (dev->model->gpio_id == GpioId::CANON_LIDE_80) {
+ val = dev->interface->read_register(REG_0x6B);
+ val = REG_0x6B_GPO18 | REG_0x6B_GPO17;
+ dev->interface->write_register(REG_0x6B, val);
+ }
+ dev->cmd_set->save_power(dev, false);
+
+ // first read gives HOME_SENSOR true
+ auto status = scanner_read_reliable_status(*dev);
+
+
+ if (status.is_at_home) {
+ DBG(DBG_info, "%s: already at home, completed\n", __func__);
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+
+ scanner_stop_action_no_move(*dev, dev->reg);
+
+ /* if motor is on, stop current action */
+ if (status.is_motor_enabled) {
+ gl841_stop_action(dev);
+ }
+
+ local_reg = dev->reg;
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ gl841_init_motor_regs(dev, sensor, &local_reg, 65536, MOTOR_ACTION_GO_HOME, MotorFlag::REVERSE);
+
+ // set up for no scan
+ regs_set_optical_off(dev->model->asic_type, local_reg);
+
+ dev->interface->write_registers(local_reg);
+
+ try {
+ scanner_start_action(*dev, true);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&]() { gl841_stop_action(dev); });
+ // restore original registers
+ catch_all_exceptions(__func__, [&]()
+ {
+ dev->interface->write_registers(dev->reg);
+ });
+ throw;
+ }
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("move_back_home");
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+
+ if (wait_until_home)
+ {
+ while (loop < 300) /* do not wait longer then 30 seconds */
+ {
+ auto status = scanner_read_status(*dev);
+ if (status.is_at_home) {
+ DBG(DBG_info, "%s: reached home position\n", __func__);
+ DBG(DBG_proc, "%s: finished\n", __func__);
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+ return;
+ }
+ dev->interface->sleep_ms(100);
+ ++loop;
+ }
+
+ // when we come here then the scanner needed too much time for this, so we better stop
+ // the motor
+ catch_all_exceptions(__func__, [&](){ gl841_stop_action(dev); });
+ dev->set_head_pos_unknown();
+ throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
+ }
+
+ DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
+}
+
+// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi
+// from very top of scanner
+void CommandSetGl841::search_start_position(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ int size;
+ Genesys_Register_Set local_reg;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ local_reg = dev->reg;
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ // FIXME: the current approach of doing search only for one resolution does not work on scanners
+ // whith employ different sensors with potentially different settings.
+ const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method);
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0; /*we should give a small offset here~60 steps*/
+ session.params.pixels = 600;
+ session.params.lines = dev->model->search_lines;
+ session.params.depth = 8;
+ session.params.channels = 1;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::GREEN;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::DISABLE_BUFFER_FULL_MOVE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ // send to scanner
+ dev->interface->write_registers(local_reg);
+
+ size = pixels * dev->model->search_lines;
+
+ std::vector<uint8_t> data(size);
+
+ dev->cmd_set->begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_start_position");
+ dev->cmd_set->end_scan(dev, &local_reg, true);
+ dev->reg = local_reg;
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl841_search_position.pnm", data.data(), 8, 1, pixels,
+ dev->model->search_lines);
+ }
+
+ dev->cmd_set->end_scan(dev, &local_reg, true);
+
+ /* update regs to copy ASIC internal state */
+ dev->reg = local_reg;
+
+ for (auto& sensor_update :
+ sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method))
+ {
+ sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels,
+ dev->model->search_lines);
+ }
+}
+
+// sets up register for coarse gain calibration
+// todo: check it for scanners using it
+void CommandSetGl841::init_regs_for_coarse_calibration(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel();
+ session.params.lines = 20;
+ session.params.depth = 16;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
+ sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres);
+
+ dev->interface->write_registers(regs);
+
+/* if (DBG_LEVEL >= DBG_info)
+ sanei_gl841_print_registers (regs);*/
+}
+
+
+// init registers for shading calibration
+void CommandSetGl841::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER_ARGS(dbg, "lines = %zu", dev->calib_lines);
+ SANE_Int ydpi;
+ unsigned starty = 0;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ ydpi = dev->motor.base_ydpi;
+ if (dev->model->motor_id == MotorId::PLUSTEK_OPTICPRO_3600) /* TODO PLUSTEK_3600: 1200dpi not yet working, produces dark bar */
+ {
+ ydpi = 600;
+ }
+ if (dev->model->motor_id == MotorId::CANON_LIDE_80) {
+ ydpi = gl841_get_dpihw(dev);
+ /* get over extra dark area for this model.
+ It looks like different devices have dark areas of different width
+ due to manufacturing variability. The initial value of starty was 140,
+ but it moves the sensor almost past the dark area completely in places
+ on certain devices.
+
+ On a particular device the black area starts at roughly position
+ 160 to 230 depending on location (the dark area is not completely
+ parallel to the frame).
+ */
+ starty = 70;
+ }
+
+ dev->calib_channels = 3;
+ dev->calib_lines = dev->model->shading_lines;
+
+ unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres);
+ unsigned factor = sensor.optical_res / resolution;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, dev->calib_channels,
+ dev->settings.scan_method);
+
+ dev->calib_pixels = calib_sensor.sensor_pixels / factor;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = ydpi;
+ session.params.startx = 0;
+ session.params.starty = starty;
+ session.params.pixels = dev->calib_pixels;
+ session.params.lines = dev->calib_lines;
+ session.params.depth = 16;
+ session.params.channels = dev->calib_channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ /*ScanFlag::DISABLE_BUFFER_FULL_MOVE |*/
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ dev->interface->write_registers(regs);
+}
+
+// set up registers for the actual scan
+void CommandSetGl841::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ float move;
+ int move_dpi;
+ float start;
+
+ debug_dump(DBG_info, dev->settings);
+
+ /* steps to move to reach scanning area:
+ - first we move to physical start of scanning
+ either by a fixed steps amount from the black strip
+ or by a fixed amount from parking position,
+ minus the steps done during shading calibration
+ - then we move by the needed offset whitin physical
+ scanning area
+
+ assumption: steps are expressed at maximum motor resolution
+
+ we need:
+ float y_offset;
+ float y_size;
+ float y_offset_calib;
+ mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
+
+ /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
+ relative from origin, else, it is from parking position */
+
+ move_dpi = dev->motor.base_ydpi;
+
+ move = 0;
+ if (dev->model->flags & GENESYS_FLAG_SEARCH_START) {
+ move += static_cast<float>(dev->model->y_offset_calib_white);
+ }
+
+ DBG(DBG_info, "%s move=%f steps\n", __func__, move);
+
+ move += static_cast<float>(dev->model->y_offset);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ move += static_cast<float>(dev->settings.tl_y);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ move = static_cast<float>((move * move_dpi) / MM_PER_INCH);
+
+/* start */
+ start = static_cast<float>(dev->model->x_offset);
+
+ start += static_cast<float>(dev->settings.tl_x);
+
+ start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH);
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ ScanFlag flags = ScanFlag::NONE;
+
+ /* true gray (led add for cis scanners) */
+ if(dev->model->is_cis && dev->settings.true_gray
+ && dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS
+ && dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80)
+ {
+ // on Lide 80 the LEDADD bit results in only red LED array being lit
+ DBG(DBG_io, "%s: activating LEDADD\n", __func__);
+ flags |= ScanFlag::ENABLE_LEDADD;
+ }
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = static_cast<unsigned>(start);
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = dev->settings.pixels;
+ session.params.requested_pixels = dev->settings.requested_pixels;
+ session.params.lines = dev->settings.lines;
+ session.params.depth = dev->settings.depth;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = flags;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &dev->reg, session);
+}
+
+
+// this function sends generic gamma table (ie linear ones) or the Sensor specific one if provided
+void CommandSetGl841::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ int size;
+
+ size = 256;
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ std::vector<uint8_t> gamma(size * 2 * 3);
+
+ sanei_genesys_generate_gamma_buffer(dev, sensor, 16, 65535, size, gamma.data());
+
+ dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3);
+}
+
+
+/* this function does the led calibration by scanning one line of the calibration
+ area below scanner's top on white strip.
+
+-needs working coarse/gain
+*/
+SensorExposure CommandSetGl841::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int total_size;
+ int i, j;
+ int val;
+ int channels;
+ int avg[3], avga, avge;
+ int turn;
+ uint16_t exp[3], target;
+ int move;
+
+ /* these 2 boundaries should be per sensor */
+ uint16_t min_exposure=500;
+ uint16_t max_exposure;
+
+ /* feed to white strip if needed */
+ if (dev->model->y_offset_calib_white > 0) {
+ move = static_cast<int>(dev->model->y_offset_calib_white);
+ move = static_cast<int>((move * (dev->motor.base_ydpi)) / MM_PER_INCH);
+ DBG(DBG_io, "%s: move=%d lines\n", __func__, move);
+ gl841_feed(dev, move);
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+
+ unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres);
+ unsigned factor = sensor.optical_res / resolution;
+
+ const auto& calib_sensor_base = sanei_genesys_find_sensor(dev, resolution, channels,
+ dev->settings.scan_method);
+
+ num_pixels = calib_sensor_base.sensor_pixels / factor;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor_base);
+
+ init_regs_for_scan_session(dev, calib_sensor_base, &regs, session);
+
+ dev->interface->write_registers(regs);
+
+
+ total_size = num_pixels * channels * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ std::vector<uint8_t> line(total_size);
+
+/*
+ we try to get equal bright leds here:
+
+ loop:
+ average per color
+ adjust exposure times
+ */
+
+ exp[0] = sensor.exposure.red;
+ exp[1] = sensor.exposure.green;
+ exp[2] = sensor.exposure.blue;
+
+ turn = 0;
+ /* max exposure is set to ~2 time initial average
+ * exposure, or 2 time last calibration exposure */
+ max_exposure=((exp[0]+exp[1]+exp[2])/3)*2;
+ target=sensor.gain_white_ref*256;
+
+ auto calib_sensor = calib_sensor_base;
+
+ bool acceptable = false;
+ do {
+ calib_sensor.exposure.red = exp[0];
+ calib_sensor.exposure.green = exp[1];
+ calib_sensor.exposure.blue = exp[2];
+
+ regs_set_exposure(dev->model->asic_type, regs, calib_sensor.exposure);
+ dev->interface->write_register(0x10, (calib_sensor.exposure.red >> 8) & 0xff);
+ dev->interface->write_register(0x11, calib_sensor.exposure.red & 0xff);
+ dev->interface->write_register(0x12, (calib_sensor.exposure.green >> 8) & 0xff);
+ dev->interface->write_register(0x13, calib_sensor.exposure.green & 0xff);
+ dev->interface->write_register(0x14, (calib_sensor.exposure.blue >> 8) & 0xff);
+ dev->interface->write_register(0x15, calib_sensor.exposure.blue & 0xff);
+
+ dev->interface->write_registers(regs);
+
+ DBG(DBG_info, "%s: starting line reading\n", __func__);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("led_calibration");
+ move_back_home(dev, true);
+ return calib_sensor.exposure;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ char fn[30];
+ std::snprintf(fn, 30, "gl841_led_%d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, line.data(), 16, channels, num_pixels, 1);
+ }
+
+ /* compute average */
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG(DBG_info,"%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
+
+ acceptable = true;
+
+ /* exposure is acceptable if each color is in the %5 range
+ * of other color channels */
+ if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
+ avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
+ avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
+ {
+ acceptable = false;
+ }
+
+ /* led exposure is not acceptable if white level is too low
+ * ~80 hardcoded value for white level */
+ if(avg[0]<20000 || avg[1]<20000 || avg[2]<20000)
+ {
+ acceptable = false;
+ }
+
+ /* for scanners using target value */
+ if(target>0)
+ {
+ acceptable = true;
+ for(i=0;i<3;i++)
+ {
+ /* we accept +- 2% delta from target */
+ if(abs(avg[i]-target)>target/50)
+ {
+ exp[i]=(exp[i]*target)/avg[i];
+ acceptable = false;
+ }
+ }
+ }
+ else
+ {
+ if (!acceptable)
+ {
+ avga = (avg[0]+avg[1]+avg[2])/3;
+ exp[0] = (exp[0] * avga) / avg[0];
+ exp[1] = (exp[1] * avga) / avg[1];
+ exp[2] = (exp[2] * avga) / avg[2];
+ /*
+ keep the resulting exposures below this value.
+ too long exposure drives the ccd into saturation.
+ we may fix this by relying on the fact that
+ we get a striped scan without shading, by means of
+ statistical calculation
+ */
+ avge = (exp[0] + exp[1] + exp[2]) / 3;
+
+ if (avge > max_exposure) {
+ exp[0] = (exp[0] * max_exposure) / avge;
+ exp[1] = (exp[1] * max_exposure) / avge;
+ exp[2] = (exp[2] * max_exposure) / avge;
+ }
+ if (avge < min_exposure) {
+ exp[0] = (exp[0] * min_exposure) / avge;
+ exp[1] = (exp[1] * min_exposure) / avge;
+ exp[2] = (exp[2] * min_exposure) / avge;
+ }
+
+ }
+ }
+
+ gl841_stop_action(dev);
+
+ turn++;
+
+ } while (!acceptable && turn < 100);
+
+ DBG(DBG_info,"%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
+
+ dev->cmd_set->move_back_home(dev, true);
+
+ return calib_sensor.exposure;
+}
+
+/** @brief calibration for AD frontend devices
+ * offset calibration assumes that the scanning head is on a black area
+ * For LiDE80 analog frontend
+ * 0x0003 : is gain and belongs to [0..63]
+ * 0x0006 : is offset
+ * We scan a line with no gain until average offset reaches the target
+ */
+static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs)
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int total_size;
+ int i;
+ int average;
+ int turn;
+ int top;
+ int bottom;
+ int target;
+
+ /* don't impact 3600 behavior since we can't test it */
+ if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) {
+ return;
+ }
+
+ unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres);
+ unsigned factor = sensor.optical_res / resolution;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3,
+ dev->settings.scan_method);
+
+ num_pixels = calib_sensor.sensor_pixels / factor;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 8;
+ session.params.channels = 3;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ total_size = num_pixels * 3 * 2 * 1;
+
+ std::vector<uint8_t> line(total_size);
+
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+
+ /* loop on scan until target offset is reached */
+ turn=0;
+ target=24;
+ bottom=0;
+ top=255;
+ do {
+ /* set up offset mid range */
+ dev->frontend.set_offset(0, (top + bottom) / 2);
+ dev->frontend.set_offset(1, (top + bottom) / 2);
+ dev->frontend.set_offset(2, (top + bottom) / 2);
+
+ /* scan line */
+ DBG(DBG_info, "%s: starting line reading\n", __func__);
+ dev->interface->write_registers(regs);
+ dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("ad_fe_offset_calibration");
+ gl841_stop_action(dev);
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+ gl841_stop_action (dev);
+ if (DBG_LEVEL >= DBG_data) {
+ char fn[30];
+ std::snprintf(fn, 30, "gl841_offset_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, line.data(), 8, 3, num_pixels, 1);
+ }
+
+ /* search for minimal value */
+ average=0;
+ for(i=0;i<total_size;i++)
+ {
+ average+=line[i];
+ }
+ average/=total_size;
+ DBG(DBG_data, "%s: average=%d\n", __func__, average);
+
+ /* if min value is above target, the current value becomes the new top
+ * else it is the new bottom */
+ if(average>target)
+ {
+ top=(top+bottom)/2;
+ }
+ else
+ {
+ bottom=(top+bottom)/2;
+ }
+ turn++;
+ } while ((top-bottom)>1 && turn < 100);
+
+ // FIXME: don't overwrite the calibrated values
+ dev->frontend.set_offset(0, 0);
+ dev->frontend.set_offset(1, 0);
+ dev->frontend.set_offset(2, 0);
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+}
+
+/* this function does the offset calibration by scanning one line of the calibration
+ area below scanner's top. There is a black margin and the remaining is white.
+ sanei_genesys_search_start() must have been called so that the offsets and margins
+ are allready known.
+
+this function expects the slider to be where?
+*/
+void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int total_size;
+ int i, j;
+ int val;
+ int channels;
+ int off[3],offh[3],offl[3],off1[3],off2[3];
+ int min1[3],min2[3];
+ int cmin[3],cmax[3];
+ int turn;
+ int mintgt = 0x400;
+
+ /* Analog Device fronted have a different calibration */
+ if ((dev->reg.find_reg(0x04).value & REG_0x04_FESET) == 0x02) {
+ return ad_fe_offset_calibration(dev, sensor, regs);
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+
+ unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres);
+ unsigned factor = sensor.optical_res / resolution;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
+ dev->settings.scan_method);
+
+ num_pixels = calib_sensor.sensor_pixels / factor;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::DISABLE_LAMP;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ total_size = num_pixels * channels * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ std::vector<uint8_t> first_line(total_size);
+ std::vector<uint8_t> second_line(total_size);
+
+ /* scan first line of data with no offset nor gain */
+/*WM8199: gain=0.73; offset=-260mV*/
+/*okay. the sensor black level is now at -260mV. we only get 0 from AFE...*/
+/* we should probably do real calibration here:
+ * -detect acceptable offset with binary search
+ * -calculate offset from this last version
+ *
+ * acceptable offset means
+ * - few completely black pixels(<10%?)
+ * - few completely white pixels(<10%?)
+ *
+ * final offset should map the minimum not completely black
+ * pixel to 0(16 bits)
+ *
+ * this does account for dummy pixels at the end of ccd
+ * this assumes slider is at black strip(which is not quite as black as "no
+ * signal").
+ *
+ */
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+ offh[0] = 0xff;
+ offh[1] = 0xff;
+ offh[2] = 0xff;
+ offl[0] = 0x00;
+ offl[1] = 0x00;
+ offl[2] = 0x00;
+ turn = 0;
+
+ bool acceptable = false;
+ do {
+
+ dev->interface->write_registers(regs);
+
+ for (j=0; j < channels; j++) {
+ off[j] = (offh[j]+offl[j])/2;
+ dev->frontend.set_offset(j, off[j]);
+ }
+
+ dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
+
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("offset_calibration");
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ char fn[30];
+ std::snprintf(fn, 30, "gl841_offset1_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, first_line.data(), 16, channels, num_pixels, 1);
+ }
+
+ acceptable = true;
+
+ for (j = 0; j < channels; j++)
+ {
+ cmin[j] = 0;
+ cmax[j] = 0;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ first_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ first_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ first_line[i * 2 * channels + 2 * j + 1] * 256 +
+ first_line[i * 2 * channels + 2 * j];
+ if (val < 10)
+ cmin[j]++;
+ if (val > 65525)
+ cmax[j]++;
+ }
+
+ /* TODO the DP685 has a black strip in the middle of the sensor
+ * should be handled in a more elegant way , could be a bug */
+ if (dev->model->sensor_id == SensorId::CCD_DP685)
+ cmin[j] -= 20;
+
+ if (cmin[j] > num_pixels/100) {
+ acceptable = false;
+ if (dev->model->is_cis)
+ offl[0] = off[0];
+ else
+ offl[j] = off[j];
+ }
+ if (cmax[j] > num_pixels/100) {
+ acceptable = false;
+ if (dev->model->is_cis)
+ offh[0] = off[0];
+ else
+ offh[j] = off[j];
+ }
+ }
+
+ DBG(DBG_info,"%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0],
+ cmin[1], cmax[1], cmin[2], cmax[2]);
+
+ if (dev->model->is_cis) {
+ offh[2] = offh[1] = offh[0];
+ offl[2] = offl[1] = offl[0];
+ }
+
+ gl841_stop_action(dev);
+
+ turn++;
+ } while (!acceptable && turn < 100);
+
+ DBG(DBG_info,"%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]);
+
+
+ for (j = 0; j < channels; j++)
+ {
+ off1[j] = off[j];
+
+ min1[j] = 65536;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ first_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ first_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ first_line[i * 2 * channels + 2 * j + 1] * 256 +
+ first_line[i * 2 * channels + 2 * j];
+ if (min1[j] > val && val >= 10)
+ min1[j] = val;
+ }
+ }
+
+
+ offl[0] = off[0];
+ offl[1] = off[0];
+ offl[2] = off[0];
+ turn = 0;
+
+ do {
+
+ for (j=0; j < channels; j++) {
+ off[j] = (offh[j]+offl[j])/2;
+ dev->frontend.set_offset(j, off[j]);
+ }
+
+ dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
+
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ dev->interface->write_registers(regs);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ char fn[30];
+ std::snprintf(fn, 30, "gl841_offset2_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, second_line.data(), 16, channels, num_pixels, 1);
+ }
+
+ acceptable = true;
+
+ for (j = 0; j < channels; j++)
+ {
+ cmin[j] = 0;
+ cmax[j] = 0;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ second_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ second_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ second_line[i * 2 * channels + 2 * j + 1] * 256 +
+ second_line[i * 2 * channels + 2 * j];
+ if (val < 10)
+ cmin[j]++;
+ if (val > 65525)
+ cmax[j]++;
+ }
+
+ if (cmin[j] > num_pixels/100) {
+ acceptable = false;
+ if (dev->model->is_cis)
+ offl[0] = off[0];
+ else
+ offl[j] = off[j];
+ }
+ if (cmax[j] > num_pixels/100) {
+ acceptable = false;
+ if (dev->model->is_cis)
+ offh[0] = off[0];
+ else
+ offh[j] = off[j];
+ }
+ }
+
+ DBG(DBG_info, "%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0],
+ cmin[1], cmax[1], cmin[2], cmax[2]);
+
+ if (dev->model->is_cis) {
+ offh[2] = offh[1] = offh[0];
+ offl[2] = offl[1] = offl[0];
+ }
+
+ gl841_stop_action(dev);
+
+ turn++;
+
+ } while (!acceptable && turn < 100);
+
+ DBG(DBG_info, "%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]);
+
+
+ for (j = 0; j < channels; j++)
+ {
+ off2[j] = off[j];
+
+ min2[j] = 65536;
+
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ second_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ second_line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ second_line[i * 2 * channels + 2 * j + 1] * 256 +
+ second_line[i * 2 * channels + 2 * j];
+ if (min2[j] > val && val != 0)
+ min2[j] = val;
+ }
+ }
+
+ DBG(DBG_info, "%s: first set: %d/%d,%d/%d,%d/%d\n", __func__, off1[0], min1[0], off1[1], min1[1],
+ off1[2], min1[2]);
+
+ DBG(DBG_info, "%s: second set: %d/%d,%d/%d,%d/%d\n", __func__, off2[0], min2[0], off2[1], min2[1],
+ off2[2], min2[2]);
+
+/*
+ calculate offset for each channel
+ based on minimal pixel value min1 at offset off1 and minimal pixel value min2
+ at offset off2
+
+ to get min at off, values are linearly interpolated:
+ min=real+off*fact
+ min1=real+off1*fact
+ min2=real+off2*fact
+
+ fact=(min1-min2)/(off1-off2)
+ real=min1-off1*(min1-min2)/(off1-off2)
+
+ off=(min-min1+off1*(min1-min2)/(off1-off2))/((min1-min2)/(off1-off2))
+
+ off=(min*(off1-off2)+min1*off2-off1*min2)/(min1-min2)
+
+ */
+ for (j = 0; j < channels; j++)
+ {
+ if (min2[j]-min1[j] == 0) {
+/*TODO: try to avoid this*/
+ DBG(DBG_warn, "%s: difference too small\n", __func__);
+ if (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j] >= 0)
+ off[j] = 0x0000;
+ else
+ off[j] = 0xffff;
+ } else
+ off[j] = (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j])/(min1[j]-min2[j]);
+ if (off[j] > 255)
+ off[j] = 255;
+ if (off[j] < 0)
+ off[j] = 0;
+ dev->frontend.set_offset(j, off[j]);
+ }
+
+ DBG(DBG_info, "%s: final offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]);
+
+ if (dev->model->is_cis) {
+ if (off[0] < off[1])
+ off[0] = off[1];
+ if (off[0] < off[2])
+ off[0] = off[2];
+ dev->frontend.set_offset(0, off[0]);
+ dev->frontend.set_offset(1, off[0]);
+ dev->frontend.set_offset(2, off[0]);
+ }
+
+ if (channels == 1)
+ {
+ dev->frontend.set_offset(1, dev->frontend.get_offset(0));
+ dev->frontend.set_offset(2, dev->frontend.get_offset(0));
+ }
+}
+
+
+/* alternative coarse gain calibration
+ this on uses the settings from offset_calibration and
+ uses only one scanline
+ */
+/*
+ with offset and coarse calibration we only want to get our input range into
+ a reasonable shape. the fine calibration of the upper and lower bounds will
+ be done with shading.
+ */
+void CommandSetGl841::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const
+{
+ DBG_HELPER_ARGS(dbg, "dpi=%d", dpi);
+ int num_pixels;
+ int total_size;
+ int i, j, channels;
+ int max[3];
+ float gain[3];
+ int val;
+ int lines=1;
+ int move;
+
+ // feed to white strip if needed
+ if (dev->model->y_offset_calib_white > 0) {
+ move = static_cast<int>(dev->model->y_offset_calib_white);
+ move = static_cast<int>((move * (dev->motor.base_ydpi)) / MM_PER_INCH);
+ DBG(DBG_io, "%s: move=%d lines\n", __func__, move);
+ gl841_feed(dev, move);
+ }
+
+ /* coarse gain calibration is allways done in color mode */
+ channels = 3;
+
+ unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres);
+ unsigned factor = sensor.optical_res / resolution;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
+ dev->settings.scan_method);
+
+ num_pixels = calib_sensor.sensor_pixels / factor;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = lines;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ dev->interface->write_registers(regs);
+
+ total_size = num_pixels * channels * 2 * lines; /* colors * bytes_per_color * scan lines */
+
+ std::vector<uint8_t> line(total_size);
+
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("coarse_gain_calibration");
+ gl841_stop_action(dev);
+ move_back_home(dev, true);
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data)
+ sanei_genesys_write_pnm_file("gl841_gain.pnm", line.data(), 16, channels, num_pixels, lines);
+
+ /* average high level for each channel and compute gain
+ to reach the target code
+ we only use the central half of the CCD data */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+
+ if (val > max[j])
+ max[j] = val;
+ }
+
+ gain[j] = 65535.0f / max[j];
+
+ uint8_t out_gain = 0;
+
+ if (dev->model->adc_id == AdcId::CANON_LIDE_35 ||
+ dev->model->adc_id == AdcId::WOLFSON_XP300 ||
+ dev->model->adc_id == AdcId::WOLFSON_DSM600)
+ {
+ gain[j] *= 0.69f; // seems we don't get the real maximum. empirically derived
+ if (283 - 208/gain[j] > 255)
+ out_gain = 255;
+ else if (283 - 208/gain[j] < 0)
+ out_gain = 0;
+ else
+ out_gain = static_cast<std::uint8_t>(283 - 208 / gain[j]);
+ } else if (dev->model->adc_id == AdcId::CANON_LIDE_80) {
+ out_gain = static_cast<std::uint8_t>(gain[j] * 12);
+ }
+ dev->frontend.set_gain(j, out_gain);
+
+ DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j],
+ out_gain);
+ }
+
+ for (j = 0; j < channels; j++)
+ {
+ if(gain[j] > 10)
+ {
+ DBG (DBG_error0, "**********************************************\n");
+ DBG (DBG_error0, "**********************************************\n");
+ DBG (DBG_error0, "**** ****\n");
+ DBG (DBG_error0, "**** Extremely low Brightness detected. ****\n");
+ DBG (DBG_error0, "**** Check the scanning head is ****\n");
+ DBG (DBG_error0, "**** unlocked and moving. ****\n");
+ DBG (DBG_error0, "**** ****\n");
+ DBG (DBG_error0, "**********************************************\n");
+ DBG (DBG_error0, "**********************************************\n");
+ throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
+ }
+
+ }
+
+ if (dev->model->is_cis) {
+ uint8_t gain0 = dev->frontend.get_gain(0);
+ if (gain0 > dev->frontend.get_gain(1)) {
+ gain0 = dev->frontend.get_gain(1);
+ }
+ if (gain0 > dev->frontend.get_gain(2)) {
+ gain0 = dev->frontend.get_gain(2);
+ }
+ dev->frontend.set_gain(0, gain0);
+ dev->frontend.set_gain(1, gain0);
+ dev->frontend.set_gain(2, gain0);
+ }
+
+ if (channels == 1) {
+ dev->frontend.set_gain(0, dev->frontend.get_gain(1));
+ dev->frontend.set_gain(2, dev->frontend.get_gain(1));
+ }
+
+ DBG(DBG_info, "%s: gain=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_gain(0),
+ dev->frontend.get_gain(1),
+ dev->frontend.get_gain(2));
+
+ gl841_stop_action(dev);
+
+ dev->cmd_set->move_back_home(dev, true);
+}
+
+// wait for lamp warmup by scanning the same line until difference
+// between 2 scans is below a threshold
+void CommandSetGl841::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* local_reg, int* channels,
+ int* total_size) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels = 4 * 300;
+ *local_reg = dev->reg;
+
+/* okay.. these should be defaults stored somewhere */
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+ dev->frontend.set_offset(0, 0x80);
+ dev->frontend.set_offset(1, 0x80);
+ dev->frontend.set_offset(2, 0x80);
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = sensor.dummy_pixel;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = *channels;
+ session.params.scan_method = dev->settings.scan_method;
+ if (*channels == 3) {
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ } else {
+ session.params.scan_mode = ScanColorMode::GRAY;
+ }
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, local_reg, session);
+
+ num_pixels = session.output_pixels;
+
+ *total_size = num_pixels * 3 * 2 * 1; /* colors * bytes_per_color * scan lines */
+
+ dev->interface->write_registers(*local_reg);
+}
+
+
+/*
+ * this function moves head without scanning, forward, then backward
+ * so that the head goes to park position.
+ * as a by-product, also check for lock
+ */
+static void sanei_gl841_repark_head(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+
+ gl841_feed(dev,232);
+
+ // toggle motor flag, put an huge step number and redo move backward
+ dev->cmd_set->move_back_home(dev, true);
+}
+
+/*
+ * initialize ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+void CommandSetGl841::init(Genesys_Device* dev) const
+{
+ size_t size;
+
+ DBG_INIT ();
+ DBG_HELPER(dbg);
+
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ /* Check if the device has already been initialized and powered up */
+ if (dev->already_initialized)
+ {
+ auto status = scanner_read_status(*dev);
+ if (!status.is_replugged) {
+ DBG(DBG_info, "%s: already initialized\n", __func__);
+ return;
+ }
+ }
+
+ dev->dark_average_data.clear();
+ dev->white_average_data.clear();
+
+ dev->settings.color_filter = ColorFilter::RED;
+
+ // ASIC reset
+ dev->interface->write_register(0x0e, 0x01);
+ dev->interface->write_register(0x0e, 0x00);
+
+ /* Set default values for registers */
+ gl841_init_registers (dev);
+
+ // Write initial registers
+ dev->interface->write_registers(dev->reg);
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ // Set analog frontend
+ dev->cmd_set->set_fe(dev, sensor, AFE_INIT);
+
+ // FIXME: move_back_home modifies dev->calib_reg and requires it to be filled
+ dev->calib_reg = dev->reg;
+
+ // Move home
+ dev->cmd_set->move_back_home(dev, true);
+
+ // Init shading data
+ sanei_genesys_init_shading_data(dev, sensor, sensor.sensor_pixels);
+
+ /* ensure head is correctly parked, and check lock */
+ if (dev->model->flags & GENESYS_FLAG_REPARK)
+ {
+ // FIXME: if repark fails, we should print an error message that the scanner is locked and
+ // the user should unlock the lock. We should also rethrow with SANE_STATUS_JAMMED
+ sanei_gl841_repark_head(dev);
+ }
+
+ // send gamma tables
+ dev->cmd_set->send_gamma_table(dev, sensor);
+
+ /* initial calibration reg values */
+ Genesys_Register_Set& regs = dev->calib_reg;
+ regs = dev->reg;
+
+ unsigned resolution = sensor.get_logical_hwdpi(300);
+ unsigned factor = sensor.optical_res / resolution;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3,
+ dev->settings.scan_method);
+
+ unsigned num_pixels = 16 / factor;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = 300;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = 3;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ dev->interface->write_registers(regs);
+
+ size = num_pixels * 3 * 2 * 1; // colors * bytes_per_color * scan lines
+
+ std::vector<uint8_t> line(size);
+
+ DBG(DBG_info, "%s: starting dummy data reading\n", __func__);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ sanei_usb_set_timeout(1000);/* 1 second*/
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("init");
+ } else {
+ // ignore errors. next read will succeed
+ catch_all_exceptions(__func__,
+ [&](){ sanei_genesys_read_data_from_scanner(dev, line.data(), size); });
+ }
+
+ sanei_usb_set_timeout(30 * 1000);/* 30 seconds*/
+
+ end_scan(dev, &regs, true);
+
+ regs = dev->reg;
+
+ // Set powersaving(default = 15 minutes)
+ set_powersaving(dev, 15);
+ dev->already_initialized = true;
+}
+
+void CommandSetGl841::update_hardware_sensors(Genesys_Scanner* s) const
+{
+ DBG_HELPER(dbg);
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+ uint8_t val;
+
+ if (s->dev->model->gpio_id == GpioId::CANON_LIDE_35
+ || s->dev->model->gpio_id == GpioId::CANON_LIDE_80)
+ {
+ val = s->dev->interface->read_register(REG_0x6D);
+ s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0);
+ s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0);
+ s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0);
+ s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0);
+ }
+
+ if (s->dev->model->gpio_id == GpioId::XP300 ||
+ s->dev->model->gpio_id == GpioId::DP665 ||
+ s->dev->model->gpio_id == GpioId::DP685)
+ {
+ val = s->dev->interface->read_register(REG_0x6D);
+
+ s->buttons[BUTTON_PAGE_LOADED_SW].write((val & 0x01) == 0);
+ s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0);
+ }
+}
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward true if searching forward, false if searching backward
+ * @param black true if searching for a black strip, false for a white strip
+ */
+void CommandSetGl841::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward,
+ bool black) const
+{
+ DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse");
+ unsigned int pixels, lines, channels;
+ Genesys_Register_Set local_reg;
+ size_t size;
+ unsigned int pass, count, found, x, y, length;
+ char title[80];
+ GenesysRegister *r;
+ uint8_t white_level=90; /**< default white level to detect white dots */
+ uint8_t black_level=60; /**< default black level to detect black dots */
+
+ /* use maximum gain when doing forward white strip detection
+ * since we don't have calibrated the sensor yet */
+ if(!black && forward)
+ {
+ dev->frontend.set_gain(0, 0xff);
+ dev->frontend.set_gain(1, 0xff);
+ dev->frontend.set_gain(2, 0xff);
+ }
+
+ dev->cmd_set->set_fe(dev, sensor, AFE_SET);
+ gl841_stop_action(dev);
+
+ // set up for a gray scan at lowest dpi
+ const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method);
+ unsigned dpi = resolution_settings.get_min_resolution_x();
+ channels = 1;
+
+ /* shading calibation is done with dev->motor.base_ydpi */
+ /* lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; */
+ lines = static_cast<unsigned>((10 * dpi) / MM_PER_INCH);
+
+ pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res;
+
+ /* 20 cm max length for calibration sheet */
+ length = static_cast<unsigned>(((200 * dpi) / MM_PER_INCH) / lines);
+
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ local_reg = dev->reg;
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA;
+ compute_session(dev, session, sensor);
+
+ size = pixels * channels * lines * (session.params.depth / 8);
+ std::vector<uint8_t> data(size);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ /* set up for reverse or forward */
+ r = sanei_genesys_get_address(&local_reg, 0x02);
+ if (forward) {
+ r->value &= ~4;
+ } else {
+ r->value |= 4;
+ }
+
+ dev->interface->write_registers(local_reg);
+
+ dev->cmd_set->begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_strip");
+ gl841_stop_action(dev);
+ return;
+ }
+
+ // waits for valid data
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ gl841_stop_action(dev);
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl841_search_strip_%s_%s%02u.pnm", black ? "black" : "white",
+ forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file(title, data.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < length && !found)
+ {
+ dev->interface->write_registers(local_reg);
+
+ //now start scan
+ dev->cmd_set->begin_scan(dev, sensor, &local_reg, true);
+
+ // waits for valid data
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ gl841_stop_action (dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl841_search_strip_%s_%s%02u.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file(title, data.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > white_level)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < black_level)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
+ pass, y);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > white_level)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < black_level)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+
+ if (found)
+ {
+ DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
+ }
+ else
+ {
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white");
+ }
+}
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+void CommandSetGl841::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ uint8_t* data, int size) const
+{
+ DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size);
+ uint32_t length, x, factor, pixels, i;
+ uint16_t dpiset, dpihw, beginpixel;
+ uint8_t *ptr,*src;
+
+ /* old method if no SHDAREA */
+ if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) == 0) {
+ dev->interface->write_buffer(0x3c, 0x0000, data, size);
+ return;
+ }
+
+ /* data is whole line, we extract only the part for the scanned area */
+ length = static_cast<std::uint32_t>(size / 3);
+ unsigned strpixel = dev->session.pixel_startx;
+ unsigned endpixel = dev->session.pixel_endx;
+
+ /* compute deletion/average factor */
+ dpiset = dev->reg.get16(REG_DPISET);
+ dpihw = gl841_get_dpihw(dev);
+ unsigned ccd_size_divisor = dev->session.ccd_size_divisor;
+ factor=dpihw/dpiset;
+ DBG(DBG_io2, "%s: dpihw=%d, dpiset=%d, ccd_size_divisor=%d, factor=%d\n", __func__, dpihw, dpiset,
+ ccd_size_divisor, factor);
+
+ /* turn pixel value into bytes 2x16 bits words */
+ strpixel*=2*2; /* 2 words of 2 bytes */
+ endpixel*=2*2;
+ pixels=endpixel-strpixel;
+
+ /* shading pixel begin is start pixel minus start pixel during shading
+ * calibration. Currently only cases handled are full and half ccd resolution.
+ */
+ beginpixel = sensor.ccd_start_xoffset / ccd_size_divisor;
+ beginpixel += sensor.dummy_pixel + 1;
+ DBG(DBG_io2, "%s: ORIGIN PIXEL=%d\n", __func__, beginpixel);
+ beginpixel = (strpixel-beginpixel*2*2)/factor;
+ DBG(DBG_io2, "%s: BEGIN PIXEL=%d\n", __func__, beginpixel/4);
+
+ dev->interface->record_key_value("shading_offset", std::to_string(beginpixel));
+ dev->interface->record_key_value("shading_pixels", std::to_string(pixels));
+ dev->interface->record_key_value("shading_length", std::to_string(length));
+
+ DBG(DBG_io2, "%s: using chunks of %d bytes (%d shading data pixels)\n", __func__, length,
+ length/4);
+ std::vector<uint8_t> buffer(pixels, 0);
+
+ /* write actual shading data contigously
+ * channel by channel, starting at addr 0x0000
+ * */
+ for(i=0;i<3;i++)
+ {
+ /* copy data to work buffer and process it */
+ /* coefficent destination */
+ ptr=buffer.data();
+
+ /* iterate on both sensor segment, data has been averaged,
+ * so is in the right order and we only have to copy it */
+ for(x=0;x<pixels;x+=4)
+ {
+ /* coefficient source */
+ src=data+x+beginpixel+i*length;
+ ptr[0]=src[0];
+ ptr[1]=src[1];
+ ptr[2]=src[2];
+ ptr[3]=src[3];
+
+ /* next shading coefficient */
+ ptr+=4;
+ }
+
+ // 0x5400 alignment for LIDE80 internal memory
+ dev->interface->write_buffer(0x3c, 0x5400 * i, buffer.data(), pixels);
+ }
+}
+
+bool CommandSetGl841::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
+{
+ (void) dev;
+ return true;
+}
+
+void CommandSetGl841::wait_for_motor_stop(Genesys_Device* dev) const
+{
+ (void) dev;
+}
+
+void CommandSetGl841::move_to_ta(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl841::asic_boot(Genesys_Device *dev, bool cold) const
+{
+ (void) dev;
+ (void) cold;
+ throw SaneException("not implemented");
+}
+
+std::unique_ptr<CommandSet> create_gl841_cmd_set()
+{
+ return std::unique_ptr<CommandSet>(new CommandSetGl841{});
+}
+
+} // namespace gl841
+} // namespace genesys
diff --git a/backend/genesys/gl841.h b/backend/genesys/gl841.h
new file mode 100644
index 0000000..5e24249
--- /dev/null
+++ b/backend/genesys/gl841.h
@@ -0,0 +1,130 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2011-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+#include "command_set.h"
+
+#ifndef BACKEND_GENESYS_GL841_H
+#define BACKEND_GENESYS_GL841_H
+
+namespace genesys {
+namespace gl841 {
+
+class CommandSetGl841 : public CommandSet
+{
+public:
+ ~CommandSetGl841() override = default;
+
+ bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override;
+
+ void init(Genesys_Device* dev) const override;
+
+ void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const override;
+
+ void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const override;
+
+ void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const override;
+ void set_powersaving(Genesys_Device* dev, int delay) const override;
+ void save_power(Genesys_Device* dev, bool enable) const override;
+
+ void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, bool start_motor) const override;
+
+ void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override;
+
+ void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void search_start_position(Genesys_Device* dev) const override;
+
+ void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const override;
+
+ SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void wait_for_motor_stop(Genesys_Device* dev) const override;
+
+ void move_back_home(Genesys_Device* dev, bool wait_until_home) const override;
+
+ void update_hardware_sensors(struct Genesys_Scanner* s) const override;
+
+ void load_document(Genesys_Device* dev) const override;
+
+ void detect_document_end(Genesys_Device* dev) const override;
+
+ void eject_document(Genesys_Device* dev) const override;
+
+ void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const override;
+
+ void move_to_ta(Genesys_Device* dev) const override;
+
+ void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,
+ int size) const override;
+
+ ScanSession calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const override;
+
+ void asic_boot(Genesys_Device* dev, bool cold) const override;
+};
+
+} // namespace gl841
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL841_H
diff --git a/backend/genesys/gl841_registers.h b/backend/genesys/gl841_registers.h
new file mode 100644
index 0000000..8e0c204
--- /dev/null
+++ b/backend/genesys/gl841_registers.h
@@ -0,0 +1,269 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL841_REGISTERS_H
+#define BACKEND_GENESYS_GL841_REGISTERS_H
+
+#include <cstdint>
+
+namespace genesys {
+namespace gl841 {
+
+using RegAddr = std::uint16_t;
+using RegMask = std::uint8_t;
+using RegShift = unsigned;
+
+static constexpr RegAddr REG_0x01 = 0x01;
+static constexpr RegMask REG_0x01_CISSET = 0x80;
+static constexpr RegMask REG_0x01_DOGENB = 0x40;
+static constexpr RegMask REG_0x01_DVDSET = 0x20;
+static constexpr RegMask REG_0x01_M16DRAM = 0x08;
+static constexpr RegMask REG_0x01_DRAMSEL = 0x04;
+static constexpr RegMask REG_0x01_SHDAREA = 0x02;
+static constexpr RegMask REG_0x01_SCAN = 0x01;
+
+static constexpr RegAddr REG_0x02 = 0x02;
+static constexpr RegMask REG_0x02_NOTHOME = 0x80;
+static constexpr RegMask REG_0x02_ACDCDIS = 0x40;
+static constexpr RegMask REG_0x02_AGOHOME = 0x20;
+static constexpr RegMask REG_0x02_MTRPWR = 0x10;
+static constexpr RegMask REG_0x02_FASTFED = 0x08;
+static constexpr RegMask REG_0x02_MTRREV = 0x04;
+static constexpr RegMask REG_0x02_HOMENEG = 0x02;
+static constexpr RegMask REG_0x02_LONGCURV = 0x01;
+
+static constexpr RegMask REG_0x03_LAMPDOG = 0x80;
+static constexpr RegMask REG_0x03_AVEENB = 0x40;
+static constexpr RegMask REG_0x03_XPASEL = 0x20;
+static constexpr RegMask REG_0x03_LAMPPWR = 0x10;
+static constexpr RegMask REG_0x03_LAMPTIM = 0x0f;
+
+static constexpr RegMask REG_0x04_LINEART = 0x80;
+static constexpr RegMask REG_0x04_BITSET = 0x40;
+static constexpr RegMask REG_0x04_AFEMOD = 0x30;
+static constexpr RegMask REG_0x04_FILTER = 0x0c;
+static constexpr RegMask REG_0x04_FESET = 0x03;
+
+static constexpr RegShift REG_0x04S_AFEMOD = 4;
+
+static constexpr RegAddr REG_0x05 = 0x05;
+static constexpr RegMask REG_0x05_DPIHW = 0xc0;
+static constexpr RegMask REG_0x05_DPIHW_600 = 0x00;
+static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40;
+static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80;
+static constexpr RegMask REG_0x05_MTLLAMP = 0x30;
+static constexpr RegMask REG_0x05_GMMENB = 0x08;
+static constexpr RegMask REG_0x05_MTLBASE = 0x03;
+
+static constexpr RegAddr REG_0x06 = 0x06;
+static constexpr RegMask REG_0x06_SCANMOD = 0xe0;
+static constexpr RegShift REG_0x06S_SCANMOD = 5;
+static constexpr RegMask REG_0x06_PWRBIT = 0x10;
+static constexpr RegMask REG_0x06_GAIN4 = 0x08;
+static constexpr RegMask REG_0x06_OPTEST = 0x07;
+
+static constexpr RegMask REG_0x07_SRAMSEL = 0x08;
+static constexpr RegMask REG_0x07_FASTDMA = 0x04;
+static constexpr RegMask REG_0x07_DMASEL = 0x02;
+static constexpr RegMask REG_0x07_DMARDWR = 0x01;
+
+static constexpr RegMask REG_0x08_DECFLAG = 0x40;
+static constexpr RegMask REG_0x08_GMMFFR = 0x20;
+static constexpr RegMask REG_0x08_GMMFFG = 0x10;
+static constexpr RegMask REG_0x08_GMMFFB = 0x08;
+static constexpr RegMask REG_0x08_GMMZR = 0x04;
+static constexpr RegMask REG_0x08_GMMZG = 0x02;
+static constexpr RegMask REG_0x08_GMMZB = 0x01;
+
+static constexpr RegMask REG_0x09_MCNTSET = 0xc0;
+static constexpr RegMask REG_0x09_CLKSET = 0x30;
+static constexpr RegMask REG_0x09_BACKSCAN = 0x08;
+static constexpr RegMask REG_0x09_ENHANCE = 0x04;
+static constexpr RegMask REG_0x09_SHORTTG = 0x02;
+static constexpr RegMask REG_0x09_NWAIT = 0x01;
+
+static constexpr RegShift REG_0x09S_MCNTSET = 6;
+static constexpr RegShift REG_0x09S_CLKSET = 4;
+
+
+static constexpr RegMask REG_0x0A_SRAMBUF = 0x01;
+
+static constexpr RegAddr REG_0x0D = 0x0d;
+static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01;
+
+static constexpr RegMask REG_0x16_CTRLHI = 0x80;
+static constexpr RegMask REG_0x16_TOSHIBA = 0x40;
+static constexpr RegMask REG_0x16_TGINV = 0x20;
+static constexpr RegMask REG_0x16_CK1INV = 0x10;
+static constexpr RegMask REG_0x16_CK2INV = 0x08;
+static constexpr RegMask REG_0x16_CTRLINV = 0x04;
+static constexpr RegMask REG_0x16_CKDIS = 0x02;
+static constexpr RegMask REG_0x16_CTRLDIS = 0x01;
+
+static constexpr RegMask REG_0x17_TGMODE = 0xc0;
+static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00;
+static constexpr RegMask REG_0x17_TGMODE_REF = 0x40;
+static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80;
+static constexpr RegMask REG_0x17_TGW = 0x3f;
+static constexpr RegShift REG_0x17S_TGW = 0;
+
+static constexpr RegMask REG_0x18_CNSET = 0x80;
+static constexpr RegMask REG_0x18_DCKSEL = 0x60;
+static constexpr RegMask REG_0x18_CKTOGGLE = 0x10;
+static constexpr RegMask REG_0x18_CKDELAY = 0x0c;
+static constexpr RegMask REG_0x18_CKSEL = 0x03;
+
+static constexpr RegMask REG_0x1A_MANUAL3 = 0x02;
+static constexpr RegMask REG_0x1A_MANUAL1 = 0x01;
+static constexpr RegMask REG_0x1A_CK4INV = 0x08;
+static constexpr RegMask REG_0x1A_CK3INV = 0x04;
+static constexpr RegMask REG_0x1A_LINECLP = 0x02;
+
+static constexpr RegMask REG_0x1C_TGTIME = 0x07;
+
+static constexpr RegMask REG_0x1D_CK4LOW = 0x80;
+static constexpr RegMask REG_0x1D_CK3LOW = 0x40;
+static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
+static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
+static constexpr RegShift REG_0x1DS_TGSHLD = 0;
+
+
+static constexpr RegAddr REG_0x1E = 0x1e;
+static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
+static constexpr RegShift REG_0x1ES_WDTIME = 4;
+static constexpr RegMask REG_0x1E_LINESEL = 0x0f;
+static constexpr RegShift REG_0x1ES_LINESEL = 0;
+
+static constexpr RegAddr REG_EXPR = 0x10;
+static constexpr RegAddr REG_EXPG = 0x12;
+static constexpr RegAddr REG_EXPB = 0x14;
+static constexpr RegAddr REG_STEPNO = 0x21;
+static constexpr RegAddr REG_FWDSTEP = 0x22;
+static constexpr RegAddr REG_BWDSTEP = 0x23;
+static constexpr RegAddr REG_FASTNO = 0x24;
+static constexpr RegAddr REG_LINCNT = 0x25;
+static constexpr RegAddr REG_DPISET = 0x2c;
+static constexpr RegAddr REG_STRPIXEL = 0x30;
+static constexpr RegAddr REG_ENDPIXEL = 0x32;
+static constexpr RegAddr REG_MAXWD = 0x35;
+static constexpr RegAddr REG_LPERIOD = 0x38;
+
+static constexpr RegAddr REG_0x40 = 0x40;
+static constexpr RegMask REG_0x40_HISPDFLG = 0x04;
+static constexpr RegMask REG_0x40_MOTMFLG = 0x02;
+static constexpr RegMask REG_0x40_DATAENB = 0x01;
+
+static constexpr RegMask REG_0x41_PWRBIT = 0x80;
+static constexpr RegMask REG_0x41_BUFEMPTY = 0x40;
+static constexpr RegMask REG_0x41_FEEDFSH = 0x20;
+static constexpr RegMask REG_0x41_SCANFSH = 0x10;
+static constexpr RegMask REG_0x41_HOMESNR = 0x08;
+static constexpr RegMask REG_0x41_LAMPSTS = 0x04;
+static constexpr RegMask REG_0x41_FEBUSY = 0x02;
+static constexpr RegMask REG_0x41_MOTORENB = 0x01;
+
+static constexpr RegMask REG_0x58_VSMP = 0xf8;
+static constexpr RegShift REG_0x58S_VSMP = 3;
+static constexpr RegMask REG_0x58_VSMPW = 0x07;
+static constexpr RegShift REG_0x58S_VSMPW = 0;
+
+static constexpr RegMask REG_0x59_BSMP = 0xf8;
+static constexpr RegShift REG_0x59S_BSMP = 3;
+static constexpr RegMask REG_0x59_BSMPW = 0x07;
+static constexpr RegShift REG_0x59S_BSMPW = 0;
+
+static constexpr RegMask REG_0x5A_ADCLKINV = 0x80;
+static constexpr RegMask REG_0x5A_RLCSEL = 0x40;
+static constexpr RegMask REG_0x5A_CDSREF = 0x30;
+static constexpr RegShift REG_0x5AS_CDSREF = 4;
+static constexpr RegMask REG_0x5A_RLC = 0x0f;
+static constexpr RegShift REG_0x5AS_RLC = 0;
+
+static constexpr RegMask REG_0x5E_DECSEL = 0xe0;
+static constexpr RegShift REG_0x5ES_DECSEL = 5;
+static constexpr RegMask REG_0x5E_STOPTIM = 0x1f;
+static constexpr RegShift REG_0x5ES_STOPTIM = 0;
+
+static constexpr RegMask REG_0x60_ZIMOD = 0x1f;
+static constexpr RegMask REG_0x61_Z1MOD = 0xff;
+static constexpr RegMask REG_0x62_Z1MOD = 0xff;
+
+static constexpr RegMask REG_0x63_Z2MOD = 0x1f;
+static constexpr RegMask REG_0x64_Z2MOD = 0xff;
+static constexpr RegMask REG_0x65_Z2MOD = 0xff;
+
+static constexpr RegMask REG_0x67_STEPSEL = 0xc0;
+static constexpr RegMask REG_0x67_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x67_HALFSTEP = 0x40;
+static constexpr RegMask REG_0x67_QUATERSTEP = 0x80;
+static constexpr RegMask REG_0x67_MTRPWM = 0x3f;
+
+static constexpr RegMask REG_0x68_FSTPSEL = 0xc0;
+static constexpr RegMask REG_0x68_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x68_HALFSTEP = 0x40;
+static constexpr RegMask REG_0x68_QUATERSTEP = 0x80;
+static constexpr RegMask REG_0x68_FASTPWM = 0x3f;
+
+static constexpr RegMask REG_0x6B_MULTFILM = 0x80;
+static constexpr RegMask REG_0x6B_GPOM13 = 0x40;
+static constexpr RegMask REG_0x6B_GPOM12 = 0x20;
+static constexpr RegMask REG_0x6B_GPOM11 = 0x10;
+static constexpr RegMask REG_0x6B_GPO18 = 0x02;
+static constexpr RegMask REG_0x6B_GPO17 = 0x01;
+
+static constexpr RegAddr REG_0x6B = 0x6b;
+
+static constexpr RegAddr REG_0x6C = 0x6c;
+static constexpr RegMask REG_0x6C_GPIOH = 0xff;
+static constexpr RegMask REG_0x6C_GPIOL = 0xff;
+
+static constexpr RegAddr REG_0x6D = 0x6d;
+static constexpr RegAddr REG_0x6E = 0x6e;
+static constexpr RegAddr REG_0x6F = 0x6f;
+
+static constexpr RegMask REG_0x87_LEDADD = 0x04;
+
+} // namespace gl841
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL841_REGISTERS_H
diff --git a/backend/genesys/gl843.cpp b/backend/genesys/gl843.cpp
new file mode 100644
index 0000000..f83ac8d
--- /dev/null
+++ b/backend/genesys/gl843.cpp
@@ -0,0 +1,3060 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "gl843_registers.h"
+#include "gl843.h"
+#include "test_settings.h"
+
+#include <string>
+#include <vector>
+
+namespace genesys {
+namespace gl843 {
+
+// Set address for writing data
+static void gl843_set_buffer_address(Genesys_Device* dev, uint32_t addr)
+{
+ DBG_HELPER_ARGS(dbg, "setting address to 0x%05x", addr & 0xffff);
+
+ dev->interface->write_register(0x5b, ((addr >> 8) & 0xff));
+ dev->interface->write_register(0x5c, (addr & 0xff));
+}
+
+/**
+ * compute the step multiplier used
+ */
+static int gl843_get_step_multiplier(Genesys_Register_Set* regs)
+{
+ GenesysRegister *r = sanei_genesys_get_address(regs, REG_0x9D);
+ int value = 1;
+ if (r != nullptr)
+ {
+ switch (r->value & 0x0c)
+ {
+ case 0x04:
+ value = 2;
+ break;
+ case 0x08:
+ value = 4;
+ break;
+ default:
+ value = 1;
+ }
+ }
+ DBG(DBG_io, "%s: step multiplier is %d\n", __func__, value);
+ return value;
+}
+
+/** copy sensor specific settings */
+static void gl843_setup_sensor(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs)
+{
+ DBG_HELPER(dbg);
+ for (const auto& custom_reg : sensor.custom_regs) {
+ regs->set8(custom_reg.address, custom_reg.value);
+ }
+ if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE) &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300 &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ regs->set8(0x7d, 0x90);
+ }
+
+ dev->segment_order = sensor.segment_order;
+}
+
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl843_init_registers (Genesys_Device * dev)
+{
+ // Within this function SENSOR_DEF marker documents that a register is part
+ // of the sensors definition and the actual value is set in
+ // gl843_setup_sensor().
+
+ // 0x6c, 0x6d, 0x6e, 0x6f, 0xa6, 0xa7, 0xa8, 0xa9 are defined in the Gpo sensor struct
+
+ DBG_HELPER(dbg);
+
+ dev->reg.clear();
+
+ dev->reg.init_reg(0x01, 0x00);
+ dev->reg.init_reg(0x02, 0x78);
+ dev->reg.init_reg(0x03, 0x1f);
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x03, 0x1d);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x03, 0x1c);
+ }
+
+ dev->reg.init_reg(0x04, 0x10);
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x04, 0x22);
+ }
+
+ // fine tune upon device description
+ dev->reg.init_reg(0x05, 0x80);
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x05, 0x08);
+ }
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+ sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res);
+
+ // TODO: on 8600F the windows driver turns off GAIN4 which is recommended
+ dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) {
+ dev->reg.init_reg(0x06, 0xd0);
+ }
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x06, 0xf0); /* SCANMOD=111, PWRBIT and no GAIN4 */
+ }
+
+ dev->reg.init_reg(0x08, 0x00);
+ dev->reg.init_reg(0x09, 0x00);
+ dev->reg.init_reg(0x0a, 0x00);
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x0a, 0x18);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x0a, 0x10);
+ }
+
+ // This register controls clock and RAM settings and is further modified in
+ // gl843_boot
+ dev->reg.init_reg(0x0b, 0x6a);
+
+ if (dev->model->model_id == ModelId::CANON_4400F) {
+ dev->reg.init_reg(0x0b, 0x69); // 16M only
+ }
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x0b, 0x89);
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) {
+ dev->reg.init_reg(0x0b, 0x2a);
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) {
+ dev->reg.init_reg(0x0b, 0x4a);
+ }
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x0b, 0x69);
+ }
+
+ if (dev->model->model_id != ModelId::CANON_8400F &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300)
+ {
+ dev->reg.init_reg(0x0c, 0x00);
+ }
+
+ // EXPR[0:15], EXPG[0:15], EXPB[0:15]: Exposure time settings.
+ dev->reg.init_reg(0x10, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x11, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x12, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x13, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x14, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x15, 0x00); // SENSOR_DEF
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ dev->reg.set16(REG_EXPR, 0x9c40);
+ dev->reg.set16(REG_EXPG, 0x9c40);
+ dev->reg.set16(REG_EXPB, 0x9c40);
+ }
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.set16(REG_EXPR, 0x2c09);
+ dev->reg.set16(REG_EXPG, 0x22b8);
+ dev->reg.set16(REG_EXPB, 0x10f0);
+ }
+
+ // CCD signal settings.
+ dev->reg.init_reg(0x16, 0x33); // SENSOR_DEF
+ dev->reg.init_reg(0x17, 0x1c); // SENSOR_DEF
+ dev->reg.init_reg(0x18, 0x10); // SENSOR_DEF
+
+ // EXPDMY[0:7]: Exposure time of dummy lines.
+ dev->reg.init_reg(0x19, 0x2a); // SENSOR_DEF
+
+ // Various CCD clock settings.
+ dev->reg.init_reg(0x1a, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x1c, 0x20); // SENSOR_DEF
+ dev->reg.init_reg(0x1d, 0x04); // SENSOR_DEF
+
+ dev->reg.init_reg(0x1e, 0x10);
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ dev->reg.init_reg(0x1e, 0x20);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x1e, 0xa0);
+ }
+
+ dev->reg.init_reg(0x1f, 0x01);
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x1f, 0xff);
+ }
+
+ dev->reg.init_reg(0x20, 0x10);
+ dev->reg.init_reg(0x21, 0x04);
+
+ dev->reg.init_reg(0x22, 0x10);
+ dev->reg.init_reg(0x23, 0x10);
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x22, 0xc8);
+ dev->reg.init_reg(0x23, 0xc8);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x22, 0x50);
+ dev->reg.init_reg(0x23, 0x50);
+ }
+
+ dev->reg.init_reg(0x24, 0x04);
+ dev->reg.init_reg(0x25, 0x00);
+ dev->reg.init_reg(0x26, 0x00);
+ dev->reg.init_reg(0x27, 0x00);
+ dev->reg.init_reg(0x2c, 0x02);
+ dev->reg.init_reg(0x2d, 0x58);
+ // BWHI[0:7]: high level of black and white threshold
+ dev->reg.init_reg(0x2e, 0x80);
+ // BWLOW[0:7]: low level of black and white threshold
+ dev->reg.init_reg(0x2f, 0x80);
+ dev->reg.init_reg(0x30, 0x00);
+ dev->reg.init_reg(0x31, 0x14);
+ dev->reg.init_reg(0x32, 0x27);
+ dev->reg.init_reg(0x33, 0xec);
+
+ // DUMMY: CCD dummy and optically black pixel count
+ dev->reg.init_reg(0x34, 0x24);
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x34, 0x14);
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x34, 0x3c);
+ }
+
+ // MAXWD: If available buffer size is less than 2*MAXWD words, then
+ // "buffer full" state will be set.
+ dev->reg.init_reg(0x35, 0x00);
+ dev->reg.init_reg(0x36, 0xff);
+ dev->reg.init_reg(0x37, 0xff);
+
+ // LPERIOD: Line period or exposure time for CCD or CIS.
+ dev->reg.init_reg(0x38, 0x55); // SENSOR_DEF
+ dev->reg.init_reg(0x39, 0xf0); // SENSOR_DEF
+
+ // FEEDL[0:24]: The number of steps of motor movement.
+ dev->reg.init_reg(0x3d, 0x00);
+ dev->reg.init_reg(0x3e, 0x00);
+ dev->reg.init_reg(0x3f, 0x01);
+
+ // Latch points for high and low bytes of R, G and B channels of AFE. If
+ // multiple clocks per pixel are consumed, then the setting defines during
+ // which clock the corresponding value will be read.
+ // RHI[0:4]: The latch point for high byte of R channel.
+ // RLOW[0:4]: The latch point for low byte of R channel.
+ // GHI[0:4]: The latch point for high byte of G channel.
+ // GLOW[0:4]: The latch point for low byte of G channel.
+ // BHI[0:4]: The latch point for high byte of B channel.
+ // BLOW[0:4]: The latch point for low byte of B channel.
+ dev->reg.init_reg(0x52, 0x01); // SENSOR_DEF
+ dev->reg.init_reg(0x53, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x54, 0x07); // SENSOR_DEF
+ dev->reg.init_reg(0x55, 0x0a); // SENSOR_DEF
+ dev->reg.init_reg(0x56, 0x0d); // SENSOR_DEF
+ dev->reg.init_reg(0x57, 0x10); // SENSOR_DEF
+
+ // VSMP[0:4]: The position of the image sampling pulse for AFE in cycles.
+ // VSMPW[0:2]: The length of the image sampling pulse for AFE in cycles.
+ dev->reg.init_reg(0x58, 0x1b); // SENSOR_DEF
+
+ dev->reg.init_reg(0x59, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x5a, 0x40); // SENSOR_DEF
+
+ // 0x5b-0x5c: GMMADDR[0:15] address for gamma or motor tables download
+ // SENSOR_DEF
+
+ // DECSEL[0:2]: The number of deceleration steps after touching home sensor
+ // STOPTIM[0:4]: The stop duration between change of directions in
+ // backtracking
+ dev->reg.init_reg(0x5e, 0x23);
+ if (dev->model->model_id == ModelId::CANON_4400F) {
+ dev->reg.init_reg(0x5e, 0x3f);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x5e, 0x85);
+ }
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x5e, 0x1f);
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x5e, 0x01);
+ }
+
+ //FMOVDEC: The number of deceleration steps in table 5 for auto-go-home
+ dev->reg.init_reg(0x5f, 0x01);
+ if (dev->model->model_id == ModelId::CANON_4400F) {
+ dev->reg.init_reg(0x5f, 0xf0);
+ }
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x5f, 0xf0);
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x5f, 0x01);
+ }
+
+ // Z1MOD[0:20]
+ dev->reg.init_reg(0x60, 0x00);
+ dev->reg.init_reg(0x61, 0x00);
+ dev->reg.init_reg(0x62, 0x00);
+
+ // Z2MOD[0:20]
+ dev->reg.init_reg(0x63, 0x00);
+ dev->reg.init_reg(0x64, 0x00);
+ dev->reg.init_reg(0x65, 0x00);
+
+ // STEPSEL[0:1]. Motor movement step mode selection for tables 1-3 in
+ // scanning mode.
+ // MTRPWM[0:5]. Motor phase PWM duty cycle setting for tables 1-3
+ dev->reg.init_reg(0x67, 0x7f);
+ // FSTPSEL[0:1]: Motor movement step mode selection for tables 4-5 in
+ // command mode.
+ // FASTPWM[5:0]: Motor phase PWM duty cycle setting for tables 4-5
+ dev->reg.init_reg(0x68, 0x7f);
+
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300) {
+ dev->reg.init_reg(0x67, 0x80);
+ dev->reg.init_reg(0x68, 0x80);
+ }
+
+ // FSHDEC[0:7]: The number of deceleration steps after scanning is finished
+ // (table 3)
+ dev->reg.init_reg(0x69, 0x01);
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x69, 64);
+ }
+
+ // FMOVNO[0:7] The number of acceleration or deceleration steps for fast
+ // moving (table 4)
+ dev->reg.init_reg(0x6a, 0x04);
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x69, 64);
+ }
+
+ // GPIO-related register bits
+ dev->reg.init_reg(0x6b, 0x30);
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ dev->reg.init_reg(0x6b, 0x72);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x6b, 0xb1);
+ }
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x6b, 0xf4);
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x6b, 0x31);
+ }
+
+ // 0x6c, 0x6d, 0x6e, 0x6f are set according to gpio tables. See
+ // gl843_init_gpio.
+
+ // RSH[0:4]: The position of rising edge of CCD RS signal in cycles
+ // RSL[0:4]: The position of falling edge of CCD RS signal in cycles
+ // CPH[0:4]: The position of rising edge of CCD CP signal in cycles.
+ // CPL[0:4]: The position of falling edge of CCD CP signal in cycles
+ dev->reg.init_reg(0x70, 0x01); // SENSOR_DEF
+ dev->reg.init_reg(0x71, 0x03); // SENSOR_DEF
+ dev->reg.init_reg(0x72, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x73, 0x05); // SENSOR_DEF
+
+ if (dev->model->model_id == ModelId::CANON_4400F) {
+ dev->reg.init_reg(0x70, 0x01);
+ dev->reg.init_reg(0x71, 0x03);
+ dev->reg.init_reg(0x72, 0x01);
+ dev->reg.init_reg(0x73, 0x03);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x70, 0x01);
+ dev->reg.init_reg(0x71, 0x03);
+ dev->reg.init_reg(0x72, 0x03);
+ dev->reg.init_reg(0x73, 0x04);
+ }
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x70, 0x00);
+ dev->reg.init_reg(0x71, 0x02);
+ dev->reg.init_reg(0x72, 0x02);
+ dev->reg.init_reg(0x73, 0x04);
+ }
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x70, 0x00);
+ dev->reg.init_reg(0x71, 0x02);
+ dev->reg.init_reg(0x72, 0x00);
+ dev->reg.init_reg(0x73, 0x00);
+ }
+
+ // CK1MAP[0:17], CK3MAP[0:17], CK4MAP[0:17]: CCD clock bit mapping setting.
+ dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF
+ dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF
+ dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF
+
+ // various AFE settings
+ dev->reg.init_reg(0x7d, 0x00);
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x7d, 0x20);
+ }
+
+ // GPOLED[x]: LED vs GPIO settings
+ dev->reg.init_reg(0x7e, 0x00);
+
+ // BSMPDLY, VSMPDLY
+ // LEDCNT[0:1]: Controls led blinking and its period
+ dev->reg.init_reg(0x7f, 0x00);
+
+ // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for
+ // moving in various situations.
+ dev->reg.init_reg(0x80, 0x00);
+ if (dev->model->model_id == ModelId::CANON_4400F) {
+ dev->reg.init_reg(0x80, 0x0c);
+ }
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->reg.init_reg(0x80, 0x28);
+ }
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x80, 0x50);
+ }
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x80, 0x0f);
+ }
+
+ if (dev->model->model_id != ModelId::CANON_4400F) {
+ dev->reg.init_reg(0x81, 0x00);
+ dev->reg.init_reg(0x82, 0x00);
+ dev->reg.init_reg(0x83, 0x00);
+ dev->reg.init_reg(0x84, 0x00);
+ dev->reg.init_reg(0x85, 0x00);
+ dev->reg.init_reg(0x86, 0x00);
+ }
+
+ dev->reg.init_reg(0x87, 0x00);
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ dev->reg.init_reg(0x87, 0x02);
+ }
+
+ // MTRPLS[0:7]: The width of the ADF motor trigger signal pulse.
+ if (dev->model->model_id != ModelId::CANON_8400F &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300)
+ {
+ dev->reg.init_reg(0x94, 0xff);
+ }
+
+ // 0x95-0x97: SCANLEN[0:19]: Controls when paper jam bit is set in sheetfed
+ // scanners.
+
+ // ONDUR[0:15]: The duration of PWM ON phase for LAMP control
+ // OFFDUR[0:15]: The duration of PWM OFF phase for LAMP control
+ // both of the above are in system clocks
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->reg.init_reg(0x98, 0x00);
+ dev->reg.init_reg(0x99, 0x00);
+ dev->reg.init_reg(0x9a, 0x00);
+ dev->reg.init_reg(0x9b, 0x00);
+ }
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ // TODO: move to set for scan
+ dev->reg.init_reg(0x98, 0x03);
+ dev->reg.init_reg(0x99, 0x30);
+ dev->reg.init_reg(0x9a, 0x01);
+ dev->reg.init_reg(0x9b, 0x80);
+ }
+
+ // RMADLY[0:1], MOTLAG, CMODE, STEPTIM, MULDMYLN, IFRS
+ dev->reg.init_reg(0x9d, 0x04);
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->reg.init_reg(0x9d, 0x00);
+ }
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8400F ||
+ dev->model->model_id == ModelId::CANON_8600F ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0x9d, 0x08); // sets the multiplier for slope tables
+ }
+
+
+ // SEL3INV, TGSTIME[0:2], TGWTIME[0:2]
+ if (dev->model->model_id != ModelId::CANON_8400F &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300)
+ {
+ dev->reg.init_reg(0x9e, 0x00); // SENSOR_DEF
+ }
+
+ if (dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) {
+ dev->reg.init_reg(0xa2, 0x0f);
+ }
+
+ // RFHSET[0:4]: Refresh time of SDRAM in units of 2us
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ dev->reg.init_reg(0xa2, 0x1f);
+ }
+
+ // 0xa6-0xa9: controls gpio, see gl843_gpio_init
+
+ // not documented
+ if (dev->model->model_id != ModelId::CANON_4400F &&
+ dev->model->model_id != ModelId::CANON_8400F &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300)
+ {
+ dev->reg.init_reg(0xaa, 0x00);
+ }
+
+ // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK. Not documented
+ if (dev->model->model_id != ModelId::CANON_8400F &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) {
+ dev->reg.init_reg(0xab, 0x50);
+ }
+ if (dev->model->model_id == ModelId::CANON_4400F) {
+ dev->reg.init_reg(0xab, 0x00);
+ }
+ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ // BUG: this should apply to ModelId::CANON_CANOSCAN_8600F too, but due to previous bug
+ // the 8400F case overwrote it
+ dev->reg.init_reg(0xab, 0x40);
+ }
+
+ // VRHOME[3:2], VRMOVE[3:2], VRBACK[3:2]: Vref setting of the motor driver IC
+ // for various situations.
+ if (dev->model->model_id == ModelId::CANON_8600F ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C)
+ {
+ dev->reg.init_reg(0xac, 0x00);
+ }
+
+ dev->calib_reg = dev->reg;
+
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) {
+ uint8_t data[32] = {
+ 0x8c, 0x8f, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6a, 0x73, 0x63, 0x68, 0x69, 0x65, 0x6e, 0x00,
+ };
+
+ dev->interface->write_buffer(0x3c, 0x3ff000, data, 32,
+ ScannerInterface::FLAG_SWAP_REGISTERS);
+ }
+}
+
+// Send slope table for motor movement slope_table in machine byte order
+static void gl843_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table,
+ int steps)
+{
+ DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps);
+
+ int i;
+ char msg[10000];
+
+ std::vector<uint8_t> table(steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ std::sprintf(msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++) {
+ std::sprintf (msg+strlen(msg), "%d", slope_table[i]);
+ }
+ DBG(DBG_io, "%s: %s\n", __func__, msg);
+ }
+
+ if (dev->interface->is_mock()) {
+ dev->interface->record_slope_table(table_nr, slope_table);
+ }
+
+ // slope table addresses are fixed : 0x40000, 0x48000, 0x50000, 0x58000, 0x60000
+ // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14);
+ dev->interface->write_gamma(0x28, 0x40000 + 0x8000 * table_nr, table.data(), steps * 2,
+ ScannerInterface::FLAG_SWAP_REGISTERS);
+
+ // FIXME: remove this when updating tests
+ gl843_set_buffer_address(dev, 0);
+}
+
+static void gl843_set_ad_fe(Genesys_Device* dev)
+{
+ for (const auto& reg : dev->frontend.regs) {
+ dev->interface->write_fe_register(reg.address, reg.value);
+ }
+}
+
+// Set values of analog frontend
+void CommandSetGl843::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const
+{
+ DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" :
+ set == AFE_SET ? "set" :
+ set == AFE_POWER_SAVE ? "powersave" : "huh?");
+ (void) sensor;
+ int i;
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+ dev->frontend = dev->frontend_initial;
+ dev->frontend_is_init = true;
+ }
+
+ // check analog frontend type
+ // FIXME: looks like we write to that register with initial data
+ uint8_t fe_type = dev->interface->read_register(REG_0x04) & REG_0x04_FESET;
+ if (fe_type == 2) {
+ gl843_set_ad_fe(dev);
+ return;
+ }
+ if (fe_type != 0) {
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type);
+ }
+
+ DBG(DBG_proc, "%s(): frontend reset complete\n", __func__);
+
+ for (i = 1; i <= 3; i++)
+ {
+ // FIXME: the check below is just historical artifact, we can remove it when convenient
+ if (!dev->frontend_is_init) {
+ dev->interface->write_fe_register(i, 0x00);
+ } else {
+ dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i));
+ }
+ }
+ for (const auto& reg : sensor.custom_fe_regs) {
+ dev->interface->write_fe_register(reg.address, reg.value);
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ // FIXME: the check below is just historical artifact, we can remove it when convenient
+ if (!dev->frontend_is_init) {
+ dev->interface->write_fe_register(0x20 + i, 0x00);
+ } else {
+ dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
+ }
+ }
+
+ if (dev->model->sensor_id == SensorId::CCD_KVSS080) {
+ for (i = 0; i < 3; i++)
+ {
+ // FIXME: the check below is just historical artifact, we can remove it when convenient
+ if (!dev->frontend_is_init) {
+ dev->interface->write_fe_register(0x24 + i, 0x00);
+ } else {
+ dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ // FIXME: the check below is just historical artifact, we can remove it when convenient
+ if (!dev->frontend_is_init) {
+ dev->interface->write_fe_register(0x28 + i, 0x00);
+ } else {
+ dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
+ }
+ }
+}
+
+
+static void gl843_init_motor_regs_scan(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const Motor_Profile& motor_profile,
+ unsigned int exposure,
+ unsigned scan_yres,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ MotorFlag flags)
+{
+ DBG_HELPER_ARGS(dbg, "exposure=%d, scan_yres=%d, step_type=%d, scan_lines=%d, scan_dummy=%d, "
+ "feed_steps=%d, flags=%x",
+ exposure, scan_yres, static_cast<unsigned>(motor_profile.step_type),
+ scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
+
+ int use_fast_fed, coeff;
+ unsigned int lincnt;
+ unsigned feedl, dist;
+ GenesysRegister *r;
+ uint32_t z1, z2;
+
+ /* get step multiplier */
+ unsigned step_multiplier = gl843_get_step_multiplier (reg);
+
+ use_fast_fed = 0;
+
+ if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, MotorFlag::FEED))) {
+ use_fast_fed = 1;
+ }
+
+ lincnt=scan_lines;
+ reg->set24(REG_LINCNT, lincnt);
+ DBG(DBG_io, "%s: lincnt=%d\n", __func__, lincnt);
+
+ /* compute register 02 value */
+ r = sanei_genesys_get_address(reg, REG_0x02);
+ r->value = 0x00;
+ sanei_genesys_set_motor_power(*reg, true);
+
+ if (use_fast_fed) {
+ r->value |= REG_0x02_FASTFED;
+ } else {
+ r->value &= ~REG_0x02_FASTFED;
+ }
+
+ /* in case of automatic go home, move until home sensor */
+ if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
+ r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
+ }
+
+ /* disable backtracking */
+ if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE)
+ ||(scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F)
+ ||(scan_yres>=sensor.optical_res))
+ {
+ r->value |= REG_0x02_ACDCDIS;
+ }
+
+ if (has_flag(flags, MotorFlag::REVERSE)) {
+ r->value |= REG_0x02_MTRREV;
+ } else {
+ r->value &= ~REG_0x02_MTRREV;
+ }
+
+ /* scan and backtracking slope table */
+ auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres, exposure,
+ dev->motor.base_ydpi, step_multiplier,
+ motor_profile);
+
+ gl843_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
+ gl843_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
+
+ reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier);
+ reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier);
+
+ // fast table
+ unsigned fast_yres = sanei_genesys_get_lowest_ydpi(dev);
+ auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_yres, exposure,
+ dev->motor.base_ydpi, step_multiplier,
+ motor_profile);
+ gl843_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count);
+ gl843_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
+ gl843_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count);
+
+ reg->set8(REG_FSHDEC, fast_table.steps_count / step_multiplier);
+ reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier);
+
+ /* substract acceleration distance from feedl */
+ feedl=feed_steps;
+ feedl <<= static_cast<unsigned>(motor_profile.step_type);
+
+ dist = scan_table.steps_count / step_multiplier;
+ if (use_fast_fed)
+ {
+ dist += (fast_table.steps_count / step_multiplier) * 2;
+ }
+ DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
+
+ /* get sure when don't insane value : XXX STEF XXX in this case we should
+ * fall back to single table move */
+ if (dist < feedl) {
+ feedl -= dist;
+ } else {
+ feedl = 1;
+ }
+
+ reg->set24(REG_FEEDL, feedl);
+ DBG(DBG_io, "%s: feedl=%d\n", __func__, feedl);
+
+ /* doesn't seem to matter that much */
+ sanei_genesys_calculate_zmod(use_fast_fed,
+ exposure,
+ scan_table.table,
+ scan_table.steps_count / step_multiplier,
+ feedl,
+ scan_table.steps_count / step_multiplier,
+ &z1,
+ &z2);
+ if(scan_yres>600)
+ {
+ z1=0;
+ z2=0;
+ }
+
+ reg->set24(REG_Z1MOD, z1);
+ DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
+
+ reg->set24(REG_Z2MOD, z2);
+ DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
+
+ r = sanei_genesys_get_address(reg, REG_0x1E);
+ r->value &= 0xf0; /* 0 dummy lines */
+ r->value |= scan_dummy; /* dummy lines */
+
+ reg->set8_mask(REG_0x67, static_cast<unsigned>(motor_profile.step_type) << REG_0x67S_STEPSEL, 0xc0);
+ reg->set8_mask(REG_0x68, static_cast<unsigned>(motor_profile.step_type) << REG_0x68S_FSTPSEL, 0xc0);
+
+ // steps for STOP table
+ reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier);
+
+ /* Vref XXX STEF XXX : optical divider or step type ? */
+ r = sanei_genesys_get_address (reg, 0x80);
+ if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE))
+ {
+ r->value = 0x50;
+ coeff = sensor.get_hwdpi_divisor_for_dpi(scan_yres);
+ if (dev->model->motor_id == MotorId::KVSS080) {
+ if(coeff>=1)
+ {
+ r->value |= 0x05;
+ }
+ }
+ else {
+ switch(coeff)
+ {
+ case 4:
+ r->value |= 0x0a;
+ break;
+ case 2:
+ r->value |= 0x0f;
+ break;
+ case 1:
+ r->value |= 0x0f;
+ break;
+ }
+ }
+ }
+}
+
+
+/** @brief setup optical related registers
+ * start and pixels are expressed in optical sensor resolution coordinate
+ * space.
+ * @param dev device to use
+ * @param reg registers to set up
+ * @param exposure exposure time to use
+ * @param used_res scanning resolution used, may differ from
+ * scan's one
+ * @param start logical start pixel coordinate
+ * @param pixels logical number of pixels to use
+ * @param channels number of color channles used (1 or 3)
+ * @param depth bit depth of the scan (1, 8 or 16 bits)
+ * @param ccd_size_divisor true specifies how much x coordinates must be shrunk
+ * @param color_filter to choose the color channel used in gray scans
+ * @param flags to drive specific settings such no calibration, XPA use ...
+ */
+static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, unsigned int exposure,
+ const ScanSession& session)
+{
+ DBG_HELPER_ARGS(dbg, "exposure=%d", exposure);
+ unsigned int dpihw;
+ unsigned int tgtime; /**> exposure time multiplier */
+ GenesysRegister *r;
+
+ /* tgtime */
+ tgtime = exposure / 65536 + 1;
+ DBG(DBG_io2, "%s: tgtime=%d\n", __func__, tgtime);
+
+ // to manage high resolution device while keeping good low resolution scanning speed, we make
+ // hardware dpi vary
+ dpihw = sensor.get_register_hwdpi(session.output_resolution);
+ DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw);
+
+ /* sensor parameters */
+ gl843_setup_sensor(dev, sensor, reg);
+
+ // resolution is divided according to CKSEL
+ unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
+ DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel);
+
+ dev->cmd_set->set_fe(dev, sensor, AFE_SET);
+
+ /* enable shading */
+ regs_set_optical_off(dev->model->asic_type, *reg);
+ r = sanei_genesys_get_address (reg, REG_0x01);
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION ||
+ (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE)))
+ {
+ r->value &= ~REG_0x01_DVDSET;
+ } else {
+ r->value |= REG_0x01_DVDSET;
+ }
+
+ bool use_shdarea = dpihw > 600;
+ if (dev->model->model_id == ModelId::CANON_4400F) {
+ use_shdarea = session.params.xres <= 600;
+ } else if (dev->model->model_id == ModelId::CANON_8400F) {
+ use_shdarea = session.params.xres <= 400;
+ }
+ if (use_shdarea) {
+ r->value |= REG_0x01_SHDAREA;
+ } else {
+ r->value &= ~REG_0x01_SHDAREA;
+ }
+
+ r = sanei_genesys_get_address (reg, REG_0x03);
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ r->value |= REG_0x03_AVEENB;
+ } else {
+ r->value &= ~REG_0x03_AVEENB;
+ }
+
+ // FIXME: we probably don't need to set exposure to registers at this point. It was this way
+ // before a refactor.
+ sanei_genesys_set_lamp_power(dev, sensor, *reg,
+ !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));
+
+ /* select XPA */
+ r->value &= ~REG_0x03_XPASEL;
+ if (has_flag(session.params.flags, ScanFlag::USE_XPA)) {
+ r->value |= REG_0x03_XPASEL;
+ }
+ reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA);
+
+ /* BW threshold */
+ r = sanei_genesys_get_address(reg, REG_0x2E);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address(reg, REG_0x2F);
+ r->value = dev->settings.threshold;
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address(reg, REG_0x04);
+ switch (session.params.depth) {
+ case 8:
+ r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG_0x04_LINEART;
+ r->value |= REG_0x04_BITSET;
+ break;
+ }
+
+ r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);
+ if (session.params.channels == 1)
+ {
+ switch (session.params.color_filter)
+ {
+ case ColorFilter::RED:
+ r->value |= 0x14;
+ break;
+ case ColorFilter::BLUE:
+ r->value |= 0x1c;
+ break;
+ case ColorFilter::GREEN:
+ r->value |= 0x18;
+ break;
+ default:
+ break; // should not happen
+ }
+ } else {
+ switch (dev->frontend.layout.type) {
+ case FrontendType::WOLFSON:
+ r->value |= 0x10; // pixel by pixel
+ break;
+ case FrontendType::ANALOG_DEVICES:
+ r->value |= 0x20; // slow color pixel by pixel
+ break;
+ default:
+ throw SaneException("Invalid frontend type %d",
+ static_cast<unsigned>(dev->frontend.layout.type));
+ }
+ }
+
+ sanei_genesys_set_dpihw(*reg, sensor, dpihw);
+
+ if (should_enable_gamma(session, sensor)) {
+ reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
+ } else {
+ reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
+ }
+
+ unsigned dpiset = session.output_resolution * session.ccd_size_divisor *
+ ccd_pixels_per_system_pixel;
+
+ if (sensor.dpiset_override != 0) {
+ dpiset = sensor.dpiset_override;
+ }
+ reg->set16(REG_DPISET, dpiset);
+ DBG(DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
+
+ reg->set16(REG_STRPIXEL, session.pixel_startx);
+ reg->set16(REG_ENDPIXEL, session.pixel_endx);
+
+ /* MAXWD is expressed in 2 words unit */
+ /* nousedspace = (mem_bank_range * 1024 / 256 -1 ) * 4; */
+ // BUG: the division by ccd_size_divisor likely does not make sense
+ reg->set24(REG_MAXWD, (session.output_line_bytes / session.ccd_size_divisor) >> 1);
+
+ reg->set16(REG_LPERIOD, exposure / tgtime);
+ DBG(DBG_io2, "%s: exposure used=%d\n", __func__, exposure/tgtime);
+
+ r = sanei_genesys_get_address (reg, REG_DUMMY);
+ r->value = sensor.dummy_pixel;
+}
+
+void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const
+{
+ DBG_HELPER(dbg);
+ session.assert_computed();
+
+ int exposure;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+
+ dummy = 0;
+ if (dev->model->model_id == ModelId::CANON_4400F && session.params.yres == 1200) {
+ dummy = 1;
+ }
+
+ /* slope_dpi */
+ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */
+ if (dev->model->is_cis)
+ slope_dpi = session.params.yres * session.params.channels;
+ else
+ slope_dpi = session.params.yres;
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ /* scan_step_type */
+ exposure = sensor.exposure_lperiod;
+ if (exposure < 0) {
+ throw std::runtime_error("Exposure not defined in sensor definition");
+ }
+ const auto& motor_profile = sanei_genesys_get_motor_profile(*gl843_motor_profiles,
+ dev->model->motor_id,
+ exposure);
+
+ DBG(DBG_info, "%s : exposure=%d pixels\n", __func__, exposure);
+ DBG(DBG_info, "%s : scan_step_type=%d\n", __func__,
+ static_cast<unsigned>(motor_profile.step_type));
+
+ // now _LOGICAL_ optical values used are known, setup registers
+ gl843_init_optical_regs_scan(dev, sensor, reg, exposure, session);
+
+ /*** motor parameters ***/
+ MotorFlag mflags = MotorFlag::NONE;
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) {
+ mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE;
+ }
+ if (has_flag(session.params.flags, ScanFlag::FEEDING)) {
+ mflags |= MotorFlag::FEED;
+ }
+ if (has_flag(session.params.flags, ScanFlag::USE_XPA)) {
+ mflags |= MotorFlag::USE_XPA;
+ }
+ if (has_flag(session.params.flags, ScanFlag::REVERSE)) {
+ mflags |= MotorFlag::REVERSE;
+ }
+
+ unsigned scan_lines = dev->model->is_cis ? session.output_line_count * session.params.channels
+ : session.output_line_count;
+
+ gl843_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure, slope_dpi,
+ scan_lines, dummy, session.params.starty, mflags);
+
+ dev->read_buffer.clear();
+ dev->read_buffer.alloc(session.buffer_size_read);
+
+ build_image_pipeline(dev, session);
+
+ dev->read_active = true;
+
+ dev->session = session;
+
+ dev->total_bytes_read = 0;
+ dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines;
+
+ DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read);
+}
+
+ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const
+{
+ DBG_HELPER(dbg);
+ debug_dump(DBG_info, settings);
+
+ int start;
+
+ /* we have 2 domains for ccd: xres below or above half ccd max dpi */
+ unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(settings.xres);
+
+ if (settings.scan_method == ScanMethod::TRANSPARENCY ||
+ settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ start = static_cast<int>(dev->model->x_offset_ta);
+ } else {
+ start = static_cast<int>(dev->model->x_offset);
+ }
+
+ if (dev->model->model_id == ModelId::CANON_8600F)
+ {
+ // FIXME: this is probably just an artifact of a bug elsewhere
+ start /= ccd_size_divisor;
+ }
+
+ start += static_cast<int>(settings.tl_x);
+ start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = settings.xres;
+ session.params.yres = settings.yres;
+ session.params.startx = start; // not used
+ session.params.starty = 0; // not used
+ session.params.pixels = settings.pixels;
+ session.params.requested_pixels = settings.requested_pixels;
+ session.params.lines = settings.lines;
+ session.params.depth = settings.depth;
+ session.params.channels = settings.get_channels();
+ session.params.scan_method = settings.scan_method;
+ session.params.scan_mode = settings.scan_mode;
+ session.params.color_filter = settings.color_filter;
+ session.params.flags = ScanFlag::NONE;
+
+ compute_session(dev, session, sensor);
+
+ return session;
+}
+
+/**
+ * for fast power saving methods only, like disabling certain amplifiers
+ * @param dev device to use
+ * @param enable true to set inot powersaving
+ * */
+void CommandSetGl843::save_power(Genesys_Device* dev, bool enable) const
+{
+ DBG_HELPER_ARGS(dbg, "enable = %d", enable);
+
+ // switch KV-SS080 lamp off
+ if (dev->model->gpio_id == GpioId::KVSS080) {
+ uint8_t val = dev->interface->read_register(REG_0x6C);
+ if (enable) {
+ val &= 0xef;
+ } else {
+ val |= 0x10;
+ }
+ dev->interface->write_register(REG_0x6C, val);
+ }
+}
+
+void CommandSetGl843::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
+{
+ (void) dev;
+ DBG_HELPER_ARGS(dbg, "delay = %d", delay);
+}
+
+static bool gl843_get_paper_sensor(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+
+ uint8_t val = dev->interface->read_register(REG_0x6D);
+
+ return (val & 0x1) == 0;
+}
+
+void CommandSetGl843::eject_document(Genesys_Device* dev) const
+{
+ (void) dev;
+ DBG_HELPER(dbg);
+}
+
+
+void CommandSetGl843::load_document(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ (void) dev;
+}
+
+/**
+ * detects end of document and adjust current scan
+ * to take it into account
+ * used by sheetfed scanners
+ */
+void CommandSetGl843::detect_document_end(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ bool paper_loaded = gl843_get_paper_sensor(dev);
+
+ /* sheetfed scanner uses home sensor as paper present */
+ if (dev->document && !paper_loaded) {
+ DBG(DBG_info, "%s: no more document\n", __func__);
+ dev->document = false;
+
+ unsigned scanned_lines = 0;
+ catch_all_exceptions(__func__, [&](){ sanei_genesys_read_scancnt(dev, &scanned_lines); });
+
+ std::size_t output_lines = dev->session.output_line_count;
+
+ std::size_t offset_lines = static_cast<std::size_t>(
+ (dev->model->post_scan * dev->session.params.yres) / MM_PER_INCH);
+
+ std::size_t scan_end_lines = scanned_lines + offset_lines;
+
+ std::size_t remaining_lines = dev->get_pipeline_source().remaining_bytes() /
+ dev->session.output_line_bytes_raw;
+
+ DBG(DBG_io, "%s: scanned_lines=%u\n", __func__, scanned_lines);
+ DBG(DBG_io, "%s: scan_end_lines=%zu\n", __func__, scan_end_lines);
+ DBG(DBG_io, "%s: output_lines=%zu\n", __func__, output_lines);
+ DBG(DBG_io, "%s: remaining_lines=%zu\n", __func__, remaining_lines);
+
+ if (scan_end_lines > output_lines) {
+ auto skip_lines = scan_end_lines - output_lines;
+
+ if (remaining_lines > skip_lines) {
+ DBG(DBG_io, "%s: skip_lines=%zu\n", __func__, skip_lines);
+
+ remaining_lines -= skip_lines;
+ dev->get_pipeline_source().set_remaining_bytes(remaining_lines *
+ dev->session.output_line_bytes_raw);
+ dev->total_bytes_to_read -= skip_lines * dev->session.output_line_bytes_requested;
+ }
+ }
+ }
+}
+
+// enables or disables XPA slider motor
+void gl843_set_xpa_motor_power(Genesys_Device* dev, Genesys_Register_Set& regs, bool set)
+{
+ DBG_HELPER(dbg);
+ uint8_t val;
+
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+
+ if (set) {
+ val = dev->interface->read_register(0x6c);
+ val &= ~(REG_0x6C_GPIO16 | REG_0x6C_GPIO13);
+ if (dev->session.output_resolution >= 2400) {
+ val &= ~REG_0x6C_GPIO10;
+ }
+ dev->interface->write_register(0x6c, val);
+
+ val = dev->interface->read_register(0xa9);
+ val |= REG_0xA9_GPO30;
+ val &= ~REG_0xA9_GPO29;
+ dev->interface->write_register(0xa9, val);
+ } else {
+ val = dev->interface->read_register(0x6c);
+ val |= REG_0x6C_GPIO16 | REG_0x6C_GPIO13;
+ dev->interface->write_register(0x6c, val);
+
+ val = dev->interface->read_register(0xa9);
+ val &= ~REG_0xA9_GPO30;
+ val |= REG_0xA9_GPO29;
+ dev->interface->write_register(0xa9, val);
+ }
+ } else if (dev->model->model_id == ModelId::CANON_8600F) {
+ if (set) {
+ val = dev->interface->read_register(REG_0x6C);
+ val &= ~REG_0x6C_GPIO14;
+ if (dev->session.output_resolution >= 2400) {
+ val |= REG_0x6C_GPIO10;
+ }
+ dev->interface->write_register(REG_0x6C, val);
+
+ val = dev->interface->read_register(REG_0xA6);
+ val |= REG_0xA6_GPIO17;
+ val &= ~REG_0xA6_GPIO23;
+ dev->interface->write_register(REG_0xA6, val);
+ } else {
+ val = dev->interface->read_register(REG_0x6C);
+ val |= REG_0x6C_GPIO14;
+ val &= ~REG_0x6C_GPIO10;
+ dev->interface->write_register(REG_0x6C, val);
+
+ val = dev->interface->read_register(REG_0xA6);
+ val &= ~REG_0xA6_GPIO17;
+ val &= ~REG_0xA6_GPIO23;
+ dev->interface->write_register(REG_0xA6, val);
+ }
+ } else if (dev->model->model_id == ModelId::HP_SCANJET_G4050) {
+ if (set) {
+ // set MULTFILM et GPOADF
+ val = dev->interface->read_register(REG_0x6B);
+ val |=REG_0x6B_MULTFILM|REG_0x6B_GPOADF;
+ dev->interface->write_register(REG_0x6B, val);
+
+ val = dev->interface->read_register(REG_0x6C);
+ val &= ~REG_0x6C_GPIO15;
+ dev->interface->write_register(REG_0x6C, val);
+
+ /* Motor power ? No move at all without this one */
+ val = dev->interface->read_register(REG_0xA6);
+ val |= REG_0xA6_GPIO20;
+ dev->interface->write_register(REG_0xA6, val);
+
+ val = dev->interface->read_register(REG_0xA8);
+ val &= ~REG_0xA8_GPO27;
+ dev->interface->write_register(REG_0xA8, val);
+
+ val = dev->interface->read_register(REG_0xA9);
+ val |= REG_0xA9_GPO32|REG_0xA9_GPO31;
+ dev->interface->write_register(REG_0xA9, val);
+ } else {
+ // unset GPOADF
+ val = dev->interface->read_register(REG_0x6B);
+ val &= ~REG_0x6B_GPOADF;
+ dev->interface->write_register(REG_0x6B, val);
+
+ val = dev->interface->read_register(REG_0xA8);
+ val |= REG_0xA8_GPO27;
+ dev->interface->write_register(REG_0xA8, val);
+
+ val = dev->interface->read_register(REG_0xA9);
+ val &= ~REG_0xA9_GPO31;
+ dev->interface->write_register(REG_0xA9, val);
+ }
+ }
+ regs.state.is_xpa_motor_on = set;
+}
+
+
+/** @brief light XPA lamp
+ * toggle gpios to switch off regular lamp and light on the
+ * XPA light
+ * @param dev device to set up
+ */
+static void gl843_set_xpa_lamp_power(Genesys_Device* dev, bool set)
+{
+ DBG_HELPER(dbg);
+
+ struct LampSettings {
+ ModelId model_id;
+ ScanMethod scan_method;
+ GenesysRegisterSettingSet regs_on;
+ GenesysRegisterSettingSet regs_off;
+ };
+
+ // FIXME: BUG: we're not clearing the registers to the previous state when returning back when
+ // turning off the lamp
+ LampSettings settings[] = {
+ { ModelId::CANON_8400F, ScanMethod::TRANSPARENCY, {
+ { 0xa6, 0x34, 0xf4 },
+ }, {
+ { 0xa6, 0x40, 0x70 },
+ }
+ },
+ { ModelId::CANON_8400F, ScanMethod::TRANSPARENCY_INFRARED, {
+ { 0x6c, 0x40, 0x40 },
+ { 0xa6, 0x01, 0xff },
+ }, {
+ { 0x6c, 0x00, 0x40 },
+ { 0xa6, 0x00, 0xff },
+ }
+ },
+ { ModelId::CANON_8600F, ScanMethod::TRANSPARENCY, {
+ { 0xa6, 0x34, 0xf4 },
+ { 0xa7, 0xe0, 0xe0 },
+ }, {
+ { 0xa6, 0x40, 0x70 },
+ }
+ },
+ { ModelId::CANON_8600F, ScanMethod::TRANSPARENCY_INFRARED, {
+ { 0xa6, 0x00, 0xc0 },
+ { 0xa7, 0xe0, 0xe0 },
+ { 0x6c, 0x80, 0x80 },
+ }, {
+ { 0xa6, 0x00, 0xc0 },
+ { 0x6c, 0x00, 0x80 },
+ }
+ },
+ { ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY, {
+ }, {
+ { 0xa6, 0x40, 0x70 }, // BUG: remove this cleanup write, it was enabled by accident
+ }
+ },
+ { ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY_INFRARED, {
+ { 0xa8, 0x07, 0x07 },
+ }, {
+ { 0xa8, 0x00, 0x07 },
+ }
+ },
+ { ModelId::PLUSTEK_OPTICFILM_7300, ScanMethod::TRANSPARENCY, {}, {} },
+ { ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY, {}, {} },
+ { ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY_INFRARED, {
+ { 0xa8, 0x07, 0x07 },
+ }, {
+ { 0xa8, 0x00, 0x07 },
+ }
+ },
+ };
+
+ for (const auto& setting : settings) {
+ if (setting.model_id == dev->model->model_id &&
+ setting.scan_method == dev->settings.scan_method)
+ {
+ apply_reg_settings_to_device(*dev, set ? setting.regs_on : setting.regs_off);
+ return;
+ }
+ }
+
+ // BUG: we're currently calling the function in shut down path of regular lamp
+ if (set) {
+ throw SaneException("Unexpected code path entered");
+ }
+
+ GenesysRegisterSettingSet regs = {
+ { 0xa6, 0x40, 0x70 },
+ };
+ apply_reg_settings_to_device(*dev, regs);
+ // TODO: throw exception when we're only calling this function in error return path
+ // throw SaneException("Could not find XPA lamp settings");
+}
+
+// Send the low-level scan command
+void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, bool start_motor) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+
+ /* set up GPIO for scan */
+ switch(dev->model->gpio_id) {
+ /* KV case */
+ case GpioId::KVSS080:
+ dev->interface->write_register(REG_0xA9, 0x00);
+ dev->interface->write_register(REG_0xA6, 0xf6);
+ // blinking led
+ dev->interface->write_register(0x7e, 0x04);
+ break;
+ case GpioId::G4050:
+ dev->interface->write_register(REG_0xA7, 0xfe);
+ dev->interface->write_register(REG_0xA8, 0x3e);
+ dev->interface->write_register(REG_0xA9, 0x06);
+ if ((reg->get8(0x05) & REG_0x05_DPIHW) == REG_0x05_DPIHW_600) {
+ dev->interface->write_register(REG_0x6C, 0x20);
+ dev->interface->write_register(REG_0xA6, 0x44);
+ } else {
+ dev->interface->write_register(REG_0x6C, 0x60);
+ dev->interface->write_register(REG_0xA6, 0x46);
+ }
+
+ if (reg->state.is_xpa_on && reg->state.is_lamp_on) {
+ gl843_set_xpa_lamp_power(dev, true);
+ }
+
+ if (reg->state.is_xpa_on) {
+ gl843_set_xpa_motor_power(dev, *reg, true);
+ }
+
+ // blinking led
+ dev->interface->write_register(REG_0x7E, 0x01);
+ break;
+ case GpioId::CANON_8400F:
+ case GpioId::CANON_8600F:
+ if (reg->state.is_xpa_on && reg->state.is_lamp_on) {
+ gl843_set_xpa_lamp_power(dev, true);
+ }
+ if (reg->state.is_xpa_on) {
+ gl843_set_xpa_motor_power(dev, *reg, true);
+ }
+ break;
+ case GpioId::PLUSTEK_OPTICFILM_7200I:
+ case GpioId::PLUSTEK_OPTICFILM_7300:
+ case GpioId::PLUSTEK_OPTICFILM_7500I: {
+ if (reg->state.is_xpa_on && reg->state.is_lamp_on) {
+ gl843_set_xpa_lamp_power(dev, true);
+ }
+ break;
+ }
+ case GpioId::CANON_4400F:
+ default:
+ break;
+ }
+
+ // clear scan and feed count
+ dev->interface->write_register(REG_0x0D, REG_0x0D_CLRLNCNT | REG_0x0D_CLRMCNT);
+
+ // enable scan and motor
+ uint8_t val = dev->interface->read_register(REG_0x01);
+ val |= REG_0x01_SCAN;
+ dev->interface->write_register(REG_0x01, val);
+
+ scanner_start_action(*dev, start_motor);
+
+ if (reg->state.is_motor_on) {
+ dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
+ }
+ if (reg->state.is_xpa_motor_on) {
+ dev->advance_head_pos_by_session(ScanHeadId::SECONDARY);
+ }
+}
+
+
+// Send the stop scan command
+void CommandSetGl843::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
+ bool check_stop) const
+{
+ DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop);
+
+ // post scan gpio
+ dev->interface->write_register(0x7e, 0x00);
+
+ // turn off XPA lamp if needed
+ // BUG: the if condition below probably shouldn't be enabled when XPA is off
+ if (reg->state.is_xpa_on || reg->state.is_lamp_on) {
+ gl843_set_xpa_lamp_power(dev, false);
+ }
+
+ if (!dev->model->is_sheetfed) {
+ scanner_stop_action(*dev);
+ }
+}
+
+/** @brief Moves the slider to the home (top) position slowly
+ * */
+void CommandSetGl843::move_back_home(Genesys_Device* dev, bool wait_until_home) const
+{
+ scanner_move_back_home(*dev, wait_until_home);
+}
+
+// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi
+// from very top of scanner
+void CommandSetGl843::search_start_position(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ Genesys_Register_Set local_reg;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ local_reg = dev->reg;
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ // FIXME: the current approach of doing search only for one resolution does not work on scanners
+ // whith employ different sensors with potentially different settings.
+ const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method);
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0; // we should give a small offset here - ~60 steps
+ session.params.pixels = 600;
+ session.params.lines = dev->model->search_lines;
+ session.params.depth = 8;
+ session.params.channels = 1;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::GREEN;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::DISABLE_BUFFER_FULL_MOVE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ // send to scanner
+ dev->interface->write_registers(local_reg);
+
+ dev->cmd_set->begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_start_position");
+ end_scan(dev, &local_reg, true);
+ dev->reg = local_reg;
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ Image image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw);
+
+ scanner_stop_action_no_move(*dev, local_reg);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl843_search_position.pnm", image);
+ }
+
+ dev->cmd_set->end_scan(dev, &local_reg, true);
+
+ /* update regs to copy ASIC internal state */
+ dev->reg = local_reg;
+
+ for (auto& sensor_update :
+ sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method))
+ {
+ sanei_genesys_search_reference_point(dev, sensor_update, image.get_row_ptr(0), 0, dpi,
+ pixels, dev->model->search_lines);
+ }
+}
+
+// sets up register for coarse gain calibration
+// todo: check it for scanners using it
+void CommandSetGl843::init_regs_for_coarse_calibration(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+
+ ScanFlag flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) {
+ flags |= ScanFlag::USE_XPA;
+ }
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel();
+ session.params.lines = 20;
+ session.params.depth = 16;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = flags;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
+ sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres);
+
+ dev->interface->write_registers(regs);
+}
+
+// init registers for shading calibration shading calibration is done at dpihw
+void CommandSetGl843::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int move, resolution, dpihw, factor;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ dev->calib_channels = 3;
+
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ dev->calib_lines = dev->model->shading_ta_lines;
+ } else {
+ dev->calib_lines = dev->model->shading_lines;
+ }
+
+ dpihw = sensor.get_logical_hwdpi(dev->settings.xres);
+ factor=sensor.optical_res/dpihw;
+ resolution=dpihw;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, dev->calib_channels,
+ dev->settings.scan_method);
+
+ if ((dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
+ dev->model->model_id == ModelId::CANON_8600F &&
+ dev->settings.xres == 4800)
+ {
+ float offset = static_cast<float>(dev->model->x_offset_ta);
+ offset /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
+ offset = static_cast<float>((offset * calib_sensor.optical_res) / MM_PER_INCH);
+
+ float size = static_cast<float>(dev->model->x_size_ta);
+ size /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
+ size = static_cast<float>((size * calib_sensor.optical_res) / MM_PER_INCH);
+
+ dev->calib_pixels_offset = static_cast<std::size_t>(offset);
+ dev->calib_pixels = static_cast<std::size_t>(size);
+ }
+ else
+ {
+ dev->calib_pixels_offset = 0;
+ dev->calib_pixels = calib_sensor.sensor_pixels / factor;
+ }
+
+ dev->calib_resolution = resolution;
+
+ ScanFlag flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::DISABLE_BUFFER_FULL_MOVE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ // note: move_to_ta() function has already been called and the sensor is at the
+ // transparency adapter
+ move = static_cast<int>(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta);
+ flags |= ScanFlag::USE_XPA;
+ } else {
+ move = static_cast<int>(dev->model->y_offset_calib_white);
+ }
+
+ move = static_cast<int>((move * resolution) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = dev->calib_pixels_offset;
+ session.params.starty = move;
+ session.params.pixels = dev->calib_pixels;
+ session.params.lines = dev->calib_lines;
+ session.params.depth = 16;
+ session.params.channels = dev->calib_channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = flags;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ // the pixel number may be updated to conform to scanner constraints
+ dev->calib_pixels = session.output_pixels;
+
+ dev->calib_session = session;
+ dev->calib_total_bytes_to_read = session.output_total_bytes_raw;
+
+ dev->interface->write_registers(regs);
+}
+
+/** @brief set up registers for the actual scan
+ */
+void CommandSetGl843::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ float move;
+ int move_dpi;
+ float start;
+
+ debug_dump(DBG_info, dev->settings);
+
+ move_dpi = dev->motor.base_ydpi;
+
+ ScanFlag flags = ScanFlag::NONE;
+
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ // note: move_to_ta() function has already been called and the sensor is at the
+ // transparency adapter
+ if (dev->ignore_offsets) {
+ move = 0;
+ } else {
+ move = static_cast<float>(dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta);
+ }
+ flags |= ScanFlag::USE_XPA;
+ } else {
+ if (dev->ignore_offsets) {
+ move = 0;
+ } else {
+ move = static_cast<float>(dev->model->y_offset);
+ }
+ }
+
+ move += static_cast<float>(dev->settings.tl_y);
+ move = static_cast<float>((move * move_dpi) / MM_PER_INCH);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ /* start */
+ if (dev->settings.scan_method==ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ start = static_cast<float>(dev->model->x_offset_ta);
+ } else {
+ start = static_cast<float>(dev->model->x_offset);
+ }
+
+ if (dev->model->model_id == ModelId::CANON_8400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ // FIXME: this is probably just an artifact of a bug elsewhere
+ start /= sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres);
+ }
+
+ start = static_cast<float>(start + dev->settings.tl_x);
+ start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = static_cast<unsigned>(start);
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = dev->settings.pixels;
+ session.params.requested_pixels = dev->settings.requested_pixels;
+ session.params.lines = dev->settings.lines;
+ session.params.depth = dev->settings.depth;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = flags;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &dev->reg, session);
+}
+
+/**
+ * This function sends gamma tables to ASIC
+ */
+void CommandSetGl843::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ int size;
+ int i;
+
+ size = 256;
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ std::vector<uint8_t> gamma(size * 2 * 3);
+
+ std::vector<uint16_t> rgamma = get_gamma_table(dev, sensor, GENESYS_RED);
+ std::vector<uint16_t> ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN);
+ std::vector<uint16_t> bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE);
+
+ // copy sensor specific's gamma tables
+ for (i = 0; i < size; i++) {
+ gamma[i * 2 + size * 0 + 0] = rgamma[i] & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (rgamma[i] >> 8) & 0xff;
+ gamma[i * 2 + size * 2 + 0] = ggamma[i] & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (ggamma[i] >> 8) & 0xff;
+ gamma[i * 2 + size * 4 + 0] = bgamma[i] & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff;
+ }
+
+ dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3,
+ ScannerInterface::FLAG_SWAP_REGISTERS);
+}
+
+/* this function does the led calibration by scanning one line of the calibration
+ area below scanner's top on white strip.
+
+-needs working coarse/gain
+*/
+SensorExposure CommandSetGl843::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int avg[3], avga, avge;
+ int turn;
+ uint16_t expr, expg, expb;
+
+ // offset calibration is always done in color mode
+ unsigned channels = 3;
+
+ // take a copy, as we're going to modify exposure
+ auto calib_sensor = sanei_genesys_find_sensor(dev, sensor.optical_res, channels,
+ dev->settings.scan_method);
+
+ num_pixels = (calib_sensor.sensor_pixels * calib_sensor.optical_res) / calib_sensor.optical_res;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ ScanSession session;
+ session.params.xres = calib_sensor.sensor_pixels;
+ session.params.yres = dev->motor.base_ydpi;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ dev->interface->write_registers(regs);
+
+/*
+ we try to get equal bright leds here:
+
+ loop:
+ average per color
+ adjust exposure times
+ */
+
+ expr = calib_sensor.exposure.red;
+ expg = calib_sensor.exposure.green;
+ expb = calib_sensor.exposure.blue;
+
+ turn = 0;
+
+ bool acceptable = false;
+ do
+ {
+
+ calib_sensor.exposure.red = expr;
+ calib_sensor.exposure.green = expg;
+ calib_sensor.exposure.blue = expb;
+
+ regs_set_exposure(dev->model->asic_type, regs, calib_sensor.exposure);
+
+ dev->interface->write_registers(regs);
+
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("led_calibration");
+ move_back_home(dev, true);
+ return calib_sensor.exposure;
+ }
+
+ auto image = read_unshuffled_image_from_scanner(dev, session,
+ session.output_total_bytes_raw);
+ scanner_stop_action_no_move(*dev, regs);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl843_led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, image);
+ }
+
+ acceptable = true;
+
+ for (unsigned ch = 0; ch < channels; ch++) {
+ avg[ch] = 0;
+ for (std::size_t x = 0; x < image.get_width(); x++) {
+ avg[ch] += image.get_raw_channel(x, 0, ch);
+ }
+ avg[ch] /= image.get_width();
+ }
+
+ DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
+
+ acceptable = true;
+
+ if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
+ avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
+ avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
+ acceptable = false;
+
+ if (!acceptable)
+ {
+ avga = (avg[0] + avg[1] + avg[2]) / 3;
+ expr = (expr * avga) / avg[0];
+ expg = (expg * avga) / avg[1];
+ expb = (expb * avga) / avg[2];
+/*
+ keep the resulting exposures below this value.
+ too long exposure drives the ccd into saturation.
+ we may fix this by relying on the fact that
+ we get a striped scan without shading, by means of
+ statistical calculation
+*/
+ avge = (expr + expg + expb) / 3;
+
+ /* don't overflow max exposure */
+ if (avge > 3000)
+ {
+ expr = (expr * 2000) / avge;
+ expg = (expg * 2000) / avge;
+ expb = (expb * 2000) / avge;
+ }
+ if (avge < 50)
+ {
+ expr = (expr * 50) / avge;
+ expg = (expg * 50) / avge;
+ expb = (expb * 50) / avge;
+ }
+
+ }
+ scanner_stop_action(*dev);
+
+ turn++;
+
+ }
+ while (!acceptable && turn < 100);
+
+ DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, expr, expg, expb);
+
+ move_back_home(dev, true);
+
+ return calib_sensor.exposure;
+}
+
+
+
+/**
+ * average dark pixels of a 8 bits scan of a given channel
+ */
+static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
+{
+ auto channels = get_pixel_channels(image.get_format());
+
+ unsigned avg[3];
+
+ // computes average values on black margin
+ for (unsigned ch = 0; ch < channels; ch++) {
+ avg[ch] = 0;
+ unsigned count = 0;
+ // FIXME: start with the second line because the black pixels often have noise on the first
+ // line; the cause is probably incorrectly cleaned up previous scan
+ for (std::size_t y = 1; y < image.get_height(); y++) {
+ for (unsigned j = 0; j < black; j++) {
+ avg[ch] += image.get_raw_channel(j, y, ch);
+ count++;
+ }
+ }
+ if (count > 0) {
+ avg[ch] /= count;
+ }
+ DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
+ }
+ DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
+ return avg[channel];
+}
+
+/** @brief calibrate AFE offset
+ * Iterate doing scans at target dpi until AFE offset if correct. One
+ * color line is scanned at a time. Scanning head doesn't move.
+ * @param dev device to calibrate
+ */
+void CommandSetGl843::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+
+ if (dev->frontend.layout.type != FrontendType::WOLFSON)
+ return;
+
+ unsigned channels;
+ int pass, resolution, lines;
+ int topavg[3], bottomavg[3], avg[3];
+ int top[3], bottom[3], black_pixels, pixels, factor, dpihw;
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ lines = 8;
+
+ // compute divider factor to compute final pixels number
+ dpihw = sensor.get_logical_hwdpi(dev->settings.xres);
+ factor = sensor.optical_res / dpihw;
+ resolution = dpihw;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
+ dev->settings.scan_method);
+
+ int target_pixels = calib_sensor.sensor_pixels / factor;
+ int start_pixel = 0;
+ black_pixels = calib_sensor.black_pixels / factor;
+
+ if ((dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
+ dev->model->model_id == ModelId::CANON_8600F &&
+ dev->settings.xres == 4800)
+ {
+ start_pixel = static_cast<int>(dev->model->x_offset_ta);
+ start_pixel /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
+ start_pixel = static_cast<int>((start_pixel * calib_sensor.optical_res) / MM_PER_INCH);
+
+ target_pixels = static_cast<int>(dev->model->x_size_ta);
+ target_pixels /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
+ target_pixels = static_cast<int>((target_pixels * calib_sensor.optical_res) / MM_PER_INCH);
+ }
+
+ ScanFlag flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ flags |= ScanFlag::USE_XPA;
+ }
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = start_pixel;
+ session.params.starty = 0;
+ session.params.pixels = target_pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = flags;
+ compute_session(dev, session, calib_sensor);
+ pixels = session.output_pixels;
+
+ DBG(DBG_io, "%s: dpihw =%d\n", __func__, dpihw);
+ DBG(DBG_io, "%s: factor =%d\n", __func__, factor);
+ DBG(DBG_io, "%s: resolution =%d\n", __func__, resolution);
+ DBG(DBG_io, "%s: pixels =%d\n", __func__, pixels);
+ DBG(DBG_io, "%s: black_pixels=%d\n", __func__, black_pixels);
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ // init gain and offset
+ for (unsigned ch = 0; ch < 3; ch++)
+ {
+ bottom[ch] = 10;
+ dev->frontend.set_offset(ch, bottom[ch]);
+ dev->frontend.set_gain(ch, 0);
+ }
+ dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
+
+ // scan with bottom AFE settings
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("offset_calibration");
+ scanner_stop_action_no_move(*dev, regs);
+ return;
+ }
+
+ auto first_line = read_unshuffled_image_from_scanner(dev, session,
+ session.output_total_bytes_raw);
+ scanner_stop_action_no_move(*dev, regs);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[40];
+ std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.pnm",
+ bottom[0], bottom[1], bottom[2]);
+ sanei_genesys_write_pnm_file(fn, first_line);
+ }
+
+ for (unsigned ch = 0; ch < 3; ch++) {
+ bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
+ DBG(DBG_io2, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
+ }
+
+ // now top value
+ for (unsigned ch = 0; ch < 3; ch++) {
+ top[ch] = 255;
+ dev->frontend.set_offset(ch, top[ch]);
+ }
+ dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
+
+ // scan with top AFE values
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+ auto second_line = read_unshuffled_image_from_scanner(dev, session,
+ session.output_total_bytes_raw);
+ scanner_stop_action_no_move(*dev, regs);
+
+ for (unsigned ch = 0; ch < 3; ch++){
+ topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
+ DBG(DBG_io2, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
+ }
+
+ pass = 0;
+
+ std::vector<uint8_t> debug_image;
+ size_t debug_image_lines = 0;
+ std::string debug_image_info;
+
+ /* loop until acceptable level */
+ while ((pass < 32)
+ && ((top[0] - bottom[0] > 1)
+ || (top[1] - bottom[1] > 1) || (top[2] - bottom[2] > 1)))
+ {
+ pass++;
+
+ // settings for new scan
+ for (unsigned ch = 0; ch < 3; ch++) {
+ if (top[ch] - bottom[ch] > 1) {
+ dev->frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
+ }
+ }
+ dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
+
+ // scan with no move
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+ second_line = read_unshuffled_image_from_scanner(dev, session,
+ session.output_total_bytes_raw);
+ scanner_stop_action_no_move(*dev, regs);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char title[100];
+ std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
+ lines, pixels,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+ debug_image_info += title;
+ std::copy(second_line.get_row_ptr(0),
+ second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
+ std::back_inserter(debug_image));
+ debug_image_lines += lines;
+ }
+
+ for (unsigned ch = 0; ch < 3; ch++) {
+ avg[ch] = dark_average_channel(second_line, black_pixels, ch);
+ DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
+ dev->frontend.get_offset(ch));
+ }
+
+ // compute new boundaries
+ for (unsigned ch = 0; ch < 3; ch++) {
+ if (topavg[ch] >= avg[ch]) {
+ topavg[ch] = avg[ch];
+ top[ch] = dev->frontend.get_offset(ch);
+ } else {
+ bottomavg[ch] = avg[ch];
+ bottom[ch] = dev->frontend.get_offset(ch);
+ }
+ }
+ }
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ sanei_genesys_write_file("gl843_offset_all_desc.txt",
+ reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
+ debug_image_info.size());
+ sanei_genesys_write_pnm_file("gl843_offset_all.pnm",
+ debug_image.data(), session.params.depth, channels, pixels,
+ debug_image_lines);
+ }
+
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+}
+
+
+/* alternative coarse gain calibration
+ this on uses the settings from offset_calibration and
+ uses only one scanline
+ */
+/*
+ with offset and coarse calibration we only want to get our input range into
+ a reasonable shape. the fine calibration of the upper and lower bounds will
+ be done with shading.
+ */
+void CommandSetGl843::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const
+{
+ DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
+ int factor, dpihw;
+ float coeff;
+ int lines;
+ int resolution;
+
+ if (dev->frontend.layout.type != FrontendType::WOLFSON)
+ return;
+
+ dpihw = sensor.get_logical_hwdpi(dpi);
+ factor=sensor.optical_res/dpihw;
+
+ // coarse gain calibration is always done in color mode
+ unsigned channels = 3;
+
+ /* follow CKSEL */
+ if (dev->model->sensor_id == SensorId::CCD_KVSS080) {
+ if(dev->settings.xres<sensor.optical_res)
+ {
+ coeff = 0.9f;
+ }
+ else
+ {
+ coeff=1.0;
+ }
+ }
+ else
+ {
+ coeff=1.0;
+ }
+ resolution=dpihw;
+ lines=10;
+ int target_pixels = sensor.sensor_pixels / factor;
+
+ ScanFlag flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ flags |= ScanFlag::USE_XPA;
+ }
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
+ dev->settings.scan_method);
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = target_pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = flags;
+ compute_session(dev, session, calib_sensor);
+ std::size_t pixels = session.output_pixels;
+
+ try {
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
+ throw;
+ }
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ dev->interface->write_registers(regs);
+
+ dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
+ dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("coarse_gain_calibration");
+ scanner_stop_action(*dev);
+ move_back_home(dev, true);
+ return;
+ }
+
+ auto line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw);
+ scanner_stop_action_no_move(*dev, regs);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl843_gain.pnm", line);
+ }
+
+ // average value on each channel
+ for (unsigned ch = 0; ch < channels; ch++) {
+
+ std::vector<uint16_t> values;
+ // FIXME: start from the second line because the first line often has artifacts. Probably
+ // caused by unclean cleanup of previous scan
+ for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
+ values.push_back(line.get_raw_channel(x, 1, ch));
+ }
+
+ // pick target value at 95th percentile of all values. There may be a lot of black values
+ // in transparency scans for example
+ std::sort(values.begin(), values.end());
+ uint16_t curr_output = values[unsigned((values.size() - 1) * 0.95)];
+ float target_value = calib_sensor.gain_white_ref * coeff;
+
+ int code = compute_frontend_gain(curr_output, target_value, dev->frontend.layout.type);
+ dev->frontend.set_gain(ch, code);
+
+ DBG(DBG_proc, "%s: channel %d, max=%d, target=%d, setting:%d\n", __func__, ch, curr_output,
+ static_cast<int>(target_value), code);
+ }
+
+ if (dev->model->is_cis) {
+ uint8_t gain0 = dev->frontend.get_gain(0);
+ if (gain0 > dev->frontend.get_gain(1)) {
+ gain0 = dev->frontend.get_gain(1);
+ }
+ if (gain0 > dev->frontend.get_gain(2)) {
+ gain0 = dev->frontend.get_gain(2);
+ }
+ dev->frontend.set_gain(0, gain0);
+ dev->frontend.set_gain(1, gain0);
+ dev->frontend.set_gain(2, gain0);
+ }
+
+ if (channels == 1) {
+ dev->frontend.set_gain(0, dev->frontend.get_gain(1));
+ dev->frontend.set_gain(2, dev->frontend.get_gain(1));
+ }
+
+ scanner_stop_action(*dev);
+
+ move_back_home(dev, true);
+}
+
+// wait for lamp warmup by scanning the same line until difference
+// between 2 scans is below a threshold
+void CommandSetGl843::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, int* channels,
+ int* total_size) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int dpihw;
+ int resolution;
+ int factor;
+
+ /* setup scan */
+ *channels=3;
+ resolution=600;
+ dpihw = sensor.get_logical_hwdpi(resolution);
+ resolution=dpihw;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, *channels,
+ dev->settings.scan_method);
+ factor = calib_sensor.optical_res/dpihw;
+ num_pixels = calib_sensor.sensor_pixels/(factor*2);
+ *total_size = num_pixels * 3 * 1;
+
+ *reg = dev->reg;
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = num_pixels/2;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 8;
+ session.params.channels = *channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, reg, session);
+
+ sanei_genesys_set_motor_power(*reg, false);
+ dev->interface->write_registers(*reg);
+}
+
+/**
+ * set up GPIO/GPOE for idle state
+WRITE GPIO[17-21]= GPIO19
+WRITE GPOE[17-21]= GPOE21 GPOE20 GPOE19 GPOE18
+genesys_write_register(0xa8,0x3e)
+GPIO(0xa8)=0x3e
+ */
+static void gl843_init_gpio(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg)
+ {
+ dev->interface->write_register(reg.address, reg.value);
+ });
+}
+
+
+/* *
+ * initialize ASIC from power on condition
+ */
+void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const
+{
+ DBG_HELPER(dbg);
+ uint8_t val;
+
+ if (cold) {
+ dev->interface->write_register(0x0e, 0x01);
+ dev->interface->write_register(0x0e, 0x00);
+ }
+
+ if(dev->usb_mode == 1)
+ {
+ val = 0x14;
+ }
+ else
+ {
+ val = 0x11;
+ }
+ dev->interface->write_0x8c(0x0f, val);
+
+ // test CHKVER
+ val = dev->interface->read_register(REG_0x40);
+ if (val & REG_0x40_CHKVER) {
+ val = dev->interface->read_register(0x00);
+ DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
+ }
+
+ /* Set default values for registers */
+ gl843_init_registers (dev);
+
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ // turns on vref control for maximum current of the motor driver
+ dev->interface->write_register(REG_0x6B, 0x72);
+ } else {
+ dev->interface->write_register(REG_0x6B, 0x02);
+ }
+
+ // Write initial registers
+ dev->interface->write_registers(dev->reg);
+
+ // Enable DRAM by setting a rising edge on bit 3 of reg 0x0b
+ val = dev->reg.find_reg(0x0b).value & REG_0x0B_DRAMSEL;
+ val = (val | REG_0x0B_ENBDRAM);
+ dev->interface->write_register(REG_0x0B, val);
+ dev->reg.find_reg(0x0b).value = val;
+
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ dev->interface->write_0x8c(0x1e, 0x01);
+ dev->interface->write_0x8c(0x10, 0xb4);
+ dev->interface->write_0x8c(0x0f, 0x02);
+ }
+ else if (dev->model->model_id == ModelId::CANON_8600F) {
+ dev->interface->write_0x8c(0x10, 0xc8);
+ } else if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ dev->interface->write_0x8c(0x10, 0xd4);
+ } else {
+ dev->interface->write_0x8c(0x10, 0xb4);
+ }
+
+ /* CLKSET */
+ int clock_freq = REG_0x0B_48MHZ;
+ switch (dev->model->model_id) {
+ case ModelId::CANON_8600F:
+ clock_freq = REG_0x0B_60MHZ;
+ break;
+ case ModelId::PLUSTEK_OPTICFILM_7200I:
+ clock_freq = REG_0x0B_30MHZ;
+ break;
+ case ModelId::PLUSTEK_OPTICFILM_7300:
+ case ModelId::PLUSTEK_OPTICFILM_7500I:
+ clock_freq = REG_0x0B_40MHZ;
+ break;
+ default:
+ break;
+ }
+
+ val = (dev->reg.find_reg(0x0b).value & ~REG_0x0B_CLKSET) | clock_freq;
+
+ dev->interface->write_register(REG_0x0B, val);
+ dev->reg.find_reg(0x0b).value = val;
+
+ /* prevent further writings by bulk write register */
+ dev->reg.remove_reg(0x0b);
+
+ if (dev->model->model_id != ModelId::CANON_8600F) {
+ // set up end access
+ // FIXME: this is overwritten in gl843_init_gpio
+ dev->interface->write_register(REG_0xA7, 0x04);
+ dev->interface->write_register(REG_0xA9, 0x00);
+ }
+
+ // set RAM read address
+ dev->interface->write_register(REG_0x29, 0x00);
+ dev->interface->write_register(REG_0x2A, 0x00);
+ dev->interface->write_register(REG_0x2B, 0x00);
+
+ // setup gpio
+ gl843_init_gpio(dev);
+
+ scanner_move(*dev, dev->model->default_method, 300, Direction::FORWARD);
+ dev->interface->sleep_ms(100);
+}
+
+/* *
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+void CommandSetGl843::init(Genesys_Device* dev) const
+{
+ DBG_INIT ();
+ DBG_HELPER(dbg);
+
+ sanei_genesys_asic_init(dev, 0);
+}
+
+void CommandSetGl843::update_hardware_sensors(Genesys_Scanner* s) const
+{
+ DBG_HELPER(dbg);
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+
+ uint8_t val = s->dev->interface->read_register(REG_0x6D);
+
+ switch (s->dev->model->gpio_id)
+ {
+ case GpioId::KVSS080:
+ s->buttons[BUTTON_SCAN_SW].write((val & 0x04) == 0);
+ break;
+ case GpioId::G4050:
+ s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0);
+ s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0);
+ s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0);
+ s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0);
+ break;
+ case GpioId::CANON_4400F:
+ case GpioId::CANON_8400F:
+ default:
+ break;
+ }
+}
+
+/** @brief move sensor to transparency adaptor
+ * Move sensor to the calibration of the transparency adapator (XPA).
+ * @param dev device to use
+ */
+void CommandSetGl843::move_to_ta(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+
+ const auto& resolution_settings = dev->model->get_resolution_settings(dev->model->default_method);
+ float resolution = resolution_settings.get_min_resolution_y();
+
+ unsigned multiplier = 16;
+ if (dev->model->model_id == ModelId::CANON_8400F) {
+ multiplier = 4;
+ }
+ unsigned feed = static_cast<unsigned>(multiplier * (dev->model->y_offset_sensor_to_ta * resolution) /
+ MM_PER_INCH);
+ scanner_move(*dev, dev->model->default_method, feed, Direction::FORWARD);
+}
+
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward true if searching forward, false if searching backward
+ * @param black true if searching for a black strip, false for a white strip
+ */
+void CommandSetGl843::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const
+{
+ DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse");
+ unsigned int pixels, lines, channels;
+ Genesys_Register_Set local_reg;
+ int dpi;
+ unsigned int pass, count, found, x, y;
+
+ dev->cmd_set->set_fe(dev, sensor, AFE_SET);
+ scanner_stop_action(*dev);
+
+ /* set up for a gray scan at lowest dpi */
+ dpi = sanei_genesys_get_lowest_dpi(dev);
+ channels = 1;
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, dpi, channels,
+ dev->settings.scan_method);
+
+ /* 10 MM */
+ /* lines = (10 * dpi) / MM_PER_INCH; */
+ /* shading calibation is done with dev->motor.base_ydpi */
+ lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
+ pixels = (calib_sensor.sensor_pixels * dpi) / calib_sensor.optical_res;
+
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ local_reg = dev->reg;
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_SHADING;
+ if (!forward) {
+ session.params.flags = ScanFlag::REVERSE;
+ }
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &local_reg, session);
+
+ dev->interface->write_registers(local_reg);
+
+ dev->cmd_set->begin_scan(dev, calib_sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_strip");
+ scanner_stop_action(*dev);
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ auto data = read_unshuffled_image_from_scanner(dev, session,
+ session.output_total_bytes_raw);
+
+ scanner_stop_action(*dev);
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[40];
+ std::snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file(fn, data);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < 20 && !found)
+ {
+ dev->interface->write_registers(local_reg);
+
+ // now start scan
+ dev->cmd_set->begin_scan(dev, calib_sensor, &local_reg, true);
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ data = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw);
+
+ scanner_stop_action(*dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[40];
+ std::snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file(fn, data);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data.get_raw_channel(x, y, 0) > 90) {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data.get_raw_channel(x, y, 0) < 60) {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
+ pass, y);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ // when searching for black, detect white pixels
+ if (black && data.get_raw_channel(x, y, 0) > 90) {
+ count++;
+ }
+ // when searching for white, detect black pixels
+ if (!black && data.get_raw_channel(x, y, 0) < 60) {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+ if (found)
+ {
+ DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
+ }
+ else
+ {
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white");
+ }
+}
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ uint8_t* data, int size) const
+{
+ DBG_HELPER(dbg);
+ uint32_t final_size, length, i;
+ uint8_t *buffer;
+ int count,offset;
+ GenesysRegister *r;
+ uint16_t strpixel, endpixel, startx;
+
+ offset=0;
+ length=size;
+ r = sanei_genesys_get_address(&dev->reg, REG_0x01);
+ if (r->value & REG_0x01_SHDAREA)
+ {
+ /* recompute STRPIXEL used shading calibration so we can
+ * compute offset within data for SHDAREA case */
+
+ // FIXME: the following is likely incorrect
+ // start coordinate in optical dpi coordinates
+ startx = (sensor.dummy_pixel / sensor.ccd_pixels_per_system_pixel()) / dev->session.hwdpi_divisor;
+ startx *= dev->session.pixel_count_multiplier;
+
+ /* current scan coordinates */
+ strpixel = dev->session.pixel_startx;
+ endpixel = dev->session.pixel_endx;
+
+ if (dev->model->model_id == ModelId::CANON_4400F ||
+ dev->model->model_id == ModelId::CANON_8600F)
+ {
+ int half_ccd_factor = dev->session.optical_resolution /
+ sensor.get_logical_hwdpi(dev->session.output_resolution);
+ strpixel /= half_ccd_factor * sensor.ccd_pixels_per_system_pixel();
+ endpixel /= half_ccd_factor * sensor.ccd_pixels_per_system_pixel();
+ }
+
+ /* 16 bit words, 2 words per color, 3 color channels */
+ offset=(strpixel-startx)*2*2*3;
+ length=(endpixel-strpixel)*2*2*3;
+ DBG(DBG_info, "%s: STRPIXEL=%d, ENDPIXEL=%d, startx=%d\n", __func__, strpixel, endpixel,
+ startx);
+ }
+
+ dev->interface->record_key_value("shading_offset", std::to_string(offset));
+ dev->interface->record_key_value("shading_length", std::to_string(length));
+
+ /* compute and allocate size for final data */
+ final_size = ((length+251) / 252) * 256;
+ DBG(DBG_io, "%s: final shading size=%04x (length=%d)\n", __func__, final_size, length);
+ std::vector<uint8_t> final_data(final_size, 0);
+
+ /* copy regular shading data to the expected layout */
+ buffer = final_data.data();
+ count = 0;
+
+ /* loop over calibration data */
+ for (i = 0; i < length; i++)
+ {
+ buffer[count] = data[offset+i];
+ count++;
+ if ((count % (256*2)) == (252*2))
+ {
+ count += 4*2;
+ }
+ }
+
+ dev->interface->write_buffer(0x3c, 0, final_data.data(), count,
+ ScannerInterface::FLAG_SMALL_ADDRESS);
+}
+
+bool CommandSetGl843::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
+{
+ (void) dev;
+ return true;
+}
+
+void CommandSetGl843::wait_for_motor_stop(Genesys_Device* dev) const
+{
+ (void) dev;
+}
+
+std::unique_ptr<CommandSet> create_gl843_cmd_set()
+{
+ return std::unique_ptr<CommandSet>(new CommandSetGl843{});
+}
+
+} // namespace gl843
+} // namespace genesys
diff --git a/backend/genesys/gl843.h b/backend/genesys/gl843.h
new file mode 100644
index 0000000..9f0a9e9
--- /dev/null
+++ b/backend/genesys/gl843.h
@@ -0,0 +1,139 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+#include "command_set.h"
+
+#ifndef BACKEND_GENESYS_GL843_H
+#define BACKEND_GENESYS_GL843_H
+
+namespace genesys {
+namespace gl843 {
+
+class CommandSetGl843 : public CommandSet
+{
+public:
+ ~CommandSetGl843() override = default;
+
+ bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override;
+
+ void init(Genesys_Device* dev) const override;
+
+ void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const override;
+
+ void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const override;
+
+ void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const override;
+ void set_powersaving(Genesys_Device* dev, int delay) const override;
+ void save_power(Genesys_Device* dev, bool enable) const override;
+
+ void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, bool start_motor) const override;
+
+ void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override;
+
+ void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void search_start_position(Genesys_Device* dev) const override;
+
+ void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const override;
+
+ SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void wait_for_motor_stop(Genesys_Device* dev) const override;
+
+ void move_back_home(Genesys_Device* dev, bool wait_until_home) const override;
+
+ void update_hardware_sensors(struct Genesys_Scanner* s) const override;
+
+ void load_document(Genesys_Device* dev) const override;
+
+ void detect_document_end(Genesys_Device* dev) const override;
+
+ void eject_document(Genesys_Device* dev) const override;
+
+ void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const override;
+
+ void move_to_ta(Genesys_Device* dev) const override;
+
+ void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,
+ int size) const override;
+
+ ScanSession calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const override;
+
+ void asic_boot(Genesys_Device* dev, bool cold) const override;
+};
+
+enum SlopeTable
+{
+ SCAN_TABLE = 0, // table 1 at 0x4000
+ BACKTRACK_TABLE = 1, // table 2 at 0x4800
+ STOP_TABLE = 2, // table 3 at 0x5000
+ FAST_TABLE = 3, // table 4 at 0x5800
+ HOME_TABLE = 4, // table 5 at 0x6000
+};
+
+} // namespace gl843
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL843_H
diff --git a/backend/genesys/gl843_registers.h b/backend/genesys/gl843_registers.h
new file mode 100644
index 0000000..8ecb0fc
--- /dev/null
+++ b/backend/genesys/gl843_registers.h
@@ -0,0 +1,382 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL843_REGISTERS_H
+#define BACKEND_GENESYS_GL843_REGISTERS_H
+
+#include <cstdint>
+
+namespace genesys {
+namespace gl843 {
+
+using RegAddr = std::uint16_t;
+using RegMask = std::uint8_t;
+using RegShift = unsigned;
+
+static constexpr RegAddr REG_0x01 = 0x01;
+static constexpr RegMask REG_0x01_CISSET = 0x80;
+static constexpr RegMask REG_0x01_DOGENB = 0x40;
+static constexpr RegMask REG_0x01_DVDSET = 0x20;
+static constexpr RegMask REG_0x01_STAGGER = 0x10;
+static constexpr RegMask REG_0x01_COMPENB = 0x08;
+static constexpr RegMask REG_0x01_TRUEGRAY = 0x04;
+static constexpr RegMask REG_0x01_SHDAREA = 0x02;
+static constexpr RegMask REG_0x01_SCAN = 0x01;
+
+static constexpr RegAddr REG_0x02 = 0x02;
+static constexpr RegMask REG_0x02_NOTHOME = 0x80;
+static constexpr RegMask REG_0x02_ACDCDIS = 0x40;
+static constexpr RegMask REG_0x02_AGOHOME = 0x20;
+static constexpr RegMask REG_0x02_MTRPWR = 0x10;
+static constexpr RegMask REG_0x02_FASTFED = 0x08;
+static constexpr RegMask REG_0x02_MTRREV = 0x04;
+static constexpr RegMask REG_0x02_HOMENEG = 0x02;
+static constexpr RegMask REG_0x02_LONGCURV = 0x01;
+
+static constexpr RegAddr REG_0x03 = 0x03;
+static constexpr RegMask REG_0x03_LAMPDOG = 0x80;
+static constexpr RegMask REG_0x03_AVEENB = 0x40;
+static constexpr RegMask REG_0x03_XPASEL = 0x20;
+static constexpr RegMask REG_0x03_LAMPPWR = 0x10;
+static constexpr RegMask REG_0x03_LAMPTIM = 0x0f;
+
+static constexpr RegAddr REG_0x04 = 0x04;
+static constexpr RegMask REG_0x04_LINEART = 0x80;
+static constexpr RegMask REG_0x04_BITSET = 0x40;
+static constexpr RegMask REG_0x04_AFEMOD = 0x30;
+static constexpr RegMask REG_0x04_FILTER = 0x0c;
+static constexpr RegMask REG_0x04_FESET = 0x03;
+
+static constexpr RegShift REG_0x04S_AFEMOD = 4;
+
+static constexpr RegAddr REG_0x05 = 0x05;
+static constexpr RegMask REG_0x05_DPIHW = 0xc0;
+static constexpr RegMask REG_0x05_DPIHW_600 = 0x00;
+static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40;
+static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80;
+static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0;
+static constexpr RegMask REG_0x05_MTLLAMP = 0x30;
+static constexpr RegMask REG_0x05_GMMENB = 0x08;
+static constexpr RegMask REG_0x05_MTLBASE = 0x03;
+
+static constexpr RegAddr REG_0x06 = 0x06;
+static constexpr RegMask REG_0x06_SCANMOD = 0xe0;
+static constexpr RegShift REG_0x06S_SCANMOD = 5;
+static constexpr RegMask REG_0x06_PWRBIT = 0x10;
+static constexpr RegMask REG_0x06_GAIN4 = 0x08;
+static constexpr RegMask REG_0x06_OPTEST = 0x07;
+
+static constexpr RegMask REG_0x07_LAMPSIM = 0x80;
+
+static constexpr RegMask REG_0x08_DECFLAG = 0x40;
+static constexpr RegMask REG_0x08_GMMFFR = 0x20;
+static constexpr RegMask REG_0x08_GMMFFG = 0x10;
+static constexpr RegMask REG_0x08_GMMFFB = 0x08;
+static constexpr RegMask REG_0x08_GMMZR = 0x04;
+static constexpr RegMask REG_0x08_GMMZG = 0x02;
+static constexpr RegMask REG_0x08_GMMZB = 0x01;
+
+static constexpr RegMask REG_0x09_MCNTSET = 0xc0;
+static constexpr RegMask REG_0x09_EVEN1ST = 0x20;
+static constexpr RegMask REG_0x09_BLINE1ST = 0x10;
+static constexpr RegMask REG_0x09_BACKSCAN = 0x08;
+static constexpr RegMask REG_0x09_ENHANCE = 0x04;
+static constexpr RegMask REG_0x09_SHORTTG = 0x02;
+static constexpr RegMask REG_0x09_NWAIT = 0x01;
+
+static constexpr RegShift REG_0x09S_MCNTSET = 6;
+static constexpr RegShift REG_0x09S_CLKSET = 4;
+
+static constexpr RegAddr REG_0x0B = 0x0b;
+static constexpr RegMask REG_0x0B_DRAMSEL = 0x07;
+static constexpr RegMask REG_0x0B_ENBDRAM = 0x08;
+static constexpr RegMask REG_0x0B_RFHDIS = 0x10;
+static constexpr RegMask REG_0x0B_CLKSET = 0xe0;
+static constexpr RegMask REG_0x0B_24MHZ = 0x00;
+static constexpr RegMask REG_0x0B_30MHZ = 0x20;
+static constexpr RegMask REG_0x0B_40MHZ = 0x40;
+static constexpr RegMask REG_0x0B_48MHZ = 0x60;
+static constexpr RegMask REG_0x0B_60MHZ = 0x80;
+
+static constexpr RegAddr REG_0x0D = 0x0d;
+static constexpr RegMask REG_0x0D_JAMPCMD = 0x80;
+static constexpr RegMask REG_0x0D_DOCCMD = 0x40;
+static constexpr RegMask REG_0x0D_CCDCMD = 0x20;
+static constexpr RegMask REG_0x0D_FULLSTP = 0x10;
+static constexpr RegMask REG_0x0D_SEND = 0x08;
+static constexpr RegMask REG_0x0D_CLRMCNT = 0x04;
+static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02;
+static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01;
+
+static constexpr RegAddr REG_0x0F = 0x0f;
+
+static constexpr RegAddr REG_EXPR = 0x10;
+static constexpr RegAddr REG_EXPG = 0x12;
+static constexpr RegAddr REG_EXPB = 0x14;
+
+static constexpr RegMask REG_0x16_CTRLHI = 0x80;
+static constexpr RegMask REG_0x16_TOSHIBA = 0x40;
+static constexpr RegMask REG_0x16_TGINV = 0x20;
+static constexpr RegMask REG_0x16_CK1INV = 0x10;
+static constexpr RegMask REG_0x16_CK2INV = 0x08;
+static constexpr RegMask REG_0x16_CTRLINV = 0x04;
+static constexpr RegMask REG_0x16_CKDIS = 0x02;
+static constexpr RegMask REG_0x16_CTRLDIS = 0x01;
+
+static constexpr RegMask REG_0x17_TGMODE = 0xc0;
+static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00;
+static constexpr RegMask REG_0x17_TGMODE_REF = 0x40;
+static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80;
+static constexpr RegMask REG_0x17_TGW = 0x3f;
+static constexpr RegShift REG_0x17S_TGW = 0;
+
+static constexpr RegAddr REG_0x18 = 0x18;
+static constexpr RegMask REG_0x18_CNSET = 0x80;
+static constexpr RegMask REG_0x18_DCKSEL = 0x60;
+static constexpr RegMask REG_0x18_CKTOGGLE = 0x10;
+static constexpr RegMask REG_0x18_CKDELAY = 0x0c;
+static constexpr RegMask REG_0x18_CKSEL = 0x03;
+
+static constexpr RegAddr REG_EXPDMY = 0x19;
+
+static constexpr RegMask REG_0x1A_TGLSW2 = 0x80;
+static constexpr RegMask REG_0x1A_TGLSW1 = 0x40;
+static constexpr RegMask REG_0x1A_MANUAL3 = 0x02;
+static constexpr RegMask REG_0x1A_MANUAL1 = 0x01;
+static constexpr RegMask REG_0x1A_CK4INV = 0x08;
+static constexpr RegMask REG_0x1A_CK3INV = 0x04;
+static constexpr RegMask REG_0x1A_LINECLP = 0x02;
+
+static constexpr RegAddr REG_0x1C = 0x1c;
+static constexpr RegMask REG_0x1C_TGTIME = 0x07;
+
+static constexpr RegMask REG_0x1D_CK4LOW = 0x80;
+static constexpr RegMask REG_0x1D_CK3LOW = 0x40;
+static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
+static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
+static constexpr RegShift REG_0x1DS_TGSHLD = 0;
+
+
+static constexpr RegAddr REG_0x1E = 0x1e;
+static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
+static constexpr RegShift REG_0x1ES_WDTIME = 4;
+static constexpr RegMask REG_0x1E_LINESEL = 0x0f;
+static constexpr RegShift REG_0x1ES_LINESEL = 0;
+
+static constexpr RegAddr REG_0x21 = 0x21;
+static constexpr RegAddr REG_STEPNO = 0x21;
+static constexpr RegAddr REG_FWDSTEP = 0x22;
+static constexpr RegAddr REG_BWDSTEP = 0x23;
+static constexpr RegAddr REG_FASTNO = 0x24;
+static constexpr RegAddr REG_LINCNT = 0x25;
+
+static constexpr RegAddr REG_0x29 = 0x29;
+static constexpr RegAddr REG_0x2A = 0x2a;
+static constexpr RegAddr REG_0x2B = 0x2b;
+static constexpr RegAddr REG_DPISET = 0x2c;
+static constexpr RegAddr REG_0x2E = 0x2e;
+static constexpr RegAddr REG_0x2F = 0x2f;
+
+static constexpr RegAddr REG_STRPIXEL = 0x30;
+static constexpr RegAddr REG_ENDPIXEL = 0x32;
+static constexpr RegAddr REG_DUMMY = 0x34;
+static constexpr RegAddr REG_MAXWD = 0x35;
+static constexpr RegAddr REG_LPERIOD = 0x38;
+static constexpr RegAddr REG_FEEDL = 0x3d;
+
+static constexpr RegAddr REG_0x40 = 0x40;
+static constexpr RegMask REG_0x40_DOCSNR = 0x80;
+static constexpr RegMask REG_0x40_ADFSNR = 0x40;
+static constexpr RegMask REG_0x40_COVERSNR = 0x20;
+static constexpr RegMask REG_0x40_CHKVER = 0x10;
+static constexpr RegMask REG_0x40_DOCJAM = 0x08;
+static constexpr RegMask REG_0x40_HISPDFLG = 0x04;
+static constexpr RegMask REG_0x40_MOTMFLG = 0x02;
+static constexpr RegMask REG_0x40_DATAENB = 0x01;
+
+static constexpr RegMask REG_0x41_PWRBIT = 0x80;
+static constexpr RegMask REG_0x41_BUFEMPTY = 0x40;
+static constexpr RegMask REG_0x41_FEEDFSH = 0x20;
+static constexpr RegMask REG_0x41_SCANFSH = 0x10;
+static constexpr RegMask REG_0x41_HOMESNR = 0x08;
+static constexpr RegMask REG_0x41_LAMPSTS = 0x04;
+static constexpr RegMask REG_0x41_FEBUSY = 0x02;
+static constexpr RegMask REG_0x41_MOTORENB = 0x01;
+
+static constexpr RegMask REG_0x58_VSMP = 0xf8;
+static constexpr RegShift REG_0x58S_VSMP = 3;
+static constexpr RegMask REG_0x58_VSMPW = 0x07;
+static constexpr RegShift REG_0x58S_VSMPW = 0;
+
+static constexpr RegMask REG_0x59_BSMP = 0xf8;
+static constexpr RegShift REG_0x59S_BSMP = 3;
+static constexpr RegMask REG_0x59_BSMPW = 0x07;
+static constexpr RegShift REG_0x59S_BSMPW = 0;
+
+static constexpr RegMask REG_0x5A_ADCLKINV = 0x80;
+static constexpr RegMask REG_0x5A_RLCSEL = 0x40;
+static constexpr RegMask REG_0x5A_CDSREF = 0x30;
+static constexpr RegShift REG_0x5AS_CDSREF = 4;
+static constexpr RegMask REG_0x5A_RLC = 0x0f;
+static constexpr RegShift REG_0x5AS_RLC = 0;
+
+static constexpr RegAddr REG_0x5E = 0x5e;
+static constexpr RegMask REG_0x5E_DECSEL = 0xe0;
+static constexpr RegShift REG_0x5ES_DECSEL = 5;
+static constexpr RegMask REG_0x5E_STOPTIM = 0x1f;
+static constexpr RegShift REG_0x5ES_STOPTIM = 0;
+
+static constexpr RegAddr REG_FMOVDEC = 0x5f;
+
+static constexpr RegAddr REG_0x60 = 0x60;
+static constexpr RegMask REG_0x60_Z1MOD = 0x1f;
+static constexpr RegAddr REG_0x61 = 0x61;
+static constexpr RegMask REG_0x61_Z1MOD = 0xff;
+static constexpr RegAddr REG_0x62 = 0x62;
+static constexpr RegMask REG_0x62_Z1MOD = 0xff;
+
+static constexpr RegAddr REG_0x63 = 0x63;
+static constexpr RegMask REG_0x63_Z2MOD = 0x1f;
+static constexpr RegAddr REG_0x64 = 0x64;
+static constexpr RegMask REG_0x64_Z2MOD = 0xff;
+static constexpr RegAddr REG_0x65 = 0x65;
+static constexpr RegMask REG_0x65_Z2MOD = 0xff;
+
+static constexpr RegAddr REG_0x67 = 0x67;
+
+static constexpr RegAddr REG_0x68 = 0x68;
+
+static constexpr RegShift REG_0x67S_STEPSEL = 6;
+static constexpr RegMask REG_0x67_STEPSEL = 0xc0;
+static constexpr RegMask REG_0x67_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x67_HALFSTEP = 0x20;
+static constexpr RegMask REG_0x67_EIGHTHSTEP = 0x60;
+static constexpr RegMask REG_0x67_16THSTEP = 0x80;
+
+static constexpr RegShift REG_0x68S_FSTPSEL = 6;
+static constexpr RegMask REG_0x68_FSTPSEL = 0xc0;
+static constexpr RegMask REG_0x68_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x68_HALFSTEP = 0x20;
+static constexpr RegMask REG_0x68_EIGHTHSTEP = 0x60;
+static constexpr RegMask REG_0x68_16THSTEP = 0x80;
+
+static constexpr RegAddr REG_FSHDEC = 0x69;
+static constexpr RegAddr REG_FMOVNO = 0x6a;
+
+static constexpr RegAddr REG_0x6B = 0x6b;
+static constexpr RegMask REG_0x6B_MULTFILM = 0x80;
+static constexpr RegMask REG_0x6B_GPOM13 = 0x40;
+static constexpr RegMask REG_0x6B_GPOM12 = 0x20;
+static constexpr RegMask REG_0x6B_GPOM11 = 0x10;
+static constexpr RegMask REG_0x6B_GPOCK4 = 0x08;
+static constexpr RegMask REG_0x6B_GPOCP = 0x04;
+static constexpr RegMask REG_0x6B_GPOLEDB = 0x02;
+static constexpr RegMask REG_0x6B_GPOADF = 0x01;
+
+static constexpr RegAddr REG_0x6C = 0x6c;
+static constexpr RegMask REG_0x6C_GPIO16 = 0x80;
+static constexpr RegMask REG_0x6C_GPIO15 = 0x40;
+static constexpr RegMask REG_0x6C_GPIO14 = 0x20;
+static constexpr RegMask REG_0x6C_GPIO13 = 0x10;
+static constexpr RegMask REG_0x6C_GPIO12 = 0x08;
+static constexpr RegMask REG_0x6C_GPIO11 = 0x04;
+static constexpr RegMask REG_0x6C_GPIO10 = 0x02;
+static constexpr RegMask REG_0x6C_GPIO9 = 0x01;
+static constexpr RegMask REG_0x6C_GPIOH = 0xff;
+static constexpr RegMask REG_0x6C_GPIOL = 0xff;
+
+static constexpr RegAddr REG_Z1MOD = 0x60;
+static constexpr RegAddr REG_Z2MOD = 0x63;
+
+static constexpr RegAddr REG_0x6D = 0x6d;
+static constexpr RegAddr REG_0x6E = 0x6e;
+static constexpr RegAddr REG_0x6F = 0x6f;
+
+static constexpr RegAddr REG_CK1MAP = 0x74;
+static constexpr RegAddr REG_CK3MAP = 0x77;
+static constexpr RegAddr REG_CK4MAP = 0x7a;
+
+static constexpr RegAddr REG_0x7E = 0x7e;
+
+static constexpr RegAddr REG_0x9D = 0x9d;
+static constexpr RegShift REG_0x9DS_STEPTIM = 2;
+
+static constexpr RegMask REG_0x87_LEDADD = 0x04;
+
+static constexpr RegAddr REG_0xA6 = 0xa6;
+static constexpr RegMask REG_0xA6_GPIO24 = 0x80;
+static constexpr RegMask REG_0xA6_GPIO23 = 0x40;
+static constexpr RegMask REG_0xA6_GPIO22 = 0x20;
+static constexpr RegMask REG_0xA6_GPIO21 = 0x10;
+static constexpr RegMask REG_0xA6_GPIO20 = 0x08;
+static constexpr RegMask REG_0xA6_GPIO19 = 0x04;
+static constexpr RegMask REG_0xA6_GPIO18 = 0x02;
+static constexpr RegMask REG_0xA6_GPIO17 = 0x01;
+static constexpr RegAddr REG_0xA7 = 0xa7;
+static constexpr RegMask REG_0xA7_GPOE24 = 0x80;
+static constexpr RegMask REG_0xA7_GPOE23 = 0x40;
+static constexpr RegMask REG_0xA7_GPOE22 = 0x20;
+static constexpr RegMask REG_0xA7_GPOE21 = 0x10;
+static constexpr RegMask REG_0xA7_GPOE20 = 0x08;
+static constexpr RegMask REG_0xA7_GPOE19 = 0x04;
+static constexpr RegMask REG_0xA7_GPOE18 = 0x02;
+static constexpr RegMask REG_0xA7_GPOE17 = 0x01;
+static constexpr RegAddr REG_0xA8 = 0xa8;
+static constexpr RegMask REG_0xA8_GPOE27 = 0x20;
+static constexpr RegMask REG_0xA8_GPOE26 = 0x10;
+static constexpr RegMask REG_0xA8_GPOE25 = 0x08;
+static constexpr RegMask REG_0xA8_GPO27 = 0x04;
+static constexpr RegMask REG_0xA8_GPO26 = 0x02;
+static constexpr RegMask REG_0xA8_GPO25 = 0x01;
+static constexpr RegAddr REG_0xA9 = 0xa9;
+static constexpr RegMask REG_0xA9_GPO33 = 0x20;
+static constexpr RegMask REG_0xA9_GPO32 = 0x10;
+static constexpr RegMask REG_0xA9_GPO31 = 0x08;
+static constexpr RegMask REG_0xA9_GPO30 = 0x04;
+static constexpr RegMask REG_0xA9_GPO29 = 0x02;
+static constexpr RegMask REG_0xA9_GPO28 = 0x01;
+
+} // namespace gl843
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL843_REGISTERS_H
diff --git a/backend/genesys/gl846.cpp b/backend/genesys/gl846.cpp
new file mode 100644
index 0000000..d309d29
--- /dev/null
+++ b/backend/genesys/gl846.cpp
@@ -0,0 +1,2098 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2012-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+/** @file
+ *
+ * This file handles GL846 and GL845 ASICs since they are really close to each other.
+ */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "gl846.h"
+#include "gl846_registers.h"
+#include "test_settings.h"
+
+#include <vector>
+
+namespace genesys {
+namespace gl846 {
+
+/**
+ * compute the step multiplier used
+ */
+static int
+gl846_get_step_multiplier (Genesys_Register_Set * regs)
+{
+ GenesysRegister *r = sanei_genesys_get_address(regs, 0x9d);
+ int value = 1;
+ if (r != nullptr) {
+ value = (r->value & 0x0f)>>1;
+ value = 1 << value;
+ }
+ DBG (DBG_io, "%s: step multiplier is %d\n", __func__, value);
+ return value;
+}
+
+/** @brief sensor specific settings
+*/
+static void gl846_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs)
+{
+ DBG_HELPER(dbg);
+
+ for (const auto& reg : sensor.custom_regs) {
+ regs->set8(reg.address, reg.value);
+ }
+
+ regs->set16(REG_EXPR, sensor.exposure.red);
+ regs->set16(REG_EXPG, sensor.exposure.green);
+ regs->set16(REG_EXPB, sensor.exposure.blue);
+
+ dev->segment_order = sensor.segment_order;
+}
+
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl846_init_registers (Genesys_Device * dev)
+{
+ DBG_HELPER(dbg);
+
+ dev->reg.clear();
+
+ dev->reg.init_reg(0x01, 0x60);
+ dev->reg.init_reg(0x02, 0x38);
+ dev->reg.init_reg(0x03, 0x03);
+ dev->reg.init_reg(0x04, 0x22);
+ dev->reg.init_reg(0x05, 0x60);
+ dev->reg.init_reg(0x06, 0x10);
+ dev->reg.init_reg(0x08, 0x60);
+ dev->reg.init_reg(0x09, 0x00);
+ dev->reg.init_reg(0x0a, 0x00);
+ dev->reg.init_reg(0x0b, 0x8b);
+ dev->reg.init_reg(0x0c, 0x00);
+ dev->reg.init_reg(0x0d, 0x00);
+ dev->reg.init_reg(0x10, 0x00);
+ dev->reg.init_reg(0x11, 0x00);
+ dev->reg.init_reg(0x12, 0x00);
+ dev->reg.init_reg(0x13, 0x00);
+ dev->reg.init_reg(0x14, 0x00);
+ dev->reg.init_reg(0x15, 0x00);
+ dev->reg.init_reg(0x16, 0xbb); // SENSOR_DEF
+ dev->reg.init_reg(0x17, 0x13); // SENSOR_DEF
+ dev->reg.init_reg(0x18, 0x10); // SENSOR_DEF
+ dev->reg.init_reg(0x19, 0x2a); // SENSOR_DEF
+ dev->reg.init_reg(0x1a, 0x34); // SENSOR_DEF
+ dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x1c, 0x20); // SENSOR_DEF
+ dev->reg.init_reg(0x1d, 0x06); // SENSOR_DEF
+ dev->reg.init_reg(0x1e, 0xf0);
+ dev->reg.init_reg(0x1f, 0x01);
+ dev->reg.init_reg(0x20, 0x03);
+ dev->reg.init_reg(0x21, 0x10);
+ dev->reg.init_reg(0x22, 0x60);
+ dev->reg.init_reg(0x23, 0x60);
+ dev->reg.init_reg(0x24, 0x60);
+ dev->reg.init_reg(0x25, 0x00);
+ dev->reg.init_reg(0x26, 0x00);
+ dev->reg.init_reg(0x27, 0x00);
+ dev->reg.init_reg(0x2c, 0x00);
+ dev->reg.init_reg(0x2d, 0x00);
+ dev->reg.init_reg(0x2e, 0x80);
+ dev->reg.init_reg(0x2f, 0x80);
+ dev->reg.init_reg(0x30, 0x00);
+ dev->reg.init_reg(0x31, 0x00);
+ dev->reg.init_reg(0x32, 0x00);
+ dev->reg.init_reg(0x33, 0x00);
+ dev->reg.init_reg(0x34, 0x1f);
+ dev->reg.init_reg(0x35, 0x00);
+ dev->reg.init_reg(0x36, 0x40);
+ dev->reg.init_reg(0x37, 0x00);
+ dev->reg.init_reg(0x38, 0x2a);
+ dev->reg.init_reg(0x39, 0xf8);
+ dev->reg.init_reg(0x3d, 0x00);
+ dev->reg.init_reg(0x3e, 0x00);
+ dev->reg.init_reg(0x3f, 0x01);
+ dev->reg.init_reg(0x52, 0x02); // SENSOR_DEF
+ dev->reg.init_reg(0x53, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x54, 0x06); // SENSOR_DEF
+ dev->reg.init_reg(0x55, 0x08); // SENSOR_DEF
+ dev->reg.init_reg(0x56, 0x0a); // SENSOR_DEF
+ dev->reg.init_reg(0x57, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x58, 0x59); // SENSOR_DEF
+ dev->reg.init_reg(0x59, 0x31); // SENSOR_DEF
+ dev->reg.init_reg(0x5a, 0x40); // SENSOR_DEF
+ dev->reg.init_reg(0x5e, 0x1f);
+ dev->reg.init_reg(0x5f, 0x01);
+ dev->reg.init_reg(0x60, 0x00);
+ dev->reg.init_reg(0x61, 0x00);
+ dev->reg.init_reg(0x62, 0x00);
+ dev->reg.init_reg(0x63, 0x00);
+ dev->reg.init_reg(0x64, 0x00);
+ dev->reg.init_reg(0x65, 0x00);
+ dev->reg.init_reg(0x67, 0x7f);
+ dev->reg.init_reg(0x68, 0x7f);
+ dev->reg.init_reg(0x69, 0x01);
+ dev->reg.init_reg(0x6a, 0x01);
+ dev->reg.init_reg(0x70, 0x01);
+ dev->reg.init_reg(0x71, 0x00);
+ dev->reg.init_reg(0x72, 0x02);
+ dev->reg.init_reg(0x73, 0x01);
+ dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x76, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x79, 0x3f); // SENSOR_DEF
+ dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x7b, 0x09); // SENSOR_DEF
+ dev->reg.init_reg(0x7c, 0x99); // SENSOR_DEF
+ dev->reg.init_reg(0x7d, 0x20);
+ dev->reg.init_reg(0x7f, 0x05);
+ dev->reg.init_reg(0x80, 0x4f);
+ dev->reg.init_reg(0x87, 0x02);
+ dev->reg.init_reg(0x94, 0xff);
+ dev->reg.init_reg(0x9d, 0x04);
+ dev->reg.init_reg(0x9e, 0x00);
+ dev->reg.init_reg(0xa1, 0xe0);
+ dev->reg.init_reg(0xa2, 0x1f);
+ dev->reg.init_reg(0xab, 0xc0);
+ dev->reg.init_reg(0xbb, 0x00);
+ dev->reg.init_reg(0xbc, 0x0f);
+ dev->reg.init_reg(0xdb, 0xff);
+ dev->reg.init_reg(0xfe, 0x08);
+ dev->reg.init_reg(0xff, 0x02);
+ dev->reg.init_reg(0x98, 0x20);
+ dev->reg.init_reg(0x99, 0x00);
+ dev->reg.init_reg(0x9a, 0x90);
+ dev->reg.init_reg(0x9b, 0x00);
+ dev->reg.init_reg(0xf8, 0x05);
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+ sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res);
+
+ /* initalize calibration reg */
+ dev->calib_reg = dev->reg;
+}
+
+/**@brief send slope table for motor movement
+ * Send slope_table in machine byte order
+ * @param dev device to send slope table
+ * @param table_nr index of the slope table in ASIC memory
+ * Must be in the [0-4] range.
+ * @param slope_table pointer to 16 bit values array of the slope table
+ * @param steps number of elements in the slope table
+ */
+static void gl846_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table,
+ int steps)
+{
+ DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps);
+ int i;
+ char msg[10000];
+
+ /* sanity check */
+ if(table_nr<0 || table_nr>4)
+ {
+ throw SaneException("invalid table number %d", table_nr);
+ }
+
+ std::vector<uint8_t> table(steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ std::sprintf(msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++)
+ {
+ std::sprintf(msg+strlen(msg), "%d", slope_table[i]);
+ }
+ DBG (DBG_io, "%s: %s\n", __func__, msg);
+ }
+
+ if (dev->interface->is_mock()) {
+ dev->interface->record_slope_table(table_nr, slope_table);
+ }
+ // slope table addresses are fixed
+ dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data());
+}
+
+/**
+ * Set register values of Analog Device type frontend
+ * */
+static void gl846_set_adi_fe(Genesys_Device* dev, uint8_t set)
+{
+ DBG_HELPER(dbg);
+ int i;
+
+ // wait for FE to be ready
+ auto status = scanner_read_status(*dev);
+ while (status.is_front_end_busy) {
+ dev->interface->sleep_ms(10);
+ status = scanner_read_status(*dev);
+ };
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+ dev->frontend = dev->frontend_initial;
+ }
+
+ // write them to analog frontend
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i));
+ }
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i));
+ }
+}
+
+// Set values of analog frontend
+void CommandSetGl846::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const
+{
+ DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" :
+ set == AFE_SET ? "set" :
+ set == AFE_POWER_SAVE ? "powersave" : "huh?");
+ (void) sensor;
+
+ /* route to specific analog frontend setup */
+ uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET;
+ switch (frontend_type) {
+ case 0x02: /* ADI FE */
+ gl846_set_adi_fe(dev, set);
+ break;
+ default:
+ throw SaneException("unsupported frontend type %d", frontend_type);
+ }
+}
+
+
+// @brief set up motor related register for scan
+static void gl846_init_motor_regs_scan(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const Motor_Profile& motor_profile,
+ unsigned int scan_exposure_time,
+ unsigned scan_yres,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ MotorFlag flags)
+{
+ DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, scan_yres=%d, step_type=%d, scan_lines=%d, "
+ "scan_dummy=%d, feed_steps=%d, flags=%x",
+ scan_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type),
+ scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
+ int use_fast_fed;
+ unsigned int fast_dpi;
+ unsigned int feedl, dist;
+ GenesysRegister *r;
+ uint32_t z1, z2;
+ unsigned int min_restep = 0x20;
+ uint8_t val;
+ unsigned int ccdlmt,tgtime;
+
+ unsigned step_multiplier = gl846_get_step_multiplier(reg);
+
+ use_fast_fed=0;
+ /* no fast fed since feed works well */
+ if (dev->settings.yres == 4444 && feed_steps > 100 && !has_flag(flags, MotorFlag::FEED)) {
+ use_fast_fed = 1;
+ }
+ DBG (DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
+
+ reg->set24(REG_LINCNT, scan_lines);
+ DBG (DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
+
+ /* compute register 02 value */
+ r = sanei_genesys_get_address(reg, REG_0x02);
+ r->value = 0x00;
+ sanei_genesys_set_motor_power(*reg, true);
+
+ if (use_fast_fed)
+ r->value |= REG_0x02_FASTFED;
+ else
+ r->value &= ~REG_0x02_FASTFED;
+
+ if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
+ r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
+ }
+
+ if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) ||(scan_yres>=sensor.optical_res)) {
+ r->value |= REG_0x02_ACDCDIS;
+ }
+ if (has_flag(flags, MotorFlag::REVERSE)) {
+ r->value |= REG_0x02_MTRREV;
+ } else {
+ r->value &= ~REG_0x02_MTRREV;
+ }
+
+ /* scan and backtracking slope table */
+ auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres,
+ scan_exposure_time, dev->motor.base_ydpi,
+ step_multiplier, motor_profile);
+
+ gl846_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
+ gl846_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
+
+ /* fast table */
+ fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
+
+ // BUG: looks like for fast moves we use inconsistent step type
+ StepType fast_step_type = motor_profile.step_type;
+ if (static_cast<unsigned>(motor_profile.step_type) >= static_cast<unsigned>(StepType::QUARTER)) {
+ fast_step_type = StepType::QUARTER;
+ }
+
+ Motor_Profile fast_motor_profile = motor_profile;
+ fast_motor_profile.step_type = fast_step_type;
+
+ auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_dpi,
+ scan_exposure_time, dev->motor.base_ydpi,
+ step_multiplier, fast_motor_profile);
+
+ gl846_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count);
+ gl846_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
+ gl846_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count);
+
+ /* correct move distance by acceleration and deceleration amounts */
+ feedl=feed_steps;
+ if (use_fast_fed)
+ {
+ feedl <<= static_cast<unsigned>(fast_step_type);
+ dist = (scan_table.steps_count + 2 * fast_table.steps_count);
+ /* TODO read and decode REG_0xAB */
+ r = sanei_genesys_get_address (reg, 0x5e);
+ dist += (r->value & 31);
+ /* FEDCNT */
+ r = sanei_genesys_get_address(reg, REG_FEDCNT);
+ dist += r->value;
+ }
+ else
+ {
+ feedl <<= static_cast<unsigned>(motor_profile.step_type);
+ dist = scan_table.steps_count;
+ if (has_flag(flags, MotorFlag::FEED)) {
+ dist *= 2;
+ }
+ }
+ DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
+
+ /* check for overflow */
+ if (dist < feedl) {
+ feedl -= dist;
+ } else {
+ feedl = 0;
+ }
+
+ reg->set24(REG_FEEDL, feedl);
+ DBG (DBG_io ,"%s: feedl=%d\n",__func__,feedl);
+
+ r = sanei_genesys_get_address(reg, REG_0x0C);
+ ccdlmt = (r->value & REG_0x0C_CCDLMT) + 1;
+
+ r = sanei_genesys_get_address(reg, REG_0x1C);
+ tgtime = 1 << (r->value & REG_0x1C_TGTIME);
+
+ /* hi res motor speed GPIO */
+ /*
+ uint8_t effective = dev->interface->read_register(REG_0x6C);
+ */
+
+ /* if quarter step, bipolar Vref2 */
+ /* XXX STEF XXX GPIO
+ if (motor_profile.step_type > 1)
+ {
+ if (motor_profile.step_type < 3)
+ {
+ val = effective & ~REG_0x6C_GPIO13;
+ }
+ else
+ {
+ val = effective | REG_0x6C_GPIO13;
+ }
+ }
+ else
+ {
+ val = effective;
+ }
+ dev->interface->write_register(REG_0x6C, val);
+ */
+
+ /* effective scan */
+ /*
+ effective = dev->interface->read_register(REG_0x6C);
+ val = effective | REG_0x6C_GPIO10;
+ dev->interface->write_register(REG_0x6C, val);
+ */
+
+ if(dev->model->gpio_id == GpioId::IMG101) {
+ if (scan_yres == sensor.get_register_hwdpi(scan_yres)) {
+ val=1;
+ }
+ else
+ {
+ val=0;
+ }
+ dev->interface->write_register(REG_0x7E, val);
+ }
+
+ min_restep = (scan_table.steps_count / step_multiplier) / 2 - 1;
+ if (min_restep < 1) {
+ min_restep = 1;
+ }
+ r = sanei_genesys_get_address(reg, REG_FWDSTEP);
+ r->value = min_restep;
+ r = sanei_genesys_get_address(reg, REG_BWDSTEP);
+ r->value = min_restep;
+
+ sanei_genesys_calculate_zmod(use_fast_fed,
+ scan_exposure_time*ccdlmt*tgtime,
+ scan_table.table,
+ scan_table.steps_count,
+ feedl,
+ min_restep * step_multiplier,
+ &z1,
+ &z2);
+
+ DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
+ reg->set24(REG_0x60, z1 | (static_cast<unsigned>(motor_profile.step_type) << (16 + REG_0x60S_STEPSEL)));
+
+ DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
+ reg->set24(REG_0x63, z2 | (static_cast<unsigned>(motor_profile.step_type) << (16 + REG_0x63S_FSTPSEL)));
+
+ r = sanei_genesys_get_address (reg, 0x1e);
+ r->value &= 0xf0; /* 0 dummy lines */
+ r->value |= scan_dummy; /* dummy lines */
+
+ r = sanei_genesys_get_address(reg, REG_0x67);
+ r->value = 0x7f;
+
+ r = sanei_genesys_get_address(reg, REG_0x68);
+ r->value = 0x7f;
+
+ reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier);
+ reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier);
+ reg->set8(REG_FSHDEC, scan_table.steps_count / step_multiplier);
+ reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier);
+ reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier);
+}
+
+
+/** @brief set up registers related to sensor
+ * Set up the following registers
+ 0x01
+ 0x03
+ 0x10-0x015 R/G/B exposures
+ 0x19 EXPDMY
+ 0x2e BWHI
+ 0x2f BWLO
+ 0x04
+ 0x87
+ 0x05
+ 0x2c,0x2d DPISET
+ 0x30,0x31 STRPIXEL
+ 0x32,0x33 ENDPIXEL
+ 0x35,0x36,0x37 MAXWD [25:2] (>>2)
+ 0x38,0x39 LPERIOD
+ 0x34 DUMMY
+ */
+static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, unsigned int exposure_time,
+ const ScanSession& session)
+{
+ DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time);
+ unsigned int dpihw;
+ GenesysRegister *r;
+
+ // resolution is divided according to ccd_pixels_per_system_pixel()
+ unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
+ DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel);
+
+ // to manage high resolution device while keeping good low resolution scanning speed,
+ // we make hardware dpi vary
+ dpihw = sensor.get_register_hwdpi(session.params.xres * ccd_pixels_per_system_pixel);
+ DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw);
+
+ gl846_setup_sensor(dev, sensor, reg);
+
+ dev->cmd_set->set_fe(dev, sensor, AFE_SET);
+
+ /* enable shading */
+ regs_set_optical_off(dev->model->asic_type, *reg);
+ r = sanei_genesys_get_address(reg, REG_0x01);
+ r->value |= REG_0x01_SHDAREA;
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ r->value &= ~REG_0x01_DVDSET;
+ }
+ else
+ {
+ r->value |= REG_0x01_DVDSET;
+ }
+
+ r = sanei_genesys_get_address(reg, REG_0x03);
+ r->value &= ~REG_0x03_AVEENB;
+
+ sanei_genesys_set_lamp_power(dev, sensor, *reg,
+ !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));
+
+ /* BW threshold */
+ r = sanei_genesys_get_address (reg, 0x2e);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address (reg, 0x2f);
+ r->value = dev->settings.threshold;
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address(reg, REG_0x04);
+ switch (session.params.depth) {
+ case 8:
+ r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG_0x04_LINEART;
+ r->value |= REG_0x04_BITSET;
+ break;
+ }
+
+ r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);
+ if (session.params.channels == 1)
+ {
+ switch (session.params.color_filter)
+ {
+ case ColorFilter::RED:
+ r->value |= 0x24;
+ break;
+ case ColorFilter::BLUE:
+ r->value |= 0x2c;
+ break;
+ case ColorFilter::GREEN:
+ r->value |= 0x28;
+ break;
+ default:
+ break; // should not happen
+ }
+ } else {
+ r->value |= 0x20; // mono
+ }
+
+ sanei_genesys_set_dpihw(*reg, sensor, dpihw);
+
+ if (should_enable_gamma(session, sensor)) {
+ reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
+ } else {
+ reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
+ }
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ /* we set up LEDADD only when asked */
+ if (dev->model->is_cis) {
+ r = sanei_genesys_get_address (reg, 0x87);
+ r->value &= ~REG_0x87_LEDADD;
+ if (session.enable_ledadd) {
+ r->value |= REG_0x87_LEDADD;
+ }
+ /* RGB weighting
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value &= ~REG_0x01_TRUEGRAY;
+ if (session.enable_ledadd))
+ {
+ r->value |= REG_0x01_TRUEGRAY;
+ }*/
+ }
+
+ unsigned dpiset = session.params.xres * ccd_pixels_per_system_pixel;
+ reg->set16(REG_DPISET, dpiset);
+ DBG(DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
+
+ reg->set16(REG_STRPIXEL, session.pixel_startx);
+ reg->set16(REG_ENDPIXEL, session.pixel_endx);
+
+ build_image_pipeline(dev, session);
+
+ /* MAXWD is expressed in 4 words unit */
+ // BUG: we shouldn't multiply by channels here
+ reg->set24(REG_MAXWD, (session.output_line_bytes_raw * session.params.channels >> 2));
+
+ reg->set16(REG_LPERIOD, exposure_time);
+ DBG (DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time);
+
+ r = sanei_genesys_get_address (reg, 0x34);
+ r->value = sensor.dummy_pixel;
+}
+
+void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const
+{
+ DBG_HELPER(dbg);
+ session.assert_computed();
+
+ int move;
+ int exposure_time;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+
+ dummy = 3-session.params.channels;
+
+/* slope_dpi */
+/* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis) {
+ slope_dpi = session.params.yres * session.params.channels;
+ } else {
+ slope_dpi = session.params.yres;
+ }
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ exposure_time = sensor.exposure_lperiod;
+ const auto& motor_profile = sanei_genesys_get_motor_profile(*gl846_motor_profiles,
+ dev->model->motor_id,
+ exposure_time);
+
+ DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
+ DBG(DBG_info, "%s : scan_step_type=%d\n", __func__,
+ static_cast<unsigned>(motor_profile.step_type));
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ gl846_init_optical_regs_scan(dev, sensor, reg, exposure_time, session);
+
+/*** motor parameters ***/
+
+ /* add tl_y to base movement */
+ move = session.params.starty;
+ DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
+
+ MotorFlag mflags = MotorFlag::NONE;
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) {
+ mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE;
+ }
+ if (has_flag(session.params.flags, ScanFlag::FEEDING)) {
+ mflags |= MotorFlag::FEED;
+ }
+ if (has_flag(session.params.flags, ScanFlag::REVERSE)) {
+ mflags |= MotorFlag::REVERSE;
+ }
+
+ gl846_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi,
+ dev->model->is_cis ? session.output_line_count * session.params.channels
+ : session.output_line_count,
+ dummy, move, mflags);
+
+ /*** prepares data reordering ***/
+
+ dev->read_buffer.clear();
+ dev->read_buffer.alloc(session.buffer_size_read);
+
+ dev->read_active = true;
+
+ dev->session = session;
+
+ dev->total_bytes_read = 0;
+ dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines;
+
+ DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read);
+}
+
+ScanSession CommandSetGl846::calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const
+{
+ int start;
+
+ DBG(DBG_info, "%s ", __func__);
+ debug_dump(DBG_info, settings);
+
+ /* start */
+ start = static_cast<int>(dev->model->x_offset);
+ start += static_cast<int>(settings.tl_x);
+ start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = settings.xres;
+ session.params.yres = settings.yres;
+ session.params.startx = start; // not used
+ session.params.starty = 0; // not used
+ session.params.pixels = settings.pixels;
+ session.params.requested_pixels = settings.requested_pixels;
+ session.params.lines = settings.lines;
+ session.params.depth = settings.depth;
+ session.params.channels = settings.get_channels();
+ session.params.scan_method = settings.scan_method;
+ session.params.scan_mode = settings.scan_mode;
+ session.params.color_filter = settings.color_filter;
+ session.params.flags = ScanFlag::NONE;
+
+ compute_session(dev, session, sensor);
+
+ return session;
+}
+
+// for fast power saving methods only, like disabling certain amplifiers
+void CommandSetGl846::save_power(Genesys_Device* dev, bool enable) const
+{
+ (void) dev;
+ DBG_HELPER_ARGS(dbg, "enable = %d", enable);
+}
+
+void CommandSetGl846::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
+{
+ (void) dev;
+ DBG_HELPER_ARGS(dbg, "delay = %d", delay);
+}
+
+// Send the low-level scan command
+void CommandSetGl846::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, bool start_motor) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+ uint8_t val;
+ GenesysRegister *r;
+
+ /* XXX STEF XXX SCAN GPIO */
+ /*
+ val = dev->interface->read_register(REG_0x6C);
+ dev->interface->write_register(REG_0x6C, val);
+ */
+
+ val = REG_0x0D_CLRLNCNT;
+ dev->interface->write_register(REG_0x0D, val);
+ val = REG_0x0D_CLRMCNT;
+ dev->interface->write_register(REG_0x0D, val);
+
+ val = dev->interface->read_register(REG_0x01);
+ val |= REG_0x01_SCAN;
+ dev->interface->write_register(REG_0x01, val);
+ r = sanei_genesys_get_address (reg, REG_0x01);
+ r->value = val;
+
+ scanner_start_action(*dev, start_motor);
+
+ dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
+}
+
+
+// Send the stop scan command
+void CommandSetGl846::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
+ bool check_stop) const
+{
+ (void) reg;
+ DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop);
+
+ if (!dev->model->is_sheetfed) {
+ scanner_stop_action(*dev);
+ }
+}
+
+// Moves the slider to the home (top) postion slowly
+void CommandSetGl846::move_back_home(Genesys_Device* dev, bool wait_until_home) const
+{
+ scanner_move_back_home(*dev, wait_until_home);
+}
+
+// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi
+// from very top of scanner
+void CommandSetGl846::search_start_position(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ int size;
+ Genesys_Register_Set local_reg;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ local_reg = dev->reg;
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ // FIXME: the current approach of doing search only for one resolution does not work on scanners
+ // whith employ different sensors with potentially different settings.
+ const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method);
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0; /*we should give a small offset here~60 steps */
+ session.params.pixels = 600;
+ session.params.lines = dev->model->search_lines;
+ session.params.depth = 8;
+ session.params.channels = 1;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::GREEN;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ // send to scanner
+ dev->interface->write_registers(local_reg);
+
+ size = pixels * dev->model->search_lines;
+
+ std::vector<uint8_t> data(size);
+
+ begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_start_position");
+ end_scan(dev, &local_reg, true);
+ dev->reg = local_reg;
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl846_search_position.pnm", data.data(), 8, 1, pixels,
+ dev->model->search_lines);
+ }
+
+ end_scan(dev, &local_reg, true);
+
+ /* update regs to copy ASIC internal state */
+ dev->reg = local_reg;
+
+ // TODO: find out where sanei_genesys_search_reference_point stores information,
+ // and use that correctly
+ for (auto& sensor_update :
+ sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method))
+ {
+ sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels,
+ dev->model->search_lines);
+ }
+}
+
+// sets up register for coarse gain calibration
+// todo: check it for scanners using it
+void CommandSetGl846::init_regs_for_coarse_calibration(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel();
+ session.params.lines = 20;
+ session.params.depth = 16;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
+ sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres);
+
+ dev->interface->write_registers(regs);
+}
+
+// init registers for shading calibration
+void CommandSetGl846::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ float move;
+
+ dev->calib_channels = 3;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres);
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution,
+ dev->calib_channels,
+ dev->settings.scan_method);
+ dev->calib_total_bytes_to_read = 0;
+ dev->calib_lines = dev->model->shading_lines;
+ if (dev->calib_resolution==4800) {
+ dev->calib_lines *= 2;
+ }
+ dev->calib_pixels = (calib_sensor.sensor_pixels * dev->calib_resolution) /
+ calib_sensor.optical_res;
+
+ DBG(DBG_io, "%s: calib_lines = %zu\n", __func__, dev->calib_lines);
+ DBG(DBG_io, "%s: calib_pixels = %zu\n", __func__, dev->calib_pixels);
+
+ /* this is aworkaround insufficent distance for slope
+ * motor acceleration TODO special motor slope for shading */
+ move=1;
+ if(dev->calib_resolution<1200)
+ {
+ move=40;
+ }
+
+ ScanSession session;
+ session.params.xres = dev->calib_resolution;
+ session.params.yres = dev->calib_resolution;
+ session.params.startx = 0;
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = dev->calib_pixels;
+ session.params.lines = dev->calib_lines;
+ session.params.depth = 16;
+ session.params.channels = dev->calib_channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::DISABLE_BUFFER_FULL_MOVE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ dev->interface->write_registers(regs);
+
+ /* we use GENESYS_FLAG_SHADING_REPARK */
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+}
+
+/** @brief set up registers for the actual scan
+ */
+void CommandSetGl846::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ float move;
+ int move_dpi;
+ float start;
+
+ debug_dump(DBG_info, dev->settings);
+
+ /* steps to move to reach scanning area:
+ - first we move to physical start of scanning
+ either by a fixed steps amount from the black strip
+ or by a fixed amount from parking position,
+ minus the steps done during shading calibration
+ - then we move by the needed offset whitin physical
+ scanning area
+
+ assumption: steps are expressed at maximum motor resolution
+
+ we need:
+ float y_offset;
+ float y_size;
+ float y_offset_calib;
+ mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
+
+ /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
+ relative from origin, else, it is from parking position */
+
+ move_dpi = dev->motor.base_ydpi;
+
+ move = static_cast<float>(dev->model->y_offset);
+ move = static_cast<float>(move + dev->settings.tl_y);
+ move = static_cast<float>((move * move_dpi) / MM_PER_INCH);
+ move -= dev->head_pos(ScanHeadId::PRIMARY);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ /* fast move to scan area */
+ /* we don't move fast the whole distance since it would involve
+ * computing acceleration/deceleration distance for scan
+ * resolution. So leave a remainder for it so scan makes the final
+ * move tuning */
+ if (dev->settings.get_channels() * dev->settings.yres >= 600 && move > 700) {
+ scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move - 500),
+ Direction::FORWARD);
+ move=500;
+ }
+
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ /* start */
+ start = static_cast<float>(dev->model->x_offset);
+ start = static_cast<float>(start + dev->settings.tl_x);
+ start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = static_cast<unsigned>(start);
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = dev->settings.pixels;
+ session.params.requested_pixels = dev->settings.requested_pixels;
+ session.params.lines = dev->settings.lines;
+ session.params.depth = dev->settings.depth;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ // backtracking isn't handled well, so don't enable it
+ session.params.flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &dev->reg, session);
+}
+
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ uint8_t* data, int size) const
+{
+ DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size);
+ uint32_t addr, length, i, x, factor, pixels;
+ uint32_t dpiset, dpihw;
+ uint8_t val,*ptr,*src;
+
+ /* shading data is plit in 3 (up to 5 with IR) areas
+ write(0x10014000,0x00000dd8)
+ URB 23429 bulk_out len 3544 wrote 0x33 0x10 0x....
+ write(0x1003e000,0x00000dd8)
+ write(0x10068000,0x00000dd8)
+ */
+ length = static_cast<uint32_t>(size / 3);
+ unsigned strpixel = dev->session.pixel_startx;
+ unsigned endpixel = dev->session.pixel_endx;
+
+ /* compute deletion factor */
+ dpiset = dev->reg.get16(REG_DPISET);
+ dpihw = sensor.get_register_hwdpi(dpiset);
+ factor=dpihw/dpiset;
+ DBG(DBG_io2, "%s: factor=%d\n", __func__, factor);
+
+ pixels=endpixel-strpixel;
+
+ /* since we're using SHDAREA, substract startx coordinate from shading */
+ strpixel -= (sensor.ccd_start_xoffset * 600) / sensor.optical_res;
+
+ /* turn pixel value into bytes 2x16 bits words */
+ strpixel*=2*2;
+ pixels*=2*2;
+
+ dev->interface->record_key_value("shading_offset", std::to_string(strpixel));
+ dev->interface->record_key_value("shading_pixels", std::to_string(pixels));
+ dev->interface->record_key_value("shading_length", std::to_string(length));
+ dev->interface->record_key_value("shading_factor", std::to_string(factor));
+
+ std::vector<uint8_t> buffer(pixels, 0);
+
+ DBG(DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n", __func__, pixels, pixels);
+
+ /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address
+ * is 8192*reg value */
+
+ /* write actual color channel data */
+ for(i=0;i<3;i++)
+ {
+ /* build up actual shading data by copying the part from the full width one
+ * to the one corresponding to SHDAREA */
+ ptr = buffer.data();
+
+ /* iterate on both sensor segment */
+ for(x=0;x<pixels;x+=4*factor)
+ {
+ /* coefficient source */
+ src=(data+strpixel+i*length)+x;
+
+ /* coefficient copy */
+ ptr[0]=src[0];
+ ptr[1]=src[1];
+ ptr[2]=src[2];
+ ptr[3]=src[3];
+
+ /* next shading coefficient */
+ ptr+=4;
+ }
+
+ val = dev->interface->read_register(0xd0+i);
+ addr = val * 8192 + 0x10000000;
+ dev->interface->write_ahb(addr, pixels, buffer.data());
+ }
+}
+
+/** @brief calibrates led exposure
+ * Calibrate exposure by scanning a white area until the used exposure gives
+ * data white enough.
+ * @param dev device to calibrate
+ */
+SensorExposure CommandSetGl846::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int total_size;
+ int used_res;
+ int i, j;
+ int val;
+ int channels;
+ int avg[3], top[3], bottom[3];
+ int turn;
+ uint16_t exp[3];
+
+ float move = static_cast<float>(dev->model->y_offset_calib_white);
+ move = static_cast<float>((move * (dev->motor.base_ydpi / 4)) / MM_PER_INCH);
+ if(move>20)
+ {
+ scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move),
+ Direction::FORWARD);
+ }
+ DBG(DBG_io, "%s: move=%f steps\n", __func__, move);
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ used_res = sensor.get_register_hwdpi(dev->settings.xres);
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, used_res, channels,
+ dev->settings.scan_method);
+ num_pixels = (calib_sensor.sensor_pixels * used_res) / calib_sensor.optical_res;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ ScanSession session;
+ session.params.xres = used_res;
+ session.params.yres = used_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ total_size = num_pixels * channels * (session.params.depth / 8) * 1;
+ std::vector<uint8_t> line(total_size);
+
+ /* initial loop values and boundaries */
+ exp[0] = calib_sensor.exposure.red;
+ exp[1] = calib_sensor.exposure.green;
+ exp[2] = calib_sensor.exposure.blue;
+
+ bottom[0]=29000;
+ bottom[1]=29000;
+ bottom[2]=29000;
+
+ top[0]=41000;
+ top[1]=51000;
+ top[2]=51000;
+
+ turn = 0;
+
+ /* no move during led calibration */
+ sanei_genesys_set_motor_power(regs, false);
+ bool acceptable = false;
+ do
+ {
+ // set up exposure
+ regs.set16(REG_EXPR, exp[0]);
+ regs.set16(REG_EXPG, exp[1]);
+ regs.set16(REG_EXPB, exp[2]);
+
+ // write registers and scan data
+ dev->interface->write_registers(regs);
+
+ DBG(DBG_info, "%s: starting line reading\n", __func__);
+ begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("led_calibration");
+ scanner_stop_action(*dev);
+ move_back_home(dev, true);
+ return calib_sensor.exposure;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ // stop scanning
+ scanner_stop_action(*dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl846_led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, line.data(), session.params.depth,
+ channels, num_pixels, 1);
+ }
+
+ /* compute average */
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
+
+ /* check if exposure gives average within the boundaries */
+ acceptable = true;
+ for(i=0;i<3;i++)
+ {
+ if(avg[i]<bottom[i])
+ {
+ exp[i]=(exp[i]*bottom[i])/avg[i];
+ acceptable = false;
+ }
+ if(avg[i]>top[i])
+ {
+ exp[i]=(exp[i]*top[i])/avg[i];
+ acceptable = false;
+ }
+ }
+
+ turn++;
+ }
+ while (!acceptable && turn < 100);
+
+ DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
+
+ // set these values as final ones for scan
+ dev->reg.set16(REG_EXPR, exp[0]);
+ dev->reg.set16(REG_EXPG, exp[1]);
+ dev->reg.set16(REG_EXPB, exp[2]);
+
+ /* go back home */
+ if(move>20)
+ {
+ move_back_home(dev, true);
+ }
+
+ return { exp[0], exp[1], exp[2] };
+}
+
+/**
+ * set up GPIO/GPOE for idle state
+ */
+static void gl846_init_gpio(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ int idx=0;
+
+ /* search GPIO profile */
+ while (gpios[idx].gpio_id != GpioId::UNKNOWN && dev->model->gpio_id != gpios[idx].gpio_id) {
+ idx++;
+ }
+ if (gpios[idx].gpio_id == GpioId::UNKNOWN)
+ {
+ throw SaneException("failed to find GPIO profile for sensor_id=%d",
+ static_cast<unsigned>(dev->model->sensor_id));
+ }
+
+ dev->interface->write_register(REG_0xA7, gpios[idx].ra7);
+ dev->interface->write_register(REG_0xA6, gpios[idx].ra6);
+
+ dev->interface->write_register(REG_0x6B, gpios[idx].r6b);
+ dev->interface->write_register(REG_0x6C, gpios[idx].r6c);
+ dev->interface->write_register(REG_0x6D, gpios[idx].r6d);
+ dev->interface->write_register(REG_0x6E, gpios[idx].r6e);
+ dev->interface->write_register(REG_0x6F, gpios[idx].r6f);
+
+ dev->interface->write_register(REG_0xA8, gpios[idx].ra8);
+ dev->interface->write_register(REG_0xA9, gpios[idx].ra9);
+}
+
+/**
+ * set memory layout by filling values in dedicated registers
+ */
+static void gl846_init_memory_layout(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ int idx = 0, i;
+ uint8_t val;
+
+ /* point to per model memory layout */
+ idx = 0;
+ while (layouts[idx].model != nullptr && strcmp(dev->model->name,layouts[idx].model)!=0) {
+ if(strcmp(dev->model->name,layouts[idx].model)!=0)
+ idx++;
+ }
+ if (layouts[idx].model == nullptr) {
+ throw SaneException("failed to find memory layout for model %s", dev->model->name);
+ }
+
+ /* CLKSET and DRAMSEL */
+ val = layouts[idx].dramsel;
+ dev->interface->write_register(REG_0x0B, val);
+ dev->reg.find_reg(0x0b).value = val;
+
+ /* prevent further writings by bulk write register */
+ dev->reg.remove_reg(0x0b);
+
+ /* setup base address for shading and scanned data. */
+ for(i=0;i<10;i++)
+ {
+ dev->interface->write_register(0xe0+i, layouts[idx].rx[i]);
+ }
+}
+
+/* *
+ * initialize ASIC from power on condition
+ */
+void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const
+{
+ DBG_HELPER(dbg);
+ uint8_t val;
+
+ // reset ASIC if cold boot
+ if (cold) {
+ dev->interface->write_register(0x0e, 0x01);
+ dev->interface->write_register(0x0e, 0x00);
+ }
+
+ if(dev->usb_mode == 1)
+ {
+ val = 0x14;
+ }
+ else
+ {
+ val = 0x11;
+ }
+ dev->interface->write_0x8c(0x0f, val);
+
+ // test CHKVER
+ val = dev->interface->read_register(REG_0x40);
+ if (val & REG_0x40_CHKVER) {
+ val = dev->interface->read_register(0x00);
+ DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
+ }
+
+ /* Set default values for registers */
+ gl846_init_registers (dev);
+
+ // Write initial registers
+ dev->interface->write_registers(dev->reg);
+
+ /* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
+ val = dev->reg.find_reg(0x0b).value & REG_0x0B_DRAMSEL;
+ val = (val | REG_0x0B_ENBDRAM);
+ dev->interface->write_register(REG_0x0B, val);
+ dev->reg.find_reg(0x0b).value = val;
+
+ /* CIS_LINE */
+ if (dev->model->is_cis)
+ {
+ dev->reg.init_reg(0x08, REG_0x08_CIS_LINE);
+ dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value);
+ }
+
+ // set up clocks
+ dev->interface->write_0x8c(0x10, 0x0e);
+ dev->interface->write_0x8c(0x13, 0x0e);
+
+ // setup gpio
+ gl846_init_gpio(dev);
+
+ // setup internal memory layout
+ gl846_init_memory_layout(dev);
+
+ dev->reg.init_reg(0xf8, 0x05);
+ dev->interface->write_register(0xf8, dev->reg.find_reg(0xf8).value);
+}
+
+/**
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+void CommandSetGl846::init(Genesys_Device* dev) const
+{
+ DBG_INIT ();
+ DBG_HELPER(dbg);
+
+ sanei_genesys_asic_init(dev, 0);
+}
+
+void CommandSetGl846::update_hardware_sensors(Genesys_Scanner* s) const
+{
+ DBG_HELPER(dbg);
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+ uint8_t val;
+ uint8_t scan, file, email, copy;
+ switch(s->dev->model->gpio_id)
+ {
+ default:
+ scan=0x01;
+ file=0x02;
+ email=0x04;
+ copy=0x08;
+ }
+ val = s->dev->interface->read_register(REG_0x6D);
+
+ s->buttons[BUTTON_SCAN_SW].write((val & scan) == 0);
+ s->buttons[BUTTON_FILE_SW].write((val & file) == 0);
+ s->buttons[BUTTON_EMAIL_SW].write((val & email) == 0);
+ s->buttons[BUTTON_COPY_SW].write((val & copy) == 0);
+}
+
+
+void CommandSetGl846::update_home_sensor_gpio(Genesys_Device& dev) const
+{
+ DBG_HELPER(dbg);
+
+ std::uint8_t val = dev.interface->read_register(REG_0x6C);
+ val |= 0x41;
+ dev.interface->write_register(REG_0x6C, val);
+}
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward true if searching forward, false if searching backward
+ * @param black true if searching for a black strip, false for a white strip
+ */
+void CommandSetGl846::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward,
+ bool black) const
+{
+ DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse");
+ unsigned int pixels, lines, channels;
+ Genesys_Register_Set local_reg;
+ size_t size;
+ unsigned int pass, count, found, x, y;
+ char title[80];
+
+ set_fe(dev, sensor, AFE_SET);
+
+ scanner_stop_action(*dev);
+
+ // set up for a gray scan at lowest dpi
+ const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method);
+ unsigned dpi = resolution_settings.get_min_resolution_x();
+ channels = 1;
+ /* 10 MM */
+ /* lines = (10 * dpi) / MM_PER_INCH; */
+ /* shading calibation is done with dev->motor.base_ydpi */
+ lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
+ pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res;
+
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ local_reg = dev->reg;
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA;
+ if (!forward) {
+ session.params.flags |= ScanFlag::REVERSE;
+ }
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ size = pixels * channels * lines * (session.params.depth / 8);
+ std::vector<uint8_t> data(size);
+
+ dev->interface->write_registers(local_reg);
+
+ begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_strip");
+ scanner_stop_action(*dev);
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ scanner_stop_action(*dev);
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl846_search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file(title, data.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < 20 && !found)
+ {
+ dev->interface->write_registers(local_reg);
+
+ // now start scan
+ begin_scan(dev, sensor, &local_reg, true);
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ scanner_stop_action(*dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl846_search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file(title, data.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
+ pass, y);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+
+ if (found)
+ {
+ DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
+ }
+ else
+ {
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white");
+ }
+}
+
+/**
+ * average dark pixels of a 8 bits scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG(DBG_info, "%s: average = %d\n", __func__, average);
+ return average;
+}
+
+void CommandSetGl846::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ unsigned channels;
+ int pass = 0, avg, total_size;
+ int topavg, bottomavg, lines;
+ int top, bottom, black_pixels, pixels;
+
+ // no gain nor offset for AKM AFE
+ uint8_t reg04 = dev->interface->read_register(REG_0x04);
+ if ((reg04 & REG_0x04_FESET) == 0x02) {
+ return;
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ dev->calib_pixels = sensor.sensor_pixels;
+ lines=1;
+ pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res;
+ black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
+ DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = sensor.optical_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ total_size = pixels * channels * lines * (session.params.depth / 8);
+
+ std::vector<uint8_t> first_line(total_size);
+ std::vector<uint8_t> second_line(total_size);
+
+ /* init gain */
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+
+ /* scan with no move */
+ bottom = 10;
+ dev->frontend.set_offset(0, bottom);
+ dev->frontend.set_offset(1, bottom);
+ dev->frontend.set_offset(2, bottom);
+
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("offset_calibration");
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl846_offset%03d.pnm", bottom);
+ sanei_genesys_write_pnm_file(fn, first_line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
+
+ /* now top value */
+ top = 255;
+ dev->frontend.set_offset(0, top);
+ dev->frontend.set_offset(1, top);
+ dev->frontend.set_offset(2, top);
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+
+ topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.set_offset(0, (top + bottom) / 2);
+ dev->frontend.set_offset(1, (top + bottom) / 2);
+ dev->frontend.set_offset(2, (top + bottom) / 2);
+
+ // scan with no move
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl846_offset%03d.pnm", dev->frontend.get_offset(1));
+ sanei_genesys_write_pnm_file(fn, second_line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.get_offset(1);
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.get_offset(1);
+ }
+ }
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+}
+
+void CommandSetGl846::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const
+{
+ DBG_HELPER(dbg);
+ int pixels;
+ int total_size;
+ int i, j, channels;
+ int max[3];
+ float gain[3],coeff;
+ int val, code, lines;
+
+ DBG(DBG_proc, "%s: dpi = %d\n", __func__, dpi);
+
+ // no gain nor offset for AKM AFE
+ uint8_t reg04 = dev->interface->read_register(REG_0x04);
+ if ((reg04 & REG_0x04_FESET) == 0x02) {
+ return;
+ }
+
+ /* coarse gain calibration is always done in color mode */
+ channels = 3;
+
+ /* follow CKSEL */
+ if(dev->settings.xres<sensor.optical_res)
+ {
+ coeff = 0.9f;
+ }
+ else
+ {
+ coeff=1.0;
+ }
+ lines=10;
+ pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res;
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = sensor.optical_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ try {
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
+ throw;
+ }
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ dev->interface->write_registers(regs);
+
+ total_size = pixels * channels * (16 / session.params.depth) * lines;
+
+ std::vector<uint8_t> line(total_size);
+
+ set_fe(dev, sensor, AFE_SET);
+ begin_scan(dev, sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("coarse_gain_calibration");
+ scanner_stop_action(*dev);
+ move_back_home(dev, true);
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl846_gain.pnm", line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* average value on each channel */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = pixels/4; i < (pixels*3/4); i++)
+ {
+ if (dev->model->is_cis)
+ val = line[i + j * pixels];
+ else
+ val = line[i * channels + j];
+
+ max[j] += val;
+ }
+ max[j] = max[j] / (pixels/2);
+
+ gain[j] = (static_cast<float>(sensor.gain_white_ref) * coeff) / max[j];
+
+ /* turn logical gain value into gain code, checking for overflow */
+ code = static_cast<int>(283 - 208 / gain[j]);
+ if (code > 255)
+ code = 255;
+ else if (code < 0)
+ code = 0;
+ dev->frontend.set_gain(j, code);
+
+ DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j],
+ dev->frontend.get_gain(j));
+ }
+
+ if (dev->model->is_cis) {
+ uint8_t gain0 = dev->frontend.get_gain(0);
+ if (gain0 > dev->frontend.get_gain(1)) {
+ gain0 = dev->frontend.get_gain(1);
+ }
+ if (gain0 > dev->frontend.get_gain(2)) {
+ gain0 = dev->frontend.get_gain(2);
+ }
+ dev->frontend.set_gain(0, gain0);
+ dev->frontend.set_gain(1, gain0);
+ dev->frontend.set_gain(2, gain0);
+ }
+
+ scanner_stop_action(*dev);
+
+ move_back_home(dev, true);
+}
+
+bool CommandSetGl846::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
+{
+ (void) dev;
+ return false;
+}
+
+void CommandSetGl846::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const
+{
+ (void) dev;
+ (void) sensor;
+ (void) regs;
+ (void) channels;
+ (void) total_size;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl846::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ sanei_genesys_send_gamma_table(dev, sensor);
+}
+
+void CommandSetGl846::wait_for_motor_stop(Genesys_Device* dev) const
+{
+ (void) dev;
+}
+
+void CommandSetGl846::load_document(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl846::detect_document_end(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl846::eject_document(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl846::move_to_ta(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+std::unique_ptr<CommandSet> create_gl846_cmd_set()
+{
+ return std::unique_ptr<CommandSet>(new CommandSetGl846{});
+}
+
+} // namespace gl846
+} // namespace genesys
diff --git a/backend/genesys/gl846.h b/backend/genesys/gl846.h
new file mode 100644
index 0000000..258015a
--- /dev/null
+++ b/backend/genesys/gl846.h
@@ -0,0 +1,218 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2012-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "genesys.h"
+#include "command_set.h"
+
+#ifndef BACKEND_GENESYS_GL846_H
+#define BACKEND_GENESYS_GL846_H
+
+namespace genesys {
+namespace gl846 {
+
+typedef struct
+{
+ GpioId gpio_id;
+ uint8_t r6b;
+ uint8_t r6c;
+ uint8_t r6d;
+ uint8_t r6e;
+ uint8_t r6f;
+ uint8_t ra6;
+ uint8_t ra7;
+ uint8_t ra8;
+ uint8_t ra9;
+} Gpio_Profile;
+
+static Gpio_Profile gpios[]={
+ { GpioId::IMG101, 0x72, 0x1f, 0xa4, 0x13, 0xa7, 0x11, 0xff, 0x19, 0x05},
+ { GpioId::PLUSTEK_OPTICBOOK_3800, 0x30, 0x01, 0x80, 0x2d, 0x80, 0x0c, 0x8f, 0x08, 0x04},
+ { GpioId::UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+typedef struct
+{
+ const char *model;
+ uint8_t dramsel;
+ /* shading data address */
+ uint8_t rd0;
+ uint8_t rd1;
+ uint8_t rd2;
+ /* scanned data address */
+ uint8_t rx[24];
+} Memory_layout;
+
+static Memory_layout layouts[]={
+ /* Image formula 101 */
+ {
+ "canon-image-formula-101",
+ 0x8b,
+ 0x0a, 0x1b, 0x00,
+ { /* RED ODD START / RED ODD END */
+ 0x00, 0xb0, 0x05, 0xe7, /* [0x00b0, 0x05e7] 1336*4000w */
+ /* RED EVEN START / RED EVEN END */
+ 0x05, 0xe8, 0x0b, 0x1f, /* [0x05e8, 0x0b1f] */
+ /* GREEN ODD START / GREEN ODD END */
+ 0x0b, 0x20, 0x10, 0x57, /* [0x0b20, 0x1057] */
+ /* GREEN EVEN START / GREEN EVEN END */
+ 0x10, 0x58, 0x15, 0x8f, /* [0x1058, 0x158f] */
+ /* BLUE ODD START / BLUE ODD END */
+ 0x15, 0x90, 0x1a, 0xc7, /* [0x1590,0x1ac7] */
+ /* BLUE EVEN START / BLUE EVEN END */
+ 0x1a, 0xc8, 0x1f, 0xff /* [0x1ac8,0x1fff] */
+ }
+ },
+ /* OpticBook 3800 */
+ {
+ "plustek-opticbook-3800",
+ 0x2a,
+ 0x0a, 0x0a, 0x0a,
+ { /* RED ODD START / RED ODD END */
+ 0x00, 0x68, 0x03, 0x00,
+ /* RED EVEN START / RED EVEN END */
+ 0x03, 0x01, 0x05, 0x99,
+ /* GREEN ODD START / GREEN ODD END */
+ 0x05, 0x9a, 0x08, 0x32,
+ /* GREEN EVEN START / GREEN EVEN END */
+ 0x08, 0x33, 0x0a, 0xcb,
+ /* BLUE ODD START / BLUE ODD END */
+ 0x0a, 0xcc, 0x0d, 0x64,
+ /* BLUE EVEN START / BLUE EVEN END */
+ 0x0d, 0x65, 0x0f, 0xfd
+ }
+ },
+ /* list terminating entry */
+ { nullptr, 0, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }
+};
+
+class CommandSetGl846 : public CommandSet
+{
+public:
+ ~CommandSetGl846() override = default;
+
+ bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override;
+
+ void init(Genesys_Device* dev) const override;
+
+ void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const override;
+
+ void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const override;
+
+ void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const override;
+ void set_powersaving(Genesys_Device* dev, int delay) const override;
+ void save_power(Genesys_Device* dev, bool enable) const override;
+
+ void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, bool start_motor) const override;
+
+ void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override;
+
+ void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void search_start_position(Genesys_Device* dev) const override;
+
+ void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const override;
+
+ SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void wait_for_motor_stop(Genesys_Device* dev) const override;
+
+ void move_back_home(Genesys_Device* dev, bool wait_until_home) const override;
+
+ void update_hardware_sensors(struct Genesys_Scanner* s) const override;
+
+ bool needs_update_home_sensor_gpio() const override { return true; }
+
+ void update_home_sensor_gpio(Genesys_Device& dev) const override;
+
+ void load_document(Genesys_Device* dev) const override;
+
+ void detect_document_end(Genesys_Device* dev) const override;
+
+ void eject_document(Genesys_Device* dev) const override;
+
+ void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const override;
+
+ void move_to_ta(Genesys_Device* dev) const override;
+
+ void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,
+ int size) const override;
+
+ ScanSession calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const override;
+
+ void asic_boot(Genesys_Device* dev, bool cold) const override;
+};
+
+enum SlopeTable
+{
+ SCAN_TABLE = 0, // table 1 at 0x4000
+ BACKTRACK_TABLE = 1, // table 2 at 0x4800
+ STOP_TABLE = 2, // table 3 at 0x5000
+ FAST_TABLE = 3, // table 4 at 0x5800
+ HOME_TABLE = 4, // table 5 at 0x6000
+};
+
+} // namespace gl846
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL846_H
diff --git a/backend/genesys/gl846_registers.h b/backend/genesys/gl846_registers.h
new file mode 100644
index 0000000..39b3029
--- /dev/null
+++ b/backend/genesys/gl846_registers.h
@@ -0,0 +1,351 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL846_REGISTERS_H
+#define BACKEND_GENESYS_GL846_REGISTERS_H
+
+#include <cstdint>
+
+namespace genesys {
+namespace gl846 {
+
+using RegAddr = std::uint16_t;
+using RegMask = std::uint8_t;
+using RegShift = unsigned;
+
+static constexpr RegAddr REG_0x01 = 0x01;
+static constexpr RegMask REG_0x01_CISSET = 0x80;
+static constexpr RegMask REG_0x01_DOGENB = 0x40;
+static constexpr RegMask REG_0x01_DVDSET = 0x20;
+static constexpr RegMask REG_0x01_STAGGER = 0x10;
+static constexpr RegMask REG_0x01_COMPENB = 0x08;
+static constexpr RegMask REG_0x01_TRUEGRAY = 0x04;
+static constexpr RegMask REG_0x01_SHDAREA = 0x02;
+static constexpr RegMask REG_0x01_SCAN = 0x01;
+
+static constexpr RegAddr REG_0x02 = 0x02;
+static constexpr RegMask REG_0x02_NOTHOME = 0x80;
+static constexpr RegMask REG_0x02_ACDCDIS = 0x40;
+static constexpr RegMask REG_0x02_AGOHOME = 0x20;
+static constexpr RegMask REG_0x02_MTRPWR = 0x10;
+static constexpr RegMask REG_0x02_FASTFED = 0x08;
+static constexpr RegMask REG_0x02_MTRREV = 0x04;
+static constexpr RegMask REG_0x02_HOMENEG = 0x02;
+static constexpr RegMask REG_0x02_LONGCURV = 0x01;
+
+static constexpr RegAddr REG_0x03 = 0x03;
+static constexpr RegMask REG_0x03_LAMPDOG = 0x80;
+static constexpr RegMask REG_0x03_AVEENB = 0x40;
+static constexpr RegMask REG_0x03_XPASEL = 0x20;
+static constexpr RegMask REG_0x03_LAMPPWR = 0x10;
+static constexpr RegMask REG_0x03_LAMPTIM = 0x0f;
+
+static constexpr RegAddr REG_0x04 = 0x04;
+static constexpr RegMask REG_0x04_LINEART = 0x80;
+static constexpr RegMask REG_0x04_BITSET = 0x40;
+static constexpr RegMask REG_0x04_AFEMOD = 0x30;
+static constexpr RegMask REG_0x04_FILTER = 0x0c;
+static constexpr RegMask REG_0x04_FESET = 0x03;
+
+static constexpr RegShift REG_0x04S_AFEMOD = 4;
+
+static constexpr RegAddr REG_0x05 = 0x05;
+static constexpr RegMask REG_0x05_DPIHW = 0xc0;
+static constexpr RegMask REG_0x05_DPIHW_600 = 0x00;
+static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40;
+static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80;
+static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0;
+static constexpr RegMask REG_0x05_MTLLAMP = 0x30;
+static constexpr RegMask REG_0x05_GMMENB = 0x08;
+static constexpr RegMask REG_0x05_MTLBASE = 0x03;
+
+static constexpr RegAddr REG_0x06 = 0x06;
+static constexpr RegMask REG_0x06_SCANMOD = 0xe0;
+static constexpr RegShift REG_0x06S_SCANMOD = 5;
+static constexpr RegMask REG_0x06_PWRBIT = 0x10;
+static constexpr RegMask REG_0x06_GAIN4 = 0x08;
+static constexpr RegMask REG_0x06_OPTEST = 0x07;
+
+static constexpr RegMask REG_0x07_LAMPSIM = 0x80;
+
+static constexpr RegMask REG_0x08_DRAM2X = 0x80;
+static constexpr RegMask REG_0x08_MPENB = 0x20;
+static constexpr RegMask REG_0x08_CIS_LINE = 0x10;
+static constexpr RegMask REG_0x08_IR1ENB = 0x08;
+static constexpr RegMask REG_0x08_IR2ENB = 0x04;
+static constexpr RegMask REG_0x08_ENB24M = 0x01;
+
+static constexpr RegMask REG_0x09_MCNTSET = 0xc0;
+static constexpr RegMask REG_0x09_EVEN1ST = 0x20;
+static constexpr RegMask REG_0x09_BLINE1ST = 0x10;
+static constexpr RegMask REG_0x09_BACKSCAN = 0x08;
+static constexpr RegMask REG_0x09_ENHANCE = 0x04;
+static constexpr RegMask REG_0x09_SHORTTG = 0x02;
+static constexpr RegMask REG_0x09_NWAIT = 0x01;
+
+static constexpr RegShift REG_0x09S_MCNTSET = 6;
+static constexpr RegShift REG_0x09S_CLKSET = 4;
+
+
+static constexpr RegAddr REG_0x0A_LPWMEN = 0x10;
+
+static constexpr RegAddr REG_0x0B = 0x0b;
+static constexpr RegMask REG_0x0B_DRAMSEL = 0x07;
+static constexpr RegMask REG_0x0B_ENBDRAM = 0x08;
+static constexpr RegMask REG_0x0B_RFHDIS = 0x10;
+static constexpr RegMask REG_0x0B_CLKSET = 0xe0;
+static constexpr RegMask REG_0x0B_24MHZ = 0x00;
+static constexpr RegMask REG_0x0B_30MHZ = 0x20;
+static constexpr RegMask REG_0x0B_40MHZ = 0x40;
+static constexpr RegMask REG_0x0B_48MHZ = 0x60;
+static constexpr RegMask REG_0x0B_60MHZ = 0x80;
+
+static constexpr RegAddr REG_0x0C = 0x0c;
+static constexpr RegMask REG_0x0C_CCDLMT = 0x0f;
+
+static constexpr RegAddr REG_0x0D = 0x0d;
+static constexpr RegMask REG_0x0D_SCSYNC = 0x40;
+static constexpr RegMask REG_0x0D_CLRERR = 0x20;
+static constexpr RegMask REG_0x0D_FULLSTP = 0x10;
+static constexpr RegMask REG_0x0D_SEND = 0x80;
+static constexpr RegMask REG_0x0D_CLRMCNT = 0x04;
+static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02;
+static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01;
+
+static constexpr RegAddr REG_0x0F = 0x0f;
+
+static constexpr RegMask REG_0x16_CTRLHI = 0x80;
+static constexpr RegMask REG_0x16_TOSHIBA = 0x40;
+static constexpr RegMask REG_0x16_TGINV = 0x20;
+static constexpr RegMask REG_0x16_CK1INV = 0x10;
+static constexpr RegMask REG_0x16_CK2INV = 0x08;
+static constexpr RegMask REG_0x16_CTRLINV = 0x04;
+static constexpr RegMask REG_0x16_CKDIS = 0x02;
+static constexpr RegMask REG_0x16_CTRLDIS = 0x01;
+
+static constexpr RegMask REG_0x17_TGMODE = 0xc0;
+static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00;
+static constexpr RegMask REG_0x17_TGMODE_REF = 0x40;
+static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80;
+static constexpr RegMask REG_0x17_TGW = 0x3f;
+static constexpr RegAddr REG_0x17S_TGW = 0;
+
+static constexpr RegAddr REG_0x18 = 0x18;
+static constexpr RegMask REG_0x18_CNSET = 0x80;
+static constexpr RegMask REG_0x18_DCKSEL = 0x60;
+static constexpr RegMask REG_0x18_CKTOGGLE = 0x10;
+static constexpr RegMask REG_0x18_CKDELAY = 0x0c;
+static constexpr RegMask REG_0x18_CKSEL = 0x03;
+
+static constexpr RegMask REG_0x1A_SW2SET = 0x80;
+static constexpr RegMask REG_0x1A_SW1SET = 0x40;
+static constexpr RegMask REG_0x1A_MANUAL3 = 0x02;
+static constexpr RegMask REG_0x1A_MANUAL1 = 0x01;
+static constexpr RegMask REG_0x1A_CK4INV = 0x08;
+static constexpr RegMask REG_0x1A_CK3INV = 0x04;
+static constexpr RegMask REG_0x1A_LINECLP = 0x02;
+
+static constexpr RegAddr REG_0x1C = 0x1c;
+static constexpr RegMask REG_0x1C_TGTIME = 0x07;
+
+static constexpr RegMask REG_0x1D_CK4LOW = 0x80;
+static constexpr RegMask REG_0x1D_CK3LOW = 0x40;
+static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
+static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
+static constexpr RegShift REG_0x1DS_TGSHLD = 0;
+
+
+static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
+static constexpr RegShift REG_0x1ES_WDTIME = 4;
+static constexpr RegMask REG_0x1E_LINESEL = 0x0f;
+static constexpr RegShift REG_0x1ES_LINESEL = 0;
+
+static constexpr RegAddr REG_FEDCNT = 0x1f;
+
+static constexpr RegAddr REG_0x24 = 0x1c;
+static constexpr RegAddr REG_0x40 = 0x40;
+static constexpr RegMask REG_0x40_DOCSNR = 0x80;
+static constexpr RegMask REG_0x40_ADFSNR = 0x40;
+static constexpr RegMask REG_0x40_COVERSNR = 0x20;
+static constexpr RegMask REG_0x40_CHKVER = 0x10;
+static constexpr RegMask REG_0x40_DOCJAM = 0x08;
+static constexpr RegMask REG_0x40_HISPDFLG = 0x04;
+static constexpr RegMask REG_0x40_MOTMFLG = 0x02;
+static constexpr RegMask REG_0x40_DATAENB = 0x01;
+
+static constexpr RegMask REG_0x41_PWRBIT = 0x80;
+static constexpr RegMask REG_0x41_BUFEMPTY = 0x40;
+static constexpr RegMask REG_0x41_FEEDFSH = 0x20;
+static constexpr RegMask REG_0x41_SCANFSH = 0x10;
+static constexpr RegMask REG_0x41_HOMESNR = 0x08;
+static constexpr RegMask REG_0x41_LAMPSTS = 0x04;
+static constexpr RegMask REG_0x41_FEBUSY = 0x02;
+static constexpr RegMask REG_0x41_MOTORENB = 0x01;
+
+static constexpr RegMask REG_0x58_VSMP = 0xf8;
+static constexpr RegShift REG_0x58S_VSMP = 3;
+static constexpr RegMask REG_0x58_VSMPW = 0x07;
+static constexpr RegAddr REG_0x58S_VSMPW = 0;
+
+static constexpr RegMask REG_0x59_BSMP = 0xf8;
+static constexpr RegAddr REG_0x59S_BSMP = 3;
+static constexpr RegMask REG_0x59_BSMPW = 0x07;
+static constexpr RegShift REG_0x59S_BSMPW = 0;
+
+static constexpr RegMask REG_0x5A_ADCLKINV = 0x80;
+static constexpr RegMask REG_0x5A_RLCSEL = 0x40;
+static constexpr RegMask REG_0x5A_CDSREF = 0x30;
+static constexpr RegShift REG_0x5AS_CDSREF = 4;
+static constexpr RegMask REG_0x5A_RLC = 0x0f;
+static constexpr RegShift REG_0x5AS_RLC = 0;
+
+static constexpr RegMask REG_0x5E_DECSEL = 0xe0;
+static constexpr RegShift REG_0x5ES_DECSEL = 5;
+static constexpr RegMask REG_0x5E_STOPTIM = 0x1f;
+static constexpr RegShift REG_0x5ES_STOPTIM = 0;
+
+static constexpr RegAddr REG_0x60 = 0x60;
+static constexpr RegMask REG_0x60_Z1MOD = 0x1f;
+static constexpr RegAddr REG_0x61 = 0x61;
+static constexpr RegMask REG_0x61_Z1MOD = 0xff;
+static constexpr RegAddr REG_0x62 = 0x62;
+static constexpr RegMask REG_0x62_Z1MOD = 0xff;
+
+static constexpr RegAddr REG_0x63 = 0x63;
+static constexpr RegMask REG_0x63_Z2MOD = 0x1f;
+static constexpr RegAddr REG_0x64 = 0x64;
+static constexpr RegMask REG_0x64_Z2MOD = 0xff;
+static constexpr RegAddr REG_0x65 = 0x65;
+static constexpr RegMask REG_0x65_Z2MOD = 0xff;
+
+static constexpr RegShift REG_0x60S_STEPSEL = 5;
+static constexpr RegMask REG_0x60_STEPSEL = 0xe0;
+static constexpr RegMask REG_0x60_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x60_HALFSTEP = 0x20;
+static constexpr RegMask REG_0x60_EIGHTHSTEP = 0x60;
+static constexpr RegMask REG_0x60_16THSTEP = 0x80;
+
+static constexpr RegShift REG_0x63S_FSTPSEL = 5;
+static constexpr RegMask REG_0x63_FSTPSEL = 0xe0;
+static constexpr RegMask REG_0x63_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x63_HALFSTEP = 0x20;
+static constexpr RegMask REG_0x63_EIGHTHSTEP = 0x60;
+static constexpr RegMask REG_0x63_16THSTEP = 0x80;
+
+static constexpr RegAddr REG_0x67 = 0x67;
+static constexpr RegMask REG_0x67_MTRPWM = 0x80;
+
+static constexpr RegAddr REG_0x68 = 0x68;
+static constexpr RegMask REG_0x68_FASTPWM = 0x80;
+
+static constexpr RegAddr REG_0x6B = 0x6b;
+static constexpr RegMask REG_0x6B_MULTFILM = 0x80;
+static constexpr RegMask REG_0x6B_GPOM13 = 0x40;
+static constexpr RegMask REG_0x6B_GPOM12 = 0x20;
+static constexpr RegMask REG_0x6B_GPOM11 = 0x10;
+static constexpr RegMask REG_0x6B_GPO18 = 0x02;
+static constexpr RegMask REG_0x6B_GPO17 = 0x01;
+
+static constexpr RegAddr REG_0x6C = 0x6c;
+static constexpr RegMask REG_0x6C_GPIO16 = 0x80;
+static constexpr RegMask REG_0x6C_GPIO15 = 0x40;
+static constexpr RegMask REG_0x6C_GPIO14 = 0x20;
+static constexpr RegMask REG_0x6C_GPIO13 = 0x10;
+static constexpr RegMask REG_0x6C_GPIO12 = 0x08;
+static constexpr RegMask REG_0x6C_GPIO11 = 0x04;
+static constexpr RegMask REG_0x6C_GPIO10 = 0x02;
+static constexpr RegMask REG_0x6C_GPIO9 = 0x01;
+static constexpr RegMask REG_0x6C_GPIOH = 0xff;
+static constexpr RegMask REG_0x6C_GPIOL = 0xff;
+
+static constexpr RegAddr REG_0x6D = 0x6d;
+static constexpr RegAddr REG_0x6E = 0x6e;
+static constexpr RegAddr REG_0x6F = 0x6f;
+static constexpr RegAddr REG_0x7E = 0x7e;
+
+static constexpr RegMask REG_0x87_ACYCNRLC = 0x10;
+static constexpr RegMask REG_0x87_ENOFFSET = 0x08;
+static constexpr RegMask REG_0x87_LEDADD = 0x04;
+static constexpr RegMask REG_0x87_CK4ADC = 0x02;
+static constexpr RegMask REG_0x87_AUTOCONF = 0x01;
+
+static constexpr RegAddr REG_0x9E = 0x9e;
+static constexpr RegAddr REG_0x9F = 0x9f;
+
+static constexpr RegAddr REG_0xA6 = 0xa6;
+static constexpr RegAddr REG_0xA7 = 0xa7;
+static constexpr RegAddr REG_0xA8 = 0xa8;
+static constexpr RegAddr REG_0xA9 = 0xa9;
+static constexpr RegAddr REG_0xAB = 0xab;
+
+static constexpr RegAddr REG_EXPR = 0x10;
+static constexpr RegAddr REG_EXPG = 0x12;
+static constexpr RegAddr REG_EXPB = 0x14;
+static constexpr RegAddr REG_EXPDMY = 0x19;
+static constexpr RegAddr REG_STEPNO = 0x21;
+static constexpr RegAddr REG_FWDSTEP = 0x22;
+static constexpr RegAddr REG_BWDSTEP = 0x23;
+static constexpr RegAddr REG_FASTNO = 0x24;
+static constexpr RegAddr REG_DPISET = 0x2c;
+static constexpr RegAddr REG_STRPIXEL = 0x30;
+static constexpr RegAddr REG_ENDPIXEL = 0x32;
+static constexpr RegAddr REG_LINCNT = 0x25;
+static constexpr RegAddr REG_MAXWD = 0x35;
+static constexpr RegAddr REG_LPERIOD = 0x38;
+static constexpr RegAddr REG_FEEDL = 0x3d;
+static constexpr RegAddr REG_FMOVDEC = 0x5f;
+static constexpr RegAddr REG_FSHDEC = 0x69;
+static constexpr RegAddr REG_FMOVNO = 0x6a;
+static constexpr RegAddr REG_CK1MAP = 0x74;
+static constexpr RegAddr REG_CK3MAP = 0x77;
+static constexpr RegAddr REG_CK4MAP = 0x7a;
+
+static constexpr RegAddr REG_0xF8 = 0xf8;
+static constexpr RegMask REG_0xF8_MAXSEL = 0xf0;
+static constexpr RegShift REG_0xF8_SMAXSEL = 4;
+static constexpr RegMask REG_0xF8_MINSEL = 0x0f;
+
+} // namespace gl846
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL846_REGISTERS_H
diff --git a/backend/genesys/gl847.cpp b/backend/genesys/gl847.cpp
new file mode 100644
index 0000000..cb0b527
--- /dev/null
+++ b/backend/genesys/gl847.cpp
@@ -0,0 +1,2140 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "gl847.h"
+#include "gl847_registers.h"
+#include "test_settings.h"
+
+#include <vector>
+
+namespace genesys {
+namespace gl847 {
+
+/**
+ * compute the step multiplier used
+ */
+static int
+gl847_get_step_multiplier (Genesys_Register_Set * regs)
+{
+ GenesysRegister *r = sanei_genesys_get_address(regs, 0x9d);
+ int value = 1;
+ if (r != nullptr)
+ {
+ value = (r->value & 0x0f)>>1;
+ value = 1 << value;
+ }
+ DBG (DBG_io, "%s: step multiplier is %d\n", __func__, value);
+ return value;
+}
+
+/** @brief sensor specific settings
+*/
+static void gl847_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs)
+{
+ DBG_HELPER(dbg);
+
+ for (const auto& reg : sensor.custom_regs) {
+ regs->set8(reg.address, reg.value);
+ }
+
+ regs->set16(REG_EXPR, sensor.exposure.red);
+ regs->set16(REG_EXPG, sensor.exposure.green);
+ regs->set16(REG_EXPB, sensor.exposure.blue);
+
+ dev->segment_order = sensor.segment_order;
+}
+
+
+/** @brief set all registers to default values .
+ * This function is called only once at the beginning and
+ * fills register startup values for registers reused across scans.
+ * Those that are rarely modified or not modified are written
+ * individually.
+ * @param dev device structure holding register set to initialize
+ */
+static void
+gl847_init_registers (Genesys_Device * dev)
+{
+ DBG_HELPER(dbg);
+ int lide700=0;
+ uint8_t val;
+
+ /* 700F class needs some different initial settings */
+ if (dev->model->model_id == ModelId::CANON_LIDE_700F) {
+ lide700 = 1;
+ }
+
+ dev->reg.clear();
+
+ dev->reg.init_reg(0x01, 0x82);
+ dev->reg.init_reg(0x02, 0x18);
+ dev->reg.init_reg(0x03, 0x50);
+ dev->reg.init_reg(0x04, 0x12);
+ dev->reg.init_reg(0x05, 0x80);
+ dev->reg.init_reg(0x06, 0x50); // FASTMODE + POWERBIT
+ dev->reg.init_reg(0x08, 0x10);
+ dev->reg.init_reg(0x09, 0x01);
+ dev->reg.init_reg(0x0a, 0x00);
+ dev->reg.init_reg(0x0b, 0x01);
+ dev->reg.init_reg(0x0c, 0x02);
+
+ // LED exposures
+ dev->reg.init_reg(0x10, 0x00);
+ dev->reg.init_reg(0x11, 0x00);
+ dev->reg.init_reg(0x12, 0x00);
+ dev->reg.init_reg(0x13, 0x00);
+ dev->reg.init_reg(0x14, 0x00);
+ dev->reg.init_reg(0x15, 0x00);
+
+ dev->reg.init_reg(0x16, 0x10); // SENSOR_DEF
+ dev->reg.init_reg(0x17, 0x08); // SENSOR_DEF
+ dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF
+
+ // EXPDMY
+ dev->reg.init_reg(0x19, 0x50); // SENSOR_DEF
+
+ dev->reg.init_reg(0x1a, 0x34); // SENSOR_DEF
+ dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x1c, 0x02); // SENSOR_DEF
+ dev->reg.init_reg(0x1d, 0x04); // SENSOR_DEF
+ dev->reg.init_reg(0x1e, 0x10);
+ dev->reg.init_reg(0x1f, 0x04);
+ dev->reg.init_reg(0x20, 0x02);
+ dev->reg.init_reg(0x21, 0x10);
+ dev->reg.init_reg(0x22, 0x7f);
+ dev->reg.init_reg(0x23, 0x7f);
+ dev->reg.init_reg(0x24, 0x10);
+ dev->reg.init_reg(0x25, 0x00);
+ dev->reg.init_reg(0x26, 0x00);
+ dev->reg.init_reg(0x27, 0x00);
+ dev->reg.init_reg(0x2c, 0x09);
+ dev->reg.init_reg(0x2d, 0x60);
+ dev->reg.init_reg(0x2e, 0x80);
+ dev->reg.init_reg(0x2f, 0x80);
+ dev->reg.init_reg(0x30, 0x00);
+ dev->reg.init_reg(0x31, 0x10);
+ dev->reg.init_reg(0x32, 0x15);
+ dev->reg.init_reg(0x33, 0x0e);
+ dev->reg.init_reg(0x34, 0x40);
+ dev->reg.init_reg(0x35, 0x00);
+ dev->reg.init_reg(0x36, 0x2a);
+ dev->reg.init_reg(0x37, 0x30);
+ dev->reg.init_reg(0x38, 0x2a);
+ dev->reg.init_reg(0x39, 0xf8);
+ dev->reg.init_reg(0x3d, 0x00);
+ dev->reg.init_reg(0x3e, 0x00);
+ dev->reg.init_reg(0x3f, 0x00);
+ dev->reg.init_reg(0x52, 0x03); // SENSOR_DEF
+ dev->reg.init_reg(0x53, 0x07); // SENSOR_DEF
+ dev->reg.init_reg(0x54, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x55, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x56, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x57, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x58, 0x2a); // SENSOR_DEF
+ dev->reg.init_reg(0x59, 0xe1); // SENSOR_DEF
+ dev->reg.init_reg(0x5a, 0x55); // SENSOR_DEF
+ dev->reg.init_reg(0x5e, 0x41);
+ dev->reg.init_reg(0x5f, 0x40);
+ dev->reg.init_reg(0x60, 0x00);
+ dev->reg.init_reg(0x61, 0x21);
+ dev->reg.init_reg(0x62, 0x40);
+ dev->reg.init_reg(0x63, 0x00);
+ dev->reg.init_reg(0x64, 0x21);
+ dev->reg.init_reg(0x65, 0x40);
+ dev->reg.init_reg(0x67, 0x80);
+ dev->reg.init_reg(0x68, 0x80);
+ dev->reg.init_reg(0x69, 0x20);
+ dev->reg.init_reg(0x6a, 0x20);
+
+ // CK1MAP
+ dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF
+
+ // CK3MAP
+ dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF
+
+ // CK4MAP
+ dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF
+ dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF
+
+ dev->reg.init_reg(0x7d, 0x00);
+
+ // NOTE: autoconf is a non working option
+ dev->reg.init_reg(0x87, 0x02);
+ dev->reg.init_reg(0x9d, 0x06);
+ dev->reg.init_reg(0xa2, 0x0f);
+ dev->reg.init_reg(0xbd, 0x18);
+ dev->reg.init_reg(0xfe, 0x08);
+
+ // gamma[0] and gamma[256] values
+ dev->reg.init_reg(0xbe, 0x00);
+ dev->reg.init_reg(0xc5, 0x00);
+ dev->reg.init_reg(0xc6, 0x00);
+ dev->reg.init_reg(0xc7, 0x00);
+ dev->reg.init_reg(0xc8, 0x00);
+ dev->reg.init_reg(0xc9, 0x00);
+ dev->reg.init_reg(0xca, 0x00);
+
+ /* LiDE 700 fixups */
+ if (lide700) {
+ dev->reg.init_reg(0x5f, 0x04);
+ dev->reg.init_reg(0x7d, 0x80);
+
+ /* we write to these registers only once */
+ val=0;
+ dev->interface->write_register(REG_0x7E, val);
+ dev->interface->write_register(REG_0x9E, val);
+ dev->interface->write_register(REG_0x9F, val);
+ dev->interface->write_register(REG_0xAB, val);
+ }
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+ sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res);
+
+ /* initalize calibration reg */
+ dev->calib_reg = dev->reg;
+}
+
+/**@brief send slope table for motor movement
+ * Send slope_table in machine byte order
+ * @param dev device to send slope table
+ * @param table_nr index of the slope table in ASIC memory
+ * Must be in the [0-4] range.
+ * @param slope_table pointer to 16 bit values array of the slope table
+ * @param steps number of elements in the slope table
+ */
+static void gl847_send_slope_table(Genesys_Device* dev, int table_nr,
+ const std::vector<uint16_t>& slope_table,
+ int steps)
+{
+ DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps);
+ int i;
+ char msg[10000];
+
+ /* sanity check */
+ if(table_nr<0 || table_nr>4)
+ {
+ throw SaneException("invalid table number %d", table_nr);
+ }
+
+ std::vector<uint8_t> table(steps * 2);
+ for (i = 0; i < steps; i++)
+ {
+ table[i * 2] = slope_table[i] & 0xff;
+ table[i * 2 + 1] = slope_table[i] >> 8;
+ }
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ std::sprintf(msg, "write slope %d (%d)=", table_nr, steps);
+ for (i = 0; i < steps; i++)
+ {
+ std::sprintf(msg + std::strlen(msg), "%d", slope_table[i]);
+ }
+ DBG (DBG_io, "%s: %s\n", __func__, msg);
+ }
+
+ if (dev->interface->is_mock()) {
+ dev->interface->record_slope_table(table_nr, slope_table);
+ }
+ // slope table addresses are fixed
+ dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data());
+}
+
+/**
+ * Set register values of Analog Device type frontend
+ * */
+static void gl847_set_ad_fe(Genesys_Device* dev, uint8_t set)
+{
+ DBG_HELPER(dbg);
+ int i;
+
+ // wait for FE to be ready
+ auto status = scanner_read_status(*dev);
+ while (status.is_front_end_busy) {
+ dev->interface->sleep_ms(10);
+ status = scanner_read_status(*dev);
+ };
+
+ if (set == AFE_INIT)
+ {
+ DBG(DBG_proc, "%s(): setting DAC %u\n", __func__,
+ static_cast<unsigned>(dev->model->adc_id));
+
+ dev->frontend = dev->frontend_initial;
+ }
+
+ // reset DAC
+ dev->interface->write_fe_register(0x00, 0x80);
+
+ // write them to analog frontend
+ dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
+
+ dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
+
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i));
+ }
+ for (i = 0; i < 3; i++) {
+ dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i));
+ }
+}
+
+// Set values of analog frontend
+void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const
+{
+ DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" :
+ set == AFE_SET ? "set" :
+ set == AFE_POWER_SAVE ? "powersave" : "huh?");
+
+ (void) sensor;
+
+ uint8_t val = dev->interface->read_register(REG_0x04);
+ uint8_t frontend_type = val & REG_0x04_FESET;
+
+ // route to AD devices
+ if (frontend_type == 0x02) {
+ gl847_set_ad_fe(dev, set);
+ return;
+ }
+
+ throw SaneException("unsupported frontend type %d", frontend_type);
+}
+
+
+// @brief set up motor related register for scan
+static void gl847_init_motor_regs_scan(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const Motor_Profile& motor_profile,
+ unsigned int scan_exposure_time,
+ unsigned scan_yres,
+ unsigned int scan_lines,
+ unsigned int scan_dummy,
+ unsigned int feed_steps,
+ MotorFlag flags)
+{
+ DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, can_yres=%d, step_type=%d, scan_lines=%d, "
+ "scan_dummy=%d, feed_steps=%d, flags=%x",
+ scan_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type),
+ scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
+ int use_fast_fed;
+ unsigned int fast_dpi;
+ unsigned int feedl, dist;
+ GenesysRegister *r;
+ uint32_t z1, z2;
+ unsigned int min_restep = 0x20;
+ uint8_t val;
+ unsigned int ccdlmt,tgtime;
+
+ unsigned step_multiplier = gl847_get_step_multiplier (reg);
+
+ use_fast_fed=0;
+ /* no fast fed since feed works well */
+ if (dev->settings.yres==4444 && feed_steps > 100 && (!has_flag(flags, MotorFlag::FEED)))
+ {
+ use_fast_fed=1;
+ }
+ DBG(DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
+
+ reg->set24(REG_LINCNT, scan_lines);
+ DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
+
+ /* compute register 02 value */
+ r = sanei_genesys_get_address(reg, REG_0x02);
+ r->value = 0x00;
+ sanei_genesys_set_motor_power(*reg, true);
+
+ if (use_fast_fed) {
+ r->value |= REG_0x02_FASTFED;
+ } else {
+ r->value &= ~REG_0x02_FASTFED;
+ }
+
+ if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
+ r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
+ }
+
+ if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE)
+ ||(scan_yres>=sensor.optical_res))
+ {
+ r->value |= REG_0x02_ACDCDIS;
+ }
+
+ if (has_flag(flags, MotorFlag::REVERSE)) {
+ r->value |= REG_0x02_MTRREV;
+ } else {
+ r->value &= ~REG_0x02_MTRREV;
+ }
+
+ /* scan and backtracking slope table */
+ auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres,
+ scan_exposure_time, dev->motor.base_ydpi,
+ step_multiplier, motor_profile);
+ gl847_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
+ gl847_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
+
+ /* fast table */
+ fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
+ StepType fast_step_type = motor_profile.step_type;
+ if (static_cast<unsigned>(motor_profile.step_type) >= static_cast<unsigned>(StepType::QUARTER)) {
+ fast_step_type = StepType::QUARTER;
+ }
+
+ Motor_Profile fast_motor_profile = motor_profile;
+ fast_motor_profile.step_type = fast_step_type;
+
+ auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_dpi,
+ scan_exposure_time, dev->motor.base_ydpi,
+ step_multiplier, fast_motor_profile);
+
+ gl847_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count);
+ gl847_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
+ gl847_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count);
+
+ /* correct move distance by acceleration and deceleration amounts */
+ feedl=feed_steps;
+ if (use_fast_fed)
+ {
+ feedl <<= static_cast<unsigned>(fast_step_type);
+ dist = (scan_table.steps_count + 2 * fast_table.steps_count);
+ /* TODO read and decode REG_0xAB */
+ r = sanei_genesys_get_address (reg, 0x5e);
+ dist += (r->value & 31);
+ /* FEDCNT */
+ r = sanei_genesys_get_address (reg, REG_FEDCNT);
+ dist += r->value;
+ }
+ else
+ {
+ feedl <<= static_cast<unsigned>(motor_profile.step_type);
+ dist = scan_table.steps_count;
+ if (has_flag(flags, MotorFlag::FEED)) {
+ dist *= 2;
+ }
+ }
+ DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
+
+ /* check for overflow */
+ if (dist < feedl) {
+ feedl -= dist;
+ } else {
+ feedl = 0;
+ }
+
+ reg->set24(REG_FEEDL, feedl);
+ DBG(DBG_io ,"%s: feedl=%d\n", __func__, feedl);
+
+ r = sanei_genesys_get_address(reg, REG_0x0C);
+ ccdlmt = (r->value & REG_0x0C_CCDLMT) + 1;
+
+ r = sanei_genesys_get_address(reg, REG_0x1C);
+ tgtime = 1<<(r->value & REG_0x1C_TGTIME);
+
+ // hi res motor speed GPIO
+ uint8_t effective = dev->interface->read_register(REG_0x6C);
+
+ // if quarter step, bipolar Vref2
+
+ if (motor_profile.step_type == StepType::QUARTER) {
+ val = effective & ~REG_0x6C_GPIO13;
+ } else if (static_cast<unsigned>(motor_profile.step_type) > static_cast<unsigned>(StepType::QUARTER)) {
+ val = effective | REG_0x6C_GPIO13;
+ } else {
+ val = effective;
+ }
+ dev->interface->write_register(REG_0x6C, val);
+
+ // effective scan
+ effective = dev->interface->read_register(REG_0x6C);
+ val = effective | REG_0x6C_GPIO10;
+ dev->interface->write_register(REG_0x6C, val);
+
+ min_restep = scan_table.steps_count / (2 * step_multiplier) - 1;
+ if (min_restep < 1) {
+ min_restep = 1;
+ }
+ r = sanei_genesys_get_address(reg, REG_FWDSTEP);
+ r->value = min_restep;
+ r = sanei_genesys_get_address(reg, REG_BWDSTEP);
+ r->value = min_restep;
+
+ sanei_genesys_calculate_zmod(use_fast_fed,
+ scan_exposure_time*ccdlmt*tgtime,
+ scan_table.table,
+ scan_table.steps_count,
+ feedl,
+ min_restep * step_multiplier,
+ &z1,
+ &z2);
+
+ DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
+ reg->set24(REG_0x60, z1 | (static_cast<unsigned>(motor_profile.step_type) << (16+REG_0x60S_STEPSEL)));
+
+ DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
+ reg->set24(REG_0x63, z2 | (static_cast<unsigned>(motor_profile.step_type) << (16+REG_0x63S_FSTPSEL)));
+
+ r = sanei_genesys_get_address (reg, 0x1e);
+ r->value &= 0xf0; /* 0 dummy lines */
+ r->value |= scan_dummy; /* dummy lines */
+
+ r = sanei_genesys_get_address(reg, REG_0x67);
+ r->value = REG_0x67_MTRPWM;
+
+ r = sanei_genesys_get_address(reg, REG_0x68);
+ r->value = REG_0x68_FASTPWM;
+
+ reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier);
+ reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier);
+ reg->set8(REG_FSHDEC, scan_table.steps_count / step_multiplier);
+ reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier);
+ reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier);
+}
+
+
+/** @brief set up registers related to sensor
+ * Set up the following registers
+ 0x01
+ 0x03
+ 0x10-0x015 R/G/B exposures
+ 0x19 EXPDMY
+ 0x2e BWHI
+ 0x2f BWLO
+ 0x04
+ 0x87
+ 0x05
+ 0x2c,0x2d DPISET
+ 0x30,0x31 STRPIXEL
+ 0x32,0x33 ENDPIXEL
+ 0x35,0x36,0x37 MAXWD [25:2] (>>2)
+ 0x38,0x39 LPERIOD
+ 0x34 DUMMY
+ */
+static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, unsigned int exposure_time,
+ const ScanSession& session)
+{
+ DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time);
+ unsigned dpihw;
+ GenesysRegister *r;
+
+ // resolution is divided according to ccd_pixels_per_system_pixel()
+ unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
+ DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel);
+
+ // to manage high resolution device while keeping good low resolution scanning speed, we make
+ // hardware dpi vary
+ dpihw = sensor.get_register_hwdpi(session.params.xres * ccd_pixels_per_system_pixel);
+ DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw);
+
+ gl847_setup_sensor(dev, sensor, reg);
+
+ dev->cmd_set->set_fe(dev, sensor, AFE_SET);
+
+ /* enable shading */
+ regs_set_optical_off(dev->model->asic_type, *reg);
+ r = sanei_genesys_get_address(reg, REG_0x01);
+ r->value |= REG_0x01_SHDAREA;
+
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) ||
+ (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ r->value &= ~REG_0x01_DVDSET;
+ }
+ else
+ {
+ r->value |= REG_0x01_DVDSET;
+ }
+
+ r = sanei_genesys_get_address (reg, REG_0x03);
+ r->value &= ~REG_0x03_AVEENB;
+
+ sanei_genesys_set_lamp_power(dev, sensor, *reg,
+ !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));
+
+ /* BW threshold */
+ r = sanei_genesys_get_address (reg, 0x2e);
+ r->value = dev->settings.threshold;
+ r = sanei_genesys_get_address (reg, 0x2f);
+ r->value = dev->settings.threshold;
+
+ /* monochrome / color scan */
+ r = sanei_genesys_get_address (reg, REG_0x04);
+ switch (session.params.depth) {
+ case 8:
+ r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
+ break;
+ case 16:
+ r->value &= ~REG_0x04_LINEART;
+ r->value |= REG_0x04_BITSET;
+ break;
+ }
+
+ r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);
+ if (session.params.channels == 1)
+ {
+ switch (session.params.color_filter)
+ {
+
+ case ColorFilter::RED:
+ r->value |= 0x14;
+ break;
+ case ColorFilter::BLUE:
+ r->value |= 0x1c;
+ break;
+ case ColorFilter::GREEN:
+ r->value |= 0x18;
+ break;
+ default:
+ break; // should not happen
+ }
+ } else {
+ r->value |= 0x10; // mono
+ }
+
+ sanei_genesys_set_dpihw(*reg, sensor, dpihw);
+
+ if (should_enable_gamma(session, sensor)) {
+ reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
+ } else {
+ reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
+ }
+
+ /* CIS scanners can do true gray by setting LEDADD */
+ /* we set up LEDADD only when asked */
+ if (dev->model->is_cis) {
+ r = sanei_genesys_get_address (reg, 0x87);
+ r->value &= ~REG_0x87_LEDADD;
+ if (session.enable_ledadd) {
+ r->value |= REG_0x87_LEDADD;
+ }
+ /* RGB weighting
+ r = sanei_genesys_get_address (reg, 0x01);
+ r->value &= ~REG_0x01_TRUEGRAY;
+ if (session.enable_ledadd) {
+ r->value |= REG_0x01_TRUEGRAY;
+ }
+ */
+ }
+
+ unsigned dpiset = session.params.xres * ccd_pixels_per_system_pixel;
+ reg->set16(REG_DPISET, dpiset);
+ DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
+
+ reg->set16(REG_STRPIXEL, session.pixel_startx);
+ reg->set16(REG_ENDPIXEL, session.pixel_endx);
+
+ build_image_pipeline(dev, session);
+
+ /* MAXWD is expressed in 4 words unit */
+ // BUG: we shouldn't multiply by channels here
+ reg->set24(REG_MAXWD, (session.output_line_bytes_raw * session.params.channels >> 2));
+
+ reg->set16(REG_LPERIOD, exposure_time);
+ DBG(DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time);
+
+ r = sanei_genesys_get_address (reg, 0x34);
+ r->value = sensor.dummy_pixel;
+}
+
+void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const
+{
+ DBG_HELPER(dbg);
+ session.assert_computed();
+
+ int move;
+ int exposure_time;
+
+ int slope_dpi = 0;
+ int dummy = 0;
+
+ dummy = 3 - session.params.channels;
+
+/* slope_dpi */
+/* cis color scan is effectively a gray scan with 3 gray lines per color
+ line and a FILTER of 0 */
+ if (dev->model->is_cis) {
+ slope_dpi = session.params.yres * session.params.channels;
+ } else {
+ slope_dpi = session.params.yres;
+ }
+
+ slope_dpi = slope_dpi * (1 + dummy);
+
+ exposure_time = sensor.exposure_lperiod;
+ const auto& motor_profile = sanei_genesys_get_motor_profile(*gl847_motor_profiles,
+ dev->model->motor_id,
+ exposure_time);
+
+ DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
+ DBG(DBG_info, "%s : scan_step_type=%d\n", __func__,
+ static_cast<unsigned>(motor_profile.step_type));
+
+ /* we enable true gray for cis scanners only, and just when doing
+ * scan since color calibration is OK for this mode
+ */
+ gl847_init_optical_regs_scan(dev, sensor, reg, exposure_time, session);
+
+ move = session.params.starty;
+ DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
+
+ MotorFlag mflags = MotorFlag::NONE;
+ if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) {
+ mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE;
+ }
+ if (has_flag(session.params.flags, ScanFlag::FEEDING)) {
+ mflags |= MotorFlag::FEED;
+ }
+ if (has_flag(session.params.flags, ScanFlag::REVERSE)) {
+ mflags |= MotorFlag::REVERSE;
+ }
+
+ gl847_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi,
+ dev->model->is_cis ? session.output_line_count * session.params.channels
+ : session.output_line_count,
+ dummy, move, mflags);
+
+ dev->read_buffer.clear();
+ dev->read_buffer.alloc(session.buffer_size_read);
+
+ dev->read_active = true;
+
+ dev->session = session;
+
+ dev->total_bytes_read = 0;
+ dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines;
+
+ DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read);
+}
+
+ScanSession CommandSetGl847::calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const
+{
+ int start;
+
+ DBG(DBG_info, "%s ", __func__);
+ debug_dump(DBG_info, settings);
+
+ /* start */
+ start = static_cast<int>(dev->model->x_offset);
+ start = static_cast<int>(start + settings.tl_x);
+ start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = settings.xres;
+ session.params.yres = settings.yres;
+ session.params.startx = start; // not used
+ session.params.starty = 0; // not used
+ session.params.pixels = settings.pixels;
+ session.params.requested_pixels = settings.requested_pixels;
+ session.params.lines = settings.lines;
+ session.params.depth = settings.depth;
+ session.params.channels = settings.get_channels();
+ session.params.scan_method = settings.scan_method;
+ session.params.scan_mode = settings.scan_mode;
+ session.params.color_filter = settings.color_filter;
+ session.params.flags = ScanFlag::NONE;
+
+ compute_session(dev, session, sensor);
+
+ return session;
+}
+
+// for fast power saving methods only, like disabling certain amplifiers
+void CommandSetGl847::save_power(Genesys_Device* dev, bool enable) const
+{
+ DBG_HELPER_ARGS(dbg, "enable = %d", enable);
+ (void) dev;
+}
+
+void CommandSetGl847::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
+{
+ (void) dev;
+ DBG_HELPER_ARGS(dbg, "delay = %d", delay);
+}
+
+// Send the low-level scan command
+void CommandSetGl847::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg, bool start_motor) const
+{
+ DBG_HELPER(dbg);
+ (void) sensor;
+ uint8_t val;
+ GenesysRegister *r;
+
+ // clear GPIO 10
+ if (dev->model->gpio_id != GpioId::CANON_LIDE_700F) {
+ val = dev->interface->read_register(REG_0x6C);
+ val &= ~REG_0x6C_GPIO10;
+ dev->interface->write_register(REG_0x6C, val);
+ }
+
+ val = REG_0x0D_CLRLNCNT;
+ dev->interface->write_register(REG_0x0D, val);
+ val = REG_0x0D_CLRMCNT;
+ dev->interface->write_register(REG_0x0D, val);
+
+ val = dev->interface->read_register(REG_0x01);
+ val |= REG_0x01_SCAN;
+ dev->interface->write_register(REG_0x01, val);
+ r = sanei_genesys_get_address (reg, REG_0x01);
+ r->value = val;
+
+ scanner_start_action(*dev, start_motor);
+
+ dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
+}
+
+
+// Send the stop scan command
+void CommandSetGl847::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
+ bool check_stop) const
+{
+ (void) reg;
+ DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop);
+
+ if (!dev->model->is_sheetfed) {
+ scanner_stop_action(*dev);
+ }
+}
+
+/** Park head
+ * Moves the slider to the home (top) position slowly
+ * @param dev device to park
+ * @param wait_until_home true to make the function waiting for head
+ * to be home before returning, if fals returne immediately
+*/
+void CommandSetGl847::move_back_home(Genesys_Device* dev, bool wait_until_home) const
+{
+ scanner_move_back_home(*dev, wait_until_home);
+}
+
+// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi
+// from very top of scanner
+void CommandSetGl847::search_start_position(Genesys_Device* dev) const
+{
+ DBG_HELPER(dbg);
+ int size;
+ Genesys_Register_Set local_reg;
+
+ int pixels = 600;
+ int dpi = 300;
+
+ local_reg = dev->reg;
+
+ /* sets for a 200 lines * 600 pixels */
+ /* normal scan with no shading */
+
+ // FIXME: the current approach of doing search only for one resolution does not work on scanners
+ // whith employ different sensors with potentially different settings.
+ const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method);
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0; /*we should give a small offset here~60 steps */
+ session.params.pixels = 600;
+ session.params.lines = dev->model->search_lines;
+ session.params.depth = 8;
+ session.params.channels = 1;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::GREEN;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ // send to scanner
+ dev->interface->write_registers(local_reg);
+
+ size = pixels * dev->model->search_lines;
+
+ std::vector<uint8_t> data(size);
+
+ begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_start_position");
+ end_scan(dev, &local_reg, true);
+ dev->reg = local_reg;
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl847_search_position.pnm", data.data(), 8, 1, pixels,
+ dev->model->search_lines);
+ }
+
+ end_scan(dev, &local_reg, true);
+
+ /* update regs to copy ASIC internal state */
+ dev->reg = local_reg;
+
+ // TODO: find out where sanei_genesys_search_reference_point stores information,
+ // and use that correctly
+ for (auto& sensor_update :
+ sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method))
+ {
+ sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels,
+ dev->model->search_lines);
+ }
+}
+
+// sets up register for coarse gain calibration
+// todo: check it for scanners using it
+void CommandSetGl847::init_regs_for_coarse_calibration(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel();
+ session.params.lines = 20;
+ session.params.depth = 16;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
+ sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres);
+
+ dev->interface->write_registers(regs);
+}
+
+// init registers for shading calibration
+void CommandSetGl847::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+
+ dev->calib_channels = 3;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres);
+
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution,
+ dev->calib_channels,
+ dev->settings.scan_method);
+
+ dev->calib_total_bytes_to_read = 0;
+ dev->calib_lines = dev->model->shading_lines;
+ if (dev->calib_resolution == 4800) {
+ dev->calib_lines *= 2;
+ }
+ dev->calib_pixels = (calib_sensor.sensor_pixels * dev->calib_resolution) /
+ calib_sensor.optical_res;
+
+ DBG(DBG_io, "%s: calib_lines = %zu\n", __func__, dev->calib_lines);
+ DBG(DBG_io, "%s: calib_pixels = %zu\n", __func__, dev->calib_pixels);
+
+ ScanSession session;
+ session.params.xres = dev->calib_resolution;
+ session.params.yres = dev->motor.base_ydpi;
+ session.params.startx = 0;
+ session.params.starty = 20;
+ session.params.pixels = dev->calib_pixels;
+ session.params.lines = dev->calib_lines;
+ session.params.depth = 16;
+ session.params.channels = dev->calib_channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::DISABLE_BUFFER_FULL_MOVE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ dev->interface->write_registers(regs);
+
+ /* we use GENESYS_FLAG_SHADING_REPARK */
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+}
+
+/** @brief set up registers for the actual scan
+ */
+void CommandSetGl847::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ DBG_HELPER(dbg);
+ float move;
+ int move_dpi;
+ float start;
+
+ debug_dump(DBG_info, dev->settings);
+
+ /* steps to move to reach scanning area:
+ - first we move to physical start of scanning
+ either by a fixed steps amount from the black strip
+ or by a fixed amount from parking position,
+ minus the steps done during shading calibration
+ - then we move by the needed offset whitin physical
+ scanning area
+
+ assumption: steps are expressed at maximum motor resolution
+
+ we need:
+ float y_offset;
+ float y_size;
+ float y_offset_calib;
+ mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
+
+ /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
+ relative from origin, else, it is from parking position */
+
+ move_dpi = dev->motor.base_ydpi;
+
+ move = static_cast<float>(dev->model->y_offset);
+ move = static_cast<float>(move + dev->settings.tl_y);
+ move = static_cast<float>((move * move_dpi) / MM_PER_INCH);
+ move -= dev->head_pos(ScanHeadId::PRIMARY);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ /* fast move to scan area */
+ /* we don't move fast the whole distance since it would involve
+ * computing acceleration/deceleration distance for scan
+ * resolution. So leave a remainder for it so scan makes the final
+ * move tuning */
+ if (dev->settings.get_channels() * dev->settings.yres >= 600 && move > 700) {
+ scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move - 500),
+ Direction::FORWARD);
+ move=500;
+ }
+
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+ DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
+
+ /* start */
+ start = static_cast<float>(dev->model->x_offset);
+ start = static_cast<float>(start + dev->settings.tl_x);
+ start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH);
+
+ ScanSession session;
+ session.params.xres = dev->settings.xres;
+ session.params.yres = dev->settings.yres;
+ session.params.startx = static_cast<unsigned>(start);
+ session.params.starty = static_cast<unsigned>(move);
+ session.params.pixels = dev->settings.pixels;
+ session.params.requested_pixels = dev->settings.requested_pixels;
+ session.params.lines = dev->settings.lines;
+ session.params.depth = dev->settings.depth;
+ session.params.channels = dev->settings.get_channels();
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = dev->settings.scan_mode;
+ session.params.color_filter = dev->settings.color_filter;
+ // backtracking isn't handled well, so don't enable it
+ session.params.flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &dev->reg, session);
+}
+
+
+/**
+ * Send shading calibration data. The buffer is considered to always hold values
+ * for all the channels.
+ */
+void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ uint8_t* data, int size) const
+{
+ DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size);
+ uint32_t addr, length, i, x, factor, pixels;
+ uint32_t dpiset, dpihw;
+ uint8_t val,*ptr,*src;
+
+ /* shading data is plit in 3 (up to 5 with IR) areas
+ write(0x10014000,0x00000dd8)
+ URB 23429 bulk_out len 3544 wrote 0x33 0x10 0x....
+ write(0x1003e000,0x00000dd8)
+ write(0x10068000,0x00000dd8)
+ */
+ length = static_cast<std::uint32_t>(size / 3);
+ std::uint32_t strpixel = dev->session.pixel_startx;
+ std::uint32_t endpixel = dev->session.pixel_endx;
+
+ /* compute deletion factor */
+ dpiset = dev->reg.get16(REG_DPISET);
+ dpihw = sensor.get_register_hwdpi(dpiset);
+ factor=dpihw/dpiset;
+ DBG(DBG_io2, "%s: factor=%d\n", __func__, factor);
+
+ pixels=endpixel-strpixel;
+
+ /* since we're using SHDAREA, substract startx coordinate from shading */
+ strpixel -= (sensor.ccd_start_xoffset * 600) / sensor.optical_res;
+
+ /* turn pixel value into bytes 2x16 bits words */
+ strpixel*=2*2;
+ pixels*=2*2;
+
+ dev->interface->record_key_value("shading_offset", std::to_string(strpixel));
+ dev->interface->record_key_value("shading_pixels", std::to_string(pixels));
+ dev->interface->record_key_value("shading_length", std::to_string(length));
+ dev->interface->record_key_value("shading_factor", std::to_string(factor));
+
+ std::vector<uint8_t> buffer(pixels, 0);
+
+ DBG(DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n", __func__, pixels, pixels);
+
+ /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address
+ * is 8192*reg value */
+
+ /* write actual color channel data */
+ for(i=0;i<3;i++)
+ {
+ /* build up actual shading data by copying the part from the full width one
+ * to the one corresponding to SHDAREA */
+ ptr = buffer.data();
+
+ /* iterate on both sensor segment */
+ for(x=0;x<pixels;x+=4*factor)
+ {
+ /* coefficient source */
+ src=(data+strpixel+i*length)+x;
+
+ /* coefficient copy */
+ ptr[0]=src[0];
+ ptr[1]=src[1];
+ ptr[2]=src[2];
+ ptr[3]=src[3];
+
+ /* next shading coefficient */
+ ptr+=4;
+ }
+
+ val = dev->interface->read_register(0xd0+i);
+ addr = val * 8192 + 0x10000000;
+ dev->interface->write_ahb(addr, pixels, buffer.data());
+ }
+}
+
+/** @brief calibrates led exposure
+ * Calibrate exposure by scanning a white area until the used exposure gives
+ * data white enough.
+ * @param dev device to calibrate
+ */
+SensorExposure CommandSetGl847::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ int num_pixels;
+ int total_size;
+ int used_res;
+ int i, j;
+ int val;
+ int channels;
+ int avg[3], top[3], bottom[3];
+ int turn;
+ uint16_t exp[3];
+ float move;
+
+ move = static_cast<float>(dev->model->y_offset_calib_white);
+ move = static_cast<float>((move * (dev->motor.base_ydpi / 4)) / MM_PER_INCH);
+ if (move > 20) {
+ scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move),
+ Direction::FORWARD);
+ }
+ DBG(DBG_io, "%s: move=%f steps\n", __func__, move);
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ used_res = sensor.get_register_hwdpi(dev->settings.xres);
+ const auto& calib_sensor = sanei_genesys_find_sensor(dev, used_res, channels,
+ dev->settings.scan_method);
+ num_pixels = (calib_sensor.sensor_pixels * used_res) / calib_sensor.optical_res;
+
+ /* initial calibration reg values */
+ regs = dev->reg;
+
+ ScanSession session;
+ session.params.xres = used_res;
+ session.params.yres = used_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = num_pixels;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, calib_sensor);
+
+ init_regs_for_scan_session(dev, calib_sensor, &regs, session);
+
+ total_size = num_pixels * channels * (session.params.depth/8) * 1;
+ std::vector<uint8_t> line(total_size);
+
+ // initial loop values and boundaries
+ exp[0] = calib_sensor.exposure.red;
+ exp[1] = calib_sensor.exposure.green;
+ exp[2] = calib_sensor.exposure.blue;
+
+ bottom[0] = 28000;
+ bottom[1] = 28000;
+ bottom[2] = 28000;
+
+ top[0] = 32000;
+ top[1] = 32000;
+ top[2] = 32000;
+
+ turn = 0;
+
+ /* no move during led calibration */
+ bool acceptable = false;
+ sanei_genesys_set_motor_power(regs, false);
+ do
+ {
+ // set up exposure
+ regs.set16(REG_EXPR,exp[0]);
+ regs.set16(REG_EXPG,exp[1]);
+ regs.set16(REG_EXPB,exp[2]);
+
+ // write registers and scan data
+ dev->interface->write_registers(regs);
+
+ DBG(DBG_info, "%s: starting line reading\n", __func__);
+ begin_scan(dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("led_calibration");
+ scanner_stop_action(*dev);
+ move_back_home(dev, true);
+ return calib_sensor.exposure;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ // stop scanning
+ scanner_stop_action(*dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl847_led_%02d.pnm", turn);
+ sanei_genesys_write_pnm_file(fn, line.data(), session.params.depth,
+ channels, num_pixels, 1);
+ }
+
+ /* compute average */
+ for (j = 0; j < channels; j++)
+ {
+ avg[j] = 0;
+ for (i = 0; i < num_pixels; i++)
+ {
+ if (dev->model->is_cis)
+ val =
+ line[i * 2 + j * 2 * num_pixels + 1] * 256 +
+ line[i * 2 + j * 2 * num_pixels];
+ else
+ val =
+ line[i * 2 * channels + 2 * j + 1] * 256 +
+ line[i * 2 * channels + 2 * j];
+ avg[j] += val;
+ }
+
+ avg[j] /= num_pixels;
+ }
+
+ DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
+
+ /* check if exposure gives average within the boundaries */
+ acceptable = true;
+ for(i=0;i<3;i++)
+ {
+ if (avg[i] < bottom[i] || avg[i] > top[i]) {
+ auto target = (bottom[i] + top[i]) / 2;
+ exp[i] = (exp[i] * target) / avg[i];
+ acceptable = false;
+ }
+ }
+
+ turn++;
+ }
+ while (!acceptable && turn < 100);
+
+ DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
+
+ // set these values as final ones for scan
+ dev->reg.set16(REG_EXPR, exp[0]);
+ dev->reg.set16(REG_EXPG, exp[1]);
+ dev->reg.set16(REG_EXPB, exp[2]);
+
+ // go back home
+ if (move>20) {
+ move_back_home(dev, true);
+ }
+
+ return { exp[0], exp[1], exp[2] };
+}
+
+/**
+ * set up GPIO/GPOE for idle state
+ */
+static void gl847_init_gpio(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ int idx=0;
+
+ /* search GPIO profile */
+ while(gpios[idx].gpio_id != GpioId::UNKNOWN && dev->model->gpio_id != gpios[idx].gpio_id) {
+ idx++;
+ }
+ if (gpios[idx].gpio_id == GpioId::UNKNOWN) {
+ throw SaneException("failed to find GPIO profile for sensor_id=%d",
+ static_cast<unsigned>(dev->model->sensor_id));
+ }
+
+ dev->interface->write_register(REG_0xA7, gpios[idx].ra7);
+ dev->interface->write_register(REG_0xA6, gpios[idx].ra6);
+
+ dev->interface->write_register(REG_0x6E, gpios[idx].r6e);
+ dev->interface->write_register(REG_0x6C, 0x00);
+
+ dev->interface->write_register(REG_0x6B, gpios[idx].r6b);
+ dev->interface->write_register(REG_0x6C, gpios[idx].r6c);
+ dev->interface->write_register(REG_0x6D, gpios[idx].r6d);
+ dev->interface->write_register(REG_0x6E, gpios[idx].r6e);
+ dev->interface->write_register(REG_0x6F, gpios[idx].r6f);
+
+ dev->interface->write_register(REG_0xA8, gpios[idx].ra8);
+ dev->interface->write_register(REG_0xA9, gpios[idx].ra9);
+}
+
+/**
+ * set memory layout by filling values in dedicated registers
+ */
+static void gl847_init_memory_layout(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+ int idx = 0;
+ uint8_t val;
+
+ /* point to per model memory layout */
+ idx = 0;
+ if (dev->model->model_id == ModelId::CANON_LIDE_100) {
+ idx = 0;
+ }
+ if (dev->model->model_id == ModelId::CANON_LIDE_200) {
+ idx = 1;
+ }
+ if (dev->model->model_id == ModelId::CANON_5600F) {
+ idx = 2;
+ }
+ if (dev->model->model_id == ModelId::CANON_LIDE_700F) {
+ idx = 3;
+ }
+
+ /* CLKSET nd DRAMSEL */
+ val = layouts[idx].dramsel;
+ dev->interface->write_register(REG_0x0B, val);
+ dev->reg.find_reg(0x0b).value = val;
+
+ /* prevent further writings by bulk write register */
+ dev->reg.remove_reg(0x0b);
+
+ /* setup base address for shading data. */
+ /* values must be multiplied by 8192=0x4000 to give address on AHB */
+ /* R-Channel shading bank0 address setting for CIS */
+ dev->interface->write_register(0xd0, layouts[idx].rd0);
+ /* G-Channel shading bank0 address setting for CIS */
+ dev->interface->write_register(0xd1, layouts[idx].rd1);
+ /* B-Channel shading bank0 address setting for CIS */
+ dev->interface->write_register(0xd2, layouts[idx].rd2);
+
+ /* setup base address for scanned data. */
+ /* values must be multiplied by 1024*2=0x0800 to give address on AHB */
+ /* R-Channel ODD image buffer 0x0124->0x92000 */
+ /* size for each buffer is 0x16d*1k word */
+ dev->interface->write_register(0xe0, layouts[idx].re0);
+ dev->interface->write_register(0xe1, layouts[idx].re1);
+ /* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
+ dev->interface->write_register(0xe2, layouts[idx].re2);
+ dev->interface->write_register(0xe3, layouts[idx].re3);
+
+ /* R-Channel EVEN image buffer 0x0292 */
+ dev->interface->write_register(0xe4, layouts[idx].re4);
+ dev->interface->write_register(0xe5, layouts[idx].re5);
+ /* R-Channel EVEN image buffer end-address 0x03ff*/
+ dev->interface->write_register(0xe6, layouts[idx].re6);
+ dev->interface->write_register(0xe7, layouts[idx].re7);
+
+ /* same for green, since CIS, same addresses */
+ dev->interface->write_register(0xe8, layouts[idx].re0);
+ dev->interface->write_register(0xe9, layouts[idx].re1);
+ dev->interface->write_register(0xea, layouts[idx].re2);
+ dev->interface->write_register(0xeb, layouts[idx].re3);
+ dev->interface->write_register(0xec, layouts[idx].re4);
+ dev->interface->write_register(0xed, layouts[idx].re5);
+ dev->interface->write_register(0xee, layouts[idx].re6);
+ dev->interface->write_register(0xef, layouts[idx].re7);
+
+/* same for blue, since CIS, same addresses */
+ dev->interface->write_register(0xf0, layouts[idx].re0);
+ dev->interface->write_register(0xf1, layouts[idx].re1);
+ dev->interface->write_register(0xf2, layouts[idx].re2);
+ dev->interface->write_register(0xf3, layouts[idx].re3);
+ dev->interface->write_register(0xf4, layouts[idx].re4);
+ dev->interface->write_register(0xf5, layouts[idx].re5);
+ dev->interface->write_register(0xf6, layouts[idx].re6);
+ dev->interface->write_register(0xf7, layouts[idx].re7);
+}
+
+/* *
+ * initialize ASIC from power on condition
+ */
+void CommandSetGl847::asic_boot(Genesys_Device* dev, bool cold) const
+{
+ DBG_HELPER(dbg);
+
+ // reset ASIC if cold boot
+ if (cold) {
+ dev->interface->write_register(0x0e, 0x01);
+ dev->interface->write_register(0x0e, 0x00);
+ }
+
+ // test CHKVER
+ uint8_t val = dev->interface->read_register(REG_0x40);
+ if (val & REG_0x40_CHKVER) {
+ val = dev->interface->read_register(0x00);
+ DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
+ }
+
+ /* Set default values for registers */
+ gl847_init_registers (dev);
+
+ // Write initial registers
+ dev->interface->write_registers(dev->reg);
+
+ /* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
+ val = dev->reg.find_reg(0x0b).value & REG_0x0B_DRAMSEL;
+ val = (val | REG_0x0B_ENBDRAM);
+ dev->interface->write_register(REG_0x0B, val);
+ dev->reg.find_reg(0x0b).value = val;
+
+ /* CIS_LINE */
+ dev->reg.init_reg(0x08, REG_0x08_CIS_LINE);
+ dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value);
+
+ // set up end access
+ dev->interface->write_0x8c(0x10, 0x0b);
+ dev->interface->write_0x8c(0x13, 0x0e);
+
+ // setup gpio
+ gl847_init_gpio(dev);
+
+ // setup internal memory layout
+ gl847_init_memory_layout (dev);
+
+ dev->reg.init_reg(0xf8, 0x01);
+ dev->interface->write_register(0xf8, dev->reg.find_reg(0xf8).value);
+}
+
+/**
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home
+ */
+void CommandSetGl847::init(Genesys_Device* dev) const
+{
+ DBG_INIT ();
+ DBG_HELPER(dbg);
+
+ sanei_genesys_asic_init(dev, 0);
+}
+
+void CommandSetGl847::update_hardware_sensors(Genesys_Scanner* s) const
+{
+ DBG_HELPER(dbg);
+ /* do what is needed to get a new set of events, but try to not lose
+ any of them.
+ */
+ uint8_t val;
+ uint8_t scan, file, email, copy;
+ switch(s->dev->model->gpio_id) {
+ case GpioId::CANON_LIDE_700F:
+ scan=0x04;
+ file=0x02;
+ email=0x01;
+ copy=0x08;
+ break;
+ default:
+ scan=0x01;
+ file=0x02;
+ email=0x04;
+ copy=0x08;
+ }
+ val = s->dev->interface->read_register(REG_0x6D);
+
+ s->buttons[BUTTON_SCAN_SW].write((val & scan) == 0);
+ s->buttons[BUTTON_FILE_SW].write((val & file) == 0);
+ s->buttons[BUTTON_EMAIL_SW].write((val & email) == 0);
+ s->buttons[BUTTON_COPY_SW].write((val & copy) == 0);
+}
+
+void CommandSetGl847::update_home_sensor_gpio(Genesys_Device& dev) const
+{
+ DBG_HELPER(dbg);
+
+ if (dev.model->gpio_id == GpioId::CANON_LIDE_700F) {
+ std::uint8_t val = dev.interface->read_register(REG_0x6C);
+ val &= ~REG_0x6C_GPIO10;
+ dev.interface->write_register(REG_0x6C, val);
+ } else {
+ std::uint8_t val = dev.interface->read_register(REG_0x6C);
+ val |= REG_0x6C_GPIO10;
+ dev.interface->write_register(REG_0x6C, val);
+ }
+}
+
+/** @brief search for a full width black or white strip.
+ * This function searches for a black or white stripe across the scanning area.
+ * When searching backward, the searched area must completely be of the desired
+ * color since this area will be used for calibration which scans forward.
+ * @param dev scanner device
+ * @param forward true if searching forward, false if searching backward
+ * @param black true if searching for a black strip, false for a white strip
+ */
+void CommandSetGl847::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward,
+ bool black) const
+{
+ DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse");
+ unsigned int pixels, lines, channels;
+ Genesys_Register_Set local_reg;
+ size_t size;
+ unsigned int pass, count, found, x, y;
+ char title[80];
+
+ set_fe(dev, sensor, AFE_SET);
+ scanner_stop_action(*dev);
+
+ // set up for a gray scan at lowest dpi
+ const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method);
+ unsigned dpi = resolution_settings.get_min_resolution_x();
+ channels = 1;
+ /* 10 MM */
+ /* lines = (10 * dpi) / MM_PER_INCH; */
+ /* shading calibation is done with dev->motor.base_ydpi */
+ lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
+ pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res;
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ local_reg = dev->reg;
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA;
+ if (!forward) {
+ session.params.flags |= ScanFlag::REVERSE;
+ }
+ compute_session(dev, session, sensor);
+
+ size = pixels * channels * lines * (session.params.depth / 8);
+ std::vector<uint8_t> data(size);
+
+ init_regs_for_scan_session(dev, sensor, &local_reg, session);
+
+ dev->interface->write_registers(local_reg);
+
+ begin_scan(dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("search_strip");
+ scanner_stop_action(*dev);
+ return;
+ }
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ scanner_stop_action(*dev);
+
+ pass = 0;
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl847_search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ sanei_genesys_write_pnm_file(title, data.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* loop until strip is found or maximum pass number done */
+ found = 0;
+ while (pass < 20 && !found)
+ {
+ dev->interface->write_registers(local_reg);
+
+ // now start scan
+ begin_scan(dev, sensor, &local_reg, true);
+
+ wait_until_buffer_non_empty(dev);
+
+ // now we're on target, we can read data
+ sanei_genesys_read_data_from_scanner(dev, data.data(), size);
+
+ scanner_stop_action(*dev);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ std::sprintf(title, "gl847_search_strip_%s_%s%02d.pnm",
+ black ? "black" : "white",
+ forward ? "fwd" : "bwd", static_cast<int>(pass));
+ sanei_genesys_write_pnm_file(title, data.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* search data to find black strip */
+ /* when searching forward, we only need one line of the searched color since we
+ * will scan forward. But when doing backward search, we need all the area of the
+ * same color */
+ if (forward)
+ {
+ for (y = 0; y < lines && !found; y++)
+ {
+ count = 0;
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+
+ /* at end of line, if count >= 3%, line is not fully of the desired color
+ * so we must go to next line of the buffer */
+ /* count*100/pixels < 3 */
+ if ((count * 100) / pixels < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
+ pass, y);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ }
+ else /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward */
+ {
+ count = 0;
+ for (y = 0; y < lines; y++)
+ {
+ /* count of white/black pixels depending on the color searched */
+ for (x = 0; x < pixels; x++)
+ {
+ /* when searching for black, detect white pixels */
+ if (black && data[y * pixels + x] > 90)
+ {
+ count++;
+ }
+ /* when searching for white, detect black pixels */
+ if (!black && data[y * pixels + x] < 60)
+ {
+ count++;
+ }
+ }
+ }
+
+ /* at end of area, if count >= 3%, area is not fully of the desired color
+ * so we must go to next buffer */
+ if ((count * 100) / (pixels * lines) < 3)
+ {
+ found = 1;
+ DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
+ }
+ else
+ {
+ DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
+ (100 * count) / pixels);
+ }
+ }
+ pass++;
+ }
+
+ if (found)
+ {
+ DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
+ }
+ else
+ {
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white");
+ }
+}
+
+/**
+ * average dark pixels of a 8 bits scan
+ */
+static int
+dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
+ unsigned int channels, unsigned int black)
+{
+ unsigned int i, j, k, average, count;
+ unsigned int avg[3];
+ uint8_t val;
+
+ /* computes average value on black margin */
+ for (k = 0; k < channels; k++)
+ {
+ avg[k] = 0;
+ count = 0;
+ for (i = 0; i < lines; i++)
+ {
+ for (j = 0; j < black; j++)
+ {
+ val = data[i * channels * pixels + j + k];
+ avg[k] += val;
+ count++;
+ }
+ }
+ if (count)
+ avg[k] /= count;
+ DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
+ }
+ average = 0;
+ for (i = 0; i < channels; i++)
+ average += avg[i];
+ average /= channels;
+ DBG(DBG_info, "%s: average = %d\n", __func__, average);
+ return average;
+}
+
+void CommandSetGl847::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const
+{
+ DBG_HELPER(dbg);
+ unsigned channels;
+ int pass = 0, avg, total_size;
+ int topavg, bottomavg, lines;
+ int top, bottom, black_pixels, pixels;
+
+ // no gain nor offset for AKM AFE
+ uint8_t reg04 = dev->interface->read_register(REG_0x04);
+ if ((reg04 & REG_0x04_FESET) == 0x02) {
+ return;
+ }
+
+ /* offset calibration is always done in color mode */
+ channels = 3;
+ dev->calib_pixels = sensor.sensor_pixels;
+ lines=1;
+ pixels= (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res;
+ black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
+ DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = sensor.optical_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ /* allocate memory for scans */
+ total_size = pixels * channels * lines * (session.params.depth / 8); /* colors * bytes_per_color * scan lines */
+
+ std::vector<uint8_t> first_line(total_size);
+ std::vector<uint8_t> second_line(total_size);
+
+ /* init gain */
+ dev->frontend.set_gain(0, 0);
+ dev->frontend.set_gain(1, 0);
+ dev->frontend.set_gain(2, 0);
+
+ /* scan with no move */
+ bottom = 10;
+ dev->frontend.set_offset(0, bottom);
+ dev->frontend.set_offset(1, bottom);
+ dev->frontend.set_offset(2, bottom);
+
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("offset_calibration");
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl847_offset%03d.pnm", bottom);
+ sanei_genesys_write_pnm_file(fn, first_line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ bottomavg = dark_average (first_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
+
+ /* now top value */
+ top = 255;
+ dev->frontend.set_offset(0, top);
+ dev->frontend.set_offset(1, top);
+ dev->frontend.set_offset(2, top);
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+
+ topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
+
+ /* loop until acceptable level */
+ while ((pass < 32) && (top - bottom > 1))
+ {
+ pass++;
+
+ /* settings for new scan */
+ dev->frontend.set_offset(0, (top + bottom) / 2);
+ dev->frontend.set_offset(1, (top + bottom) / 2);
+ dev->frontend.set_offset(2, (top + bottom) / 2);
+
+ // scan with no move
+ set_fe(dev, sensor, AFE_SET);
+ dev->interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ begin_scan(dev, sensor, &regs, true);
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data)
+ {
+ char fn[30];
+ std::snprintf(fn, 30, "gl847_offset%03d.pnm", dev->frontend.get_offset(1));
+ sanei_genesys_write_pnm_file(fn, second_line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
+ DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
+
+ /* compute new boundaries */
+ if (topavg == avg)
+ {
+ topavg = avg;
+ top = dev->frontend.get_offset(1);
+ }
+ else
+ {
+ bottomavg = avg;
+ bottom = dev->frontend.get_offset(1);
+ }
+ }
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev->frontend.get_offset(0),
+ dev->frontend.get_offset(1),
+ dev->frontend.get_offset(2));
+}
+
+void CommandSetGl847::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const
+{
+ DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
+ int pixels;
+ int total_size;
+ int i, j, channels;
+ int max[3];
+ float gain[3],coeff;
+ int val, code, lines;
+
+ // no gain nor offset for AKM AFE
+ uint8_t reg04 = dev->interface->read_register(REG_0x04);
+ if ((reg04 & REG_0x04_FESET) == 0x02) {
+ return;
+ }
+
+ /* coarse gain calibration is always done in color mode */
+ channels = 3;
+
+ /* follow CKSEL */
+ if(dev->settings.xres<sensor.optical_res)
+ {
+ coeff = 0.9f;
+ }
+ else
+ {
+ coeff=1.0;
+ }
+ lines=10;
+ pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res;
+
+ ScanSession session;
+ session.params.xres = sensor.optical_res;
+ session.params.yres = sensor.optical_res;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev->settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev->settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_LINE_DISTANCE;
+ compute_session(dev, session, sensor);
+
+ try {
+ init_regs_for_scan_session(dev, sensor, &regs, session);
+ } catch (...) {
+ catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
+ throw;
+ }
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ dev->interface->write_registers(regs);
+
+ total_size = pixels * channels * (16 / session.params.depth) * lines;
+
+ std::vector<uint8_t> line(total_size);
+
+ set_fe(dev, sensor, AFE_SET);
+ begin_scan(dev, sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev->interface->test_checkpoint("coarse_gain_calibration");
+ scanner_stop_action(*dev);
+ move_back_home(dev, true);
+ return;
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
+
+ if (DBG_LEVEL >= DBG_data) {
+ sanei_genesys_write_pnm_file("gl847_gain.pnm", line.data(), session.params.depth,
+ channels, pixels, lines);
+ }
+
+ /* average value on each channel */
+ for (j = 0; j < channels; j++)
+ {
+ max[j] = 0;
+ for (i = pixels/4; i < (pixels*3/4); i++)
+ {
+ if (dev->model->is_cis) {
+ val = line[i + j * pixels];
+ } else {
+ val = line[i * channels + j];
+ }
+
+ max[j] += val;
+ }
+ max[j] = max[j] / (pixels/2);
+
+ gain[j] = (static_cast<float>(sensor.gain_white_ref) * coeff) / max[j];
+
+ /* turn logical gain value into gain code, checking for overflow */
+ code = static_cast<int>(283 - 208 / gain[j]);
+ if (code > 255)
+ code = 255;
+ else if (code < 0)
+ code = 0;
+ dev->frontend.set_gain(j, code);
+
+ DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j],
+ dev->frontend.get_gain(j));
+ }
+
+ if (dev->model->is_cis) {
+ uint8_t gain0 = dev->frontend.get_gain(0);
+ if (gain0 > dev->frontend.get_gain(1)) {
+ gain0 = dev->frontend.get_gain(1);
+ }
+ if (gain0 > dev->frontend.get_gain(2)) {
+ gain0 = dev->frontend.get_gain(2);
+ }
+ dev->frontend.set_gain(0, gain0);
+ dev->frontend.set_gain(1, gain0);
+ dev->frontend.set_gain(2, gain0);
+ }
+
+ if (channels == 1) {
+ dev->frontend.set_gain(0, dev->frontend.get_gain(1));
+ dev->frontend.set_gain(2, dev->frontend.get_gain(1));
+ }
+
+ scanner_stop_action(*dev);
+
+ move_back_home(dev, true);
+}
+
+bool CommandSetGl847::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
+{
+ (void) dev;
+ return false;
+}
+
+void CommandSetGl847::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const
+{
+ (void) dev;
+ (void) sensor;
+ (void) regs;
+ (void) channels;
+ (void) total_size;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl847::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
+{
+ sanei_genesys_send_gamma_table(dev, sensor);
+}
+
+void CommandSetGl847::wait_for_motor_stop(Genesys_Device* dev) const
+{
+ (void) dev;
+}
+
+void CommandSetGl847::load_document(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl847::detect_document_end(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl847::eject_document(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+void CommandSetGl847::move_to_ta(Genesys_Device* dev) const
+{
+ (void) dev;
+ throw SaneException("not implemented");
+}
+
+std::unique_ptr<CommandSet> create_gl847_cmd_set()
+{
+ return std::unique_ptr<CommandSet>(new CommandSetGl847{});
+}
+
+} // namespace gl847
+} // namespace genesys
diff --git a/backend/genesys/gl847.h b/backend/genesys/gl847.h
new file mode 100644
index 0000000..a51c293
--- /dev/null
+++ b/backend/genesys/gl847.h
@@ -0,0 +1,206 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL847_H
+#define BACKEND_GENESYS_GL847_H
+
+#include "genesys.h"
+#include "command_set.h"
+
+namespace genesys {
+namespace gl847 {
+
+typedef struct
+{
+ GpioId gpio_id;
+ uint8_t r6b;
+ uint8_t r6c;
+ uint8_t r6d;
+ uint8_t r6e;
+ uint8_t r6f;
+ uint8_t ra6;
+ uint8_t ra7;
+ uint8_t ra8;
+ uint8_t ra9;
+} Gpio_Profile;
+
+static Gpio_Profile gpios[]={
+ { GpioId::CANON_LIDE_200, 0x02, 0xf9, 0x20, 0xff, 0x00, 0x04, 0x04, 0x00, 0x00},
+ { GpioId::CANON_LIDE_700F, 0x06, 0xdb, 0xff, 0xff, 0x80, 0x15, 0x07, 0x20, 0x10},
+ { GpioId::UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+typedef struct
+{
+ uint8_t dramsel;
+ uint8_t rd0;
+ uint8_t rd1;
+ uint8_t rd2;
+ uint8_t re0;
+ uint8_t re1;
+ uint8_t re2;
+ uint8_t re3;
+ uint8_t re4;
+ uint8_t re5;
+ uint8_t re6;
+ uint8_t re7;
+} Memory_layout;
+
+static Memory_layout layouts[]={
+ /* LIDE 100 */
+ {
+ 0x29,
+ 0x0a, 0x15, 0x20,
+ 0x00, 0xac, 0x02, 0x55, 0x02, 0x56, 0x03, 0xff
+ },
+ /* LIDE 200 */
+ {
+ 0x29,
+ 0x0a, 0x1f, 0x34,
+ 0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
+ },
+ /* 5600F */
+ {
+ 0x29,
+ 0x0a, 0x1f, 0x34,
+ 0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
+ },
+ /* LIDE 700F */
+ {
+ 0x2a,
+ 0x0a, 0x33, 0x5c,
+ 0x02, 0x14, 0x09, 0x09, 0x09, 0x0a, 0x0f, 0xff
+ }
+};
+
+class CommandSetGl847 : public CommandSet
+{
+public:
+ ~CommandSetGl847() override = default;
+
+ bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override;
+
+ void init(Genesys_Device* dev) const override;
+
+ void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, int* channels,
+ int* total_size) const override;
+
+ void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* reg,
+ const ScanSession& session) const override;
+
+ void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const override;
+ void set_powersaving(Genesys_Device* dev, int delay) const override;
+ void save_power(Genesys_Device* dev, bool enable) const override;
+
+ void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set* regs, bool start_motor) const override;
+
+ void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override;
+
+ void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override;
+
+ void search_start_position(Genesys_Device* dev) const override;
+
+ void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, int dpi) const override;
+
+ SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs) const override;
+
+ void wait_for_motor_stop(Genesys_Device* dev) const override;
+
+ void move_back_home(Genesys_Device* dev, bool wait_until_home) const override;
+
+ void update_hardware_sensors(struct Genesys_Scanner* s) const override;
+
+ bool needs_update_home_sensor_gpio() const override { return true; }
+
+ void update_home_sensor_gpio(Genesys_Device& dev) const override;
+
+ void load_document(Genesys_Device* dev) const override;
+
+ void detect_document_end(Genesys_Device* dev) const override;
+
+ void eject_document(Genesys_Device* dev) const override;
+
+ void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ bool forward, bool black) const override;
+
+ void move_to_ta(Genesys_Device* dev) const override;
+
+ void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,
+ int size) const override;
+
+ ScanSession calculate_scan_session(const Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ const Genesys_Settings& settings) const override;
+
+ void asic_boot(Genesys_Device* dev, bool cold) const override;
+};
+
+enum SlopeTable
+{
+ SCAN_TABLE = 0, // table 1 at 0x4000
+ BACKTRACK_TABLE = 1, // table 2 at 0x4800
+ STOP_TABLE = 2, // table 3 at 0x5000
+ FAST_TABLE = 3, // table 4 at 0x5800
+ HOME_TABLE = 4, // table 5 at 0x6000
+};
+
+} // namespace gl847
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL847_H
diff --git a/backend/genesys/gl847_registers.h b/backend/genesys/gl847_registers.h
new file mode 100644
index 0000000..0603a6a
--- /dev/null
+++ b/backend/genesys/gl847_registers.h
@@ -0,0 +1,333 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_GL847_REGISTERS_H
+#define BACKEND_GENESYS_GL847_REGISTERS_H
+
+#include <cstdint>
+
+namespace genesys {
+namespace gl847 {
+
+using RegAddr = std::uint16_t;
+using RegMask = std::uint8_t;
+using RegShift = unsigned;
+
+static constexpr RegAddr REG_0x01 = 0x01;
+static constexpr RegMask REG_0x01_CISSET = 0x80;
+static constexpr RegMask REG_0x01_DOGENB = 0x40;
+static constexpr RegMask REG_0x01_DVDSET = 0x20;
+static constexpr RegMask REG_0x01_STAGGER = 0x10;
+static constexpr RegMask REG_0x01_COMPENB = 0x08;
+static constexpr RegMask REG_0x01_TRUEGRAY = 0x04;
+static constexpr RegMask REG_0x01_SHDAREA = 0x02;
+static constexpr RegMask REG_0x01_SCAN = 0x01;
+
+static constexpr RegAddr REG_0x02 = 0x02;
+static constexpr RegMask REG_0x02_NOTHOME = 0x80;
+static constexpr RegMask REG_0x02_ACDCDIS = 0x40;
+static constexpr RegMask REG_0x02_AGOHOME = 0x20;
+static constexpr RegMask REG_0x02_MTRPWR = 0x10;
+static constexpr RegMask REG_0x02_FASTFED = 0x08;
+static constexpr RegMask REG_0x02_MTRREV = 0x04;
+static constexpr RegMask REG_0x02_HOMENEG = 0x02;
+static constexpr RegMask REG_0x02_LONGCURV = 0x01;
+
+static constexpr RegAddr REG_0x03 = 0x03;
+static constexpr RegMask REG_0x03_LAMPDOG = 0x80;
+static constexpr RegMask REG_0x03_AVEENB = 0x40;
+static constexpr RegMask REG_0x03_XPASEL = 0x20;
+static constexpr RegMask REG_0x03_LAMPPWR = 0x10;
+static constexpr RegMask REG_0x03_LAMPTIM = 0x0f;
+
+static constexpr RegAddr REG_0x04 = 0x04;
+static constexpr RegMask REG_0x04_LINEART = 0x80;
+static constexpr RegMask REG_0x04_BITSET = 0x40;
+static constexpr RegMask REG_0x04_AFEMOD = 0x30;
+static constexpr RegMask REG_0x04_FILTER = 0x0c;
+static constexpr RegMask REG_0x04_FESET = 0x03;
+static constexpr RegShift REG_0x04S_AFEMOD = 4;
+
+static constexpr RegAddr REG_0x05 = 0x05;
+static constexpr RegMask REG_0x05_DPIHW = 0xc0;
+static constexpr RegMask REG_0x05_DPIHW_600 = 0x00;
+static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40;
+static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80;
+static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0;
+static constexpr RegMask REG_0x05_MTLLAMP = 0x30;
+static constexpr RegMask REG_0x05_GMMENB = 0x08;
+static constexpr RegMask REG_0x05_MTLBASE = 0x03;
+
+static constexpr RegAddr REG_0x06 = 0x06;
+static constexpr RegMask REG_0x06_SCANMOD = 0xe0;
+static constexpr RegMask REG_0x06S_SCANMOD = 5;
+static constexpr RegMask REG_0x06_PWRBIT = 0x10;
+static constexpr RegMask REG_0x06_GAIN4 = 0x08;
+static constexpr RegMask REG_0x06_OPTEST = 0x07;
+
+static constexpr RegMask REG_0x07_LAMPSIM = 0x80;
+
+static constexpr RegMask REG_0x08_DRAM2X = 0x80;
+static constexpr RegMask REG_0x08_MPENB = 0x20;
+static constexpr RegMask REG_0x08_CIS_LINE = 0x10;
+static constexpr RegMask REG_0x08_IR1ENB = 0x08;
+static constexpr RegMask REG_0x08_IR2ENB = 0x04;
+static constexpr RegMask REG_0x08_ENB24M = 0x01;
+
+static constexpr RegMask REG_0x09_MCNTSET = 0xc0;
+static constexpr RegMask REG_0x09_EVEN1ST = 0x20;
+static constexpr RegMask REG_0x09_BLINE1ST = 0x10;
+static constexpr RegMask REG_0x09_BACKSCAN = 0x08;
+static constexpr RegMask REG_0x09_ENHANCE = 0x04;
+static constexpr RegMask REG_0x09_SHORTTG = 0x02;
+static constexpr RegMask REG_0x09_NWAIT = 0x01;
+
+static constexpr RegShift REG_0x09S_MCNTSET = 6;
+static constexpr RegShift REG_0x09S_CLKSET = 4;
+
+static constexpr RegMask REG_0x0A_LPWMEN = 0x10;
+
+static constexpr RegAddr REG_0x0B = 0x0b;
+static constexpr RegMask REG_0x0B_DRAMSEL = 0x07;
+static constexpr RegMask REG_0x0B_ENBDRAM = 0x08;
+static constexpr RegMask REG_0x0B_RFHDIS = 0x10;
+static constexpr RegMask REG_0x0B_CLKSET = 0xe0;
+static constexpr RegMask REG_0x0B_24MHZ = 0x00;
+static constexpr RegMask REG_0x0B_30MHZ = 0x20;
+static constexpr RegMask REG_0x0B_40MHZ = 0x40;
+static constexpr RegMask REG_0x0B_48MHZ = 0x60;
+static constexpr RegMask REG_0x0B_60MHZ = 0x80;
+
+static constexpr RegAddr REG_0x0C = 0x0c;
+static constexpr RegMask REG_0x0C_CCDLMT = 0x0f;
+
+static constexpr RegAddr REG_0x0D = 0x0d;
+static constexpr RegMask REG_0x0D_FULLSTP = 0x10;
+static constexpr RegMask REG_0x0D_SEND = 0x80;
+static constexpr RegMask REG_0x0D_CLRMCNT = 0x04;
+static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02;
+static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01;
+
+static constexpr RegAddr REG_0x0F = 0x0f;
+
+static constexpr RegMask REG_0x16_CTRLHI = 0x80;
+static constexpr RegMask REG_0x16_TOSHIBA = 0x40;
+static constexpr RegMask REG_0x16_TGINV = 0x20;
+static constexpr RegMask REG_0x16_CK1INV = 0x10;
+static constexpr RegMask REG_0x16_CK2INV = 0x08;
+static constexpr RegMask REG_0x16_CTRLINV = 0x04;
+static constexpr RegMask REG_0x16_CKDIS = 0x02;
+static constexpr RegMask REG_0x16_CTRLDIS = 0x01;
+
+static constexpr RegMask REG_0x17_TGMODE = 0xc0;
+static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00;
+static constexpr RegMask REG_0x17_TGMODE_REF = 0x40;
+static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80;
+static constexpr RegMask REG_0x17_TGW = 0x3f;
+static constexpr RegMask REG_0x17S_TGW = 0;
+
+static constexpr RegAddr REG_0x18 = 0x18;
+static constexpr RegMask REG_0x18_CNSET = 0x80;
+static constexpr RegMask REG_0x18_DCKSEL = 0x60;
+static constexpr RegMask REG_0x18_CKTOGGLE = 0x10;
+static constexpr RegMask REG_0x18_CKDELAY = 0x0c;
+static constexpr RegMask REG_0x18_CKSEL = 0x03;
+
+static constexpr RegMask REG_0x1A_SW2SET = 0x80;
+static constexpr RegMask REG_0x1A_SW1SET = 0x40;
+static constexpr RegMask REG_0x1A_MANUAL3 = 0x02;
+static constexpr RegMask REG_0x1A_MANUAL1 = 0x01;
+static constexpr RegMask REG_0x1A_CK4INV = 0x08;
+static constexpr RegMask REG_0x1A_CK3INV = 0x04;
+static constexpr RegMask REG_0x1A_LINECLP = 0x02;
+
+static constexpr RegAddr REG_0x1C = 0x1c;
+static constexpr RegMask REG_0x1C_TGTIME = 0x07;
+
+static constexpr RegMask REG_0x1D_CK4LOW = 0x80;
+static constexpr RegMask REG_0x1D_CK3LOW = 0x40;
+static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
+static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
+static constexpr RegMask REG_0x1DS_TGSHLD = 0;
+
+static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
+static constexpr RegMask REG_0x1ES_WDTIME = 4;
+static constexpr RegMask REG_0x1E_LINESEL = 0x0f;
+static constexpr RegMask REG_0x1ES_LINESEL = 0;
+
+static constexpr RegAddr REG_FEDCNT = 0x1f;
+
+static constexpr RegAddr REG_0x24 = 0x1c;
+static constexpr RegAddr REG_0x40 = 0x40;
+static constexpr RegMask REG_0x40_CHKVER = 0x10;
+static constexpr RegMask REG_0x40_HISPDFLG = 0x04;
+static constexpr RegMask REG_0x40_MOTMFLG = 0x02;
+static constexpr RegMask REG_0x40_DATAENB = 0x01;
+
+static constexpr RegMask REG_0x41_PWRBIT = 0x80;
+static constexpr RegMask REG_0x41_BUFEMPTY = 0x40;
+static constexpr RegMask REG_0x41_FEEDFSH = 0x20;
+static constexpr RegMask REG_0x41_SCANFSH = 0x10;
+static constexpr RegMask REG_0x41_HOMESNR = 0x08;
+static constexpr RegMask REG_0x41_LAMPSTS = 0x04;
+static constexpr RegMask REG_0x41_FEBUSY = 0x02;
+static constexpr RegMask REG_0x41_MOTORENB = 0x01;
+
+static constexpr RegMask REG_0x58_VSMP = 0xf8;
+static constexpr RegShift REG_0x58S_VSMP = 3;
+static constexpr RegMask REG_0x58_VSMPW = 0x07;
+static constexpr RegShift REG_0x58S_VSMPW = 0;
+
+static constexpr RegMask REG_0x59_BSMP = 0xf8;
+static constexpr RegShift REG_0x59S_BSMP = 3;
+static constexpr RegMask REG_0x59_BSMPW = 0x07;
+static constexpr RegShift REG_0x59S_BSMPW = 0;
+
+static constexpr RegMask REG_0x5A_ADCLKINV = 0x80;
+static constexpr RegMask REG_0x5A_RLCSEL = 0x40;
+static constexpr RegMask REG_0x5A_CDSREF = 0x30;
+static constexpr RegShift REG_0x5AS_CDSREF = 4;
+static constexpr RegMask REG_0x5A_RLC = 0x0f;
+static constexpr RegShift REG_0x5AS_RLC = 0;
+
+static constexpr RegMask REG_0x5E_DECSEL = 0xe0;
+static constexpr RegShift REG_0x5ES_DECSEL = 5;
+static constexpr RegMask REG_0x5E_STOPTIM = 0x1f;
+static constexpr RegShift REG_0x5ES_STOPTIM = 0;
+
+static constexpr RegAddr REG_0x60 = 0x60;
+static constexpr RegMask REG_0x60_Z1MOD = 0x1f;
+static constexpr RegAddr REG_0x61 = 0x61;
+static constexpr RegMask REG_0x61_Z1MOD = 0xff;
+static constexpr RegAddr REG_0x62 = 0x62;
+static constexpr RegMask REG_0x62_Z1MOD = 0xff;
+
+static constexpr RegAddr REG_0x63 = 0x63;
+static constexpr RegMask REG_0x63_Z2MOD = 0x1f;
+static constexpr RegAddr REG_0x64 = 0x64;
+static constexpr RegMask REG_0x64_Z2MOD = 0xff;
+static constexpr RegAddr REG_0x65 = 0x65;
+static constexpr RegMask REG_0x65_Z2MOD = 0xff;
+
+static constexpr RegShift REG_0x60S_STEPSEL = 5;
+static constexpr RegMask REG_0x60_STEPSEL = 0xe0;
+static constexpr RegMask REG_0x60_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x60_HALFSTEP = 0x20;
+static constexpr RegMask REG_0x60_EIGHTHSTEP = 0x60;
+static constexpr RegMask REG_0x60_16THSTEP = 0x80;
+
+static constexpr RegShift REG_0x63S_FSTPSEL = 5;
+static constexpr RegMask REG_0x63_FSTPSEL = 0xe0;
+static constexpr RegMask REG_0x63_FULLSTEP = 0x00;
+static constexpr RegMask REG_0x63_HALFSTEP = 0x20;
+static constexpr RegMask REG_0x63_EIGHTHSTEP = 0x60;
+static constexpr RegMask REG_0x63_16THSTEP = 0x80;
+
+static constexpr RegAddr REG_0x67 = 0x67;
+static constexpr RegMask REG_0x67_MTRPWM = 0x80;
+
+static constexpr RegAddr REG_0x68 = 0x68;
+static constexpr RegMask REG_0x68_FASTPWM = 0x80;
+
+static constexpr RegAddr REG_0x6B = 0x6b;
+static constexpr RegMask REG_0x6B_MULTFILM = 0x80;
+static constexpr RegMask REG_0x6B_GPOM13 = 0x40;
+static constexpr RegMask REG_0x6B_GPOM12 = 0x20;
+static constexpr RegMask REG_0x6B_GPOM11 = 0x10;
+static constexpr RegMask REG_0x6B_GPO18 = 0x02;
+static constexpr RegMask REG_0x6B_GPO17 = 0x01;
+
+static constexpr RegShift REG_0x6C = 0x6c;
+static constexpr RegMask REG_0x6C_GPIO16 = 0x80;
+static constexpr RegMask REG_0x6C_GPIO15 = 0x40;
+static constexpr RegMask REG_0x6C_GPIO14 = 0x20;
+static constexpr RegMask REG_0x6C_GPIO13 = 0x10;
+static constexpr RegMask REG_0x6C_GPIO12 = 0x08;
+static constexpr RegMask REG_0x6C_GPIO11 = 0x04;
+static constexpr RegMask REG_0x6C_GPIO10 = 0x02;
+static constexpr RegMask REG_0x6C_GPIO9 = 0x01;
+static constexpr RegMask REG_0x6C_GPIOH = 0xff;
+static constexpr RegMask REG_0x6C_GPIOL = 0xff;
+
+static constexpr RegAddr REG_0x6D = 0x6d;
+static constexpr RegAddr REG_0x6E = 0x6e;
+static constexpr RegAddr REG_0x6F = 0x6f;
+static constexpr RegAddr REG_0x7E = 0x7e;
+
+static constexpr RegMask REG_0x87_LEDADD = 0x04;
+
+static constexpr RegAddr REG_0x9E = 0x9e;
+static constexpr RegAddr REG_0x9F = 0x9f;
+
+static constexpr RegAddr REG_0xA6 = 0xa6;
+static constexpr RegAddr REG_0xA7 = 0xa7;
+static constexpr RegAddr REG_0xA8 = 0xa8;
+static constexpr RegAddr REG_0xA9 = 0xa9;
+static constexpr RegAddr REG_0xAB = 0xab;
+
+static constexpr RegAddr REG_EXPR = 0x10;
+static constexpr RegAddr REG_EXPG = 0x12;
+static constexpr RegAddr REG_EXPB = 0x14;
+static constexpr RegAddr REG_EXPDMY = 0x19;
+static constexpr RegAddr REG_STEPNO = 0x21;
+static constexpr RegAddr REG_FWDSTEP = 0x22;
+static constexpr RegAddr REG_BWDSTEP = 0x23;
+static constexpr RegAddr REG_FASTNO = 0x24;
+static constexpr RegAddr REG_DPISET = 0x2c;
+static constexpr RegAddr REG_STRPIXEL = 0x30;
+static constexpr RegAddr REG_ENDPIXEL = 0x32;
+static constexpr RegAddr REG_LINCNT = 0x25;
+static constexpr RegAddr REG_MAXWD = 0x35;
+static constexpr RegAddr REG_LPERIOD = 0x38;
+static constexpr RegAddr REG_FEEDL = 0x3d;
+static constexpr RegAddr REG_FMOVDEC = 0x5f;
+static constexpr RegAddr REG_FSHDEC = 0x69;
+static constexpr RegAddr REG_FMOVNO = 0x6a;
+static constexpr RegAddr REG_CK1MAP = 0x74;
+static constexpr RegAddr REG_CK3MAP = 0x77;
+static constexpr RegAddr REG_CK4MAP = 0x7a;
+
+} // namespace gl847
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_GL847_REGISTERS_H
diff --git a/backend/genesys/image.cpp b/backend/genesys/image.cpp
new file mode 100644
index 0000000..7d386c6
--- /dev/null
+++ b/backend/genesys/image.cpp
@@ -0,0 +1,204 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "image.h"
+
+#include <array>
+
+namespace genesys {
+
+Image::Image() = default;
+
+Image::Image(std::size_t width, std::size_t height, PixelFormat format) :
+ width_{width},
+ height_{height},
+ format_{format},
+ row_bytes_{get_pixel_row_bytes(format_, width_)}
+{
+ data_.resize(get_row_bytes() * height);
+}
+
+std::uint8_t* Image::get_row_ptr(std::size_t y)
+{
+ return data_.data() + row_bytes_ * y;
+}
+
+const std::uint8_t* Image::get_row_ptr(std::size_t y) const
+{
+ return data_.data() + row_bytes_ * y;
+}
+
+Pixel Image::get_pixel(std::size_t x, std::size_t y) const
+{
+ return get_pixel_from_row(get_row_ptr(y), x, format_);
+}
+
+void Image::set_pixel(std::size_t x, std::size_t y, const Pixel& pixel)
+{
+ set_pixel_to_row(get_row_ptr(y), x, pixel, format_);
+}
+
+RawPixel Image::get_raw_pixel(std::size_t x, std::size_t y) const
+{
+ return get_raw_pixel_from_row(get_row_ptr(y), x, format_);
+}
+
+std::uint16_t Image::get_raw_channel(std::size_t x, std::size_t y, unsigned channel) const
+{
+ return get_raw_channel_from_row(get_row_ptr(y), x, channel, format_);
+}
+
+void Image::set_raw_pixel(std::size_t x, std::size_t y, const RawPixel& pixel)
+{
+ set_raw_pixel_to_row(get_row_ptr(y), x, pixel, format_);
+}
+
+void Image::resize(std::size_t width, std::size_t height, PixelFormat format)
+{
+ width_ = width;
+ height_ = height;
+ format_ = format;
+ row_bytes_ = get_pixel_row_bytes(format_, width_);
+ data_.resize(get_row_bytes() * height);
+}
+
+template<PixelFormat SrcFormat, PixelFormat DstFormat>
+void convert_pixel_row_impl2(const std::uint8_t* in_data, std::uint8_t* out_data,
+ std::size_t count)
+{
+ for (std::size_t i = 0; i < count; ++i) {
+ Pixel pixel = get_pixel_from_row(in_data, i, SrcFormat);
+ set_pixel_to_row(out_data, i, pixel, DstFormat);
+ }
+}
+
+template<PixelFormat SrcFormat>
+void convert_pixel_row_impl(const std::uint8_t* in_data, std::uint8_t* out_data,
+ PixelFormat out_format, std::size_t count)
+{
+ switch (out_format) {
+ case PixelFormat::I1: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::I1>(in_data, out_data, count);
+ return;
+ }
+ case PixelFormat::RGB111: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB111>(in_data, out_data, count);
+ return;
+ }
+ case PixelFormat::I8: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::I8>(in_data, out_data, count);
+ return;
+ }
+ case PixelFormat::RGB888: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB888>(in_data, out_data, count);
+ return;
+ }
+ case PixelFormat::BGR888: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::BGR888>(in_data, out_data, count);
+ return;
+ }
+ case PixelFormat::I16: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::I16>(in_data, out_data, count);
+ return;
+ }
+ case PixelFormat::RGB161616: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB161616>(in_data, out_data, count);
+ return;
+ }
+ case PixelFormat::BGR161616: {
+ convert_pixel_row_impl2<SrcFormat, PixelFormat::BGR161616>(in_data, out_data, count);
+ return;
+ }
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(out_format));
+ }
+}
+void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format,
+ std::uint8_t* out_data, PixelFormat out_format, std::size_t count)
+{
+ if (in_format == out_format) {
+ std::memcpy(out_data, in_data, get_pixel_row_bytes(in_format, count));
+ return;
+ }
+
+ switch (in_format) {
+ case PixelFormat::I1: {
+ convert_pixel_row_impl<PixelFormat::I1>(in_data, out_data, out_format, count);
+ return;
+ }
+ case PixelFormat::RGB111: {
+ convert_pixel_row_impl<PixelFormat::RGB111>(in_data, out_data, out_format, count);
+ return;
+ }
+ case PixelFormat::I8: {
+ convert_pixel_row_impl<PixelFormat::I8>(in_data, out_data, out_format, count);
+ return;
+ }
+ case PixelFormat::RGB888: {
+ convert_pixel_row_impl<PixelFormat::RGB888>(in_data, out_data, out_format, count);
+ return;
+ }
+ case PixelFormat::BGR888: {
+ convert_pixel_row_impl<PixelFormat::BGR888>(in_data, out_data, out_format, count);
+ return;
+ }
+ case PixelFormat::I16: {
+ convert_pixel_row_impl<PixelFormat::I16>(in_data, out_data, out_format, count);
+ return;
+ }
+ case PixelFormat::RGB161616: {
+ convert_pixel_row_impl<PixelFormat::RGB161616>(in_data, out_data, out_format, count);
+ return;
+ }
+ case PixelFormat::BGR161616: {
+ convert_pixel_row_impl<PixelFormat::BGR161616>(in_data, out_data, out_format, count);
+ return;
+ }
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(in_format));
+ }
+}
+
+} // namespace genesys
diff --git a/backend/genesys/image.h b/backend/genesys/image.h
new file mode 100644
index 0000000..c96b1bb
--- /dev/null
+++ b/backend/genesys/image.h
@@ -0,0 +1,87 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_IMAGE_H
+#define BACKEND_GENESYS_IMAGE_H
+
+#include "image_pixel.h"
+#include <vector>
+
+namespace genesys {
+
+class Image
+{
+public:
+ Image();
+ Image(std::size_t width, std::size_t height, PixelFormat format);
+
+ std::size_t get_width() const { return width_; }
+ std::size_t get_height() const { return height_; }
+ PixelFormat get_format() const { return format_; }
+ std::size_t get_row_bytes() const { return row_bytes_; }
+
+ std::uint8_t* get_row_ptr(std::size_t y);
+ const std::uint8_t* get_row_ptr(std::size_t y) const;
+
+ Pixel get_pixel(std::size_t x, std::size_t y) const;
+ void set_pixel(std::size_t x, std::size_t y, const Pixel& pixel);
+
+ RawPixel get_raw_pixel(std::size_t x, std::size_t y) const;
+ std::uint16_t get_raw_channel(std::size_t x, std::size_t y, unsigned channel) const;
+ void set_raw_pixel(std::size_t x, std::size_t y, const RawPixel& pixel);
+
+ void resize(std::size_t width, std::size_t height, PixelFormat format);
+private:
+ std::size_t width_ = 0;
+ std::size_t height_ = 0;
+ PixelFormat format_ = PixelFormat::UNKNOWN;
+ std::size_t row_bytes_ = 0;
+ std::vector<std::uint8_t> data_;
+};
+
+void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format,
+ std::uint8_t* out_data, PixelFormat out_format, std::size_t count);
+
+} // namespace genesys
+
+#endif // ifndef BACKEND_GENESYS_IMAGE_H
diff --git a/backend/genesys/image_buffer.cpp b/backend/genesys/image_buffer.cpp
new file mode 100644
index 0000000..07c6987
--- /dev/null
+++ b/backend/genesys/image_buffer.cpp
@@ -0,0 +1,203 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "image_buffer.h"
+#include "image.h"
+
+namespace genesys {
+
+ImageBuffer::ImageBuffer(std::size_t size, ProducerCallback producer) :
+ producer_{producer},
+ size_{size},
+ buffer_offset_{size}
+{
+ buffer_.resize(size_);
+}
+
+bool ImageBuffer::get_data(std::size_t size, std::uint8_t* out_data)
+{
+ const std::uint8_t* out_data_end = out_data + size;
+
+ auto copy_buffer = [&]()
+ {
+ std::size_t bytes_copy = std::min<std::size_t>(out_data_end - out_data, available());
+ std::memcpy(out_data, buffer_.data() + buffer_offset_, bytes_copy);
+ out_data += bytes_copy;
+ buffer_offset_ += bytes_copy;
+ };
+
+ // first, read remaining data from buffer
+ if (available() > 0) {
+ copy_buffer();
+ }
+
+ if (out_data == out_data_end) {
+ return true;
+ }
+
+ // now the buffer is empty and there's more data to be read
+ bool got_data = true;
+ do {
+ buffer_offset_ = 0;
+ got_data &= producer_(size_, buffer_.data());
+
+ copy_buffer();
+ } while(out_data < out_data_end && got_data);
+
+ return got_data;
+}
+
+void FakeBufferModel::push_step(std::size_t buffer_size, std::size_t row_bytes)
+{
+ sizes_.push_back(buffer_size);
+ available_sizes_.push_back(0);
+ row_bytes_.push_back(row_bytes);
+}
+
+std::size_t FakeBufferModel::available_space() const
+{
+ if (sizes_.empty())
+ throw SaneException("Model has not been setup");
+ return sizes_.front() - available_sizes_.front();
+}
+
+void FakeBufferModel::simulate_read(std::size_t size)
+{
+ if (sizes_.empty()) {
+ throw SaneException("Model has not been setup");
+ }
+ if (available_space() < size) {
+ throw SaneException("Attempted to simulate read of too much memory");
+ }
+
+ available_sizes_.front() += size;
+
+ for (unsigned i = 1; i < sizes_.size(); ++i) {
+ auto avail_src = available_sizes_[i - 1];
+ auto avail_dst = sizes_[i] - available_sizes_[i];
+
+ auto avail = (std::min(avail_src, avail_dst) / row_bytes_[i]) * row_bytes_[i];
+ available_sizes_[i - 1] -= avail;
+ available_sizes_[i] += avail;
+ }
+ available_sizes_.back() = 0;
+}
+
+ImageBufferGenesysUsb::ImageBufferGenesysUsb(std::size_t total_size,
+ const FakeBufferModel& buffer_model,
+ ProducerCallback producer) :
+ remaining_size_{total_size},
+ buffer_model_{buffer_model},
+ producer_{producer}
+{}
+
+bool ImageBufferGenesysUsb::get_data(std::size_t size, std::uint8_t* out_data)
+{
+ const std::uint8_t* out_data_end = out_data + size;
+
+ auto copy_buffer = [&]()
+ {
+ std::size_t bytes_copy = std::min<std::size_t>(out_data_end - out_data, available());
+ std::memcpy(out_data, buffer_.data() + buffer_offset_, bytes_copy);
+ out_data += bytes_copy;
+ buffer_offset_ += bytes_copy;
+ };
+
+ // first, read remaining data from buffer
+ if (available() > 0) {
+ copy_buffer();
+ }
+
+ if (out_data == out_data_end) {
+ return true;
+ }
+
+ // now the buffer is empty and there's more data to be read
+ do {
+ if (remaining_size_ == 0)
+ return false;
+
+ auto bytes_to_read = get_read_size();
+ buffer_offset_ = 0;
+ buffer_end_ = bytes_to_read;
+ buffer_.resize(bytes_to_read);
+
+ producer_(bytes_to_read, buffer_.data());
+
+ if (remaining_size_ < bytes_to_read) {
+ remaining_size_ = 0;
+ } else {
+ remaining_size_ -= bytes_to_read;
+ }
+
+ copy_buffer();
+ } while(out_data < out_data_end);
+ return true;
+}
+
+std::size_t ImageBufferGenesysUsb::get_read_size()
+{
+ std::size_t size = buffer_model_.available_space();
+
+ // never read an odd number. exception: last read
+ // the chip internal counter does not count half words.
+ size &= ~1;
+
+ // Some setups need the reads to be multiples of 256 bytes
+ size &= ~0xff;
+
+ if (remaining_size_ < size) {
+ size = remaining_size_;
+ /*round up to a multiple of 256 bytes */
+ size += (size & 0xff) ? 0x100 : 0x00;
+ size &= ~0xff;
+ }
+
+ buffer_model_.simulate_read(size);
+
+ return size;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/image_buffer.h b/backend/genesys/image_buffer.h
new file mode 100644
index 0000000..43c3eb7
--- /dev/null
+++ b/backend/genesys/image_buffer.h
@@ -0,0 +1,129 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_IMAGE_BUFFER_H
+#define BACKEND_GENESYS_IMAGE_BUFFER_H
+
+#include "enums.h"
+#include "row_buffer.h"
+#include <algorithm>
+#include <functional>
+
+namespace genesys {
+
+// This class allows reading from row-based source in smaller or larger chunks of data
+class ImageBuffer
+{
+public:
+ using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>;
+
+ ImageBuffer() {}
+ ImageBuffer(std::size_t size, ProducerCallback producer);
+
+ std::size_t size() const { return size_; }
+ std::size_t available() const { return size_ - buffer_offset_; }
+
+ bool get_data(std::size_t size, std::uint8_t* out_data);
+
+private:
+ ProducerCallback producer_;
+ std::size_t size_ = 0;
+
+ std::size_t buffer_offset_ = 0;
+ std::vector<std::uint8_t> buffer_;
+};
+
+class FakeBufferModel
+{
+public:
+ FakeBufferModel() {}
+
+ void push_step(std::size_t buffer_size, std::size_t row_bytes);
+
+ std::size_t available_space() const;
+
+ void simulate_read(std::size_t size);
+
+private:
+ std::vector<std::size_t> sizes_;
+ std::vector<std::size_t> available_sizes_;
+ std::vector<std::size_t> row_bytes_;
+};
+
+// This class is similar to ImageBuffer, but preserves historical peculiarities of buffer handling
+// in the backend to preserve exact behavior
+class ImageBufferGenesysUsb
+{
+public:
+ using ProducerCallback = std::function<void(std::size_t size, std::uint8_t* out_data)>;
+
+ ImageBufferGenesysUsb() {}
+ ImageBufferGenesysUsb(std::size_t total_size, const FakeBufferModel& buffer_model,
+ ProducerCallback producer);
+
+ std::size_t remaining_size() const { return remaining_size_; }
+
+ void set_remaining_size(std::size_t bytes) { remaining_size_ = bytes; }
+
+ std::size_t available() const { return buffer_end_ - buffer_offset_; }
+
+ bool get_data(std::size_t size, std::uint8_t* out_data);
+
+private:
+
+ std::size_t get_read_size();
+
+ std::size_t remaining_size_ = 0;
+
+ std::size_t buffer_offset_ = 0;
+ std::size_t buffer_end_ = 0;
+ std::vector<std::uint8_t> buffer_;
+
+ FakeBufferModel buffer_model_;
+
+ ProducerCallback producer_;
+};
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_IMAGE_BUFFER_H
diff --git a/backend/genesys/image_pipeline.cpp b/backend/genesys/image_pipeline.cpp
new file mode 100644
index 0000000..c01b7f4
--- /dev/null
+++ b/backend/genesys/image_pipeline.cpp
@@ -0,0 +1,839 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "image_pipeline.h"
+#include "image.h"
+#include "low.h"
+#include <cmath>
+#include <numeric>
+
+namespace genesys {
+
+ImagePipelineNode::~ImagePipelineNode() {}
+
+std::size_t ImagePipelineNodeBytesSource::consume_remaining_bytes(std::size_t bytes)
+{
+ if (bytes > remaining_bytes_) {
+ bytes = remaining_bytes_;
+ }
+ remaining_bytes_ -= bytes;
+ return bytes;
+}
+
+bool ImagePipelineNodeCallableSource::get_next_row_data(std::uint8_t* out_data)
+{
+ bool got_data = producer_(get_row_bytes(), out_data);
+ if (!got_data)
+ eof_ = true;
+ return got_data;
+}
+
+ImagePipelineNodeBufferedCallableSource::ImagePipelineNodeBufferedCallableSource(
+ std::size_t width, std::size_t height, PixelFormat format, std::size_t input_batch_size,
+ ProducerCallback producer) :
+ width_{width},
+ height_{height},
+ format_{format},
+ buffer_{input_batch_size, producer}
+{
+ set_remaining_bytes(height_ * get_row_bytes());
+}
+
+bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* out_data)
+{
+ if (curr_row_ >= get_height()) {
+ DBG(DBG_warn, "%s: reading out of bounds. Row %zu, height: %zu\n", __func__,
+ curr_row_, get_height());
+ eof_ = true;
+ return false;
+ }
+
+ bool got_data = true;
+
+ auto row_bytes = get_row_bytes();
+ auto bytes_to_ask = consume_remaining_bytes(row_bytes);
+ if (bytes_to_ask < row_bytes) {
+ got_data = false;
+ }
+
+ got_data &= buffer_.get_data(bytes_to_ask, out_data);
+ curr_row_++;
+ if (!got_data) {
+ eof_ = true;
+ }
+ return got_data;
+}
+
+
+ImagePipelineNodeBufferedGenesysUsb::ImagePipelineNodeBufferedGenesysUsb(
+ std::size_t width, std::size_t height, PixelFormat format, std::size_t total_size,
+ const FakeBufferModel& buffer_model, ProducerCallback producer) :
+ width_{width},
+ height_{height},
+ format_{format},
+ buffer_{total_size, buffer_model, producer}
+{
+ set_remaining_bytes(total_size);
+}
+
+bool ImagePipelineNodeBufferedGenesysUsb::get_next_row_data(std::uint8_t* out_data)
+{
+ if (remaining_bytes() != buffer_.remaining_size() + buffer_.available()) {
+ buffer_.set_remaining_size(remaining_bytes() - buffer_.available());
+ }
+ bool got_data = true;
+
+ std::size_t row_bytes = get_row_bytes();
+ std::size_t ask_bytes = consume_remaining_bytes(row_bytes);
+ if (ask_bytes < row_bytes) {
+ got_data = false;
+ }
+ got_data &= buffer_.get_data(ask_bytes, out_data);
+ if (!got_data) {
+ eof_ = true;
+ }
+ return got_data;
+}
+
+ImagePipelineNodeArraySource::ImagePipelineNodeArraySource(std::size_t width, std::size_t height,
+ PixelFormat format,
+ std::vector<std::uint8_t> data) :
+ width_{width},
+ height_{height},
+ format_{format},
+ data_{std::move(data)},
+ next_row_{0}
+{
+ auto size = get_row_bytes() * height_;
+ if (data_.size() < size) {
+ throw SaneException("The given array is too small (%zu bytes). Need at least %zu",
+ data_.size(), size);
+ }
+ set_remaining_bytes(size);
+}
+
+bool ImagePipelineNodeArraySource::get_next_row_data(std::uint8_t* out_data)
+{
+ if (next_row_ >= height_) {
+ eof_ = true;
+ return false;
+ }
+
+ bool got_data = true;
+
+ auto row_bytes = get_row_bytes();
+ auto bytes_to_ask = consume_remaining_bytes(row_bytes);
+ if (bytes_to_ask < row_bytes) {
+ got_data = false;
+ }
+
+ std::memcpy(out_data, data_.data() + get_row_bytes() * next_row_, bytes_to_ask);
+ next_row_++;
+
+ if (!got_data) {
+ eof_ = true;
+ }
+ return got_data;
+}
+
+
+ImagePipelineNodeImageSource::ImagePipelineNodeImageSource(const Image& source) :
+ source_{source}
+{}
+
+bool ImagePipelineNodeImageSource::get_next_row_data(std::uint8_t* out_data)
+{
+ if (next_row_ >= get_height()) {
+ return false;
+ }
+ std::memcpy(out_data, source_.get_row_ptr(next_row_), get_row_bytes());
+ next_row_++;
+ return true;
+}
+
+bool ImagePipelineNodeFormatConvert::get_next_row_data(std::uint8_t* out_data)
+{
+ auto src_format = source_.get_format();
+ if (src_format == dst_format_) {
+ return source_.get_next_row_data(out_data);
+ }
+
+ buffer_.clear();
+ buffer_.resize(source_.get_row_bytes());
+ bool got_data = source_.get_next_row_data(buffer_.data());
+
+ convert_pixel_row_format(buffer_.data(), src_format, out_data, dst_format_, get_width());
+ return got_data;
+}
+
+ImagePipelineNodeDesegment::ImagePipelineNodeDesegment(ImagePipelineNode& source,
+ std::size_t output_width,
+ const std::vector<unsigned>& segment_order,
+ std::size_t segment_pixels,
+ std::size_t interleaved_lines,
+ std::size_t pixels_per_chunk) :
+ source_(source),
+ output_width_{output_width},
+ segment_order_{segment_order},
+ segment_pixels_{segment_pixels},
+ interleaved_lines_{interleaved_lines},
+ pixels_per_chunk_{pixels_per_chunk},
+ buffer_{source_.get_row_bytes()}
+{
+ DBG_HELPER_ARGS(dbg, "segment_count=%zu, segment_size=%zu, interleaved_lines=%zu, "
+ "pixels_per_shunk=%zu", segment_order.size(), segment_pixels,
+ interleaved_lines, pixels_per_chunk);
+
+ if (source_.get_height() % interleaved_lines_ > 0) {
+ throw SaneException("Height is not a multiple of the number of lines to interelave %zu/%zu",
+ source_.get_height(), interleaved_lines_);
+ }
+}
+
+ImagePipelineNodeDesegment::ImagePipelineNodeDesegment(ImagePipelineNode& source,
+ std::size_t output_width,
+ std::size_t segment_count,
+ std::size_t segment_pixels,
+ std::size_t interleaved_lines,
+ std::size_t pixels_per_chunk) :
+ source_(source),
+ output_width_{output_width},
+ segment_pixels_{segment_pixels},
+ interleaved_lines_{interleaved_lines},
+ pixels_per_chunk_{pixels_per_chunk},
+ buffer_{source_.get_row_bytes()}
+{
+ DBG_HELPER_ARGS(dbg, "segment_count=%zu, segment_size=%zu, interleaved_lines=%zu, "
+ "pixels_per_shunk=%zu", segment_count, segment_pixels, interleaved_lines,
+ pixels_per_chunk);
+
+ segment_order_.resize(segment_count);
+ std::iota(segment_order_.begin(), segment_order_.end(), 0);
+}
+
+bool ImagePipelineNodeDesegment::get_next_row_data(uint8_t* out_data)
+{
+ bool got_data = true;
+
+ buffer_.clear();
+ for (std::size_t i = 0; i < interleaved_lines_; ++i) {
+ buffer_.push_back();
+ got_data &= source_.get_next_row_data(buffer_.get_row_ptr(i));
+ }
+ if (!buffer_.is_linear()) {
+ throw SaneException("Buffer is not linear");
+ }
+
+ auto format = get_format();
+ auto segment_count = segment_order_.size();
+
+ const std::uint8_t* in_data = buffer_.get_row_ptr(0);
+
+ std::size_t groups_count = output_width_ / (segment_order_.size() * pixels_per_chunk_);
+
+ for (std::size_t igroup = 0; igroup < groups_count; ++igroup) {
+ for (std::size_t isegment = 0; isegment < segment_count; ++isegment) {
+ auto input_offset = igroup * pixels_per_chunk_;
+ input_offset += segment_pixels_ * segment_order_[isegment];
+ auto output_offset = (igroup * segment_count + isegment) * pixels_per_chunk_;
+
+ for (std::size_t ipixel = 0; ipixel < pixels_per_chunk_; ++ipixel) {
+ auto pixel = get_raw_pixel_from_row(in_data, input_offset + ipixel, format);
+ set_raw_pixel_to_row(out_data, output_offset + ipixel, pixel, format);
+ }
+ }
+ }
+ return got_data;
+}
+
+ImagePipelineNodeDeinterleaveLines::ImagePipelineNodeDeinterleaveLines(
+ ImagePipelineNode& source, std::size_t interleaved_lines, std::size_t pixels_per_chunk) :
+ ImagePipelineNodeDesegment(source, source.get_width() * interleaved_lines,
+ interleaved_lines, source.get_width(),
+ interleaved_lines, pixels_per_chunk)
+{}
+
+ImagePipelineNodeSwap16BitEndian::ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source) :
+ source_(source),
+ needs_swapping_{false}
+{
+ if (get_pixel_format_depth(source_.get_format()) == 16) {
+ needs_swapping_ = true;
+ } else {
+ DBG(DBG_info, "%s: this pipeline node does nothing for non 16-bit formats", __func__);
+ }
+}
+
+bool ImagePipelineNodeSwap16BitEndian::get_next_row_data(std::uint8_t* out_data)
+{
+ bool got_data = source_.get_next_row_data(out_data);
+ if (needs_swapping_) {
+ std::size_t pixels = get_row_bytes() / 2;
+ for (std::size_t i = 0; i < pixels; ++i) {
+ std::swap(*out_data, *(out_data + 1));
+ out_data += 2;
+ }
+ }
+ return got_data;
+}
+
+ImagePipelineNodeMergeMonoLines::ImagePipelineNodeMergeMonoLines(ImagePipelineNode& source,
+ ColorOrder color_order) :
+ source_(source),
+ buffer_(source_.get_row_bytes())
+{
+ DBG_HELPER_ARGS(dbg, "color_order %d", static_cast<unsigned>(color_order));
+
+ output_format_ = get_output_format(source_.get_format(), color_order);
+}
+
+bool ImagePipelineNodeMergeMonoLines::get_next_row_data(std::uint8_t* out_data)
+{
+ bool got_data = true;
+
+ buffer_.clear();
+ for (unsigned i = 0; i < 3; ++i) {
+ buffer_.push_back();
+ got_data &= source_.get_next_row_data(buffer_.get_row_ptr(i));
+ }
+
+ const auto* row0 = buffer_.get_row_ptr(0);
+ const auto* row1 = buffer_.get_row_ptr(1);
+ const auto* row2 = buffer_.get_row_ptr(2);
+
+ auto format = source_.get_format();
+
+ for (std::size_t x = 0, width = get_width(); x < width; ++x) {
+ std::uint16_t ch0 = get_raw_channel_from_row(row0, x, 0, format);
+ std::uint16_t ch1 = get_raw_channel_from_row(row1, x, 0, format);
+ std::uint16_t ch2 = get_raw_channel_from_row(row2, x, 0, format);
+ set_raw_channel_to_row(out_data, x, 0, ch0, output_format_);
+ set_raw_channel_to_row(out_data, x, 1, ch1, output_format_);
+ set_raw_channel_to_row(out_data, x, 2, ch2, output_format_);
+ }
+ return got_data;
+}
+
+PixelFormat ImagePipelineNodeMergeMonoLines::get_output_format(PixelFormat input_format,
+ ColorOrder order)
+{
+ switch (input_format) {
+ case PixelFormat::I1: {
+ if (order == ColorOrder::RGB) {
+ return PixelFormat::RGB111;
+ }
+ break;
+ }
+ case PixelFormat::I8: {
+ if (order == ColorOrder::RGB) {
+ return PixelFormat::RGB888;
+ }
+ if (order == ColorOrder::BGR) {
+ return PixelFormat::BGR888;
+ }
+ break;
+ }
+ case PixelFormat::I16: {
+ if (order == ColorOrder::RGB) {
+ return PixelFormat::RGB161616;
+ }
+ if (order == ColorOrder::BGR) {
+ return PixelFormat::BGR161616;
+ }
+ break;
+ }
+ default: break;
+ }
+ throw SaneException("Unsupported format combidation %d %d",
+ static_cast<unsigned>(input_format),
+ static_cast<unsigned>(order));
+}
+
+ImagePipelineNodeSplitMonoLines::ImagePipelineNodeSplitMonoLines(ImagePipelineNode& source) :
+ source_(source),
+ next_channel_{0}
+{
+ output_format_ = get_output_format(source_.get_format());
+}
+
+bool ImagePipelineNodeSplitMonoLines::get_next_row_data(std::uint8_t* out_data)
+{
+ bool got_data = true;
+
+ if (next_channel_ == 0) {
+ buffer_.resize(source_.get_row_bytes());
+ got_data &= source_.get_next_row_data(buffer_.data());
+ }
+
+ const auto* row = buffer_.data();
+ auto format = source_.get_format();
+
+ for (std::size_t x = 0, width = get_width(); x < width; ++x) {
+ std::uint16_t ch = get_raw_channel_from_row(row, x, next_channel_, format);
+ set_raw_channel_to_row(out_data, x, 0, ch, output_format_);
+ }
+ next_channel_ = (next_channel_ + 1) % 3;
+
+ return got_data;
+}
+
+PixelFormat ImagePipelineNodeSplitMonoLines::get_output_format(PixelFormat input_format)
+{
+ switch (input_format) {
+ case PixelFormat::RGB111: return PixelFormat::I1;
+ case PixelFormat::RGB888:
+ case PixelFormat::BGR888: return PixelFormat::I8;
+ case PixelFormat::RGB161616:
+ case PixelFormat::BGR161616: return PixelFormat::I16;
+ default: break;
+ }
+ throw SaneException("Unsupported input format %d", static_cast<unsigned>(input_format));
+}
+
+ImagePipelineNodeComponentShiftLines::ImagePipelineNodeComponentShiftLines(
+ ImagePipelineNode& source, unsigned shift_r, unsigned shift_g, unsigned shift_b) :
+ source_(source),
+ buffer_{source.get_row_bytes()}
+{
+ DBG_HELPER_ARGS(dbg, "shifts={%d, %d, %d}", shift_r, shift_g, shift_b);
+
+ switch (source.get_format()) {
+ case PixelFormat::RGB111:
+ case PixelFormat::RGB888:
+ case PixelFormat::RGB161616: {
+ channel_shifts_ = { shift_r, shift_g, shift_b };
+ break;
+ }
+ case PixelFormat::BGR888:
+ case PixelFormat::BGR161616: {
+ channel_shifts_ = { shift_b, shift_g, shift_r };
+ break;
+ }
+ default:
+ throw SaneException("Unsupported input format %d",
+ static_cast<unsigned>(source.get_format()));
+ }
+ extra_height_ = *std::max_element(channel_shifts_.begin(), channel_shifts_.end());
+}
+
+bool ImagePipelineNodeComponentShiftLines::get_next_row_data(std::uint8_t* out_data)
+{
+ bool got_data = true;
+
+ if (!buffer_.empty()) {
+ buffer_.pop_front();
+ }
+ while (buffer_.height() < extra_height_ + 1) {
+ buffer_.push_back();
+ got_data &= source_.get_next_row_data(buffer_.get_back_row_ptr());
+ }
+
+ auto format = get_format();
+ const auto* row0 = buffer_.get_row_ptr(channel_shifts_[0]);
+ const auto* row1 = buffer_.get_row_ptr(channel_shifts_[1]);
+ const auto* row2 = buffer_.get_row_ptr(channel_shifts_[2]);
+
+ for (std::size_t x = 0, width = get_width(); x < width; ++x) {
+ std::uint16_t ch0 = get_raw_channel_from_row(row0, x, 0, format);
+ std::uint16_t ch1 = get_raw_channel_from_row(row1, x, 1, format);
+ std::uint16_t ch2 = get_raw_channel_from_row(row2, x, 2, format);
+ set_raw_channel_to_row(out_data, x, 0, ch0, format);
+ set_raw_channel_to_row(out_data, x, 1, ch1, format);
+ set_raw_channel_to_row(out_data, x, 2, ch2, format);
+ }
+ return got_data;
+}
+
+ImagePipelineNodePixelShiftLines::ImagePipelineNodePixelShiftLines(
+ ImagePipelineNode& source, const std::vector<std::size_t>& shifts) :
+ source_(source),
+ pixel_shifts_{shifts},
+ buffer_{get_row_bytes()}
+{
+ DBG_HELPER(dbg);
+ DBG(DBG_proc, "%s: shifts={", __func__);
+ for (auto el : pixel_shifts_) {
+ DBG(DBG_proc, " %zu", el);
+ }
+ DBG(DBG_proc, " }\n");
+
+ if (pixel_shifts_.size() > MAX_SHIFTS) {
+ throw SaneException("Unsupported number of shift configurations %zu", pixel_shifts_.size());
+ }
+
+ extra_height_ = *std::max_element(pixel_shifts_.begin(), pixel_shifts_.end());
+}
+
+bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data)
+{
+ bool got_data = true;
+
+ if (!buffer_.empty()) {
+ buffer_.pop_front();
+ }
+ while (buffer_.height() < extra_height_ + 1) {
+ buffer_.push_back();
+ got_data &= source_.get_next_row_data(buffer_.get_back_row_ptr());
+ }
+
+ auto format = get_format();
+ auto shift_count = pixel_shifts_.size();
+
+ std::array<std::uint8_t*, MAX_SHIFTS> rows;
+
+ for (std::size_t irow = 0; irow < shift_count; ++irow) {
+ rows[irow] = buffer_.get_row_ptr(pixel_shifts_[irow]);
+ }
+
+ for (std::size_t x = 0, width = get_width(); x < width;) {
+ for (std::size_t irow = 0; irow < shift_count && x < width; irow++, x++) {
+ RawPixel pixel = get_raw_pixel_from_row(rows[irow], x, format);
+ set_raw_pixel_to_row(out_data, x, pixel, format);
+ }
+ }
+ return got_data;
+}
+
+ImagePipelineNodeExtract::ImagePipelineNodeExtract(ImagePipelineNode& source,
+ std::size_t offset_x, std::size_t offset_y,
+ std::size_t width, std::size_t height) :
+ source_(source),
+ offset_x_{offset_x},
+ offset_y_{offset_y},
+ width_{width},
+ height_{height}
+{
+ cached_line_.resize(source_.get_row_bytes());
+}
+
+ImagePipelineNodeExtract::~ImagePipelineNodeExtract() {}
+
+ImagePipelineNodeScaleRows::ImagePipelineNodeScaleRows(ImagePipelineNode& source,
+ std::size_t width) :
+ source_(source),
+ width_{width}
+{
+ cached_line_.resize(source_.get_row_bytes());
+}
+
+bool ImagePipelineNodeScaleRows::get_next_row_data(std::uint8_t* out_data)
+{
+ auto src_width = source_.get_width();
+ auto dst_width = width_;
+
+ bool got_data = source_.get_next_row_data(cached_line_.data());
+
+ const auto* src_data = cached_line_.data();
+ auto format = get_format();
+ auto channels = get_pixel_channels(format);
+
+ if (src_width > dst_width) {
+ // average
+ std::uint32_t counter = src_width / 2;
+ unsigned src_x = 0;
+ for (unsigned dst_x = 0; dst_x < dst_width; dst_x++) {
+ unsigned avg[3] = {0, 0, 0};
+ unsigned count = 0;
+ while (counter < src_width && src_x < src_width) {
+ counter += dst_width;
+
+ for (unsigned c = 0; c < channels; c++) {
+ avg[c] += get_raw_channel_from_row(src_data, src_x, c, format);
+ }
+
+ src_x++;
+ count++;
+ }
+ counter -= src_width;
+
+ for (unsigned c = 0; c < channels; c++) {
+ set_raw_channel_to_row(out_data, dst_x, c, avg[c] / count, format);
+ }
+ }
+ } else {
+ // interpolate and copy pixels
+ std::uint32_t counter = dst_width / 2;
+ unsigned dst_x = 0;
+
+ for (unsigned src_x = 0; src_x < src_width; src_x++) {
+ unsigned avg[3] = {0, 0, 0};
+ for (unsigned c = 0; c < channels; c++) {
+ avg[c] += get_raw_channel_from_row(src_data, src_x, c, format);
+ }
+ while ((counter < dst_width || src_x + 1 == src_width) && dst_x < dst_width) {
+ counter += src_width;
+
+ for (unsigned c = 0; c < channels; c++) {
+ set_raw_channel_to_row(out_data, dst_x, c, avg[c], format);
+ }
+ dst_x++;
+ }
+ counter -= dst_width;
+ }
+ }
+ return got_data;
+}
+
+bool ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data)
+{
+ bool got_data = true;
+
+ while (current_line_ < offset_y_) {
+ got_data &= source_.get_next_row_data(cached_line_.data());
+ current_line_++;
+ }
+ if (current_line_ >= offset_y_ + source_.get_height()) {
+ std::fill(out_data, out_data + get_row_bytes(), 0);
+ current_line_++;
+ return got_data;
+ }
+ // now we're sure that the following holds:
+ // offset_y_ <= current_line_ < offset_y_ + source_.get_height())
+ got_data &= source_.get_next_row_data(cached_line_.data());
+
+ auto format = get_format();
+ auto x_src_width = source_.get_width() > offset_x_ ? source_.get_width() - offset_x_ : 0;
+ x_src_width = std::min(x_src_width, width_);
+ auto x_pad_after = width_ > x_src_width ? width_ - x_src_width : 0;
+
+ if (get_pixel_format_depth(format) < 8) {
+ // we need to copy pixels one-by-one as there's no per-bit addressing
+ for (std::size_t i = 0; i < x_src_width; ++i) {
+ auto pixel = get_raw_pixel_from_row(cached_line_.data(), i + offset_x_, format);
+ set_raw_pixel_to_row(out_data, i, pixel, format);
+ }
+ for (std::size_t i = 0; i < x_pad_after; ++i) {
+ set_raw_pixel_to_row(out_data, i + x_src_width, RawPixel{}, format);
+ }
+ } else {
+ std::size_t bpp = get_pixel_format_depth(format) / 8;
+ if (x_src_width > 0) {
+ std::memcpy(out_data, cached_line_.data() + offset_x_ * bpp,
+ x_src_width * bpp);
+ }
+ if (x_pad_after > 0) {
+ std::fill(out_data + x_src_width * bpp,
+ out_data + (x_src_width + x_pad_after) * bpp, 0);
+ }
+ }
+
+ current_line_++;
+
+ return got_data;
+}
+
+ImagePipelineNodeCalibrate::ImagePipelineNodeCalibrate(ImagePipelineNode& source,
+ const std::vector<std::uint16_t>& bottom,
+ const std::vector<std::uint16_t>& top) :
+ source_{source}
+{
+ auto size = std::min(bottom.size(), top.size());
+ offset_.reserve(size);
+ multiplier_.reserve(size);
+
+ for (std::size_t i = 0; i < size; ++i) {
+ offset_.push_back(bottom[i] / 65535.0f);
+ multiplier_.push_back(65535.0f / (top[i] - bottom[i]));
+ }
+}
+
+bool ImagePipelineNodeCalibrate::get_next_row_data(std::uint8_t* out_data)
+{
+ bool ret = source_.get_next_row_data(out_data);
+
+ auto format = get_format();
+ auto depth = get_pixel_format_depth(format);
+ std::size_t max_value = 1;
+ switch (depth) {
+ case 8: max_value = 255; break;
+ case 16: max_value = 65535; break;
+ default:
+ throw SaneException("Unsupported depth for calibration %d", depth);
+ }
+ unsigned channels = get_pixel_channels(format);
+
+ std::size_t max_calib_i = offset_.size();
+ std::size_t curr_calib_i = 0;
+
+ for (std::size_t x = 0, width = get_width(); x < width && curr_calib_i < max_calib_i; ++x) {
+ for (unsigned ch = 0; ch < channels && curr_calib_i < max_calib_i; ++ch) {
+ std::int32_t value = get_raw_channel_from_row(out_data, x, ch, format);
+
+ float value_f = static_cast<float>(value) / max_value;
+ value_f = (value_f - offset_[curr_calib_i]) * multiplier_[curr_calib_i];
+ value_f = std::round(value_f * max_value);
+ value = clamp<std::int32_t>(static_cast<std::int32_t>(value_f), 0, max_value);
+ set_raw_channel_to_row(out_data, x, ch, value, format);
+
+ curr_calib_i++;
+ }
+ }
+ return ret;
+}
+
+ImagePipelineNodeDebug::ImagePipelineNodeDebug(ImagePipelineNode& source,
+ const std::string& path) :
+ source_(source),
+ path_{path},
+ buffer_{source_.get_row_bytes()}
+{}
+
+ImagePipelineNodeDebug::~ImagePipelineNodeDebug()
+{
+ catch_all_exceptions(__func__, [&]()
+ {
+ if (buffer_.empty())
+ return;
+
+ auto format = get_format();
+ buffer_.linearize();
+ sanei_genesys_write_pnm_file(path_.c_str(), buffer_.get_front_row_ptr(),
+ get_pixel_format_depth(format),
+ get_pixel_channels(format),
+ get_width(), buffer_.height());
+ });
+}
+
+bool ImagePipelineNodeDebug::get_next_row_data(std::uint8_t* out_data)
+{
+ buffer_.push_back();
+ bool got_data = source_.get_next_row_data(out_data);
+ std::memcpy(buffer_.get_back_row_ptr(), out_data, get_row_bytes());
+ return got_data;
+}
+
+std::size_t ImagePipelineStack::get_input_width() const
+{
+ ensure_node_exists();
+ return nodes_.front()->get_width();
+}
+
+std::size_t ImagePipelineStack::get_input_height() const
+{
+ ensure_node_exists();
+ return nodes_.front()->get_height();
+}
+
+PixelFormat ImagePipelineStack::get_input_format() const
+{
+ ensure_node_exists();
+ return nodes_.front()->get_format();
+}
+
+std::size_t ImagePipelineStack::get_input_row_bytes() const
+{
+ ensure_node_exists();
+ return nodes_.front()->get_row_bytes();
+}
+
+std::size_t ImagePipelineStack::get_output_width() const
+{
+ ensure_node_exists();
+ return nodes_.back()->get_width();
+}
+
+std::size_t ImagePipelineStack::get_output_height() const
+{
+ ensure_node_exists();
+ return nodes_.back()->get_height();
+}
+
+PixelFormat ImagePipelineStack::get_output_format() const
+{
+ ensure_node_exists();
+ return nodes_.back()->get_format();
+}
+
+std::size_t ImagePipelineStack::get_output_row_bytes() const
+{
+ ensure_node_exists();
+ return nodes_.back()->get_row_bytes();
+}
+
+void ImagePipelineStack::ensure_node_exists() const
+{
+ if (nodes_.empty()) {
+ throw SaneException("The pipeline does not contain any nodes");
+ }
+}
+
+void ImagePipelineStack::clear()
+{
+ // we need to destroy the nodes back to front, so that the destructors still have valid
+ // references to sources
+ for (auto it = nodes_.rbegin(); it != nodes_.rend(); ++it) {
+ it->reset();
+ }
+ nodes_.clear();
+}
+
+std::vector<std::uint8_t> ImagePipelineStack::get_all_data()
+{
+ auto row_bytes = get_output_row_bytes();
+ auto height = get_output_height();
+
+ std::vector<std::uint8_t> ret;
+ ret.resize(row_bytes * height);
+
+ for (std::size_t i = 0; i < height; ++i) {
+ get_next_row_data(ret.data() + row_bytes * i);
+ }
+ return ret;
+}
+
+Image ImagePipelineStack::get_image()
+{
+ auto height = get_output_height();
+
+ Image ret;
+ ret.resize(get_output_width(), height, get_output_format());
+
+ for (std::size_t i = 0; i < height; ++i) {
+ get_next_row_data(ret.get_row_ptr(i));
+ }
+ return ret;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/image_pipeline.h b/backend/genesys/image_pipeline.h
new file mode 100644
index 0000000..2986837
--- /dev/null
+++ b/backend/genesys/image_pipeline.h
@@ -0,0 +1,572 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H
+#define BACKEND_GENESYS_IMAGE_PIPELINE_H
+
+#include "image.h"
+#include "image_pixel.h"
+#include "image_buffer.h"
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+
+namespace genesys {
+
+class ImagePipelineNode
+{
+public:
+ virtual ~ImagePipelineNode();
+
+ virtual std::size_t get_width() const = 0;
+ virtual std::size_t get_height() const = 0;
+ virtual PixelFormat get_format() const = 0;
+
+ std::size_t get_row_bytes() const
+ {
+ return get_pixel_row_bytes(get_format(), get_width());
+ }
+
+ virtual bool eof() const = 0;
+
+ // returns true if the row was filled successfully, false otherwise (e.g. if not enough data
+ // was available.
+ virtual bool get_next_row_data(std::uint8_t* out_data) = 0;
+};
+
+class ImagePipelineNodeBytesSource : public ImagePipelineNode
+{
+public:
+ std::size_t remaining_bytes() const { return remaining_bytes_; }
+ void set_remaining_bytes(std::size_t bytes) { remaining_bytes_ = bytes; }
+
+ std::size_t consume_remaining_bytes(std::size_t bytes);
+
+private:
+ std::size_t remaining_bytes_ = 0;
+};
+
+// A pipeline node that produces data from a callable
+class ImagePipelineNodeCallableSource : public ImagePipelineNode
+{
+public:
+ using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>;
+
+ ImagePipelineNodeCallableSource(std::size_t width, std::size_t height, PixelFormat format,
+ ProducerCallback producer) :
+ producer_{producer},
+ width_{width},
+ height_{height},
+ format_{format}
+ {}
+
+ std::size_t get_width() const override { return width_; }
+ std::size_t get_height() const override { return height_; }
+ PixelFormat get_format() const override { return format_; }
+
+ bool eof() const override { return eof_; }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ProducerCallback producer_;
+ std::size_t width_ = 0;
+ std::size_t height_ = 0;
+ PixelFormat format_ = PixelFormat::UNKNOWN;
+ bool eof_ = false;
+};
+
+// A pipeline node that produces data from a callable requesting fixed-size chunks.
+class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNodeBytesSource
+{
+public:
+ using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>;
+
+ ImagePipelineNodeBufferedCallableSource(std::size_t width, std::size_t height,
+ PixelFormat format, std::size_t input_batch_size,
+ ProducerCallback producer);
+
+ std::size_t get_width() const override { return width_; }
+ std::size_t get_height() const override { return height_; }
+ PixelFormat get_format() const override { return format_; }
+
+ bool eof() const override { return eof_; }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+ std::size_t buffer_size() const { return buffer_.size(); }
+ std::size_t buffer_available() const { return buffer_.available(); }
+
+private:
+ ProducerCallback producer_;
+ std::size_t width_ = 0;
+ std::size_t height_ = 0;
+ PixelFormat format_ = PixelFormat::UNKNOWN;
+
+ bool eof_ = false;
+ std::size_t curr_row_ = 0;
+
+ ImageBuffer buffer_;
+};
+
+class ImagePipelineNodeBufferedGenesysUsb : public ImagePipelineNodeBytesSource
+{
+public:
+ using ProducerCallback = std::function<void(std::size_t size, std::uint8_t* out_data)>;
+
+ ImagePipelineNodeBufferedGenesysUsb(std::size_t width, std::size_t height,
+ PixelFormat format, std::size_t total_size,
+ const FakeBufferModel& buffer_model,
+ ProducerCallback producer);
+
+ std::size_t get_width() const override { return width_; }
+ std::size_t get_height() const override { return height_; }
+ PixelFormat get_format() const override { return format_; }
+
+ bool eof() const override { return eof_; }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+ std::size_t buffer_available() const { return buffer_.available(); }
+
+private:
+ ProducerCallback producer_;
+ std::size_t width_ = 0;
+ std::size_t height_ = 0;
+ PixelFormat format_ = PixelFormat::UNKNOWN;
+
+ bool eof_ = false;
+
+ ImageBufferGenesysUsb buffer_;
+};
+
+// A pipeline node that produces data from the given array.
+class ImagePipelineNodeArraySource : public ImagePipelineNodeBytesSource
+{
+public:
+ ImagePipelineNodeArraySource(std::size_t width, std::size_t height, PixelFormat format,
+ std::vector<std::uint8_t> data);
+
+ std::size_t get_width() const override { return width_; }
+ std::size_t get_height() const override { return height_; }
+ PixelFormat get_format() const override { return format_; }
+
+ bool eof() const override { return eof_; }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ std::size_t width_ = 0;
+ std::size_t height_ = 0;
+ PixelFormat format_ = PixelFormat::UNKNOWN;
+
+ bool eof_ = false;
+
+ std::vector<std::uint8_t> data_;
+ std::size_t next_row_ = 0;
+};
+
+
+/// A pipeline node that produces data from the given image
+class ImagePipelineNodeImageSource : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeImageSource(const Image& source);
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height(); }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return next_row_ >= get_height(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ const Image& source_;
+ std::size_t next_row_ = 0;
+};
+
+// A pipeline node that converts between pixel formats
+class ImagePipelineNodeFormatConvert : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeFormatConvert(ImagePipelineNode& source, PixelFormat dst_format) :
+ source_(source),
+ dst_format_{dst_format}
+ {}
+
+ ~ImagePipelineNodeFormatConvert() override = default;
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height(); }
+ PixelFormat get_format() const override { return dst_format_; }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ PixelFormat dst_format_;
+ std::vector<std::uint8_t> buffer_;
+};
+
+// A pipeline node that handles data that comes out of segmented sensors. Note that the width of
+// the output data does not necessarily match the input data width, because in many cases almost
+// all width of the image needs to be read in order to desegment it.
+class ImagePipelineNodeDesegment : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeDesegment(ImagePipelineNode& source,
+ std::size_t output_width,
+ const std::vector<unsigned>& segment_order,
+ std::size_t segment_pixels,
+ std::size_t interleaved_lines,
+ std::size_t pixels_per_chunk);
+
+ ImagePipelineNodeDesegment(ImagePipelineNode& source,
+ std::size_t output_width,
+ std::size_t segment_count,
+ std::size_t segment_pixels,
+ std::size_t interleaved_lines,
+ std::size_t pixels_per_chunk);
+
+ ~ImagePipelineNodeDesegment() override = default;
+
+ std::size_t get_width() const override { return output_width_; }
+ std::size_t get_height() const override { return source_.get_height() / interleaved_lines_; }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ std::size_t output_width_;
+ std::vector<unsigned> segment_order_;
+ std::size_t segment_pixels_ = 0;
+ std::size_t interleaved_lines_ = 0;
+ std::size_t pixels_per_chunk_ = 0;
+
+ RowBuffer buffer_;
+};
+
+// A pipeline node that deinterleaves data on multiple lines
+class ImagePipelineNodeDeinterleaveLines : public ImagePipelineNodeDesegment
+{
+public:
+ ImagePipelineNodeDeinterleaveLines(ImagePipelineNode& source,
+ std::size_t interleaved_lines,
+ std::size_t pixels_per_chunk);
+};
+
+// A pipeline that swaps bytes in 16-bit components on big-endian systems
+class ImagePipelineNodeSwap16BitEndian : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source);
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height(); }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ bool needs_swapping_ = false;
+};
+
+// A pipeline node that merges 3 mono lines into a color channel
+class ImagePipelineNodeMergeMonoLines : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeMergeMonoLines(ImagePipelineNode& source,
+ ColorOrder color_order);
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height() / 3; }
+ PixelFormat get_format() const override { return output_format_; }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ static PixelFormat get_output_format(PixelFormat input_format, ColorOrder order);
+
+ ImagePipelineNode& source_;
+ PixelFormat output_format_ = PixelFormat::UNKNOWN;
+
+ RowBuffer buffer_;
+};
+
+// A pipeline node that splits a color channel into 3 mono lines
+class ImagePipelineNodeSplitMonoLines : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeSplitMonoLines(ImagePipelineNode& source);
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height() * 3; }
+ PixelFormat get_format() const override { return output_format_; }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ static PixelFormat get_output_format(PixelFormat input_format);
+
+ ImagePipelineNode& source_;
+ PixelFormat output_format_ = PixelFormat::UNKNOWN;
+
+ std::vector<std::uint8_t> buffer_;
+ unsigned next_channel_ = 0;
+};
+
+// A pipeline node that shifts colors across lines by the given offsets
+class ImagePipelineNodeComponentShiftLines : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeComponentShiftLines(ImagePipelineNode& source,
+ unsigned shift_r, unsigned shift_g, unsigned shift_b);
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height() - extra_height_; }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ std::size_t extra_height_ = 0;
+
+ std::array<unsigned, 3> channel_shifts_;
+
+ RowBuffer buffer_;
+};
+
+// A pipeline node that shifts pixels across lines by the given offsets (performs unstaggering)
+class ImagePipelineNodePixelShiftLines : public ImagePipelineNode
+{
+public:
+ constexpr static std::size_t MAX_SHIFTS = 2;
+
+ ImagePipelineNodePixelShiftLines(ImagePipelineNode& source,
+ const std::vector<std::size_t>& shifts);
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height() - extra_height_; }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ std::size_t extra_height_ = 0;
+
+ std::vector<std::size_t> pixel_shifts_;
+
+ RowBuffer buffer_;
+};
+
+// A pipeline node that extracts a sub-image from the image. Padding and cropping is done as needed.
+// The class can't pad to the left of the image currently, as only positive offsets are accepted.
+class ImagePipelineNodeExtract : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeExtract(ImagePipelineNode& source,
+ std::size_t offset_x, std::size_t offset_y,
+ std::size_t width, std::size_t height);
+
+ ~ImagePipelineNodeExtract() override;
+
+ std::size_t get_width() const override { return width_; }
+ std::size_t get_height() const override { return height_; }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ std::size_t offset_x_ = 0;
+ std::size_t offset_y_ = 0;
+ std::size_t width_ = 0;
+ std::size_t height_ = 0;
+
+ std::size_t current_line_ = 0;
+ std::vector<std::uint8_t> cached_line_;
+};
+
+// A pipeline node that scales rows to the specified width by using a point filter
+class ImagePipelineNodeScaleRows : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeScaleRows(ImagePipelineNode& source, std::size_t width);
+
+ std::size_t get_width() const override { return width_; }
+ std::size_t get_height() const override { return source_.get_height(); }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ std::size_t width_ = 0;
+
+ std::vector<std::uint8_t> cached_line_;
+};
+
+// A pipeline node that mimics the calibration behavior on Genesys chips
+class ImagePipelineNodeCalibrate : public ImagePipelineNode
+{
+public:
+
+ ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector<std::uint16_t>& bottom,
+ const std::vector<std::uint16_t>& top);
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height(); }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+
+ std::vector<float> offset_;
+ std::vector<float> multiplier_;
+};
+
+class ImagePipelineNodeDebug : public ImagePipelineNode
+{
+public:
+ ImagePipelineNodeDebug(ImagePipelineNode& source, const std::string& path);
+ ~ImagePipelineNodeDebug() override;
+
+ std::size_t get_width() const override { return source_.get_width(); }
+ std::size_t get_height() const override { return source_.get_height(); }
+ PixelFormat get_format() const override { return source_.get_format(); }
+
+ bool eof() const override { return source_.eof(); }
+
+ bool get_next_row_data(std::uint8_t* out_data) override;
+
+private:
+ ImagePipelineNode& source_;
+ std::string path_;
+ RowBuffer buffer_;
+};
+
+class ImagePipelineStack
+{
+public:
+ ImagePipelineStack() {}
+ ~ImagePipelineStack() { clear(); }
+
+ std::size_t get_input_width() const;
+ std::size_t get_input_height() const;
+ PixelFormat get_input_format() const;
+ std::size_t get_input_row_bytes() const;
+
+ std::size_t get_output_width() const;
+ std::size_t get_output_height() const;
+ PixelFormat get_output_format() const;
+ std::size_t get_output_row_bytes() const;
+
+ ImagePipelineNode& front() { return *(nodes_.front().get()); }
+
+ bool eof() const { return nodes_.back()->eof(); }
+
+ void clear();
+
+ template<class Node, class... Args>
+ void push_first_node(Args&&... args)
+ {
+ if (!nodes_.empty()) {
+ throw SaneException("Trying to append first node when there are existing nodes");
+ }
+ nodes_.emplace_back(std::unique_ptr<Node>(new Node(std::forward<Args>(args)...)));
+ }
+
+ template<class Node, class... Args>
+ void push_node(Args&&... args)
+ {
+ ensure_node_exists();
+ nodes_.emplace_back(std::unique_ptr<Node>(new Node(*nodes_.back(),
+ std::forward<Args>(args)...)));
+ }
+
+ bool get_next_row_data(std::uint8_t* out_data)
+ {
+ return nodes_.back()->get_next_row_data(out_data);
+ }
+
+ std::vector<std::uint8_t> get_all_data();
+
+ Image get_image();
+
+private:
+ void ensure_node_exists() const;
+
+ std::vector<std::unique_ptr<ImagePipelineNode>> nodes_;
+};
+
+} // namespace genesys
+
+#endif // ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H
diff --git a/backend/genesys/image_pixel.cpp b/backend/genesys/image_pixel.cpp
new file mode 100644
index 0000000..1b83e12
--- /dev/null
+++ b/backend/genesys/image_pixel.cpp
@@ -0,0 +1,509 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "image.h"
+
+#include <array>
+
+namespace genesys {
+
+struct PixelFormatDesc
+{
+ PixelFormat format;
+ unsigned depth;
+ unsigned channels;
+ ColorOrder order;
+};
+
+const PixelFormatDesc s_known_pixel_formats[] = {
+ { PixelFormat::I1, 1, 1, ColorOrder::RGB },
+ { PixelFormat::I8, 8, 1, ColorOrder::RGB },
+ { PixelFormat::I16, 16, 1, ColorOrder::RGB },
+ { PixelFormat::RGB111, 1, 3, ColorOrder::RGB },
+ { PixelFormat::RGB888, 8, 3, ColorOrder::RGB },
+ { PixelFormat::RGB161616, 16, 3, ColorOrder::RGB },
+ { PixelFormat::BGR888, 8, 3, ColorOrder::BGR },
+ { PixelFormat::BGR161616, 16, 3, ColorOrder::BGR },
+};
+
+
+ColorOrder get_pixel_format_color_order(PixelFormat format)
+{
+ for (const auto& desc : s_known_pixel_formats) {
+ if (desc.format == format)
+ return desc.order;
+ }
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+}
+
+
+unsigned get_pixel_format_depth(PixelFormat format)
+{
+ for (const auto& desc : s_known_pixel_formats) {
+ if (desc.format == format)
+ return desc.depth;
+ }
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+}
+
+unsigned get_pixel_channels(PixelFormat format)
+{
+ for (const auto& desc : s_known_pixel_formats) {
+ if (desc.format == format)
+ return desc.channels;
+ }
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+}
+
+std::size_t get_pixel_row_bytes(PixelFormat format, std::size_t width)
+{
+ std::size_t depth = get_pixel_format_depth(format) * get_pixel_channels(format);
+ std::size_t total_bits = depth * width;
+ return total_bits / 8 + ((total_bits % 8 > 0) ? 1 : 0);
+}
+
+std::size_t get_pixels_from_row_bytes(PixelFormat format, std::size_t row_bytes)
+{
+ std::size_t depth = get_pixel_format_depth(format) * get_pixel_channels(format);
+ return (row_bytes * 8) / depth;
+}
+
+PixelFormat create_pixel_format(unsigned depth, unsigned channels, ColorOrder order)
+{
+ for (const auto& desc : s_known_pixel_formats) {
+ if (desc.depth == depth && desc.channels == channels && desc.order == order) {
+ return desc.format;
+ }
+ }
+ throw SaneException("Unknown pixel format %d %d %d", depth, channels,
+ static_cast<unsigned>(order));
+}
+
+static inline unsigned read_bit(const std::uint8_t* data, std::size_t x)
+{
+ return (data[x / 8] >> (7 - (x % 8))) & 0x1;
+}
+
+static inline void write_bit(std::uint8_t* data, std::size_t x, unsigned value)
+{
+ value = (value & 0x1) << (7 - (x % 8));
+ std::uint8_t mask = 0x1 << (7 - (x % 8));
+
+ data[x / 8] = (data[x / 8] & ~mask) | (value & mask);
+}
+
+Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format)
+{
+ switch (format) {
+ case PixelFormat::I1: {
+ std::uint16_t val = read_bit(data, x) ? 0xffff : 0x0000;
+ return Pixel(val, val, val);
+ }
+ case PixelFormat::RGB111: {
+ x *= 3;
+ std::uint16_t r = read_bit(data, x) ? 0xffff : 0x0000;
+ std::uint16_t g = read_bit(data, x + 1) ? 0xffff : 0x0000;
+ std::uint16_t b = read_bit(data, x + 2) ? 0xffff : 0x0000;
+ return Pixel(r, g, b);
+ }
+ case PixelFormat::I8: {
+ std::uint16_t val = std::uint16_t(data[x]) | (data[x] << 8);
+ return Pixel(val, val, val);
+ }
+ case PixelFormat::I16: {
+ x *= 2;
+ std::uint16_t val = std::uint16_t(data[x]) | (data[x + 1] << 8);
+ return Pixel(val, val, val);
+ }
+ case PixelFormat::RGB888: {
+ x *= 3;
+ std::uint16_t r = std::uint16_t(data[x]) | (data[x] << 8);
+ std::uint16_t g = std::uint16_t(data[x + 1]) | (data[x + 1] << 8);
+ std::uint16_t b = std::uint16_t(data[x + 2]) | (data[x + 2] << 8);
+ return Pixel(r, g, b);
+ }
+ case PixelFormat::BGR888: {
+ x *= 3;
+ std::uint16_t b = std::uint16_t(data[x]) | (data[x] << 8);
+ std::uint16_t g = std::uint16_t(data[x + 1]) | (data[x + 1] << 8);
+ std::uint16_t r = std::uint16_t(data[x + 2]) | (data[x + 2] << 8);
+ return Pixel(r, g, b);
+ }
+ case PixelFormat::RGB161616: {
+ x *= 6;
+ std::uint16_t r = std::uint16_t(data[x]) | (data[x + 1] << 8);
+ std::uint16_t g = std::uint16_t(data[x + 2]) | (data[x + 3] << 8);
+ std::uint16_t b = std::uint16_t(data[x + 4]) | (data[x + 5] << 8);
+ return Pixel(r, g, b);
+ }
+ case PixelFormat::BGR161616: {
+ x *= 6;
+ std::uint16_t b = std::uint16_t(data[x]) | (data[x + 1] << 8);
+ std::uint16_t g = std::uint16_t(data[x + 2]) | (data[x + 3] << 8);
+ std::uint16_t r = std::uint16_t(data[x + 4]) | (data[x + 5] << 8);
+ return Pixel(r, g, b);
+ }
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+ }
+}
+
+void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel, PixelFormat format)
+{
+ switch (format) {
+ case PixelFormat::I1:
+ write_bit(data, x, pixel.r & 0x8000 ? 1 : 0);
+ return;
+ case PixelFormat::RGB111: {
+ x *= 3;
+ write_bit(data, x, pixel.r & 0x8000 ? 1 : 0);
+ write_bit(data, x + 1,pixel.g & 0x8000 ? 1 : 0);
+ write_bit(data, x + 2, pixel.b & 0x8000 ? 1 : 0);
+ return;
+ }
+ case PixelFormat::I8: {
+ float val = (pixel.r >> 8) * 0.3f;
+ val += (pixel.g >> 8) * 0.59f;
+ val += (pixel.b >> 8) * 0.11f;
+ data[x] = static_cast<std::uint16_t>(val);
+ return;
+ }
+ case PixelFormat::I16: {
+ x *= 2;
+ float val = pixel.r * 0.3f;
+ val += pixel.g * 0.59f;
+ val += pixel.b * 0.11f;
+ auto val16 = static_cast<std::uint16_t>(val);
+ data[x] = val16 & 0xff;
+ data[x + 1] = (val16 >> 8) & 0xff;
+ return;
+ }
+ case PixelFormat::RGB888: {
+ x *= 3;
+ data[x] = pixel.r >> 8;
+ data[x + 1] = pixel.g >> 8;
+ data[x + 2] = pixel.b >> 8;
+ return;
+ }
+ case PixelFormat::BGR888: {
+ x *= 3;
+ data[x] = pixel.b >> 8;
+ data[x + 1] = pixel.g >> 8;
+ data[x + 2] = pixel.r >> 8;
+ return;
+ }
+ case PixelFormat::RGB161616: {
+ x *= 6;
+ data[x] = pixel.r & 0xff;
+ data[x + 1] = (pixel.r >> 8) & 0xff;
+ data[x + 2] = pixel.g & 0xff;
+ data[x + 3] = (pixel.g >> 8) & 0xff;
+ data[x + 4] = pixel.b & 0xff;
+ data[x + 5] = (pixel.b >> 8) & 0xff;
+ return;
+ }
+ case PixelFormat::BGR161616:
+ x *= 6;
+ data[x] = pixel.b & 0xff;
+ data[x + 1] = (pixel.b >> 8) & 0xff;
+ data[x + 2] = pixel.g & 0xff;
+ data[x + 3] = (pixel.g >> 8) & 0xff;
+ data[x + 4] = pixel.r & 0xff;
+ data[x + 5] = (pixel.r >> 8) & 0xff;
+ return;
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+ }
+}
+
+RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format)
+{
+ switch (format) {
+ case PixelFormat::I1:
+ return RawPixel(read_bit(data, x));
+ case PixelFormat::RGB111: {
+ x *= 3;
+ return RawPixel(read_bit(data, x) << 2 |
+ (read_bit(data, x + 1) << 1) |
+ (read_bit(data, x + 2)));
+ }
+ case PixelFormat::I8:
+ return RawPixel(data[x]);
+ case PixelFormat::I16: {
+ x *= 2;
+ return RawPixel(data[x], data[x + 1]);
+ }
+ case PixelFormat::RGB888:
+ case PixelFormat::BGR888: {
+ x *= 3;
+ return RawPixel(data[x], data[x + 1], data[x + 2]);
+ }
+ case PixelFormat::RGB161616:
+ case PixelFormat::BGR161616: {
+ x *= 6;
+ return RawPixel(data[x], data[x + 1], data[x + 2],
+ data[x + 3], data[x + 4], data[x + 5]);
+ }
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+ }
+}
+
+void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel, PixelFormat format)
+{
+ switch (format) {
+ case PixelFormat::I1:
+ write_bit(data, x, pixel.data[0] & 0x1);
+ return;
+ case PixelFormat::RGB111: {
+ x *= 3;
+ write_bit(data, x, (pixel.data[0] >> 2) & 0x1);
+ write_bit(data, x + 1, (pixel.data[0] >> 1) & 0x1);
+ write_bit(data, x + 2, (pixel.data[0]) & 0x1);
+ return;
+ }
+ case PixelFormat::I8:
+ data[x] = pixel.data[0];
+ return;
+ case PixelFormat::I16: {
+ x *= 2;
+ data[x] = pixel.data[0];
+ data[x + 1] = pixel.data[1];
+ return;
+ }
+ case PixelFormat::RGB888:
+ case PixelFormat::BGR888: {
+ x *= 3;
+ data[x] = pixel.data[0];
+ data[x + 1] = pixel.data[1];
+ data[x + 2] = pixel.data[2];
+ return;
+ }
+ case PixelFormat::RGB161616:
+ case PixelFormat::BGR161616: {
+ x *= 6;
+ data[x] = pixel.data[0];
+ data[x + 1] = pixel.data[1];
+ data[x + 2] = pixel.data[2];
+ data[x + 3] = pixel.data[3];
+ data[x + 4] = pixel.data[4];
+ data[x + 5] = pixel.data[5];
+ return;
+ }
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+ }
+}
+
+std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel,
+ PixelFormat format)
+{
+ switch (format) {
+ case PixelFormat::I1:
+ return read_bit(data, x);
+ case PixelFormat::RGB111:
+ return read_bit(data, x * 3 + channel);
+ case PixelFormat::I8:
+ return data[x];
+ case PixelFormat::I16: {
+ x *= 2;
+ return data[x] | (data[x + 1] << 8);
+ }
+ case PixelFormat::RGB888:
+ case PixelFormat::BGR888:
+ return data[x * 3 + channel];
+ case PixelFormat::RGB161616:
+ case PixelFormat::BGR161616:
+ return data[x * 6 + channel * 2] | (data[x * 6 + channel * 2 + 1]) << 8;
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+ }
+}
+
+void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel,
+ std::uint16_t pixel, PixelFormat format)
+{
+ switch (format) {
+ case PixelFormat::I1:
+ write_bit(data, x, pixel & 0x1);
+ return;
+ case PixelFormat::RGB111: {
+ write_bit(data, x * 3 + channel, pixel & 0x1);
+ return;
+ }
+ case PixelFormat::I8:
+ data[x] = pixel;
+ return;
+ case PixelFormat::I16: {
+ x *= 2;
+ data[x] = pixel;
+ data[x + 1] = pixel >> 8;
+ return;
+ }
+ case PixelFormat::RGB888:
+ case PixelFormat::BGR888: {
+ x *= 3;
+ data[x + channel] = pixel;
+ return;
+ }
+ case PixelFormat::RGB161616:
+ case PixelFormat::BGR161616: {
+ x *= 6;
+ data[x + channel * 2] = pixel;
+ data[x + channel * 2 + 1] = pixel >> 8;
+ return;
+ }
+ default:
+ throw SaneException("Unknown pixel format %d", static_cast<unsigned>(format));
+ }
+}
+
+template<PixelFormat Format>
+Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x)
+{
+ return get_pixel_from_row(data, x, Format);
+}
+
+template<PixelFormat Format>
+void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel)
+{
+ set_pixel_to_row(data, x, pixel, Format);
+}
+
+template<PixelFormat Format>
+RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x)
+{
+ return get_raw_pixel_from_row(data, x, Format);
+}
+
+template<PixelFormat Format>
+void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel)
+{
+ set_raw_pixel_to_row(data, x, pixel, Format);
+}
+
+template<PixelFormat Format>
+std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel)
+{
+ return get_raw_channel_from_row(data, x, channel, Format);
+}
+
+template<PixelFormat Format>
+void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel)
+{
+ set_raw_channel_to_row(data, x, channel, pixel, Format);
+}
+
+template Pixel get_pixel_from_row<PixelFormat::I1>(const std::uint8_t* data, std::size_t x);
+template Pixel get_pixel_from_row<PixelFormat::RGB111>(const std::uint8_t* data, std::size_t x);
+template Pixel get_pixel_from_row<PixelFormat::I8>(const std::uint8_t* data, std::size_t x);
+template Pixel get_pixel_from_row<PixelFormat::RGB888>(const std::uint8_t* data, std::size_t x);
+template Pixel get_pixel_from_row<PixelFormat::BGR888>(const std::uint8_t* data, std::size_t x);
+template Pixel get_pixel_from_row<PixelFormat::I16>(const std::uint8_t* data, std::size_t x);
+template Pixel get_pixel_from_row<PixelFormat::RGB161616>(const std::uint8_t* data, std::size_t x);
+template Pixel get_pixel_from_row<PixelFormat::BGR161616>(const std::uint8_t* data, std::size_t x);
+
+template RawPixel get_raw_pixel_from_row<PixelFormat::I1>(const std::uint8_t* data, std::size_t x);
+template RawPixel get_raw_pixel_from_row<PixelFormat::RGB111>(const std::uint8_t* data, std::size_t x);
+template RawPixel get_raw_pixel_from_row<PixelFormat::I8>(const std::uint8_t* data, std::size_t x);
+template RawPixel get_raw_pixel_from_row<PixelFormat::RGB888>(const std::uint8_t* data, std::size_t x);
+template RawPixel get_raw_pixel_from_row<PixelFormat::BGR888>(const std::uint8_t* data, std::size_t x);
+template RawPixel get_raw_pixel_from_row<PixelFormat::I16>(const std::uint8_t* data, std::size_t x);
+template RawPixel get_raw_pixel_from_row<PixelFormat::RGB161616>(const std::uint8_t* data, std::size_t x);
+template RawPixel get_raw_pixel_from_row<PixelFormat::BGR161616>(const std::uint8_t* data, std::size_t x);
+
+template std::uint16_t get_raw_channel_from_row<PixelFormat::I1>(
+ const std::uint8_t* data, std::size_t x, unsigned channel);
+template std::uint16_t get_raw_channel_from_row<PixelFormat::RGB111>(
+ const std::uint8_t* data, std::size_t x, unsigned channel);
+template std::uint16_t get_raw_channel_from_row<PixelFormat::I8>(
+ const std::uint8_t* data, std::size_t x, unsigned channel);
+template std::uint16_t get_raw_channel_from_row<PixelFormat::RGB888>(
+ const std::uint8_t* data, std::size_t x, unsigned channel);
+template std::uint16_t get_raw_channel_from_row<PixelFormat::BGR888>(
+ const std::uint8_t* data, std::size_t x, unsigned channel);
+template std::uint16_t get_raw_channel_from_row<PixelFormat::I16>(
+ const std::uint8_t* data, std::size_t x, unsigned channel);
+template std::uint16_t get_raw_channel_from_row<PixelFormat::RGB161616>(
+ const std::uint8_t* data, std::size_t x, unsigned channel);
+template std::uint16_t get_raw_channel_from_row<PixelFormat::BGR161616>
+ (const std::uint8_t* data, std::size_t x, unsigned channel);
+
+template void set_pixel_to_row<PixelFormat::I1>(std::uint8_t* data, std::size_t x, Pixel pixel);
+template void set_pixel_to_row<PixelFormat::RGB111>(std::uint8_t* data, std::size_t x, Pixel pixel);
+template void set_pixel_to_row<PixelFormat::I8>(std::uint8_t* data, std::size_t x, Pixel pixel);
+template void set_pixel_to_row<PixelFormat::RGB888>(std::uint8_t* data, std::size_t x, Pixel pixel);
+template void set_pixel_to_row<PixelFormat::BGR888>(std::uint8_t* data, std::size_t x, Pixel pixel);
+template void set_pixel_to_row<PixelFormat::I16>(std::uint8_t* data, std::size_t x, Pixel pixel);
+template void set_pixel_to_row<PixelFormat::RGB161616>(std::uint8_t* data, std::size_t x, Pixel pixel);
+template void set_pixel_to_row<PixelFormat::BGR161616>(std::uint8_t* data, std::size_t x, Pixel pixel);
+
+template void set_raw_pixel_to_row<PixelFormat::I1>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+template void set_raw_pixel_to_row<PixelFormat::RGB111>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+template void set_raw_pixel_to_row<PixelFormat::I8>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+template void set_raw_pixel_to_row<PixelFormat::RGB888>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+template void set_raw_pixel_to_row<PixelFormat::BGR888>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+template void set_raw_pixel_to_row<PixelFormat::I16>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+template void set_raw_pixel_to_row<PixelFormat::RGB161616>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+template void set_raw_pixel_to_row<PixelFormat::BGR161616>(std::uint8_t* data, std::size_t x, RawPixel pixel);
+
+template void set_raw_channel_to_row<PixelFormat::I1>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+template void set_raw_channel_to_row<PixelFormat::RGB111>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+template void set_raw_channel_to_row<PixelFormat::I8>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+template void set_raw_channel_to_row<PixelFormat::RGB888>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+template void set_raw_channel_to_row<PixelFormat::BGR888>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+template void set_raw_channel_to_row<PixelFormat::I16>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+template void set_raw_channel_to_row<PixelFormat::RGB161616>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+template void set_raw_channel_to_row<PixelFormat::BGR161616>(
+ std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel);
+
+} // namespace genesys
diff --git a/backend/genesys/image_pixel.h b/backend/genesys/image_pixel.h
new file mode 100644
index 0000000..2dda271
--- /dev/null
+++ b/backend/genesys/image_pixel.h
@@ -0,0 +1,144 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_IMAGE_PIXEL_H
+#define BACKEND_GENESYS_IMAGE_PIXEL_H
+
+#include "enums.h"
+#include <algorithm>
+#include <cstdint>
+#include <cstddef>
+
+namespace genesys {
+
+enum class PixelFormat
+{
+ UNKNOWN,
+ I1,
+ RGB111,
+ I8,
+ RGB888,
+ BGR888,
+ I16,
+ RGB161616,
+ BGR161616,
+};
+
+struct Pixel
+{
+ Pixel() = default;
+ Pixel(std::uint16_t red, std::uint16_t green, std::uint16_t blue) :
+ r{red}, g{green}, b{blue} {}
+
+ std::uint16_t r = 0;
+ std::uint16_t g = 0;
+ std::uint16_t b = 0;
+
+ bool operator==(const Pixel& other) const
+ {
+ return r == other.r && g == other.g && b == other.b;
+ }
+};
+
+struct RawPixel
+{
+ RawPixel() = default;
+ RawPixel(std::uint8_t d0) : data{d0, 0, 0, 0, 0, 0} {}
+ RawPixel(std::uint8_t d0, std::uint8_t d1) : data{d0, d1, 0, 0, 0, 0} {}
+ RawPixel(std::uint8_t d0, std::uint8_t d1, std::uint8_t d2) : data{d0, d1, d2, 0, 0, 0} {}
+ RawPixel(std::uint8_t d0, std::uint8_t d1, std::uint8_t d2,
+ std::uint8_t d3, std::uint8_t d4, std::uint8_t d5) : data{d0, d1, d2, d3, d4, d5} {}
+ std::uint8_t data[6] = {};
+
+ bool operator==(const RawPixel& other) const
+ {
+ return std::equal(std::begin(data), std::end(data),
+ std::begin(other.data));
+ }
+};
+
+ColorOrder get_pixel_format_color_order(PixelFormat format);
+unsigned get_pixel_format_depth(PixelFormat format);
+unsigned get_pixel_channels(PixelFormat format);
+std::size_t get_pixel_row_bytes(PixelFormat format, std::size_t width);
+
+std::size_t get_pixels_from_row_bytes(PixelFormat format, std::size_t row_bytes);
+
+PixelFormat create_pixel_format(unsigned depth, unsigned channels, ColorOrder order);
+
+// retrieves or sets the logical pixel values in 16-bit range.
+Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format);
+void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel, PixelFormat format);
+
+// retrieves or sets the physical pixel values. The low bytes of the RawPixel are interpreted as
+// the retrieved values / values to set
+RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format);
+void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel, PixelFormat format);
+
+// retrieves or sets the physical value of specific channel of the pixel. The channels are numbered
+// in the same order as the pixel is laid out in memory, that is, whichever channel comes first
+// has the index 0. E.g. 0-th channel in RGB888 is the red byte, but in BGR888 is the blue byte.
+std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel,
+ PixelFormat format);
+void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel,
+ PixelFormat format);
+
+template<PixelFormat Format>
+Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x);
+template<PixelFormat Format>
+void set_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel);
+
+template<PixelFormat Format>
+Pixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x);
+template<PixelFormat Format>
+void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel);
+
+template<PixelFormat Format>
+std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel);
+template<PixelFormat Format>
+void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel,
+ std::uint16_t pixel);
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_IMAGE_PIXEL_H
diff --git a/backend/genesys/low.cpp b/backend/genesys/low.cpp
new file mode 100644
index 0000000..7937fcc
--- /dev/null
+++ b/backend/genesys/low.cpp
@@ -0,0 +1,1994 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "low.h"
+#include "assert.h"
+#include "test_settings.h"
+
+#include "gl124_registers.h"
+#include "gl646_registers.h"
+#include "gl841_registers.h"
+#include "gl843_registers.h"
+#include "gl846_registers.h"
+#include "gl847_registers.h"
+#include "gl646_registers.h"
+
+#include <cstdio>
+#include <cmath>
+#include <vector>
+
+/* ------------------------------------------------------------------------ */
+/* functions calling ASIC specific functions */
+/* ------------------------------------------------------------------------ */
+
+namespace genesys {
+
+/**
+ * setup the hardware dependent functions
+ */
+
+namespace gl124 { std::unique_ptr<CommandSet> create_gl124_cmd_set(); }
+namespace gl646 { std::unique_ptr<CommandSet> create_gl646_cmd_set(); }
+namespace gl841 { std::unique_ptr<CommandSet> create_gl841_cmd_set(); }
+namespace gl843 { std::unique_ptr<CommandSet> create_gl843_cmd_set(); }
+namespace gl846 { std::unique_ptr<CommandSet> create_gl846_cmd_set(); }
+namespace gl847 { std::unique_ptr<CommandSet> create_gl847_cmd_set(); }
+
+void sanei_genesys_init_cmd_set(Genesys_Device* dev)
+{
+ DBG_INIT ();
+ DBG_HELPER(dbg);
+ switch (dev->model->asic_type) {
+ case AsicType::GL646: dev->cmd_set = gl646::create_gl646_cmd_set(); break;
+ case AsicType::GL841: dev->cmd_set = gl841::create_gl841_cmd_set(); break;
+ case AsicType::GL843: dev->cmd_set = gl843::create_gl843_cmd_set(); break;
+ case AsicType::GL845: // since only a few reg bits differs we handle both together
+ case AsicType::GL846: dev->cmd_set = gl846::create_gl846_cmd_set(); break;
+ case AsicType::GL847: dev->cmd_set = gl847::create_gl847_cmd_set(); break;
+ case AsicType::GL124: dev->cmd_set = gl124::create_gl124_cmd_set(); break;
+ default: throw SaneException(SANE_STATUS_INVAL, "unknown ASIC type");
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* General IO and debugging functions */
+/* ------------------------------------------------------------------------ */
+
+void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, std::size_t length)
+{
+ DBG_HELPER(dbg);
+ std::FILE* out = std::fopen(filename, "w");
+ if (!out) {
+ throw SaneException("could not open %s for writing: %s", filename, strerror(errno));
+ }
+ std::fwrite(data, 1, length, out);
+ std::fclose(out);
+}
+
+// Write data to a pnm file (e.g. calibration). For debugging only
+// data is RGB or grey, with little endian byte order
+void sanei_genesys_write_pnm_file(const char* filename, const std::uint8_t* data, int depth,
+ int channels, int pixels_per_line, int lines)
+{
+ DBG_HELPER_ARGS(dbg, "depth=%d, channels=%d, ppl=%d, lines=%d", depth, channels,
+ pixels_per_line, lines);
+ int count;
+
+ std::FILE* out = std::fopen(filename, "w");
+ if (!out)
+ {
+ throw SaneException("could not open %s for writing: %s\n", filename, strerror(errno));
+ }
+ if(depth==1)
+ {
+ fprintf (out, "P4\n%d\n%d\n", pixels_per_line, lines);
+ }
+ else
+ {
+ std::fprintf(out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6', pixels_per_line, lines,
+ static_cast<int>(std::pow(static_cast<double>(2),
+ static_cast<double>(depth - 1))));
+ }
+ if (channels == 3)
+ {
+ for (count = 0; count < (pixels_per_line * lines * 3); count++)
+ {
+ if (depth == 16)
+ fputc (*(data + 1), out);
+ fputc (*(data++), out);
+ if (depth == 16)
+ data++;
+ }
+ }
+ else
+ {
+ if (depth==1)
+ {
+ pixels_per_line/=8;
+ }
+ for (count = 0; count < (pixels_per_line * lines); count++)
+ {
+ switch (depth)
+ {
+ case 8:
+ fputc (*(data + count), out);
+ break;
+ case 16:
+ fputc (*(data + 1), out);
+ fputc (*(data), out);
+ data += 2;
+ break;
+ default:
+ fputc(data[count], out);
+ break;
+ }
+ }
+ }
+ std::fclose(out);
+}
+
+void sanei_genesys_write_pnm_file16(const char* filename, const uint16_t* data, unsigned channels,
+ unsigned pixels_per_line, unsigned lines)
+{
+ DBG_HELPER_ARGS(dbg, "channels=%d, ppl=%d, lines=%d", channels,
+ pixels_per_line, lines);
+
+ std::FILE* out = std::fopen(filename, "w");
+ if (!out) {
+ throw SaneException("could not open %s for writing: %s\n", filename, strerror(errno));
+ }
+ std::fprintf(out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6',
+ pixels_per_line, lines, 256 * 256 - 1);
+
+ for (unsigned count = 0; count < (pixels_per_line * lines * channels); count++) {
+ fputc(*data >> 8, out);
+ fputc(*data & 0xff, out);
+ data++;
+ }
+ std::fclose(out);
+}
+
+bool is_supported_write_pnm_file_image_format(PixelFormat format)
+{
+ switch (format) {
+ case PixelFormat::I1:
+ case PixelFormat::RGB111:
+ case PixelFormat::I8:
+ case PixelFormat::RGB888:
+ case PixelFormat::I16:
+ case PixelFormat::RGB161616:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void sanei_genesys_write_pnm_file(const char* filename, const Image& image)
+{
+ if (!is_supported_write_pnm_file_image_format(image.get_format())) {
+ throw SaneException("Unsupported format %d", static_cast<unsigned>(image.get_format()));
+ }
+
+ sanei_genesys_write_pnm_file(filename, image.get_row_ptr(0),
+ get_pixel_format_depth(image.get_format()),
+ get_pixel_channels(image.get_format()),
+ image.get_width(), image.get_height());
+}
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+unsigned sanei_genesys_get_bulk_max_size(AsicType asic_type)
+{
+ /* Genesys supports 0xFE00 maximum size in general, wheraus GL646 supports
+ 0xFFC0. We use 0xF000 because that's the packet limit in the Linux usbmon
+ USB capture stack. By default it limits packet size to b_size / 5 where
+ b_size is the size of the ring buffer. By default it's 300*1024, so the
+ packet is limited 61440 without any visibility to acquiring software.
+ */
+ if (asic_type == AsicType::GL124 ||
+ asic_type == AsicType::GL846 ||
+ asic_type == AsicType::GL847)
+ {
+ return 0xeff0;
+ }
+ return 0xf000;
+}
+
+// Set address for writing data
+void sanei_genesys_set_buffer_address(Genesys_Device* dev, uint32_t addr)
+{
+ DBG_HELPER(dbg);
+
+ if (dev->model->asic_type==AsicType::GL847 ||
+ dev->model->asic_type==AsicType::GL845 ||
+ dev->model->asic_type==AsicType::GL846 ||
+ dev->model->asic_type==AsicType::GL124)
+ {
+ DBG(DBG_warn, "%s: shouldn't be used for GL846+ ASICs\n", __func__);
+ return;
+ }
+
+ DBG(DBG_io, "%s: setting address to 0x%05x\n", __func__, addr & 0xfffffff0);
+
+ addr = addr >> 4;
+
+ dev->interface->write_register(0x2b, (addr & 0xff));
+
+ addr = addr >> 8;
+ dev->interface->write_register(0x2a, (addr & 0xff));
+}
+
+/* ------------------------------------------------------------------------ */
+/* Medium level functions */
+/* ------------------------------------------------------------------------ */
+
+Status scanner_read_status(Genesys_Device& dev)
+{
+ DBG_HELPER(dbg);
+ std::uint16_t address = 0;
+
+ switch (dev.model->asic_type) {
+ case AsicType::GL124: address = 0x101; break;
+ case AsicType::GL646:
+ case AsicType::GL841:
+ case AsicType::GL843:
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847: address = 0x41; break;
+ default: throw SaneException("Unsupported asic type");
+ }
+
+ // same for all chips
+ constexpr std::uint8_t PWRBIT = 0x80;
+ constexpr std::uint8_t BUFEMPTY = 0x40;
+ constexpr std::uint8_t FEEDFSH = 0x20;
+ constexpr std::uint8_t SCANFSH = 0x10;
+ constexpr std::uint8_t HOMESNR = 0x08;
+ constexpr std::uint8_t LAMPSTS = 0x04;
+ constexpr std::uint8_t FEBUSY = 0x02;
+ constexpr std::uint8_t MOTORENB = 0x01;
+
+ auto value = dev.interface->read_register(address);
+ Status status;
+ status.is_replugged = !(value & PWRBIT);
+ status.is_buffer_empty = value & BUFEMPTY;
+ status.is_feeding_finished = value & FEEDFSH;
+ status.is_scanning_finished = value & SCANFSH;
+ status.is_at_home = value & HOMESNR;
+ status.is_lamp_on = value & LAMPSTS;
+ status.is_front_end_busy = value & FEBUSY;
+ status.is_motor_enabled = value & MOTORENB;
+
+ if (DBG_LEVEL >= DBG_io) {
+ debug_print_status(dbg, status);
+ }
+
+ return status;
+}
+
+Status scanner_read_reliable_status(Genesys_Device& dev)
+{
+ DBG_HELPER(dbg);
+
+ scanner_read_status(dev);
+ dev.interface->sleep_ms(100);
+ return scanner_read_status(dev);
+}
+
+void scanner_read_print_status(Genesys_Device& dev)
+{
+ scanner_read_status(dev);
+}
+
+/**
+ * decodes and prints content of status register
+ * @param val value read from status register
+ */
+void debug_print_status(DebugMessageHelper& dbg, Status val)
+{
+ std::stringstream str;
+ str << val;
+ dbg.vlog(DBG_info, "status=%s\n", str.str().c_str());
+}
+
+#if 0
+/* returns pixels per line from register set */
+/*candidate for moving into chip specific files?*/
+static int
+genesys_pixels_per_line (Genesys_Register_Set * reg)
+{
+ int pixels_per_line;
+
+ pixels_per_line = reg->get8(0x32) * 256 + reg->get8(0x33);
+ pixels_per_line -= (reg->get8(0x30) * 256 + reg->get8(0x31));
+
+ return pixels_per_line;
+}
+
+/* returns dpiset from register set */
+/*candidate for moving into chip specific files?*/
+static int
+genesys_dpiset (Genesys_Register_Set * reg)
+{
+ return reg->get8(0x2c) * 256 + reg->get8(0x2d);
+}
+#endif
+
+/** read the number of valid words in scanner's RAM
+ * ie registers 42-43-44
+ */
+// candidate for moving into chip specific files?
+void sanei_genesys_read_valid_words(Genesys_Device* dev, unsigned int* words)
+{
+ DBG_HELPER(dbg);
+
+ switch (dev->model->asic_type)
+ {
+ case AsicType::GL124:
+ *words = dev->interface->read_register(0x102) & 0x03;
+ *words = *words * 256 + dev->interface->read_register(0x103);
+ *words = *words * 256 + dev->interface->read_register(0x104);
+ *words = *words * 256 + dev->interface->read_register(0x105);
+ break;
+
+ case AsicType::GL845:
+ case AsicType::GL846:
+ *words = dev->interface->read_register(0x42) & 0x02;
+ *words = *words * 256 + dev->interface->read_register(0x43);
+ *words = *words * 256 + dev->interface->read_register(0x44);
+ *words = *words * 256 + dev->interface->read_register(0x45);
+ break;
+
+ case AsicType::GL847:
+ *words = dev->interface->read_register(0x42) & 0x03;
+ *words = *words * 256 + dev->interface->read_register(0x43);
+ *words = *words * 256 + dev->interface->read_register(0x44);
+ *words = *words * 256 + dev->interface->read_register(0x45);
+ break;
+
+ default:
+ *words = dev->interface->read_register(0x44);
+ *words += dev->interface->read_register(0x43) * 256;
+ if (dev->model->asic_type == AsicType::GL646) {
+ *words += ((dev->interface->read_register(0x42) & 0x03) * 256 * 256);
+ } else {
+ *words += ((dev->interface->read_register(0x42) & 0x0f) * 256 * 256);
+ }
+ }
+
+ DBG(DBG_proc, "%s: %d words\n", __func__, *words);
+}
+
+/** read the number of lines scanned
+ * ie registers 4b-4c-4d
+ */
+void sanei_genesys_read_scancnt(Genesys_Device* dev, unsigned int* words)
+{
+ DBG_HELPER(dbg);
+
+ if (dev->model->asic_type == AsicType::GL124) {
+ *words = (dev->interface->read_register(0x10b) & 0x0f) << 16;
+ *words += (dev->interface->read_register(0x10c) << 8);
+ *words += dev->interface->read_register(0x10d);
+ }
+ else
+ {
+ *words = dev->interface->read_register(0x4d);
+ *words += dev->interface->read_register(0x4c) * 256;
+ if (dev->model->asic_type == AsicType::GL646) {
+ *words += ((dev->interface->read_register(0x4b) & 0x03) * 256 * 256);
+ } else {
+ *words += ((dev->interface->read_register(0x4b) & 0x0f) * 256 * 256);
+ }
+ }
+
+ DBG(DBG_proc, "%s: %d lines\n", __func__, *words);
+}
+
+/** @brief Check if the scanner's internal data buffer is empty
+ * @param *dev device to test for data
+ * @param *empty return value
+ * @return empty will be set to true if there is no scanned data.
+ **/
+bool sanei_genesys_is_buffer_empty(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+
+ dev->interface->sleep_ms(1);
+
+ auto status = scanner_read_status(*dev);
+
+ if (status.is_buffer_empty) {
+ /* fix timing issue on USB3 (or just may be too fast) hardware
+ * spotted by John S. Weber <jweber53@gmail.com>
+ */
+ dev->interface->sleep_ms(1);
+ DBG(DBG_io2, "%s: buffer is empty\n", __func__);
+ return true;
+ }
+
+
+ DBG(DBG_io, "%s: buffer is filled\n", __func__);
+ return false;
+}
+
+void wait_until_buffer_non_empty(Genesys_Device* dev, bool check_status_twice)
+{
+ // FIXME: reduce MAX_RETRIES once tests are updated
+ const unsigned MAX_RETRIES = 100000;
+ for (unsigned i = 0; i < MAX_RETRIES; ++i) {
+
+ if (check_status_twice) {
+ // FIXME: this only to preserve previous behavior, can be removed
+ scanner_read_status(*dev);
+ }
+
+ bool empty = sanei_genesys_is_buffer_empty(dev);
+ dev->interface->sleep_ms(10);
+ if (!empty)
+ return;
+ }
+ throw SaneException(SANE_STATUS_IO_ERROR, "failed to read data");
+}
+
+void wait_until_has_valid_words(Genesys_Device* dev)
+{
+ unsigned words = 0;
+ unsigned sleep_time_ms = 10;
+
+ for (unsigned wait_ms = 0; wait_ms < 50000; wait_ms += sleep_time_ms) {
+ sanei_genesys_read_valid_words(dev, &words);
+ if (words != 0)
+ break;
+ dev->interface->sleep_ms(sleep_time_ms);
+ }
+
+ if (words == 0) {
+ throw SaneException(SANE_STATUS_IO_ERROR, "timeout, buffer does not get filled");
+ }
+}
+
+// Read data (e.g scanned image) from scan buffer
+void sanei_genesys_read_data_from_scanner(Genesys_Device* dev, uint8_t* data, size_t size)
+{
+ DBG_HELPER_ARGS(dbg, "size = %zu bytes", size);
+
+ if (size & 1)
+ DBG(DBG_info, "WARNING %s: odd number of bytes\n", __func__);
+
+ wait_until_has_valid_words(dev);
+
+ dev->interface->bulk_read_data(0x45, data, size);
+}
+
+Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session,
+ std::size_t total_bytes)
+{
+ DBG_HELPER(dbg);
+
+ auto format = create_pixel_format(session.params.depth,
+ dev->model->is_cis ? 1 : session.params.channels,
+ dev->model->line_mode_color_order);
+
+ auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw);
+ auto height = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1);
+
+ Image image(width, height, format);
+
+ auto max_bytes = image.get_row_bytes() * height;
+ if (total_bytes > max_bytes) {
+ throw SaneException("Trying to read too much data %zu (max %zu)", total_bytes, max_bytes);
+ }
+ if (total_bytes != max_bytes) {
+ DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu\n", __func__,
+ total_bytes, max_bytes);
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, image.get_row_ptr(0), total_bytes);
+
+ ImagePipelineStack pipeline;
+ pipeline.push_first_node<ImagePipelineNodeImageSource>(image);
+
+ if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && session.params.depth == 16) {
+ dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ }
+
+#ifdef WORDS_BIGENDIAN
+ if (depth == 16) {
+ dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ }
+#endif
+
+ if (dev->model->is_cis && session.params.channels == 3) {
+ dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);
+ }
+
+ if (dev->pipeline.get_output_format() == PixelFormat::BGR888) {
+ dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
+ }
+
+ if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) {
+ dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
+ }
+
+ return pipeline.get_image();
+}
+
+void sanei_genesys_read_feed_steps(Genesys_Device* dev, unsigned int* steps)
+{
+ DBG_HELPER(dbg);
+
+ if (dev->model->asic_type == AsicType::GL124) {
+ *steps = (dev->interface->read_register(0x108) & 0x1f) << 16;
+ *steps += (dev->interface->read_register(0x109) << 8);
+ *steps += dev->interface->read_register(0x10a);
+ }
+ else
+ {
+ *steps = dev->interface->read_register(0x4a);
+ *steps += dev->interface->read_register(0x49) * 256;
+ if (dev->model->asic_type == AsicType::GL646) {
+ *steps += ((dev->interface->read_register(0x48) & 0x03) * 256 * 256);
+ } else if (dev->model->asic_type == AsicType::GL841) {
+ *steps += ((dev->interface->read_register(0x48) & 0x0f) * 256 * 256);
+ } else {
+ *steps += ((dev->interface->read_register(0x48) & 0x1f) * 256 * 256);
+ }
+ }
+
+ DBG(DBG_proc, "%s: %d steps\n", __func__, *steps);
+}
+
+void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, bool set)
+{
+ static const uint8_t REG_0x03_LAMPPWR = 0x10;
+
+ if (set) {
+ regs.find_reg(0x03).value |= REG_0x03_LAMPPWR;
+
+ if (dev->model->asic_type == AsicType::GL841) {
+ regs_set_exposure(dev->model->asic_type, regs,
+ sanei_genesys_fixup_exposure(sensor.exposure));
+ regs.set8(0x19, 0x50);
+ }
+
+ if (dev->model->asic_type == AsicType::GL843) {
+ regs_set_exposure(dev->model->asic_type, regs, sensor.exposure);
+
+ // we don't actually turn on lamp on infrared scan
+ if ((dev->model->model_id == ModelId::CANON_8400F ||
+ dev->model->model_id == ModelId::CANON_8600F ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) &&
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR;
+ }
+ }
+ } else {
+ regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR;
+
+ if (dev->model->asic_type == AsicType::GL841) {
+ regs_set_exposure(dev->model->asic_type, regs, {0x0101, 0x0101, 0x0101});
+ regs.set8(0x19, 0xff);
+ }
+
+ if (dev->model->asic_type == AsicType::GL843) {
+ if (dev->model->model_id == ModelId::PANASONIC_KV_SS080 ||
+ dev->model->model_id == ModelId::HP_SCANJET_4850C ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
+ dev->model->model_id == ModelId::HP_SCANJET_G4050)
+ {
+ // BUG: datasheet says we shouldn't set exposure to zero
+ regs_set_exposure(dev->model->asic_type, regs, {0, 0, 0});
+ }
+ }
+ }
+ regs.state.is_lamp_on = set;
+}
+
+void sanei_genesys_set_motor_power(Genesys_Register_Set& regs, bool set)
+{
+ static const uint8_t REG_0x02_MTRPWR = 0x10;
+
+ if (set) {
+ regs.find_reg(0x02).value |= REG_0x02_MTRPWR;
+ } else {
+ regs.find_reg(0x02).value &= ~REG_0x02_MTRPWR;
+ }
+ regs.state.is_motor_on = set;
+}
+
+bool should_enable_gamma(const ScanSession& session, const Genesys_Sensor& sensor)
+{
+ if ((session.params.flags & ScanFlag::DISABLE_GAMMA) != ScanFlag::NONE) {
+ return false;
+ }
+ if (sensor.gamma[0] == 1.0f || sensor.gamma[1] == 1.0f || sensor.gamma[2] == 1.0f) {
+ return false;
+ }
+ if (session.params.depth == 16)
+ return false;
+
+ return true;
+}
+
+std::vector<uint16_t> get_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ int color)
+{
+ if (!dev->gamma_override_tables[color].empty()) {
+ return dev->gamma_override_tables[color];
+ } else {
+ std::vector<uint16_t> ret;
+ sanei_genesys_create_default_gamma_table(dev, ret, sensor.gamma[color]);
+ return ret;
+ }
+}
+
+/** @brief generates gamma buffer to transfer
+ * Generates gamma table buffer to send to ASIC. Applies
+ * contrast and brightness if set.
+ * @param dev device to set up
+ * @param bits number of bits used by gamma
+ * @param max value for gamma
+ * @param size of the gamma table
+ * @param gamma allocated gamma buffer to fill
+ */
+void sanei_genesys_generate_gamma_buffer(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ int bits,
+ int max,
+ int size,
+ uint8_t* gamma)
+{
+ DBG_HELPER(dbg);
+ std::vector<uint16_t> rgamma = get_gamma_table(dev, sensor, GENESYS_RED);
+ std::vector<uint16_t> ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN);
+ std::vector<uint16_t> bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE);
+
+ if(dev->settings.contrast!=0 || dev->settings.brightness!=0)
+ {
+ std::vector<uint16_t> lut(65536);
+ sanei_genesys_load_lut(reinterpret_cast<unsigned char *>(lut.data()),
+ bits,
+ bits,
+ 0,
+ max,
+ dev->settings.contrast,
+ dev->settings.brightness);
+ for (int i = 0; i < size; i++)
+ {
+ uint16_t value=rgamma[i];
+ value=lut[value];
+ gamma[i * 2 + size * 0 + 0] = value & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
+
+ value=ggamma[i];
+ value=lut[value];
+ gamma[i * 2 + size * 2 + 0] = value & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
+
+ value=bgamma[i];
+ value=lut[value];
+ gamma[i * 2 + size * 4 + 0] = value & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < size; i++)
+ {
+ uint16_t value=rgamma[i];
+ gamma[i * 2 + size * 0 + 0] = value & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
+
+ value=ggamma[i];
+ gamma[i * 2 + size * 2 + 0] = value & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
+
+ value=bgamma[i];
+ gamma[i * 2 + size * 4 + 0] = value & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
+ }
+ }
+}
+
+
+/** @brief send gamma table to scanner
+ * This function sends generic gamma table (ie ones built with
+ * provided gamma) or the user defined one if provided by
+ * fontend. Used by gl846+ ASICs
+ * @param dev device to write to
+ */
+void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor)
+{
+ DBG_HELPER(dbg);
+ int size;
+ int i;
+
+ size = 256 + 1;
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ std::vector<uint8_t> gamma(size * 2 * 3, 255);
+
+ sanei_genesys_generate_gamma_buffer(dev, sensor, 16, 65535, size, gamma.data());
+
+ // loop sending gamma tables NOTE: 0x01000000 not 0x10000000
+ for (i = 0; i < 3; i++) {
+ // clear corresponding GMM_N bit
+ uint8_t val = dev->interface->read_register(0xbd);
+ val &= ~(0x01 << i);
+ dev->interface->write_register(0xbd, val);
+
+ // clear corresponding GMM_F bit
+ val = dev->interface->read_register(0xbe);
+ val &= ~(0x01 << i);
+ dev->interface->write_register(0xbe, val);
+
+ // FIXME: currently the last word of each gamma table is not initialied, so to work around
+ // unstable data, just set it to 0 which is the most likely value of uninitialized memory
+ // (proper value is probably 0xff)
+ gamma[size * 2 * i + size * 2 - 2] = 0;
+ gamma[size * 2 * i + size * 2 - 1] = 0;
+
+ /* set GMM_Z */
+ dev->interface->write_register(0xc5+2*i, gamma[size*2*i+1]);
+ dev->interface->write_register(0xc6+2*i, gamma[size*2*i]);
+
+ dev->interface->write_ahb(0x01000000 + 0x200 * i, (size-1) * 2,
+ gamma.data() + i * size * 2+2);
+ }
+}
+
+static unsigned align_int_up(unsigned num, unsigned alignment)
+{
+ unsigned mask = alignment - 1;
+ if (num & mask)
+ num = (num & ~mask) + alignment;
+ return num;
+}
+
+void compute_session_buffer_sizes(AsicType asic, ScanSession& s)
+{
+ size_t line_bytes = s.output_line_bytes;
+ size_t line_bytes_stagger = s.output_line_bytes;
+
+ if (asic != AsicType::GL646) {
+ // BUG: this is historical artifact and should be removed. Note that buffer sizes affect
+ // how often we request the scanner for data and thus change the USB traffic.
+ line_bytes_stagger =
+ multiply_by_depth_ceil(s.optical_pixels, s.params.depth) * s.params.channels;
+ }
+
+ struct BufferConfig {
+ size_t* result_size = nullptr;
+ size_t lines = 0;
+ size_t lines_mult = 0;
+ size_t max_size = 0; // does not apply if 0
+ size_t stagger_lines = 0;
+
+ BufferConfig() = default;
+ BufferConfig(std::size_t* rs, std::size_t l, std::size_t lm, std::size_t ms,
+ std::size_t sl) :
+ result_size{rs},
+ lines{l},
+ lines_mult{lm},
+ max_size{ms},
+ stagger_lines{sl}
+ {}
+ };
+
+ std::array<BufferConfig, 4> configs;
+ if (asic == AsicType::GL124 || asic == AsicType::GL843) {
+ configs = { {
+ { &s.buffer_size_read, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
+ { &s.buffer_size_lines, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
+ { &s.buffer_size_shrink, 16, 1, 0, 0 },
+ { &s.buffer_size_out, 8, 1, 0, 0 },
+ } };
+ } else if (asic == AsicType::GL841) {
+ size_t max_buf = sanei_genesys_get_bulk_max_size(asic);
+ configs = { {
+ { &s.buffer_size_read, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines },
+ { &s.buffer_size_lines, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines },
+ { &s.buffer_size_shrink, 8, 1, max_buf, 0 },
+ { &s.buffer_size_out, 8, 1, 0, 0 },
+ } };
+ } else {
+ configs = { {
+ { &s.buffer_size_read, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
+ { &s.buffer_size_lines, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
+ { &s.buffer_size_shrink, 8, 1, 0, 0 },
+ { &s.buffer_size_out, 8, 1, 0, 0 },
+ } };
+ }
+
+ for (BufferConfig& config : configs) {
+ size_t buf_size = line_bytes * config.lines;
+ if (config.max_size > 0 && buf_size > config.max_size) {
+ buf_size = (config.max_size / line_bytes) * line_bytes;
+ }
+ buf_size *= config.lines_mult;
+ buf_size += line_bytes_stagger * config.stagger_lines;
+ *config.result_size = buf_size;
+ }
+}
+
+void compute_session_pipeline(const Genesys_Device* dev, ScanSession& s)
+{
+ auto channels = s.params.channels;
+ auto depth = s.params.depth;
+
+ s.pipeline_needs_reorder = true;
+ if (channels != 3 && depth != 16) {
+ s.pipeline_needs_reorder = false;
+ }
+#ifndef WORDS_BIGENDIAN
+ if (channels != 3 && depth == 16) {
+ s.pipeline_needs_reorder = false;
+ }
+ if (channels == 3 && depth == 16 && !dev->model->is_cis &&
+ dev->model->line_mode_color_order == ColorOrder::RGB)
+ {
+ s.pipeline_needs_reorder = false;
+ }
+#endif
+ if (channels == 3 && depth == 8 && !dev->model->is_cis &&
+ dev->model->line_mode_color_order == ColorOrder::RGB)
+ {
+ s.pipeline_needs_reorder = false;
+ }
+ s.pipeline_needs_ccd = s.max_color_shift_lines + s.num_staggered_lines > 0;
+ s.pipeline_needs_shrink = dev->settings.requested_pixels != s.output_pixels;
+}
+
+void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s,
+ const Genesys_Sensor& sensor)
+{
+ unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
+
+ if (dev->model->asic_type == AsicType::GL646) {
+
+ // startx cannot be below dummy pixel value
+ s.pixel_startx = sensor.dummy_pixel;
+ if (has_flag(s.params.flags, ScanFlag::USE_XCORRECTION) && sensor.ccd_start_xoffset > 0) {
+ s.pixel_startx = sensor.ccd_start_xoffset;
+ }
+ s.pixel_startx += s.params.startx;
+
+ if (sensor.stagger_config.stagger_at_resolution(s.params.xres, s.params.yres) > 0) {
+ s.pixel_startx |= 1;
+ }
+
+ s.pixel_endx = s.pixel_startx + s.optical_pixels;
+
+ s.pixel_startx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor;
+ s.pixel_endx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor;
+
+ } else if (dev->model->asic_type == AsicType::GL841) {
+ s.pixel_startx = ((sensor.ccd_start_xoffset + s.params.startx) * s.optical_resolution)
+ / sensor.optical_res;
+
+ s.pixel_startx += sensor.dummy_pixel + 1;
+
+ if (s.num_staggered_lines > 0 && (s.pixel_startx & 1) == 0) {
+ s.pixel_startx++;
+ }
+
+ /* In case of SHDAREA, we need to align start on pixel average factor, startx is
+ different than 0 only when calling for function to setup for scan, where shading data
+ needs to be align.
+
+ NOTE: we can check the value of the register here, because we don't set this bit
+ anywhere except in initialization.
+ */
+ const uint8_t REG_0x01_SHDAREA = 0x02;
+ if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) != 0) {
+ unsigned average_factor = s.optical_resolution / s.params.xres;
+ s.pixel_startx = align_multiple_floor(s.pixel_startx, average_factor);
+ }
+
+ s.pixel_endx = s.pixel_startx + s.optical_pixels;
+
+ } else if (dev->model->asic_type == AsicType::GL843) {
+
+ s.pixel_startx = (s.params.startx + sensor.dummy_pixel) / ccd_pixels_per_system_pixel;
+ s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel;
+
+ s.pixel_startx /= s.hwdpi_divisor;
+ s.pixel_endx /= s.hwdpi_divisor;
+
+ // in case of stagger we have to start at an odd coordinate
+ bool stagger_starts_even = dev->model->model_id == ModelId::CANON_8400F;
+ if (s.num_staggered_lines > 0) {
+ if (!stagger_starts_even && (s.pixel_startx & 1) == 0) {
+ s.pixel_startx++;
+ s.pixel_endx++;
+ } else if (stagger_starts_even && (s.pixel_startx & 1) != 0) {
+ s.pixel_startx++;
+ s.pixel_endx++;
+ }
+ }
+
+ } else if (dev->model->asic_type == AsicType::GL845 ||
+ dev->model->asic_type == AsicType::GL846 ||
+ dev->model->asic_type == AsicType::GL847)
+ {
+ s.pixel_startx = s.params.startx;
+
+ if (s.num_staggered_lines > 0) {
+ s.pixel_startx |= 1;
+ }
+
+ s.pixel_startx += sensor.ccd_start_xoffset * ccd_pixels_per_system_pixel;
+ s.pixel_endx = s.pixel_startx + s.optical_pixels_raw;
+
+ s.pixel_startx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel;
+ s.pixel_endx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel;
+
+ } else if (dev->model->asic_type == AsicType::GL124) {
+ s.pixel_startx = s.params.startx;
+
+ if (s.num_staggered_lines > 0) {
+ s.pixel_startx |= 1;
+ }
+
+ s.pixel_startx /= ccd_pixels_per_system_pixel;
+ // FIXME: should we add sensor.dummy_pxel to pixel_startx at this point?
+ s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel;
+
+ s.pixel_startx /= s.hwdpi_divisor * s.segment_count;
+ s.pixel_endx /= s.hwdpi_divisor * s.segment_count;
+
+ std::uint32_t segcnt = (sensor.custom_regs.get_value(gl124::REG_SEGCNT) << 16) +
+ (sensor.custom_regs.get_value(gl124::REG_SEGCNT + 1) << 8) +
+ sensor.custom_regs.get_value(gl124::REG_SEGCNT + 2);
+ if (s.pixel_endx == segcnt) {
+ s.pixel_endx = 0;
+ }
+ }
+
+ s.pixel_count_multiplier = sensor.pixel_count_multiplier;
+
+ s.pixel_startx *= sensor.pixel_count_multiplier;
+ s.pixel_endx *= sensor.pixel_count_multiplier;
+}
+
+void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor)
+{
+ DBG_HELPER(dbg);
+
+ (void) dev;
+ s.params.assert_valid();
+
+ if (s.params.depth != 8 && s.params.depth != 16) {
+ throw SaneException("Unsupported depth setting %d", s.params.depth);
+ }
+
+ unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
+
+ // compute optical and output resolutions
+
+ if (dev->model->asic_type == AsicType::GL843) {
+ // FIXME: this may be incorrect, but need more scanners to test
+ s.hwdpi_divisor = sensor.get_hwdpi_divisor_for_dpi(s.params.xres);
+ } else {
+ s.hwdpi_divisor = sensor.get_hwdpi_divisor_for_dpi(s.params.xres * ccd_pixels_per_system_pixel);
+ }
+
+ s.ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(s.params.xres);
+
+ if (dev->model->asic_type == AsicType::GL646) {
+ s.optical_resolution = sensor.optical_res;
+ } else {
+ s.optical_resolution = sensor.optical_res / s.ccd_size_divisor;
+ }
+ s.output_resolution = s.params.xres;
+
+ if (s.output_resolution > s.optical_resolution) {
+ throw std::runtime_error("output resolution higher than optical resolution");
+ }
+
+ // compute the number of optical pixels that will be acquired by the chip
+ s.optical_pixels = (s.params.pixels * s.optical_resolution) / s.output_resolution;
+ if (s.optical_pixels * s.output_resolution < s.params.pixels * s.optical_resolution) {
+ s.optical_pixels++;
+ }
+
+ if (dev->model->asic_type == AsicType::GL841) {
+ if (s.optical_pixels & 1)
+ s.optical_pixels++;
+ }
+
+ if (dev->model->asic_type == AsicType::GL646 && s.params.xres == 400) {
+ s.optical_pixels = (s.optical_pixels / 6) * 6;
+ }
+
+ if (dev->model->asic_type == AsicType::GL843) {
+ // ensure the number of optical pixels is divisible by 2.
+ // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number
+ s.optical_pixels = align_int_up(s.optical_pixels, 2 * s.ccd_size_divisor);
+
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
+ {
+ s.optical_pixels = align_int_up(s.optical_pixels, 16);
+ }
+ }
+
+ // after all adjustments on the optical pixels have been made, compute the number of pixels
+ // to retrieve from the chip
+ s.output_pixels = (s.optical_pixels * s.output_resolution) / s.optical_resolution;
+
+ // Note: staggering is not applied for calibration. Staggering starts at 2400 dpi
+ s.num_staggered_lines = 0;
+ if (!has_flag(s.params.flags, ScanFlag::IGNORE_LINE_DISTANCE))
+ {
+ s.num_staggered_lines = sensor.stagger_config.stagger_at_resolution(s.params.xres,
+ s.params.yres);
+ }
+
+ s.color_shift_lines_r = dev->model->ld_shift_r;
+ s.color_shift_lines_g = dev->model->ld_shift_g;
+ s.color_shift_lines_b = dev->model->ld_shift_b;
+
+ if (dev->model->motor_id == MotorId::G4050 && s.params.yres > 600) {
+ // it seems base_dpi of the G4050 motor is changed above 600 dpi
+ s.color_shift_lines_r = (s.color_shift_lines_r * 3800) / dev->motor.base_ydpi;
+ s.color_shift_lines_g = (s.color_shift_lines_g * 3800) / dev->motor.base_ydpi;
+ s.color_shift_lines_b = (s.color_shift_lines_b * 3800) / dev->motor.base_ydpi;
+ }
+
+ s.color_shift_lines_r = (s.color_shift_lines_r * s.params.yres) / dev->motor.base_ydpi;
+ s.color_shift_lines_g = (s.color_shift_lines_g * s.params.yres) / dev->motor.base_ydpi;
+ s.color_shift_lines_b = (s.color_shift_lines_b * s.params.yres) / dev->motor.base_ydpi;
+
+ s.max_color_shift_lines = 0;
+ if (s.params.channels > 1 && !has_flag(s.params.flags, ScanFlag::IGNORE_LINE_DISTANCE)) {
+ s.max_color_shift_lines = std::max(s.color_shift_lines_r, std::max(s.color_shift_lines_g,
+ s.color_shift_lines_b));
+ }
+
+ s.output_line_count = s.params.lines + s.max_color_shift_lines + s.num_staggered_lines;
+
+ s.output_channel_bytes = multiply_by_depth_ceil(s.output_pixels, s.params.depth);
+ s.output_line_bytes = s.output_channel_bytes * s.params.channels;
+
+ s.segment_count = sensor.get_segment_count();
+
+ s.optical_pixels_raw = s.optical_pixels;
+ s.output_line_bytes_raw = s.output_line_bytes;
+ s.conseq_pixel_dist = 0;
+
+ if (dev->model->asic_type == AsicType::GL845 ||
+ dev->model->asic_type == AsicType::GL846 ||
+ dev->model->asic_type == AsicType::GL847)
+ {
+ if (s.segment_count > 1) {
+ s.conseq_pixel_dist = sensor.segment_size;
+
+ // in case of multi-segments sensor, we have to add the width of the sensor crossed by
+ // the scan area
+ unsigned extra_segment_scan_area = align_multiple_ceil(s.conseq_pixel_dist, 2);
+ extra_segment_scan_area *= s.segment_count - 1;
+ extra_segment_scan_area *= s.hwdpi_divisor * s.segment_count;
+ extra_segment_scan_area *= ccd_pixels_per_system_pixel;
+
+ s.optical_pixels_raw += extra_segment_scan_area;
+ }
+
+ s.output_line_bytes_raw = multiply_by_depth_ceil(
+ (s.optical_pixels_raw * s.output_resolution) / sensor.optical_res / s.segment_count,
+ s.params.depth);
+ }
+
+ if (dev->model->asic_type == AsicType::GL841) {
+ if (dev->model->is_cis) {
+ s.output_line_bytes_raw = s.output_channel_bytes;
+ }
+ }
+
+ if (dev->model->asic_type == AsicType::GL124) {
+ if (dev->model->is_cis) {
+ s.output_line_bytes_raw = s.output_channel_bytes;
+ }
+ s.conseq_pixel_dist = s.output_pixels / s.ccd_size_divisor / s.segment_count;
+ }
+
+ if (dev->model->asic_type == AsicType::GL843) {
+ s.conseq_pixel_dist = s.output_pixels / s.segment_count;
+ }
+
+ s.output_segment_pixel_group_count = 0;
+ if (dev->model->asic_type == AsicType::GL124 ||
+ dev->model->asic_type == AsicType::GL843)
+ {
+ s.output_segment_pixel_group_count = multiply_by_depth_ceil(
+ s.output_pixels / s.ccd_size_divisor / s.segment_count, s.params.depth);
+ }
+ if (dev->model->asic_type == AsicType::GL845 ||
+ dev->model->asic_type == AsicType::GL846 ||
+ dev->model->asic_type == AsicType::GL847)
+ {
+ s.output_segment_pixel_group_count = multiply_by_depth_ceil(
+ s.optical_pixels / (s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel),
+ s.params.depth);
+ }
+
+ s.output_line_bytes_requested = multiply_by_depth_ceil(
+ s.params.get_requested_pixels() * s.params.channels, s.params.depth);
+
+ s.output_total_bytes_raw = s.output_line_bytes_raw * s.output_line_count;
+ s.output_total_bytes = s.output_line_bytes * s.output_line_count;
+
+ compute_session_buffer_sizes(dev->model->asic_type, s);
+ compute_session_pipeline(dev, s);
+ compute_session_pixel_offsets(dev, s, sensor);
+
+ if (dev->model->asic_type == AsicType::GL124 ||
+ dev->model->asic_type == AsicType::GL845 ||
+ dev->model->asic_type == AsicType::GL846)
+ {
+ s.enable_ledadd = (s.params.channels == 1 && dev->model->is_cis && dev->settings.true_gray);
+ }
+
+ if (dev->model->asic_type == AsicType::GL841 ||
+ dev->model->asic_type == AsicType::GL843)
+ {
+ // no 16 bit gamma for this ASIC
+ if (s.params.depth == 16) {
+ s.params.flags |= ScanFlag::DISABLE_GAMMA;
+ }
+ }
+
+ s.computed = true;
+
+ DBG(DBG_info, "%s ", __func__);
+ debug_dump(DBG_info, s);
+}
+
+static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& session)
+{
+ switch (asic) {
+ case AsicType::GL646:
+ // buffer not used on this chip set
+ return 1;
+
+ case AsicType::GL124:
+ // BUG: we shouldn't multiply by channels here nor divide by ccd_size_divisor
+ return session.output_line_bytes_raw / session.ccd_size_divisor * session.params.channels;
+
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847:
+ // BUG: we shouldn't multiply by channels here
+ return session.output_line_bytes_raw * session.params.channels;
+
+ case AsicType::GL843:
+ return session.output_line_bytes_raw * 2;
+
+ default:
+ throw SaneException("Unknown asic type");
+ }
+}
+
+static FakeBufferModel get_fake_usb_buffer_model(const ScanSession& session)
+{
+ FakeBufferModel model;
+ model.push_step(session.buffer_size_read, 1);
+
+ if (session.pipeline_needs_reorder) {
+ model.push_step(session.buffer_size_lines, session.output_line_bytes);
+ }
+ if (session.pipeline_needs_ccd) {
+ model.push_step(session.buffer_size_shrink, session.output_line_bytes);
+ }
+ if (session.pipeline_needs_shrink) {
+ model.push_step(session.buffer_size_out, session.output_line_bytes);
+ }
+
+ return model;
+}
+
+void build_image_pipeline(Genesys_Device* dev, const ScanSession& session)
+{
+ static unsigned s_pipeline_index = 0;
+
+ s_pipeline_index++;
+
+ auto format = create_pixel_format(session.params.depth,
+ dev->model->is_cis ? 1 : session.params.channels,
+ dev->model->line_mode_color_order);
+ auto depth = get_pixel_format_depth(format);
+ auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw);
+
+ auto read_data_from_usb = [dev](std::size_t size, std::uint8_t* data)
+ {
+ dev->interface->bulk_read_data(0x45, data, size);
+ return true;
+ };
+
+ auto lines = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1);
+
+ dev->pipeline.clear();
+
+ // FIXME: here we are complicating things for the time being to preserve the existing behaviour
+ // This allows to be sure that the changes to the image pipeline have not introduced
+ // regressions.
+
+ if (session.segment_count > 1) {
+ // BUG: we're reading one line too much
+ dev->pipeline.push_first_node<ImagePipelineNodeBufferedCallableSource>(
+ width, lines + 1, format,
+ get_usb_buffer_read_size(dev->model->asic_type, session), read_data_from_usb);
+
+ auto output_width = session.output_segment_pixel_group_count * session.segment_count;
+ dev->pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order,
+ session.conseq_pixel_dist,
+ 1, 1);
+ } else {
+ auto read_bytes_left_after_deseg = session.output_line_bytes * session.output_line_count;
+ if (dev->model->asic_type == AsicType::GL646) {
+ read_bytes_left_after_deseg *= dev->model->is_cis ? session.params.channels : 1;
+ }
+
+ dev->pipeline.push_first_node<ImagePipelineNodeBufferedGenesysUsb>(
+ width, lines, format, read_bytes_left_after_deseg,
+ get_fake_usb_buffer_model(session), read_data_from_usb);
+ }
+
+ if (DBG_LEVEL >= DBG_io2) {
+ dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
+ std::to_string(s_pipeline_index) +
+ "_0_before_swap.pnm");
+ }
+
+ if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && depth == 16) {
+ dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ }
+
+#ifdef WORDS_BIGENDIAN
+ if (depth == 16) {
+ dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ }
+#endif
+
+ if (DBG_LEVEL >= DBG_io2) {
+ dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
+ std::to_string(s_pipeline_index) +
+ "_1_after_swap.pnm");
+ }
+
+ if (dev->model->is_cis && session.params.channels == 3) {
+ dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);
+ }
+
+ if (dev->pipeline.get_output_format() == PixelFormat::BGR888) {
+ dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
+ }
+
+ if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) {
+ dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
+ }
+
+ if (session.max_color_shift_lines > 0 && session.params.channels == 3) {
+ dev->pipeline.push_node<ImagePipelineNodeComponentShiftLines>(
+ session.color_shift_lines_r,
+ session.color_shift_lines_g,
+ session.color_shift_lines_b);
+ }
+
+ if (DBG_LEVEL >= DBG_io2) {
+ dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
+ std::to_string(s_pipeline_index) +
+ "_2_after_shift.pnm");
+ }
+
+ if (session.num_staggered_lines > 0) {
+ std::vector<std::size_t> shifts{0, session.num_staggered_lines};
+ dev->pipeline.push_node<ImagePipelineNodePixelShiftLines>(shifts);
+ }
+
+ if (DBG_LEVEL >= DBG_io2) {
+ dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
+ std::to_string(s_pipeline_index) +
+ "_3_after_stagger.pnm");
+ }
+
+ if ((dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) &&
+ !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ {
+ dev->pipeline.push_node<ImagePipelineNodeCalibrate>(dev->dark_average_data,
+ dev->white_average_data);
+
+ if (DBG_LEVEL >= DBG_io2) {
+ dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
+ std::to_string(s_pipeline_index) +
+ "_4_after_calibrate.pnm");
+ }
+ }
+
+ if (session.output_pixels != session.params.get_requested_pixels()) {
+ dev->pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels());
+ }
+
+ auto read_from_pipeline = [dev](std::size_t size, std::uint8_t* out_data)
+ {
+ (void) size; // will be always equal to dev->pipeline.get_output_row_bytes()
+ return dev->pipeline.get_next_row_data(out_data);
+ };
+ dev->pipeline_buffer = ImageBuffer{dev->pipeline.get_output_row_bytes(),
+ read_from_pipeline};
+}
+
+std::uint8_t compute_frontend_gain_wolfson(float value, float target_value)
+{
+ /* the flow of data through the frontend ADC is as follows (see e.g. WM8192 datasheet)
+ input
+ -> apply offset (o = i + 260mV * (DAC[7:0]-127.5)/127.5) ->
+ -> apply gain (o = i * 208/(283-PGA[7:0])
+ -> ADC
+
+ Here we have some input data that was acquired with zero gain (PGA==0).
+ We want to compute gain such that the output would approach full ADC range (controlled by
+ target_value).
+
+ We want to solve the following for {PGA}:
+
+ {value} = {input} * 208 / (283 - 0)
+ {target_value} = {input} * 208 / (283 - {PGA})
+
+ The solution is the following equation:
+
+ {PGA} = 283 * (1 - {value} / {target_value})
+ */
+ float gain = value / target_value;
+ int code = static_cast<int>(283 * (1 - gain));
+ return clamp(code, 0, 255);
+}
+
+std::uint8_t compute_frontend_gain_analog_devices(float value, float target_value)
+{
+ /* The flow of data through the frontend ADC is as follows (see e.g. AD9826 datasheet)
+ input
+ -> apply offset (o = i + 300mV * (OFFSET[8] ? 1 : -1) * (OFFSET[7:0] / 127)
+ -> apply gain (o = i * 6 / (1 + 5 * ( 63 - PGA[5:0] ) / 63 ) )
+ -> ADC
+
+ We want to solve the following for {PGA}:
+
+ {value} = {input} * 6 / (1 + 5 * ( 63 - 0) / 63 ) )
+ {target_value} = {input} * 6 / (1 + 5 * ( 63 - {PGA}) / 63 ) )
+
+ The solution is the following equation:
+
+ {PGA} = (378 / 5) * ({target_value} - {value} / {target_value})
+ */
+ int code = static_cast<int>((378.0f / 5.0f) * ((target_value - value) / target_value));
+ return clamp(code, 0, 63);
+}
+
+std::uint8_t compute_frontend_gain(float value, float target_value,
+ FrontendType frontend_type)
+{
+ if (frontend_type == FrontendType::WOLFSON) {
+ return compute_frontend_gain_wolfson(value, target_value);
+ }
+ if (frontend_type == FrontendType::ANALOG_DEVICES) {
+ return compute_frontend_gain_analog_devices(value, target_value);
+ }
+ throw SaneException("Unknown frontend to compute gain for");
+}
+
+/** @brief initialize device
+ * Initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home. Designed for gl846+ ASICs.
+ * Detects cold boot (ie first boot since device plugged) in this case
+ * an extensice setup up is done at hardware level.
+ *
+ * @param dev device to initialize
+ * @param max_regs umber of maximum used registers
+ */
+void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/)
+{
+ DBG_HELPER(dbg);
+
+ uint8_t val;
+ bool cold = true;
+
+ // URB 16 control 0xc0 0x0c 0x8e 0x0b len 1 read 0x00 */
+ dev->interface->get_usb_device().control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER,
+ VALUE_GET_REGISTER, 0x00, 1, &val);
+
+ DBG (DBG_io2, "%s: value=0x%02x\n", __func__, val);
+ DBG (DBG_info, "%s: device is %s\n", __func__, (val & 0x08) ? "USB 1.0" : "USB2.0");
+ if (val & 0x08)
+ {
+ dev->usb_mode = 1;
+ }
+ else
+ {
+ dev->usb_mode = 2;
+ }
+
+ /* Check if the device has already been initialized and powered up. We read register 0x06 and
+ check PWRBIT, if reset scanner has been freshly powered up. This bit will be set to later
+ so that following reads can detect power down/up cycle
+ */
+ if (!is_testing_mode()) {
+ if (dev->interface->read_register(0x06) & 0x10) {
+ cold = false;
+ }
+ }
+ DBG (DBG_info, "%s: device is %s\n", __func__, cold ? "cold" : "warm");
+
+ /* don't do anything if backend is initialized and hardware hasn't been
+ * replug */
+ if (dev->already_initialized && !cold)
+ {
+ DBG (DBG_info, "%s: already initialized, nothing to do\n", __func__);
+ return;
+ }
+
+ // set up hardware and registers
+ dev->cmd_set->asic_boot(dev, cold);
+
+ /* now hardware part is OK, set up device struct */
+ dev->white_average_data.clear();
+ dev->dark_average_data.clear();
+
+ dev->settings.color_filter = ColorFilter::RED;
+
+ /* duplicate initial values into calibration registers */
+ dev->calib_reg = dev->reg;
+
+ const auto& sensor = sanei_genesys_find_sensor_any(dev);
+
+ // Set analog frontend
+ dev->cmd_set->set_fe(dev, sensor, AFE_INIT);
+
+ dev->already_initialized = true;
+
+ // Move to home if needed
+ dev->cmd_set->move_back_home(dev, true);
+ dev->set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ // Set powersaving (default = 15 minutes)
+ dev->cmd_set->set_powersaving(dev, 15);
+}
+
+void scanner_start_action(Genesys_Device& dev, bool start_motor)
+{
+ DBG_HELPER(dbg);
+ switch (dev.model->asic_type) {
+ case AsicType::GL646:
+ case AsicType::GL841:
+ case AsicType::GL843:
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847:
+ case AsicType::GL124:
+ break;
+ default:
+ throw SaneException("Unsupported chip");
+ }
+
+ if (start_motor) {
+ dev.interface->write_register(0x0f, 0x01);
+ } else {
+ dev.interface->write_register(0x0f, 0);
+ }
+}
+
+void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& sensor,
+ unsigned dpihw)
+{
+ // same across GL646, GL841, GL843, GL846, GL847, GL124
+ const uint8_t REG_0x05_DPIHW_MASK = 0xc0;
+ const uint8_t REG_0x05_DPIHW_600 = 0x00;
+ const uint8_t REG_0x05_DPIHW_1200 = 0x40;
+ const uint8_t REG_0x05_DPIHW_2400 = 0x80;
+ const uint8_t REG_0x05_DPIHW_4800 = 0xc0;
+
+ if (sensor.register_dpihw_override != 0) {
+ dpihw = sensor.register_dpihw_override;
+ }
+
+ uint8_t dpihw_setting;
+ switch (dpihw) {
+ case 600:
+ dpihw_setting = REG_0x05_DPIHW_600;
+ break;
+ case 1200:
+ dpihw_setting = REG_0x05_DPIHW_1200;
+ break;
+ case 2400:
+ dpihw_setting = REG_0x05_DPIHW_2400;
+ break;
+ case 4800:
+ dpihw_setting = REG_0x05_DPIHW_4800;
+ break;
+ default:
+ throw SaneException("Unknown dpihw value: %d", dpihw);
+ }
+ regs.set8_mask(0x05, dpihw_setting, REG_0x05_DPIHW_MASK);
+}
+
+void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs,
+ const SensorExposure& exposure)
+{
+ switch (asic_type) {
+ case AsicType::GL124: {
+ regs.set24(gl124::REG_EXPR, exposure.red);
+ regs.set24(gl124::REG_EXPG, exposure.green);
+ regs.set24(gl124::REG_EXPB, exposure.blue);
+ break;
+ }
+ case AsicType::GL646: {
+ regs.set16(gl646::REG_EXPR, exposure.red);
+ regs.set16(gl646::REG_EXPG, exposure.green);
+ regs.set16(gl646::REG_EXPB, exposure.blue);
+ break;
+ }
+ case AsicType::GL841: {
+ regs.set16(gl841::REG_EXPR, exposure.red);
+ regs.set16(gl841::REG_EXPG, exposure.green);
+ regs.set16(gl841::REG_EXPB, exposure.blue);
+ break;
+ }
+ case AsicType::GL843: {
+ regs.set16(gl843::REG_EXPR, exposure.red);
+ regs.set16(gl843::REG_EXPG, exposure.green);
+ regs.set16(gl843::REG_EXPB, exposure.blue);
+ break;
+ }
+ case AsicType::GL845:
+ case AsicType::GL846: {
+ regs.set16(gl846::REG_EXPR, exposure.red);
+ regs.set16(gl846::REG_EXPG, exposure.green);
+ regs.set16(gl846::REG_EXPB, exposure.blue);
+ break;
+ }
+ case AsicType::GL847: {
+ regs.set16(gl847::REG_EXPR, exposure.red);
+ regs.set16(gl847::REG_EXPG, exposure.green);
+ regs.set16(gl847::REG_EXPB, exposure.blue);
+ break;
+ }
+ default:
+ throw SaneException("Unsupported asic");
+ }
+}
+
+void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs)
+{
+ DBG_HELPER(dbg);
+ switch (asic_type) {
+ case AsicType::GL646: {
+ regs.find_reg(gl646::REG_0x01).value &= ~gl646::REG_0x01_SCAN;
+ break;
+ }
+ case AsicType::GL841: {
+ regs.find_reg(gl841::REG_0x01).value &= ~gl841::REG_0x01_SCAN;
+ break;
+ }
+ case AsicType::GL843: {
+ regs.find_reg(gl843::REG_0x01).value &= ~gl843::REG_0x01_SCAN;
+ break;
+ }
+ case AsicType::GL845:
+ case AsicType::GL846: {
+ regs.find_reg(gl846::REG_0x01).value &= ~gl846::REG_0x01_SCAN;
+ break;
+ }
+ case AsicType::GL847: {
+ regs.find_reg(gl847::REG_0x01).value &= ~gl847::REG_0x01_SCAN;
+ break;
+ }
+ case AsicType::GL124: {
+ regs.find_reg(gl124::REG_0x01).value &= ~gl124::REG_0x01_SCAN;
+ break;
+ }
+ default:
+ throw SaneException("Unsupported asic");
+ }
+}
+
+bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& regs)
+{
+ switch (asic_type) {
+ case AsicType::GL646:
+ return static_cast<bool>(regs.get8(gl646::REG_0x06) & gl646::REG_0x06_GAIN4);
+ case AsicType::GL841:
+ return static_cast<bool>(regs.get8(gl841::REG_0x06) & gl841::REG_0x06_GAIN4);
+ case AsicType::GL843:
+ return static_cast<bool>(regs.get8(gl843::REG_0x06) & gl843::REG_0x06_GAIN4);
+ case AsicType::GL845:
+ case AsicType::GL846:
+ return static_cast<bool>(regs.get8(gl846::REG_0x06) & gl846::REG_0x06_GAIN4);
+ case AsicType::GL847:
+ return static_cast<bool>(regs.get8(gl847::REG_0x06) & gl847::REG_0x06_GAIN4);
+ case AsicType::GL124:
+ return static_cast<bool>(regs.get8(gl124::REG_0x06) & gl124::REG_0x06_GAIN4);
+ default:
+ throw SaneException("Unsupported chipset");
+ }
+}
+
+/**
+ * Wait for the scanning head to park
+ */
+void sanei_genesys_wait_for_home(Genesys_Device* dev)
+{
+ DBG_HELPER(dbg);
+
+ /* clear the parking status whatever the outcome of the function */
+ dev->parking = false;
+
+ if (is_testing_mode()) {
+ return;
+ }
+
+ // read initial status, if head isn't at home and motor is on we are parking, so we wait.
+ // gl847/gl124 need 2 reads for reliable results
+ auto status = scanner_read_status(*dev);
+ dev->interface->sleep_ms(10);
+ status = scanner_read_status(*dev);
+
+ if (status.is_at_home) {
+ DBG (DBG_info,
+ "%s: already at home\n", __func__);
+ return;
+ }
+
+ unsigned timeout_ms = 200000;
+ unsigned elapsed_ms = 0;
+ do
+ {
+ dev->interface->sleep_ms(100);
+ elapsed_ms += 100;
+
+ status = scanner_read_status(*dev);
+ } while (elapsed_ms < timeout_ms && !status.is_at_home);
+
+ /* if after the timeout, head is still not parked, error out */
+ if (elapsed_ms >= timeout_ms && !status.is_at_home) {
+ DBG (DBG_error, "%s: failed to reach park position in %dseconds\n", __func__,
+ timeout_ms / 1000);
+ throw SaneException(SANE_STATUS_IO_ERROR, "failed to reach park position");
+ }
+}
+
+/** @brief motor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at full step and at a reference exposure. Use first entry
+ * by default.
+ * @param motors motor profile database
+ * @param motor_type motor id
+ * @param exposure exposure time
+ * @return a pointer to a Motor_Profile struct
+ */
+const Motor_Profile& sanei_genesys_get_motor_profile(const std::vector<Motor_Profile>& motors,
+ MotorId motor_id, int exposure)
+{
+ int idx;
+
+ idx=-1;
+ for (std::size_t i = 0; i < motors.size(); ++i) {
+ // exact match
+ if (motors[i].motor_id == motor_id && motors[i].exposure==exposure) {
+ return motors[i];
+ }
+
+ // closest match
+ if (motors[i].motor_id == motor_id) {
+ /* if profile exposure is higher than the required one,
+ * the entry is a candidate for the closest match */
+ if (motors[i].exposure == 0 || motors[i].exposure >= exposure)
+ {
+ if(idx<0)
+ {
+ /* no match found yet */
+ idx=i;
+ }
+ else
+ {
+ /* test for better match */
+ if(motors[i].exposure<motors[idx].exposure)
+ {
+ idx=i;
+ }
+ }
+ }
+ }
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default motor profile\n",__func__);
+ idx=0;
+ }
+
+ return motors[idx];
+}
+
+MotorSlopeTable sanei_genesys_slope_table(AsicType asic_type, int dpi, int exposure, int base_dpi,
+ unsigned step_multiplier,
+ const Motor_Profile& motor_profile)
+{
+ unsigned target_speed_w = ((exposure * dpi) / base_dpi);
+
+ auto table = create_slope_table(motor_profile.slope, target_speed_w, motor_profile.step_type,
+ step_multiplier, 2 * step_multiplier,
+ get_slope_table_max_size(asic_type));
+ return table;
+}
+
+MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier,
+ const Motor_Profile& motor_profile)
+{
+ return create_slope_table(motor_profile.slope, motor_profile.slope.max_speed_w,
+ motor_profile.step_type,
+ step_multiplier, 2 * step_multiplier,
+ get_slope_table_max_size(asic_type));
+}
+
+/** @brief returns the lowest possible ydpi for the device
+ * Parses device entry to find lowest motor dpi.
+ * @param dev device description
+ * @return lowest motor resolution
+ */
+int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev)
+{
+ const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method);
+ return resolution_settings.get_min_resolution_y();
+}
+
+/** @brief returns the lowest possible dpi for the device
+ * Parses device entry to find lowest motor or sensor dpi.
+ * @param dev device description
+ * @return lowest motor resolution
+ */
+int sanei_genesys_get_lowest_dpi(Genesys_Device *dev)
+{
+ const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method);
+ return std::min(resolution_settings.get_min_resolution_x(),
+ resolution_settings.get_min_resolution_y());
+}
+
+/** @brief check is a cache entry may be used
+ * Compares current settings with the cache entry and return
+ * true if they are compatible.
+ * A calibration cache is compatible if color mode and x dpi match the user
+ * requested scan. In the case of CIS scanners, dpi isn't a criteria.
+ * flatbed cache entries are considred too old and then expires if they
+ * are older than the expiration time option, forcing calibration at least once
+ * then given time. */
+bool sanei_genesys_is_compatible_calibration(Genesys_Device* dev,
+ const ScanSession& session,
+ const Genesys_Calibration_Cache* cache,
+ bool for_overwrite)
+{
+ DBG_HELPER(dbg);
+#ifdef HAVE_SYS_TIME_H
+ struct timeval time;
+#endif
+
+ bool compatible = true;
+
+ const auto& dev_params = session.params;
+
+ if (dev_params.scan_method != cache->params.scan_method) {
+ dbg.vlog(DBG_io, "incompatible: scan_method %d vs. %d\n",
+ static_cast<unsigned>(dev_params.scan_method),
+ static_cast<unsigned>(cache->params.scan_method));
+ compatible = false;
+ }
+
+ if (dev_params.xres != cache->params.xres) {
+ dbg.vlog(DBG_io, "incompatible: params.xres %d vs. %d\n",
+ dev_params.xres, cache->params.xres);
+ compatible = false;
+ }
+
+ if (dev_params.yres != cache->params.yres) {
+ // exposure depends on selected sensor and we select the sensor according to yres
+ dbg.vlog(DBG_io, "incompatible: params.yres %d vs. %d\n",
+ dev_params.yres, cache->params.yres);
+ compatible = false;
+ }
+
+ if (dev_params.channels != cache->params.channels) {
+ // exposure depends on total number of pixels at least on gl841
+ dbg.vlog(DBG_io, "incompatible: params.channels %d vs. %d\n",
+ dev_params.channels, cache->params.channels);
+ compatible = false;
+ }
+
+ if (dev_params.startx != cache->params.startx) {
+ // exposure depends on total number of pixels at least on gl841
+ dbg.vlog(DBG_io, "incompatible: params.startx %d vs. %d\n",
+ dev_params.startx, cache->params.startx);
+ compatible = false;
+ }
+
+ if (dev_params.pixels != cache->params.pixels) {
+ // exposure depends on total number of pixels at least on gl841
+ dbg.vlog(DBG_io, "incompatible: params.pixels %d vs. %d\n",
+ dev_params.pixels, cache->params.pixels);
+ compatible = false;
+ }
+
+ if (!compatible)
+ {
+ DBG (DBG_proc, "%s: completed, non compatible cache\n", __func__);
+ return false;
+ }
+
+ /* a cache entry expires after afetr expiration time for non sheetfed scanners */
+ /* this is not taken into account when overwriting cache entries */
+#ifdef HAVE_SYS_TIME_H
+ if (!for_overwrite && dev->settings.expiration_time >=0)
+ {
+ gettimeofday(&time, nullptr);
+ if ((time.tv_sec - cache->last_calibration > dev->settings.expiration_time*60)
+ && !dev->model->is_sheetfed
+ && (dev->settings.scan_method == ScanMethod::FLATBED))
+ {
+ DBG (DBG_proc, "%s: expired entry, non compatible cache\n", __func__);
+ return false;
+ }
+ }
+#endif
+
+ return true;
+}
+
+/** @brief build lookup table for digital enhancements
+ * Function to build a lookup table (LUT), often
+ used by scanners to implement brightness/contrast/gamma
+ or by backends to speed binarization/thresholding
+
+ offset and slope inputs are -127 to +127
+
+ slope rotates line around central input/output val,
+ 0 makes horizontal line
+
+ pos zero neg
+ . x . . x
+ . x . . x
+ out . x .xxxxxxxxxxx . x
+ . x . . x
+ ....x....... ............ .......x....
+ in in in
+
+ offset moves line vertically, and clamps to output range
+ 0 keeps the line crossing the center of the table
+
+ high low
+ . xxxxxxxx .
+ . x .
+ out x . x
+ . . x
+ ............ xxxxxxxx....
+ in in
+
+ out_min/max provide bounds on output values,
+ useful when building thresholding lut.
+ 0 and 255 are good defaults otherwise.
+ * @param lut pointer where to store the generated lut
+ * @param in_bits number of bits for in values
+ * @param out_bits number of bits of out values
+ * @param out_min minimal out value
+ * @param out_max maximal out value
+ * @param slope slope of the generated data
+ * @param offset offset of the generated data
+ */
+void sanei_genesys_load_lut(unsigned char* lut,
+ int in_bits, int out_bits,
+ int out_min, int out_max,
+ int slope, int offset)
+{
+ DBG_HELPER(dbg);
+ int i, j;
+ double shift, rise;
+ int max_in_val = (1 << in_bits) - 1;
+ int max_out_val = (1 << out_bits) - 1;
+ uint8_t *lut_p8 = lut;
+ uint16_t* lut_p16 = reinterpret_cast<std::uint16_t*>(lut);
+
+ /* slope is converted to rise per unit run:
+ * first [-127,127] to [-.999,.999]
+ * then to [-PI/4,PI/4] then [0,PI/2]
+ * then take the tangent (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ auto pi_4 = M_PI / 4.0;
+ rise = std::tan(static_cast<double>(slope) / 128 * pi_4 + pi_4) * max_out_val / max_in_val;
+
+ /* line must stay vertically centered, so figure
+ * out vertical offset at central input value */
+ shift = static_cast<double>(max_out_val) / 2 - (rise * max_in_val / 2);
+
+ /* convert the user offset setting to scale of output
+ * first [-127,127] to [-1,1]
+ * then to [-max_out_val/2,max_out_val/2]*/
+ shift += static_cast<double>(offset) / 127 * max_out_val / 2;
+
+ for (i = 0; i <= max_in_val; i++)
+ {
+ j = static_cast<int>(rise * i + shift);
+
+ /* cap data to required range */
+ if (j < out_min)
+ {
+ j = out_min;
+ }
+ else if (j > out_max)
+ {
+ j = out_max;
+ }
+
+ /* copy result according to bit depth */
+ if (out_bits <= 8)
+ {
+ *lut_p8 = j;
+ lut_p8++;
+ }
+ else
+ {
+ *lut_p16 = j;
+ lut_p16++;
+ }
+ }
+}
+
+} // namespace genesys
diff --git a/backend/genesys/low.h b/backend/genesys/low.h
new file mode 100644
index 0000000..d7f5dd2
--- /dev/null
+++ b/backend/genesys/low.h
@@ -0,0 +1,525 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
+ Parts of the structs have been taken from the gt68xx backend by
+ Sergey Vlasov <vsu@altlinux.ru> et al.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef GENESYS_LOW_H
+#define GENESYS_LOW_H
+
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <stddef.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MKDIR
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+
+#include "../include/_stdint.h"
+
+#include "device.h"
+#include "enums.h"
+#include "error.h"
+#include "fwd.h"
+#include "usb_device.h"
+#include "sensor.h"
+#include "serialize.h"
+#include "settings.h"
+#include "static_init.h"
+#include "status.h"
+#include "register.h"
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#define GENESYS_RED 0
+#define GENESYS_GREEN 1
+#define GENESYS_BLUE 2
+
+/* Flags */
+#define GENESYS_FLAG_UNTESTED (1 << 0) /**< Print a warning for these scanners */
+#define GENESYS_FLAG_14BIT_GAMMA (1 << 1) /**< use 14bit Gamma table instead of 12 */
+#define GENESYS_FLAG_XPA (1 << 3)
+#define GENESYS_FLAG_SKIP_WARMUP (1 << 4) /**< skip genesys_warmup() */
+/** @brief offset calibration flag
+ * signals that the scanner does offset calibration. In this case off_calibration() and
+ * coarse_gain_calibration() functions must be implemented
+ */
+#define GENESYS_FLAG_OFFSET_CALIBRATION (1 << 5)
+#define GENESYS_FLAG_SEARCH_START (1 << 6) /**< do start search before scanning */
+#define GENESYS_FLAG_REPARK (1 << 7) /**< repark head (and check for lock) by
+ moving without scanning */
+#define GENESYS_FLAG_DARK_CALIBRATION (1 << 8) /**< do dark calibration */
+
+#define GENESYS_FLAG_MUST_WAIT (1 << 10) /**< tells wether the scanner must wait for the head when parking */
+
+
+#define GENESYS_FLAG_HAS_UTA (1 << 11) /**< scanner has a transparency adapter */
+
+#define GENESYS_FLAG_DARK_WHITE_CALIBRATION (1 << 12) /**< yet another calibration method. does white and dark shading in one run, depending on a black and a white strip*/
+#define GENESYS_FLAG_CUSTOM_GAMMA (1 << 13) /**< allow custom gamma tables */
+#define GENESYS_FLAG_NO_CALIBRATION (1 << 14) /**< allow scanners to use skip the calibration, needed for sheetfed scanners */
+#define GENESYS_FLAG_SIS_SENSOR (1 << 16) /**< handling of multi-segments sensors in software */
+#define GENESYS_FLAG_SHADING_NO_MOVE (1 << 17) /**< scanner doesn't move sensor during shading calibration */
+#define GENESYS_FLAG_SHADING_REPARK (1 << 18) /**< repark head between shading scans */
+#define GENESYS_FLAG_FULL_HWDPI_MODE (1 << 19) /**< scanner always use maximum hw dpi to setup the sensor */
+// scanner has infrared transparency scanning capability
+#define GENESYS_FLAG_HAS_UTA_INFRARED (1 << 20)
+// scanner calibration is handled on the host side
+#define GENESYS_FLAG_CALIBRATION_HOST_SIDE (1 << 21)
+#define GENESYS_FLAG_16BIT_DATA_INVERTED (1 << 22)
+
+#define GENESYS_HAS_NO_BUTTONS 0 /**< scanner has no supported button */
+#define GENESYS_HAS_SCAN_SW (1 << 0) /**< scanner has SCAN button */
+#define GENESYS_HAS_FILE_SW (1 << 1) /**< scanner has FILE button */
+#define GENESYS_HAS_COPY_SW (1 << 2) /**< scanner has COPY button */
+#define GENESYS_HAS_EMAIL_SW (1 << 3) /**< scanner has EMAIL button */
+#define GENESYS_HAS_PAGE_LOADED_SW (1 << 4) /**< scanner has paper in detection */
+#define GENESYS_HAS_OCR_SW (1 << 5) /**< scanner has OCR button */
+#define GENESYS_HAS_POWER_SW (1 << 6) /**< scanner has power button */
+#define GENESYS_HAS_CALIBRATE (1 << 7) /**< scanner has 'calibrate' software button to start calibration */
+#define GENESYS_HAS_EXTRA_SW (1 << 8) /**< scanner has extra function button */
+
+/* USB control message values */
+#define REQUEST_TYPE_IN (USB_TYPE_VENDOR | USB_DIR_IN)
+#define REQUEST_TYPE_OUT (USB_TYPE_VENDOR | USB_DIR_OUT)
+#define REQUEST_REGISTER 0x0c
+#define REQUEST_BUFFER 0x04
+#define VALUE_BUFFER 0x82
+#define VALUE_SET_REGISTER 0x83
+#define VALUE_READ_REGISTER 0x84
+#define VALUE_WRITE_REGISTER 0x85
+#define VALUE_INIT 0x87
+#define GPIO_OUTPUT_ENABLE 0x89
+#define GPIO_READ 0x8a
+#define GPIO_WRITE 0x8b
+#define VALUE_BUF_ENDACCESS 0x8c
+#define VALUE_GET_REGISTER 0x8e
+#define INDEX 0x00
+
+/* todo: used?
+#define VALUE_READ_STATUS 0x86
+*/
+
+/* Read/write bulk data/registers */
+#define BULK_OUT 0x01
+#define BULK_IN 0x00
+#define BULK_RAM 0x00
+#define BULK_REGISTER 0x11
+
+#define BULKOUT_MAXSIZE 0xF000
+
+/* AFE values */
+#define AFE_INIT 1
+#define AFE_SET 2
+#define AFE_POWER_SAVE 4
+
+#define LOWORD(x) ((uint16_t)((x) & 0xffff))
+#define HIWORD(x) ((uint16_t)((x) >> 16))
+#define LOBYTE(x) ((uint8_t)((x) & 0xFF))
+#define HIBYTE(x) ((uint8_t)((x) >> 8))
+
+/* Global constants */
+/* TODO: emove this leftover of early backend days */
+#define MOTOR_SPEED_MAX 350
+#define DARK_VALUE 0
+
+#define MAX_RESOLUTIONS 13
+#define MAX_DPI 4
+
+namespace genesys {
+
+struct Genesys_USB_Device_Entry {
+
+ Genesys_USB_Device_Entry(unsigned v, unsigned p, const Genesys_Model& m) :
+ vendor(v), product(p), model(m)
+ {}
+
+ // USB vendor identifier
+ std::uint16_t vendor;
+ // USB product identifier
+ std::uint16_t product;
+ // Scanner model information
+ Genesys_Model model;
+};
+
+/**
+ * structure for motor database
+ */
+struct Motor_Profile
+{
+ MotorId motor_id;
+ int exposure; // used only to select the wanted motor
+ StepType step_type; // default step type for given exposure
+ MotorSlope slope;
+};
+
+extern StaticInit<std::vector<Motor_Profile>> gl843_motor_profiles;
+extern StaticInit<std::vector<Motor_Profile>> gl846_motor_profiles;
+extern StaticInit<std::vector<Motor_Profile>> gl847_motor_profiles;
+extern StaticInit<std::vector<Motor_Profile>> gl124_motor_profiles;
+
+/*--------------------------------------------------------------------------*/
+/* common functions needed by low level specific functions */
+/*--------------------------------------------------------------------------*/
+
+inline GenesysRegister* sanei_genesys_get_address(Genesys_Register_Set* regs, uint16_t addr)
+{
+ auto* ret = regs->find_reg_address(addr);
+ if (ret == nullptr) {
+ DBG(DBG_error, "%s: failed to find address for register 0x%02x, crash expected !\n",
+ __func__, addr);
+ }
+ return ret;
+}
+
+extern void sanei_genesys_init_cmd_set(Genesys_Device* dev);
+
+// reads the status of the scanner
+Status scanner_read_status(Genesys_Device& dev);
+
+// reads the status of the scanner reliably. This is done by reading the status twice. The first
+// read sometimes returns the home sensor as engaged when this is not true.
+Status scanner_read_reliable_status(Genesys_Device& dev);
+
+// reads and prints the scanner status
+void scanner_read_print_status(Genesys_Device& dev);
+
+void debug_print_status(DebugMessageHelper& dbg, Status status);
+
+extern void sanei_genesys_write_ahb(Genesys_Device* dev, uint32_t addr, uint32_t size,
+ uint8_t* data);
+
+extern void sanei_genesys_init_structs (Genesys_Device * dev);
+
+const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev);
+const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, unsigned dpi,
+ unsigned channels, ScanMethod scan_method);
+bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels,
+ ScanMethod scan_method);
+Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigned dpi,
+ unsigned channels, ScanMethod scan_method);
+
+std::vector<std::reference_wrapper<const Genesys_Sensor>>
+ sanei_genesys_find_sensors_all(Genesys_Device* dev, ScanMethod scan_method);
+std::vector<std::reference_wrapper<Genesys_Sensor>>
+ sanei_genesys_find_sensors_all_for_write(Genesys_Device* dev, ScanMethod scan_method);
+
+extern void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ int pixels_per_line);
+
+extern void sanei_genesys_read_valid_words(Genesys_Device* dev, unsigned int* steps);
+
+extern void sanei_genesys_read_scancnt(Genesys_Device* dev, unsigned int* steps);
+
+extern void sanei_genesys_read_feed_steps(Genesys_Device* dev, unsigned int* steps);
+
+void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, bool set);
+
+void sanei_genesys_set_motor_power(Genesys_Register_Set& regs, bool set);
+
+bool should_enable_gamma(const ScanSession& session, const Genesys_Sensor& sensor);
+
+/** Calculates the values of the Z{1,2}MOD registers. They are a phase correction to synchronize
+ with the line clock during acceleration and deceleration.
+
+ two_table is true if moving is done by two tables, false otherwise.
+
+ acceleration_steps is the number of steps for acceleration, i.e. the number written to
+ REG_STEPNO.
+
+ move_steps number of steps to move, i.e. the number written to REG_FEEDL.
+
+ buffer_acceleration_steps, the number of steps for acceleration when buffer condition is met,
+ i.e. the number written to REG_FWDSTEP.
+*/
+void sanei_genesys_calculate_zmod(bool two_table,
+ uint32_t exposure_time,
+ const std::vector<uint16_t>& slope_table,
+ unsigned acceleration_steps,
+ unsigned move_steps,
+ unsigned buffer_acceleration_steps,
+ uint32_t* out_z1, uint32_t* out_z2);
+
+extern void sanei_genesys_set_buffer_address(Genesys_Device* dev, uint32_t addr);
+
+unsigned sanei_genesys_get_bulk_max_size(AsicType asic_type);
+
+SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, float ydpi, StepType step_type,
+ int endpixel, int led_exposure);
+
+MotorSlopeTable sanei_genesys_create_slope_table3(AsicType asic_type, const Genesys_Motor& motor,
+ StepType step_type, int exposure_time,
+ unsigned yres);
+
+void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
+ std::vector<uint16_t>& gamma_table, float gamma);
+
+std::vector<uint16_t> get_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ int color);
+
+void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor);
+
+extern void sanei_genesys_stop_motor(Genesys_Device* dev);
+
+extern void sanei_genesys_search_reference_point(Genesys_Device* dev, Genesys_Sensor& sensor,
+ const uint8_t* src_data, int start_pixel, int dpi,
+ int width, int height);
+
+// moves the scan head by the specified steps at the motor base dpi
+void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, Direction direction);
+
+void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home);
+void scanner_move_back_home_ta(Genesys_Device& dev);
+
+void scanner_clear_scan_and_feed_counts(Genesys_Device& dev);
+
+extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data,
+ std::size_t length);
+
+extern void sanei_genesys_write_pnm_file(const char* filename, const std::uint8_t* data, int depth,
+ int channels, int pixels_per_line, int lines);
+
+void sanei_genesys_write_pnm_file(const char* filename, const Image& image);
+
+extern void sanei_genesys_write_pnm_file16(const char* filename, const uint16_t *data, unsigned channels,
+ unsigned pixels_per_line, unsigned lines);
+
+void wait_until_buffer_non_empty(Genesys_Device* dev, bool check_status_twice = false);
+
+extern void sanei_genesys_read_data_from_scanner(Genesys_Device* dev, uint8_t* data, size_t size);
+
+Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session,
+ std::size_t total_bytes);
+
+void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs,
+ const SensorExposure& exposure);
+
+void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs);
+
+void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& sensor,
+ unsigned dpihw);
+
+inline uint16_t sanei_genesys_fixup_exposure_value(uint16_t value)
+{
+ if ((value & 0xff00) == 0) {
+ value |= 0x100;
+ }
+ if ((value & 0x00ff) == 0) {
+ value |= 0x1;
+ }
+ return value;
+}
+
+inline SensorExposure sanei_genesys_fixup_exposure(SensorExposure exposure)
+{
+ exposure.red = sanei_genesys_fixup_exposure_value(exposure.red);
+ exposure.green = sanei_genesys_fixup_exposure_value(exposure.green);
+ exposure.blue = sanei_genesys_fixup_exposure_value(exposure.blue);
+ return exposure;
+}
+
+bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& regs);
+
+extern void sanei_genesys_wait_for_home(Genesys_Device* dev);
+
+extern void sanei_genesys_asic_init(Genesys_Device* dev, bool cold);
+
+void scanner_start_action(Genesys_Device& dev, bool start_motor);
+void scanner_stop_action(Genesys_Device& dev);
+void scanner_stop_action_no_move(Genesys_Device& dev, Genesys_Register_Set& regs);
+
+bool scanner_is_motor_stopped(Genesys_Device& dev);
+
+const Motor_Profile& sanei_genesys_get_motor_profile(const std::vector<Motor_Profile>& motors,
+ MotorId motor_id, int exposure);
+
+MotorSlopeTable sanei_genesys_slope_table(AsicType asic_type, int dpi, int exposure, int base_dpi,
+ unsigned step_multiplier,
+ const Motor_Profile& motor_profile);
+
+MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier,
+ const Motor_Profile& motor_profile);
+
+/** @brief find lowest motor resolution for the device.
+ * Parses the resolution list for motor and
+ * returns the lowest value.
+ * @param dev for which to find the lowest motor resolution
+ * @return the lowest available motor resolution for the device
+ */
+extern
+int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev);
+
+/** @brief find lowest resolution for the device.
+ * Parses the resolution list for motor and sensor and
+ * returns the lowest value.
+ * @param dev for which to find the lowest resolution
+ * @return the lowest available resolution for the device
+ */
+extern
+int sanei_genesys_get_lowest_dpi(Genesys_Device *dev);
+
+bool sanei_genesys_is_compatible_calibration(Genesys_Device* dev,
+ const ScanSession& session,
+ const Genesys_Calibration_Cache* cache,
+ bool for_overwrite);
+
+extern void sanei_genesys_load_lut(unsigned char* lut,
+ int in_bits, int out_bits,
+ int out_min, int out_max,
+ int slope, int offset);
+
+extern void sanei_genesys_generate_gamma_buffer(Genesys_Device* dev,
+ const Genesys_Sensor& sensor,
+ int bits,
+ int max,
+ int size,
+ uint8_t* gamma);
+
+void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor);
+
+void build_image_pipeline(Genesys_Device* dev, const ScanSession& session);
+
+std::uint8_t compute_frontend_gain(float value, float target_value,
+ FrontendType frontend_type);
+
+template<class T>
+inline T abs_diff(T a, T b)
+{
+ if (a < b) {
+ return b - a;
+ } else {
+ return a - b;
+ }
+}
+
+inline uint64_t align_multiple_floor(uint64_t x, uint64_t multiple)
+{
+ return (x / multiple) * multiple;
+}
+
+inline uint64_t align_multiple_ceil(uint64_t x, uint64_t multiple)
+{
+ return ((x + multiple - 1) / multiple) * multiple;
+}
+
+inline uint64_t multiply_by_depth_ceil(uint64_t pixels, uint64_t depth)
+{
+ if (depth == 1) {
+ return (pixels / 8) + ((pixels % 8) ? 1 : 0);
+ } else {
+ return pixels * (depth / 8);
+ }
+}
+
+template<class T>
+inline T clamp(const T& value, const T& lo, const T& hi)
+{
+ if (value < lo)
+ return lo;
+ if (value > hi)
+ return hi;
+ return value;
+}
+
+/*---------------------------------------------------------------------------*/
+/* ASIC specific functions declarations */
+/*---------------------------------------------------------------------------*/
+
+extern StaticInit<std::vector<Genesys_Sensor>> s_sensors;
+extern StaticInit<std::vector<Genesys_Frontend>> s_frontends;
+extern StaticInit<std::vector<Genesys_Gpo>> s_gpo;
+extern StaticInit<std::vector<Genesys_Motor>> s_motors;
+extern StaticInit<std::vector<Genesys_USB_Device_Entry>> s_usb_devices;
+
+void genesys_init_sensor_tables();
+void genesys_init_frontend_tables();
+void genesys_init_gpo_tables();
+void genesys_init_motor_tables();
+void genesys_init_motor_profile_tables();
+void genesys_init_usb_device_tables();
+
+template<class T>
+void debug_dump(unsigned level, const T& value)
+{
+ std::stringstream out;
+ out << value;
+ DBG(level, "%s\n", out.str().c_str());
+}
+
+} // namespace genesys
+
+#endif /* not GENESYS_LOW_H */
diff --git a/backend/genesys/motor.cpp b/backend/genesys/motor.cpp
new file mode 100644
index 0000000..910266a
--- /dev/null
+++ b/backend/genesys/motor.cpp
@@ -0,0 +1,180 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "motor.h"
+#include "utilities.h"
+#include <cmath>
+
+namespace genesys {
+
+unsigned MotorSlope::get_table_step_shifted(unsigned step, StepType step_type) const
+{
+ // first two steps are always equal to the initial speed
+ if (step < 2) {
+ return initial_speed_w >> static_cast<unsigned>(step_type);
+ }
+ step--;
+
+ float initial_speed_v = 1.0f / initial_speed_w;
+ float speed_v = std::sqrt(initial_speed_v * initial_speed_v + 2 * acceleration * step);
+ return static_cast<unsigned>(1.0f / speed_v) >> static_cast<unsigned>(step_type);
+}
+
+float compute_acceleration_for_steps(unsigned initial_w, unsigned max_w, unsigned steps)
+{
+ float initial_speed_v = 1.0f / static_cast<float>(initial_w);
+ float max_speed_v = 1.0f / static_cast<float>(max_w);
+ return (max_speed_v * max_speed_v - initial_speed_v * initial_speed_v) / (2 * steps);
+}
+
+
+MotorSlope MotorSlope::create_from_steps(unsigned initial_w, unsigned max_w,
+ unsigned steps)
+{
+ MotorSlope slope;
+ slope.initial_speed_w = initial_w;
+ slope.max_speed_w = max_w;
+ slope.acceleration = compute_acceleration_for_steps(initial_w, max_w, steps);
+ return slope;
+}
+
+void MotorSlopeTable::slice_steps(unsigned count)
+{
+ if (count >= table.size() || count > steps_count) {
+ throw SaneException("Excepssive steps count");
+ }
+ steps_count = count;
+}
+
+unsigned get_slope_table_max_size(AsicType asic_type)
+{
+ switch (asic_type) {
+ case AsicType::GL646:
+ case AsicType::GL841: return 255;
+ case AsicType::GL843:
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847:
+ case AsicType::GL124: return 1024;
+ default:
+ throw SaneException("Unknown asic type");
+ }
+}
+
+MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_speed_w,
+ StepType step_type, unsigned steps_alignment,
+ unsigned min_size, unsigned max_size)
+{
+ DBG_HELPER_ARGS(dbg, "target_speed_w: %d, step_type: %d, steps_alignment: %d, min_size: %d",
+ target_speed_w, static_cast<unsigned>(step_type), steps_alignment, min_size);
+ MotorSlopeTable table;
+
+ unsigned step_shift = static_cast<unsigned>(step_type);
+
+ unsigned target_speed_shifted_w = target_speed_w >> step_shift;
+ unsigned max_speed_shifted_w = slope.max_speed_w >> step_shift;
+
+ if (target_speed_shifted_w < max_speed_shifted_w) {
+ dbg.log(DBG_warn, "failed to reach target speed");
+ }
+
+ unsigned final_speed = std::max(target_speed_shifted_w, max_speed_shifted_w);
+
+ table.table.reserve(max_size);
+
+ while (table.table.size() < max_size - 1) {
+ unsigned current = slope.get_table_step_shifted(table.table.size(), step_type);
+ if (current <= final_speed) {
+ break;
+ }
+ table.table.push_back(current);
+ table.pixeltime_sum += current;
+ }
+
+ // make sure the target speed (or the max speed if target speed is too high) is present in
+ // the table
+ table.table.push_back(final_speed);
+ table.pixeltime_sum += table.table.back();
+
+ // fill the table up to the specified size
+ while (table.table.size() < max_size - 1 &&
+ (table.table.size() % steps_alignment != 0 || table.table.size() < min_size))
+ {
+ table.table.push_back(table.table.back());
+ table.pixeltime_sum += table.table.back();
+ }
+
+ table.steps_count = table.table.size();
+
+ // fill the rest of the table with the final speed
+ table.table.resize(max_size, final_speed);
+
+ return table;
+}
+
+std::ostream& operator<<(std::ostream& out, const MotorSlope& slope)
+{
+ out << "MotorSlope{\n"
+ << " initial_speed_w: " << slope.initial_speed_w << '\n'
+ << " max_speed_w: " << slope.max_speed_w << '\n'
+ << " a: " << slope.acceleration << '\n'
+ << '}';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor)
+{
+ out << "Genesys_Motor{\n"
+ << " id: " << static_cast<unsigned>(motor.id) << '\n'
+ << " base_ydpi: " << motor.base_ydpi << '\n'
+ << " optical_ydpi: " << motor.optical_ydpi << '\n'
+ << " slopes: "
+ << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorSlope",
+ motor.slopes))
+ << '}';
+ return out;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/motor.h b/backend/genesys/motor.h
new file mode 100644
index 0000000..d80da6d
--- /dev/null
+++ b/backend/genesys/motor.h
@@ -0,0 +1,177 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_MOTOR_H
+#define BACKEND_GENESYS_MOTOR_H
+
+#include <cstdint>
+#include <vector>
+#include "enums.h"
+
+namespace genesys {
+
+/* Describes a motor acceleration curve.
+
+ Definitions:
+ v - speed in steps per pixeltime
+ w - speed in pixel times per step. w = 1 / v
+ a - acceleration in steps per pixeltime squared
+ s - distance travelled in steps
+ t - time in pixeltime
+
+ The physical mode defines the curve in physical quantities. We asssume that the scanner head
+ accelerates from standstill to the target speed uniformly. Then:
+
+ v(t) = v(0) + a * t (2)
+
+ Where `a` is acceleration, `t` is time. Also we can calculate the travelled distance `s`:
+
+ s(t) = v(0) * t + a * t^2 / 2 (3)
+
+ The actual motor slope is defined as the duration of each motor step. That means we need to
+ define speed in terms of travelled distance.
+
+ Solving (3) for `t` gives:
+
+ sqrt( v(0)^2 + 2 * a * s ) - v(0)
+ t(s) = --------------------------------- (4)
+ a
+
+ Combining (4) and (2) will yield:
+
+ v(s) = sqrt( v(0)^2 + 2 * a * s ) (5)
+
+ The data in the slope struct MotorSlope corresponds to the above in the following way:
+
+ maximum_start_speed is `w(0) = 1/v(0)`
+
+ maximum_speed is defines maximum speed which should not be exceeded
+
+ minimum_steps is not used
+
+ g is `a`
+
+ Given the start and target speeds on a known motor curve, `a` can be computed as follows:
+
+ v(t1)^2 - v(t0)^2
+ a = ----------------- (6)
+ 2 * s
+
+ Here `v(t0)` and `v(t1)` are the start and target speeds and `s` is the number of step required
+ to reach the target speeds.
+*/
+struct MotorSlope
+{
+ // initial speed in pixeltime per step
+ unsigned initial_speed_w = 0;
+
+ // max speed in pixeltime per step
+ unsigned max_speed_w = 0;
+
+ // maximum number of steps in the table
+ unsigned max_step_count;
+
+ // acceleration in steps per pixeltime squared.
+ float acceleration = 0;
+
+ unsigned get_table_step_shifted(unsigned step, StepType step_type) const;
+
+ static MotorSlope create_from_steps(unsigned initial_w, unsigned max_w,
+ unsigned steps);
+};
+
+struct MotorSlopeTable
+{
+ std::vector<std::uint16_t> table;
+ unsigned steps_count = 0;
+ unsigned pixeltime_sum = 0;
+
+ void slice_steps(unsigned count);
+};
+
+unsigned get_slope_table_max_size(AsicType asic_type);
+
+MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_speed_w,
+ StepType step_type, unsigned steps_alignment,
+ unsigned min_size, unsigned max_size);
+
+std::ostream& operator<<(std::ostream& out, const MotorSlope& slope);
+
+
+struct Genesys_Motor
+{
+ Genesys_Motor() = default;
+
+ // id of the motor description
+ MotorId id = MotorId::UNKNOWN;
+ // motor base steps. Unit: 1/inch
+ int base_ydpi = 0;
+ // maximum resolution in y-direction. Unit: 1/inch
+ int optical_ydpi = 0;
+ // slopes to derive individual slopes from
+ std::vector<MotorSlope> slopes;
+
+ MotorSlope& get_slope(StepType step_type)
+ {
+ return slopes[static_cast<unsigned>(step_type)];
+ }
+
+ const MotorSlope& get_slope(StepType step_type) const
+ {
+ return slopes[static_cast<unsigned>(step_type)];
+ }
+
+ StepType max_step_type() const
+ {
+ if (slopes.empty()) {
+ throw std::runtime_error("Slopes table is empty");
+ }
+ return static_cast<StepType>(slopes.size() - 1);
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor);
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_MOTOR_H
diff --git a/backend/genesys/register.h b/backend/genesys/register.h
new file mode 100644
index 0000000..bbc7ec8
--- /dev/null
+++ b/backend/genesys/register.h
@@ -0,0 +1,537 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_REGISTER_H
+#define BACKEND_GENESYS_REGISTER_H
+
+#include "utilities.h"
+
+#include <algorithm>
+#include <climits>
+#include <cstdint>
+#include <iostream>
+#include <iomanip>
+#include <stdexcept>
+#include <vector>
+
+namespace genesys {
+
+template<class Value>
+struct Register
+{
+ std::uint16_t address = 0;
+ Value value = 0;
+};
+
+using GenesysRegister = Register<std::uint8_t>;
+
+template<class Value>
+inline bool operator<(const Register<Value>& lhs, const Register<Value>& rhs)
+{
+ return lhs.address < rhs.address;
+}
+
+struct GenesysRegisterSetState
+{
+ bool is_lamp_on = false;
+ bool is_xpa_on = false;
+ bool is_motor_on = false;
+ bool is_xpa_motor_on = false;
+};
+
+template<class Value>
+class RegisterContainer
+{
+public:
+
+ enum Options {
+ SEQUENTIAL = 1
+ };
+
+ using RegisterType = Register<Value>;
+ using ContainerType = std::vector<RegisterType>;
+ using iterator = typename ContainerType::iterator;
+ using const_iterator = typename ContainerType::const_iterator;
+
+ RegisterContainer() = default;
+
+ RegisterContainer(Options opts) : RegisterContainer()
+ {
+ if ((opts & SEQUENTIAL) == SEQUENTIAL) {
+ sorted_ = false;
+ }
+ }
+
+ void init_reg(std::uint16_t address, Value default_value)
+ {
+ if (find_reg_index(address) >= 0) {
+ set(address, default_value);
+ return;
+ }
+ RegisterType reg;
+ reg.address = address;
+ reg.value = default_value;
+ registers_.push_back(reg);
+ if (sorted_)
+ std::sort(registers_.begin(), registers_.end());
+ }
+
+ bool has_reg(std::uint16_t address) const
+ {
+ return find_reg_index(address) >= 0;
+ }
+
+ void remove_reg(std::uint16_t address)
+ {
+ int i = find_reg_index(address);
+ if (i < 0) {
+ throw std::runtime_error("the register does not exist");
+ }
+ registers_.erase(registers_.begin() + i);
+ }
+
+ RegisterType& find_reg(std::uint16_t address)
+ {
+ int i = find_reg_index(address);
+ if (i < 0) {
+ throw std::runtime_error("the register does not exist");
+ }
+ return registers_[i];
+ }
+
+ const RegisterType& find_reg(std::uint16_t address) const
+ {
+ int i = find_reg_index(address);
+ if (i < 0) {
+ throw std::runtime_error("the register does not exist");
+ }
+ return registers_[i];
+ }
+
+ void set(std::uint16_t address, Value value)
+ {
+ find_reg(address).value = value;
+ }
+
+ Value get(std::uint16_t address) const
+ {
+ return find_reg(address).value;
+ }
+
+ void reserve(std::size_t size) { registers_.reserve(size); }
+ void clear() { registers_.clear(); }
+ std::size_t size() const { return registers_.size(); }
+
+ iterator begin() { return registers_.begin(); }
+ const_iterator begin() const { return registers_.begin(); }
+
+ iterator end() { return registers_.end(); }
+ const_iterator end() const { return registers_.end(); }
+
+private:
+ int find_reg_index(std::uint16_t address) const
+ {
+ if (!sorted_) {
+ for (std::size_t i = 0; i < registers_.size(); i++) {
+ if (registers_[i].address == address) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ RegisterType search;
+ search.address = address;
+ auto it = std::lower_bound(registers_.begin(), registers_.end(), search);
+ if (it == registers_.end())
+ return -1;
+ if (it->address != address)
+ return -1;
+ return std::distance(registers_.begin(), it);
+ }
+
+ // registers are stored in a sorted vector
+ bool sorted_ = true;
+ std::vector<RegisterType> registers_;
+};
+
+template<class Value>
+std::ostream& operator<<(std::ostream& out, const RegisterContainer<Value>& container)
+{
+ StreamStateSaver state_saver{out};
+
+ out << "RegisterContainer{\n";
+ out << std::hex;
+ out.fill('0');
+
+ for (const auto& reg : container) {
+ unsigned address_width = sizeof(reg.address) * 2;
+ unsigned value_width = sizeof(reg.value) * 2;
+
+ out << " 0x" << std::setw(address_width) << static_cast<unsigned>(reg.address)
+ << " = 0x" << std::setw(value_width) << static_cast<unsigned>(reg.value) << '\n';
+ }
+ out << "}";
+ return out;
+}
+
+class Genesys_Register_Set
+{
+public:
+ static constexpr unsigned MAX_REGS = 256;
+
+ using ContainerType = RegisterContainer<std::uint8_t>;
+ using iterator = typename ContainerType::iterator;
+ using const_iterator = typename ContainerType::const_iterator;
+
+ // FIXME: this shouldn't live here, but in a separate struct that contains Genesys_Register_Set
+ GenesysRegisterSetState state;
+
+ enum Options {
+ SEQUENTIAL = 1
+ };
+
+ Genesys_Register_Set()
+ {
+ registers_.reserve(MAX_REGS);
+ }
+
+ // by default the register set is sorted by address. In certain cases it's importand to send
+ // the registers in certain order: use the SEQUENTIAL option for that
+ Genesys_Register_Set(Options opts) : registers_{static_cast<ContainerType::Options>(opts)}
+ {
+ registers_.reserve(MAX_REGS);
+ }
+
+ const ContainerType& registers() const
+ {
+ return registers_;
+ }
+
+ void init_reg(std::uint16_t address, std::uint8_t default_value)
+ {
+ registers_.init_reg(address, default_value);
+ }
+
+ bool has_reg(std::uint16_t address) const { return registers_.has_reg(address); }
+
+ void remove_reg(std::uint16_t address) { registers_.remove_reg(address); }
+
+ GenesysRegister& find_reg(std::uint16_t address)
+ {
+ return registers_.find_reg(address);
+ }
+
+ const GenesysRegister& find_reg(std::uint16_t address) const
+ {
+ return registers_.find_reg(address);
+ }
+
+ GenesysRegister* find_reg_address(std::uint16_t address)
+ {
+ return &find_reg(address);
+ }
+
+ const GenesysRegister* find_reg_address(std::uint16_t address) const
+ {
+ return &find_reg(address);
+ }
+
+ void set8(std::uint16_t address, std::uint8_t value)
+ {
+ find_reg(address).value = value;
+ }
+
+ void set8_mask(std::uint16_t address, std::uint8_t value, std::uint8_t mask)
+ {
+ auto& reg = find_reg(address);
+ reg.value = (reg.value & ~mask) | value;
+ }
+
+ void set16(std::uint16_t address, std::uint16_t value)
+ {
+ find_reg(address).value = (value >> 8) & 0xff;
+ find_reg(address + 1).value = value & 0xff;
+ }
+
+ void set24(std::uint16_t address, std::uint32_t value)
+ {
+ find_reg(address).value = (value >> 16) & 0xff;
+ find_reg(address + 1).value = (value >> 8) & 0xff;
+ find_reg(address + 2).value = value & 0xff;
+ }
+
+ std::uint8_t get8(std::uint16_t address) const
+ {
+ return find_reg(address).value;
+ }
+
+ std::uint16_t get16(std::uint16_t address) const
+ {
+ return (find_reg(address).value << 8) | find_reg(address + 1).value;
+ }
+
+ std::uint32_t get24(std::uint16_t address) const
+ {
+ return (find_reg(address).value << 16) |
+ (find_reg(address + 1).value << 8) |
+ find_reg(address + 2).value;
+ }
+
+ void clear() { registers_.clear(); }
+ std::size_t size() const { return registers_.size(); }
+
+ iterator begin() { return registers_.begin(); }
+ const_iterator begin() const { return registers_.begin(); }
+
+ iterator end() { return registers_.end(); }
+ const_iterator end() const { return registers_.end(); }
+
+private:
+
+ // registers are stored in a sorted vector
+ ContainerType registers_;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const Genesys_Register_Set& regs)
+{
+ out << regs.registers();
+ return out;
+}
+
+template<class Value>
+struct RegisterSetting
+{
+ using ValueType = Value;
+ using AddressType = std::uint16_t;
+
+ RegisterSetting() = default;
+
+ RegisterSetting(AddressType p_address, ValueType p_value) :
+ address(p_address), value(p_value)
+ {}
+
+ RegisterSetting(AddressType p_address, ValueType p_value, ValueType p_mask) :
+ address(p_address), value(p_value), mask(p_mask)
+ {}
+
+ AddressType address = 0;
+ ValueType value = 0;
+ ValueType mask = 0xff;
+
+ bool operator==(const RegisterSetting& other) const
+ {
+ return address == other.address && value == other.value && mask == other.mask;
+ }
+};
+
+using GenesysRegisterSetting = RegisterSetting<std::uint8_t>;
+using GenesysRegisterSetting16 = RegisterSetting<std::uint16_t>;
+
+template<class Stream, class Value>
+void serialize(Stream& str, RegisterSetting<Value>& reg)
+{
+ serialize(str, reg.address);
+ serialize(str, reg.value);
+ serialize(str, reg.mask);
+}
+
+template<class Value>
+class RegisterSettingSet
+{
+public:
+ using ValueType = Value;
+ using SettingType = RegisterSetting<ValueType>;
+ using AddressType = typename SettingType::AddressType;
+
+ using container = std::vector<SettingType>;
+ using iterator = typename container::iterator;
+ using const_iterator = typename container::const_iterator;
+
+ RegisterSettingSet() = default;
+ RegisterSettingSet(std::initializer_list<SettingType> ilist) :
+ registers_(ilist)
+ {}
+
+ iterator begin() { return registers_.begin(); }
+ const_iterator begin() const { return registers_.begin(); }
+ iterator end() { return registers_.end(); }
+ const_iterator end() const { return registers_.end(); }
+
+ SettingType& operator[](std::size_t i) { return registers_[i]; }
+ const SettingType& operator[](std::size_t i) const { return registers_[i]; }
+
+ std::size_t size() const { return registers_.size(); }
+ bool empty() const { return registers_.empty(); }
+ void clear() { registers_.clear(); }
+
+ void push_back(SettingType reg) { registers_.push_back(reg); }
+
+ void merge(const RegisterSettingSet& other)
+ {
+ for (const auto& reg : other) {
+ set_value(reg.address, reg.value);
+ }
+ }
+
+ SettingType& find_reg(AddressType address)
+ {
+ int i = find_reg_index(address);
+ if (i < 0) {
+ throw std::runtime_error("the register does not exist");
+ }
+ return registers_[i];
+ }
+
+ const SettingType& find_reg(AddressType address) const
+ {
+ int i = find_reg_index(address);
+ if (i < 0) {
+ throw std::runtime_error("the register does not exist");
+ }
+ return registers_[i];
+ }
+
+ ValueType get_value(AddressType address) const
+ {
+ int index = find_reg_index(address);
+ if (index >= 0) {
+ return registers_[index].value;
+ }
+ throw std::out_of_range("Unknown register");
+ }
+
+ void set_value(AddressType address, ValueType value)
+ {
+ int index = find_reg_index(address);
+ if (index >= 0) {
+ registers_[index].value = value;
+ return;
+ }
+ push_back(SettingType(address, value));
+ }
+
+ template<class V>
+ friend void serialize(std::istream& str, RegisterSettingSet<V>& reg);
+ template<class V>
+ friend void serialize(std::ostream& str, RegisterSettingSet<V>& reg);
+
+ bool operator==(const RegisterSettingSet& other) const
+ {
+ return registers_ == other.registers_;
+ }
+
+private:
+
+ int find_reg_index(AddressType address) const
+ {
+ for (std::size_t i = 0; i < registers_.size(); i++) {
+ if (registers_[i].address == address) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ std::vector<SettingType> registers_;
+};
+
+using GenesysRegisterSettingSet = RegisterSettingSet<std::uint8_t>;
+using GenesysRegisterSettingSet16 = RegisterSettingSet<std::uint16_t>;
+
+template<class Value>
+std::ostream& operator<<(std::ostream& out, const RegisterSettingSet<Value>& container)
+{
+ StreamStateSaver state_saver{out};
+
+ out << "RegisterSettingSet{\n";
+ out << std::hex;
+ out.fill('0');
+
+ for (const auto& reg : container) {
+ unsigned address_width = sizeof(reg.address) * 2;
+ unsigned value_width = sizeof(reg.value) * 2;
+ unsigned mask_width = sizeof(reg.mask) * 2;
+
+ out << " 0x" << std::setw(address_width) << static_cast<unsigned>(reg.address)
+ << " = 0x" << std::setw(value_width) << static_cast<unsigned>(reg.value)
+ << " & 0x" << std::setw(mask_width) << static_cast<unsigned>(reg.mask) << '\n';
+ }
+ out << "}";
+ return out;
+}
+
+template<class Value>
+inline void serialize(std::istream& str, RegisterSettingSet<Value>& reg)
+{
+ using AddressType = typename RegisterSetting<Value>::AddressType;
+
+ reg.clear();
+ const std::size_t max_register_address = 1 << (sizeof(AddressType) * CHAR_BIT);
+ serialize(str, reg.registers_, max_register_address);
+}
+
+template<class Value>
+inline void serialize(std::ostream& str, RegisterSettingSet<Value>& reg)
+{
+ serialize(str, reg.registers_);
+}
+
+template<class F, class Value>
+void apply_registers_ordered(const RegisterSettingSet<Value>& set,
+ std::initializer_list<std::uint16_t> order, F f)
+{
+ for (std::uint16_t addr : order) {
+ f(set.find_reg(addr));
+ }
+ for (const auto& reg : set) {
+ if (std::find(order.begin(), order.end(), reg.address) != order.end()) {
+ continue;
+ }
+ f(reg);
+ }
+}
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_REGISTER_H
diff --git a/backend/genesys/register_cache.h b/backend/genesys/register_cache.h
new file mode 100644
index 0000000..dce701a
--- /dev/null
+++ b/backend/genesys/register_cache.h
@@ -0,0 +1,92 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_REGISTER_CACHE_H
+#define BACKEND_GENESYS_REGISTER_CACHE_H
+
+#include "register.h"
+
+namespace genesys {
+
+template<class Value>
+class RegisterCache
+{
+public:
+ void update(std::uint16_t address, Value value)
+ {
+ if (regs_.has_reg(address)) {
+ regs_.set(address, value);
+ } else {
+ regs_.init_reg(address, value);
+ }
+ }
+
+ void update(const Genesys_Register_Set& regs)
+ {
+ for (const auto& reg : regs) {
+ update(reg.address, reg.value);
+ }
+ }
+
+ Value get(std::uint16_t address) const
+ {
+ return regs_.get(address);
+ }
+
+private:
+ RegisterContainer<Value> regs_;
+
+ template<class V>
+ friend std::ostream& operator<<(std::ostream& out, const RegisterCache<V>& cache);
+};
+
+template<class Value>
+std::ostream& operator<<(std::ostream& out, const RegisterCache<Value>& cache)
+{
+ out << cache.regs_;
+ return out;
+}
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_LINE_BUFFER_H
diff --git a/backend/genesys/row_buffer.h b/backend/genesys/row_buffer.h
new file mode 100644
index 0000000..e1a0c82
--- /dev/null
+++ b/backend/genesys/row_buffer.h
@@ -0,0 +1,214 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_LINE_BUFFER_H
+#define BACKEND_GENESYS_LINE_BUFFER_H
+
+#include "error.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstddef>
+#include <vector>
+
+namespace genesys {
+
+class RowBuffer
+{
+public:
+ RowBuffer(std::size_t line_bytes) : row_bytes_{line_bytes} {}
+ RowBuffer(const RowBuffer&) = default;
+ RowBuffer& operator=(const RowBuffer&) = default;
+ ~RowBuffer() = default;
+
+ const std::uint8_t* get_row_ptr(std::size_t y) const
+ {
+ if (y >= height()) {
+ throw SaneException("y %zu is out of range", y);
+ }
+ return data_.data() + row_bytes_ * get_row_index(y);
+ }
+
+ std::uint8_t* get_row_ptr(std::size_t y)
+ {
+ if (y >= height()) {
+ throw SaneException("y %zu is out of range", y);
+ }
+ return data_.data() + row_bytes_ * get_row_index(y);
+ }
+
+ const std::uint8_t* get_front_row_ptr() const { return get_row_ptr(0); }
+ std::uint8_t* get_front_row_ptr() { return get_row_ptr(0); }
+ const std::uint8_t* get_back_row_ptr() const { return get_row_ptr(height() - 1); }
+ std::uint8_t* get_back_row_ptr() { return get_row_ptr(height() - 1); }
+
+ bool empty() const { return is_linear_ && first_ == last_; }
+
+ bool full()
+ {
+ if (is_linear_) {
+ return last_ == buffer_end_;
+ }
+ return first_ == last_;
+ }
+
+ bool is_linear() const { return is_linear_; }
+
+ void linearize()
+ {
+ if (!is_linear_) {
+ std::rotate(data_.begin(), data_.begin() + row_bytes_ * first_, data_.end());
+ last_ = height();
+ first_ = 0;
+ is_linear_ = true;
+ }
+ }
+
+ void pop_front()
+ {
+ if (empty()) {
+ throw SaneException("Trying to pop out of empty() line buffer");
+ }
+
+ first_++;
+ if (first_ == last_) {
+ first_ = 0;
+ last_ = 0;
+ is_linear_ = true;
+ } else if (first_ == buffer_end_) {
+ first_ = 0;
+ is_linear_ = true;
+ }
+ }
+
+ void push_front()
+ {
+ if (height() + 1 >= height_capacity()) {
+ ensure_capacity(std::max<std::size_t>(1, height() * 2));
+ }
+
+ if (first_ == 0) {
+ is_linear_ = false;
+ first_ = buffer_end_;
+ }
+ first_--;
+ }
+
+ void pop_back()
+ {
+ if (empty()) {
+ throw SaneException("Trying to pop out of empty() line buffer");
+ }
+ if (last_ == 0) {
+ last_ = buffer_end_;
+ is_linear_ = true;
+ }
+ last_--;
+ if (first_ == last_) {
+ first_ = 0;
+ last_ = 0;
+ is_linear_ = true;
+ }
+ }
+
+ void push_back()
+ {
+ if (height() + 1 >= height_capacity()) {
+ ensure_capacity(std::max<std::size_t>(1, height() * 2));
+ }
+
+ if (last_ == buffer_end_) {
+ is_linear_ = false;
+ last_ = 0;
+ }
+ last_++;
+ }
+
+ std::size_t row_bytes() const { return row_bytes_; }
+
+ std::size_t height() const
+ {
+ if (!is_linear_) {
+ return last_ + buffer_end_ - first_;
+ }
+ return last_ - first_;
+ }
+
+ std::size_t height_capacity() const { return buffer_end_; }
+
+ void clear()
+ {
+ first_ = 0;
+ last_ = 0;
+ }
+
+private:
+ std::size_t get_row_index(std::size_t index) const
+ {
+ if (index >= buffer_end_ - first_) {
+ return index - (buffer_end_ - first_);
+ }
+ return index + first_;
+ }
+
+ void ensure_capacity(std::size_t capacity)
+ {
+ if (capacity < height_capacity())
+ return;
+ linearize();
+ data_.resize(capacity * row_bytes_);
+ buffer_end_ = capacity;
+ }
+
+private:
+ std::size_t row_bytes_ = 0;
+ std::size_t first_ = 0;
+ std::size_t last_ = 0;
+ std::size_t buffer_end_ = 0;
+ bool is_linear_ = true;
+ std::vector<std::uint8_t> data_;
+};
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_LINE_BUFFER_H
diff --git a/backend/genesys/scanner_interface.cpp b/backend/genesys/scanner_interface.cpp
new file mode 100644
index 0000000..0b60b66
--- /dev/null
+++ b/backend/genesys/scanner_interface.cpp
@@ -0,0 +1,52 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "scanner_interface.h"
+
+namespace genesys {
+
+ScannerInterface::~ScannerInterface() = default;
+
+} // namespace genesys
diff --git a/backend/genesys/scanner_interface.h b/backend/genesys/scanner_interface.h
new file mode 100644
index 0000000..03c7132
--- /dev/null
+++ b/backend/genesys/scanner_interface.h
@@ -0,0 +1,112 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_SCANNER_INTERFACE_H
+#define BACKEND_GENESYS_SCANNER_INTERFACE_H
+
+#include "fwd.h"
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace genesys {
+
+// Represents an interface through which all low level operations are performed.
+class ScannerInterface
+{
+public:
+ enum Flags {
+ FLAG_NONE = 0,
+ FLAG_SWAP_REGISTERS = 1 << 0,
+ FLAG_SMALL_ADDRESS = 1 << 1
+ };
+
+ virtual ~ScannerInterface();
+
+ virtual bool is_mock() const = 0;
+
+ virtual std::uint8_t read_register(std::uint16_t address) = 0;
+ virtual void write_register(std::uint16_t address, std::uint8_t value) = 0;
+ virtual void write_registers(const Genesys_Register_Set& regs) = 0;
+
+ virtual void write_0x8c(std::uint8_t index, std::uint8_t value) = 0;
+ virtual void bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) = 0;
+ virtual void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) = 0;
+
+ // GL646, GL841, GL843 have different ways to write to RAM and to gamma tables
+ // FIXME: remove flags when updating tests
+ virtual void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags = FLAG_NONE) = 0;
+
+ virtual void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags = FLAG_NONE) = 0;
+
+ // GL845, GL846, GL847 and GL124 have a uniform way to write to RAM tables
+ virtual void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) = 0;
+
+ virtual std::uint16_t read_fe_register(std::uint8_t address) = 0;
+ virtual void write_fe_register(std::uint8_t address, std::uint16_t value) = 0;
+
+ virtual IUsbDevice& get_usb_device() = 0;
+
+ // sleeps the specified number of microseconds. Will not sleep if testing mode is enabled.
+ virtual void sleep_us(unsigned microseconds) = 0;
+
+ void sleep_ms(unsigned milliseconds)
+ {
+ sleep_us(milliseconds * 1000);
+ }
+
+ virtual void record_progress_message(const char* msg) = 0;
+
+ virtual void record_slope_table(unsigned table_nr, const std::vector<std::uint16_t>& steps) = 0;
+
+ virtual void record_key_value(const std::string& key, const std::string& value) = 0;
+
+ virtual void test_checkpoint(const std::string& name) = 0;
+};
+
+} // namespace genesys
+
+#endif
diff --git a/backend/genesys/scanner_interface_usb.cpp b/backend/genesys/scanner_interface_usb.cpp
new file mode 100644
index 0000000..d4d83dd
--- /dev/null
+++ b/backend/genesys/scanner_interface_usb.cpp
@@ -0,0 +1,515 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "scanner_interface_usb.h"
+#include "low.h"
+#include <thread>
+
+namespace genesys {
+
+ScannerInterfaceUsb::~ScannerInterfaceUsb() = default;
+
+ScannerInterfaceUsb::ScannerInterfaceUsb(Genesys_Device* dev) : dev_{dev} {}
+
+bool ScannerInterfaceUsb::is_mock() const
+{
+ return false;
+}
+
+std::uint8_t ScannerInterfaceUsb::read_register(std::uint16_t address)
+{
+ DBG_HELPER(dbg);
+
+ std::uint8_t value = 0;
+
+ if (dev_->model->asic_type == AsicType::GL847 ||
+ dev_->model->asic_type == AsicType::GL845 ||
+ dev_->model->asic_type == AsicType::GL846 ||
+ dev_->model->asic_type == AsicType::GL124)
+ {
+ std::uint8_t value2x8[2];
+ std::uint16_t address16 = 0x22 + (address << 8);
+
+ std::uint16_t usb_value = VALUE_GET_REGISTER;
+ if (address > 0xff) {
+ usb_value |= 0x100;
+ }
+
+ usb_dev_.control_msg(REQUEST_TYPE_IN, REQUEST_BUFFER, usb_value, address16, 2, value2x8);
+
+ // check usb link status
+ if (value2x8[1] != 0x55) {
+ throw SaneException(SANE_STATUS_IO_ERROR, "invalid read, scanner unplugged?");
+ }
+
+ DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value2x8[0]);
+
+ value = value2x8[0];
+
+ } else {
+
+ if (address > 0xff) {
+ throw SaneException("Invalid register address 0x%04x", address);
+ }
+
+ std::uint8_t address8 = address & 0xff;
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX,
+ 1, &address8);
+ usb_dev_.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_READ_REGISTER, INDEX,
+ 1, &value);
+ }
+
+ DBG(DBG_proc, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value);
+ return value;
+}
+
+void ScannerInterfaceUsb::write_register(std::uint16_t address, std::uint8_t value)
+{
+ DBG_HELPER_ARGS(dbg, "address: 0x%04x, value: 0x%02x", static_cast<unsigned>(address),
+ static_cast<unsigned>(value));
+
+ if (dev_->model->asic_type == AsicType::GL847 ||
+ dev_->model->asic_type == AsicType::GL845 ||
+ dev_->model->asic_type == AsicType::GL846 ||
+ dev_->model->asic_type == AsicType::GL124)
+ {
+ std::uint8_t buffer[2];
+
+ buffer[0] = address & 0xff;
+ buffer[1] = value;
+
+ std::uint16_t usb_value = VALUE_SET_REGISTER;
+ if (address > 0xff) {
+ usb_value |= 0x100;
+ }
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, usb_value, INDEX,
+ 2, buffer);
+
+ } else {
+ if (address > 0xff) {
+ throw SaneException("Invalid register address 0x%04x", address);
+ }
+
+ std::uint8_t address8 = address & 0xff;
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX,
+ 1, &address8);
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_WRITE_REGISTER, INDEX,
+ 1, &value);
+
+ }
+ DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value);
+}
+
+void ScannerInterfaceUsb::write_registers(const Genesys_Register_Set& regs)
+{
+ DBG_HELPER(dbg);
+ if (dev_->model->asic_type == AsicType::GL646 ||
+ dev_->model->asic_type == AsicType::GL841)
+ {
+ uint8_t outdata[8];
+ std::vector<uint8_t> buffer;
+ buffer.reserve(regs.size() * 2);
+
+ /* copy registers and values in data buffer */
+ for (const auto& r : regs) {
+ buffer.push_back(r.address);
+ buffer.push_back(r.value);
+ }
+
+ DBG(DBG_io, "%s (elems= %zu, size = %zu)\n", __func__, regs.size(), buffer.size());
+
+ if (dev_->model->asic_type == AsicType::GL646) {
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_REGISTER;
+ outdata[2] = 0x00;
+ outdata[3] = 0x00;
+ outdata[4] = (buffer.size() & 0xff);
+ outdata[5] = ((buffer.size() >> 8) & 0xff);
+ outdata[6] = ((buffer.size() >> 16) & 0xff);
+ outdata[7] = ((buffer.size() >> 24) & 0xff);
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, INDEX,
+ sizeof(outdata), outdata);
+
+ size_t write_size = buffer.size();
+
+ usb_dev_.bulk_write(buffer.data(), &write_size);
+ } else {
+ for (std::size_t i = 0; i < regs.size();) {
+ std::size_t c = regs.size() - i;
+ if (c > 32) /*32 is max on GL841. checked that.*/
+ c = 32;
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_SET_REGISTER,
+ INDEX, c * 2, buffer.data() + i * 2);
+
+ i += c;
+ }
+ }
+ } else {
+ for (const auto& r : regs) {
+ write_register(r.address, r.value);
+ }
+ }
+
+ DBG(DBG_io, "%s: wrote %zu registers\n", __func__, regs.size());
+}
+
+void ScannerInterfaceUsb::write_0x8c(std::uint8_t index, std::uint8_t value)
+{
+ DBG_HELPER_ARGS(dbg, "0x%02x,0x%02x", index, value);
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_BUF_ENDACCESS, index, 1, &value);
+}
+
+static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, size_t size)
+{
+ DBG_HELPER(dbg);
+
+ uint8_t outdata[8];
+ if (asic_type == AsicType::GL124 ||
+ asic_type == AsicType::GL846 ||
+ asic_type == AsicType::GL847)
+ {
+ // hard coded 0x10000000 address
+ outdata[0] = 0;
+ outdata[1] = 0;
+ outdata[2] = 0;
+ outdata[3] = 0x10;
+ } else if (asic_type == AsicType::GL841 ||
+ asic_type == AsicType::GL843) {
+ outdata[0] = BULK_IN;
+ outdata[1] = BULK_RAM;
+ outdata[2] = 0x82; //
+ outdata[3] = 0x00;
+ } else {
+ outdata[0] = BULK_IN;
+ outdata[1] = BULK_RAM;
+ outdata[2] = 0x00;
+ outdata[3] = 0x00;
+ }
+
+ /* data size to transfer */
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00,
+ sizeof(outdata), outdata);
+}
+
+void ScannerInterfaceUsb::bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size)
+{
+ // currently supported: GL646, GL841, GL843, GL846, GL847, GL124
+ DBG_HELPER(dbg);
+
+ unsigned is_addr_used = 1;
+ unsigned has_header_before_each_chunk = 0;
+ if (dev_->model->asic_type == AsicType::GL124 ||
+ dev_->model->asic_type == AsicType::GL846 ||
+ dev_->model->asic_type == AsicType::GL847)
+ {
+ is_addr_used = 0;
+ has_header_before_each_chunk = 1;
+ }
+
+ if (is_addr_used) {
+ DBG(DBG_io, "%s: requesting %zu bytes from 0x%02x addr\n", __func__, size, addr);
+ } else {
+ DBG(DBG_io, "%s: requesting %zu bytes\n", __func__, size);
+ }
+
+ if (size == 0)
+ return;
+
+ if (is_addr_used) {
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, 0x00,
+ 1, &addr);
+ }
+
+ std::size_t target_size = size;
+
+ std::size_t max_in_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type);
+
+ if (!has_header_before_each_chunk) {
+ bulk_read_data_send_header(usb_dev_, dev_->model->asic_type, size);
+ }
+
+ // loop until computed data size is read
+ while (target_size > 0) {
+ std::size_t block_size = std::min(target_size, max_in_size);
+
+ if (has_header_before_each_chunk) {
+ bulk_read_data_send_header(usb_dev_, dev_->model->asic_type, block_size);
+ }
+
+ DBG(DBG_io2, "%s: trying to read %zu bytes of data\n", __func__, block_size);
+
+ usb_dev_.bulk_read(data, &block_size);
+
+ DBG(DBG_io2, "%s: read %zu bytes, %zu remaining\n", __func__, block_size, target_size - block_size);
+
+ target_size -= block_size;
+ data += block_size;
+ }
+}
+
+void ScannerInterfaceUsb::bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t len)
+{
+ DBG_HELPER_ARGS(dbg, "writing %zu bytes", len);
+
+ // supported: GL646, GL841, GL843
+ std::size_t size;
+ std::uint8_t outdata[8];
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX,
+ 1, &addr);
+
+ std::size_t max_out_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type);
+
+ while (len) {
+ if (len > max_out_size)
+ size = max_out_size;
+ else
+ size = len;
+
+ if (dev_->model->asic_type == AsicType::GL841) {
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_RAM;
+ // both 0x82 and 0x00 works on GL841.
+ outdata[2] = 0x82;
+ outdata[3] = 0x00;
+ } else {
+ outdata[0] = BULK_OUT;
+ outdata[1] = BULK_RAM;
+ // 8600F uses 0x82, but 0x00 works too. 8400F uses 0x02 for certain transactions.
+ outdata[2] = 0x00;
+ outdata[3] = 0x00;
+ }
+
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00,
+ sizeof(outdata), outdata);
+
+ usb_dev_.bulk_write(data, &size);
+
+ DBG(DBG_io2, "%s: wrote %zu bytes, %zu remaining\n", __func__, size, len - size);
+
+ len -= size;
+ data += size;
+ }
+}
+
+void ScannerInterfaceUsb::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags)
+{
+ DBG_HELPER_ARGS(dbg, "type: 0x%02x, addr: 0x%08x, size: 0x%08zx", type, addr, size);
+ if (dev_->model->asic_type != AsicType::GL646 &&
+ dev_->model->asic_type != AsicType::GL841 &&
+ dev_->model->asic_type != AsicType::GL843)
+ {
+ throw SaneException("Unsupported transfer mode");
+ }
+
+ if (dev_->model->asic_type == AsicType::GL843) {
+ if (flags & FLAG_SWAP_REGISTERS) {
+ if (!(flags & FLAG_SMALL_ADDRESS)) {
+ write_register(0x29, ((addr >> 20) & 0xff));
+ }
+ write_register(0x2a, ((addr >> 12) & 0xff));
+ write_register(0x2b, ((addr >> 4) & 0xff));
+ } else {
+ write_register(0x2b, ((addr >> 4) & 0xff));
+ write_register(0x2a, ((addr >> 12) & 0xff));
+ if (!(flags & FLAG_SMALL_ADDRESS)) {
+ write_register(0x29, ((addr >> 20) & 0xff));
+ }
+ }
+ } else {
+ write_register(0x2b, ((addr >> 4) & 0xff));
+ write_register(0x2a, ((addr >> 12) & 0xff));
+ }
+ bulk_write_data(type, data, size);
+}
+
+void ScannerInterfaceUsb::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags)
+{
+ DBG_HELPER_ARGS(dbg, "type: 0x%02x, addr: 0x%08x, size: 0x%08zx", type, addr, size);
+ if (dev_->model->asic_type != AsicType::GL646 &&
+ dev_->model->asic_type != AsicType::GL841 &&
+ dev_->model->asic_type != AsicType::GL843)
+ {
+ throw SaneException("Unsupported transfer mode");
+ }
+
+ if (flags & FLAG_SWAP_REGISTERS) {
+ write_register(0x5b, ((addr >> 12) & 0xff));
+ write_register(0x5c, ((addr >> 4) & 0xff));
+ } else {
+ write_register(0x5c, ((addr >> 4) & 0xff));
+ write_register(0x5b, ((addr >> 12) & 0xff));
+ }
+ bulk_write_data(type, data, size);
+}
+
+void ScannerInterfaceUsb::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data)
+{
+ DBG_HELPER_ARGS(dbg, "address: 0x%08x, size: %d", static_cast<unsigned>(addr),
+ static_cast<unsigned>(size));
+
+ if (dev_->model->asic_type != AsicType::GL845 &&
+ dev_->model->asic_type != AsicType::GL846 &&
+ dev_->model->asic_type != AsicType::GL847 &&
+ dev_->model->asic_type != AsicType::GL124)
+ {
+ throw SaneException("Unsupported transfer type");
+ }
+ std::uint8_t outdata[8];
+ outdata[0] = addr & 0xff;
+ outdata[1] = ((addr >> 8) & 0xff);
+ outdata[2] = ((addr >> 16) & 0xff);
+ outdata[3] = ((addr >> 24) & 0xff);
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ // write addr and size for AHB
+ usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x01, 8, outdata);
+
+ std::size_t max_out_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type);
+
+ // write actual data
+ std::size_t written = 0;
+ do {
+ std::size_t block_size = std::min(size - written, max_out_size);
+
+ usb_dev_.bulk_write(data + written, &block_size);
+
+ written += block_size;
+ } while (written < size);
+}
+
+std::uint16_t ScannerInterfaceUsb::read_fe_register(std::uint8_t address)
+{
+ DBG_HELPER(dbg);
+ Genesys_Register_Set reg;
+
+ reg.init_reg(0x50, address);
+
+ // set up read address
+ write_registers(reg);
+
+ // read data
+ std::uint16_t value = read_register(0x46) << 8;
+ value |= read_register(0x47);
+
+ DBG(DBG_io, "%s (0x%02x, 0x%04x)\n", __func__, address, value);
+ return value;
+}
+
+void ScannerInterfaceUsb::write_fe_register(std::uint8_t address, std::uint16_t value)
+{
+ DBG_HELPER_ARGS(dbg, "0x%02x, 0x%04x", address, value);
+ Genesys_Register_Set reg(Genesys_Register_Set::SEQUENTIAL);
+
+ reg.init_reg(0x51, address);
+ if (dev_->model->asic_type == AsicType::GL124) {
+ reg.init_reg(0x5d, (value / 256) & 0xff);
+ reg.init_reg(0x5e, value & 0xff);
+ } else {
+ reg.init_reg(0x3a, (value / 256) & 0xff);
+ reg.init_reg(0x3b, value & 0xff);
+ }
+
+ write_registers(reg);
+}
+
+IUsbDevice& ScannerInterfaceUsb::get_usb_device()
+{
+ return usb_dev_;
+}
+
+void ScannerInterfaceUsb::sleep_us(unsigned microseconds)
+{
+ if (sanei_usb_is_replay_mode_enabled()) {
+ return;
+ }
+ std::this_thread::sleep_for(std::chrono::microseconds{microseconds});
+}
+
+void ScannerInterfaceUsb::record_progress_message(const char* msg)
+{
+ sanei_usb_testing_record_message(msg);
+}
+
+void ScannerInterfaceUsb::record_slope_table(unsigned table_nr,
+ const std::vector<std::uint16_t>& steps)
+{
+ (void) table_nr;
+ (void) steps;
+}
+
+void ScannerInterfaceUsb::record_key_value(const std::string& key, const std::string& value)
+{
+ (void) key;
+ (void) value;
+}
+
+void ScannerInterfaceUsb::test_checkpoint(const std::string& name)
+{
+ (void) name;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/scanner_interface_usb.h b/backend/genesys/scanner_interface_usb.h
new file mode 100644
index 0000000..06b51ff
--- /dev/null
+++ b/backend/genesys/scanner_interface_usb.h
@@ -0,0 +1,98 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_SCANNER_INTERFACE_USB_H
+#define BACKEND_GENESYS_SCANNER_INTERFACE_USB_H
+
+#include "scanner_interface.h"
+#include "usb_device.h"
+
+namespace genesys {
+
+class ScannerInterfaceUsb : public ScannerInterface
+{
+public:
+ ScannerInterfaceUsb(Genesys_Device* dev);
+
+ ~ScannerInterfaceUsb() override;
+
+ bool is_mock() const override;
+
+ std::uint8_t read_register(std::uint16_t address) override;
+ void write_register(std::uint16_t address, std::uint8_t value) override;
+ void write_registers(const Genesys_Register_Set& regs) override;
+
+ void write_0x8c(std::uint8_t index, std::uint8_t value) override;
+ void bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override;
+ void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override;
+
+ void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags) override;
+ void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags) override;
+
+ void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) override;
+
+ std::uint16_t read_fe_register(std::uint8_t address) override;
+ void write_fe_register(std::uint8_t address, std::uint16_t value) override;
+
+ IUsbDevice& get_usb_device() override;
+
+ void sleep_us(unsigned microseconds) override;
+
+ void record_progress_message(const char* msg) override;
+
+ void record_slope_table(unsigned table_nr, const std::vector<std::uint16_t>& steps) override;
+
+ void record_key_value(const std::string& key, const std::string& value) override;
+
+ void test_checkpoint(const std::string& name) override;
+
+private:
+ Genesys_Device* dev_;
+ UsbDevice usb_dev_;
+};
+
+} // namespace genesys
+
+#endif
diff --git a/backend/genesys/sensor.cpp b/backend/genesys/sensor.cpp
new file mode 100644
index 0000000..e54af65
--- /dev/null
+++ b/backend/genesys/sensor.cpp
@@ -0,0 +1,160 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "sensor.h"
+#include "utilities.h"
+#include <iomanip>
+
+namespace genesys {
+
+std::ostream& operator<<(std::ostream& out, const StaggerConfig& config)
+{
+ out << "StaggerConfig{\n"
+ << " min_resolution: " << config.min_resolution() << '\n'
+ << " lines_at_min: " << config.lines_at_min() << '\n'
+ << "}";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const FrontendType& type)
+{
+ switch (type) {
+ case FrontendType::UNKNOWN: out << "UNKNOWN"; break;
+ case FrontendType::WOLFSON: out << "WOLFSON"; break;
+ case FrontendType::ANALOG_DEVICES: out << "ANALOG_DEVICES"; break;
+ default: out << "(unknown value)";
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const GenesysFrontendLayout& layout)
+{
+ StreamStateSaver state_saver{out};
+
+ out << "GenesysFrontendLayout{\n"
+ << " type: " << layout.type << '\n'
+ << std::hex
+ << " offset_addr[0]: " << layout.offset_addr[0] << '\n'
+ << " offset_addr[1]: " << layout.offset_addr[1] << '\n'
+ << " offset_addr[2]: " << layout.offset_addr[2] << '\n'
+ << " gain_addr[0]: " << layout.gain_addr[0] << '\n'
+ << " gain_addr[1]: " << layout.gain_addr[1] << '\n'
+ << " gain_addr[2]: " << layout.gain_addr[2] << '\n'
+ << '}';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Frontend& frontend)
+{
+ StreamStateSaver state_saver{out};
+
+ out << "Genesys_Frontend{\n"
+ << " id: " << static_cast<unsigned>(frontend.id) << '\n'
+ << " regs: " << format_indent_braced_list(4, frontend.regs) << '\n'
+ << std::hex
+ << " reg2[0]: " << frontend.reg2[0] << '\n'
+ << " reg2[1]: " << frontend.reg2[1] << '\n'
+ << " reg2[2]: " << frontend.reg2[2] << '\n'
+ << " layout: " << format_indent_braced_list(4, frontend.layout) << '\n'
+ << '}';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure)
+{
+ out << "SensorExposure{\n"
+ << " red: " << exposure.red << '\n'
+ << " green: " << exposure.green << '\n'
+ << " blue: " << exposure.blue << '\n'
+ << '}';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const ResolutionFilter& resolutions)
+{
+ if (resolutions.matches_any()) {
+ out << "ANY";
+ return out;
+ }
+ out << format_vector_unsigned(4, resolutions.resolutions());
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor)
+{
+ out << "Genesys_Sensor{\n"
+ << " sensor_id: " << static_cast<unsigned>(sensor.sensor_id) << '\n'
+ << " optical_res: " << sensor.optical_res << '\n'
+ << " resolutions: " << format_indent_braced_list(4, sensor.resolutions) << '\n'
+ << " channels: " << format_vector_unsigned(4, sensor.channels) << '\n'
+ << " method: " << sensor.method << '\n'
+ << " register_dpihw_override: " << sensor.register_dpihw_override << '\n'
+ << " logical_dpihw_override: " << sensor.logical_dpihw_override << '\n'
+ << " dpiset_override: " << sensor.dpiset_override << '\n'
+ << " ccd_size_divisor: " << sensor.ccd_size_divisor << '\n'
+ << " pixel_count_multiplier: " << sensor.pixel_count_multiplier << '\n'
+ << " black_pixels: " << sensor.black_pixels << '\n'
+ << " dummy_pixel: " << sensor.dummy_pixel << '\n'
+ << " ccd_start_xoffset: " << sensor.ccd_start_xoffset << '\n'
+ << " sensor_pixels: " << sensor.sensor_pixels << '\n'
+ << " fau_gain_white_ref: " << sensor.fau_gain_white_ref << '\n'
+ << " gain_white_ref: " << sensor.gain_white_ref << '\n'
+ << " exposure: " << format_indent_braced_list(4, sensor.exposure) << '\n'
+ << " exposure_lperiod: " << sensor.exposure_lperiod << '\n'
+ << " segment_size: " << sensor.segment_size << '\n'
+ << " segment_order: "
+ << format_indent_braced_list(4, format_vector_unsigned(4, sensor.segment_order)) << '\n'
+ << " stagger_config: " << format_indent_braced_list(4, sensor.stagger_config) << '\n'
+ << " custom_base_regs: " << format_indent_braced_list(4, sensor.custom_base_regs) << '\n'
+ << " custom_regs: " << format_indent_braced_list(4, sensor.custom_regs) << '\n'
+ << " custom_fe_regs: " << format_indent_braced_list(4, sensor.custom_fe_regs) << '\n'
+ << " gamma.red: " << sensor.gamma[0] << '\n'
+ << " gamma.green: " << sensor.gamma[1] << '\n'
+ << " gamma.blue: " << sensor.gamma[2] << '\n'
+ << "}";
+ return out;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/sensor.h b/backend/genesys/sensor.h
new file mode 100644
index 0000000..e70728e
--- /dev/null
+++ b/backend/genesys/sensor.h
@@ -0,0 +1,470 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_SENSOR_H
+#define BACKEND_GENESYS_SENSOR_H
+
+#include "enums.h"
+#include "register.h"
+#include "serialize.h"
+#include <array>
+#include <functional>
+
+namespace genesys {
+
+template<class T, size_t Size>
+struct AssignableArray : public std::array<T, Size> {
+ AssignableArray() = default;
+ AssignableArray(const AssignableArray&) = default;
+ AssignableArray& operator=(const AssignableArray&) = default;
+
+ AssignableArray& operator=(std::initializer_list<T> init)
+ {
+ if (init.size() != std::array<T, Size>::size())
+ throw std::runtime_error("An array of incorrect size assigned");
+ std::copy(init.begin(), init.end(), std::array<T, Size>::begin());
+ return *this;
+ }
+};
+
+
+class StaggerConfig
+{
+public:
+ StaggerConfig() = default;
+ StaggerConfig(unsigned min_resolution, unsigned lines_at_min) :
+ min_resolution_{min_resolution},
+ lines_at_min_{lines_at_min}
+ {
+ }
+
+ unsigned stagger_at_resolution(unsigned xresolution, unsigned yresolution) const
+ {
+ if (min_resolution_ == 0 || xresolution < min_resolution_)
+ return 0;
+ return yresolution / min_resolution_ * lines_at_min_;
+ }
+
+ unsigned min_resolution() const { return min_resolution_; }
+ unsigned lines_at_min() const { return lines_at_min_; }
+
+ bool operator==(const StaggerConfig& other) const
+ {
+ return min_resolution_ == other.min_resolution_ &&
+ lines_at_min_ == other.lines_at_min_;
+ }
+
+private:
+ unsigned min_resolution_ = 0;
+ unsigned lines_at_min_ = 0;
+
+ template<class Stream>
+ friend void serialize(Stream& str, StaggerConfig& x);
+};
+
+template<class Stream>
+void serialize(Stream& str, StaggerConfig& x)
+{
+ serialize(str, x.min_resolution_);
+ serialize(str, x.lines_at_min_);
+}
+
+std::ostream& operator<<(std::ostream& out, const StaggerConfig& config);
+
+
+enum class FrontendType : unsigned
+{
+ UNKNOWN,
+ WOLFSON,
+ ANALOG_DEVICES
+};
+
+inline void serialize(std::istream& str, FrontendType& x)
+{
+ unsigned value;
+ serialize(str, value);
+ x = static_cast<FrontendType>(value);
+}
+
+inline void serialize(std::ostream& str, FrontendType& x)
+{
+ unsigned value = static_cast<unsigned>(x);
+ serialize(str, value);
+}
+
+std::ostream& operator<<(std::ostream& out, const FrontendType& type);
+
+struct GenesysFrontendLayout
+{
+ FrontendType type = FrontendType::UNKNOWN;
+ std::array<std::uint16_t, 3> offset_addr = {};
+ std::array<std::uint16_t, 3> gain_addr = {};
+
+ bool operator==(const GenesysFrontendLayout& other) const
+ {
+ return type == other.type &&
+ offset_addr == other.offset_addr &&
+ gain_addr == other.gain_addr;
+ }
+};
+
+template<class Stream>
+void serialize(Stream& str, GenesysFrontendLayout& x)
+{
+ serialize(str, x.type);
+ serialize_newline(str);
+ serialize(str, x.offset_addr);
+ serialize_newline(str);
+ serialize(str, x.gain_addr);
+}
+
+std::ostream& operator<<(std::ostream& out, const GenesysFrontendLayout& layout);
+
+/** @brief Data structure to set up analog frontend.
+ The analog frontend converts analog value from image sensor to digital value. It has its own
+ control registers which are set up with this structure. The values are written using
+ fe_write_data.
+ */
+struct Genesys_Frontend
+{
+ Genesys_Frontend() = default;
+
+ // id of the frontend description
+ AdcId id = AdcId::UNKNOWN;
+
+ // all registers of the frontend. Note that the registers can hold 9-bit values
+ RegisterSettingSet<std::uint16_t> regs;
+
+ // extra control registers
+ std::array<std::uint16_t, 3> reg2 = {};
+
+ GenesysFrontendLayout layout;
+
+ void set_offset(unsigned which, std::uint16_t value)
+ {
+ regs.set_value(layout.offset_addr[which], value);
+ }
+
+ void set_gain(unsigned which, std::uint16_t value)
+ {
+ regs.set_value(layout.gain_addr[which], value);
+ }
+
+ std::uint16_t get_offset(unsigned which) const
+ {
+ return regs.get_value(layout.offset_addr[which]);
+ }
+
+ std::uint16_t get_gain(unsigned which) const
+ {
+ return regs.get_value(layout.gain_addr[which]);
+ }
+
+ bool operator==(const Genesys_Frontend& other) const
+ {
+ return id == other.id &&
+ regs == other.regs &&
+ reg2 == other.reg2 &&
+ layout == other.layout;
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Frontend& frontend);
+
+template<class Stream>
+void serialize(Stream& str, Genesys_Frontend& x)
+{
+ serialize(str, x.id);
+ serialize_newline(str);
+ serialize(str, x.regs);
+ serialize_newline(str);
+ serialize(str, x.reg2);
+ serialize_newline(str);
+ serialize(str, x.layout);
+}
+
+struct SensorExposure {
+ std::uint16_t red = 0;
+ std::uint16_t green = 0;
+ std::uint16_t blue = 0;
+
+ SensorExposure() = default;
+ SensorExposure(std::uint16_t r, std::uint16_t g, std::uint16_t b) :
+ red{r}, green{g}, blue{b}
+ {}
+
+ bool operator==(const SensorExposure& other) const
+ {
+ return red == other.red && green == other.green && blue == other.blue;
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure);
+
+
+class ResolutionFilter
+{
+public:
+ struct Any {};
+ static constexpr Any ANY{};
+
+ ResolutionFilter() : matches_any_{false} {}
+ ResolutionFilter(Any) : matches_any_{true} {}
+ ResolutionFilter(std::initializer_list<unsigned> resolutions) :
+ matches_any_{false},
+ resolutions_{resolutions}
+ {}
+
+ bool matches(unsigned resolution) const
+ {
+ if (matches_any_)
+ return true;
+ auto it = std::find(resolutions_.begin(), resolutions_.end(), resolution);
+ return it != resolutions_.end();
+ }
+
+ bool operator==(const ResolutionFilter& other) const
+ {
+ return matches_any_ == other.matches_any_ && resolutions_ == other.resolutions_;
+ }
+
+ bool matches_any() const { return matches_any_; }
+ const std::vector<unsigned>& resolutions() const { return resolutions_; }
+
+private:
+ bool matches_any_ = false;
+ std::vector<unsigned> resolutions_;
+
+ template<class Stream>
+ friend void serialize(Stream& str, ResolutionFilter& x);
+};
+
+std::ostream& operator<<(std::ostream& out, const ResolutionFilter& resolutions);
+
+template<class Stream>
+void serialize(Stream& str, ResolutionFilter& x)
+{
+ serialize(str, x.matches_any_);
+ serialize_newline(str);
+ serialize(str, x.resolutions_);
+}
+
+
+struct Genesys_Sensor {
+
+ Genesys_Sensor() = default;
+ ~Genesys_Sensor() = default;
+
+ // id of the sensor description
+ SensorId sensor_id = SensorId::UNKNOWN;
+
+ // sensor resolution in CCD pixels. Note that we may read more than one CCD pixel per logical
+ // pixel, see ccd_pixels_per_system_pixel()
+ unsigned optical_res = 0;
+
+ // the resolution list that the sensor is usable at.
+ ResolutionFilter resolutions = ResolutionFilter::ANY;
+
+ // the channel list that the sensor is usable at
+ std::vector<unsigned> channels = { 1, 3 };
+
+ // the scan method used with the sensor
+ ScanMethod method = ScanMethod::FLATBED;
+
+ // The scanner may be setup to use a custom dpihw that does not correspond to any actual
+ // resolution. The value zero does not set the override.
+ unsigned register_dpihw_override = 0;
+
+ // The scanner may be setup to use a custom logical dpihw that does not correspond to any actual
+ // resolution. The value zero does not set the override.
+ unsigned logical_dpihw_override = 0;
+
+ // The scanner may be setup to use a custom dpiset value that does not correspond to any actual
+ // resolution. The value zero does not set the override.
+ unsigned dpiset_override = 0;
+
+ // CCD may present itself as half or quarter-size CCD on certain resolutions
+ int ccd_size_divisor = 1;
+
+ // Some scanners need an additional multiplier over the scan coordinates
+ int pixel_count_multiplier = 1;
+
+ int black_pixels = 0;
+ // value of the dummy register
+ int dummy_pixel = 0;
+ // last pixel of CCD margin at optical resolution
+ int ccd_start_xoffset = 0;
+ // total pixels used by the sensor
+ int sensor_pixels = 0;
+ // TA CCD target code (reference gain)
+ int fau_gain_white_ref = 0;
+ // CCD target code (reference gain)
+ int gain_white_ref = 0;
+
+ // red, green and blue initial exposure values
+ SensorExposure exposure;
+
+ int exposure_lperiod = -1;
+
+ // the number of pixels in a single segment.
+ // only on gl843
+ unsigned segment_size = 0;
+
+ // the order of the segments, if any, for the sensor. If the sensor is not segmented or uses
+ // only single segment, this array can be empty
+ // only on gl843
+ std::vector<unsigned> segment_order;
+
+ // some CCDs use two arrays of pixels for double resolution. On such CCDs when scanning at
+ // high-enough resolution, every other pixel column is shifted
+ StaggerConfig stagger_config;
+
+ GenesysRegisterSettingSet custom_base_regs; // gl646-specific
+ GenesysRegisterSettingSet custom_regs;
+ GenesysRegisterSettingSet custom_fe_regs;
+
+ // red, green and blue gamma coefficient for default gamma tables
+ AssignableArray<float, 3> gamma;
+
+ std::function<unsigned(const Genesys_Sensor&, unsigned)> get_logical_hwdpi_fun;
+ std::function<unsigned(const Genesys_Sensor&, unsigned)> get_register_hwdpi_fun;
+ std::function<unsigned(const Genesys_Sensor&, unsigned)> get_ccd_size_divisor_fun;
+ std::function<unsigned(const Genesys_Sensor&, unsigned)> get_hwdpi_divisor_fun;
+
+ unsigned get_logical_hwdpi(unsigned xres) const { return get_logical_hwdpi_fun(*this, xres); }
+ unsigned get_register_hwdpi(unsigned xres) const { return get_register_hwdpi_fun(*this, xres); }
+ unsigned get_ccd_size_divisor_for_dpi(unsigned xres) const
+ {
+ return get_ccd_size_divisor_fun(*this, xres);
+ }
+ unsigned get_hwdpi_divisor_for_dpi(unsigned xres) const
+ {
+ return get_hwdpi_divisor_fun(*this, xres);
+ }
+
+ // how many CCD pixels are processed per system pixel time. This corresponds to CKSEL + 1
+ unsigned ccd_pixels_per_system_pixel() const
+ {
+ // same on GL646, GL841, GL843, GL846, GL847, GL124
+ constexpr unsigned REG_CKSEL = 0x03;
+ return (custom_regs.get_value(0x18) & REG_CKSEL) + 1;
+ }
+
+ bool matches_channel_count(unsigned count) const
+ {
+ return std::find(channels.begin(), channels.end(), count) != channels.end();
+ }
+
+ unsigned get_segment_count() const
+ {
+ if (segment_order.size() < 2)
+ return 1;
+ return segment_order.size();
+ }
+
+ bool operator==(const Genesys_Sensor& other) const
+ {
+ return sensor_id == other.sensor_id &&
+ optical_res == other.optical_res &&
+ resolutions == other.resolutions &&
+ method == other.method &&
+ ccd_size_divisor == other.ccd_size_divisor &&
+ black_pixels == other.black_pixels &&
+ dummy_pixel == other.dummy_pixel &&
+ ccd_start_xoffset == other.ccd_start_xoffset &&
+ sensor_pixels == other.sensor_pixels &&
+ fau_gain_white_ref == other.fau_gain_white_ref &&
+ gain_white_ref == other.gain_white_ref &&
+ exposure == other.exposure &&
+ exposure_lperiod == other.exposure_lperiod &&
+ segment_size == other.segment_size &&
+ segment_order == other.segment_order &&
+ stagger_config == other.stagger_config &&
+ custom_base_regs == other.custom_base_regs &&
+ custom_regs == other.custom_regs &&
+ custom_fe_regs == other.custom_fe_regs &&
+ gamma == other.gamma;
+ }
+};
+
+template<class Stream>
+void serialize(Stream& str, Genesys_Sensor& x)
+{
+ serialize(str, x.sensor_id);
+ serialize(str, x.optical_res);
+ serialize(str, x.resolutions);
+ serialize(str, x.method);
+ serialize(str, x.ccd_size_divisor);
+ serialize(str, x.black_pixels);
+ serialize(str, x.dummy_pixel);
+ serialize(str, x.ccd_start_xoffset);
+ serialize(str, x.sensor_pixels);
+ serialize(str, x.fau_gain_white_ref);
+ serialize(str, x.gain_white_ref);
+ serialize_newline(str);
+ serialize(str, x.exposure.blue);
+ serialize(str, x.exposure.green);
+ serialize(str, x.exposure.red);
+ serialize(str, x.exposure_lperiod);
+ serialize_newline(str);
+ serialize(str, x.segment_size);
+ serialize_newline(str);
+ serialize(str, x.segment_order);
+ serialize_newline(str);
+ serialize(str, x.stagger_config);
+ serialize_newline(str);
+ serialize(str, x.custom_base_regs);
+ serialize_newline(str);
+ serialize(str, x.custom_regs);
+ serialize_newline(str);
+ serialize(str, x.custom_fe_regs);
+ serialize_newline(str);
+ serialize(str, x.gamma);
+ serialize_newline(str);
+}
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor);
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_SENSOR_H
diff --git a/backend/genesys_serialize.cc b/backend/genesys/serialize.cpp
index e69de29..e69de29 100644
--- a/backend/genesys_serialize.cc
+++ b/backend/genesys/serialize.cpp
diff --git a/backend/genesys_serialize.h b/backend/genesys/serialize.h
index 481e872..ed40a4e 100644
--- a/backend/genesys_serialize.h
+++ b/backend/genesys/serialize.h
@@ -44,18 +44,22 @@
#ifndef BACKEND_GENESYS_SERIALIZE_H
#define BACKEND_GENESYS_SERIALIZE_H
-#include "genesys_error.h"
+#include "error.h"
#include <array>
#include <iostream>
#include <limits>
#include <string>
#include <vector>
+namespace genesys {
+
// it would be best to use something like boost.serialization
inline void serialize_newline(std::ostream& str) { str << '\n'; }
inline void serialize_newline(std::istream& str) { (void) str; }
+inline void serialize(std::ostream& str, bool x) { str << static_cast<unsigned>(x) << " "; }
+inline void serialize(std::istream& str, bool& x) { unsigned v; str >> v; x = v; }
inline void serialize(std::ostream& str, char x) { str << static_cast<int>(x) << " "; }
inline void serialize(std::istream& str, char& x) { int v; str >> v; x = v; }
inline void serialize(std::ostream& str, unsigned char x) { str << static_cast<unsigned>(x) << " "; }
@@ -141,4 +145,6 @@ void serialize(std::istream& str, std::array<T, Size>& x)
}
}
+} // namespace genesys
+
#endif
diff --git a/backend/genesys/settings.cpp b/backend/genesys/settings.cpp
new file mode 100644
index 0000000..41c66de
--- /dev/null
+++ b/backend/genesys/settings.cpp
@@ -0,0 +1,142 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "settings.h"
+#include "utilities.h"
+#include <iomanip>
+
+namespace genesys {
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Settings& settings)
+{
+ StreamStateSaver state_saver{out};
+
+ out << "Genesys_Settings{\n"
+ << " xres: " << settings.xres << " yres: " << settings.yres << '\n'
+ << " lines: " << settings.lines << '\n'
+ << " pixels per line (actual): " << settings.pixels << '\n'
+ << " pixels per line (requested): " << settings.requested_pixels << '\n'
+ << " depth: " << settings.depth << '\n';
+ auto prec = out.precision();
+ out.precision(3);
+ out << " tl_x: " << settings.tl_x << " tl_y: " << settings.tl_y << '\n';
+ out.precision(prec);
+ out << " scan_mode: " << settings.scan_mode << '\n'
+ << '}';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const SetupParams& params)
+{
+ StreamStateSaver state_saver{out};
+
+ out << "SetupParams{\n"
+ << " xres: " << params.xres << " yres: " << params.yres << '\n'
+ << " lines: " << params.lines << '\n'
+ << " pixels per line (actual): " << params.pixels << '\n'
+ << " pixels per line (requested): " << params.requested_pixels << '\n'
+ << " depth: " << params.depth << '\n'
+ << " channels: " << params.channels << '\n'
+ << " startx: " << params.startx << " starty: " << params.starty << '\n'
+ << " scan_mode: " << params.scan_mode << '\n'
+ << " color_filter: " << params.color_filter << '\n'
+ << " flags: " << params.flags << '\n'
+ << "}";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const ScanSession& session)
+{
+ out << "ScanSession{\n"
+ << " computed: " << session.computed << '\n'
+ << " hwdpi_divisor: " << session.hwdpi_divisor << '\n'
+ << " ccd_size_divisor: " << session.ccd_size_divisor << '\n'
+ << " optical_resolution: " << session.optical_resolution << '\n'
+ << " optical_pixels: " << session.optical_pixels << '\n'
+ << " optical_pixels_raw: " << session.optical_pixels_raw << '\n'
+ << " output_resolution: " << session.output_resolution << '\n'
+ << " output_pixels: " << session.output_pixels << '\n'
+ << " output_line_bytes: " << session.output_line_bytes << '\n'
+ << " output_line_bytes_raw: " << session.output_line_bytes_raw << '\n'
+ << " output_line_count: " << session.output_line_count << '\n'
+ << " num_staggered_lines: " << session.num_staggered_lines << '\n'
+ << " color_shift_lines_r: " << session.color_shift_lines_r << '\n'
+ << " color_shift_lines_g: " << session.color_shift_lines_g << '\n'
+ << " color_shift_lines_b: " << session.color_shift_lines_b << '\n'
+ << " max_color_shift_lines: " << session.max_color_shift_lines << '\n'
+ << " enable_ledadd: " << session.enable_ledadd << '\n'
+ << " segment_count: " << session.segment_count << '\n'
+ << " pixel_startx: " << session.pixel_startx << '\n'
+ << " pixel_endx: " << session.pixel_endx << '\n'
+ << " conseq_pixel_dist: " << session.conseq_pixel_dist << '\n'
+ << " output_segment_pixel_group_count: "
+ << session.output_segment_pixel_group_count << '\n'
+ << " buffer_size_read: " << session.buffer_size_read << '\n'
+ << " buffer_size_read: " << session.buffer_size_lines << '\n'
+ << " buffer_size_shrink: " << session.buffer_size_shrink << '\n'
+ << " buffer_size_out: " << session.buffer_size_out << '\n'
+ << " filters: "
+ << (session.pipeline_needs_reorder ? " reorder": "")
+ << (session.pipeline_needs_ccd ? " ccd": "")
+ << (session.pipeline_needs_shrink ? " shrink": "") << '\n'
+ << " params: " << format_indent_braced_list(4, session.params) << '\n'
+ << "}";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const SANE_Parameters& params)
+{
+ out << "SANE_Parameters{\n"
+ << " format: " << static_cast<unsigned>(params.format) << '\n'
+ << " last_frame: " << params.last_frame << '\n'
+ << " bytes_per_line: " << params.bytes_per_line << '\n'
+ << " pixels_per_line: " << params.pixels_per_line << '\n'
+ << " lines: " << params.lines << '\n'
+ << " depth: " << params.depth << '\n'
+ << '}';
+ return out;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/settings.h b/backend/genesys/settings.h
new file mode 100644
index 0000000..a697e60
--- /dev/null
+++ b/backend/genesys/settings.h
@@ -0,0 +1,328 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_SETTINGS_H
+#define BACKEND_GENESYS_SETTINGS_H
+
+#include "enums.h"
+#include "serialize.h"
+
+namespace genesys {
+
+struct Genesys_Settings
+{
+ ScanMethod scan_method = ScanMethod::FLATBED;
+ ScanColorMode scan_mode = ScanColorMode::LINEART;
+
+ // horizontal dpi
+ unsigned xres = 0;
+ // vertical dpi
+ unsigned yres = 0;
+
+ //x start on scan table in mm
+ double tl_x = 0;
+ // y start on scan table in mm
+ double tl_y = 0;
+
+ // number of lines at scan resolution
+ unsigned int lines = 0;
+ // number of pixels expected from the scanner
+ unsigned int pixels = 0;
+ // number of pixels expected by the frontend
+ unsigned requested_pixels = 0;
+
+ // bit depth of the scan
+ unsigned int depth = 0;
+
+ ColorFilter color_filter = ColorFilter::NONE;
+
+ // true if scan is true gray, false if monochrome scan
+ int true_gray = 0;
+
+ // lineart threshold
+ int threshold = 0;
+
+ // lineart threshold curve for dynamic rasterization
+ int threshold_curve = 0;
+
+ // Disable interpolation for xres<yres
+ int disable_interpolation = 0;
+
+ // value for contrast enhancement in the [-100..100] range
+ int contrast = 0;
+
+ // value for brightness enhancement in the [-100..100] range
+ int brightness = 0;
+
+ // cache entries expiration time
+ int expiration_time = 0;
+
+ unsigned get_channels() const
+ {
+ if (scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
+ return 3;
+ return 1;
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const Genesys_Settings& settings);
+
+
+struct SetupParams {
+
+ static constexpr unsigned NOT_SET = std::numeric_limits<unsigned>::max();
+
+ // resolution in x direction
+ unsigned xres = NOT_SET;
+ // resolution in y direction
+ unsigned yres = NOT_SET;
+ // start pixel in X direction, from dummy_pixel + 1
+ unsigned startx = NOT_SET;
+ // start pixel in Y direction, counted according to base_ydpi
+ unsigned starty = NOT_SET;
+ // the number of pixels in X direction. Note that each logical pixel may correspond to more
+ // than one CCD pixel, see CKSEL and GenesysSensor::ccd_pixels_per_system_pixel()
+ unsigned pixels = NOT_SET;
+
+ // the number of pixels in the X direction as requested by the frontend. This will be different
+ // from `pixels` if the X resolution requested by the frontend is different than the actual
+ // resolution. This is only needed to compute dev->total_bytes_to_read. If 0, then the value
+ // is the same as pixels.
+ // TODO: move the computation of total_bytes_to_read to a higher layer.
+ unsigned requested_pixels = 0;
+
+ // the number of pixels in Y direction
+ unsigned lines = NOT_SET;
+ // the depth of the scan in bits. Allowed are 1, 8, 16
+ unsigned depth = NOT_SET;
+ // the number of channels
+ unsigned channels = NOT_SET;
+
+ ScanMethod scan_method = static_cast<ScanMethod>(NOT_SET);
+
+ ScanColorMode scan_mode = static_cast<ScanColorMode>(NOT_SET);
+
+ ColorFilter color_filter = static_cast<ColorFilter>(NOT_SET);
+
+ ScanFlag flags;
+
+ unsigned get_requested_pixels() const
+ {
+ if (requested_pixels != 0) {
+ return requested_pixels;
+ }
+ return pixels;
+ }
+
+ void assert_valid() const
+ {
+ if (xres == NOT_SET || yres == NOT_SET || startx == NOT_SET || starty == NOT_SET ||
+ pixels == NOT_SET || lines == NOT_SET ||depth == NOT_SET || channels == NOT_SET ||
+ scan_method == static_cast<ScanMethod>(NOT_SET) ||
+ scan_mode == static_cast<ScanColorMode>(NOT_SET) ||
+ color_filter == static_cast<ColorFilter>(NOT_SET))
+ {
+ throw std::runtime_error("SetupParams are not valid");
+ }
+ }
+
+ bool operator==(const SetupParams& other) const
+ {
+ return xres == other.xres &&
+ yres == other.yres &&
+ startx == other.startx &&
+ starty == other.starty &&
+ pixels == other.pixels &&
+ requested_pixels == other.requested_pixels &&
+ lines == other.lines &&
+ depth == other.depth &&
+ channels == other.channels &&
+ scan_method == other.scan_method &&
+ scan_mode == other.scan_mode &&
+ color_filter == other.color_filter &&
+ flags == other.flags;
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const SetupParams& params);
+
+template<class Stream>
+void serialize(Stream& str, SetupParams& x)
+{
+ serialize(str, x.xres);
+ serialize(str, x.yres);
+ serialize(str, x.startx);
+ serialize(str, x.starty);
+ serialize(str, x.pixels);
+ serialize(str, x.requested_pixels);
+ serialize(str, x.lines);
+ serialize(str, x.depth);
+ serialize(str, x.channels);
+ serialize(str, x.scan_method);
+ serialize(str, x.scan_mode);
+ serialize(str, x.color_filter);
+ serialize(str, x.flags);
+}
+
+struct ScanSession {
+ SetupParams params;
+
+ // whether the session setup has been computed via compute_session()
+ bool computed = false;
+
+ // specifies the reduction (if any) of hardware dpi on the Genesys chip side.
+ // except gl646
+ unsigned hwdpi_divisor = 1;
+
+ // specifies the reduction (if any) of CCD effective dpi which is performed by latching the
+ // data coming from CCD in such a way that 1/2 or 3/4 of pixel data is ignored.
+ unsigned ccd_size_divisor = 1;
+
+ // the optical resolution of the scanner.
+ unsigned optical_resolution = 0;
+
+ // the number of pixels at the optical resolution, not including segmentation overhead.
+ unsigned optical_pixels = 0;
+
+ // the number of pixels at the optical resolution, including segmentation overhead.
+ // only on gl846, g847
+ unsigned optical_pixels_raw = 0;
+
+ // the resolution of the output data.
+ // gl843-only
+ unsigned output_resolution = 0;
+
+ // the number of pixels in output data (after desegmentation)
+ unsigned output_pixels = 0;
+
+ // the number of bytes in the output of a channel of a single line (after desegmentation)
+ unsigned output_channel_bytes = 0;
+
+ // the number of bytes in the output of a single line (after desegmentation)
+ unsigned output_line_bytes = 0;
+
+ // the number of bytes per line in the output data from the scanner (before desegmentation)
+ // Equal to output_line_bytes if sensor does not have segments
+ unsigned output_line_bytes_raw = 0;
+
+ // the number of bytes per line as requested by the frontend
+ unsigned output_line_bytes_requested = 0;
+
+ // the number of lines in the output of the scanner. This must be larger than the user
+ // requested number due to line staggering and color channel shifting.
+ unsigned output_line_count = 0;
+
+ // the total number of bytes to read from the scanner (before desegmentation)
+ unsigned output_total_bytes_raw = 0;
+
+ // the total number of bytes to read from the scanner (after desegmentation)
+ unsigned output_total_bytes = 0;
+
+ // the number of staggered lines (i.e. lines that overlap during scanning due to line being
+ // thinner than the CCD element)
+ unsigned num_staggered_lines = 0;
+
+ // the number of lines that color channels shift due to different physical positions of
+ // different color channels.
+ unsigned max_color_shift_lines = 0;
+
+ // actual line shift of the red color
+ unsigned color_shift_lines_r = 0;
+ // actual line shift of the green color
+ unsigned color_shift_lines_g = 0;
+ // actual line shift of the blue color
+ unsigned color_shift_lines_b = 0;
+
+ // the number of scanner segments used in the current scan
+ unsigned segment_count = 1;
+
+ // the physical pixel positions that are sent to the registers
+ unsigned pixel_startx = 0;
+ unsigned pixel_endx = 0;
+
+ // certain scanners require the logical pixel count to be multiplied on certain resolutions
+ unsigned pixel_count_multiplier = 1;
+
+ // Distance in pixels between consecutive pixels, e.g. between odd and even pixels. Note that
+ // the number of segments can be large.
+ // only on gl124, gl846, gl847
+ unsigned conseq_pixel_dist = 0;
+
+ // The number of "even" pixels to scan. This corresponds to the number of pixels that will be
+ // scanned from a single segment
+ // only on gl124, gl846, gl847
+ unsigned output_segment_pixel_group_count = 0;
+
+ // The number of bytes to skip at start of line during desegmentation.
+ // Currently it's always zero.
+ unsigned output_segment_start_offset = 0;
+
+ // the sizes of the corresponding buffers
+ size_t buffer_size_read = 0;
+ size_t buffer_size_lines = 0;
+ size_t buffer_size_shrink = 0;
+ size_t buffer_size_out = 0;
+
+ // whether to enable ledadd functionality
+ bool enable_ledadd = false;
+
+ // what pipeline modifications are needed
+ bool pipeline_needs_reorder = false;
+ bool pipeline_needs_ccd = false;
+ bool pipeline_needs_shrink = false;
+
+ void assert_computed() const
+ {
+ if (!computed) {
+ throw std::runtime_error("ScanSession is not computed");
+ }
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const ScanSession& session);
+
+std::ostream& operator<<(std::ostream& out, const SANE_Parameters& params);
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_SETTINGS_H
diff --git a/backend/genesys/static_init.cpp b/backend/genesys/static_init.cpp
new file mode 100644
index 0000000..c0f3748
--- /dev/null
+++ b/backend/genesys/static_init.cpp
@@ -0,0 +1,70 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "static_init.h"
+#include <vector>
+
+namespace genesys {
+
+static std::unique_ptr<std::vector<std::function<void()>>> s_functions_run_at_backend_exit;
+
+void add_function_to_run_at_backend_exit(const std::function<void()>& function)
+{
+ if (!s_functions_run_at_backend_exit)
+ s_functions_run_at_backend_exit.reset(new std::vector<std::function<void()>>());
+ s_functions_run_at_backend_exit->push_back(std::move(function));
+}
+
+void run_functions_at_backend_exit()
+{
+ for (auto it = s_functions_run_at_backend_exit->rbegin();
+ it != s_functions_run_at_backend_exit->rend(); ++it)
+ {
+ (*it)();
+ }
+ s_functions_run_at_backend_exit.reset();
+}
+
+} // namespace genesys
diff --git a/backend/genesys/static_init.h b/backend/genesys/static_init.h
new file mode 100644
index 0000000..3ffa62c
--- /dev/null
+++ b/backend/genesys/static_init.h
@@ -0,0 +1,88 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_STATIC_INIT_H
+#define BACKEND_GENESYS_STATIC_INIT_H
+
+#include <functional>
+#include <memory>
+
+namespace genesys {
+
+void add_function_to_run_at_backend_exit(const std::function<void()>& function);
+
+// calls functions added via add_function_to_run_at_backend_exit() in reverse order of being
+// added.
+void run_functions_at_backend_exit();
+
+template<class T>
+class StaticInit {
+public:
+ StaticInit() = default;
+ StaticInit(const StaticInit&) = delete;
+ StaticInit& operator=(const StaticInit&) = delete;
+
+ template<class... Args>
+ void init(Args&& ... args)
+ {
+ ptr_ = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ add_function_to_run_at_backend_exit([this](){ deinit(); });
+ }
+
+ void deinit()
+ {
+ ptr_.reset();
+ }
+
+ const T* operator->() const { return ptr_.get(); }
+ T* operator->() { return ptr_.get(); }
+ const T& operator*() const { return *ptr_.get(); }
+ T& operator*() { return *ptr_.get(); }
+
+private:
+ std::unique_ptr<T> ptr_;
+};
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_STATIC_INIT_H
diff --git a/backend/genesys/status.cpp b/backend/genesys/status.cpp
new file mode 100644
index 0000000..7f883b0
--- /dev/null
+++ b/backend/genesys/status.cpp
@@ -0,0 +1,66 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "status.h"
+#include <iostream>
+
+namespace genesys {
+
+std::ostream& operator<<(std::ostream& out, Status status)
+{
+ out << "Status{\n"
+ << " replugged: " << (status.is_replugged ? "yes" : "no") << '\n'
+ << " is_buffer_empty: " << (status.is_buffer_empty ? "yes" : "no") << '\n'
+ << " is_feeding_finished: " << (status.is_feeding_finished ? "yes" : "no") << '\n'
+ << " is_scanning_finished: " << (status.is_scanning_finished ? "yes" : "no") << '\n'
+ << " is_at_home: " << (status.is_at_home ? "yes" : "no") << '\n'
+ << " is_lamp_on: " << (status.is_lamp_on ? "yes" : "no") << '\n'
+ << " is_front_end_busy: " << (status.is_front_end_busy ? "yes" : "no") << '\n'
+ << " is_motor_enabled: " << (status.is_motor_enabled ? "yes" : "no") << '\n'
+ << "}\n";
+ return out;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/status.h b/backend/genesys/status.h
new file mode 100644
index 0000000..91f4692
--- /dev/null
+++ b/backend/genesys/status.h
@@ -0,0 +1,68 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_STATUS_H
+#define BACKEND_GENESYS_STATUS_H
+
+#include <iosfwd>
+
+namespace genesys {
+
+/// Represents the scanner status register
+struct Status
+{
+ bool is_replugged = false;
+ bool is_buffer_empty = false;
+ bool is_feeding_finished = false;
+ bool is_scanning_finished = false;
+ bool is_at_home = false;
+ bool is_lamp_on = false;
+ bool is_front_end_busy = false;
+ bool is_motor_enabled = false;
+};
+
+std::ostream& operator<<(std::ostream& out, Status status);
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_STATUS_H
diff --git a/backend/genesys/tables_frontend.cpp b/backend/genesys/tables_frontend.cpp
new file mode 100644
index 0000000..1edf32f
--- /dev/null
+++ b/backend/genesys/tables_frontend.cpp
@@ -0,0 +1,653 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "low.h"
+
+namespace genesys {
+
+StaticInit<std::vector<Genesys_Frontend>> s_frontends;
+
+void genesys_init_frontend_tables()
+{
+ s_frontends.init();
+
+ GenesysFrontendLayout wolfson_layout;
+ wolfson_layout.type = FrontendType::WOLFSON;
+ wolfson_layout.offset_addr = { 0x20, 0x21, 0x22 };
+ wolfson_layout.gain_addr = { 0x28, 0x29, 0x2a };
+
+ GenesysFrontendLayout analog_devices;
+ analog_devices.type = FrontendType::ANALOG_DEVICES;
+
+
+ Genesys_Frontend fe;
+ fe.id = AdcId::WOLFSON_UMAX;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x03 },
+ { 0x02, 0x05 },
+ { 0x03, 0x11 },
+ { 0x20, 0x80 },
+ { 0x21, 0x80 },
+ { 0x22, 0x80 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x02 },
+ { 0x29, 0x02 },
+ { 0x2a, 0x02 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_ST12;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x03 },
+ { 0x02, 0x05 },
+ { 0x03, 0x03 },
+ { 0x20, 0xc8 },
+ { 0x21, 0xc8 },
+ { 0x22, 0xc8 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x04 },
+ { 0x29, 0x04 },
+ { 0x2a, 0x04 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_ST24;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x03 },
+ { 0x02, 0x05 },
+ { 0x03, 0x21 },
+ { 0x20, 0xc8 },
+ { 0x21, 0xc8 },
+ { 0x22, 0xc8 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x06 },
+ { 0x29, 0x06 },
+ { 0x2a, 0x06 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_5345;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x03 },
+ { 0x02, 0x05 },
+ { 0x03, 0x12 },
+ { 0x20, 0xb8 },
+ { 0x21, 0xb8 },
+ { 0x22, 0xb8 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x04 },
+ { 0x29, 0x04 },
+ { 0x2a, 0x04 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ // reg3=0x02 for 50-600 dpi, 0x32 (0x12 also works well) at 1200
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_HP2400;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x03 },
+ { 0x02, 0x05 },
+ { 0x03, 0x02 },
+ { 0x20, 0xb4 },
+ { 0x21, 0xb6 },
+ { 0x22, 0xbc },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x06 },
+ { 0x29, 0x09 },
+ { 0x2a, 0x08 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_HP2300;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x03 },
+ { 0x02, 0x04 },
+ { 0x03, 0x02 },
+ { 0x20, 0xbe },
+ { 0x21, 0xbe },
+ { 0x22, 0xbe },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x04 },
+ { 0x29, 0x04 },
+ { 0x2a, 0x04 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_LIDE_35;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x3d },
+ { 0x02, 0x08 },
+ { 0x03, 0x00 },
+ { 0x20, 0xe1 },
+ { 0x21, 0xe1 },
+ { 0x22, 0xe1 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x93 },
+ { 0x29, 0x93 },
+ { 0x2a, 0x93 },
+ };
+ fe.reg2 = {0x00, 0x19, 0x06};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::AD_XP200;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x58 },
+ { 0x01, 0x80 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x20, 0x09 },
+ { 0x21, 0x09 },
+ { 0x22, 0x09 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x09 },
+ { 0x29, 0x09 },
+ { 0x2a, 0x09 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_XP300;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x35 },
+ { 0x02, 0x20 },
+ { 0x03, 0x14 },
+ { 0x20, 0xe1 },
+ { 0x21, 0xe1 },
+ { 0x22, 0xe1 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x93 },
+ { 0x29, 0x93 },
+ { 0x2a, 0x93 },
+ };
+ fe.reg2 = {0x07, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_HP3670;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x03 },
+ { 0x02, 0x05 },
+ { 0x03, 0x32 },
+ { 0x20, 0xba },
+ { 0x21, 0xb8 },
+ { 0x22, 0xb8 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x06 },
+ { 0x29, 0x05 },
+ { 0x2a, 0x04 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::WOLFSON_DSM600;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x35 },
+ { 0x02, 0x20 },
+ { 0x03, 0x14 },
+ { 0x20, 0x85 },
+ { 0x21, 0x85 },
+ { 0x22, 0x85 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0xa0 },
+ { 0x29, 0xa0 },
+ { 0x2a, 0xa0 },
+ };
+ fe.reg2 = {0x07, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_LIDE_200;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x9d },
+ { 0x01, 0x91 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x3f },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x32 },
+ { 0x29, 0x04 },
+ { 0x2a, 0x00 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_LIDE_700F;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x9d },
+ { 0x01, 0x9e },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x3f },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x2f },
+ { 0x29, 0x04 },
+ { 0x2a, 0x00 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::KVSS080;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x23 },
+ { 0x02, 0x24 },
+ { 0x03, 0x0f },
+ { 0x20, 0x80 },
+ { 0x21, 0x80 },
+ { 0x22, 0x80 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x4b },
+ { 0x29, 0x4b },
+ { 0x2a, 0x4b },
+ };
+ fe.reg2 = {0x00,0x00,0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::G4050;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x23 },
+ { 0x02, 0x24 },
+ { 0x03, 0x1f },
+ { 0x20, 0x45 },
+ { 0x21, 0x45 },
+ { 0x22, 0x45 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x4b },
+ { 0x29, 0x4b },
+ { 0x2a, 0x4b },
+ };
+ fe.reg2 = {0x00,0x00,0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_LIDE_110;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x80 },
+ { 0x01, 0x8a },
+ { 0x02, 0x23 },
+ { 0x03, 0x4c },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0xca },
+ { 0x26, 0x94 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x00 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+ /** @brief GL124 special case
+ * for GL124 based scanners, this struct is "abused"
+ * in fact the fields are map like below to AFE registers
+ * (from Texas Instrument or alike ?)
+ */
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_LIDE_120;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x80 },
+ { 0x01, 0xa3 },
+ { 0x02, 0x2b },
+ { 0x03, 0x4c },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 }, // actual address 0x05
+ { 0x25, 0xca }, // actual address 0x06
+ { 0x26, 0x95 }, // actual address 0x07
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x00 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::PLUSTEK_OPTICPRO_3600;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x70 },
+ { 0x01, 0x80 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x3f },
+ { 0x29, 0x3d },
+ { 0x2a, 0x3d },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::PLUSTEK_OPTICFILM_7200I;
+ fe.layout = analog_devices;
+ fe.regs = {
+ { 0x00, 0xf8 },
+ { 0x01, 0x80 },
+ { 0x02, 0x0a },
+ { 0x03, 0x06 },
+ { 0x04, 0x0f },
+ { 0x05, 0x56 },
+ { 0x06, 0x64 },
+ { 0x07, 0x56 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::PLUSTEK_OPTICFILM_7300;
+ fe.layout = analog_devices;
+ fe.regs = {
+ { 0x00, 0xf8 },
+ { 0x01, 0x80 },
+ { 0x02, 0x10 },
+ { 0x03, 0x06 },
+ { 0x04, 0x06 },
+ { 0x05, 0x09 },
+ { 0x06, 0x0a },
+ { 0x07, 0x0102 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::PLUSTEK_OPTICFILM_7500I;
+ fe.layout = analog_devices;
+ fe.regs = {
+ { 0x00, 0xf8 },
+ { 0x01, 0x80 },
+ { 0x02, 0x1d },
+ { 0x03, 0x17 },
+ { 0x04, 0x13 },
+ { 0x05, 0x00 },
+ { 0x06, 0x00 },
+ { 0x07, 0x0111 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_4400F;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x23 },
+ { 0x02, 0x24 },
+ { 0x03, 0x2f },
+ { 0x20, 0x6d },
+ { 0x21, 0x67 },
+ { 0x22, 0x5b },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0xd8 },
+ { 0x29, 0xd1 },
+ { 0x2a, 0xb9 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_8400F;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x23 },
+ { 0x02, 0x24 },
+ { 0x03, 0x0f },
+ { 0x20, 0x60 },
+ { 0x21, 0x5c },
+ { 0x22, 0x6c },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x8a },
+ { 0x29, 0x9f },
+ { 0x2a, 0xc2 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_8600F;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x00 },
+ { 0x01, 0x23 },
+ { 0x02, 0x24 },
+ { 0x03, 0x2f },
+ { 0x20, 0x67 },
+ { 0x21, 0x69 },
+ { 0x22, 0x68 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0xdb },
+ { 0x29, 0xda },
+ { 0x2a, 0xd7 },
+ };
+ fe.reg2 = { 0x00, 0x00, 0x00 };
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::IMG101;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x78 },
+ { 0x01, 0xf0 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x00 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ fe = Genesys_Frontend();
+ fe.id = AdcId::PLUSTEK_OPTICBOOK_3800;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x78 },
+ { 0x01, 0xf0 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x00 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+
+
+ /* reg0: control 74 data, 70 no data
+ * reg3: offset
+ * reg6: gain
+ * reg0 , reg3, reg6 */
+ fe = Genesys_Frontend();
+ fe.id = AdcId::CANON_LIDE_80;
+ fe.layout = wolfson_layout;
+ fe.regs = {
+ { 0x00, 0x70 },
+ { 0x01, 0x16 },
+ { 0x02, 0x60 },
+ { 0x03, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x00 },
+ };
+ fe.reg2 = {0x00, 0x00, 0x00};
+ s_frontends->push_back(fe);
+}
+
+} // namespace genesys
diff --git a/backend/genesys/tables_gpo.cpp b/backend/genesys/tables_gpo.cpp
new file mode 100644
index 0000000..2c9ad5e
--- /dev/null
+++ b/backend/genesys/tables_gpo.cpp
@@ -0,0 +1,415 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "low.h"
+
+namespace genesys {
+
+StaticInit<std::vector<Genesys_Gpo>> s_gpo;
+
+void genesys_init_gpo_tables()
+{
+ s_gpo.init();
+
+ Genesys_Gpo gpo;
+ gpo.id = GpioId::UMAX;
+ gpo.regs = {
+ { 0x66, 0x11 },
+ { 0x67, 0x00 },
+ { 0x68, 0x51 },
+ { 0x69, 0x20 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::ST12;
+ gpo.regs = {
+ { 0x66, 0x11 },
+ { 0x67, 0x00 },
+ { 0x68, 0x51 },
+ { 0x69, 0x20 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::ST24;
+ gpo.regs = {
+ { 0x66, 0x00 },
+ { 0x67, 0x00 },
+ { 0x68, 0x51 },
+ { 0x69, 0x20 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::MD_5345; // bits 11-12 are for bipolar V-ref input voltage
+ gpo.regs = {
+ { 0x66, 0x30 },
+ { 0x67, 0x18 },
+ { 0x68, 0xa0 },
+ { 0x69, 0x18 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::HP2400;
+ gpo.regs = {
+ { 0x66, 0x30 },
+ { 0x67, 0x00 },
+ { 0x68, 0x31 },
+ { 0x69, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::HP2300;
+ gpo.regs = {
+ { 0x66, 0x00 },
+ { 0x67, 0x00 },
+ { 0x68, 0x00 },
+ { 0x69, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_LIDE_35;
+ gpo.regs = {
+ { 0x6c, 0x02 },
+ { 0x6d, 0x80 },
+ { 0x6e, 0xef },
+ { 0x6f, 0x80 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::XP200;
+ gpo.regs = {
+ { 0x66, 0x30 },
+ { 0x67, 0x00 },
+ { 0x68, 0xb0 },
+ { 0x69, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::HP3670;
+ gpo.regs = {
+ { 0x66, 0x00 },
+ { 0x67, 0x00 },
+ { 0x68, 0x00 },
+ { 0x69, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::XP300;
+ gpo.regs = {
+ { 0x6c, 0x09 },
+ { 0x6d, 0xc6 },
+ { 0x6e, 0xbb },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::DP665;
+ gpo.regs = {
+ { 0x6c, 0x18 },
+ { 0x6d, 0x00 },
+ { 0x6e, 0xbb },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::DP685;
+ gpo.regs = {
+ { 0x6c, 0x3f },
+ { 0x6d, 0x46 },
+ { 0x6e, 0xfb },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_LIDE_200;
+ gpo.regs = {
+ { 0x6c, 0xfb }, // 0xfb when idle , 0xf9/0xe9 (1200) when scanning
+ { 0x6d, 0x20 },
+ { 0x6e, 0xff },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_LIDE_700F;
+ gpo.regs = {
+ { 0x6c, 0xdb },
+ { 0x6d, 0xff },
+ { 0x6e, 0xff },
+ { 0x6f, 0x80 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::KVSS080;
+ gpo.regs = {
+ { 0x6c, 0xf5 },
+ { 0x6d, 0x20 },
+ { 0x6e, 0x7e },
+ { 0x6f, 0xa1 },
+ { 0xa6, 0x06 },
+ { 0xa7, 0x0f },
+ { 0xa8, 0x00 },
+ { 0xa9, 0x08 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::G4050;
+ gpo.regs = {
+ { 0x6c, 0x20 },
+ { 0x6d, 0x00 },
+ { 0x6e, 0xfc },
+ { 0x6f, 0x00 },
+ { 0xa6, 0x08 },
+ { 0xa7, 0x1e },
+ { 0xa8, 0x3e },
+ { 0xa9, 0x06 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::HP_N6310;
+ gpo.regs = {
+ { 0x6c, 0xa3 },
+ { 0x6d, 0x00 },
+ { 0x6e, 0x7f },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_LIDE_110;
+ gpo.regs = {
+ { 0x6c, 0xfb },
+ { 0x6d, 0x20 },
+ { 0x6e, 0xff },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_LIDE_120;
+ gpo.regs = {
+ { 0x6c, 0xfb },
+ { 0x6d, 0x20 },
+ { 0x6e, 0xff },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_LIDE_210;
+ gpo.regs = {
+ { 0x6c, 0xfb },
+ { 0x6d, 0x20 },
+ { 0x6e, 0xff },
+ { 0x6f, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::PLUSTEK_OPTICPRO_3600;
+ gpo.regs = {
+ { 0x6c, 0x02 },
+ { 0x6d, 0x00 },
+ { 0x6e, 0x1e },
+ { 0x6f, 0x80 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::PLUSTEK_OPTICFILM_7200I;
+ gpo.regs = {
+ { 0x6c, 0x4c },
+ { 0x6d, 0x80 },
+ { 0x6e, 0x4c },
+ { 0x6f, 0x80 },
+ { 0xa6, 0x00 },
+ { 0xa7, 0x07 },
+ { 0xa8, 0x20 },
+ { 0xa9, 0x01 },
+ };
+ s_gpo->push_back(gpo);
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::PLUSTEK_OPTICFILM_7300;
+ gpo.regs = {
+ { 0x6c, 0x4c },
+ { 0x6d, 0x00 },
+ { 0x6e, 0x4c },
+ { 0x6f, 0x80 },
+ { 0xa6, 0x00 },
+ { 0xa7, 0x07 },
+ { 0xa8, 0x20 },
+ { 0xa9, 0x01 },
+ };
+ s_gpo->push_back(gpo);
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::PLUSTEK_OPTICFILM_7500I;
+ gpo.regs = {
+ { 0x6c, 0x4c },
+ { 0x6d, 0x00 },
+ { 0x6e, 0x4c },
+ { 0x6f, 0x80 },
+ { 0xa6, 0x00 },
+ { 0xa7, 0x07 },
+ { 0xa8, 0x20 },
+ { 0xa9, 0x01 },
+ };
+ s_gpo->push_back(gpo);
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_4400F;
+ gpo.regs = {
+ { 0x6c, 0x01 },
+ { 0x6d, 0x7f },
+ { 0x6e, 0xff },
+ { 0x6f, 0x00 },
+ { 0xa6, 0x00 },
+ { 0xa7, 0xff },
+ { 0xa8, 0x07 },
+ { 0xa9, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_8400F;
+ gpo.regs = {
+ { 0x6c, 0x9a },
+ { 0x6d, 0xdf },
+ { 0x6e, 0xfe },
+ { 0x6f, 0x60 },
+ { 0xa6, 0x00 },
+ { 0xa7, 0x03 },
+ { 0xa8, 0x00 },
+ { 0xa9, 0x02 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_8600F;
+ gpo.regs = {
+ { 0x6c, 0x20 },
+ { 0x6d, 0x7c },
+ { 0x6e, 0xff },
+ { 0x6f, 0x00 },
+ { 0xa6, 0x00 },
+ { 0xa7, 0xff },
+ { 0xa8, 0x00 },
+ { 0xa9, 0x00 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::IMG101;
+ gpo.regs = {
+ { 0x6c, 0x41 },
+ { 0x6d, 0xa4 },
+ { 0x6e, 0x13 },
+ { 0x6f, 0xa7 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::PLUSTEK_OPTICBOOK_3800;
+ gpo.regs = {
+ { 0x6c, 0x41 },
+ { 0x6d, 0xa4 },
+ { 0x6e, 0x13 },
+ { 0x6f, 0xa7 },
+ };
+ s_gpo->push_back(gpo);
+
+
+ gpo = Genesys_Gpo();
+ gpo.id = GpioId::CANON_LIDE_80;
+ gpo.regs = {
+ { 0x6c, 0x28 },
+ { 0x6d, 0x90 },
+ { 0x6e, 0x75 },
+ { 0x6f, 0x80 },
+ };
+ s_gpo->push_back(gpo);
+}
+
+} // namespace genesys
diff --git a/backend/genesys/tables_model.cpp b/backend/genesys/tables_model.cpp
new file mode 100644
index 0000000..0b3a0af
--- /dev/null
+++ b/backend/genesys/tables_model.cpp
@@ -0,0 +1,2958 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2003 Oliver Rauch
+ Copyright (C) 2003-2005 Henning Meier-Geinitz <henning@meier-geinitz.de>
+ Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
+ Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
+ Copyright (C) 2007 Luke <iceyfor@gmail.com>
+ Copyright (C) 2010 Jack McGill <jmcgill85258@yahoo.com>
+ Copyright (C) 2010 Andrey Loginov <avloginov@gmail.com>,
+ xerox travelscan device entry
+ Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
+ for Plustek Opticbook 3600 support
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "low.h"
+
+namespace genesys {
+
+StaticInit<std::vector<Genesys_USB_Device_Entry>> s_usb_devices;
+
+void genesys_init_usb_device_tables()
+{
+ s_usb_devices.init();
+
+ Genesys_Model model;
+ model.name = "umax-astra-4500";
+ model.vendor = "UMAX";
+ model.model = "Astra 4500";
+ model.model_id = ModelId::UMAX_ASTRA_4500;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 75 },
+ { 2400, 1200, 600, 300, 150, 75 }
+ }
+ };
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 3.5;
+ model.y_offset = 7.5;
+ model.x_size = 218.0;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 1.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 8;
+ model.ld_shift_b = 16;
+
+ model.line_mode_color_order = ColorOrder::BGR;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_UMAX;
+ model.adc_id = AdcId::WOLFSON_UMAX;
+ model.gpio_id = GpioId::UMAX;
+ model.motor_id = MotorId::UMAX;
+ model.flags = GENESYS_FLAG_UNTESTED;
+ model.buttons = GENESYS_HAS_NO_BUTTONS;
+ model.shading_lines = 20;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x0638, 0x0a10, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-50";
+ model.vendor = "Canon";
+ model.model = "LiDE 35/40/50";
+ model.model_id = ModelId::CANON_LIDE_50;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 200, 150, 75 },
+ { 2400, 1200, 600, 300, 200, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.42;
+ model.y_offset = 7.9;
+ model.x_size = 218.0;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 6.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_35;
+ model.adc_id = AdcId::CANON_LIDE_35;
+ model.gpio_id = GpioId::CANON_LIDE_35;
+ model.motor_id = MotorId::CANON_LIDE_35;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_WHITE_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_FILE_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_COPY_SW;
+ model.shading_lines = 280;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x2213, model);
+
+
+ model = Genesys_Model();
+ model.name = "panasonic-kv-ss080";
+ model.vendor = "Panasonic";
+ model.model = "KV-SS080";
+ model.model_id = ModelId::PANASONIC_KV_SS080;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, /* 500, 400,*/ 300, 200, 150, 100, 75 },
+ { 1200, 600, /* 500, 400, */ 300, 200, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 7.2;
+ model.y_offset = 14.7;
+ model.x_size = 217.7;
+ model.y_size = 300.0;
+
+ model.y_offset_calib_white = 9.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 0.0;
+ model.y_size_ta = 0.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 8;
+ model.ld_shift_b = 16;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_KVSS080;
+ model.adc_id = AdcId::KVSS080;
+ model.gpio_id = GpioId::KVSS080;
+ model.motor_id = MotorId::KVSS080;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x04da, 0x100f, model);
+
+
+ model = Genesys_Model();
+ model.name = "hewlett-packard-scanjet-4850c";
+ model.vendor = "Hewlett Packard";
+ model.model = "ScanJet 4850C";
+ model.model_id = ModelId::HP_SCANJET_4850C;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100 },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 7.9;
+ model.y_offset = 10.0;
+ model.x_size = 219.6;
+ model.y_size = 314.5;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_HP_4850C;
+ model.adc_id = AdcId::G4050;
+ model.gpio_id = GpioId::G4050;
+ model.motor_id = MotorId::G4050;
+ model.flags = GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+ s_usb_devices->emplace_back(0x03f0, 0x1b05, model);
+
+
+ model = Genesys_Model();
+ model.name = "hewlett-packard-scanjet-g4010";
+ model.vendor = "Hewlett Packard";
+ model.model = "ScanJet G4010";
+ model.model_id = ModelId::HP_SCANJET_G4010;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100 },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 8.0;
+ model.y_offset = 13.00;
+ model.x_size = 217.9;
+ model.y_size = 315.0;
+
+ model.y_offset_calib_white = 3.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_G4050;
+ model.adc_id = AdcId::G4050;
+ model.gpio_id = GpioId::G4050;
+ model.motor_id = MotorId::G4050;
+ model.flags = GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x03f0, 0x4505, model);
+
+
+ model = Genesys_Model();
+ model.name = "hewlett-packard-scanjet-g4050";
+ model.vendor = "Hewlett Packard";
+ model.model = "ScanJet G4050";
+ model.model_id = ModelId::HP_SCANJET_G4050;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100 },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 8.0;
+ model.y_offset = 10.00;
+ model.x_size = 217.9;
+ model.y_size = 315.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 8.0;
+ model.y_offset_ta = 13.00;
+ model.x_size_ta = 217.9;
+ model.y_size_ta = 250.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 40.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_G4050;
+ model.adc_id = AdcId::G4050;
+ model.gpio_id = GpioId::G4050;
+ model.motor_id = MotorId::G4050;
+ model.flags = GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x03f0, 0x4605, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-canoscan-4400f";
+ model.vendor = "Canon";
+ model.model = "Canoscan 4400f";
+ model.model_id = ModelId::CANON_4400F;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300 },
+ { 1200, 600, 300 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 6.0;
+ model.y_offset = 12.00;
+ model.x_size = 215.9;
+ model.y_size = 297.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 8.0;
+ model.y_offset_ta = 13.00;
+ model.x_size_ta = 217.9;
+ model.y_size_ta = 250.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 40.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 96;
+ model.ld_shift_g = 48;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_CANON_4400F;
+ model.adc_id = AdcId::CANON_4400F;
+ model.gpio_id = GpioId::CANON_4400F;
+ model.motor_id = MotorId::CANON_4400F;
+ model.flags = GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_FULL_HWDPI_MODE |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SHADING_REPARK;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x04a9, 0x2228, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-canoscan-8400f";
+ model.vendor = "Canon";
+ model.model = "Canoscan 8400f";
+ model.model_id = ModelId::CANON_8400F;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 3200, 1600, 800, 400 },
+ { 3200, 1600, 800, 400 },
+ }, {
+ { ScanMethod::TRANSPARENCY },
+ { 3200, 1600, 800, 400 },
+ { 3200, 1600, 800, 400 },
+ }, {
+ { ScanMethod::TRANSPARENCY_INFRARED },
+ { 3200, 1600, 800, 400 },
+ { 3200, 1600, 800, 400 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 3.5;
+ model.y_offset = 17.00;
+ model.x_size = 219.9;
+ model.y_size = 300.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 10.0;
+
+ model.x_offset_ta = 75.0;
+ model.y_offset_ta = 45.00;
+ model.x_size_ta = 75.0;
+ model.y_size_ta = 230.0;
+
+ model.y_offset_sensor_to_ta = 22.0;
+ model.y_offset_calib_white_ta = 25.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_CANON_8400F;
+ model.adc_id = AdcId::CANON_8400F;
+ model.gpio_id = GpioId::CANON_8400F;
+ model.motor_id = MotorId::CANON_8400F;
+ model.flags = GENESYS_FLAG_HAS_UTA |
+ GENESYS_FLAG_HAS_UTA_INFRARED |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_FULL_HWDPI_MODE |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SHADING_REPARK;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 50;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x04a9, 0x221e, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-canoscan-8600f";
+ model.vendor = "Canon";
+ model.model = "Canoscan 8600f";
+ model.model_id = ModelId::CANON_8600F;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300 },
+ { 1200, 600, 300 },
+ }, {
+ { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED },
+ { 4800, 2400, 1200, 600, 300 },
+ { 4800, 2400, 1200, 600, 300 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 24.0;
+ model.y_offset = 10.0;
+ model.x_size = 216.0;
+ model.y_size = 297.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 8.0;
+
+ model.x_offset_ta = 85.0;
+ model.y_offset_ta = 26.0;
+ model.x_size_ta = 70.0;
+ model.y_size_ta = 230.0;
+
+ model.y_offset_sensor_to_ta = 11.5;
+ model.y_offset_calib_white_ta = 14.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 48;
+ model.ld_shift_b = 96;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_CANON_8600F;
+ model.adc_id = AdcId::CANON_8600F;
+ model.gpio_id = GpioId::CANON_8600F;
+ model.motor_id = MotorId::CANON_8600F;
+ model.flags = GENESYS_FLAG_HAS_UTA |
+ GENESYS_FLAG_HAS_UTA_INFRARED |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_FULL_HWDPI_MODE |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SHADING_REPARK;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW;
+ model.shading_lines = 50;
+ model.shading_ta_lines = 50;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x04a9, 0x2229, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-100";
+ model.vendor = "Canon";
+ model.model = "LiDE 100";
+ model.model_id = ModelId::CANON_LIDE_100;
+ model.asic_type = AsicType::GL847;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 2400, 1200, 600, 300, 200, 150, 100, 75 },
+ { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 1.1;
+ model.y_offset = 8.3;
+ model.x_size = 216.07;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 1.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_100;
+ model.adc_id = AdcId::CANON_LIDE_200;
+ model.gpio_id = GpioId::CANON_LIDE_200;
+ model.motor_id = MotorId::CANON_LIDE_100;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_SIS_SENSOR |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW;
+ model.shading_lines = 50;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x1904, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-110";
+ model.vendor = "Canon";
+ model.model = "LiDE 110";
+ model.model_id = ModelId::CANON_LIDE_110;
+ model.asic_type = AsicType::GL124;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 },
+ { 4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 2.2;
+ model.y_offset = 9.0;
+ model.x_size = 216.70;
+ model.y_size = 300.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_110;
+ model.adc_id = AdcId::CANON_LIDE_110;
+ model.gpio_id = GpioId::CANON_LIDE_110;
+ model.motor_id = MotorId::CANON_LIDE_110;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW;
+ model.shading_lines = 25;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x1909, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-120";
+ model.vendor = "Canon";
+ model.model = "LiDE 120";
+ model.model_id = ModelId::CANON_LIDE_120;
+ model.asic_type = AsicType::GL124;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 2400, 1200, 600, 300, 150, 100, 75 },
+ { 4800, 2400, 1200, 600, 300, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 8.0;
+ model.x_size = 216.0;
+ model.y_size = 300.0;
+
+ model.y_offset_calib_white = 1.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+ model.line_mode_color_order = ColorOrder::RGB;
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_120;
+ model.adc_id = AdcId::CANON_LIDE_120;
+ model.gpio_id = GpioId::CANON_LIDE_120;
+ model.motor_id = MotorId::CANON_LIDE_120;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW;
+ model.shading_lines = 50;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x190e, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-210";
+ model.vendor = "Canon";
+ model.model = "LiDE 210";
+ model.model_id = ModelId::CANON_LIDE_210;
+ model.asic_type = AsicType::GL124;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ // BUG: 4800 resolution crashes
+ { /*4800,*/ 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 },
+ { /*4800,*/ 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 2.2;
+ model.y_offset = 8.7;
+ model.x_size = 216.70;
+ model.y_size = 297.5;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_210;
+ model.adc_id = AdcId::CANON_LIDE_110;
+ model.gpio_id = GpioId::CANON_LIDE_210;
+ model.motor_id = MotorId::CANON_LIDE_210;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW |
+ GENESYS_HAS_EXTRA_SW;
+ model.shading_lines = 60;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x190a, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-220";
+ model.vendor = "Canon";
+ model.model = "LiDE 220";
+ model.model_id = ModelId::CANON_LIDE_220;
+ model.asic_type = AsicType::GL124; // or a compatible one
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ // BUG: 4800 resolution crashes
+ { /*4800,*/ 2400, 1200, 600, 300, 150, 100, 75 },
+ { /*4800,*/ 2400, 1200, 600, 300, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 2.2;
+ model.y_offset = 8.7;
+ model.x_size = 216.70;
+ model.y_size = 297.5;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_220;
+ model.adc_id = AdcId::CANON_LIDE_110;
+ model.gpio_id = GpioId::CANON_LIDE_210;
+ model.motor_id = MotorId::CANON_LIDE_210;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW |
+ GENESYS_HAS_EXTRA_SW;
+ model.shading_lines = 60;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x190f, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-5600f";
+ model.vendor = "Canon";
+ model.model = "5600F";
+ model.model_id = ModelId::CANON_5600F;
+ model.asic_type = AsicType::GL847;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 75 },
+ { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 1.1;
+ model.y_offset = 8.3;
+ model.x_size = 216.07;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 3.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_200;
+ model.adc_id = AdcId::CANON_LIDE_200;
+ model.gpio_id = GpioId::CANON_LIDE_200;
+ model.motor_id = MotorId::CANON_LIDE_200;
+ model.flags = GENESYS_FLAG_UNTESTED |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_SIS_SENSOR |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW;
+ model.shading_lines = 50;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x1906, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-700f";
+ model.vendor = "Canon";
+ model.model = "LiDE 700F";
+ model.model_id = ModelId::CANON_LIDE_700F;
+ model.asic_type = AsicType::GL847;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 },
+ { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 3.1;
+ model.y_offset = 8.1;
+ model.x_size = 216.07;
+ model.y_size = 297.0;
+
+ model.y_offset_calib_white = 1.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_700F;
+ model.adc_id = AdcId::CANON_LIDE_700F;
+ model.gpio_id = GpioId::CANON_LIDE_700F;
+ model.motor_id = MotorId::CANON_LIDE_700;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_SIS_SENSOR |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW;
+ model.shading_lines = 70;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x1907, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-200";
+ model.vendor = "Canon";
+ model.model = "LiDE 200";
+ model.model_id = ModelId::CANON_LIDE_200;
+ model.asic_type = AsicType::GL847;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 },
+ { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 1.1;
+ model.y_offset = 8.3;
+ model.x_size = 216.07;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_200;
+ model.adc_id = AdcId::CANON_LIDE_200;
+ model.gpio_id = GpioId::CANON_LIDE_200;
+ model.motor_id = MotorId::CANON_LIDE_200;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_SIS_SENSOR |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_FILE_SW;
+ model.shading_lines = 50;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x1905, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-60";
+ model.vendor = "Canon";
+ model.model = "LiDE 60";
+ model.model_id = ModelId::CANON_LIDE_60;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 75 },
+ { 2400, 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.42;
+ model.y_offset = 7.9;
+ model.x_size = 218.0;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 6.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_35;
+ model.adc_id = AdcId::CANON_LIDE_35;
+ model.gpio_id = GpioId::CANON_LIDE_35;
+ model.motor_id = MotorId::CANON_LIDE_35;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_WHITE_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+
+ model.buttons = GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_FILE_SW |
+ GENESYS_HAS_EMAIL_SW;
+ model.shading_lines = 300;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+ // this is completely untested
+ s_usb_devices->emplace_back(0x04a9, 0x221c, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-lide-80";
+ model.vendor = "Canon";
+ model.model = "LiDE 80";
+ model.model_id = ModelId::CANON_LIDE_80;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 100, 75 },
+ { 2400, 1200, 600, 300, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+ model.x_offset = 0.42;
+ model.y_offset = 7.90;
+ model.x_size = 216.07;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 4.5;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CIS_CANON_LIDE_80;
+ model.adc_id = AdcId::CANON_LIDE_80;
+ model.gpio_id = GpioId::CANON_LIDE_80;
+ model.motor_id = MotorId::CANON_LIDE_80;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_WHITE_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW |
+ GENESYS_HAS_FILE_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_COPY_SW;
+ model.shading_lines = 160;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a9, 0x2214, model);
+
+
+ model = Genesys_Model();
+ model.name = "hewlett-packard-scanjet-2300c";
+ model.vendor = "Hewlett Packard";
+ model.model = "ScanJet 2300c";
+ model.model_id = ModelId::HP_SCANJET_2300C;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 2.0;
+ model.y_offset = 7.5;
+ model.x_size = 215.9;
+ model.y_size = 295.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 1.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 16;
+ model.ld_shift_g = 8;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_HP2300;
+ model.adc_id = AdcId::WOLFSON_HP2300;
+ model.gpio_id = GpioId::HP2300;
+ model.motor_id = MotorId::HP2300;
+ model.flags = GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_SEARCH_START |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW;
+ model.shading_lines = 40;
+ model.shading_ta_lines = 0;
+ model.search_lines = 132;
+
+ s_usb_devices->emplace_back(0x03f0, 0x0901, model);
+
+
+ model = Genesys_Model();
+ model.name = "hewlett-packard-scanjet-2400c";
+ model.vendor = "Hewlett Packard";
+ model.model = "ScanJet 2400c";
+ model.model_id = ModelId::HP_SCANJET_2400C;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 100, 50 },
+ { 1200, 600, 300, 150, 100, 50 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 6.5;
+ model.y_offset = 2.5;
+ model.x_size = 220.0;
+ model.y_size = 297.2;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 1.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_HP2400;
+ model.adc_id = AdcId::WOLFSON_HP2400;
+ model.gpio_id = GpioId::HP2400;
+ model.motor_id = MotorId::HP2400;
+ model.flags = GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW;
+ model.shading_lines = 20;
+ model.shading_ta_lines = 0;
+ model.search_lines = 132;
+
+ s_usb_devices->emplace_back(0x03f0, 0x0a01, model);
+
+
+ model = Genesys_Model();
+ model.name = "visioneer-strobe-xp200";
+ model.vendor = "Visioneer";
+ model.model = "Strobe XP200";
+ model.model_id = ModelId::VISIONEER_STROBE_XP200;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 200, 100, 75 },
+ { 600, 300, 200, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.5;
+ model.y_offset = 16.0;
+ model.x_size = 215.9;
+ model.y_size = 297.2;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CIS_XP200;
+ model.adc_id = AdcId::AD_XP200;
+ model.gpio_id = GpioId::XP200;
+ model.motor_id = MotorId::XP200;
+ model.flags = GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 120;
+ model.shading_ta_lines = 0;
+ model.search_lines = 132;
+
+ s_usb_devices->emplace_back(0x04a7, 0x0426, model);
+
+
+ model = Genesys_Model();
+ model.name = "hewlett-packard-scanjet-3670";
+ model.vendor = "Hewlett Packard";
+ model.model = "ScanJet 3670";
+ model.model_id = ModelId::HP_SCANJET_3670;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 100, 75, 50 },
+ { 1200, 600, 300, 150, 100, 75, 50 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 8.5;
+ model.y_offset = 11.0;
+ model.x_size = 215.9;
+ model.y_size = 300.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 1.0;
+
+ model.x_offset_ta = 104.0;
+ model.y_offset_ta = 55.6;
+ model.x_size_ta = 25.6;
+ model.y_size_ta = 78.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 76.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_HP3670;
+ model.adc_id = AdcId::WOLFSON_HP3670;
+ model.gpio_id = GpioId::HP3670;
+ model.motor_id = MotorId::HP3670;
+ model.flags = GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_XPA |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW;
+ model.shading_lines = 20;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x03f0, 0x1405, model);
+
+
+ model = Genesys_Model();
+ model.name = "plustek-opticpro-st12";
+ model.vendor = "Plustek";
+ model.model = "OpticPro ST12";
+ model.model_id = ModelId::PLUSTEK_OPTICPRO_ST12;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 3.5;
+ model.y_offset = 7.5;
+ model.x_size = 218.0;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 1.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 8;
+ model.ld_shift_b = 16;
+
+ model.line_mode_color_order = ColorOrder::BGR;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_ST12;
+ model.adc_id = AdcId::WOLFSON_ST12;
+ model.gpio_id = GpioId::ST12;
+ model.motor_id = MotorId::UMAX;
+ model.flags = GENESYS_FLAG_UNTESTED | GENESYS_FLAG_14BIT_GAMMA;
+ model.buttons = GENESYS_HAS_NO_BUTTONS;
+ model.shading_lines = 20;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x07b3, 0x0600, model);
+
+ model = Genesys_Model();
+ model.name = "plustek-opticpro-st24";
+ model.vendor = "Plustek";
+ model.model = "OpticPro ST24";
+ model.model_id = ModelId::PLUSTEK_OPTICPRO_ST24;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 75 },
+ { 2400, 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 3.5;
+ model.y_offset = 7.5;
+ model.x_size = 218.0;
+ model.y_size = 299.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 1.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 8;
+ model.ld_shift_b = 16;
+
+ model.line_mode_color_order = ColorOrder::BGR;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_ST24;
+ model.adc_id = AdcId::WOLFSON_ST24;
+ model.gpio_id = GpioId::ST24;
+ model.motor_id = MotorId::ST24;
+ model.flags = GENESYS_FLAG_UNTESTED |
+ GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SEARCH_START |
+ GENESYS_FLAG_OFFSET_CALIBRATION;
+ model.buttons = GENESYS_HAS_NO_BUTTONS;
+ model.shading_lines = 20;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x07b3, 0x0601, model);
+
+ model = Genesys_Model();
+ model.name = "medion-md5345-model";
+ model.vendor = "Medion";
+ model.model = "MD5345/MD6228/MD6471";
+ model.model_id = ModelId::MEDION_MD5345;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 400, 300, 200, 150, 100, 75, 50 },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.30;
+ model.y_offset = 0.80;
+ model.x_size = 220.0;
+ model.y_size = 296.4;
+
+ model.y_offset_calib_white = 0.00;
+ model.x_offset_calib_black = 0.00;
+
+ model.x_offset_ta = 0.00;
+ model.y_offset_ta = 0.00;
+ model.x_size_ta = 0.00;
+ model.y_size_ta = 0.00;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.00;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 48;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 0;
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_5345;
+ model.adc_id = AdcId::WOLFSON_5345;
+ model.gpio_id = GpioId::MD_5345;
+ model.motor_id = MotorId::MD_5345;
+ model.flags = GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_SEARCH_START |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_SHADING_NO_MOVE |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_POWER_SW |
+ GENESYS_HAS_OCR_SW |
+ GENESYS_HAS_SCAN_SW;
+ model.shading_lines = 40;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x0461, 0x0377, model);
+
+ model = Genesys_Model();
+ model.name = "visioneer-strobe-xp300";
+ model.vendor = "Visioneer";
+ model.model = "Strobe XP300";
+ model.model_id = ModelId::VISIONEER_STROBE_XP300;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 1.0;
+ model.x_size = 435.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 26.5;
+ // this is larger than needed -- accounts for second sensor head, which is a calibration item
+ model.eject_feed = 0.0;
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_XP300;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::XP300;
+ model.motor_id = MotorId::XP300;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a7, 0x0474, model);
+
+ model = Genesys_Model();
+ model.name = "syscan-docketport-665";
+ model.vendor = "Syscan/Ambir";
+ model.model = "DocketPORT 665";
+ model.model_id = ModelId::SYSCAN_DOCKETPORT_665;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 108.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 17.5;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_DP665;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::DP665;
+ model.motor_id = MotorId::DP665;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x0a82, 0x4803, model);
+
+ model = Genesys_Model();
+ model.name = "visioneer-roadwarrior";
+ model.vendor = "Visioneer";
+ model.model = "Readwarrior";
+ model.model_id = ModelId::VISIONEER_ROADWARRIOR;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 220.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 16.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_ROADWARRIOR;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::DP665;
+ model.motor_id = MotorId::ROADWARRIOR;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a7, 0x0494, model);
+
+ model = Genesys_Model();
+ model.name = "syscan-docketport-465";
+ model.vendor = "Syscan";
+ model.model = "DocketPORT 465";
+ model.model_id = ModelId::SYSCAN_DOCKETPORT_465;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 220.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 16.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_ROADWARRIOR;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::DP665;
+ model.motor_id = MotorId::ROADWARRIOR;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_NO_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_UNTESTED;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW;
+ model.shading_lines = 300;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x0a82, 0x4802, model);
+
+
+ model = Genesys_Model();
+ model.name = "visioneer-xp100-revision3";
+ model.vendor = "Visioneer";
+ model.model = "XP100 Revision 3";
+ model.model_id = ModelId::VISIONEER_STROBE_XP100_REVISION3;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 220.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 16.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_ROADWARRIOR;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::DP665;
+ model.motor_id = MotorId::ROADWARRIOR;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a7, 0x049b, model);
+
+ model = Genesys_Model();
+ model.name = "pentax-dsmobile-600";
+ model.vendor = "Pentax";
+ model.model = "DSmobile 600";
+ model.model_id = ModelId::PENTAX_DSMOBILE_600;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 220.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 16.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_DSMOBILE600;
+ model.adc_id = AdcId::WOLFSON_DSM600;
+ model.gpio_id = GpioId::DP665;
+ model.motor_id = MotorId::DSMOBILE_600;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x0a17, 0x3210, model);
+ // clone, only usb id is different
+ s_usb_devices->emplace_back(0x04f9, 0x2038, model);
+
+ model = Genesys_Model();
+ model.name = "syscan-docketport-467";
+ model.vendor = "Syscan";
+ model.model = "DocketPORT 467";
+ model.model_id = ModelId::SYSCAN_DOCKETPORT_467;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 220.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 16.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_DSMOBILE600;
+ model.adc_id = AdcId::WOLFSON_DSM600;
+ model.gpio_id = GpioId::DP665;
+ model.motor_id = MotorId::DSMOBILE_600;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x1dcc, 0x4812, model);
+
+ model = Genesys_Model();
+ model.name = "syscan-docketport-685";
+ model.vendor = "Syscan/Ambir";
+ model.model = "DocketPORT 685";
+ model.model_id = ModelId::SYSCAN_DOCKETPORT_685;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 1.0;
+ model.x_size = 212.0;
+ model.y_size = 500;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 26.5;
+ // this is larger than needed -- accounts for second sensor head, which is a calibration item
+ model.eject_feed = 0.0;
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_DP685;
+ model.adc_id = AdcId::WOLFSON_DSM600;
+ model.gpio_id = GpioId::DP685;
+ model.motor_id = MotorId::XP300;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+
+ s_usb_devices->emplace_back(0x0a82, 0x480c, model);
+
+
+ model = Genesys_Model();
+ model.name = "syscan-docketport-485";
+ model.vendor = "Syscan/Ambir";
+ model.model = "DocketPORT 485";
+ model.model_id = ModelId::SYSCAN_DOCKETPORT_485;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 1.0;
+ model.x_size = 435.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 26.5;
+ // this is larger than needed -- accounts for second sensor head, which is a calibration item
+ model.eject_feed = 0.0;
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_XP300;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::XP300;
+ model.motor_id = MotorId::XP300;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x0a82, 0x4800, model);
+
+
+ model = Genesys_Model();
+ model.name = "dct-docketport-487";
+ model.vendor = "DCT";
+ model.model = "DocketPORT 487";
+ model.model_id = ModelId::DCT_DOCKETPORT_487;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.0;
+ model.y_offset = 1.0;
+ model.x_size = 435.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 26.5;
+ // this is larger than needed -- accounts for second sensor head, which is a calibration item
+ model.eject_feed = 0.0;
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_XP300;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::XP300;
+ model.motor_id = MotorId::XP300;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_UNTESTED;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x1dcc, 0x4810, model);
+
+
+ model = Genesys_Model();
+ model.name = "visioneer-7100-model";
+ model.vendor = "Visioneer";
+ model.model = "OneTouch 7100";
+ model.model_id = ModelId::VISIONEER_7100;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 400, 300, 200, 150, 100, 75, 50 },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 4.00;
+ model.y_offset = 0.80;
+ model.x_size = 215.9;
+ model.y_size = 296.4;
+
+ model.y_offset_calib_white = 0.00;
+ model.x_offset_calib_black = 0.00;
+
+ model.x_offset_ta = 0.00;
+ model.y_offset_ta = 0.00;
+ model.x_size_ta = 0.00;
+ model.y_size_ta = 0.00;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.00;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 48;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 0;
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_5345;
+ model.adc_id = AdcId::WOLFSON_5345;
+ model.gpio_id = GpioId::MD_5345;
+ model.motor_id = MotorId::MD_5345;
+ model.flags = GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_SEARCH_START |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_POWER_SW |
+ GENESYS_HAS_OCR_SW |
+ GENESYS_HAS_SCAN_SW;
+ model.shading_lines = 40;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x04a7, 0x0229, model);
+
+
+ model = Genesys_Model();
+ model.name = "xerox-2400-model";
+ model.vendor = "Xerox";
+ model.model = "OneTouch 2400";
+ model.model_id = ModelId::XEROX_2400;
+ model.asic_type = AsicType::GL646;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 400, 300, 200, 150, 100, 75, 50 },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 4.00;
+ model.y_offset = 0.80;
+ model.x_size = 215.9;
+ model.y_size = 296.4;
+
+ model.y_offset_calib_white = 0.00;
+ model.x_offset_calib_black = 0.00;
+
+ model.x_offset_ta = 0.00;
+ model.y_offset_ta = 0.00;
+ model.x_size_ta = 0.00;
+ model.y_size_ta = 0.00;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.00;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 48;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 0;
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_5345;
+ model.adc_id = AdcId::WOLFSON_5345;
+ model.gpio_id = GpioId::MD_5345;
+ model.motor_id = MotorId::MD_5345;
+ model.flags = GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_SEARCH_START |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_COPY_SW |
+ GENESYS_HAS_EMAIL_SW |
+ GENESYS_HAS_POWER_SW |
+ GENESYS_HAS_OCR_SW |
+ GENESYS_HAS_SCAN_SW;
+ model.shading_lines = 40;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x0461, 0x038b, model);
+
+
+ model = Genesys_Model();
+ model.name = "xerox-travelscanner";
+ model.vendor = "Xerox";
+ model.model = "Travelscanner 100";
+ model.model_id = ModelId::XEROX_TRAVELSCANNER_100;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 600, 300, 150, 75 },
+ { 1200, 600, 300, 150, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 4.0;
+ model.y_offset = 0.0;
+ model.x_size = 220.0;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 16.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = true;
+ model.is_sheetfed = true;
+ model.sensor_id = SensorId::CCD_ROADWARRIOR;
+ model.adc_id = AdcId::WOLFSON_XP300;
+ model.gpio_id = GpioId::DP665;
+ model.motor_id = MotorId::ROADWARRIOR;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION;
+ model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 400;
+
+ s_usb_devices->emplace_back(0x04a7, 0x04ac, model);
+
+
+ model = Genesys_Model();
+ model.name = "plustek-opticbook-3600";
+ model.vendor = "PLUSTEK";
+ model.model = "OpticBook 3600";
+ model.model_id = ModelId::PLUSTEK_OPTICPRO_3600;
+ model.asic_type = AsicType::GL841;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { /*1200,*/ 600, 400, 300, 200, 150, 100, 75 },
+ { /*2400,*/ 1200, 600, 400, 300, 200, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 0.42;
+ model.y_offset = 6.75;
+ model.x_size = 216.0;
+ model.y_size = 297.0;
+
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 0.0;
+ model.y_size_ta = 0.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600;
+ model.adc_id = AdcId::PLUSTEK_OPTICPRO_3600;
+ model.gpio_id = GpioId::PLUSTEK_OPTICPRO_3600;
+ model.motor_id = MotorId::PLUSTEK_OPTICPRO_3600;
+ model.flags = GENESYS_FLAG_UNTESTED | // not fully working yet
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION;
+ model.buttons = GENESYS_HAS_NO_BUTTONS;
+ model.shading_lines = 7;
+ model.shading_ta_lines = 0;
+ model.search_lines = 200;
+
+ s_usb_devices->emplace_back(0x07b3, 0x0900, model);
+
+
+ model = Genesys_Model();
+ model.name = "plustek-opticfilm-7200i";
+ model.vendor = "PLUSTEK";
+ model.model = "OpticFilm 7200i";
+ model.model_id = ModelId::PLUSTEK_OPTICFILM_7200I;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED },
+ { 7200, 3600, 1800, 900 },
+ { 7200, 3600, 1800, 900 },
+ }
+ };
+
+ model.bpp_gray_values = { 16 };
+ model.bpp_color_values = { 16 };
+ model.default_method = ScanMethod::TRANSPARENCY;
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 36.0;
+ model.y_size = 44.0;
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 6.5;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 29.0;
+ model.x_size_ta = 36.0;
+ model.y_size_ta = 24.0;
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_black_ta = 6.5;
+ model.y_offset_calib_white_ta = 0.0;
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 12;
+ model.ld_shift_b = 24;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+
+ model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200I;
+ model.adc_id = AdcId::PLUSTEK_OPTICFILM_7200I;
+ model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7200I;
+ model.motor_id = MotorId::PLUSTEK_OPTICFILM_7200I;
+
+ model.flags = GENESYS_FLAG_HAS_UTA |
+ GENESYS_FLAG_HAS_UTA_INFRARED |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_HAS_NO_BUTTONS |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CALIBRATION_HOST_SIDE |
+ GENESYS_FLAG_16BIT_DATA_INVERTED;
+
+ model.shading_lines = 7;
+ model.shading_ta_lines = 50;
+ model.search_lines = 200;
+ s_usb_devices->emplace_back(0x07b3, 0x0c04, model);
+
+
+ model = Genesys_Model();
+ model.name = "plustek-opticfilm-7300";
+ model.vendor = "PLUSTEK";
+ model.model = "OpticFilm 7300";
+ model.model_id = ModelId::PLUSTEK_OPTICFILM_7300;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::TRANSPARENCY },
+ { 7200, 3600, 1800, 900 },
+ { 7200, 3600, 1800, 900 },
+ }
+ };
+
+ model.bpp_gray_values = { 16 };
+ model.bpp_color_values = { 16 };
+ model.default_method = ScanMethod::TRANSPARENCY;
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 36.0;
+ model.y_size = 44.0;
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 6.5;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 29.0;
+ model.x_size_ta = 36.0;
+ model.y_size_ta = 24.0;
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_black_ta = 6.5;
+ model.y_offset_calib_white_ta = 0.0;
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 12;
+ model.ld_shift_b = 24;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+
+ model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300;
+ model.adc_id = AdcId::PLUSTEK_OPTICFILM_7300;
+ model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7300;
+ model.motor_id = MotorId::PLUSTEK_OPTICFILM_7300;
+
+ model.flags = GENESYS_FLAG_HAS_UTA |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_HAS_NO_BUTTONS |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CALIBRATION_HOST_SIDE;
+
+ model.shading_lines = 7;
+ model.shading_ta_lines = 50;
+ model.search_lines = 200;
+ s_usb_devices->emplace_back(0x07b3, 0x0c12, model);
+
+
+ model = Genesys_Model();
+ model.name = "plustek-opticfilm-7500i";
+ model.vendor = "PLUSTEK";
+ model.model = "OpticFilm 7500i";
+ model.model_id = ModelId::PLUSTEK_OPTICFILM_7500I;
+ model.asic_type = AsicType::GL843;
+
+ model.resolutions = {
+ {
+ { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED },
+ { 7200, 3600, 1800, 900 },
+ { 7200, 3600, 1800, 900 },
+ }
+ };
+
+ model.bpp_gray_values = { 16 };
+ model.bpp_color_values = { 16 };
+ model.default_method = ScanMethod::TRANSPARENCY;
+
+ model.x_offset = 0.0;
+ model.y_offset = 0.0;
+ model.x_size = 36.0;
+ model.y_size = 44.0;
+ model.y_offset_calib_white = 0.0;
+ model.x_offset_calib_black = 6.5;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 29.0;
+ model.x_size_ta = 36.0;
+ model.y_size_ta = 24.0;
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_black_ta = 6.5;
+ model.y_offset_calib_white_ta = 0.0;
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 12;
+ model.ld_shift_b = 24;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+
+ model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7500I;
+ model.adc_id = AdcId::PLUSTEK_OPTICFILM_7500I;
+ model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7500I;
+ model.motor_id = MotorId::PLUSTEK_OPTICFILM_7500I;
+
+ model.flags = GENESYS_FLAG_HAS_UTA |
+ GENESYS_FLAG_HAS_UTA_INFRARED |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_HAS_NO_BUTTONS |
+ GENESYS_FLAG_SHADING_REPARK |
+ GENESYS_FLAG_CALIBRATION_HOST_SIDE;
+
+ model.shading_lines = 7;
+ model.shading_ta_lines = 50;
+ model.search_lines = 200;
+ s_usb_devices->emplace_back(0x07b3, 0x0c13, model);
+
+
+ model = Genesys_Model();
+ model.name = "hewlett-packard-scanjet-N6310";
+ model.vendor = "Hewlett Packard";
+ model.model = "ScanJet N6310";
+ model.model_id = ModelId::HP_SCANJET_N6310;
+ model.asic_type = AsicType::GL847;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 75 },
+ { 2400, 1200, 600, 400, 300, 200, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 6;
+ model.y_offset = 2;
+ model.x_size = 216;
+ model.y_size = 511;
+
+ model.y_offset_calib_white = 3.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 100.0;
+ model.y_size_ta = 100.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0;
+
+ model.post_scan = 0;
+ model.eject_feed = 0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 0;
+ model.ld_shift_b = 0;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_HP_N6310;
+ model.adc_id = AdcId::CANON_LIDE_200; // Not defined yet for N6310
+ model.gpio_id = GpioId::HP_N6310;
+ model.motor_id = MotorId::CANON_LIDE_200; // Not defined yet for N6310
+ model.flags = GENESYS_FLAG_UNTESTED |
+ GENESYS_FLAG_14BIT_GAMMA |
+ GENESYS_FLAG_DARK_CALIBRATION |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_NO_CALIBRATION;
+
+ model.buttons = GENESYS_HAS_NO_BUTTONS;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x03f0, 0x4705, model);
+
+
+ model = Genesys_Model();
+ model.name = "plustek-opticbook-3800";
+ model.vendor = "PLUSTEK";
+ model.model = "OpticBook 3800";
+ model.model_id = ModelId::PLUSTEK_OPTICBOOK_3800;
+ model.asic_type = AsicType::GL845;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 100, 75 },
+ { 1200, 600, 300, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 7.2;
+ model.y_offset = 14.7;
+ model.x_size = 217.7;
+ model.y_size = 300.0;
+
+ model.y_offset_calib_white = 9.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 0.0;
+ model.y_size_ta = 0.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_PLUSTEK_OPTICBOOK_3800;
+ model.adc_id = AdcId::PLUSTEK_OPTICBOOK_3800;
+ model.gpio_id = GpioId::PLUSTEK_OPTICBOOK_3800;
+ model.motor_id = MotorId::PLUSTEK_OPTICBOOK_3800;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA;
+ model.buttons = GENESYS_HAS_NO_BUTTONS; // TODO there are 4 buttons to support
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x07b3, 0x1300, model);
+
+
+ model = Genesys_Model();
+ model.name = "canon-image-formula-101";
+ model.vendor = "Canon";
+ model.model = "Image Formula 101";
+ model.model_id = ModelId::CANON_IMAGE_FORMULA_101;
+ model.asic_type = AsicType::GL846;
+
+ model.resolutions = {
+ {
+ { ScanMethod::FLATBED },
+ { 1200, 600, 300, 150, 100, 75 },
+ { 1200, 600, 300, 150, 100, 75 },
+ }
+ };
+
+ model.bpp_gray_values = { 8, 16 };
+ model.bpp_color_values = { 8, 16 };
+
+ model.x_offset = 7.2;
+ model.y_offset = 14.7;
+ model.x_size = 217.7;
+ model.y_size = 300.0;
+
+ model.y_offset_calib_white = 9.0;
+ model.x_offset_calib_black = 0.0;
+
+ model.x_offset_ta = 0.0;
+ model.y_offset_ta = 0.0;
+ model.x_size_ta = 0.0;
+ model.y_size_ta = 0.0;
+
+ model.y_offset_sensor_to_ta = 0.0;
+ model.y_offset_calib_white_ta = 0.0;
+
+ model.post_scan = 0.0;
+ model.eject_feed = 0.0;
+
+ model.ld_shift_r = 0;
+ model.ld_shift_g = 24;
+ model.ld_shift_b = 48;
+
+ model.line_mode_color_order = ColorOrder::RGB;
+
+ model.is_cis = false;
+ model.is_sheetfed = false;
+ model.sensor_id = SensorId::CCD_IMG101;
+ model.adc_id = AdcId::IMG101;
+ model.gpio_id = GpioId::IMG101;
+ model.motor_id = MotorId::IMG101;
+ model.flags = GENESYS_FLAG_SKIP_WARMUP |
+ GENESYS_FLAG_OFFSET_CALIBRATION |
+ GENESYS_FLAG_CUSTOM_GAMMA |
+ GENESYS_FLAG_UNTESTED;
+ model.buttons = GENESYS_HAS_NO_BUTTONS ;
+ model.shading_lines = 100;
+ model.shading_ta_lines = 0;
+ model.search_lines = 100;
+
+ s_usb_devices->emplace_back(0x1083, 0x162e, model);
+ }
+
+} // namespace genesys
diff --git a/backend/genesys/tables_motor.cpp b/backend/genesys/tables_motor.cpp
new file mode 100644
index 0000000..2484d2d
--- /dev/null
+++ b/backend/genesys/tables_motor.cpp
@@ -0,0 +1,325 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "low.h"
+
+namespace genesys {
+
+StaticInit<std::vector<Genesys_Motor>> s_motors;
+
+void genesys_init_motor_tables()
+{
+ s_motors.init();
+
+ Genesys_Motor motor;
+ motor.id = MotorId::UMAX;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 2400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128));
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::MD_5345; // MD5345/6228/6471
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 2400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(2000, 1375, 128));
+ motor.slopes.push_back(MotorSlope::create_from_steps(2000, 1375, 128));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::ST24;
+ motor.base_ydpi = 2400;
+ motor.optical_ydpi = 2400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(2289, 2100, 128));
+ motor.slopes.push_back(MotorSlope::create_from_steps(2289, 2100, 128));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::HP3670;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 1200;
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128));
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::HP2400;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 1200;
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128));
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::HP2300;
+ motor.base_ydpi = 600;
+ motor.optical_ydpi = 1200;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3200, 1200, 128));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3200, 1200, 128));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_35;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 2400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1400, 60));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::XP200;
+ motor.base_ydpi = 600;
+ motor.optical_ydpi = 600;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::XP300;
+ motor.base_ydpi = 300;
+ motor.optical_ydpi = 600;
+ // works best with GPIO10, GPIO14 off
+ motor.slopes.push_back(MotorSlope::create_from_steps(3700, 3700, 2));
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 11000, 2));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::DP665;
+ motor.base_ydpi = 750;
+ motor.optical_ydpi = 1500;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 2500, 10));
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 11000, 2));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::ROADWARRIOR;
+ motor.base_ydpi = 750;
+ motor.optical_ydpi = 1500;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 2600, 10));
+ motor.slopes.push_back(MotorSlope::create_from_steps(11000, 11000, 2));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::DSMOBILE_600;
+ motor.base_ydpi = 750;
+ motor.optical_ydpi = 1500;
+ motor.slopes.push_back(MotorSlope::create_from_steps(6666, 3700, 8));
+ motor.slopes.push_back(MotorSlope::create_from_steps(6666, 3700, 8));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_100;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 6400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 127));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1500, 127));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3 * 2712, 3 * 2712, 16));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_200;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 6400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 127));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1500, 127));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3 * 2712, 3 * 2712, 16));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_700;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 6400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 127));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1500, 127));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3 * 2712, 3 * 2712, 16));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::KVSS080;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 1200;
+ motor.slopes.push_back(MotorSlope::create_from_steps(22222, 500, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(22222, 500, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(22222, 500, 246));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::G4050;
+ motor.base_ydpi = 2400;
+ motor.optical_ydpi = 9600;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_4400F;
+ motor.base_ydpi = 2400;
+ motor.optical_ydpi = 9600;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_8400F;
+ motor.base_ydpi = 1600;
+ motor.optical_ydpi = 6400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_8600F;
+ motor.base_ydpi = 2400;
+ motor.optical_ydpi = 9600;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_110;
+ motor.base_ydpi = 4800;
+ motor.optical_ydpi = 9600;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 256));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_120;
+ motor.base_ydpi = 4800;
+ motor.optical_ydpi = 9600;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 256));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_210;
+ motor.base_ydpi = 4800;
+ motor.optical_ydpi = 9600;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 256));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::PLUSTEK_OPTICPRO_3600;
+ motor.base_ydpi = 1200;
+ motor.optical_ydpi = 2400;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 3250, 60));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::PLUSTEK_OPTICFILM_7200I;
+ motor.base_ydpi = 3600;
+ motor.optical_ydpi = 3600;
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::PLUSTEK_OPTICFILM_7300;
+ motor.base_ydpi = 3600;
+ motor.optical_ydpi = 3600;
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::PLUSTEK_OPTICFILM_7500I;
+ motor.base_ydpi = 3600;
+ motor.optical_ydpi = 3600;
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::IMG101;
+ motor.base_ydpi = 600;
+ motor.optical_ydpi = 1200;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 3250, 60));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::PLUSTEK_OPTICBOOK_3800;
+ motor.base_ydpi = 600;
+ motor.optical_ydpi = 1200;
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60));
+ motor.slopes.push_back(MotorSlope::create_from_steps(3500, 3250, 60));
+ s_motors->push_back(std::move(motor));
+
+
+ motor = Genesys_Motor();
+ motor.id = MotorId::CANON_LIDE_80;
+ motor.base_ydpi = 2400;
+ motor.optical_ydpi = 4800; // 9600
+ motor.slopes.push_back(MotorSlope::create_from_steps(9560, 1912, 31));
+ s_motors->push_back(std::move(motor));
+}
+
+} // namespace genesys
diff --git a/backend/genesys/tables_motor_profile.cpp b/backend/genesys/tables_motor_profile.cpp
new file mode 100644
index 0000000..18f7271
--- /dev/null
+++ b/backend/genesys/tables_motor_profile.cpp
@@ -0,0 +1,380 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "low.h"
+
+namespace genesys {
+
+StaticInit<std::vector<Motor_Profile>> gl843_motor_profiles;
+
+void genesys_init_motor_profile_tables_gl843()
+{
+ gl843_motor_profiles.init();
+
+ auto profile = Motor_Profile();
+ profile.motor_id = MotorId::KVSS080;
+ profile.exposure = 8000;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(44444, 500, 489);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::G4050;
+ profile.exposure = 8016;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(7842, 320, 602);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::G4050;
+ profile.exposure = 15624;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(9422, 254, 1004);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::G4050;
+ profile.exposure = 42752;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(42752, 1706, 610);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::G4050;
+ profile.exposure = 56064;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(28032, 2238, 604);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_4400F;
+ profile.exposure = 11640;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(49152, 484, 1014);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_8400F;
+ profile.exposure = 50000;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(8743, 300, 794);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_8600F;
+ profile.exposure = 0x59d8;
+ profile.step_type = StepType::QUARTER;
+ // FIXME: if the exposure is lower then we'll select another motor
+ profile.slope = MotorSlope::create_from_steps(54612, 1500, 219);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::PLUSTEK_OPTICFILM_7200I;
+ profile.exposure = 0;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(39682, 1191, 15);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::PLUSTEK_OPTICFILM_7300;
+ profile.exposure = 0x2f44;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(31250, 1512, 6);
+ gl843_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::PLUSTEK_OPTICFILM_7500I;
+ profile.exposure = 0;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(31250, 1375, 7);
+ gl843_motor_profiles->push_back(profile);
+}
+
+StaticInit<std::vector<Motor_Profile>> gl846_motor_profiles;
+
+void genesys_init_motor_profile_tables_gl846()
+{
+ gl846_motor_profiles.init();
+
+ auto profile = Motor_Profile();
+ profile.motor_id = MotorId::IMG101;
+ profile.exposure = 11000;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(22000, 1000, 1017);
+
+ gl846_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::PLUSTEK_OPTICBOOK_3800;
+ profile.exposure = 11000;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(22000, 1000, 1017);
+ gl846_motor_profiles->push_back(profile);
+}
+
+/**
+ * database of motor profiles
+ */
+
+StaticInit<std::vector<Motor_Profile>> gl847_motor_profiles;
+
+void genesys_init_motor_profile_tables_gl847()
+{
+ gl847_motor_profiles.init();
+
+ auto profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_100;
+ profile.exposure = 2848;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_100;
+ profile.exposure = 1424;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_100;
+ profile.exposure = 1432;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_100;
+ profile.exposure = 2712;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 279);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_100;
+ profile.exposure = 5280;
+ profile.step_type = StepType::EIGHTH;
+ profile.slope = MotorSlope::create_from_steps(31680, 534, 247);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_200;
+ profile.exposure = 2848;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_200;
+ profile.exposure = 1424;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_200;
+ profile.exposure = 1432;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_200;
+ profile.exposure = 2712;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 279);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_200;
+ profile.exposure = 5280;
+ profile.step_type = StepType::EIGHTH;
+ profile.slope = MotorSlope::create_from_steps(31680, 534, 247);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_200;
+ profile.exposure = 10416;
+ profile.step_type = StepType::EIGHTH;
+ profile.slope = MotorSlope::create_from_steps(31680, 534, 247);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_700;
+ profile.exposure = 2848;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_700;
+ profile.exposure = 1424;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_700;
+ profile.exposure = 1504;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 534, 255);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_700;
+ profile.exposure = 2696;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(46876, 2022, 127);
+ gl847_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_700;
+ profile.exposure = 10576;
+ profile.step_type = StepType::EIGHTH;
+ profile.slope = MotorSlope::create_from_steps(46876, 15864, 2);
+ gl847_motor_profiles->push_back(profile);
+}
+
+StaticInit<std::vector<Motor_Profile>> gl124_motor_profiles;
+
+void genesys_init_motor_profile_tables_gl124()
+{
+ gl124_motor_profiles.init();
+
+ // NEXT LPERIOD=PREVIOUS*2-192
+ Motor_Profile profile;
+ profile.motor_id = MotorId::CANON_LIDE_110;
+ profile.exposure = 2768;
+ profile.step_type = StepType::FULL;
+ profile.slope = MotorSlope::create_from_steps(62496, 335, 255);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_110;
+ profile.exposure = 5360;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(62496, 335, 469);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_110;
+ profile.exposure = 10528;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(62496, 2632, 3);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_110;
+ profile.exposure = 20864;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(62496, 10432, 3);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_120;
+ profile.exposure = 4608;
+ profile.step_type = StepType::FULL;
+ profile.slope = MotorSlope::create_from_steps(62496, 864, 127);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_120;
+ profile.exposure = 5360;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(62496, 2010, 63);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_120;
+ profile.exposure = 10528;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(62464, 2632, 3);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_120;
+ profile.exposure = 20864;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(62592, 10432, 5);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_210;
+ profile.exposure = 2768;
+ profile.step_type = StepType::FULL;
+ profile.slope = MotorSlope::create_from_steps(62496, 335, 255);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_210;
+ profile.exposure = 5360;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(62496, 335, 469);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_210;
+ profile.exposure = 10528;
+ profile.step_type = StepType::HALF;
+ profile.slope = MotorSlope::create_from_steps(62496, 2632, 3);
+ gl124_motor_profiles->push_back(profile);
+
+ profile = Motor_Profile();
+ profile.motor_id = MotorId::CANON_LIDE_210;
+ profile.exposure = 20864;
+ profile.step_type = StepType::QUARTER;
+ profile.slope = MotorSlope::create_from_steps(62496, 10432, 4);
+ gl124_motor_profiles->push_back(profile);
+}
+
+void genesys_init_motor_profile_tables()
+{
+ genesys_init_motor_profile_tables_gl843();
+ genesys_init_motor_profile_tables_gl846();
+ genesys_init_motor_profile_tables_gl847();
+ genesys_init_motor_profile_tables_gl124();
+}
+
+} // namespace genesys
diff --git a/backend/genesys/tables_sensor.cpp b/backend/genesys/tables_sensor.cpp
new file mode 100644
index 0000000..bbbe441
--- /dev/null
+++ b/backend/genesys/tables_sensor.cpp
@@ -0,0 +1,3668 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "low.h"
+
+namespace genesys {
+
+inline unsigned default_get_logical_hwdpi(const Genesys_Sensor& sensor, unsigned xres)
+{
+ if (sensor.logical_dpihw_override)
+ return sensor.logical_dpihw_override;
+
+ // can't be below 600 dpi
+ if (xres <= 600) {
+ return 600;
+ }
+ if (xres <= static_cast<unsigned>(sensor.optical_res) / 4) {
+ return sensor.optical_res / 4;
+ }
+ if (xres <= static_cast<unsigned>(sensor.optical_res) / 2) {
+ return sensor.optical_res / 2;
+ }
+ return sensor.optical_res;
+}
+
+inline unsigned get_sensor_optical_with_ccd_divisor(const Genesys_Sensor& sensor, unsigned xres)
+{
+ unsigned hwres = sensor.optical_res / sensor.get_ccd_size_divisor_for_dpi(xres);
+
+ if (xres <= hwres / 4) {
+ return hwres / 4;
+ }
+ if (xres <= hwres / 2) {
+ return hwres / 2;
+ }
+ return hwres;
+}
+
+inline unsigned default_get_ccd_size_divisor_for_dpi(const Genesys_Sensor& sensor, unsigned xres)
+{
+ if (sensor.ccd_size_divisor >= 4 && xres * 4 <= static_cast<unsigned>(sensor.optical_res)) {
+ return 4;
+ }
+ if (sensor.ccd_size_divisor >= 2 && xres * 2 <= static_cast<unsigned>(sensor.optical_res)) {
+ return 2;
+ }
+ return 1;
+}
+
+inline unsigned get_ccd_size_divisor_exact(const Genesys_Sensor& sensor, unsigned xres)
+{
+ (void) xres;
+ return sensor.ccd_size_divisor;
+}
+
+inline unsigned get_ccd_size_divisor_gl124(const Genesys_Sensor& sensor, unsigned xres)
+{
+ // we have 2 domains for ccd: xres below or above half ccd max dpi
+ if (xres <= 300 && sensor.ccd_size_divisor > 1) {
+ return 2;
+ }
+ return 1;
+}
+
+inline unsigned default_get_hwdpi_divisor_for_dpi(const Genesys_Sensor& sensor, unsigned xres)
+{
+ return sensor.optical_res / default_get_logical_hwdpi(sensor, xres);
+}
+
+StaticInit<std::vector<Genesys_Sensor>> s_sensors;
+
+void genesys_init_sensor_tables()
+{
+ s_sensors.init();
+
+ Genesys_Sensor sensor;
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_UMAX;
+ sensor.optical_res = 1200;
+ sensor.black_pixels = 48;
+ sensor.dummy_pixel = 64;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10800;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.custom_regs = {
+ { 0x08, 0x01 },
+ { 0x09, 0x03 },
+ { 0x0a, 0x05 },
+ { 0x0b, 0x07 },
+ { 0x16, 0x33 },
+ { 0x17, 0x05 },
+ { 0x18, 0x31 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x13 },
+ { 0x53, 0x17 },
+ { 0x54, 0x03 },
+ { 0x55, 0x07 },
+ { 0x56, 0x0b },
+ { 0x57, 0x0f },
+ { 0x58, 0x23 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_ST12;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 48;
+ sensor.dummy_pixel = 85;
+ sensor.ccd_start_xoffset = 152;
+ sensor.sensor_pixels = 5416;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.custom_regs = {
+ { 0x08, 0x02 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x04 },
+ { 0x16, 0x2b },
+ { 0x17, 0x08 },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x0c },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_ST24;
+ sensor.optical_res = 1200;
+ sensor.black_pixels = 48;
+ sensor.dummy_pixel = 64;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10800;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.custom_regs = {
+ { 0x08, 0x0e },
+ { 0x09, 0x0c },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x0c },
+ { 0x16, 0x33 },
+ { 0x17, 0x08 },
+ { 0x18, 0x31 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x17 },
+ { 0x53, 0x03 },
+ { 0x54, 0x07 },
+ { 0x55, 0x0b },
+ { 0x56, 0x0f },
+ { 0x57, 0x13 },
+ { 0x58, 0x03 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_5345;
+ sensor.optical_res = 1200;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 48;
+ sensor.dummy_pixel = 16;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10872;
+ sensor.fau_gain_white_ref = 190;
+ sensor.gain_white_ref = 190;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.stagger_config = StaggerConfig{ 1200, 4 }; // FIXME: may be incorrect
+ sensor.custom_base_regs = {
+ { 0x08, 0x0d },
+ { 0x09, 0x0f },
+ { 0x0a, 0x11 },
+ { 0x0b, 0x13 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x30 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x23 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 },
+ };
+ sensor.gamma = { 2.38f, 2.35f, 2.34f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ unsigned exposure_lperiod;
+ unsigned ccd_size_divisor;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 50 }, 12000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 75 }, 11000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 100 }, 11000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 150 }, 11000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 200 }, 11000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 300 }, 11000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 400 }, 11000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 600 }, 11000, 2, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x28 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 1200 }, 11000, 1, {
+ { 0x08, 0x0d },
+ { 0x09, 0x0f },
+ { 0x0a, 0x11 },
+ { 0x0b, 0x13 },
+ { 0x16, 0x0b },
+ { 0x17, 0x0a },
+ { 0x18, 0x30 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x03 },
+ { 0x52, 0x03 },
+ { 0x53, 0x07 },
+ { 0x54, 0x0b },
+ { 0x55, 0x0f },
+ { 0x56, 0x13 },
+ { 0x57, 0x17 },
+ { 0x58, 0x23 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.ccd_size_divisor = setting.ccd_size_divisor;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_HP2400;
+ sensor.optical_res = 1200;
+ sensor.black_pixels = 48;
+ sensor.dummy_pixel = 15;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10872;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.stagger_config = StaggerConfig{1200, 4}; // FIXME: may be incorrect
+ sensor.custom_base_regs = {
+ { 0x08, 0x14 },
+ { 0x09, 0x15 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0xbf },
+ { 0x17, 0x08 },
+ { 0x18, 0x3f },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0f },
+ { 0x54, 0x13 },
+ { 0x55, 0x17 },
+ { 0x56, 0x03 },
+ { 0x57, 0x07 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x0e },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 },
+ };
+ sensor.gamma = { 2.1f, 2.1f, 2.1f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ unsigned exposure_lperiod;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 50 }, 7211, {
+ { 0x08, 0x14 },
+ { 0x09, 0x15 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0xbf },
+ { 0x17, 0x08 },
+ { 0x18, 0x3f },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0f },
+ { 0x54, 0x13 },
+ { 0x55, 0x17 },
+ { 0x56, 0x03 },
+ { 0x57, 0x07 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 100 }, 7211, {
+ { 0x08, 0x14 },
+ { 0x09, 0x15 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0xbf },
+ { 0x17, 0x08 },
+ { 0x18, 0x3f },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0f },
+ { 0x54, 0x13 },
+ { 0x55, 0x17 },
+ { 0x56, 0x03 },
+ { 0x57, 0x07 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 150 }, 7211, {
+ { 0x08, 0x14 },
+ { 0x09, 0x15 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0xbf },
+ { 0x17, 0x08 },
+ { 0x18, 0x3f },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0f },
+ { 0x54, 0x13 },
+ { 0x55, 0x17 },
+ { 0x56, 0x03 },
+ { 0x57, 0x07 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 300 }, 8751, {
+ { 0x08, 0x14 },
+ { 0x09, 0x15 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0xbf },
+ { 0x17, 0x08 },
+ { 0x18, 0x3f },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0f },
+ { 0x54, 0x13 },
+ { 0x55, 0x17 },
+ { 0x56, 0x03 },
+ { 0x57, 0x07 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 600 }, 18760, {
+ { 0x08, 0x0e },
+ { 0x09, 0x0f },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0xbf },
+ { 0x17, 0x08 },
+ { 0x18, 0x31 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x03 },
+ { 0x53, 0x07 },
+ { 0x54, 0x0b },
+ { 0x55, 0x0f },
+ { 0x56, 0x13 },
+ { 0x57, 0x17 },
+ { 0x58, 0x23 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 1200 }, 21749, {
+ { 0x08, 0x02 },
+ { 0x09, 0x04 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0xbf },
+ { 0x17, 0x08 },
+ { 0x18, 0x30 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x42 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0f },
+ { 0x54, 0x13 },
+ { 0x55, 0x17 },
+ { 0x56, 0x03 },
+ { 0x57, 0x07 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x0e },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_HP2300;
+ sensor.optical_res = 600;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 48;
+ sensor.dummy_pixel = 20;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 5368;
+ sensor.fau_gain_white_ref = 180;
+ sensor.gain_white_ref = 180;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.custom_base_regs = {
+ { 0x08, 0x16 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x01 },
+ { 0x0b, 0x03 },
+ { 0x16, 0xb7 },
+ { 0x17, 0x0a },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x6a },
+ { 0x1b, 0x8a },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x05 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x06 },
+ { 0x5c, 0x0b },
+ { 0x5d, 0x10 },
+ { 0x5e, 0x16 },
+ };
+ sensor.gamma = { 2.1f, 2.1f, 2.1f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ unsigned exposure_lperiod;
+ unsigned ccd_size_divisor;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75 }, 4480, 2, {
+ { 0x08, 0x16 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x01 },
+ { 0x0b, 0x03 },
+ { 0x16, 0xb7 },
+ { 0x17, 0x0a },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x6a },
+ { 0x1b, 0x8a },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x85 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x06 },
+ { 0x5c, 0x0b },
+ { 0x5d, 0x10 },
+ { 0x5e, 0x16 }
+ }
+ },
+ { { 150 }, 4350, 2, {
+ { 0x08, 0x16 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x01 },
+ { 0x0b, 0x03 },
+ { 0x16, 0xb7 },
+ { 0x17, 0x0a },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x6a },
+ { 0x1b, 0x8a },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x85 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x06 },
+ { 0x5c, 0x0b },
+ { 0x5d, 0x10 },
+ { 0x5e, 0x16 }
+ }
+ },
+ { { 300 }, 4350, 2, {
+ { 0x08, 0x16 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x01 },
+ { 0x0b, 0x03 },
+ { 0x16, 0xb7 },
+ { 0x17, 0x0a },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x6a },
+ { 0x1b, 0x8a },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x85 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x06 },
+ { 0x5c, 0x0b },
+ { 0x5d, 0x10 },
+ { 0x5e, 0x16 }
+ }
+ },
+ { { 600 }, 8700, 1, {
+ { 0x08, 0x01 },
+ { 0x09, 0x03 },
+ { 0x0a, 0x04 },
+ { 0x0b, 0x06 },
+ { 0x16, 0xb7 },
+ { 0x17, 0x0a },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x6a },
+ { 0x1b, 0x8a },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x05 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x06 },
+ { 0x5c, 0x0b },
+ { 0x5d, 0x10 },
+ { 0x5e, 0x16 }
+ }
+ },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.ccd_size_divisor = setting.ccd_size_divisor;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_35;
+ sensor.optical_res = 1200;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 87;
+ sensor.dummy_pixel = 87;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10400;
+ sensor.fau_gain_white_ref = 0;
+ sensor.gain_white_ref = 0;
+ sensor.exposure = { 0x0400, 0x0400, 0x0400 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x02 },
+ { 0x18, 0x00 },
+ { 0x19, 0x50 },
+ { 0x1a, 0x00 }, // TODO: 1a-1d: these do no harm, but may be neccessery for CCD
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x05 }, // [GB](HI|LOW) not needed for cis
+ { 0x53, 0x07 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x3a },
+ { 0x59, 0x03 },
+ { 0x5a, 0x40 },
+ { 0x5b, 0x00 }, // TODO: 5b-5e
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_XP200;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 5;
+ sensor.dummy_pixel = 38;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 5200;
+ sensor.fau_gain_white_ref = 200;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x1450, 0x0c80, 0x0a28 };
+ sensor.custom_base_regs = {
+ { 0x08, 0x16 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x01 },
+ { 0x0b, 0x03 },
+ { 0x16, 0xb7 },
+ { 0x17, 0x0a },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x6a },
+ { 0x1b, 0x8a },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x05 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x06 },
+ { 0x5c, 0x0b },
+ { 0x5d, 0x10 },
+ { 0x5e, 0x16 },
+ };
+ sensor.custom_regs = {
+ { 0x08, 0x06 },
+ { 0x09, 0x07 },
+ { 0x0a, 0x0a },
+ { 0x0b, 0x04 },
+ { 0x16, 0x24 },
+ { 0x17, 0x04 },
+ { 0x18, 0x00 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x0a },
+ { 0x1b, 0x0a },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x11 },
+ { 0x52, 0x08 },
+ { 0x53, 0x02 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x1a },
+ { 0x59, 0x51 },
+ { 0x5a, 0x00 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ };
+ sensor.gamma = { 2.1f, 2.1f, 2.1f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ std::vector<unsigned> channels;
+ unsigned exposure_lperiod;
+ SensorExposure exposure;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e } },
+ { { 100 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e } },
+ { { 200 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e } },
+ { { 300 }, { 3 }, 9000, { 0x1644, 0x0c80, 0x092e } },
+ { { 600 }, { 3 }, 16000, { 0x1644, 0x0c80, 0x092e } },
+ { { 75 }, { 1 }, 16000, { 0x050a, 0x0fa0, 0x1010 } },
+ { { 100 }, { 1 }, 7800, { 0x050a, 0x0fa0, 0x1010 } },
+ { { 200 }, { 1 }, 11000, { 0x050a, 0x0fa0, 0x1010 } },
+ { { 300 }, { 1 }, 13000, { 0x050a, 0x0fa0, 0x1010 } },
+ { { 600 }, { 1 }, 24000, { 0x050a, 0x0fa0, 0x1010 } },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ sensor.resolutions = setting.resolutions;
+ sensor.channels = setting.channels;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_HP3670;
+ sensor.optical_res = 1200;
+ sensor.black_pixels = 48;
+ sensor.dummy_pixel = 16;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10872;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0, 0, 0 };
+ sensor.stagger_config = StaggerConfig{1200, 4}; // FIXME: may be incorrect
+ sensor.custom_base_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x0a },
+ { 0x0a, 0x0b },
+ { 0x0b, 0x0d },
+ { 0x16, 0x33 },
+ { 0x17, 0x07 },
+ { 0x18, 0x20 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x00 },
+ { 0x5a, 0x15 },
+ { 0x5b, 0x05 },
+ { 0x5c, 0x0a },
+ { 0x5d, 0x0f },
+ { 0x5e, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ unsigned exposure_lperiod;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 50 }, 5758, {
+ { 0x08, 0x00 },
+ { 0x09, 0x0a },
+ { 0x0a, 0x0b },
+ { 0x0b, 0x0d },
+ { 0x16, 0x33 },
+ { 0x17, 0x07 },
+ { 0x18, 0x33 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x02 },
+ { 0x1b, 0x13 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x15 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x05 },
+ { 0x5c, 0x0a },
+ { 0x5d, 0x0f },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 75 }, 4879, {
+ { 0x08, 0x00 },
+ { 0x09, 0x0a },
+ { 0x0a, 0x0b },
+ { 0x0b, 0x0d },
+ { 0x16, 0x33 },
+ { 0x17, 0x07 },
+ { 0x18, 0x33 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x02 },
+ { 0x1b, 0x13 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x15 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x05 },
+ { 0x5c, 0x0a },
+ { 0x5d, 0x0f },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 100 }, 4487, {
+ { 0x08, 0x00 },
+ { 0x09, 0x0a },
+ { 0x0a, 0x0b },
+ { 0x0b, 0x0d },
+ { 0x16, 0x33 },
+ { 0x17, 0x07 },
+ { 0x18, 0x33 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x02 },
+ { 0x1b, 0x13 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x15 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x05 },
+ { 0x5c, 0x0a },
+ { 0x5d, 0x0f },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 150 }, 4879, {
+ { 0x08, 0x00 },
+ { 0x09, 0x0a },
+ { 0x0a, 0x0b },
+ { 0x0b, 0x0d },
+ { 0x16, 0x33 },
+ { 0x17, 0x07 },
+ { 0x18, 0x33 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x02 },
+ { 0x1b, 0x13 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x15 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x05 },
+ { 0x5c, 0x0a },
+ { 0x5d, 0x0f },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 300 }, 4503, {
+ { 0x08, 0x00 },
+ { 0x09, 0x0a },
+ { 0x0a, 0x0b },
+ { 0x0b, 0x0d },
+ { 0x16, 0x33 },
+ { 0x17, 0x07 },
+ { 0x18, 0x33 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x02 },
+ { 0x1b, 0x13 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x0f },
+ { 0x53, 0x13 },
+ { 0x54, 0x17 },
+ { 0x55, 0x03 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0b },
+ { 0x58, 0x83 },
+ { 0x59, 0x15 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x05 },
+ { 0x5c, 0x0a },
+ { 0x5d, 0x0f },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 600 }, 10251, {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x06 },
+ { 0x0b, 0x08 },
+ { 0x16, 0x33 },
+ { 0x17, 0x07 },
+ { 0x18, 0x31 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x02 },
+ { 0x1b, 0x0e },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0f },
+ { 0x54, 0x13 },
+ { 0x55, 0x17 },
+ { 0x56, 0x03 },
+ { 0x57, 0x07 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x02 },
+ { 0x5c, 0x0e },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ { { 1200 }, 12750, {
+ { 0x08, 0x0d },
+ { 0x09, 0x0f },
+ { 0x0a, 0x11 },
+ { 0x0b, 0x13 },
+ { 0x16, 0x2b },
+ { 0x17, 0x07 },
+ { 0x18, 0x30 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x43 },
+ { 0x52, 0x03 },
+ { 0x53, 0x07 },
+ { 0x54, 0x0b },
+ { 0x55, 0x0f },
+ { 0x56, 0x13 },
+ { 0x57, 0x17 },
+ { 0x58, 0x23 },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc1 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x00 }
+ }
+ },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_DP665;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 27;
+ sensor.dummy_pixel = 27;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 2496;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x1100, 0x1100, 0x1100 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x02 },
+ { 0x18, 0x04 },
+ { 0x19, 0x50 },
+ { 0x1a, 0x10 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
+ { 0x53, 0x05 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x54 },
+ { 0x59, 0x03 },
+ { 0x5a, 0x00 },
+ { 0x5b, 0x00 }, // TODO: 5b-5e
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x01 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_ROADWARRIOR;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 27;
+ sensor.dummy_pixel = 27;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 5200;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x1100, 0x1100, 0x1100 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x02 },
+ { 0x18, 0x04 },
+ { 0x19, 0x50 },
+ { 0x1a, 0x10 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
+ { 0x53, 0x05 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x54 },
+ { 0x59, 0x03 },
+ { 0x5a, 0x00 },
+ { 0x5b, 0x00 }, // TODO: 5b-5e
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x01 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_DSMOBILE600;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 28;
+ sensor.dummy_pixel = 28;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 5200;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x1544, 0x1544, 0x1544 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x02 },
+ { 0x18, 0x04 },
+ { 0x19, 0x50 },
+ { 0x1a, 0x10 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
+ { 0x53, 0x05 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x54 },
+ { 0x59, 0x03 },
+ { 0x5a, 0x00 },
+ { 0x5b, 0x00 }, // TODO: 5b-5e
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x01 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_XP300;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 27;
+ sensor.dummy_pixel = 27;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10240;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x1100, 0x1100, 0x1100 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x02 },
+ { 0x18, 0x04 },
+ { 0x19, 0x50 },
+ { 0x1a, 0x10 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
+ { 0x53, 0x05 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x54 },
+ { 0x59, 0x03 },
+ { 0x5a, 0x00 },
+ { 0x5b, 0x00 }, // TODO: 5b-5e
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x01 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_DP685;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 27;
+ sensor.dummy_pixel = 27;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 5020;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x1100, 0x1100, 0x1100 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x02 },
+ { 0x18, 0x04 },
+ { 0x19, 0x50 },
+ { 0x1a, 0x10 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x02 },
+ { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
+ { 0x53, 0x05 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x54 },
+ { 0x59, 0x03 },
+ { 0x5a, 0x00 },
+ { 0x5b, 0x00 }, // TODO: 5b-5e
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x01 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_200;
+ sensor.optical_res = 4800;
+ sensor.black_pixels = 87*4;
+ sensor.dummy_pixel = 16*4;
+ sensor.ccd_start_xoffset = 320*8;
+ sensor.sensor_pixels = 5136*8;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.gamma = { 2.2f, 2.2f, 2.2f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ SensorExposure exposure;
+ unsigned segment_size;
+ std::vector<unsigned> segment_order;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ // Note: Windows driver uses 1424 lperiod and enables dummy line (0x17)
+ { { 75, 100, 150, 200 }, 2848, { 304, 203, 180 }, 5136, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ // Note: Windows driver uses 788 lperiod and enables dummy line (0x17)
+ { { 300, 400 }, 1424, { 304, 203, 180 }, 5136, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 600 }, 1432, { 492, 326, 296 }, 5136, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 1200 }, 2712, { 935, 592, 538 }, 5136, { 0, 1 }, {
+ { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 2400 }, 5280, { 1777, 1125, 979 }, 5136, { 0, 2, 1, 3 }, {
+ { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 4800 }, 10416, { 3377, 2138, 1780 }, 5136, { 0, 2, 4, 6, 1, 3, 5, 7 }, {
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ }
+ };
+
+ for (const auto& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ sensor.segment_size = setting.segment_size;
+ sensor.segment_order = setting.segment_order;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_700F;
+ sensor.optical_res = 4800;
+ sensor.black_pixels = 73*8; // black pixels 73 at 600 dpi
+ sensor.dummy_pixel = 16*8;
+ // 384 at 600 dpi
+ sensor.ccd_start_xoffset = 384*8;
+ // 8x5570 segments, 5187+1 for rounding
+ sensor.sensor_pixels = 5188*8;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ SensorExposure exposure;
+ unsigned segment_size;
+ std::vector<unsigned> segment_order;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75, 100, 150, 200 }, 2848, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 300 }, 1424, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 600 }, 1504, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 1200 }, 2696, { 1464, 844, 555 }, 5187, { 0, 1 }, {
+ { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 2400 }, 10576, { 2798, 1558, 972 }, 5187, { 0, 1, 2, 3 }, {
+ { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 4800 }, 10576, { 2798, 1558, 972 }, 5187, { 0, 1, 4, 5, 2, 3, 6, 7 }, {
+ { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ }
+ };
+
+ for (const auto& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ sensor.segment_size = setting.segment_size;
+ sensor.segment_order = setting.segment_order;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_100;
+ sensor.optical_res = 2400;
+ sensor.black_pixels = 87*4;
+ sensor.dummy_pixel = 16*4;
+ sensor.ccd_start_xoffset = 320*4;
+ sensor.sensor_pixels = 5136*4;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x01c1, 0x0126, 0x00e5 };
+ sensor.gamma = { 2.2f, 2.2f, 2.2f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ SensorExposure exposure;
+ unsigned segment_size;
+ std::vector<unsigned> segment_order;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75, 100, 150, 200 }, 2304, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 300 }, 1728, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 600 }, 1432, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ },
+ },
+ { { 1200 }, 2712, { 791, 542, 403 }, 5136, {0, 1}, {
+ { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ },
+ { { 2400 }, 5280, { 1504, 1030, 766 }, 5136, {0, 2, 1, 3}, {
+ { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },
+ { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 },
+ { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ }
+ }
+ };
+
+ for (const auto& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ sensor.segment_size = setting.segment_size;
+ sensor.segment_order = setting.segment_order;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_KVSS080;
+ sensor.optical_res = 600;
+ sensor.black_pixels = 38;
+ sensor.dummy_pixel = 38;
+ sensor.ccd_start_xoffset = 152;
+ sensor.sensor_pixels = 5376;
+ sensor.fau_gain_white_ref = 160;
+ sensor.gain_white_ref = 160;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.exposure_lperiod = 8000;
+ sensor.custom_regs = {
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },
+ { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0xff },
+ { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
+ { 0x0c, 0x00 },
+ { 0x70, 0x01 },
+ { 0x71, 0x03 },
+ { 0x9e, 0x00 },
+ { 0xaa, 0x00 },
+ { 0x16, 0x33 },
+ { 0x17, 0x1c },
+ { 0x18, 0x00 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x2c },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x04 },
+ { 0x52, 0x0c },
+ { 0x53, 0x0f },
+ { 0x54, 0x00 },
+ { 0x55, 0x03 },
+ { 0x56, 0x06 },
+ { 0x57, 0x09 },
+ { 0x58, 0x6b },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc0 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_G4050;
+ sensor.optical_res = 4800;
+ sensor.black_pixels = 50*8;
+ // 31 at 600 dpi dummy_pixels 58 at 1200
+ sensor.dummy_pixel = 58;
+ sensor.ccd_start_xoffset = 152;
+ sensor.sensor_pixels = 5360*8;
+ sensor.fau_gain_white_ref = 160;
+ sensor.gain_white_ref = 160;
+ sensor.exposure = { 0x2c09, 0x22b8, 0x10f0 };
+ sensor.stagger_config = StaggerConfig{ 2400, 4 }; // FIXME: may be incorrect
+ sensor.custom_regs = {};
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ ScanMethod method;
+ GenesysRegisterSettingSet extra_custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 100, 150, 200, 300, 400, 600 }, 8016, ScanMethod::FLATBED, {
+ { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff },
+ { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
+ { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
+ { 0x0c, 0x00 },
+ { 0x70, 0x00 },
+ { 0x71, 0x02 },
+ { 0x9e, 0x00 },
+ { 0xaa, 0x00 },
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x00 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x08 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0e },
+ { 0x54, 0x11 },
+ { 0x55, 0x02 },
+ { 0x56, 0x05 },
+ { 0x57, 0x08 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ }
+ },
+ { { 1200 }, 56064, ScanMethod::FLATBED, {
+ { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff },
+ { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff },
+ { 0x0c, 0x20 },
+ { 0x70, 0x08 },
+ { 0x71, 0x0c },
+ { 0x9e, 0xc0 },
+ { 0xaa, 0x05 },
+ { 0x16, 0x3b },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x38 },
+ { 0x1b, 0x10 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x08 },
+ { 0x52, 0x02 },
+ { 0x53, 0x05 },
+ { 0x54, 0x08 },
+ { 0x55, 0x0b },
+ { 0x56, 0x0e },
+ { 0x57, 0x11 },
+ { 0x58, 0x1b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ }
+ },
+ { { 2400 }, 56064, ScanMethod::FLATBED, {
+ { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
+ { 0x0c, 0x20 },
+ { 0x70, 0x08 },
+ { 0x71, 0x0a },
+ { 0x9e, 0xc0 },
+ { 0xaa, 0x05 },
+ { 0x16, 0x3b },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x38 },
+ { 0x1b, 0x10 },
+ { 0x1c, 0xc0 },
+ { 0x1d, 0x08 },
+ { 0x52, 0x02 },
+ { 0x53, 0x05 },
+ { 0x54, 0x08 },
+ { 0x55, 0x0b },
+ { 0x56, 0x0e },
+ { 0x57, 0x11 },
+ { 0x58, 0x1b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ }
+ },
+ { { 4800 }, 42752, ScanMethod::FLATBED, {
+ { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
+ { 0x0c, 0x21 },
+ { 0x70, 0x08 },
+ { 0x71, 0x0a },
+ { 0x9e, 0xc0 },
+ { 0xaa, 0x07 },
+ { 0x16, 0x3b },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x38 },
+ { 0x1b, 0x10 },
+ { 0x1c, 0xc1 },
+ { 0x1d, 0x08 },
+ { 0x52, 0x02 },
+ { 0x53, 0x05 },
+ { 0x54, 0x08 },
+ { 0x55, 0x0b },
+ { 0x56, 0x0e },
+ { 0x57, 0x11 },
+ { 0x58, 0x1b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ }
+ },
+ { ResolutionFilter::ANY, 15624, ScanMethod::TRANSPARENCY, {
+ { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f },
+ { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
+ { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
+ { 0x0c, 0x00 },
+ { 0x70, 0x00 },
+ { 0x71, 0x02 },
+ { 0x9e, 0x00 },
+ { 0xaa, 0x00 },
+ { 0x16, 0x33 },
+ { 0x17, 0x4c },
+ { 0x18, 0x01 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x08 },
+ { 0x52, 0x0e },
+ { 0x53, 0x11 },
+ { 0x54, 0x02 },
+ { 0x55, 0x05 },
+ { 0x56, 0x08 },
+ { 0x57, 0x0b },
+ { 0x58, 0x6b },
+ { 0x59, 0x00 },
+ { 0x5a, 0xc0 },
+ }
+ }
+ };
+
+ auto base_custom_regs = sensor.custom_regs;
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.method = setting.method;
+ sensor.custom_regs = base_custom_regs;
+ sensor.custom_regs.merge(setting.extra_custom_regs);
+ s_sensors->push_back(sensor);
+ }
+ }
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_HP_4850C;
+ sensor.optical_res = 4800;
+ sensor.black_pixels = 100;
+ sensor.dummy_pixel = 58;
+ sensor.ccd_start_xoffset = 152;
+ sensor.sensor_pixels = 5360*8;
+ sensor.fau_gain_white_ref = 160;
+ sensor.gain_white_ref = 160;
+ sensor.exposure = { 0x2c09, 0x22b8, 0x10f0 };
+ sensor.stagger_config = StaggerConfig{ 2400, 4 }; // FIXME: may be incorrect
+ sensor.custom_regs = {};
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ ScanMethod method;
+ GenesysRegisterSettingSet extra_custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 100, 150, 200, 300, 400, 600 }, 8016, ScanMethod::FLATBED, {
+ { 0x0c, 0x00 },
+ { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0x2a },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 },
+ { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 },
+ { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x70, 0x00 }, { 0x71, 0x02 },
+ { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff },
+ { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
+ { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
+ { 0x9e, 0x00 },
+ { 0xaa, 0x00 },
+ }
+ },
+ { { 1200 }, 56064, ScanMethod::FLATBED, {
+ { 0x0c, 0x20 },
+ { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a },
+ { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0x00 }, { 0x1d, 0x08 },
+ { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b },
+ { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x70, 0x08 }, { 0x71, 0x0c },
+ { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff },
+ { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff },
+ { 0x9e, 0xc0 },
+ { 0xaa, 0x05 },
+ }
+ },
+ { { 2400 }, 56064, ScanMethod::FLATBED, {
+ { 0x0c, 0x20 },
+ { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a },
+ { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc0 }, { 0x1d, 0x08 },
+ { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b },
+ { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x70, 0x08 }, { 0x71, 0x0a },
+ { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
+ { 0x9e, 0xc0 },
+ { 0xaa, 0x05 },
+ }
+ },
+ { { 4800 }, 42752, ScanMethod::FLATBED, {
+ { 0x0c, 0x21 },
+ { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a },
+ { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc1 }, { 0x1d, 0x08 },
+ { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b },
+ { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x70, 0x08 }, { 0x71, 0x0a },
+ { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
+ { 0x9e, 0xc0 },
+ { 0xaa, 0x07 },
+ }
+ },
+ { ResolutionFilter::ANY, 15624, ScanMethod::TRANSPARENCY, {
+ { 0x0c, 0x00 },
+ { 0x16, 0x33 }, { 0x17, 0x4c }, { 0x18, 0x01 }, { 0x19, 0x2a },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 },
+ { 0x52, 0x0e }, { 0x53, 0x11 }, { 0x54, 0x02 }, { 0x55, 0x05 },
+ { 0x56, 0x08 }, { 0x57, 0x0b }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 },
+ { 0x70, 0x00 }, { 0x71, 0x02 },
+ { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f },
+ { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
+ { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
+ { 0x9e, 0x00 },
+ { 0xaa, 0x00 },
+ }
+ }
+ };
+
+ auto base_custom_regs = sensor.custom_regs;
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.method = setting.method;
+ sensor.custom_regs = base_custom_regs;
+ sensor.custom_regs.merge(setting.extra_custom_regs);
+ s_sensors->push_back(sensor);
+ }
+ }
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_CANON_4400F;
+ sensor.optical_res = 4800;
+ sensor.ccd_size_divisor = 4;
+ sensor.black_pixels = 50*8;
+ // 31 at 600 dpi, 58 at 1200 dpi
+ sensor.dummy_pixel = 20;
+ sensor.ccd_start_xoffset = 152;
+ // 5360 max at 600 dpi
+ sensor.sensor_pixels = 5700 * 8;
+ sensor.fau_gain_white_ref = 160;
+ sensor.gain_white_ref = 160;
+ sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = get_sensor_optical_with_ccd_divisor;
+ sensor.get_register_hwdpi_fun = [](const Genesys_Sensor&, unsigned) { return 4800; };
+ sensor.get_hwdpi_divisor_fun = [](const Genesys_Sensor&, unsigned) { return 1; };
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ std::vector<ScanMethod> methods;
+ GenesysRegisterSettingSet extra_custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 300, 600, 1200 }, 11640, { ScanMethod::FLATBED }, {
+ { 0x16, 0x13 },
+ { 0x17, 0x0a },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x6b },
+ { 0x52, 0x0a },
+ { 0x53, 0x0d },
+ { 0x54, 0x00 },
+ { 0x55, 0x03 },
+ { 0x56, 0x06 },
+ { 0x57, 0x08 },
+ { 0x58, 0x5b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 },
+ { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 },
+ { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 },
+ { 0x9e, 0x2d },
+ }
+ },
+ { { 300, 600, 1200 }, 33300, { ScanMethod::TRANSPARENCY }, {
+ { 0x16, 0x13 },
+ { 0x17, 0x0a },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x6b },
+ { 0x52, 0x0a },
+ { 0x53, 0x0d },
+ { 0x54, 0x00 },
+ { 0x55, 0x03 },
+ { 0x56, 0x06 },
+ { 0x57, 0x08 },
+ { 0x58, 0x5b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x00 }, { 0x73, 0x02 },
+ { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 },
+ { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 },
+ { 0x9e, 0x2d },
+ }
+ },
+ { { 2400 }, 33300, { ScanMethod::TRANSPARENCY }, {
+ { 0x16, 0x13 },
+ { 0x17, 0x0a },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x01 },
+ { 0x1d, 0x75 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0d },
+ { 0x54, 0x00 },
+ { 0x55, 0x03 },
+ { 0x56, 0x06 },
+ { 0x57, 0x09 },
+ { 0x58, 0x53 },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 },
+ { 0x74, 0x00 }, { 0x75, 0xff }, { 0x76, 0x00 },
+ { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x54 }, { 0x7c, 0x92 },
+ { 0x9e, 0x2d },
+ }
+ },
+ { { 4800 }, 33300, { ScanMethod::TRANSPARENCY }, {
+ { 0x16, 0x13 },
+ { 0x17, 0x0a },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x61 },
+ { 0x1d, 0x75 },
+ { 0x52, 0x02 },
+ { 0x53, 0x05 },
+ { 0x54, 0x08 },
+ { 0x55, 0x0b },
+ { 0x56, 0x0d },
+ { 0x57, 0x0f },
+ { 0x58, 0x1b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x08 }, { 0x71, 0x0a }, { 0x72, 0x0a }, { 0x73, 0x0c },
+ { 0x74, 0x00 }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0xff },
+ { 0x7a, 0x00 }, { 0x7b, 0x54 }, { 0x7c, 0x92 },
+ { 0x9e, 0x2d },
+ }
+ }
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ for (auto method : setting.methods) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.method = method;
+ sensor.custom_regs = setting.extra_custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_CANON_8400F;
+ sensor.optical_res = 3200;
+ sensor.register_dpihw_override = 4800;
+ sensor.ccd_size_divisor = 1;
+ sensor.black_pixels = 50*8;
+ // 31 at 600 dpi, 58 at 1200 dpi
+ sensor.dummy_pixel = 20;
+ sensor.ccd_start_xoffset = 152;
+ sensor.sensor_pixels = 27200;
+ sensor.fau_gain_white_ref = 160;
+ sensor.gain_white_ref = 160;
+ sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 };
+ sensor.stagger_config = StaggerConfig{ 3200, 6 };
+ sensor.custom_regs = {};
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = get_sensor_optical_with_ccd_divisor;
+ sensor.get_register_hwdpi_fun = [](const Genesys_Sensor&, unsigned) { return 4800; };
+ sensor.get_hwdpi_divisor_fun = [](const Genesys_Sensor&, unsigned) { return 1; };
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ unsigned dpiset_override;
+ unsigned pixel_count_multiplier;
+ int exposure_lperiod;
+ std::vector<ScanMethod> methods;
+ GenesysRegisterSettingSet extra_custom_regs;
+ GenesysRegisterSettingSet custom_fe_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 400 }, 2400, 1, 7200, { ScanMethod::FLATBED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x13 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa0 },
+ { 0x52, 0x0d },
+ { 0x53, 0x10 },
+ { 0x54, 0x01 },
+ { 0x55, 0x04 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0a },
+ { 0x58, 0x6b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 },
+ { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb },
+ { 0x80, 0x2a },
+ }, {}
+ },
+ { { 800 }, 4800, 1, 7200, { ScanMethod::FLATBED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x13 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa0 },
+ { 0x52, 0x0d },
+ { 0x53, 0x10 },
+ { 0x54, 0x01 },
+ { 0x55, 0x04 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0a },
+ { 0x58, 0x6b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 },
+ { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb },
+ { 0x80, 0x20 },
+ }, {}
+ },
+ { { 1600 }, 4800, 1, 14400, { ScanMethod::FLATBED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x11 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa1 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0e },
+ { 0x54, 0x11 },
+ { 0x55, 0x02 },
+ { 0x56, 0x05 },
+ { 0x57, 0x08 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x03 },
+ { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 },
+ { 0x80, 0x28 },
+ }, {
+ { 0x03, 0x1f },
+ }
+ },
+ { { 3200 }, 4800, 1, 28800, { ScanMethod::FLATBED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa1 },
+ { 0x52, 0x02 },
+ { 0x53, 0x05 },
+ { 0x54, 0x08 },
+ { 0x55, 0x0b },
+ { 0x56, 0x0e },
+ { 0x57, 0x11 },
+ { 0x58, 0x1b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x09 }, { 0x71, 0x0a }, { 0x72, 0x0b }, { 0x73, 0x0c },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 },
+ { 0x80, 0x2b },
+ }, {
+ { 0x03, 0x1f },
+ },
+ },
+ { { 400 }, 2400, 1, 14400, { ScanMethod::TRANSPARENCY,
+ ScanMethod::TRANSPARENCY_INFRARED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x13 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa0 },
+ { 0x52, 0x0d },
+ { 0x53, 0x10 },
+ { 0x54, 0x01 },
+ { 0x55, 0x04 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0a },
+ { 0x58, 0x6b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 },
+ { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb },
+ { 0x80, 0x20 },
+ }, {}
+ },
+ { { 800 }, 4800, 1, 14400, { ScanMethod::TRANSPARENCY,
+ ScanMethod::TRANSPARENCY_INFRARED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x13 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa0 },
+ { 0x52, 0x0d },
+ { 0x53, 0x10 },
+ { 0x54, 0x01 },
+ { 0x55, 0x04 },
+ { 0x56, 0x07 },
+ { 0x57, 0x0a },
+ { 0x58, 0x6b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 },
+ { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb },
+ { 0x80, 0x20 },
+ }, {}
+ },
+ { { 1600 }, 4800, 1, 28800, { ScanMethod::TRANSPARENCY,
+ ScanMethod::TRANSPARENCY_INFRARED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x11 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa0 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0e },
+ { 0x54, 0x11 },
+ { 0x55, 0x02 },
+ { 0x56, 0x05 },
+ { 0x57, 0x08 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 },
+ { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 },
+ { 0x80, 0x29 },
+ }, {
+ { 0x03, 0x1f },
+ },
+ },
+ { { 3200 }, 4800, 1, 28800, { ScanMethod::TRANSPARENCY,
+ ScanMethod::TRANSPARENCY_INFRARED }, {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x84 },
+ { 0x1e, 0xa0 },
+ { 0x52, 0x02 },
+ { 0x53, 0x05 },
+ { 0x54, 0x08 },
+ { 0x55, 0x0b },
+ { 0x56, 0x0e },
+ { 0x57, 0x11 },
+ { 0x58, 0x1b },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ { 0x70, 0x09 }, { 0x71, 0x0a }, { 0x72, 0x0b }, { 0x73, 0x0c },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 },
+ { 0x80, 0x2b },
+ }, {
+ { 0x03, 0x1f },
+ },
+ },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings)
+ {
+ for (auto method : setting.methods) {
+ sensor.resolutions = setting.resolutions;
+ sensor.dpiset_override = setting.dpiset_override;
+ sensor.pixel_count_multiplier = setting.pixel_count_multiplier;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.method = method;
+ sensor.custom_regs = setting.extra_custom_regs;
+ sensor.custom_fe_regs = setting.custom_fe_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_CANON_8600F;
+ sensor.optical_res = 4800;
+ sensor.ccd_size_divisor = 4;
+ sensor.black_pixels = 31;
+ sensor.dummy_pixel = 20;
+ sensor.ccd_start_xoffset = 0; // not used at the moment
+ // 11372 pixels at 1200 dpi
+ sensor.sensor_pixels = 11372*4;
+ sensor.fau_gain_white_ref = 160;
+ sensor.gain_white_ref = 160;
+ sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 };
+ sensor.stagger_config = StaggerConfig{4800, 8};
+ sensor.custom_regs = {};
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = get_sensor_optical_with_ccd_divisor;
+ sensor.get_register_hwdpi_fun = [](const Genesys_Sensor&, unsigned) { return 4800; };
+ sensor.get_hwdpi_divisor_fun = [](const Genesys_Sensor&, unsigned) { return 1; };
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ std::vector<ScanMethod> methods;
+ GenesysRegisterSettingSet extra_custom_regs;
+ GenesysRegisterSettingSet custom_fe_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 300, 600, 1200 }, 24000, { ScanMethod::FLATBED }, {
+ { 0x0c, 0x00 },
+ { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b },
+ { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 },
+ { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 },
+ { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 },
+ { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
+ { 0x9e, 0x2d },
+ { 0xaa, 0x00 },
+ },
+ {},
+ },
+ { { 300, 600, 1200 }, 45000, { ScanMethod::TRANSPARENCY,
+ ScanMethod::TRANSPARENCY_INFRARED }, {
+ { 0x0c, 0x00 },
+ { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b },
+ { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 },
+ { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 },
+ { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 },
+ { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
+ { 0x9e, 0x2d },
+ { 0xaa, 0x00 },
+ },
+ {},
+ },
+ { { 2400 }, 45000, { ScanMethod::TRANSPARENCY,
+ ScanMethod::TRANSPARENCY_INFRARED }, {
+ { 0x0c, 0x00 },
+ { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x01 }, { 0x1d, 0x75 },
+ { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 },
+ { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 },
+ { 0x74, 0x03 }, { 0x75, 0xfe }, { 0x76, 0x00 },
+ { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
+ { 0x9e, 0x2d },
+ { 0xaa, 0x00 },
+ },
+ {},
+ },
+ { { 4800 }, 45000, { ScanMethod::TRANSPARENCY,
+ ScanMethod::TRANSPARENCY_INFRARED }, {
+ { 0x0c, 0x00 },
+ { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x61 }, { 0x1d, 0x75 },
+ { 0x52, 0x03 }, { 0x53, 0x06 }, { 0x54, 0x09 }, { 0x55, 0x0c },
+ { 0x56, 0x0f }, { 0x57, 0x00 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0x40 },
+ { 0x70, 0x0a }, { 0x71, 0x0c }, { 0x72, 0x0c }, { 0x73, 0x0e },
+ { 0x74, 0x03 }, { 0x75, 0xff }, { 0x76, 0xff },
+ { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
+ { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
+ { 0x9e, 0x2d },
+ { 0xaa, 0x00 },
+ },
+ { { 0x03, 0x1f },
+ },
+ },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings) {
+ for (auto method : setting.methods) {
+ sensor.resolutions = setting.resolutions;
+ sensor.method = method;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.custom_regs = setting.extra_custom_regs;
+ sensor.custom_fe_regs = setting.custom_fe_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_HP_N6310;
+ sensor.optical_res = 2400;
+ // sensor.ccd_size_divisor = 2; Possibly half CCD, needs checking
+ sensor.black_pixels = 96;
+ sensor.dummy_pixel = 26;
+ sensor.ccd_start_xoffset = 128;
+ sensor.sensor_pixels = 42720;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.custom_regs = {
+ { 0x16, 0x33 },
+ { 0x17, 0x0c },
+ { 0x18, 0x02 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x30 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x08 },
+ { 0x52, 0x0b },
+ { 0x53, 0x0e },
+ { 0x54, 0x11 },
+ { 0x55, 0x02 },
+ { 0x56, 0x05 },
+ { 0x57, 0x08 },
+ { 0x58, 0x63 },
+ { 0x59, 0x00 },
+ { 0x5a, 0x40 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_110;
+ sensor.optical_res = 2400;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 87;
+ sensor.dummy_pixel = 16;
+ sensor.ccd_start_xoffset = 303;
+ sensor.sensor_pixels = 5168*4;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.gamma = { 2.2f, 2.2f, 2.2f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ SensorExposure exposure;
+ std::vector<unsigned> segment_order;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75, 100, 150 }, 4608, { 462, 609, 453 }, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 },
+ { 0x96, 0x00 }, { 0x97, 0x9a },
+ { 0x98, 0x21 },
+ }
+ },
+ { { 300 }, 4608, { 462, 609, 453 }, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x0c },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 },
+ { 0x96, 0x00 }, { 0x97, 0x9a },
+ { 0x98, 0x21 },
+ }
+ },
+ { { 600 }, 5360, { 823, 1117, 805 }, std::vector<unsigned>{}, {
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x0a },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x21 },
+ },
+ },
+ { { 1200 }, 10528, { 6071, 6670, 6042 }, { 0, 1 }, {
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 },{ 0x20, 0x08 },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x12 }, { 0x89, 0x47 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x22 },
+ }
+ },
+ { { 2400 }, 20864, { 7451, 8661, 7405 }, { 0, 2, 1, 3 }, {
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x06 },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x12 }, { 0x89, 0x47 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x24 },
+ }
+ }
+ };
+
+ for (const auto& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ sensor.segment_order = setting.segment_order;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_120;
+ sensor.optical_res = 2400;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 87;
+ sensor.dummy_pixel = 16;
+ sensor.ccd_start_xoffset = 303;
+ // SEGCNT at 600 DPI by number of segments
+ sensor.sensor_pixels = 5104*4;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.gamma = { 2.2f, 2.2f, 2.2f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ SensorExposure exposure;
+ std::vector<unsigned> segment_order;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75, 100, 150, 300 }, 4608, { 1244, 1294, 1144 }, std::vector<unsigned>{}, {
+ { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },
+ { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 },
+ { 0x61, 0x20 },
+ { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x5e },
+ { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 },
+ { 0x96, 0x00 }, { 0x97, 0x70 },
+ { 0x98, 0x21 },
+ },
+ },
+ { { 600 }, 5360, { 2394, 2444, 2144 }, std::vector<unsigned>{}, {
+ { 0x16, 0x11 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },
+ { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 },
+ { 0x61, 0x20 },
+ { 0x70, 0x1f }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x5e },
+ { 0x93, 0x00 }, { 0x94, 0x13 }, { 0x95, 0xf0 },
+ { 0x96, 0x00 }, { 0x97, 0x8b },
+ { 0x98, 0x21 },
+ },
+ },
+ { { 1200 }, 10528, { 4694, 4644, 4094 }, std::vector<unsigned>{}, {
+ { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },
+ { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 },
+ { 0x61, 0x20 },
+ { 0x70, 0x1f }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x5e },
+ { 0x93, 0x00 }, { 0x94, 0x27 }, { 0x95, 0xe0 },
+ { 0x96, 0x00 }, { 0x97, 0xc0 },
+ { 0x98, 0x21 },
+ },
+ },
+ { { 2400 }, 20864, { 8944, 8144, 7994 }, std::vector<unsigned>{}, {
+ { 0x16, 0x11 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },
+ { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 },
+ { 0x61, 0x20 },
+ { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x5e },
+ { 0x93, 0x00 }, { 0x94, 0x4f }, { 0x95, 0xc0 },
+ { 0x96, 0x01 }, { 0x97, 0x2a },
+ { 0x98, 0x21 },
+ }
+ },
+ };
+
+ for (const auto& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ sensor.segment_order = setting.segment_order;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_210;
+ sensor.optical_res = 2400;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 87;
+ sensor.dummy_pixel = 16;
+ sensor.ccd_start_xoffset = 303;
+ sensor.sensor_pixels = 5168*4;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.gamma = { 2.2f, 2.2f, 2.2f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ SensorExposure exposure;
+ std::vector<unsigned> segment_order;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector<unsigned>{}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 },
+ { 0x96, 0x00 }, { 0x97, 0x9a },
+ { 0x98, 0x21 },
+ }
+ },
+ { { 600 }, 5360, { 388, 574, 393 }, std::vector<unsigned>{}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0a },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x21 },
+ }
+ },
+ { { 1200 }, 10528, { 388, 574, 393 }, {0, 1}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x08 },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x22 },
+ },
+ },
+ { { 2400 }, 20864, { 6839, 8401, 6859 }, {0, 2, 1, 3}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x06 },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x12 }, { 0x89, 0x47 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x24 },
+ },
+ }
+ };
+
+ for (const auto& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ sensor.segment_order = setting.segment_order;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_220;
+ sensor.optical_res = 2400;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 87;
+ sensor.dummy_pixel = 16;
+ sensor.ccd_start_xoffset = 303;
+ sensor.sensor_pixels = 5168*4;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.gamma = { 2.2f, 2.2f, 2.2f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;
+
+ {
+ struct CustomSensorSettings {
+ ResolutionFilter resolutions;
+ int exposure_lperiod;
+ SensorExposure exposure;
+ std::vector<unsigned> segment_order;
+ GenesysRegisterSettingSet custom_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector<unsigned>{}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 },
+ { 0x96, 0x00 }, { 0x97, 0x9a },
+ { 0x98, 0x21 },
+ }
+ },
+ { { 600 }, 5360, { 388, 574, 393 }, std::vector<unsigned>{}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0a },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x21 },
+ }
+ },
+ { { 1200 }, 10528, { 388, 574, 393 }, {0, 1}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x08 },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x00 }, { 0x89, 0x65 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x22 },
+ }
+ },
+ { { 2400 }, 20864, { 6839, 8401, 6859 }, {0, 2, 1, 3}, {
+ // { 0x16, 0x00 }, // FIXME: check if default value is different
+ { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },
+ { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x06 },
+ { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 },
+ { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 },
+ { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 },
+ { 0x61, 0x20 },
+ // { 0x70, 0x00 }, // FIXME: check if default value is different
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ { 0x88, 0x12 }, { 0x89, 0x47 },
+ { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 },
+ { 0x96, 0x00 }, { 0x97, 0xa3 },
+ { 0x98, 0x24 },
+ },
+ }
+ };
+
+ for (const auto& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.exposure = setting.exposure;
+ sensor.segment_order = setting.segment_order;
+ sensor.custom_regs = setting.custom_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600;
+ sensor.optical_res = 1200;
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 87;
+ sensor.dummy_pixel = 87;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10100;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x0b, 0x00 },
+ { 0x16, 0x33 },
+ { 0x17, 0x0b },
+ { 0x18, 0x11 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0xc4 },
+ { 0x52, 0x07 }, // [GB](HI|LOW) not needed for cis
+ { 0x53, 0x0a },
+ { 0x54, 0x0c },
+ { 0x55, 0x00 },
+ { 0x56, 0x02 },
+ { 0x57, 0x06 },
+ { 0x58, 0x22 },
+ { 0x59, 0x69 },
+ { 0x5a, 0x40 },
+ { 0x5b, 0x00 }, // TODO: 5b-5e
+ { 0x5c, 0x00 },
+ { 0x5d, 0x00 },
+ { 0x5e, 0x02 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200I;
+ sensor.optical_res = 7200;
+ sensor.register_dpihw_override = 1200;
+ sensor.black_pixels = 88; // TODO
+ sensor.dummy_pixel = 20;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10200; // TODO
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.stagger_config = StaggerConfig{7200, 4};
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x16, 0x23 },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x21 },
+ { 0x1d, 0x84 },
+ { 0x52, 0x0a },
+ { 0x53, 0x0d },
+ { 0x54, 0x10 },
+ { 0x55, 0x01 },
+ { 0x56, 0x04 },
+ { 0x57, 0x07 },
+ { 0x58, 0x3a },
+ { 0x59, 0x81 },
+ { 0x5a, 0xc0 },
+ { 0x70, 0x0a },
+ { 0x71, 0x0b },
+ { 0x72, 0x0c },
+ { 0x73, 0x0d },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+ {
+ struct CustomSensorSettings
+ {
+ ResolutionFilter resolutions;
+ ScanMethod method;
+ unsigned ccd_size_divisor;
+ unsigned logical_dpihw_override;
+ unsigned pixel_count_multiplier;
+ unsigned exposure_lperiod;
+ unsigned dpiset_override;
+ GenesysRegisterSettingSet custom_fe_regs;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 900 }, ScanMethod::TRANSPARENCY, 1, 900, 8, 0x2538, 150, {} },
+ { { 1800 }, ScanMethod::TRANSPARENCY, 1, 1800, 4, 0x2538, 300, {} },
+ { { 3600 }, ScanMethod::TRANSPARENCY, 1, 3600, 2, 0x2538, 600, {} },
+ { { 7200 }, ScanMethod::TRANSPARENCY, 1, 7200, 1, 0x19c8, 1200, {
+ { 0x02, 0x1b },
+ { 0x03, 0x14 },
+ { 0x04, 0x20 },
+ }
+ },
+ { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 900, 8, 0x1f54, 150, {} },
+ { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 1800, 4, 0x1f54, 300, {} },
+ { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 3600, 2, 0x1f54, 600, {} },
+ { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 7200, 1, 0x1f54, 1200, {} },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.method = setting.method;
+ sensor.ccd_size_divisor = setting.ccd_size_divisor;
+ sensor.logical_dpihw_override = setting.logical_dpihw_override;
+ sensor.pixel_count_multiplier = setting.pixel_count_multiplier;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.dpiset_override = setting.dpiset_override;
+ sensor.custom_fe_regs = setting.custom_fe_regs;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300;
+ sensor.optical_res = 7200;
+ sensor.method = ScanMethod::TRANSPARENCY;
+ sensor.register_dpihw_override = 1200;
+ sensor.black_pixels = 88; // TODO
+ sensor.dummy_pixel = 20;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10200; // TODO
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.exposure_lperiod = 0x2f44;
+ sensor.stagger_config = StaggerConfig{7200, 4};
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x16, 0x27 },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x84 },
+ { 0x52, 0x0a },
+ { 0x53, 0x0d },
+ { 0x54, 0x0f },
+ { 0x55, 0x01 },
+ { 0x56, 0x04 },
+ { 0x57, 0x07 },
+ { 0x58, 0x31 },
+ { 0x59, 0x79 },
+ { 0x5a, 0xc0 },
+ { 0x70, 0x0c },
+ { 0x71, 0x0d },
+ { 0x72, 0x0e },
+ { 0x73, 0x0f },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+ {
+ struct CustomSensorSettings
+ {
+ ResolutionFilter resolutions;
+ unsigned ccd_size_divisor;
+ unsigned logical_dpihw_override;
+ unsigned pixel_count_multiplier;
+ unsigned dpiset_override;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 900 }, 1, 900, 8, 150 },
+ { { 1800 }, 1, 1800, 4, 300 },
+ { { 3600 }, 1, 3600, 2, 600 },
+ { { 7200 }, 1, 7200, 1, 1200 },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.ccd_size_divisor = setting.ccd_size_divisor;
+ sensor.logical_dpihw_override = setting.logical_dpihw_override;
+ sensor.pixel_count_multiplier = setting.pixel_count_multiplier;
+ sensor.dpiset_override = setting.dpiset_override;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7500I;
+ sensor.optical_res = 7200;
+ sensor.register_dpihw_override = 1200;
+ sensor.black_pixels = 88; // TODO
+ sensor.dummy_pixel = 20;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10200; // TODO
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 230;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.stagger_config = StaggerConfig{7200, 4};
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0a, 0x00 },
+ { 0x16, 0x27 },
+ { 0x17, 0x0c },
+ { 0x18, 0x10 },
+ { 0x19, 0x2a },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x20 },
+ { 0x1d, 0x84 },
+ { 0x52, 0x0a },
+ { 0x53, 0x0d },
+ { 0x54, 0x0f },
+ { 0x55, 0x01 },
+ { 0x56, 0x04 },
+ { 0x57, 0x07 },
+ { 0x58, 0x31 },
+ { 0x59, 0x79 },
+ { 0x5a, 0xc0 },
+ { 0x70, 0x0c },
+ { 0x71, 0x0d },
+ { 0x72, 0x0e },
+ { 0x73, 0x0f },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;
+ {
+ struct CustomSensorSettings
+ {
+ ResolutionFilter resolutions;
+ ScanMethod method;
+ unsigned ccd_size_divisor;
+ unsigned logical_dpihw_override;
+ unsigned pixel_count_multiplier;
+ unsigned exposure_lperiod;
+ unsigned dpiset_override;
+ };
+
+ CustomSensorSettings custom_settings[] = {
+ { { 900 }, ScanMethod::TRANSPARENCY, 1, 900, 8, 0x2f44, 150 },
+ { { 1800 }, ScanMethod::TRANSPARENCY, 1, 1800, 4, 0x2f44, 300 },
+ { { 3600 }, ScanMethod::TRANSPARENCY, 1, 3600, 2, 0x2f44, 600 },
+ { { 7200 }, ScanMethod::TRANSPARENCY, 1, 7200, 1, 0x2f44, 1200 },
+ { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 900, 8, 0x2af8, 150 },
+ { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 1800, 4, 0x2af8, 300 },
+ { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 3600, 2, 0x2af8, 600 },
+ { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 7200, 1, 0x2af8, 1200 },
+ };
+
+ for (const CustomSensorSettings& setting : custom_settings) {
+ sensor.resolutions = setting.resolutions;
+ sensor.method = setting.method;
+ sensor.ccd_size_divisor = setting.ccd_size_divisor;
+ sensor.logical_dpihw_override = setting.logical_dpihw_override;
+ sensor.pixel_count_multiplier = setting.pixel_count_multiplier;
+ sensor.exposure_lperiod = setting.exposure_lperiod;
+ sensor.dpiset_override = setting.dpiset_override;
+ s_sensors->push_back(sensor);
+ }
+ }
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_IMG101;
+ sensor.resolutions = { 75, 100, 150, 300, 600, 1200 };
+ sensor.exposure_lperiod = 11000;
+ sensor.segment_size = 5136;
+ sensor.segment_order = {0, 1};
+ sensor.optical_res = 1200;
+ sensor.black_pixels = 31;
+ sensor.dummy_pixel = 31;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10800;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0x0000, 0x0000, 0x0000 };
+ sensor.custom_regs = {
+ { 0x16, 0xbb }, { 0x17, 0x13 }, { 0x18, 0x10 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x06 },
+ { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 },
+ { 0x56, 0x0a }, { 0x57, 0x00 }, { 0x58, 0x59 }, { 0x59, 0x31 }, { 0x5a, 0x40 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ };
+ sensor.gamma = { 1.7f, 1.7f, 1.7f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICBOOK_3800;
+ sensor.resolutions = { 75, 100, 150, 300, 600, 1200 };
+ sensor.exposure_lperiod = 11000;
+ sensor.optical_res = 1200;
+ sensor.black_pixels = 31;
+ sensor.dummy_pixel = 31;
+ sensor.ccd_start_xoffset = 0;
+ sensor.sensor_pixels = 10200;
+ sensor.fau_gain_white_ref = 210;
+ sensor.gain_white_ref = 200;
+ sensor.exposure = { 0, 0, 0 };
+ sensor.custom_regs = {
+ { 0x16, 0xbb }, { 0x17, 0x13 }, { 0x18, 0x10 }, { 0x19, 0xff },
+ { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x06 },
+ { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 },
+ { 0x56, 0x0a }, { 0x57, 0x00 }, { 0x58, 0x59 }, { 0x59, 0x31 }, { 0x5a, 0x40 },
+ { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },
+ { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f },
+ { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },
+ };
+ sensor.gamma = { 1.7f, 1.7f, 1.7f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+
+
+ sensor = Genesys_Sensor();
+ sensor.sensor_id = SensorId::CIS_CANON_LIDE_80;
+ sensor.optical_res = 1200; // real hardware limit is 2400
+ sensor.ccd_size_divisor = 2;
+ sensor.black_pixels = 20;
+ sensor.dummy_pixel = 6;
+ // tuned to give 3*8 multiple startx coordinate during shading calibration
+ sensor.ccd_start_xoffset = 34; // 14=>3, 20=>2
+ // 10400, too wide=>10288 in shading data 10240~
+ // 10208 too short for shading, max shading data = 10240 pixels, endpix-startpix=10208
+ sensor.sensor_pixels = 10240;
+ sensor.fau_gain_white_ref = 150;
+ sensor.gain_white_ref = 150;
+ // maps to 0x70-0x73 for GL841
+ sensor.exposure = { 0x1000, 0x1000, 0x0500 };
+ sensor.custom_regs = {
+ { 0x08, 0x00 },
+ { 0x09, 0x05 },
+ { 0x0a, 0x07 },
+ { 0x0b, 0x09 },
+ { 0x16, 0x00 },
+ { 0x17, 0x01 },
+ { 0x18, 0x00 },
+ { 0x19, 0x06 },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x04 },
+ { 0x52, 0x03 },
+ { 0x53, 0x07 },
+ { 0x54, 0x00 },
+ { 0x55, 0x00 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x29 },
+ { 0x59, 0x69 },
+ { 0x5a, 0x55 },
+ { 0x5b, 0x00 },
+ { 0x5c, 0x00 },
+ { 0x5d, 0x20 },
+ { 0x5e, 0x41 },
+ };
+ sensor.gamma = { 1.0f, 1.0f, 1.0f };
+ sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_register_hwdpi_fun = default_get_logical_hwdpi;
+ sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi;
+ sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;
+ s_sensors->push_back(sensor);
+}
+
+} // namespace genesys
diff --git a/backend/genesys/test_scanner_interface.cpp b/backend/genesys/test_scanner_interface.cpp
new file mode 100644
index 0000000..12f726f
--- /dev/null
+++ b/backend/genesys/test_scanner_interface.cpp
@@ -0,0 +1,229 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "test_scanner_interface.h"
+#include "device.h"
+#include <cstring>
+
+namespace genesys {
+
+TestScannerInterface::TestScannerInterface(Genesys_Device* dev) : dev_{dev}
+{
+ // initialize status registers
+ if (dev_->model->asic_type == AsicType::GL124) {
+ write_register(0x101, 0x00);
+ } else {
+ write_register(0x41, 0x00);
+ }
+ if (dev_->model->asic_type == AsicType::GL841 ||
+ dev_->model->asic_type == AsicType::GL843 ||
+ dev_->model->asic_type == AsicType::GL845 ||
+ dev_->model->asic_type == AsicType::GL846 ||
+ dev_->model->asic_type == AsicType::GL847)
+ {
+ write_register(0x40, 0x00);
+ }
+
+ // initialize other registers that we read on init
+ if (dev_->model->asic_type == AsicType::GL124) {
+ write_register(0x33, 0x00);
+ write_register(0xbd, 0x00);
+ write_register(0xbe, 0x00);
+ write_register(0x100, 0x00);
+ }
+
+ if (dev_->model->asic_type == AsicType::GL845 ||
+ dev_->model->asic_type == AsicType::GL846 ||
+ dev_->model->asic_type == AsicType::GL847)
+ {
+ write_register(0xbd, 0x00);
+ write_register(0xbe, 0x00);
+
+ write_register(0xd0, 0x00);
+ write_register(0xd1, 0x01);
+ write_register(0xd2, 0x02);
+ write_register(0xd3, 0x03);
+ write_register(0xd4, 0x04);
+ write_register(0xd5, 0x05);
+ write_register(0xd6, 0x06);
+ write_register(0xd7, 0x07);
+ write_register(0xd8, 0x08);
+ write_register(0xd9, 0x09);
+ }
+}
+
+TestScannerInterface::~TestScannerInterface() = default;
+
+bool TestScannerInterface::is_mock() const
+{
+ return true;
+}
+
+std::uint8_t TestScannerInterface::read_register(std::uint16_t address)
+{
+ return cached_regs_.get(address);
+}
+
+void TestScannerInterface::write_register(std::uint16_t address, std::uint8_t value)
+{
+ cached_regs_.update(address, value);
+}
+
+void TestScannerInterface::write_registers(const Genesys_Register_Set& regs)
+{
+ cached_regs_.update(regs);
+}
+
+
+void TestScannerInterface::write_0x8c(std::uint8_t index, std::uint8_t value)
+{
+ (void) index;
+ (void) value;
+}
+
+void TestScannerInterface::bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size)
+{
+ (void) addr;
+ std::memset(data, 0, size);
+}
+
+void TestScannerInterface::bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size)
+{
+ (void) addr;
+ (void) data;
+ (void) size;
+}
+
+void TestScannerInterface::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags)
+{
+ (void) type;
+ (void) addr;
+ (void) data;
+ (void) size;
+ (void) flags;
+}
+
+void TestScannerInterface::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags)
+{
+ (void) type;
+ (void) addr;
+ (void) data;
+ (void) size;
+ (void) flags;
+}
+
+void TestScannerInterface::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data)
+{
+ (void) addr;
+ (void) size;
+ (void) data;
+}
+
+std::uint16_t TestScannerInterface::read_fe_register(std::uint8_t address)
+{
+ return cached_fe_regs_.get(address);
+}
+
+void TestScannerInterface::write_fe_register(std::uint8_t address, std::uint16_t value)
+{
+ cached_fe_regs_.update(address, value);
+}
+
+IUsbDevice& TestScannerInterface::get_usb_device()
+{
+ return usb_dev_;
+}
+
+void TestScannerInterface::sleep_us(unsigned microseconds)
+{
+ (void) microseconds;
+}
+
+void TestScannerInterface::record_slope_table(unsigned table_nr,
+ const std::vector<std::uint16_t>& steps)
+{
+ slope_tables_[table_nr] = steps;
+}
+
+std::map<unsigned, std::vector<std::uint16_t>>& TestScannerInterface::recorded_slope_tables()
+{
+ return slope_tables_;
+}
+
+void TestScannerInterface::record_progress_message(const char* msg)
+{
+ last_progress_message_ = msg;
+}
+
+const std::string& TestScannerInterface::last_progress_message() const
+{
+ return last_progress_message_;
+}
+
+void TestScannerInterface::record_key_value(const std::string& key, const std::string& value)
+{
+ key_values_[key] = value;
+}
+
+std::map<std::string, std::string>& TestScannerInterface::recorded_key_values()
+{
+ return key_values_;
+}
+
+void TestScannerInterface::test_checkpoint(const std::string& name)
+{
+ if (checkpoint_callback_) {
+ checkpoint_callback_(*dev_, *this, name);
+ }
+}
+
+void TestScannerInterface::set_checkpoint_callback(TestCheckpointCallback callback)
+{
+ checkpoint_callback_ = callback;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/test_scanner_interface.h b/backend/genesys/test_scanner_interface.h
new file mode 100644
index 0000000..acf0f6d
--- /dev/null
+++ b/backend/genesys/test_scanner_interface.h
@@ -0,0 +1,122 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_TEST_SCANNER_INTERFACE_H
+#define BACKEND_GENESYS_TEST_SCANNER_INTERFACE_H
+
+#include "scanner_interface.h"
+#include "register_cache.h"
+#include "test_usb_device.h"
+#include "test_settings.h"
+
+#include <map>
+
+namespace genesys {
+
+class TestScannerInterface : public ScannerInterface
+{
+public:
+ TestScannerInterface(Genesys_Device* dev);
+
+ ~TestScannerInterface() override;
+
+ bool is_mock() const override;
+
+ const RegisterCache<std::uint8_t>& cached_regs() const { return cached_regs_; }
+ const RegisterCache<std::uint16_t>& cached_fe_regs() const { return cached_fe_regs_; }
+
+ std::uint8_t read_register(std::uint16_t address) override;
+ void write_register(std::uint16_t address, std::uint8_t value) override;
+ void write_registers(const Genesys_Register_Set& regs) override;
+
+ void write_0x8c(std::uint8_t index, std::uint8_t value) override;
+ void bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override;
+ void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override;
+
+ void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags) override;
+ void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data,
+ std::size_t size, Flags flags) override;
+ void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) override;
+
+ std::uint16_t read_fe_register(std::uint8_t address) override;
+ void write_fe_register(std::uint8_t address, std::uint16_t value) override;
+
+ IUsbDevice& get_usb_device() override;
+
+ void sleep_us(unsigned microseconds) override;
+
+ void record_progress_message(const char* msg) override;
+
+ const std::string& last_progress_message() const;
+
+ void record_slope_table(unsigned table_nr, const std::vector<std::uint16_t>& steps) override;
+
+ std::map<unsigned, std::vector<std::uint16_t>>& recorded_slope_tables();
+
+ void record_key_value(const std::string& key, const std::string& value) override;
+
+ std::map<std::string, std::string>& recorded_key_values();
+
+ void test_checkpoint(const std::string& name) override;
+
+ void set_checkpoint_callback(TestCheckpointCallback callback);
+
+private:
+ Genesys_Device* dev_;
+
+ RegisterCache<std::uint8_t> cached_regs_;
+ RegisterCache<std::uint16_t> cached_fe_regs_;
+ TestUsbDevice usb_dev_;
+
+ TestCheckpointCallback checkpoint_callback_;
+
+ std::map<unsigned, std::vector<std::uint16_t>> slope_tables_;
+
+ std::string last_progress_message_;
+ std::map<std::string, std::string> key_values_;
+};
+
+} // namespace genesys
+
+#endif
diff --git a/backend/genesys/test_settings.cpp b/backend/genesys/test_settings.cpp
new file mode 100644
index 0000000..425f09c
--- /dev/null
+++ b/backend/genesys/test_settings.cpp
@@ -0,0 +1,106 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "test_settings.h"
+
+namespace genesys {
+
+namespace {
+
+bool s_testing_mode = false;
+std::uint16_t s_vendor_id = 0;
+std::uint16_t s_product_id = 0;
+TestCheckpointCallback s_checkpoint_callback;
+
+} // namespace
+
+bool is_testing_mode()
+{
+ return s_testing_mode;
+}
+
+void disable_testing_mode()
+{
+ s_testing_mode = false;
+ s_vendor_id = 0;
+ s_product_id = 0;
+
+}
+
+void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id,
+ TestCheckpointCallback checkpoint_callback)
+{
+ s_testing_mode = true;
+ s_vendor_id = vendor_id;
+ s_product_id = product_id;
+ s_checkpoint_callback = checkpoint_callback;
+}
+
+std::uint16_t get_testing_vendor_id()
+{
+ return s_vendor_id;
+}
+
+std::uint16_t get_testing_product_id()
+{
+ return s_product_id;
+}
+
+std::string get_testing_device_name()
+{
+ std::string name;
+ unsigned max_size = 50;
+ name.resize(max_size);
+ name.resize(std::snprintf(&name.front(), max_size, "test device:0x%04x:0x%04x",
+ s_vendor_id, s_product_id));
+ return name;
+}
+
+TestCheckpointCallback get_testing_checkpoint_callback()
+{
+ return s_checkpoint_callback;
+}
+
+} // namespace genesys
diff --git a/backend/genesys/test_settings.h b/backend/genesys/test_settings.h
new file mode 100644
index 0000000..8ac03e0
--- /dev/null
+++ b/backend/genesys/test_settings.h
@@ -0,0 +1,70 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_TEST_SETTINGS_H
+#define BACKEND_GENESYS_TEST_SETTINGS_H
+
+#include "scanner_interface.h"
+#include "register_cache.h"
+#include "test_usb_device.h"
+#include <functional>
+
+namespace genesys {
+
+using TestCheckpointCallback = std::function<void(const Genesys_Device&,
+ TestScannerInterface&,
+ const std::string&)>;
+
+bool is_testing_mode();
+void disable_testing_mode();
+void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id,
+ TestCheckpointCallback checkpoint_callback);
+std::uint16_t get_testing_vendor_id();
+std::uint16_t get_testing_product_id();
+std::string get_testing_device_name();
+TestCheckpointCallback get_testing_checkpoint_callback();
+
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_TEST_SETTINGS_H
diff --git a/backend/genesys/test_usb_device.cpp b/backend/genesys/test_usb_device.cpp
new file mode 100644
index 0000000..de2399e
--- /dev/null
+++ b/backend/genesys/test_usb_device.cpp
@@ -0,0 +1,141 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#define DEBUG_DECLARE_ONLY
+
+#include "test_usb_device.h"
+#include "low.h"
+
+namespace genesys {
+
+TestUsbDevice::TestUsbDevice(std::uint16_t vendor, std::uint16_t product) :
+ vendor_{vendor},
+ product_{product}
+{
+}
+
+TestUsbDevice::~TestUsbDevice()
+{
+ if (is_open()) {
+ DBG(DBG_error, "TestUsbDevice not closed; closing automatically");
+ close();
+ }
+}
+
+void TestUsbDevice::open(const char* dev_name)
+{
+ DBG_HELPER(dbg);
+
+ if (is_open()) {
+ throw SaneException("device already open");
+ }
+ name_ = dev_name;
+ is_open_ = true;
+}
+
+void TestUsbDevice::clear_halt()
+{
+ DBG_HELPER(dbg);
+ assert_is_open();
+}
+
+void TestUsbDevice::reset()
+{
+ DBG_HELPER(dbg);
+ assert_is_open();
+}
+
+void TestUsbDevice::close()
+{
+ DBG_HELPER(dbg);
+ assert_is_open();
+
+ is_open_ = false;
+ name_ = "";
+}
+
+void TestUsbDevice::get_vendor_product(int& vendor, int& product)
+{
+ DBG_HELPER(dbg);
+ assert_is_open();
+ vendor = vendor_;
+ product = product_;
+}
+
+void TestUsbDevice::control_msg(int rtype, int reg, int value, int index, int length,
+ std::uint8_t* data)
+{
+ (void) reg;
+ (void) value;
+ (void) index;
+ DBG_HELPER(dbg);
+ assert_is_open();
+ if (rtype == REQUEST_TYPE_IN) {
+ std::memset(data, 0, length);
+ }
+}
+
+void TestUsbDevice::bulk_read(std::uint8_t* buffer, std::size_t* size)
+{
+
+ DBG_HELPER(dbg);
+ assert_is_open();
+ std::memset(buffer, 0, *size);
+}
+
+void TestUsbDevice::bulk_write(const std::uint8_t* buffer, std::size_t* size)
+{
+ (void) buffer;
+ (void) size;
+ DBG_HELPER(dbg);
+ assert_is_open();
+}
+
+void TestUsbDevice::assert_is_open() const
+{
+ if (!is_open()) {
+ throw SaneException("device not open");
+ }
+}
+
+} // namespace genesys
diff --git a/backend/genesys_sanei.h b/backend/genesys/test_usb_device.h
index 0e41600..abbd78a 100644
--- a/backend/genesys_sanei.h
+++ b/backend/genesys/test_usb_device.h
@@ -41,57 +41,45 @@
If you do not wish that, delete this exception notice.
*/
-#ifndef BACKEND_GENESYS_SANEI_H
-#define BACKEND_GENESYS_SANEI_H
+#ifndef BACKEND_GENESYS_TEST_USB_DEVICE_H
+#define BACKEND_GENESYS_TEST_USB_DEVICE_H
-#include "genesys_error.h"
-#include "../include/sane/sanei_usb.h"
+#include "usb_device.h"
-#include <cstdint>
-#include <string>
+namespace genesys {
-class UsbDevice {
+class TestUsbDevice : public IUsbDevice {
public:
- UsbDevice() = default;
- UsbDevice(const UsbDevice& other) = delete;
- UsbDevice& operator=(const UsbDevice&) = delete;
+ TestUsbDevice(std::uint16_t vendor, std::uint16_t product);
+ TestUsbDevice() = default;
- UsbDevice(UsbDevice&& other) :
- name_(other.name_),
- is_open_(other.is_open_),
- device_num_(other.device_num_)
- {
- other.set_not_open();
- }
+ ~TestUsbDevice() override;
- ~UsbDevice();
+ bool is_open() const override { return is_open_; }
- bool is_open() const { return is_open_; }
+ const std::string& name() const override { return name_; }
- int device_number() const { return device_num_; }
+ void open(const char* dev_name) override;
- const std::string& name() const { return name_; }
+ void clear_halt() override;
+ void reset() override;
+ void close() override;
- void open(const char* dev_name);
-
- void clear_halt();
- void reset();
- void close();
-
- void get_vendor_product(int& vendor, int& product);
-
- void control_msg(int rtype, int reg, int value, int index, int length, uint8_t* data);
- void bulk_read(uint8_t* buffer, size_t* size);
- void bulk_write(const uint8_t* buffer, size_t* size);
+ void get_vendor_product(int& vendor, int& product) override;
+ void control_msg(int rtype, int reg, int value, int index, int length,
+ std::uint8_t* data) override;
+ void bulk_read(std::uint8_t* buffer, std::size_t* size) override;
+ void bulk_write(const std::uint8_t* buffer, std::size_t* size) override;
private:
-
void assert_is_open() const;
- void set_not_open();
std::string name_;
bool is_open_ = false;
- int device_num_ = 0;
+ std::uint16_t vendor_ = 0;
+ std::uint16_t product_ = 0;
};
-#endif // BACKEND_GENESYS_SANEI_H
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_TEST_USB_DEVICE_H
diff --git a/backend/genesys_sanei.cc b/backend/genesys/usb_device.cpp
index 5b5b40a..2d02219 100644
--- a/backend/genesys_sanei.cc
+++ b/backend/genesys/usb_device.cpp
@@ -43,7 +43,11 @@
#define DEBUG_DECLARE_ONLY
-#include "genesys_sanei.h"
+#include "usb_device.h"
+
+namespace genesys {
+
+IUsbDevice::~IUsbDevice() = default;
UsbDevice::~UsbDevice()
{
@@ -104,21 +108,22 @@ void UsbDevice::get_vendor_product(int& vendor, int& product)
TIE(sanei_usb_get_vendor_product(device_num_, &vendor, &product));
}
-void UsbDevice::control_msg(int rtype, int reg, int value, int index, int length, uint8_t* data)
+void UsbDevice::control_msg(int rtype, int reg, int value, int index, int length,
+ std::uint8_t* data)
{
DBG_HELPER(dbg);
assert_is_open();
TIE(sanei_usb_control_msg(device_num_, rtype, reg, value, index, length, data));
}
-void UsbDevice::bulk_read(uint8_t* buffer, size_t* size)
+void UsbDevice::bulk_read(std::uint8_t* buffer, std::size_t* size)
{
DBG_HELPER(dbg);
assert_is_open();
TIE(sanei_usb_read_bulk(device_num_, buffer, size));
}
-void UsbDevice::bulk_write(const uint8_t* buffer, size_t* size)
+void UsbDevice::bulk_write(const std::uint8_t* buffer, std::size_t* size)
{
DBG_HELPER(dbg);
assert_is_open();
@@ -138,3 +143,5 @@ void UsbDevice::set_not_open()
is_open_ = false;
name_ = "";
}
+
+} // namespace genesys
diff --git a/backend/genesys/usb_device.h b/backend/genesys/usb_device.h
new file mode 100644
index 0000000..265c57c
--- /dev/null
+++ b/backend/genesys/usb_device.h
@@ -0,0 +1,118 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_USB_DEVICE_H
+#define BACKEND_GENESYS_USB_DEVICE_H
+
+#include "error.h"
+#include "../include/sane/sanei_usb.h"
+
+#include <cstdint>
+#include <string>
+
+namespace genesys {
+
+class IUsbDevice {
+public:
+ IUsbDevice() = default;
+
+ IUsbDevice(const IUsbDevice& other) = delete;
+ IUsbDevice& operator=(const IUsbDevice&) = delete;
+
+ virtual ~IUsbDevice();
+
+ virtual bool is_open() const = 0;
+
+ virtual const std::string& name() const = 0;
+
+ virtual void open(const char* dev_name) = 0;
+
+ virtual void clear_halt() = 0;
+ virtual void reset() = 0;
+ virtual void close() = 0;
+
+ virtual void get_vendor_product(int& vendor, int& product) = 0;
+
+ virtual void control_msg(int rtype, int reg, int value, int index, int length,
+ std::uint8_t* data) = 0;
+ virtual void bulk_read(std::uint8_t* buffer, std::size_t* size) = 0;
+ virtual void bulk_write(const std::uint8_t* buffer, std::size_t* size) = 0;
+
+};
+
+class UsbDevice : public IUsbDevice {
+public:
+ UsbDevice() = default;
+
+ ~UsbDevice() override;
+
+ bool is_open() const override { return is_open_; }
+
+ const std::string& name() const override { return name_; }
+
+ void open(const char* dev_name) override;
+
+ void clear_halt() override;
+ void reset() override;
+ void close() override;
+
+ void get_vendor_product(int& vendor, int& product) override;
+
+ void control_msg(int rtype, int reg, int value, int index, int length,
+ std::uint8_t* data) override;
+ void bulk_read(std::uint8_t* buffer, std::size_t* size) override;
+ void bulk_write(const std::uint8_t* buffer, std::size_t* size) override;
+
+private:
+
+ void assert_is_open() const;
+ void set_not_open();
+
+ std::string name_;
+ bool is_open_ = false;
+ int device_num_ = 0;
+};
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_USB_DEVICE_H
diff --git a/backend/genesys/utilities.h b/backend/genesys/utilities.h
new file mode 100644
index 0000000..1e268b5
--- /dev/null
+++ b/backend/genesys/utilities.h
@@ -0,0 +1,180 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef BACKEND_GENESYS_UTILITIES_H
+#define BACKEND_GENESYS_UTILITIES_H
+
+#include "error.h"
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+namespace genesys {
+
+template<class T>
+void compute_array_percentile_approx(T* result, const T* data,
+ std::size_t line_count, std::size_t elements_per_line,
+ float percentile)
+{
+ if (line_count == 0) {
+ throw SaneException("invalid line count");
+ }
+
+ if (line_count == 1) {
+ std::copy(data, data + elements_per_line, result);
+ return;
+ }
+
+ std::vector<T> column_elems;
+ column_elems.resize(line_count, 0);
+
+ std::size_t select_elem = std::min(static_cast<std::size_t>(line_count * percentile),
+ line_count - 1);
+
+ auto select_it = column_elems.begin() + select_elem;
+
+ for (std::size_t ix = 0; ix < elements_per_line; ++ix) {
+ for (std::size_t iy = 0; iy < line_count; ++iy) {
+ column_elems[iy] = data[iy * elements_per_line + ix];
+ }
+
+ std::nth_element(column_elems.begin(), select_it, column_elems.end());
+
+ *result++ = *select_it;
+ }
+}
+
+template<class Char, class Traits>
+class BasicStreamStateSaver
+{
+public:
+ explicit BasicStreamStateSaver(std::basic_ios<Char, Traits>& stream) :
+ stream_{stream}
+ {
+ flags_ = stream_.flags();
+ width_ = stream_.width();
+ precision_ = stream_.precision();
+ fill_ = stream_.fill();
+ }
+
+ ~BasicStreamStateSaver()
+ {
+ stream_.flags(flags_);
+ stream_.width(width_);
+ stream_.precision(precision_);
+ stream_.fill(fill_);
+ }
+
+ BasicStreamStateSaver(const BasicStreamStateSaver&) = delete;
+ BasicStreamStateSaver& operator=(const BasicStreamStateSaver&) = delete;
+
+private:
+ std::basic_ios<Char, Traits>& stream_;
+ std::ios_base::fmtflags flags_;
+ std::streamsize width_ = 0;
+ std::streamsize precision_ = 0;
+ Char fill_ = ' ';
+};
+
+using StreamStateSaver = BasicStreamStateSaver<char, std::char_traits<char>>;
+
+template<class T>
+std::string format_indent_braced_list(unsigned indent, const T& x)
+{
+ std::string indent_str(indent, ' ');
+ std::ostringstream out;
+ out << x;
+ auto formatted_str = out.str();
+ if (formatted_str.empty()) {
+ return formatted_str;
+ }
+
+ std::string out_str;
+ for (std::size_t i = 0; i < formatted_str.size(); ++i) {
+ out_str += formatted_str[i];
+
+ if (formatted_str[i] == '\n' &&
+ i < formatted_str.size() - 1 &&
+ formatted_str[i + 1] != '\n')
+ {
+ out_str += indent_str;
+ }
+ }
+ return out_str;
+}
+
+template<class T>
+std::string format_vector_unsigned(unsigned indent, const std::vector<T>& arg)
+{
+ std::ostringstream out;
+ std::string indent_str(indent, ' ');
+
+ out << "std::vector<T>{ ";
+ for (const auto& el : arg) {
+ out << indent_str << static_cast<unsigned>(el) << "\n";
+ }
+ out << "}";
+ return out.str();
+}
+
+template<class T>
+std::string format_vector_indent_braced(unsigned indent, const char* type,
+ const std::vector<T>& arg)
+{
+ if (arg.empty()) {
+ return "{}";
+ }
+ std::string indent_str(indent, ' ');
+ std::stringstream out;
+ out << "std::vector<" << type << ">{\n";
+ for (const auto& item : arg) {
+ out << indent_str << format_indent_braced_list(indent, item) << '\n';
+ }
+ out << "}";
+ return out.str();
+}
+
+} // namespace genesys
+
+#endif // BACKEND_GENESYS_UTILITIES_H
diff --git a/backend/genesys_conv.cc b/backend/genesys_conv.cc
deleted file mode 100644
index 06fd4e0..0000000
--- a/backend/genesys_conv.cc
+++ /dev/null
@@ -1,474 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2005, 2006 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
- Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-/*
- * Conversion filters for genesys backend
- */
-
-
-/*8 bit*/
-#define SINGLE_BYTE
-#define BYTES_PER_COMPONENT 1
-#define COMPONENT_TYPE uint8_t
-
-#define FUNC_NAME(f) f ## _8
-
-#include "genesys_conv_hlp.cc"
-
-#undef FUNC_NAME
-
-#undef COMPONENT_TYPE
-#undef BYTES_PER_COMPONENT
-#undef SINGLE_BYTE
-
-/*16 bit*/
-#define DOUBLE_BYTE
-#define BYTES_PER_COMPONENT 2
-#define COMPONENT_TYPE uint16_t
-
-#define FUNC_NAME(f) f ## _16
-
-#include "genesys_conv_hlp.cc"
-
-#undef FUNC_NAME
-
-#undef COMPONENT_TYPE
-#undef BYTES_PER_COMPONENT
-#undef DOUBLE_BYTE
-
-static SANE_Status
-genesys_reverse_bits(
- uint8_t *src_data,
- uint8_t *dst_data,
- size_t bytes)
-{
- size_t i;
- for(i = 0; i < bytes; i++) {
- *dst_data++ = ~ *src_data++;
- }
- return SANE_STATUS_GOOD;
-}
-
-/**
- * uses the threshold/threshold_curve to control software binarization
- * This code was taken from the epjistsu backend by m. allan noah
- * @param dev device set up for the scan
- * @param src pointer to raw data
- * @param dst pointer where to store result
- * @param width width of the processed line
- * */
-static SANE_Status
-binarize_line(Genesys_Device * dev, uint8_t *src, uint8_t *dst, int width)
-{
- int j, windowX, sum = 0;
- int thresh;
- int offset, addCol, dropCol;
- unsigned char mask;
-
- int x;
- uint8_t min, max;
-
- /* normalize line */
- min = 255;
- max = 0;
- for (x = 0; x < width; x++)
- {
- if (src[x] > max)
- {
- max = src[x];
- }
- if (src[x] < min)
- {
- min = src[x];
- }
- }
-
- /* safeguard against dark or white areas */
- if(min>80)
- min=0;
- if(max<80)
- max=255;
- for (x = 0; x < width; x++)
- {
- src[x] = ((src[x] - min) * 255) / (max - min);
- }
-
- /* ~1mm works best, but the window needs to have odd # of pixels */
- windowX = (6 * dev->settings.xres) / 150;
- if (!(windowX % 2))
- windowX++;
-
- /* second, prefill the sliding sum */
- for (j = 0; j < windowX; j++)
- sum += src[j];
-
- /* third, walk the input buffer, update the sliding sum, */
- /* determine threshold, output bits */
- for (j = 0; j < width; j++)
- {
- /* output image location */
- offset = j % 8;
- mask = 0x80 >> offset;
- thresh = dev->settings.threshold;
-
- /* move sum/update threshold only if there is a curve */
- if (dev->settings.threshold_curve)
- {
- addCol = j + windowX / 2;
- dropCol = addCol - windowX;
-
- if (dropCol >= 0 && addCol < width)
- {
- sum -= src[dropCol];
- sum += src[addCol];
- }
- thresh = dev->lineart_lut[sum / windowX];
- }
-
- /* use average to lookup threshold */
- if (src[j] > thresh)
- *dst &= ~mask; /* white */
- else
- *dst |= mask; /* black */
-
- if (offset == 7)
- dst++;
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/**
- * software lineart using data from a 8 bit gray scan. We assume true gray
- * or monochrome scan as input.
- */
-static SANE_Status
-genesys_gray_lineart(
- Genesys_Device *dev,
- uint8_t *src_data,
- uint8_t *dst_data,
- size_t pixels,
- size_t lines,
- uint8_t threshold)
-{
- size_t y;
-
- DBG(DBG_io2, "%s: converting %lu lines of %lu pixels\n", __func__, (unsigned long)lines,
- (unsigned long)pixels);
- DBG(DBG_io2, "%s: threshold=%d\n", __func__, threshold);
-
- for (y = 0; y < lines; y++)
- {
- binarize_line (dev, src_data + y * pixels, dst_data, pixels);
- dst_data += pixels / 8;
- }
- return SANE_STATUS_GOOD;
-}
-
-/** @brief shrink or grow scanned data to fit the final scan size
- * This function shrinks the scanned data it the required resolution is lower than the hardware one,
- * or grows it in case it is the opposite like when motor resolution is higher than
- * sensor's one.
- */
-static SANE_Status
-genesys_shrink_lines_1 (
- uint8_t *src_data,
- uint8_t *dst_data,
- unsigned int lines,
- unsigned int src_pixels,
- unsigned int dst_pixels,
- unsigned int channels)
-{
- unsigned int dst_x, src_x, y, c, cnt;
- unsigned int avg[3], val;
- uint8_t *src = (uint8_t *) src_data;
- uint8_t *dst = (uint8_t *) dst_data;
-
- /* choose between case where me must reduce or grow the scanned data */
- if (src_pixels > dst_pixels)
- {
- /* shrink data */
- /* TODO action must be taken at bit level, no bytes */
- src_pixels /= 8;
- dst_pixels /= 8;
- /*take first _byte_ */
- for (y = 0; y < lines; y++)
- {
- cnt = src_pixels / 2;
- src_x = 0;
- for (dst_x = 0; dst_x < dst_pixels; dst_x++)
- {
- while (cnt < src_pixels && src_x < src_pixels)
- {
- cnt += dst_pixels;
-
- for (c = 0; c < channels; c++)
- avg[c] = *src++;
- src_x++;
- }
- cnt -= src_pixels;
-
- for (c = 0; c < channels; c++)
- *dst++ = avg[c];
- }
- }
- }
- else
- {
- /* common case where y res is double x res */
- for (y = 0; y < lines; y++)
- {
- if (2 * src_pixels == dst_pixels)
- {
- /* double and interleave on line */
- for (c = 0; c < src_pixels/8; c++)
- {
- /* first 4 bits */
- val = 0;
- val |= (*src & 0x80) >> 0; /* X___ ____ --> X___ ____ */
- val |= (*src & 0x80) >> 1; /* X___ ____ --> _X__ ____ */
- val |= (*src & 0x40) >> 1; /* _X__ ____ --> __X_ ____ */
- val |= (*src & 0x40) >> 2; /* _X__ ____ --> ___X ____ */
- val |= (*src & 0x20) >> 2; /* __X_ ____ --> ____ X___ */
- val |= (*src & 0x20) >> 3; /* __X_ ____ --> ____ _X__ */
- val |= (*src & 0x10) >> 3; /* ___X ____ --> ____ __X_ */
- val |= (*src & 0x10) >> 4; /* ___X ____ --> ____ ___X */
- *dst = val;
- dst++;
-
- /* last for bits */
- val = 0;
- val |= (*src & 0x08) << 4; /* ____ X___ --> X___ ____ */
- val |= (*src & 0x08) << 3; /* ____ X___ --> _X__ ____ */
- val |= (*src & 0x04) << 3; /* ____ _X__ --> __X_ ____ */
- val |= (*src & 0x04) << 2; /* ____ _X__ --> ___X ____ */
- val |= (*src & 0x02) << 2; /* ____ __X_ --> ____ X___ */
- val |= (*src & 0x02) << 1; /* ____ __X_ --> ____ _X__ */
- val |= (*src & 0x01) << 1; /* ____ ___X --> ____ __X_ */
- val |= (*src & 0x01) << 0; /* ____ ___X --> ____ ___X */
- *dst = val;
- dst++;
- src++;
- }
- }
- else
- {
- /* TODO: since depth is 1, we must interpolate bit within bytes */
- DBG (DBG_warn, "%s: inaccurate bit expansion!\n", __func__);
- cnt = dst_pixels / 2;
- dst_x = 0;
- for (src_x = 0; src_x < src_pixels; src_x++)
- {
- for (c = 0; c < channels; c++)
- avg[c] = *src++;
- while (cnt < dst_pixels && dst_x < dst_pixels)
- {
- cnt += src_pixels;
- for (c = 0; c < channels; c++)
- *dst++ = avg[c];
- dst_x++;
- }
- cnt -= dst_pixels;
- }
- }
- }
- }
-
- return SANE_STATUS_GOOD;
-}
-
-
-/** Look in image for likely left/right/bottom paper edges, then crop image.
- * Since failing to crop isn't fatal, we always return SANE_STATUS_GOOD .
- */
-static SANE_Status
-genesys_crop(Genesys_Scanner *s)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Device *dev = s->dev;
- int top = 0;
- int bottom = 0;
- int left = 0;
- int right = 0;
-
- DBG (DBG_proc, "%s: start\n", __func__);
-
- /* first find edges if any */
- status = sanei_magic_findEdges (&s->params,
- dev->img_buffer.data(),
- dev->settings.xres,
- dev->settings.yres,
- &top,
- &bottom,
- &left,
- &right);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_info, "%s: bad or no edges, bailing\n", __func__);
- return SANE_STATUS_GOOD;
- }
- DBG (DBG_io, "%s: t:%d b:%d l:%d r:%d\n", __func__, top, bottom, left,
- right);
-
- /* now crop the image */
- status =
- sanei_magic_crop (&(s->params), dev->img_buffer.data(), top, bottom, left, right);
- if (status)
- {
- DBG (DBG_warn, "%s: failed to crop\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- /* update counters to new image size */
- dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines;
-
- DBG (DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/** Look in image for likely upper and left paper edges, then rotate
- * image so that upper left corner of paper is upper left of image.
- * @return since failure doens't prevent scanning, we always return
- * SANE_STATUS_GOOD
- */
-static SANE_Status
-genesys_deskew(Genesys_Scanner *s, const Genesys_Sensor& sensor)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Device *dev = s->dev;
-
- int x = 0, y = 0, bg;
- double slope = 0;
-
- DBG (DBG_proc, "%s: start\n", __func__);
-
- bg=0;
- if(s->params.format==SANE_FRAME_GRAY && s->params.depth == 1)
- {
- bg=0xff;
- }
- status = sanei_magic_findSkew (&s->params,
- dev->img_buffer.data(),
- sensor.optical_res,
- sensor.optical_res,
- &x,
- &y,
- &slope);
- if (status!=SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: bad findSkew, bailing\n", __func__);
- return SANE_STATUS_GOOD;
- }
- DBG(DBG_info, "%s: slope=%f => %f\n",__func__,slope, (slope/M_PI_2)*90);
- /* rotate image slope is in [-PI/2,PI/2]
- * positive values rotate trigonometric direction wise */
- status = sanei_magic_rotate (&s->params,
- dev->img_buffer.data(),
- x,
- y,
- slope,
- bg);
- if (status!=SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: rotate error: %s", __func__, sane_strstatus(status));
- }
-
- DBG (DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/** remove lone dots
- * @return since failure doens't prevent scanning, we always return
- * SANE_STATUS_GOOD
- */
-static SANE_Status
-genesys_despeck(Genesys_Scanner *s)
-{
- if(sanei_magic_despeck(&s->params,
- s->dev->img_buffer.data(),
- s->despeck)!=SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: bad despeck, bailing\n",__func__);
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/** Look if image needs rotation and apply it
- * */
-static SANE_Status
-genesys_derotate (Genesys_Scanner * s)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int angle = 0;
-
- DBGSTART;
- status = sanei_magic_findTurn (&s->params,
- s->dev->img_buffer.data(),
- s->resolution,
- s->resolution,
- &angle);
-
- if (status)
- {
- DBG (DBG_warn, "%s: failed : %d\n", __func__, status);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- /* apply rotation angle found */
- status = sanei_magic_turn (&s->params, s->dev->img_buffer.data(), angle);
- if (status)
- {
- DBG (DBG_warn, "%s: failed : %d\n", __func__, status);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- /* update counters to new image size */
- s->dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_conv_hlp.cc b/backend/genesys_conv_hlp.cc
deleted file mode 100644
index 7663a87..0000000
--- a/backend/genesys_conv_hlp.cc
+++ /dev/null
@@ -1,345 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2005 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-/*
- * Conversion filters for genesys backend
- */
-
-static SANE_Status
-FUNC_NAME(genesys_reorder_components_cis) (
- uint8_t *src_data,
- uint8_t *dst_data,
- unsigned int lines,
- unsigned int pixels)
-{
- unsigned int x, y;
- uint8_t *src[3];
- uint8_t *dst = dst_data;
- unsigned int rest = pixels * 2 * BYTES_PER_COMPONENT;
-
- src[0] = src_data + pixels * BYTES_PER_COMPONENT * 0;
- src[1] = src_data + pixels * BYTES_PER_COMPONENT * 1;
- src[2] = src_data + pixels * BYTES_PER_COMPONENT * 2;
-
- for(y = 0; y < lines; y++) {
- for(x = 0; x < pixels; x++) {
-
-#ifndef DOUBLE_BYTE
- *dst++ = *src[0]++;
- *dst++ = *src[1]++;
- *dst++ = *src[2]++;
-#else
-# ifndef WORDS_BIGENDIAN
- *dst++ = *src[0]++;
- *dst++ = *src[0]++;
- *dst++ = *src[1]++;
- *dst++ = *src[1]++;
- *dst++ = *src[2]++;
- *dst++ = *src[2]++;
-# else
- *dst++ = src[0][1];
- *dst++ = src[0][0];
- *dst++ = src[1][1];
- *dst++ = src[1][0];
- *dst++ = src[2][1];
- *dst++ = src[2][0];
- src[0] += 2;
- src[1] += 2;
- src[2] += 2;
-# endif
-#endif
- }
-
- src[0] += rest;
- src[1] += rest;
- src[2] += rest;
- }
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-FUNC_NAME(genesys_reorder_components_cis_bgr) (
- uint8_t *src_data,
- uint8_t *dst_data,
- unsigned int lines,
- unsigned int pixels)
-{
- unsigned int x, y;
- uint8_t *src[3];
- uint8_t *dst = dst_data;
- unsigned int rest = pixels * 2 * BYTES_PER_COMPONENT;
-
- src[0] = src_data + pixels * BYTES_PER_COMPONENT * 0;
- src[1] = src_data + pixels * BYTES_PER_COMPONENT * 1;
- src[2] = src_data + pixels * BYTES_PER_COMPONENT * 2;
-
- for(y = 0; y < lines; y++) {
- for(x = 0; x < pixels; x++) {
-#ifndef DOUBLE_BYTE
- *dst++ = *src[2]++;
- *dst++ = *src[1]++;
- *dst++ = *src[0]++;
-#else
-# ifndef WORDS_BIGENDIAN
- *dst++ = *src[2]++;
- *dst++ = *src[2]++;
- *dst++ = *src[1]++;
- *dst++ = *src[1]++;
- *dst++ = *src[0]++;
- *dst++ = *src[0]++;
-# else
- *dst++ = src[2][1];
- *dst++ = src[2][0];
- *dst++ = src[1][1];
- *dst++ = src[1][0];
- *dst++ = src[0][1];
- *dst++ = src[0][0];
- src[0] += 2;
- src[1] += 2;
- src[2] += 2;
-# endif
-#endif
- }
-
- src[0] += rest;
- src[1] += rest;
- src[2] += rest;
- }
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-FUNC_NAME(genesys_reorder_components_bgr) (
- uint8_t *src_data,
- uint8_t *dst_data,
- unsigned int lines,
- unsigned int pixels)
-{
- unsigned int c;
- uint8_t *src = src_data;
- uint8_t *dst = dst_data;
-
- for(c = 0; c < lines * pixels; c++) {
-
-#ifndef DOUBLE_BYTE
- *dst++ = src[2];
- *dst++ = src[1];
- *dst++ = src[0];
- src += 3;
-#else
-# ifndef WORDS_BIGENDIAN
- *dst++ = src[2 * 2 + 0];
- *dst++ = src[2 * 2 + 1];
- *dst++ = src[1 * 2 + 0];
- *dst++ = src[1 * 2 + 1];
- *dst++ = src[0 * 2 + 0];
- *dst++ = src[0 * 2 + 1];
-# else
- *dst++ = src[2 * 2 + 1];
- *dst++ = src[2 * 2 + 0];
- *dst++ = src[1 * 2 + 1];
- *dst++ = src[1 * 2 + 0];
- *dst++ = src[0 * 2 + 1];
- *dst++ = src[0 * 2 + 0];
-# endif
- src += 3 * 2;
-#endif
-
- }
- return SANE_STATUS_GOOD;
-}
-
-#if defined(DOUBLE_BYTE) && defined(WORDS_BIGENDIAN)
-static SANE_Status
-FUNC_NAME(genesys_reorder_components_endian) (
- uint8_t *src_data,
- uint8_t *dst_data,
- unsigned int lines,
- unsigned int pixels,
- unsigned int channels)
-{
- unsigned int c;
- uint8_t *src = src_data;
- uint8_t *dst = dst_data;
-
- for(c = 0; c < lines * pixels * channels; c++) {
- *dst++ = src[1];
- *dst++ = src[0];
- src += 2;
- }
-return SANE_STATUS_GOOD;
-}
-#endif /*defined(DOUBLE_BYTE) && defined(WORDS_BIGENDIAN)*/
-
-
-static SANE_Status
-FUNC_NAME(genesys_reverse_ccd) (
- uint8_t *src_data,
- uint8_t *dst_data,
- unsigned int lines,
- unsigned int components_per_line,
- unsigned int *ccd_shift,
- unsigned int component_count)
-{
- unsigned int x, y, c;
- COMPONENT_TYPE *src = (COMPONENT_TYPE *)src_data;
- COMPONENT_TYPE *dst = (COMPONENT_TYPE *)dst_data;
- COMPONENT_TYPE *srcp;
- COMPONENT_TYPE *dstp;
- unsigned int pitch = components_per_line;
- unsigned int ccd_shift_pitch[12];
- unsigned int *csp;
-
- for (c = 0; c < component_count; c++)
- ccd_shift_pitch[c] = ccd_shift[c] * pitch;
-
-/*
- * cache efficiency:
- we are processing a single line component_count times, so it should fit
- into the cpu cache for maximum efficiency. our lines take
- maximum 252kb(3 channels, 16bit, 2400dpi, full gl841 shading range)
- * instruction efficiency:
- the innermost loop runs long and consists of 3 adds, one compare,
- 2 derefences.
- */
-/*
- for (y = 0; y < lines; y++) {
- csp = ccd_shift_pitch;
- for (c = 0; c < component_count; c++) {
- srcp = src + c + *csp++;
- dstp = dst + c;
- for (x = 0; x < pitch; x += component_count) {
- *dstp = *srcp;
- srcp += component_count;
- dstp += component_count;
- }
- }
- dst += pitch;
- src += pitch;
- }
- */
-/*
- * cache efficency:
- here only line_dist_pitch needs to stay in cache. 12*4 = 48 bytes
- * instruction efficiency:
- we have a short running inner loop, consisting of 4 incs, 2 compare, 1 add,
- 2 dereference and 1 indexed dereference.
- the enclosing loop is long running, consisting of 1 add, 1 compare.
- */
- srcp = src;
- dstp = dst;
- for (y = 0; y < lines; y++) {
- for (x = 0; x < pitch; x += component_count) {
- csp = ccd_shift_pitch;
- for (c = 0; c < component_count && c + x < pitch; c++) {
- *dstp = srcp[*csp++];
- dstp++;
- srcp++;
- }
- }
- }
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-FUNC_NAME(genesys_shrink_lines) (
- uint8_t *src_data,
- uint8_t *dst_data,
- unsigned int lines,
- unsigned int src_pixels,
- unsigned int dst_pixels,
- unsigned int channels)
-{
- unsigned int dst_x, src_x, y, c, cnt;
- unsigned int avg[3];
- unsigned int count;
- COMPONENT_TYPE *src = (COMPONENT_TYPE *)src_data;
- COMPONENT_TYPE *dst = (COMPONENT_TYPE *)dst_data;
-
- if (src_pixels > dst_pixels) {
-/*average*/
- for (c = 0; c < channels; c++)
- avg[c] = 0;
- for(y = 0; y < lines; y++) {
- cnt = src_pixels / 2;
- src_x = 0;
- for (dst_x = 0; dst_x < dst_pixels; dst_x++) {
- count = 0;
- while (cnt < src_pixels && src_x < src_pixels) {
- cnt += dst_pixels;
-
- for (c = 0; c < channels; c++)
- avg[c] += *src++;
- src_x++;
- count++;
- }
- cnt -= src_pixels;
-
- for (c = 0; c < channels; c++) {
- *dst++ = avg[c] / count;
- avg[c] = 0;
- }
- }
- }
- } else {
-/*interpolate. copy pixels*/
- for(y = 0; y < lines; y++) {
- cnt = dst_pixels / 2;
- dst_x = 0;
- for (src_x = 0; src_x < src_pixels; src_x++) {
- for (c = 0; c < channels; c++)
- avg[c] = *src++;
- while ((cnt < dst_pixels || src_x + 1 == src_pixels) &&
- dst_x < dst_pixels) {
- cnt += src_pixels;
-
- for (c = 0; c < channels; c++)
- *dst++ = avg[c];
- dst_x++;
- }
- cnt -= dst_pixels;
- }
- }
- }
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_devices.cc b/backend/genesys_devices.cc
deleted file mode 100644
index b4ae5ca..0000000
--- a/backend/genesys_devices.cc
+++ /dev/null
@@ -1,5165 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2003 Oliver Rauch
- Copyright (C) 2003-2005 Henning Meier-Geinitz <henning@meier-geinitz.de>
- Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
- Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
- Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
- Copyright (C) 2007 Luke <iceyfor@gmail.com>
- Copyright (C) 2010 Jack McGill <jmcgill85258@yahoo.com>
- Copyright (C) 2010 Andrey Loginov <avloginov@gmail.com>,
- xerox travelscan device entry
- Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
- for Plustek Opticbook 3600 support
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-/* ------------------------------------------------------------------------ */
-/* Some setup DAC and CCD tables */
-/* ------------------------------------------------------------------------ */
-
-#include "genesys_low.h"
-
-StaticInit<std::vector<Genesys_Frontend>> s_frontends;
-
-void genesys_init_frontend_tables()
-{
- s_frontends.init();
-
- GenesysFrontendLayout wolfson_layout;
- wolfson_layout.offset_addr = { 0x20, 0x21, 0x22 };
- wolfson_layout.gain_addr = { 0x28, 0x29, 0x2a };
-
- Genesys_Frontend fe;
- fe.fe_id = DAC_WOLFSON_UMAX;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x03 },
- { 0x02, 0x05 },
- { 0x03, 0x11 },
- { 0x20, 0x80 },
- { 0x21, 0x80 },
- { 0x22, 0x80 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x02 },
- { 0x29, 0x02 },
- { 0x2a, 0x02 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_ST12;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x03 },
- { 0x02, 0x05 },
- { 0x03, 0x03 },
- { 0x20, 0xc8 },
- { 0x21, 0xc8 },
- { 0x22, 0xc8 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x04 },
- { 0x29, 0x04 },
- { 0x2a, 0x04 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_ST24;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x03 },
- { 0x02, 0x05 },
- { 0x03, 0x21 },
- { 0x20, 0xc8 },
- { 0x21, 0xc8 },
- { 0x22, 0xc8 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x06 },
- { 0x29, 0x06 },
- { 0x2a, 0x06 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_5345;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x03 },
- { 0x02, 0x05 },
- { 0x03, 0x12 },
- { 0x20, 0xb8 },
- { 0x21, 0xb8 },
- { 0x22, 0xb8 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x04 },
- { 0x29, 0x04 },
- { 0x2a, 0x04 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- // reg3=0x02 for 50-600 dpi, 0x32 (0x12 also works well) at 1200
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_HP2400;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x03 },
- { 0x02, 0x05 },
- { 0x03, 0x02 },
- { 0x20, 0xb4 },
- { 0x21, 0xb6 },
- { 0x22, 0xbc },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x06 },
- { 0x29, 0x09 },
- { 0x2a, 0x08 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_HP2300;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x03 },
- { 0x02, 0x04 },
- { 0x03, 0x02 },
- { 0x20, 0xbe },
- { 0x21, 0xbe },
- { 0x22, 0xbe },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x04 },
- { 0x29, 0x04 },
- { 0x2a, 0x04 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CANONLIDE35;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x3d },
- { 0x02, 0x08 },
- { 0x03, 0x00 },
- { 0x20, 0xe1 },
- { 0x21, 0xe1 },
- { 0x22, 0xe1 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x93 },
- { 0x29, 0x93 },
- { 0x2a, 0x93 },
- };
- fe.reg2 = {0x00, 0x19, 0x06};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_AD_XP200;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x58 },
- { 0x01, 0x80 },
- { 0x02, 0x00 },
- { 0x03, 0x00 },
- { 0x20, 0x09 },
- { 0x21, 0x09 },
- { 0x22, 0x09 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x09 },
- { 0x29, 0x09 },
- { 0x2a, 0x09 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_XP300;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x35 },
- { 0x02, 0x20 },
- { 0x03, 0x14 },
- { 0x20, 0xe1 },
- { 0x21, 0xe1 },
- { 0x22, 0xe1 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x93 },
- { 0x29, 0x93 },
- { 0x2a, 0x93 },
- };
- fe.reg2 = {0x07, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_HP3670;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x03 },
- { 0x02, 0x05 },
- { 0x03, 0x32 },
- { 0x20, 0xba },
- { 0x21, 0xb8 },
- { 0x22, 0xb8 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x06 },
- { 0x29, 0x05 },
- { 0x2a, 0x04 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_WOLFSON_DSM600;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x35 },
- { 0x02, 0x20 },
- { 0x03, 0x14 },
- { 0x20, 0x85 },
- { 0x21, 0x85 },
- { 0x22, 0x85 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0xa0 },
- { 0x29, 0xa0 },
- { 0x2a, 0xa0 },
- };
- fe.reg2 = {0x07, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CANONLIDE200;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x9d },
- { 0x01, 0x91 },
- { 0x02, 0x00 },
- { 0x03, 0x00 },
- { 0x20, 0x00 },
- { 0x21, 0x3f },
- { 0x22, 0x00 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x32 },
- { 0x29, 0x04 },
- { 0x2a, 0x00 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CANONLIDE700;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x9d },
- { 0x01, 0x9e },
- { 0x02, 0x00 },
- { 0x03, 0x00 },
- { 0x20, 0x00 },
- { 0x21, 0x3f },
- { 0x22, 0x00 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x2f },
- { 0x29, 0x04 },
- { 0x2a, 0x00 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_KVSS080;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x23 },
- { 0x02, 0x24 },
- { 0x03, 0x0f },
- { 0x20, 0x80 },
- { 0x21, 0x80 },
- { 0x22, 0x80 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x4b },
- { 0x29, 0x4b },
- { 0x2a, 0x4b },
- };
- fe.reg2 = {0x00,0x00,0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_G4050;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x23 },
- { 0x02, 0x24 },
- { 0x03, 0x1f },
- { 0x20, 0x45 },
- { 0x21, 0x45 },
- { 0x22, 0x45 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x4b },
- { 0x29, 0x4b },
- { 0x2a, 0x4b },
- };
- fe.reg2 = {0x00,0x00,0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CANONLIDE110;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x80 },
- { 0x01, 0x8a },
- { 0x02, 0x23 },
- { 0x03, 0x4c },
- { 0x20, 0x00 },
- { 0x21, 0x00 },
- { 0x22, 0x00 },
- { 0x24, 0x00 },
- { 0x25, 0xca },
- { 0x26, 0x94 },
- { 0x28, 0x00 },
- { 0x29, 0x00 },
- { 0x2a, 0x00 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
- /** @brief GL124 special case
- * for GL124 based scanners, this struct is "abused"
- * in fact the fields are map like below to AFE registers
- * (from Texas Instrument or alike ?)
- */
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CANONLIDE120;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x80 },
- { 0x01, 0xa3 },
- { 0x02, 0x2b },
- { 0x03, 0x4c },
- { 0x20, 0x00 },
- { 0x21, 0x00 },
- { 0x22, 0x00 },
- { 0x24, 0x00 }, // actual address 0x05
- { 0x25, 0xca }, // actual address 0x06
- { 0x26, 0x95 }, // actual address 0x07
- { 0x28, 0x00 },
- { 0x29, 0x00 },
- { 0x2a, 0x00 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_PLUSTEK_3600;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x70 },
- { 0x01, 0x80 },
- { 0x02, 0x00 },
- { 0x03, 0x00 },
- { 0x20, 0x00 },
- { 0x21, 0x00 },
- { 0x22, 0x00 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x3f },
- { 0x29, 0x3d },
- { 0x2a, 0x3d },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CS8400F;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x23 },
- { 0x02, 0x24 },
- { 0x03, 0x0f },
- { 0x20, 0x60 },
- { 0x21, 0x5c },
- { 0x22, 0x6c },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x8a },
- { 0x29, 0x9f },
- { 0x2a, 0xc2 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CS8600F;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x00 },
- { 0x01, 0x23 },
- { 0x02, 0x24 },
- { 0x03, 0x2f },
- { 0x20, 0x67 },
- { 0x21, 0x69 },
- { 0x22, 0x68 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0xdb },
- { 0x29, 0xda },
- { 0x2a, 0xd7 },
- };
- fe.reg2 = { 0x00, 0x00, 0x00 };
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_IMG101;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x78 },
- { 0x01, 0xf0 },
- { 0x02, 0x00 },
- { 0x03, 0x00 },
- { 0x20, 0x00 },
- { 0x21, 0x00 },
- { 0x22, 0x00 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x00 },
- { 0x29, 0x00 },
- { 0x2a, 0x00 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- fe = Genesys_Frontend();
- fe.fe_id = DAC_PLUSTEK3800;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x78 },
- { 0x01, 0xf0 },
- { 0x02, 0x00 },
- { 0x03, 0x00 },
- { 0x20, 0x00 },
- { 0x21, 0x00 },
- { 0x22, 0x00 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x00 },
- { 0x29, 0x00 },
- { 0x2a, 0x00 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-
-
- /* reg0: control 74 data, 70 no data
- * reg3: offset
- * reg6: gain
- * reg0 , reg3, reg6 */
- fe = Genesys_Frontend();
- fe.fe_id = DAC_CANONLIDE80;
- fe.layout = wolfson_layout;
- fe.regs = {
- { 0x00, 0x70 },
- { 0x01, 0x16 },
- { 0x02, 0x60 },
- { 0x03, 0x00 },
- { 0x20, 0x00 },
- { 0x21, 0x00 },
- { 0x22, 0x00 },
- { 0x24, 0x00 },
- { 0x25, 0x00 },
- { 0x26, 0x00 },
- { 0x28, 0x00 },
- { 0x29, 0x00 },
- { 0x2a, 0x00 },
- };
- fe.reg2 = {0x00, 0x00, 0x00};
- s_frontends->push_back(fe);
-}
-
-
-/** for setting up the sensor-specific settings:
- * Optical Resolution, number of black pixels, number of dummy pixels,
- * CCD_start_xoffset, and overall number of sensor pixels
- * registers 0x08-0x0b, 0x10-0x1d and 0x52-0x5e
- */
-StaticInit<std::vector<Genesys_Sensor>> s_sensors;
-
-void genesys_init_sensor_tables()
-{
- s_sensors.init();
-
- Genesys_Sensor sensor;
- sensor.sensor_id = CCD_UMAX;
- sensor.optical_res = 1200;
- sensor.black_pixels = 48;
- sensor.dummy_pixel = 64;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10800;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 230;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x01 },
- { 0x09, 0x03 },
- { 0x0a, 0x05 },
- { 0x0b, 0x07 },
- { 0x16, 0x33 },
- { 0x17, 0x05 },
- { 0x18, 0x31 },
- { 0x19, 0x2a },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x02 },
- { 0x52, 0x13 },
- { 0x53, 0x17 },
- { 0x54, 0x03 },
- { 0x55, 0x07 },
- { 0x56, 0x0b },
- { 0x57, 0x0f },
- { 0x58, 0x23 },
- { 0x59, 0x00 },
- { 0x5a, 0xc1 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_ST12;
- sensor.optical_res = 600;
- sensor.black_pixels = 48;
- sensor.dummy_pixel = 85;
- sensor.CCD_start_xoffset = 152;
- sensor.sensor_pixels = 5416;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 230;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x02 },
- { 0x09, 0x00 },
- { 0x0a, 0x06 },
- { 0x0b, 0x04 },
- { 0x16, 0x2b },
- { 0x17, 0x08 },
- { 0x18, 0x20 },
- { 0x19, 0x2a },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0x0c },
- { 0x1d, 0x03 },
- { 0x52, 0x0f },
- { 0x53, 0x13 },
- { 0x54, 0x17 },
- { 0x55, 0x03 },
- { 0x56, 0x07 },
- { 0x57, 0x0b },
- { 0x58, 0x83 },
- { 0x59, 0x00 },
- { 0x5a, 0xc1 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_ST24;
- sensor.optical_res = 1200;
- sensor.black_pixels = 48;
- sensor.dummy_pixel = 64;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10800;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 230;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x0e },
- { 0x09, 0x0c },
- { 0x0a, 0x00 },
- { 0x0b, 0x0c },
- { 0x16, 0x33 },
- { 0x17, 0x08 },
- { 0x18, 0x31 },
- { 0x19, 0x2a },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x02 },
- { 0x52, 0x17 },
- { 0x53, 0x03 },
- { 0x54, 0x07 },
- { 0x55, 0x0b },
- { 0x56, 0x0f },
- { 0x57, 0x13 },
- { 0x58, 0x03 },
- { 0x59, 0x00 },
- { 0x5a, 0xc1 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_5345;
- sensor.optical_res = 1200;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 48;
- sensor.dummy_pixel = 16;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10872;
- sensor.fau_gain_white_ref = 190;
- sensor.gain_white_ref = 190;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x0d },
- { 0x09, 0x0f },
- { 0x0a, 0x11 },
- { 0x0b, 0x13 },
- { 0x16, 0x0b },
- { 0x17, 0x0a },
- { 0x18, 0x30 },
- { 0x19, 0x2a },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x03 },
- { 0x52, 0x0f },
- { 0x53, 0x13 },
- { 0x54, 0x17 },
- { 0x55, 0x03 },
- { 0x56, 0x07 },
- { 0x57, 0x0b },
- { 0x58, 0x23 },
- { 0x59, 0x00 },
- { 0x5a, 0xc1 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {2.38, 2.35, 2.34};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_HP2400;
- sensor.optical_res = 1200,
- sensor.black_pixels = 48;
- sensor.dummy_pixel = 15;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10872;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x14 },
- { 0x09, 0x15 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0xbf },
- { 0x17, 0x08 },
- { 0x18, 0x3f },
- { 0x19, 0x2a },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x02 },
- { 0x52, 0x0b },
- { 0x53, 0x0f },
- { 0x54, 0x13 },
- { 0x55, 0x17 },
- { 0x56, 0x03 },
- { 0x57, 0x07 },
- { 0x58, 0x63 },
- { 0x59, 0x00 },
- { 0x5a, 0xc1 },
- { 0x5b, 0x00 },
- { 0x5c, 0x0e },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {2.1, 2.1, 2.1};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_HP2300;
- sensor.optical_res = 600;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 48;
- sensor.dummy_pixel = 20;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 5368;
- sensor.fau_gain_white_ref = 180;
- sensor.gain_white_ref = 180;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x16 },
- { 0x09, 0x00 },
- { 0x0a, 0x01 },
- { 0x0b, 0x03 },
- { 0x16, 0xb7 },
- { 0x17, 0x0a },
- { 0x18, 0x20 },
- { 0x19, 0x2a },
- { 0x1a, 0x6a },
- { 0x1b, 0x8a },
- { 0x1c, 0x00 },
- { 0x1d, 0x05 },
- { 0x52, 0x0f },
- { 0x53, 0x13 },
- { 0x54, 0x17 },
- { 0x55, 0x03 },
- { 0x56, 0x07 },
- { 0x57, 0x0b },
- { 0x58, 0x83 },
- { 0x59, 0x00 },
- { 0x5a, 0xc1 },
- { 0x5b, 0x06 },
- { 0x5c, 0x0b },
- { 0x5d, 0x10 },
- { 0x5e, 0x16 },
- };
- sensor.gamma = {2.1, 2.1, 2.1};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_CANONLIDE35;
- sensor.optical_res = 1200;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 87;
- sensor.dummy_pixel = 87;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10400;
- sensor.fau_gain_white_ref = 0;
- sensor.gain_white_ref = 0;
- sensor.exposure = { 0x0400, 0x0400, 0x0400 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x00 },
- { 0x17, 0x02 },
- { 0x18, 0x00 },
- { 0x19, 0x50 },
- { 0x1a, 0x00 }, // TODO: 1a-1d: these do no harm, but may be neccessery for CCD
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x02 },
- { 0x52, 0x05 }, // [GB](HI|LOW) not needed for cis
- { 0x53, 0x07 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x3a },
- { 0x59, 0x03 },
- { 0x5a, 0x40 },
- { 0x5b, 0x00 }, // TODO: 5b-5e
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_XP200;
- sensor.optical_res = 600;
- sensor.black_pixels = 5;
- sensor.dummy_pixel = 38;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 5200;
- sensor.fau_gain_white_ref = 200;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x1450, 0x0c80, 0x0a28 };
- sensor.custom_regs = {
- { 0x08, 0x16 },
- { 0x09, 0x00 },
- { 0x0a, 0x01 },
- { 0x0b, 0x03 },
- { 0x16, 0xb7 },
- { 0x17, 0x0a },
- { 0x18, 0x20 },
- { 0x19, 0x2a },
- { 0x1a, 0x6a },
- { 0x1b, 0x8a },
- { 0x1c, 0x00 },
- { 0x1d, 0x05 },
- { 0x52, 0x0f },
- { 0x53, 0x13 },
- { 0x54, 0x17 },
- { 0x55, 0x03 },
- { 0x56, 0x07 },
- { 0x57, 0x0b },
- { 0x58, 0x83 },
- { 0x59, 0x00 },
- { 0x5a, 0xc1 },
- { 0x5b, 0x06 },
- { 0x5c, 0x0b },
- { 0x5d, 0x10 },
- { 0x5e, 0x16 },
- };
- sensor.gamma = {2.1, 2.1, 2.1};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_HP3670;
- sensor.optical_res = 1200;
- sensor.black_pixels = 48;
- sensor.dummy_pixel = 16;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10872;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x0a },
- { 0x0a, 0x0b },
- { 0x0b, 0x0d },
- { 0x16, 0x33 },
- { 0x17, 0x07 },
- { 0x18, 0x20 },
- { 0x19, 0x2a },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0xc0 },
- { 0x1d, 0x43 },
- { 0x52, 0x0f },
- { 0x53, 0x13 },
- { 0x54, 0x17 },
- { 0x55, 0x03 },
- { 0x56, 0x07 },
- { 0x57, 0x0b },
- { 0x58, 0x83 },
- { 0x59, 0x00 },
- { 0x5a, 0x15 },
- { 0x5b, 0x05 },
- { 0x5c, 0x0a },
- { 0x5d, 0x0f },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_DP665;
- sensor.optical_res = 600;
- sensor.black_pixels = 27;
- sensor.dummy_pixel = 27;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 2496;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x1100, 0x1100, 0x1100 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x00 },
- { 0x17, 0x02 },
- { 0x18, 0x04 },
- { 0x19, 0x50 },
- { 0x1a, 0x10 },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x02 },
- { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
- { 0x53, 0x05 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x54 },
- { 0x59, 0x03 },
- { 0x5a, 0x00 },
- { 0x5b, 0x00 }, // TODO: 5b-5e
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x01 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_ROADWARRIOR;
- sensor.optical_res = 600;
- sensor.black_pixels = 27;
- sensor.dummy_pixel = 27;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 5200;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x1100, 0x1100, 0x1100 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x00 },
- { 0x17, 0x02 },
- { 0x18, 0x04 },
- { 0x19, 0x50 },
- { 0x1a, 0x10 },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x02 },
- { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
- { 0x53, 0x05 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x54 },
- { 0x59, 0x03 },
- { 0x5a, 0x00 },
- { 0x5b, 0x00 }, // TODO: 5b-5e
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x01 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_DSMOBILE600;
- sensor.optical_res = 600;
- sensor.black_pixels = 28;
- sensor.dummy_pixel = 28;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 5200;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x1544, 0x1544, 0x1544 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x00 },
- { 0x17, 0x02 },
- { 0x18, 0x04 },
- { 0x19, 0x50 },
- { 0x1a, 0x10 },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x02 },
- { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
- { 0x53, 0x05 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x54 },
- { 0x59, 0x03 },
- { 0x5a, 0x00 },
- { 0x5b, 0x00 }, // TODO: 5b-5e
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x01 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_XP300;
- sensor.optical_res = 600;
- sensor.black_pixels = 27;
- sensor.dummy_pixel = 27;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10240;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x1100, 0x1100, 0x1100 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x00 },
- { 0x17, 0x02 },
- { 0x18, 0x04 },
- { 0x19, 0x50 },
- { 0x1a, 0x10 },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x02 },
- { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
- { 0x53, 0x05 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x54 },
- { 0x59, 0x03 },
- { 0x5a, 0x00 },
- { 0x5b, 0x00 }, // TODO: 5b-5e
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x01 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_DP685;
- sensor.optical_res = 600;
- sensor.black_pixels = 27;
- sensor.dummy_pixel = 27;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 5020;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x1100, 0x1100, 0x1100 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x00 },
- { 0x17, 0x02 },
- { 0x18, 0x04 },
- { 0x19, 0x50 },
- { 0x1a, 0x10 },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x02 },
- { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis
- { 0x53, 0x05 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x54 },
- { 0x59, 0x03 },
- { 0x5a, 0x00 },
- { 0x5b, 0x00 }, // TODO: 5b-5e
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x01 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE200;
- sensor.optical_res = 4800;
- sensor.black_pixels = 87*4;
- sensor.dummy_pixel = 16*4;
- sensor.CCD_start_xoffset = 320*8;
- sensor.sensor_pixels = 5136*8;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x10 },
- { 0x17, 0x08 },
- { 0x18, 0x00 },
- { 0x19, 0xff },
- { 0x1a, 0x34 },
- { 0x1b, 0x00 },
- { 0x1c, 0x02 },
- { 0x1d, 0x04 },
- { 0x52, 0x03 },
- { 0x53, 0x07 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x2a },
- { 0x59, 0xe1 },
- { 0x5a, 0x55 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x41 },
- };
- sensor.gamma = {1.7, 1.7, 1.7};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE700;
- sensor.optical_res = 4800;
- sensor.black_pixels = 73*8; // black pixels 73 at 600 dpi
- sensor.dummy_pixel = 16*8;
- // 384 at 600 dpi
- sensor.CCD_start_xoffset = 384*8;
- // 8x5570 segments, 5187+1 for rounding
- sensor.sensor_pixels = 5188*8;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x10 },
- { 0x17, 0x08 },
- { 0x18, 0x00 },
- { 0x19, 0xff },
- { 0x1a, 0x34 },
- { 0x1b, 0x00 },
- { 0x1c, 0x02 },
- { 0x1d, 0x04 },
- { 0x52, 0x07 },
- { 0x53, 0x03 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x2a },
- { 0x59, 0xe1 },
- { 0x5a, 0x55 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x41 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE100;
- sensor.optical_res = 2400;
- sensor.black_pixels = 87*4, /* black pixels */
- sensor.dummy_pixel = 16*4;
- sensor.CCD_start_xoffset = 320*4;
- sensor.sensor_pixels = 5136*4;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x01c1, 0x0126, 0x00e5 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x10 },
- { 0x17, 0x08 },
- { 0x18, 0x00 },
- { 0x19, 0x50 },
- { 0x1a, 0x34 },
- { 0x1b, 0x00 },
- { 0x1c, 0x02 },
- { 0x1d, 0x04 },
- { 0x52, 0x03 },
- { 0x53, 0x07 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x2a },
- { 0x59, 0xe1 },
- { 0x5a, 0x55 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x41 },
- };
- sensor.gamma = {1.7, 1.7, 1.7};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_KVSS080;
- sensor.optical_res = 600;
- sensor.black_pixels = 38;
- sensor.dummy_pixel = 38;
- sensor.CCD_start_xoffset = 152;
- sensor.sensor_pixels = 5376;
- sensor.fau_gain_white_ref = 160;
- sensor.gain_white_ref = 160;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.exposure_lperiod = 8000;
- sensor.custom_regs = {
- { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },
- { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0xff },
- { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
- { 0x0c, 0x00 },
- { 0x70, 0x01 },
- { 0x71, 0x03 },
- { 0x9e, 0x00 },
- { 0xaa, 0x00 },
- { 0x16, 0x33 },
- { 0x17, 0x1c },
- { 0x18, 0x00 },
- { 0x19, 0x2a },
- { 0x1a, 0x2c },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x04 },
- { 0x52, 0x0c },
- { 0x53, 0x0f },
- { 0x54, 0x00 },
- { 0x55, 0x03 },
- { 0x56, 0x06 },
- { 0x57, 0x09 },
- { 0x58, 0x6b },
- { 0x59, 0x00 },
- { 0x5a, 0xc0 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_G4050;
- sensor.optical_res = 4800;
- sensor.black_pixels = 50*8;
- // 31 at 600 dpi dummy_pixels 58 at 1200
- sensor.dummy_pixel = 58;
- sensor.CCD_start_xoffset = 152;
- sensor.sensor_pixels = 5360*8;
- sensor.fau_gain_white_ref = 160;
- sensor.gain_white_ref = 160;
- sensor.exposure = { 0x2c09, 0x22b8, 0x10f0 };
- sensor.custom_regs = {};
- sensor.gamma = {1.0, 1.0, 1.0};
-
- {
- struct CustomSensorSettings {
- int min_resolution;
- int max_resolution;
- int exposure_lperiod;
- ScanMethod method;
- GenesysRegisterSettingSet extra_custom_regs;
- };
-
- CustomSensorSettings custom_settings[] = {
- { -1, 600, 8016, ScanMethod::FLATBED, {
- { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff },
- { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
- { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
- { 0x0c, 0x00 },
- { 0x70, 0x00 },
- { 0x71, 0x02 },
- { 0x9e, 0x00 },
- { 0xaa, 0x00 },
- { 0x16, 0x33 },
- { 0x17, 0x0c },
- { 0x18, 0x00 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x08 },
- { 0x52, 0x0b },
- { 0x53, 0x0e },
- { 0x54, 0x11 },
- { 0x55, 0x02 },
- { 0x56, 0x05 },
- { 0x57, 0x08 },
- { 0x58, 0x63 },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- }
- },
- { 1200, 1200, 56064, ScanMethod::FLATBED, {
- { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
- { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff },
- { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff },
- { 0x0c, 0x20 },
- { 0x70, 0x08 },
- { 0x71, 0x0c },
- { 0x9e, 0xc0 },
- { 0xaa, 0x05 },
- { 0x16, 0x3b },
- { 0x17, 0x0c },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x38 },
- { 0x1b, 0x10 },
- { 0x1c, 0x00 },
- { 0x1d, 0x08 },
- { 0x52, 0x02 },
- { 0x53, 0x05 },
- { 0x54, 0x08 },
- { 0x55, 0x0b },
- { 0x56, 0x0e },
- { 0x57, 0x11 },
- { 0x58, 0x1b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- }
- },
- { 2400, 2400, 56064, ScanMethod::FLATBED, {
- { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
- { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
- { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
- { 0x0c, 0x20 },
- { 0x70, 0x08 },
- { 0x71, 0x0a },
- { 0x9e, 0xc0 },
- { 0xaa, 0x05 },
- { 0x16, 0x3b },
- { 0x17, 0x0c },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x38 },
- { 0x1b, 0x10 },
- { 0x1c, 0xc0 },
- { 0x1d, 0x08 },
- { 0x52, 0x02 },
- { 0x53, 0x05 },
- { 0x54, 0x08 },
- { 0x55, 0x0b },
- { 0x56, 0x0e },
- { 0x57, 0x11 },
- { 0x58, 0x1b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- }
- },
- { 4800, 4800, 42752, ScanMethod::FLATBED, {
- { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff },
- { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
- { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },
- { 0x0c, 0x21 },
- { 0x70, 0x08 },
- { 0x71, 0x0a },
- { 0x9e, 0xc0 },
- { 0xaa, 0x07 },
- { 0x16, 0x3b },
- { 0x17, 0x0c },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x38 },
- { 0x1b, 0x10 },
- { 0x1c, 0xc1 },
- { 0x1d, 0x08 },
- { 0x52, 0x02 },
- { 0x53, 0x05 },
- { 0x54, 0x08 },
- { 0x55, 0x0b },
- { 0x56, 0x0e },
- { 0x57, 0x11 },
- { 0x58, 0x1b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- }
- },
- { -1, -1, 15624, ScanMethod::TRANSPARENCY, {
- { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f },
- { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
- { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff },
- { 0x0c, 0x00 },
- { 0x70, 0x00 },
- { 0x71, 0x02 },
- { 0x9e, 0x00 },
- { 0xaa, 0x00 },
- { 0x16, 0x33 },
- { 0x17, 0x4c },
- { 0x18, 0x01 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x08 },
- { 0x52, 0x0e },
- { 0x53, 0x11 },
- { 0x54, 0x02 },
- { 0x55, 0x05 },
- { 0x56, 0x08 },
- { 0x57, 0x0b },
- { 0x58, 0x6b },
- { 0x59, 0x00 },
- { 0x5a, 0xc0 },
- }
- }
- };
-
- auto base_custom_regs = sensor.custom_regs;
- for (const CustomSensorSettings& setting : custom_settings)
- {
- sensor.min_resolution = setting.min_resolution;
- sensor.max_resolution = setting.max_resolution;
- sensor.exposure_lperiod = setting.exposure_lperiod;
- sensor.method = setting.method;
- sensor.custom_regs = base_custom_regs;
- sensor.custom_regs.merge(setting.extra_custom_regs);
- s_sensors->push_back(sensor);
- }
- }
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_CS4400F;
- sensor.optical_res = 4800;
- sensor.ccd_size_divisor = 4;
- sensor.black_pixels = 50*8;
- // 31 at 600 dpi, 58 at 1200 dpi
- sensor.dummy_pixel = 20;
- sensor.CCD_start_xoffset = 152;
- // 5360 max at 600 dpi
- sensor.sensor_pixels = 5360*8;
- sensor.fau_gain_white_ref = 160;
- sensor.gain_white_ref = 160;
- sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 };
- sensor.exposure_lperiod = 11640;
- sensor.custom_regs = {
- { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 },
- { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 },
- { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 },
- { 0x0c, 0x00 },
- { 0x70, 0x00 },
- { 0x71, 0x02 },
- { 0x9e, 0x2d },
- { 0xaa, 0x00 },
- { 0x16, 0x13 },
- { 0x17, 0x0a },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x6b },
- { 0x52, 0x0a },
- { 0x53, 0x0d },
- { 0x54, 0x00 },
- { 0x55, 0x03 },
- { 0x56, 0x06 },
- { 0x57, 0x08 },
- { 0x58, 0x5b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_CS8400F;
- sensor.optical_res = 4800;
- sensor.black_pixels = 50*8;
- // 31 at 600 dpi, 58 at 1200 dpi
- sensor.dummy_pixel = 20;
- sensor.CCD_start_xoffset = 152;
- // 5360 max at 600 dpi
- sensor.sensor_pixels = 5360*8;
- sensor.fau_gain_white_ref = 160;
- sensor.gain_white_ref = 160;
- sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 };
- sensor.exposure_lperiod = 7200;
- sensor.custom_regs = {
- { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },
- { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },
- { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb },
- { 0x0c, 0x00 },
- { 0x70, 0x01 },
- { 0x71, 0x02 },
- { 0x9e, 0x00 },
- { 0xaa, 0x00 },
- { 0x16, 0x33 },
- { 0x17, 0x0c },
- { 0x18, 0x13 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x84 },
- { 0x52, 0x0d },
- { 0x53, 0x10 },
- { 0x54, 0x01 },
- { 0x55, 0x04 },
- { 0x56, 0x07 },
- { 0x57, 0x0a },
- { 0x58, 0x6b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_CS8600F;
- sensor.optical_res = 4800;
- sensor.ccd_size_divisor = 4;
- sensor.black_pixels = 31;
- sensor.dummy_pixel = 20;
- sensor.CCD_start_xoffset = 0; // not used at the moment
- // 11372 pixels at 1200 dpi
- sensor.sensor_pixels = 11372*4;
- sensor.fau_gain_white_ref = 160;
- sensor.gain_white_ref = 160;
- sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 };
- sensor.custom_regs = {};
- sensor.gamma = {1.0, 1.0, 1.0};
-
- {
- struct CustomSensorSettings {
- int min_resolution;
- int max_resolution;
- int exposure_lperiod;
- ScanMethod method;
- GenesysRegisterSettingSet extra_custom_regs;
- GenesysRegisterSettingSet custom_fe_regs;
- };
-
- CustomSensorSettings custom_settings[] = {
- { -1, 1200, 24000, ScanMethod::FLATBED, {
- { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 },
- { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 },
- { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
- { 0x0c, 0x00 },
- { 0x70, 0x00 },
- { 0x71, 0x02 },
- { 0x9e, 0x2d },
- { 0xaa, 0x00 },
- { 0x16, 0x13 },
- { 0x17, 0x0a },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x6b },
- { 0x52, 0x0c },
- { 0x53, 0x0f },
- { 0x54, 0x00 },
- { 0x55, 0x03 },
- { 0x56, 0x06 },
- { 0x57, 0x09 },
- { 0x58, 0x6b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- },
- {},
- },
- { -1, 1200, 24000, ScanMethod::TRANSPARENCY, {
- { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 },
- { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 },
- { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
- { 0x0c, 0x00 },
- { 0x70, 0x00 },
- { 0x71, 0x02 },
- { 0x9e, 0x2d },
- { 0xaa, 0x00 },
- { 0x16, 0x13 },
- { 0x17, 0x0a },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x6b },
- { 0x52, 0x0c },
- { 0x53, 0x0f },
- { 0x54, 0x00 },
- { 0x55, 0x03 },
- { 0x56, 0x06 },
- { 0x57, 0x09 },
- { 0x58, 0x6b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- },
- {},
- },
- { 2400, 2400, 24000, ScanMethod::TRANSPARENCY, {
- { 0x74, 0x03 }, { 0x75, 0xfe }, { 0x76, 0x00 },
- { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 },
- { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
- { 0x0c, 0x00 },
- { 0x70, 0x00 },
- { 0x71, 0x02 },
- { 0x9e, 0x2d },
- { 0xaa, 0x00 },
- { 0x16, 0x13 },
- { 0x17, 0x15 },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x01 },
- { 0x1d, 0x75 },
- { 0x52, 0x0c },
- { 0x53, 0x0f },
- { 0x54, 0x00 },
- { 0x55, 0x03 },
- { 0x56, 0x06 },
- { 0x57, 0x09 },
- { 0x58, 0x6b },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- },
- {},
- },
- { 4800, 4800, 24000, ScanMethod::TRANSPARENCY, {
- { 0x74, 0x03 }, { 0x75, 0xff }, { 0x76, 0xff },
- { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff },
- { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 },
- { 0x0c, 0x00 },
- { 0x70, 0x0a },
- { 0x71, 0x0c },
- { 0x72, 0x0c },
- { 0x73, 0x0e },
- { 0x9e, 0x2d },
- { 0xaa, 0x00 },
- { 0x16, 0x13 },
- { 0x17, 0x15 },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x61 },
- { 0x1d, 0x75 },
- { 0x52, 0x03 },
- { 0x53, 0x06 },
- { 0x54, 0x09 },
- { 0x55, 0x0c },
- { 0x56, 0x0f },
- { 0x57, 0x00 },
- { 0x58, 0x23 },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- },
- { { 0x03, 0x1f },
- },
- },
- };
-
- auto base_custom_regs = sensor.custom_regs;
- for (const CustomSensorSettings& setting : custom_settings)
- {
- sensor.min_resolution = setting.min_resolution;
- sensor.max_resolution = setting.max_resolution;
- sensor.method = setting.method;
- sensor.exposure_lperiod = setting.exposure_lperiod;
- sensor.custom_regs = base_custom_regs;
- sensor.custom_regs.merge(setting.extra_custom_regs);
- sensor.custom_fe_regs = setting.custom_fe_regs;
- s_sensors->push_back(sensor);
- }
- }
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_HP_N6310;
- sensor.optical_res = 2400;
- // sensor.ccd_size_divisor = 2; Possibly half CCD, needs checking
- sensor.black_pixels = 96;
- sensor.dummy_pixel = 26;
- sensor.CCD_start_xoffset = 128;
- sensor.sensor_pixels = 42720;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 230;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x10 },
- { 0x0a, 0x10 },
- { 0x0b, 0x0c },
- { 0x16, 0x33 },
- { 0x17, 0x0c },
- { 0x18, 0x02 },
- { 0x19, 0x2a },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x08 },
- { 0x52, 0x0b },
- { 0x53, 0x0e },
- { 0x54, 0x11 },
- { 0x55, 0x02 },
- { 0x56, 0x05 },
- { 0x57, 0x08 },
- { 0x58, 0x63 },
- { 0x59, 0x00 },
- { 0x5a, 0x40 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x06 },
- { 0x5e, 0x6f },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE110;
- sensor.optical_res = 2400;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 87;
- sensor.dummy_pixel = 16;
- sensor.CCD_start_xoffset = 303;
- sensor.sensor_pixels = 5168*4;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x10 },
- { 0x17, 0x04 },
- { 0x18, 0x00 },
- { 0x19, 0x01 },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x02 },
- { 0x1d, 0x01 },
- { 0x52, 0x00 },
- { 0x53, 0x02 },
- { 0x54, 0x04 },
- { 0x55, 0x06 },
- { 0x56, 0x04 },
- { 0x57, 0x04 },
- { 0x58, 0x04 },
- { 0x59, 0x04 },
- { 0x5a, 0x1a },
- { 0x5b, 0x00 },
- { 0x5c, 0xc0 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {2.1, 2.1, 2.1};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE120;
- sensor.optical_res = 2400;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 87;
- sensor.dummy_pixel = 16;
- sensor.CCD_start_xoffset = 303;
- // SEGCNT at 600 DPI by number of segments
- sensor.sensor_pixels = 5104*4;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x15 },
- { 0x17, 0x04 },
- { 0x18, 0x00 },
- { 0x19, 0x01 },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x02 },
- { 0x1d, 0x01 },
- { 0x52, 0x04 },
- { 0x53, 0x06 },
- { 0x54, 0x00 },
- { 0x55, 0x02 },
- { 0x56, 0x04 },
- { 0x57, 0x04 },
- { 0x58, 0x04 },
- { 0x59, 0x04 },
- { 0x5a, 0x3a },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x1f },
- };
- sensor.gamma = {2.1, 2.1, 2.1};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE210;
- sensor.optical_res = 2400;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 87;
- sensor.dummy_pixel = 16;
- sensor.CCD_start_xoffset = 303;
- sensor.sensor_pixels = 5168*4;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x10 },
- { 0x17, 0x04 },
- { 0x18, 0x00 },
- { 0x19, 0x01 },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x02 },
- { 0x1d, 0x01 },
- { 0x52, 0x00 },
- { 0x53, 0x02 },
- { 0x54, 0x04 },
- { 0x55, 0x06 },
- { 0x56, 0x04 },
- { 0x57, 0x04 },
- { 0x58, 0x04 },
- { 0x59, 0x04 },
- { 0x5a, 0x1a },
- { 0x5b, 0x00 },
- { 0x5c, 0xc0 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {2.1, 2.1, 2.1};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE220;
- sensor.optical_res = 2400;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 87;
- sensor.dummy_pixel = 16;
- sensor.CCD_start_xoffset = 303;
- sensor.sensor_pixels = 5168*4;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x10 },
- { 0x17, 0x04 },
- { 0x18, 0x00 },
- { 0x19, 0x01 },
- { 0x1a, 0x30 },
- { 0x1b, 0x00 },
- { 0x1c, 0x02 },
- { 0x1d, 0x01 },
- { 0x52, 0x00 },
- { 0x53, 0x02 },
- { 0x54, 0x04 },
- { 0x55, 0x06 },
- { 0x56, 0x04 },
- { 0x57, 0x04 },
- { 0x58, 0x04 },
- { 0x59, 0x04 },
- { 0x5a, 0x1a },
- { 0x5b, 0x00 },
- { 0x5c, 0xc0 },
- { 0x5d, 0x00 },
- { 0x5e, 0x00 },
- };
- sensor.gamma = {2.1, 2.1, 2.1};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_PLUSTEK_3600;
- sensor.optical_res = 1200;
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 87;
- sensor.dummy_pixel = 87;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10100;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 230;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x00 },
- { 0x16, 0x33 },
- { 0x17, 0x0b },
- { 0x18, 0x11 },
- { 0x19, 0x2a },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0xc4 },
- { 0x52, 0x07 }, // [GB](HI|LOW) not needed for cis
- { 0x53, 0x0a },
- { 0x54, 0x0c },
- { 0x55, 0x00 },
- { 0x56, 0x02 },
- { 0x57, 0x06 },
- { 0x58, 0x22 },
- { 0x59, 0x69 },
- { 0x5a, 0x40 },
- { 0x5b, 0x00 }, // TODO: 5b-5e
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x02 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_IMG101;
- sensor.optical_res = 1200;
- sensor.black_pixels = 31;
- sensor.dummy_pixel = 31;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10800;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x60 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x8b },
- { 0x16, 0xbb },
- { 0x17, 0x13 },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x34 },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x06 },
- { 0x52, 0x02 },
- { 0x53, 0x04 },
- { 0x54, 0x06 },
- { 0x55, 0x08 },
- { 0x56, 0x0a },
- { 0x57, 0x00 },
- { 0x58, 0x59 },
- { 0x59, 0x31 },
- { 0x5a, 0x40 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x1f },
- };
- sensor.gamma = {1.7, 1.7, 1.7};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CCD_PLUSTEK3800;
- sensor.optical_res = 1200;
- sensor.black_pixels = 31;
- sensor.dummy_pixel = 31;
- sensor.CCD_start_xoffset = 0;
- sensor.sensor_pixels = 10200;
- sensor.fau_gain_white_ref = 210;
- sensor.gain_white_ref = 200;
- sensor.exposure = { 0x0000, 0x0000, 0x0000 };
- sensor.custom_regs = {
- { 0x08, 0x60 },
- { 0x09, 0x00 },
- { 0x0a, 0x00 },
- { 0x0b, 0x8b },
- { 0x16, 0xbb },
- { 0x17, 0x13 },
- { 0x18, 0x10 },
- { 0x19, 0x2a },
- { 0x1a, 0x34 },
- { 0x1b, 0x00 },
- { 0x1c, 0x20 },
- { 0x1d, 0x06 },
- { 0x52, 0x02 },
- { 0x53, 0x04 },
- { 0x54, 0x06 },
- { 0x55, 0x08 },
- { 0x56, 0x0a },
- { 0x57, 0x00 },
- { 0x58, 0x59 },
- { 0x59, 0x31 },
- { 0x5a, 0x40 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x00 },
- { 0x5e, 0x1f },
- };
- sensor.gamma = {1.7, 1.7, 1.7};
- s_sensors->push_back(sensor);
-
-
- sensor = Genesys_Sensor();
- sensor.sensor_id = CIS_CANONLIDE80,
- sensor.optical_res = 1200; // real hardware limit is 2400
- sensor.ccd_size_divisor = 2;
- sensor.black_pixels = 20;
- sensor.dummy_pixel = 6;
- // tuned to give 3*8 multiple startx coordinate during shading calibration
- sensor.CCD_start_xoffset = 34; // 14=>3, 20=>2
- // 10400, too wide=>10288 in shading data 10240~
- // 10208 too short for shading, max shading data = 10240 pixels, endpix-startpix=10208
- sensor.sensor_pixels = 10240;
- sensor.fau_gain_white_ref = 150;
- sensor.gain_white_ref = 150;
- // maps to 0x70-0x73 for GL841
- sensor.exposure = { 0x1000, 0x1000, 0x0500 };
- sensor.custom_regs = {
- { 0x08, 0x00 },
- { 0x09, 0x05 },
- { 0x0a, 0x07 },
- { 0x0b, 0x09 },
- { 0x16, 0x00 },
- { 0x17, 0x01 },
- { 0x18, 0x00 },
- { 0x19, 0x06 },
- { 0x1a, 0x00 },
- { 0x1b, 0x00 },
- { 0x1c, 0x00 },
- { 0x1d, 0x04 },
- { 0x52, 0x03 },
- { 0x53, 0x07 },
- { 0x54, 0x00 },
- { 0x55, 0x00 },
- { 0x56, 0x00 },
- { 0x57, 0x00 },
- { 0x58, 0x29 },
- { 0x59, 0x69 },
- { 0x5a, 0x55 },
- { 0x5b, 0x00 },
- { 0x5c, 0x00 },
- { 0x5d, 0x20 },
- { 0x5e, 0x41 },
- };
- sensor.gamma = {1.0, 1.0, 1.0};
- s_sensors->push_back(sensor);
-}
-
-/** for General Purpose Output specific settings:
- * initial GPO value (registers 0x66-0x67/0x6c-0x6d)
- * GPO enable mask (registers 0x68-0x69/0x6e-0x6f)
- * The first register is for GPIO9-GPIO16, the second for GPIO1-GPIO8
- */
-static Genesys_Gpo Gpo[] = {
- /* UMAX */
- {GPO_UMAX,
- {0x11, 0x00}
- ,
- {0x51, 0x20}
- ,
- }
- ,
- /* Plustek OpticPro S12/ST12 */
- {GPO_ST12,
- {0x11, 0x00}
- ,
- {0x51, 0x20}
- ,
- }
- ,
- /* Plustek OpticPro S24/ST24 */
- {GPO_ST24,
- {0x00, 0x00}
- ,
- {0x51, 0x20}
- ,
- }
- ,
- /* MD5345/MD6471 */
- {GPO_5345,
- {0x30, 0x18}
- , /* bits 11-12 are for bipolar V-ref input voltage */
- {0xa0, 0x18}
- ,
- }
- ,
- /* HP2400C */
- {GPO_HP2400,
- {0x30, 0x00}
- ,
- {0x31, 0x00}
- ,
- }
- ,
- /* HP2300C */
- {GPO_HP2300,
- {0x00, 0x00}
- ,
- {0x00, 0x00}
- ,
- }
- ,
- /* CANONLIDE35 */
- {GPO_CANONLIDE35,
- {0x02, 0x80}
- ,
- {0xef, 0x80}
- ,
- }
- ,
- /* 7: XP200 */
- {GPO_XP200,
- {0x30, 0x00}
- ,
- {0xb0, 0x00}
- ,
- },
- /* HP3670 */
- {GPO_HP3670,
- {0x00, 0x00}
- ,
- {0x00, 0x00}
- }
- ,
- /* 8: XP300 */
- {GPO_XP300,
- {0x09, 0xc6},
- {0xbb, 0x00},
- }
- ,
- /* Syscan DP 665 */
- {
- GPO_DP665,
- {0x18, 0x00},/*0x19,0x00*/
- {0xbb, 0x00},
- }
- ,
- /* Syscan DP 685 */
- {
- GPO_DP685,
- {0x3f, 0x46}, /* 6c, 6d */
- {0xfb, 0x00}, /* 6e, 6f */
- },
- /* CANONLIDE200 */
- {GPO_CANONLIDE200,
- {0xfb, 0x20}, /* 0xfb when idle , 0xf9/0xe9 (1200) when scanning */
- {0xff, 0x00},
- },
- /* CANONLIDE700 */
- {GPO_CANONLIDE700,
- {0xdb, 0xff},
- {0xff, 0x80},
- },
- {GPO_KVSS080,
- {0xf5, 0x20},
- {0x7e, 0xa1},
- }
- ,
- {GPO_G4050,
- {0x20, 0x00},
- {0xfc, 0x00},
- }
- ,
- /* HP N6310 */
- {GPO_HP_N6310,
- {0xa3, 0x00},
- {0x7f, 0x00},
- }
- ,
- /* CANONLIDE110 */
- {GPO_CANONLIDE110,
- {0xfb, 0x20},
- {0xff, 0x00},
- }
- ,
- /* CANONLIDE120 */
- {GPO_CANONLIDE120,
- {0xfb, 0x20},
- {0xff, 0x00},
- }
- ,
- /* CANONLIDE210 */
- {GPO_CANONLIDE210,
- {0xfb, 0x20},
- {0xff, 0x00},
- }
- ,
- /* Plustek 3600 */
- {GPO_PLUSTEK_3600,
- {0x02, 0x00},
- {0x1e, 0x80},
- }
- /* CanoScan 4400f */
- ,
- {GPO_CS4400F,
- {0x01, 0x7f},
- {0xff, 0x00},
- }
- /* CanoScan 8400f */
- ,
- {GPO_CS8400F,
- {0x9a, 0xdf},
- {0xfe, 0x60},
- }
- /* CanoScan 8600F */
- ,
- { GPO_CS8600F,
- { 0x20, 0x7c },
- { 0xff, 0x00 },
- }
- /* Canon Image formula 101 */
- ,
- {GPO_IMG101,
- {0x41, 0xa4},
- {0x13, 0xa7}
- }
- /* Plustek OpticBook 3800 */
- ,
- {GPO_PLUSTEK3800,
- {0x41, 0xa4},
- {0x13, 0xa7}
- },
- /* Canon LiDE 80 */
- {
- GPO_CANONLIDE80,
- {0x28, 0x90},
- {0x75, 0x80},
- }
-};
-
-static Genesys_Motor Motor[] = {
- /* UMAX */
- {MOTOR_UMAX,
- 1200, /* motor base steps */
- 2400, /* maximum motor resolution */
- 1, /* maximum step mode */
- 1, /* number of power modes*/
- {{{
- 11000, /* maximum start speed */
- 3000, /* maximum end speed */
- 128, /* step count */
- 1.0, /* nonlinearity */
- },
- {
- 11000,
- 3000,
- 128,
- 1.0,
- },},},
- },
- {MOTOR_5345, /* MD5345/6228/6471 */
- 1200,
- 2400,
- 1,
- 1,
- {{{
- 2000,
- 1375,
- 128,
- 0.5,
- },
- {
- 2000,
- 1375,
- 128,
- 0.5,
- },},},
- },
- {MOTOR_ST24, /* ST24 */
- 2400,
- 2400,
- 1,
- 1,
- {{{
- 2289,
- 2100,
- 128,
- 0.3,
- },
- {
- 2289,
- 2100,
- 128,
- 0.3,
- },},},
- },
- {MOTOR_HP3670, /* HP 3670 */
- 1200,
- 2400,
- 1,
- 1,
- {{{
- 11000, /* start speed */
- 3000, /* max speed */
- 128, /* min steps */
- 0.25,
- },
- {
- 11000,
- 3000,
- 128,
- 0.5,
- },},},
- },
- {MOTOR_HP2400, /* HP 2400c */
- 1200,
- 1200,
- 1,
- 1,
- {{{
- 11000, /* start speed */
- 3000, /* max speed */
- 128, /* min steps */
- 0.25,
- },
- {
- 11000,
- 3000,
- 128,
- 0.5,
- },},},
- },
- {MOTOR_HP2300, /* HP 2300c */
- 600, /* 600/1200 */
- 1200,
- 1,
- 1,
- {{{
- 3200,
- 1200,
- 128,
- 0.5,
- },
- {
- 3200,
- 1200,
- 128,
- 0.5,
- },},},
- },
- {MOTOR_CANONLIDE35, /* Canon LiDE 35 */
- 1200,
- 2400,
- 1,
- 1,
- {{{ 3500, 1300, 60, 0.8, },
- { 3500, 1400, 60, 0.8, },},},
- },
- {MOTOR_XP200, /* Strobe XP200 */
- 600,
- 600,
- 1,
- 1,
- {{{
- 3500,
- 1300,
- 60,
- 0.25,
- },
- {
- 3500,
- 1400,
- 60,
- 0.5,
- },},},
- },
- {MOTOR_XP300, /* 7: Visioneer Strobe XP300 */
- 300,
- 600,
- 1,
- 1,
- {{{ /* works best with GPIO10, GPIO14 off */
- 3700,
- 3700,
- 2,
- 0.8,
- },
- {
- 11000,
- 11000,
- 2,
- 0.8,
- },},},
- },
- {MOTOR_DP665, /* Syscan DP 665 */
- 750,
- 1500,
- 1,
- 1,
- {{{
- 3000,
- 2500,
- 10,
- 0.8,
- },
- {
- 11000,
- 11000,
- 2,
- 0.8,
- },},},
- },
- {MOTOR_ROADWARRIOR, /* Visioneer Roadwarrior */
- 750,
- 1500,
- 1,
- 1,
- {{{
- 3000,
- 2600,
- 10,
- 0.8,
- },
- {
- 11000,
- 11000,
- 2,
- 0.8,
- },},},
- },
- {MOTOR_DSMOBILE_600, /* Pentax DSmobile 600 */
- 750,
- 1500,
- 2,
- 1,
- {{{
- 6666,
- 3700,
- 8,
- 0.8,
- },
- {
- 6666,
- 3700,
- 8,
- 0.8,
- },},},
- },
- {MOTOR_CANONLIDE100, /* Canon LiDE 100 */
- 1200,
- 6400,
- 2, /* maximum step type count */
- 1, /* maximum power modes count */
- { /* motor slopes */
- { /* power mode 0 */
- { 3000, 1000, 127, 0.50}, /* full step */
- { 3000, 1500, 127, 0.50}, /* half step */
- { 3*2712, 3*2712, 16, 0.80}, /* quarter step 0.75*2712 */
- },
- },
- },
- {MOTOR_CANONLIDE200, /* Canon LiDE 200 */
- 1200,
- 6400,
- 2,
- 1,
- { /* motor slopes */
- { /* power mode 0 */
- { 3000, 1000, 127, 0.50}, /* full step */
- { 3000, 1500, 127, 0.50}, /* half step */
- { 3*2712, 3*2712, 16, 0.80}, /* quarter step 0.75*2712 */
- },
- },
- },
- {MOTOR_CANONLIDE700, /* Canon LiDE 700 */
- 1200,
- 6400,
- 2,
- 1,
- { /* motor slopes */
- { /* power mode 0 */
- { 3000, 1000, 127, 0.50}, /* full step */
- { 3000, 1500, 127, 0.50}, /* half step */
- { 3*2712, 3*2712, 16, 0.80}, /* quarter step 0.75*2712 */
- },
- },
- },
- {MOTOR_KVSS080,
- 1200,
- 1200,
- 2,
- 1,
- { /* motor slopes */
- { /* power mode 0 */
- { 22222, 500, 246, 0.5 }, /* max speed / dpi * base dpi => exposure */
- { 22222, 500, 246, 0.5 },
- { 22222, 500, 246, 0.5 },
- },
- },
- },
- {MOTOR_G4050,
- 2400,
- 9600,
- 2,
- 1,
- { /* motor slopes */
- { /* power mode 0 */
- { 3961, 240, 246, 0.8 }, /* full step */
- { 3961, 240, 246, 0.8 }, /* half step */
- { 3961, 240, 246, 0.8 }, /* quarter step */
- },
- },
- },
- {MOTOR_CS8400F,
- 2400,
- 9600,
- 2,
- 1,
- { /* motor slopes */
- { /* power mode 0 */
- { 3961, 240, 246, 0.8 }, /* full step */
- { 3961, 240, 246, 0.8 }, /* half step */
- { 3961, 240, 246, 0.8 }, /* quarter step */
- },
- },
- },
- {
- MOTOR_CS8600F,
- 2400,
- 9600,
- 2,
- 1,
- { /* motor slopes */
- { /* power mode 0 */
- { 3961, 240, 246, 0.8 }, /* full step */
- { 3961, 240, 246, 0.8 }, /* half step */
- { 3961, 240, 246, 0.8 }, /* quarter step */
- },
- },
- },
- {MOTOR_CANONLIDE110, /* Canon LiDE 110 */
- 4800,
- 9600,
- 1, /* maximum step type count */
- 1, /* maximum power modes count */
- { /* motor slopes */
- { /* power mode 0 */
- { 3000, 1000, 256, 0.50}, /* full step */
- },
- },
- },
- {MOTOR_CANONLIDE120, /* Canon LiDE 120 */
- 4800,
- 9600,
- 1, /* maximum step type count */
- 1, /* maximum power modes count */
- { /* motor slopes */
- { /* power mode 0 */
- { 3000, 1000, 256, 0.50}, /* full step */
- },
- },
- },
- {MOTOR_CANONLIDE210, /* Canon LiDE 210 */
- 4800,
- 9600,
- 1, /* maximum step type count */
- 1, /* maximum power modes count */
- { /* motor slopes */
- { /* power mode 0 */
- { 3000, 1000, 256, 0.50}, /* full step */
- },
- },
- },
- {MOTOR_PLUSTEK_3600, /* PLUSTEK 3600 */
- 1200,
- 2400,
- 1,
- 1,
- {
- {
- { 3500, 1300, 60, 0.8 },
- { 3500, 3250, 60, 0.8 },
- },
- },},
- {MOTOR_IMG101, /* Canon Image Formula 101 */
- 600,
- 1200,
- 1,
- 1,
- {
- {
- { 3500, 1300, 60, 0.8 },
- { 3500, 3250, 60, 0.8 },
- },
- },},
- {MOTOR_PLUSTEK3800, /* Plustek OpticBook 3800 */
- 600,
- 1200,
- 1,
- 1,
- {
- {
- { 3500, 1300, 60, 0.8 },
- { 3500, 3250, 60, 0.8 },
- },
- },},
- {MOTOR_CANONLIDE80,
- 2400, /* 2400 ???? */
- 4800, /* 9600 ???? */
- 1, /* max step type */
- 1, /* power mode count */
- {
- { /* start speed, max end speed, step number */
- /* maximum speed (second field) is used to compute exposure as seen by motor */
- /* exposure=max speed/ slope dpi * base dpi */
- /* 5144 = max pixels at 600 dpi */
- /* 1288=(5144+8)*ydpi(=300)/base_dpi(=1200) , where 5152 is exposure */
- /* 6440=9660/(1932/1288) */
- { 9560, 1912, 31, 0.8 },
- },
- },},
-};
-
-/* here we have the various device settings...
- */
-static Genesys_Model umax_astra_4500_model = {
- "umax-astra-4500", /* Name */
- "UMAX", /* Device vendor string */
- "Astra 4500", /* Device model name */
- MODEL_UMAX_ASTRA_4500,
- GENESYS_GL646,
- NULL,
-
- {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (3.5), /* Start of scan area in mm (x) */
- SANE_FIX (7.5), /* Start of scan area in mm (y) */
- SANE_FIX (218.0), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (1.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_UMAX,
- DAC_WOLFSON_UMAX,
- GPO_UMAX,
- MOTOR_UMAX,
- GENESYS_FLAG_UNTESTED, /* Which flags are needed for this scanner? */
- /* untested, values set by hmg */
- GENESYS_HAS_NO_BUTTONS, /* no buttons supported */
- 20,
- 0, // shading_ta_lines
- 200
-};
-
-static Genesys_Model canon_lide_50_model = {
- "canon-lide-50", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 35/40/50", /* Device model name */
- MODEL_CANON_LIDE_50,
- GENESYS_GL841,
- NULL,
-
- { 1200, 600, 400, 300, 240, 200, 150, 75, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 400, 300, 240, 200, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.42), /* Start of scan area in mm (x) */
- SANE_FIX (7.9), /* Start of scan area in mm (y) */
- SANE_FIX (218.0), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (6.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_CANONLIDE35,
- DAC_CANONLIDE35,
- GPO_CANONLIDE35,
- MOTOR_CANONLIDE35,
- GENESYS_FLAG_LAZY_INIT | /* Which flags are needed for this scanner? */
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_DARK_WHITE_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW |
- GENESYS_HAS_FILE_SW |
- GENESYS_HAS_EMAIL_SW |
- GENESYS_HAS_COPY_SW,
- 280,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model panasonic_kvss080_model = {
- "panasonic-kv-ss080", /* Name */
- "Panasonic", /* Device vendor string */
- "KV-SS080", /* Device model name */
- MODEL_PANASONIC_KV_SS080,
- GENESYS_GL843,
- NULL,
-
- { 600, /* 500, 400,*/ 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
- { 1200, 600, /* 500, 400, */ 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (7.2), /* Start of scan area in mm (x) */
- SANE_FIX (14.7), /* Start of scan area in mm (y) */
- SANE_FIX (217.7), /* Size of scan area in mm (x) */
- SANE_FIX (300.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (9.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_KVSS080,
- DAC_KVSS080,
- GPO_KVSS080,
- MOTOR_KVSS080,
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW ,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-static Genesys_Model hp4850c_model = {
- "hewlett-packard-scanjet-4850c", /* Name */
- "Hewlett Packard", /* Device vendor string */
- "ScanJet 4850C", /* Device model name */
- MODEL_HP_SCANJET_4850C,
- GENESYS_GL843,
- NULL,
-
- {2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- {2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (7.9), /* Start of scan area in mm (x) */
- SANE_FIX (5.9), /* Start of scan area in mm (y) */
- SANE_FIX (219.6), /* Size of scan area in mm (x) */
- SANE_FIX (314.5), /* Size of scan area in mm (y) */
-
- SANE_FIX (3.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in line number */
- /* 0 38 76 OK 1200/2400 */
- /* 0 24 48 OK [100,600] dpi */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_G4050,
- DAC_G4050,
- GPO_G4050,
- MOTOR_G4050,
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_STAGGERED_LINE |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_DARK_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-static Genesys_Model hpg4010_model = {
- "hewlett-packard-scanjet-g4010", /* Name */
- "Hewlett Packard", /* Device vendor string */
- "ScanJet G4010", /* Device model name */
- MODEL_HP_SCANJET_G4010,
- GENESYS_GL843,
- NULL,
-
- { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (8.0), /* Start of scan area in mm (x) */
- SANE_FIX (13.00), /* Start of scan area in mm (y) */
- SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
- SANE_FIX (315.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (3.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in line number */
- /* 0 38 76 OK 1200/2400 */
- /* 0 24 48 OK [100,600] dpi */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_G4050,
- DAC_G4050,
- GPO_G4050,
- MOTOR_G4050,
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_STAGGERED_LINE |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_DARK_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-static Genesys_Model hpg4050_model = {
- "hewlett-packard-scanjet-g4050", /* Name */
- "Hewlett Packard", /* Device vendor string */
- "ScanJet G4050", /* Device model name */
- MODEL_HP_SCANJET_G4050,
- GENESYS_GL843,
- NULL,
-
- { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- { 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (8.0), /* Start of scan area in mm (x) */
- SANE_FIX (13.00), /* Start of scan area in mm (y) */
- SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
- SANE_FIX (315.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (3.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (8.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (13.00), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (217.9), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (250.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (40.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in line number */
- /* 0 38 76 OK 1200/2400 */
- /* 0 24 48 OK [100,600] dpi */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_G4050,
- DAC_G4050,
- GPO_G4050,
- MOTOR_G4050,
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_STAGGERED_LINE |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_DARK_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-
-static Genesys_Model canon_4400f_model = {
- "canon-canoscan-4400f", /* Name */
- "Canon", /* Device vendor string */
- "Canoscan 4400f", /* Device model name */
- MODEL_CANON_CANOSCAN_4400F,
- GENESYS_GL843,
- NULL,
-
- { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (6.0), /* Start of scan area in mm (x) */
- SANE_FIX (13.00), /* Start of scan area in mm (y) */
- SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
- SANE_FIX (315.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (3.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (8.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (13.00), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (217.9), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (250.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (40.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in line number */
- /* 0 38 76 OK 1200/2400 */
- /* 0 24 48 OK [100,600] dpi */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_CS4400F,
- DAC_G4050,
- GPO_CS4400F,
- MOTOR_G4050,
- GENESYS_FLAG_NO_CALIBRATION |
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_STAGGERED_LINE |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_DARK_CALIBRATION |
- GENESYS_FLAG_FULL_HWDPI_MODE |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-
-static Genesys_Model canon_8400f_model = {
- "canon-canoscan-8400f", /* Name */
- "Canon", /* Device vendor string */
- "Canoscan 8400f", /* Device model name */
- MODEL_CANON_CANOSCAN_8400F,
- GENESYS_GL843,
- NULL,
-
- { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 0},
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (4.0), /* Start of scan area in mm (x) */
- SANE_FIX (13.00), /* Start of scan area in mm (y) */
- SANE_FIX (217.9), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
- SANE_FIX (315.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (3.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (8.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (13.00), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (217.9), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (250.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (40.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in line number */
- /* 0 38 76 OK 1200/2400 */
- /* 0 24 48 OK [100,600] dpi */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_CS8400F,
- DAC_CS8400F,
- GPO_CS8400F,
- MOTOR_CS8400F,
- GENESYS_FLAG_NO_CALIBRATION |
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_STAGGERED_LINE |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_DARK_CALIBRATION |
- GENESYS_FLAG_FULL_HWDPI_MODE |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-
-static Genesys_Model canon_8600f_model = {
- "canon-canoscan-8600f", // name
- "Canon", // Device vendor string
- "Canoscan 8600f", // Device model name
- MODEL_CANON_CANOSCAN_8600F,
- GENESYS_GL843, // ASIC type
- NULL,
-
- { 4800, 2400, 1200, 600, 400, 300, 0}, // TODO: resolutions for non-XPA mode
- { 4800, 2400, 1200, 600, 400, 300, 0}, // TODO: resolutions for non-XPA mode
- { 16, 8, 0 }, // possible depths in gray mode
- { 16, 8, 0 }, // possible depths in color mode
-
- SANE_FIX(24.0), // Start of scan area in mm (x)
- SANE_FIX(10.0), // Start of scan area in mm (y)
- SANE_FIX(216.0), // Size of scan area in mm (x)
- SANE_FIX(297.0), // Size of scan area in mm (y)
-
- SANE_FIX(0.0), // Start of white strip in mm (y)
- SANE_FIX(8.0), // Start of black mark in mm (x)
-
- SANE_FIX(95.0), // x_offset_ta
- SANE_FIX(26.0), // y_offset_ta
- SANE_FIX(70.0), // x_size_ta
- SANE_FIX(230.0), // y_size_ta
-
- SANE_FIX(12.5), // y_offset_calib
-
- SANE_FIX(0.0), // Size of scan area after paper sensor stops
- // sensing document in mm
- SANE_FIX(0.0), // Amount of feeding needed to eject document
- // after finishing scanning in mm
-
- 0, 48, 96, // RGB CCD Line-distance correction in line number
-
- COLOR_ORDER_RGB, // Order of the CCD/CIS colors
-
- SANE_FALSE, // Is this a CIS scanner?
- SANE_FALSE, // Is this a sheetfed scanner?
- CCD_CS8600F,
- DAC_CS8600F,
- GPO_CS8600F,
- MOTOR_CS8600F,
- GENESYS_FLAG_HAS_UTA |
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_STAGGERED_LINE |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_DARK_CALIBRATION |
- GENESYS_FLAG_FULL_HWDPI_MODE |
- GENESYS_FLAG_CUSTOM_GAMMA |
- GENESYS_FLAG_SHADING_REPARK,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW,
- 50, // shading_lines
- 50, // shading_ta_lines
- 100
-};
-
-
-static Genesys_Model canon_lide_100_model = {
- "canon-lide-100", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 100", /* Device model name */
- MODEL_CANON_LIDE_100,
- GENESYS_GL847,
- NULL,
-
- {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
- {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (1.1), /* Start of scan area in mm (x) */
- SANE_FIX (8.3), /* Start of scan area in mm (y) */
- SANE_FIX (216.07), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (1.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE100,
- DAC_CANONLIDE200,
- GPO_CANONLIDE200,
- MOTOR_CANONLIDE100,
- /* Which flags are needed for this scanner? */
- GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_SIS_SENSOR
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_SHADING_REPARK
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
- 50,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model canon_lide_110_model = {
- "canon-lide-110", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 110", /* Device model name */
- MODEL_CANON_LIDE_110,
- GENESYS_GL124,
- NULL,
-
- {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible x-resolutions */
- {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (2.2), /* Start of scan area in mm (x) */
- SANE_FIX (9.0), /* Start of scan area in mm (y) */
- SANE_FIX (216.70), /* Size of scan area in mm (x) */
- SANE_FIX (300.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (1.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE110,
- DAC_CANONLIDE110,
- GPO_CANONLIDE110,
- MOTOR_CANONLIDE110,
- GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_SHADING_REPARK
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
- 50,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model canon_lide_120_model = {
- "canon-lide-120", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 120", /* Device model name */
- MODEL_CANON_LIDE_120,
- GENESYS_GL124,
- NULL,
-
- {4800, 2400, 1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
- {4800, 2400, 1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (8.0), /* Start of scan area in mm (y) */
- SANE_FIX (216.0), /* Size of scan area in mm (x) */
- SANE_FIX (300.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (1.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE120,
- DAC_CANONLIDE120,
- GPO_CANONLIDE120,
- MOTOR_CANONLIDE120,
- GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_SHADING_REPARK
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
- 50,
- 0, // shading_ta_lines
- 400
-};
-
-
-static Genesys_Model canon_lide_210_model = {
- "canon-lide-210", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 210", /* Device model name */
- MODEL_CANON_LIDE_210,
- GENESYS_GL124,
- NULL,
-
- {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible x-resolutions */
- {4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (2.2), /* Start of scan area in mm (x) */
- SANE_FIX (8.7), /* Start of scan area in mm (y) */
- SANE_FIX (216.70), /* Size of scan area in mm (x) */
- SANE_FIX (297.5), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE210,
- DAC_CANONLIDE110,
- GPO_CANONLIDE210,
- MOTOR_CANONLIDE210,
- GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_SHADING_REPARK
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EXTRA_SW,
- 60,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model canon_lide_220_model = {
- "canon-lide-220", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 220", /* Device model name */
- MODEL_CANON_LIDE_220,
- GENESYS_GL124, /* or a compatible one */
- NULL,
-
- {4800, 2400, 1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
- {4800, 2400, 1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (2.2), /* Start of scan area in mm (x) */
- SANE_FIX (8.7), /* Start of scan area in mm (y) */
- SANE_FIX (216.70), /* Size of scan area in mm (x) */
- SANE_FIX (297.5), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE220,
- DAC_CANONLIDE110,
- GPO_CANONLIDE210,
- MOTOR_CANONLIDE210,
- GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_SHADING_REPARK
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EXTRA_SW,
- 60,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model canon_5600f_model = {
- "canon-5600f", /* Name */
- "Canon", /* Device vendor string */
- "5600F", /* Device model name */
- MODEL_CANON_CANOSCAN_5600F,
- GENESYS_GL847,
- NULL,
-
- {1200, 600, 400, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
- {1200, 600, 400, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (1.1), /* Start of scan area in mm (x) */
- SANE_FIX (8.3), /* Start of scan area in mm (y) */
- SANE_FIX (216.07), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (3.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE200,
- DAC_CANONLIDE200,
- GPO_CANONLIDE200,
- MOTOR_CANONLIDE200,
- GENESYS_FLAG_UNTESTED /* not working yet */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_SIS_SENSOR
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
- 50,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model canon_lide_700f_model = {
- "canon-lide-700f", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 700F", /* Device model name */
- MODEL_CANON_LIDE_700F,
- GENESYS_GL847,
- NULL,
-
- {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
- {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (3.1), /* Start of scan area in mm (x) */
- SANE_FIX (8.1), /* Start of scan area in mm (y) */
- SANE_FIX (216.07), /* Size of scan area in mm (x) */
- SANE_FIX (297.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (1.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE700,
- DAC_CANONLIDE700,
- GPO_CANONLIDE700,
- MOTOR_CANONLIDE700,
- GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_SIS_SENSOR
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_SHADING_REPARK
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
- 70,
- 0, // shading_ta_lines
- 400
-};
-
-
-
-static Genesys_Model canon_lide_200_model = {
- "canon-lide-200", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 200", /* Device model name */
- MODEL_CANON_LIDE_200,
- GENESYS_GL847,
- NULL,
-
- {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
- {4800, 2400, 1200, 600, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (1.1), /* Start of scan area in mm (x) */
- SANE_FIX (8.3), /* Start of scan area in mm (y) */
- SANE_FIX (216.07), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE200,
- DAC_CANONLIDE200,
- GPO_CANONLIDE200,
- MOTOR_CANONLIDE200,
- GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_SIS_SENSOR
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_SHADING_REPARK
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW,
- 50,
- 0, // shading_ta_lines
- 400
-};
-
-
-static Genesys_Model canon_lide_60_model = {
- "canon-lide-60", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 60", /* Device model name */
- MODEL_CANON_LIDE_60,
- GENESYS_GL841,
- NULL,
-
- {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.42), /* Start of scan area in mm (x) */
- SANE_FIX (7.9), /* Start of scan area in mm (y) */
- SANE_FIX (218.0), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (6.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_CANONLIDE35,
- DAC_CANONLIDE35,
- GPO_CANONLIDE35,
- MOTOR_CANONLIDE35,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_WHITE_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
-
- GENESYS_HAS_COPY_SW /* Has four buttons: COPY, SCAN, PDF, EMAIL */
- | GENESYS_HAS_SCAN_SW
- | GENESYS_HAS_FILE_SW
- | GENESYS_HAS_EMAIL_SW,
- 300,
- 0, // shading_ta_lines
- 400
-}; /* this is completely untested -- hmg */
-
-static Genesys_Model canon_lide_80_model = {
- "canon-lide-80", /* Name */
- "Canon", /* Device vendor string */
- "LiDE 80", /* Device model name */
- MODEL_CANON_LIDE_80,
- GENESYS_GL841,
- NULL,
-
- { 1200, 600, 400, 300, 240, 150, 100, 75, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 400, 300, 240, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
- SANE_FIX (0.42), /* Start of scan area in mm (x) 0.42 */
- SANE_FIX (7.90), /* Start of scan area in mm (y) 7.90 */
- SANE_FIX (216.07), /* Size of scan area in mm (x) 218.00 */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (4.5), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CIS_CANONLIDE80,
- DAC_CANONLIDE80,
- GPO_CANONLIDE80,
- MOTOR_CANONLIDE80,
- GENESYS_FLAG_LAZY_INIT | /* Which flags are needed for this scanner? */
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_DARK_WHITE_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW |
- GENESYS_HAS_FILE_SW |
- GENESYS_HAS_EMAIL_SW |
- GENESYS_HAS_COPY_SW,
- 160, /* 280 @2400 */
- 0, // shading_ta_lines
- 400
-};
-
-
-static Genesys_Model hp2300c_model = {
- "hewlett-packard-scanjet-2300c", /* Name */
- "Hewlett Packard", /* Device vendor string */
- "ScanJet 2300c", /* Device model name */
- MODEL_HP_SCANJET_2300C,
- GENESYS_GL646,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions, motor can go up to 1200 dpi */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (2.0), /* Start of scan area in mm (x_offset) */
- SANE_FIX (7.5), /* Start of scan area in mm (y_offset) */
- SANE_FIX (215.9), /* Size of scan area in mm (x) */
- SANE_FIX (295.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (1.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 16, 8, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_HP2300,
- DAC_WOLFSON_HP2300,
- GPO_HP2300,
- MOTOR_HP2300,
- GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_SEARCH_START
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW,
- 40,
- 0, // shading_ta_lines
- 132
-};
-
-static
-Genesys_Model hp2400c_model = {
- "hewlett-packard-scanjet-2400c", /* Name */
- "Hewlett Packard", /* Device vendor string */
- "ScanJet 2400c", /* Device model name */
- MODEL_HP_SCANJET_2400C,
- GENESYS_GL646,
- NULL,
-
- {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (6.5), /* Start of scan area in mm (x) */
- SANE_FIX (2.5), /* Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (297.2), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (1.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_HP2400,
- DAC_WOLFSON_HP2400,
- GPO_HP2400,
- MOTOR_HP2400,
- GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_STAGGERED_LINE
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW,
- 20,
- 0, // shading_ta_lines
- 132
-};
-
-static
-Genesys_Model visioneer_xp200_model = {
- "visioneer-strobe-xp200", /* Name */
- "Visioneer", /* Device vendor string */
- "Strobe XP200", /* Device model name */
- MODEL_VISIONEER_STROBE_XP200,
- GENESYS_GL646,
- NULL,
-
- {600, 300, 200, 100, 75, 0}, /* possible x-resolutions */
- {600, 300, 200, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.5), /* Start of scan area in mm (x) */
- SANE_FIX (16.0), /* Start of scan area in mm (y) */
- SANE_FIX (215.9), /* Size of scan area in mm (x) */
- SANE_FIX (297.2), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CIS_XP200,
- DAC_AD_XP200, /* Analog Device frontend */
- GPO_XP200,
- MOTOR_XP200,
- GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 120,
- 0, // shading_ta_lines
- 132
-};
-
-static Genesys_Model hp3670c_model = {
- "hewlett-packard-scanjet-3670c", /* Name */
- "Hewlett Packard", /* Device vendor string */
- "ScanJet 3670c", /* Device model name */
- MODEL_HP_SCANJET_3670C,
- GENESYS_GL646,
- NULL,
-
- {1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (8.5), /* Start of scan area in mm (x) */
- SANE_FIX (11.0), /* Start of scan area in mm (y) */
- SANE_FIX (215.9), /* Size of scan area in mm (x) */
- SANE_FIX (300.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (1.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (104.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (55.6), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (25.6), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (78.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (76.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_HP3670,
- DAC_WOLFSON_HP3670,
- GPO_HP3670,
- MOTOR_HP3670,
- GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_XPA
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_STAGGERED_LINE
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW,
- 20,
- 0, // shading_ta_lines
- 200
-};
-
-static Genesys_Model plustek_st12_model = {
- "plustek-opticpro-st12", /* Name */
- "Plustek", /* Device vendor string */
- "OpticPro ST12", /* Device model name */
- MODEL_PLUSTEK_OPTICPRO_ST12,
- GENESYS_GL646,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (3.5), /* Start of scan area in mm (x) */
- SANE_FIX (7.5), /* Start of scan area in mm (y) */
- SANE_FIX (218.0), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (1.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_ST12,
- DAC_WOLFSON_ST12,
- GPO_ST12,
- MOTOR_UMAX,
- GENESYS_FLAG_UNTESTED | GENESYS_FLAG_14BIT_GAMMA, /* Which flags are needed for this scanner? */
- GENESYS_HAS_NO_BUTTONS, /* no buttons supported */
- 20,
- 0, // shading_ta_lines
- 200
-};
-
-static Genesys_Model plustek_st24_model = {
- "plustek-opticpro-st24", /* Name */
- "Plustek", /* Device vendor string */
- "OpticPro ST24", /* Device model name */
- MODEL_PLUSTEK_OPTICPRO_ST24,
- GENESYS_GL646,
- NULL,
-
- {1200, 600, 300, 150, 75, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (3.5), /* Start of scan area in mm (x) */
- SANE_FIX (7.5), /* Start of scan area in mm (y) */
- SANE_FIX (218.0), /* Size of scan area in mm (x) */
- SANE_FIX (299.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (1.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 8, 16, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_ST24,
- DAC_WOLFSON_ST24,
- GPO_ST24,
- MOTOR_ST24,
- GENESYS_FLAG_UNTESTED
- | GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_SEARCH_START
- | GENESYS_FLAG_OFFSET_CALIBRATION,
- GENESYS_HAS_NO_BUTTONS, /* no buttons supported */
- 20,
- 0, // shading_ta_lines
- 200
-};
-
-static Genesys_Model medion_md5345_model = {
- "medion-md5345-model", /* Name */
- "Medion", /* Device vendor string */
- "MD5345/MD6228/MD6471", /* Device model name */
- MODEL_MEDION_MD5345,
- GENESYS_GL646,
- NULL,
-
- {1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX ( 0.30), /* Start of scan area in mm (x) */
- SANE_FIX ( 0.80), /* 2.79 < Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (296.4), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.00), /* Start of white strip in mm (y) */
- SANE_FIX (0.00), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.00), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.00), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (0.00), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (0.00), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.00), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_5345,
- DAC_WOLFSON_5345,
- GPO_5345,
- MOTOR_5345,
- GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_SEARCH_START
- | GENESYS_FLAG_STAGGERED_LINE
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_SHADING_NO_MOVE
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW,
- 40,
- 0, // shading_ta_lines
- 200
-};
-
-static Genesys_Model visioneer_xp300_model = {
- "visioneer-strobe-xp300", /* Name */
- "Visioneer", /* Device vendor string */
- "Strobe XP300", /* Device model name */
- MODEL_VISIONEER_STROBE_XP300,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (1.0), /* Start of scan area in mm (y) */
- SANE_FIX (435.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (26.5), /* Size of scan area after paper sensor stops
- sensing document in mm */
- /* this is larger than needed -- accounts for second sensor head, which is a
- calibration item */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_XP300,
- DAC_WOLFSON_XP300,
- GPO_XP300,
- MOTOR_XP300,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model syscan_docketport_665_model = {
- "syscan-docketport-665", /* Name */
- "Syscan/Ambir", /* Device vendor string */
- "DocketPORT 665", /* Device model name */
- MODEL_SYSCAN_DOCKETPORT_665,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in mm (y) */
- SANE_FIX (108.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (17.5), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_DP665,
- DAC_WOLFSON_XP300,
- GPO_DP665,
- MOTOR_DP665,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model visioneer_roadwarrior_model = {
- "visioneer-roadwarrior", /* Name */
- "Visioneer", /* Device vendor string */
- "Readwarrior", /* Device model name */
- MODEL_VISIONEER_ROADWARRIOR,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (16.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_ROADWARRIOR,
- DAC_WOLFSON_XP300,
- GPO_DP665,
- MOTOR_ROADWARRIOR,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model syscan_docketport_465_model = {
- "syscan-docketport-465", /* Name */
- "Syscan", /* Device vendor string */
- "DocketPORT 465", /* Device model name */
- MODEL_SYSCAN_DOCKETPORT_465,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (16.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_ROADWARRIOR,
- DAC_WOLFSON_XP300,
- GPO_DP665,
- MOTOR_ROADWARRIOR,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_NO_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_UNTESTED,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW,
- 300,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model visioneer_xp100_r3_model = {
- "visioneer-xp100-revision3", /* Name */
- "Visioneer", /* Device vendor string */
- "XP100 Revision 3", /* Device model name */
- MODEL_VISIONEER_STROBE_XP100_REVISION3,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (16.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_ROADWARRIOR,
- DAC_WOLFSON_XP300,
- GPO_DP665,
- MOTOR_ROADWARRIOR,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model pentax_dsmobile_600_model = {
- "pentax-dsmobile-600", /* Name */
- "Pentax", /* Device vendor string */
- "DSmobile 600", /* Device model name */
- MODEL_PENTAX_DSMOBILE_600,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (16.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_DSMOBILE600,
- DAC_WOLFSON_DSM600,
- GPO_DP665,
- MOTOR_DSMOBILE_600,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model syscan_docketport_467_model = {
- "syscan-docketport-467", /* Name */
- "Syscan", /* Device vendor string */
- "DocketPORT 467", /* Device model name */
- MODEL_SYSCAN_DOCKETPORT_467,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (16.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_DSMOBILE600,
- DAC_WOLFSON_DSM600,
- GPO_DP665,
- MOTOR_DSMOBILE_600,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model syscan_docketport_685_model = {
- "syscan-docketport-685", /* Name */
- "Syscan/Ambir", /* Device vendor string */
- "DocketPORT 685", /* Device model name */
- MODEL_SYSCAN_DOCKETPORT_685,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (1.0), /* Start of scan area in mm (y) */
- SANE_FIX (212.0), /* Size of scan area in mm (x) */
- SANE_FIX (500), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (26.5), /* Size of scan area after paper sensor stops
- sensing document in mm */
- /* this is larger than needed -- accounts for second sensor head, which is a
- calibration item */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_DP685,
- DAC_WOLFSON_DSM600,
- GPO_DP685,
- MOTOR_XP300,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model syscan_docketport_485_model = {
- "syscan-docketport-485", /* Name */
- "Syscan/Ambir", /* Device vendor string */
- "DocketPORT 485", /* Device model name */
- MODEL_SYSCAN_DOCKETPORT_485,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (1.0), /* Start of scan area in mm (y) */
- SANE_FIX (435.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (26.5), /* Size of scan area after paper sensor stops
- sensing document in mm */
- /* this is larger than needed -- accounts for second sensor head, which is a
- calibration item */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_XP300,
- DAC_WOLFSON_XP300,
- GPO_XP300,
- MOTOR_XP300,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model dct_docketport_487_model = {
- "dct-docketport-487", /* Name */
- "DCT", /* Device vendor string */
- "DocketPORT 487", /* Device model name */
- MODEL_DCT_DOCKETPORT_487,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.0), /* Start of scan area in mm (x) */
- SANE_FIX (1.0), /* Start of scan area in mm (y) */
- SANE_FIX (435.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (26.5), /* Size of scan area after paper sensor stops
- sensing document in mm */
- /* this is larger than needed -- accounts for second sensor head, which is a
- calibration item */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_XP300,
- DAC_WOLFSON_XP300,
- GPO_XP300,
- MOTOR_XP300,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_UNTESTED,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model visioneer_7100_model = {
- "visioneer-7100-model", /* Name */
- "Visioneer", /* Device vendor string */
- "OneTouch 7100", /* Device model name */
- MODEL_VISIONEER_7100,
- GENESYS_GL646,
- NULL,
-
- {1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX ( 4.00), /* Start of scan area in mm (x) */
- SANE_FIX ( 0.80), /* 2.79 < Start of scan area in mm (y) */
- SANE_FIX (215.9), /* Size of scan area in mm (x) */
- SANE_FIX (296.4), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.00), /* Start of white strip in mm (y) */
- SANE_FIX (0.00), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.00), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.00), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (0.00), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (0.00), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.00), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
-/* 48, 24, 0, */
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_5345,
- DAC_WOLFSON_5345,
- GPO_5345,
- MOTOR_5345,
- GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_SEARCH_START
- | GENESYS_FLAG_STAGGERED_LINE
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW,
- 40,
- 0, // shading_ta_lines
- 200
-};
-
-static Genesys_Model xerox_2400_model = {
- "xerox-2400-model", /* Name */
- "Xerox", /* Device vendor string */
- "OneTouch 2400", /* Device model name */
- MODEL_XEROX_2400,
- GENESYS_GL646,
- NULL,
-
- {1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */
- {2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX ( 4.00), /* Start of scan area in mm (x) */
- SANE_FIX ( 0.80), /* 2.79 < Start of scan area in mm (y) */
- SANE_FIX (215.9), /* Size of scan area in mm (x) */
- SANE_FIX (296.4), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.00), /* Start of white strip in mm (y) */
- SANE_FIX (0.00), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.00), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.00), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (0.00), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (0.00), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.00), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 48, 24, 0, /* RGB CCD Line-distance correction in pixel */
-/* 48, 24, 0, */
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_5345,
- DAC_WOLFSON_5345,
- GPO_5345,
- MOTOR_5345,
- GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_SEARCH_START
- | GENESYS_FLAG_STAGGERED_LINE
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW,
- 40,
- 0, // shading_ta_lines
- 200
-};
-
-
-static Genesys_Model xerox_travelscanner_model = {
- "xerox-travelscanner", /* Name */
- "Xerox", /* Device vendor string */
- "Travelscanner 100", /* Device model name */
- MODEL_XEROX_TRAVELSCANNER_100,
- GENESYS_GL841,
- NULL,
-
- {600, 300, 150, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (4.0), /* Start of scan area in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in mm (y) */
- SANE_FIX (220.0), /* Size of scan area in mm (x) */
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (16.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_TRUE, /* Is this a CIS scanner? */
- SANE_TRUE, /* Is this a sheetfed scanner? */
- CCD_ROADWARRIOR,
- DAC_WOLFSON_XP300,
- GPO_DP665,
- MOTOR_ROADWARRIOR,
- GENESYS_FLAG_LAZY_INIT /* Which flags are needed for this scanner? */
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION,
- GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE,
- 100,
- 0, // shading_ta_lines
- 400
-};
-
-static Genesys_Model plustek_3600_model = {
- "plustek-opticbook-3600", /* Name */
- "PLUSTEK", /* Device vendor string */
- "OpticBook 3600", /* Device model name */
- MODEL_PLUSTEK_OPTICPRO_3600,
- GENESYS_GL841,
- NULL,
- {/*1200,*/ 600, 400, 300, 200, 150, 100, 75, 0}, /* possible x-resolutions */
- {/*2400,*/ 1200, 600, 400, 300, 200, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (0.42),/*SANE_FIX (0.42), Start of scan area in mm (x) */
- SANE_FIX (6.75),/*SANE_FIX (7.9), Start of scan area in mm (y) */
- SANE_FIX (216.0),/*SANE_FIX (216.0), Size of scan area in mm (x) */
- SANE_FIX (297.0),/*SANE_FIX (297.0), Size of scan area in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_PLUSTEK_3600,
- DAC_PLUSTEK_3600,
- GPO_PLUSTEK_3600,
- MOTOR_PLUSTEK_3600,
- GENESYS_FLAG_UNTESTED /* not fully working yet */
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_LAZY_INIT,/*
- | GENESYS_FLAG_NO_CALIBRATION,*/
- GENESYS_HAS_NO_BUTTONS,
- 7,
- 0, // shading_ta_lines
- 200
-};
-
-static Genesys_Model hpn6310_model = {
- "hewlett-packard-scanjet-N6310", /* Name */
- "Hewlett Packard", /* Device vendor string */
- "ScanJet N6310", /* Device model name */
- MODEL_HP_SCANJET_N6310,
- GENESYS_GL847,
- NULL,
-
- { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 0},
- { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 0},
-
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (6), /* Start of scan area in mm (x) */
- SANE_FIX (2), /* Start of scan area in mm (y) */
- SANE_FIX (216), /* Size of scan area in mm (x) 5148 pixels at 600 dpi*/
- SANE_FIX (511), /* Size of scan area in mm (y) */
-
- SANE_FIX (3.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 0, 0, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_HP_N6310,
- DAC_CANONLIDE200, /*Not defined yet for N6310 */
- GPO_HP_N6310,
- MOTOR_CANONLIDE200, /*Not defined yet for N6310 */
- GENESYS_FLAG_UNTESTED /* not fully working yet */
- | GENESYS_FLAG_LAZY_INIT
- | GENESYS_FLAG_14BIT_GAMMA
- | GENESYS_FLAG_DARK_CALIBRATION
- | GENESYS_FLAG_OFFSET_CALIBRATION
- | GENESYS_FLAG_CUSTOM_GAMMA
- | GENESYS_FLAG_SKIP_WARMUP
- | GENESYS_FLAG_NO_CALIBRATION,
-
- GENESYS_HAS_NO_BUTTONS,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-
-static Genesys_Model plustek_3800_model = {
- "plustek-opticbook-3800", /* Name */
- "PLUSTEK", /* Device vendor string */
- "OpticBook 3800", /* Device model name */
- MODEL_PLUSTEK_OPTICBOOK_3800,
- GENESYS_GL845,
- NULL,
-
- {1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (7.2), /* Start of scan area in mm (x) */
- SANE_FIX (14.7), /* Start of scan area in mm (y) */
- SANE_FIX (217.7), /* Size of scan area in mm (x) */
- SANE_FIX (300.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (9.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_PLUSTEK3800,
- DAC_PLUSTEK3800,
- GPO_PLUSTEK3800,
- MOTOR_PLUSTEK3800,
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_NO_BUTTONS, /* TODO there are 4 buttons to support */
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-
-static Genesys_Model canon_formula101_model = {
- "canon-image-formula-101", /* Name */
- "Canon", /* Device vendor string */
- "Image Formula 101", /* Device model name */
- MODEL_CANON_IMAGE_FORMULA_101,
- GENESYS_GL846,
- NULL,
-
- {1200, 600, 300, 150, 100, 75, 0}, /* possible x-resolutions */
- {1200, 600, 300, 150, 100, 75, 0}, /* possible y-resolutions */
- {16, 8, 0}, /* possible depths in gray mode */
- {16, 8, 0}, /* possible depths in color mode */
-
- SANE_FIX (7.2), /* Start of scan area in mm (x) */
- SANE_FIX (14.7), /* Start of scan area in mm (y) */
- SANE_FIX (217.7), /* Size of scan area in mm (x) */
- SANE_FIX (300.0), /* Size of scan area in mm (y) */
-
- SANE_FIX (9.0), /* Start of white strip in mm (y) */
- SANE_FIX (0.0), /* Start of black mark in mm (x) */
-
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (x) */
- SANE_FIX (0.0), /* Size of scan area in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */
-
- SANE_FIX (0.0), /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_FIX (0.0), /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- 0, 24, 48, /* RGB CCD Line-distance correction in pixel */
-
- COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */
-
- SANE_FALSE, /* Is this a CIS scanner? */
- SANE_FALSE, /* Is this a sheetfed scanner? */
- CCD_IMG101,
- DAC_IMG101,
- GPO_IMG101,
- MOTOR_IMG101,
- GENESYS_FLAG_LAZY_INIT |
- GENESYS_FLAG_SKIP_WARMUP |
- GENESYS_FLAG_OFFSET_CALIBRATION |
- GENESYS_FLAG_CUSTOM_GAMMA,
- GENESYS_HAS_NO_BUTTONS ,
- 100,
- 0, // shading_ta_lines
- 100
-};
-
-
-static Genesys_USB_Device_Entry genesys_usb_device_list[] = {
- /* GL646 devices */
- {0x03f0, 0x0901, &hp2300c_model},
- {0x03f0, 0x0a01, &hp2400c_model},
- {0x03f0, 0x1405, &hp3670c_model},
- {0x0461, 0x0377, &medion_md5345_model},
- {0x04a7, 0x0229, &visioneer_7100_model},
- {0x0461, 0x038b, &xerox_2400_model},
- {0x04a7, 0x0426, &visioneer_xp200_model},
- {0x0638, 0x0a10, &umax_astra_4500_model},
- {0x07b3, 0x0600, &plustek_st12_model},
- {0x07b3, 0x0601, &plustek_st24_model},
- /* GL841 devices */
- {0x04a7, 0x0474, &visioneer_xp300_model},
- {0x04a7, 0x0494, &visioneer_roadwarrior_model},
- {0x04a7, 0x049b, &visioneer_xp100_r3_model},
- {0x04a7, 0x04ac, &xerox_travelscanner_model},
- {0x04a9, 0x2213, &canon_lide_50_model},
- {0x04a9, 0x221c, &canon_lide_60_model},
- {0x04a9, 0x2214, &canon_lide_80_model},
- {0x07b3, 0x0900, &plustek_3600_model},
- {0x0a17, 0x3210, &pentax_dsmobile_600_model},
- {0x04f9, 0x2038, &pentax_dsmobile_600_model}, /* clone, only usb id is different */
- {0x0a82, 0x4800, &syscan_docketport_485_model},
- {0x0a82, 0x4802, &syscan_docketport_465_model},
- {0x0a82, 0x4803, &syscan_docketport_665_model},
- {0x0a82, 0x480c, &syscan_docketport_685_model},
- {0x1dcc, 0x4810, &dct_docketport_487_model},
- {0x1dcc, 0x4812, &syscan_docketport_467_model},
- /* GL843 devices */
- {0x04da, 0x100f, &panasonic_kvss080_model},
- {0x03f0, 0x1b05, &hp4850c_model},
- {0x03f0, 0x4505, &hpg4010_model},
- {0x03f0, 0x4605, &hpg4050_model},
- {0x04a9, 0x2228, &canon_4400f_model},
- {0x04a9, 0x221e, &canon_8400f_model},
- {0x04a9, 0x2229, &canon_8600f_model},
- /* GL845 devices */
- {0x07b3, 0x1300, &plustek_3800_model},
- /* GL846 devices */
- {0x1083, 0x162e, &canon_formula101_model},
- /* GL847 devices */
- {0x04a9, 0x1904, &canon_lide_100_model},
- {0x04a9, 0x1905, &canon_lide_200_model},
- {0x04a9, 0x1906, &canon_5600f_model},
- {0x04a9, 0x1907, &canon_lide_700f_model},
- {0x03f0, 0x4705, &hpn6310_model},
- /* GL124 devices */
- {0x04a9, 0x1909, &canon_lide_110_model},
- {0x04a9, 0x190e, &canon_lide_120_model},
- {0x04a9, 0x190a, &canon_lide_210_model},
- {0x04a9, 0x190f, &canon_lide_220_model},
- {0, 0, NULL}
-};
-
-#define MAX_SCANNERS (sizeof(genesys_usb_device_list) / \
- sizeof(genesys_usb_device_list[0]))
diff --git a/backend/genesys_gl124.cc b/backend/genesys_gl124.cc
deleted file mode 100644
index a535d58..0000000
--- a/backend/genesys_gl124.cc
+++ /dev/null
@@ -1,3592 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2010-2016 Stéphane Voltz <stef.dev@free.fr>
-
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#define DEBUG_DECLARE_ONLY
-
-#include "genesys_gl124.h"
-
-#include <vector>
-
-/****************************************************************************
- Mid level functions
- ****************************************************************************/
-
-static SANE_Bool gl124_get_fast_feed_bit(Genesys_Register_Set* regs)
-{
- return (bool)(regs->get8(REG02) & REG02_FASTFED);
-}
-
-static SANE_Bool gl124_get_filter_bit(Genesys_Register_Set* regs)
-{
- return (bool)(regs->get8(REG04) & REG04_FILTER);
-}
-
-static SANE_Bool gl124_get_lineart_bit(Genesys_Register_Set* regs)
-{
- return (bool)(regs->get8(REG04) & REG04_LINEART);
-}
-
-static SANE_Bool gl124_get_bitset_bit(Genesys_Register_Set* regs)
-{
- return (bool)(regs->get8(REG04) & REG04_BITSET);
-}
-
-static SANE_Bool gl124_get_gain4_bit (Genesys_Register_Set * regs)
-{
- return (bool)(regs->get8(REG06) & REG06_GAIN4);
-}
-
-static SANE_Bool
-gl124_test_buffer_empty_bit (SANE_Byte val)
-{
- if (val & BUFEMPTY)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl124_test_motor_flag_bit (SANE_Byte val)
-{
- if (val & MOTORENB)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-/** @brief sensor profile
- * search for the database of motor profiles and get the best one. Each
- * profile is at a specific dpihw. Use LiDE 110 table by default.
- * @param sensor_type sensor id
- * @param dpi hardware dpi for the scan
- * @param half_ccd flag to signal half ccd mode
- * @return a pointer to a Sensor_Profile struct
- */
-static Sensor_Profile *get_sensor_profile(int sensor_type, int dpi, int half_ccd)
-{
- unsigned int i;
- int idx;
-
- i=0;
- idx=-1;
- while(i<sizeof(sensors)/sizeof(Sensor_Profile))
- {
- /* exact match */
- if(sensors[i].sensor_type==sensor_type
- && sensors[i].dpi==dpi
- && sensors[i].half_ccd==half_ccd)
- {
- return &(sensors[i]);
- }
-
- /* closest match */
- if(sensors[i].sensor_type==sensor_type
- && sensors[i].half_ccd==half_ccd)
- {
- if(idx<0)
- {
- idx=i;
- }
- else
- {
- if(sensors[i].dpi>=dpi
- && sensors[i].dpi<sensors[idx].dpi)
- {
- idx=i;
- }
- }
- }
- i++;
- }
-
- /* default fallback */
- if(idx<0)
- {
- DBG (DBG_warn,"%s: using default sensor profile\n",__func__);
- idx=0;
- }
-
- return &(sensors[idx]);
-}
-
-
-static SANE_Status
-gl124_homsnr_gpio(Genesys_Device *dev)
-{
-uint8_t val;
-SANE_Status status=SANE_STATUS_GOOD;
-
- RIE (sanei_genesys_read_register (dev, REG32, &val));
- val &= ~REG32_GPIO10;
- RIE (sanei_genesys_write_register (dev, REG32, val));
- return status;
-}
-
-/**@brief compute half ccd mode
- * Compute half CCD mode flag. Half CCD is on when dpiset it twice
- * the actual scanning resolution. Used for fast scans.
- * @param model pointer to device model
- * @param xres required horizontal resolution
- * @return SANE_TRUE if half CCD mode enabled
- */
-static SANE_Bool compute_half_ccd(const Genesys_Sensor& sensor, int xres)
-{
- /* we have 2 domains for ccd: xres below or above half ccd max dpi */
- if (xres<=300 && sensor.ccd_size_divisor > 1)
- {
- return SANE_TRUE;
- }
- return SANE_FALSE;
-}
-
-/** @brief set all registers to default values .
- * This function is called only once at the beginning and
- * fills register startup values for registers reused across scans.
- * Those that are rarely modified or not modified are written
- * individually.
- * @param dev device structure holding register set to initialize
- */
-static void
-gl124_init_registers (Genesys_Device * dev)
-{
- DBGSTART;
-
- dev->reg.clear();
-
- /* default to LiDE 110 */
- SETREG (0x01,0xa2); /* + REG01_SHDAREA */
- SETREG (0x02,0x90);
- SETREG (0x03,0x50);
- SETREG (0x04,0x03);
- SETREG (0x05,0x00);
- if(dev->model->ccd_type==CIS_CANONLIDE120)
- {
- SETREG (0x06,0x50);
- SETREG (0x07,0x00);
- }
- else
- {
- SETREG (0x03,0x50 & ~REG03_AVEENB);
- SETREG (0x06,0x50 | REG06_GAIN4);
- }
- SETREG (0x09,0x00);
- SETREG (0x0a,0xc0);
- SETREG (0x0b,0x2a);
- SETREG (0x0c,0x12);
- SETREG (0x11,0x00);
- SETREG (0x12,0x00);
- SETREG (0x13,0x0f);
- SETREG (0x14,0x00);
- SETREG (0x15,0x80);
- SETREG (0x16,0x10);
- SETREG (0x17,0x04);
- SETREG (0x18,0x00);
- SETREG (0x19,0x01);
- SETREG (0x1a,0x30);
- SETREG (0x1b,0x00);
- SETREG (0x1c,0x00);
- SETREG (0x1d,0x01);
- SETREG (0x1e,0x10);
- SETREG (0x1f,0x00);
- SETREG (0x20,0x15);
- SETREG (0x21,0x00);
- if(dev->model->ccd_type!=CIS_CANONLIDE120)
- {
- SETREG (0x22,0x02);
- }
- else
- {
- SETREG (0x22,0x14);
- }
- SETREG (0x23,0x00);
- SETREG (0x24,0x00);
- SETREG (0x25,0x00);
- SETREG (0x26,0x0d);
- SETREG (0x27,0x48);
- SETREG (0x28,0x00);
- SETREG (0x29,0x56);
- SETREG (0x2a,0x5e);
- SETREG (0x2b,0x02);
- SETREG (0x2c,0x02);
- SETREG (0x2d,0x58);
- SETREG (0x3b,0x00);
- SETREG (0x3c,0x00);
- SETREG (0x3d,0x00);
- SETREG (0x3e,0x00);
- SETREG (0x3f,0x02);
- SETREG (0x40,0x00);
- SETREG (0x41,0x00);
- SETREG (0x42,0x00);
- SETREG (0x43,0x00);
- SETREG (0x44,0x00);
- SETREG (0x45,0x00);
- SETREG (0x46,0x00);
- SETREG (0x47,0x00);
- SETREG (0x48,0x00);
- SETREG (0x49,0x00);
- SETREG (0x4f,0x00);
- SETREG (0x52,0x00);
- SETREG (0x53,0x02);
- SETREG (0x54,0x04);
- SETREG (0x55,0x06);
- SETREG (0x56,0x04);
- SETREG (0x57,0x04);
- SETREG (0x58,0x04);
- SETREG (0x59,0x04);
- SETREG (0x5a,0x1a);
- SETREG (0x5b,0x00);
- SETREG (0x5c,0xc0);
- SETREG (0x5f,0x00);
- SETREG (0x60,0x02);
- SETREG (0x61,0x00);
- SETREG (0x62,0x00);
- SETREG (0x63,0x00);
- SETREG (0x64,0x00);
- SETREG (0x65,0x00);
- SETREG (0x66,0x00);
- SETREG (0x67,0x00);
- SETREG (0x68,0x00);
- SETREG (0x69,0x00);
- SETREG (0x6a,0x00);
- SETREG (0x6b,0x00);
- SETREG (0x6c,0x00);
- SETREG (0x6e,0x00);
- SETREG (0x6f,0x00);
- if(dev->model->ccd_type!=CIS_CANONLIDE120)
- {
- SETREG (0x6d,0xd0);
- SETREG (0x71,0x08);
- }
- else
- {
- SETREG (0x6d,0x00);
- SETREG (0x71,0x1f);
- }
- SETREG (0x70,0x00);
- SETREG (0x72,0x08);
- SETREG (0x73,0x0a);
-
- /* CKxMAP */
- SETREG (0x74,0x00);
- SETREG (0x75,0x00);
- SETREG (0x76,0x3c);
- SETREG (0x77,0x00);
- SETREG (0x78,0x00);
- SETREG (0x79,0x9f);
- SETREG (0x7a,0x00);
- SETREG (0x7b,0x00);
- SETREG (0x7c,0x55);
-
- SETREG (0x7d,0x00);
- SETREG (0x7e,0x08);
- SETREG (0x7f,0x58);
- if(dev->model->ccd_type!=CIS_CANONLIDE120)
- {
- SETREG (0x80,0x00);
- SETREG (0x81,0x14);
- }
- else
- {
- SETREG (0x80,0x00);
- SETREG (0x81,0x10);
- }
-
- /* STRPIXEL */
- SETREG (0x82,0x00);
- SETREG (0x83,0x00);
- SETREG (0x84,0x00);
- /* ENDPIXEL */
- SETREG (0x85,0x00);
- SETREG (0x86,0x00);
- SETREG (0x87,0x00);
-
- SETREG (0x88,0x00);
- SETREG (0x89,0x65);
- SETREG (0x8a,0x00);
- SETREG (0x8b,0x00);
- SETREG (0x8c,0x00);
- SETREG (0x8d,0x00);
- SETREG (0x8e,0x00);
- SETREG (0x8f,0x00);
- SETREG (0x90,0x00);
- SETREG (0x91,0x00);
- SETREG (0x92,0x00);
- SETREG (0x93,0x00);
- SETREG (0x94,0x14);
- SETREG (0x95,0x30);
- SETREG (0x96,0x00);
- SETREG (0x97,0x90);
- SETREG (0x98,0x01);
- SETREG (0x99,0x1f);
- SETREG (0x9a,0x00);
- SETREG (0x9b,0x80);
- SETREG (0x9c,0x80);
- SETREG (0x9d,0x3f);
- SETREG (0x9e,0x00);
- SETREG (0x9f,0x00);
- SETREG (0xa0,0x20);
- SETREG (0xa1,0x30);
- SETREG (0xa2,0x00);
- SETREG (0xa3,0x20);
- SETREG (0xa4,0x01);
- SETREG (0xa5,0x00);
- SETREG (0xa6,0x00);
- SETREG (0xa7,0x08);
- SETREG (0xa8,0x00);
- SETREG (0xa9,0x08);
- SETREG (0xaa,0x01);
- SETREG (0xab,0x00);
- SETREG (0xac,0x00);
- SETREG (0xad,0x40);
- SETREG (0xae,0x01);
- SETREG (0xaf,0x00);
- SETREG (0xb0,0x00);
- SETREG (0xb1,0x40);
- SETREG (0xb2,0x00);
- SETREG (0xb3,0x09);
- SETREG (0xb4,0x5b);
- SETREG (0xb5,0x00);
- SETREG (0xb6,0x10);
- SETREG (0xb7,0x3f);
- SETREG (0xb8,0x00);
- SETREG (0xbb,0x00);
- SETREG (0xbc,0xff);
- SETREG (0xbd,0x00);
- SETREG (0xbe,0x07);
- SETREG (0xc3,0x00);
- SETREG (0xc4,0x00);
-
- /* gamma
- SETREG (0xc5,0x00);
- SETREG (0xc6,0x00);
- SETREG (0xc7,0x00);
- SETREG (0xc8,0x00);
- SETREG (0xc9,0x00);
- SETREG (0xca,0x00);
- SETREG (0xcb,0x00);
- SETREG (0xcc,0x00);
- SETREG (0xcd,0x00);
- SETREG (0xce,0x00);
- */
- if(dev->model->ccd_type==CIS_CANONLIDE120)
- {
- SETREG (0xc5,0x20);
- SETREG (0xc6,0xeb);
- SETREG (0xc7,0x20);
- SETREG (0xc8,0xeb);
- SETREG (0xc9,0x20);
- SETREG (0xca,0xeb);
- }
-
- /* memory layout
- SETREG (0xd0,0x0a);
- SETREG (0xd1,0x1f);
- SETREG (0xd2,0x34); */
- SETREG (0xd3,0x00);
- SETREG (0xd4,0x00);
- SETREG (0xd5,0x00);
- SETREG (0xd6,0x00);
- SETREG (0xd7,0x00);
- SETREG (0xd8,0x00);
- SETREG (0xd9,0x00);
-
- /* memory layout
- SETREG (0xe0,0x00);
- SETREG (0xe1,0x48);
- SETREG (0xe2,0x15);
- SETREG (0xe3,0x90);
- SETREG (0xe4,0x15);
- SETREG (0xe5,0x91);
- SETREG (0xe6,0x2a);
- SETREG (0xe7,0xd9);
- SETREG (0xe8,0x2a);
- SETREG (0xe9,0xad);
- SETREG (0xea,0x40);
- SETREG (0xeb,0x22);
- SETREG (0xec,0x40);
- SETREG (0xed,0x23);
- SETREG (0xee,0x55);
- SETREG (0xef,0x6b);
- SETREG (0xf0,0x55);
- SETREG (0xf1,0x6c);
- SETREG (0xf2,0x6a);
- SETREG (0xf3,0xb4);
- SETREG (0xf4,0x6a);
- SETREG (0xf5,0xb5);
- SETREG (0xf6,0x7f);
- SETREG (0xf7,0xfd);*/
-
- SETREG (0xf8,0x01); /* other value is 0x05 */
- SETREG (0xf9,0x00);
- SETREG (0xfa,0x00);
- SETREG (0xfb,0x00);
- SETREG (0xfc,0x00);
- SETREG (0xff,0x00);
-
- /* fine tune upon device description */
- dev->reg.find_reg(0x05).value &= ~REG05_DPIHW;
- switch (sanei_genesys_find_sensor_any(dev).optical_res)
- {
- case 600:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_600;
- break;
- case 1200:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_1200;
- break;
- case 2400:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_2400;
- break;
- case 4800:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_4800;
- break;
- }
-
- dev->calib_reg = dev->reg;
-
- DBGCOMPLETED;
-}
-
-/**@brief send slope table for motor movement
- * Send slope_table in machine byte order
- * @param dev device to send slope table
- * @param table_nr index of the slope table in ASIC memory
- * Must be in the [0-4] range.
- * @param slope_table pointer to 16 bit values array of the slope table
- * @param steps number of elemnts in the slope table
- */
-static SANE_Status
-gl124_send_slope_table (Genesys_Device * dev, int table_nr,
- uint16_t * slope_table, int steps)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- char msg[10000];
-
- DBG (DBG_proc, "%s (table_nr = %d, steps = %d)\n", __func__,
- table_nr, steps);
-
- /* sanity check */
- if(table_nr<0 || table_nr>4)
- {
- DBG (DBG_error, "%s: invalid table number %d!\n", __func__, table_nr);
- return SANE_STATUS_INVAL;
- }
-
- std::vector<uint8_t> table(steps * 2);
- for (i = 0; i < steps; i++)
- {
- table[i * 2] = slope_table[i] & 0xff;
- table[i * 2 + 1] = slope_table[i] >> 8;
- }
-
- if (DBG_LEVEL >= DBG_io)
- {
- sprintf (msg, "write slope %d (%d)=", table_nr, steps);
- for (i = 0; i < steps; i++)
- {
- sprintf (msg+strlen(msg), ",%d", slope_table[i]);
- }
- DBG (DBG_io, "%s: %s\n", __func__, msg);
- }
-
- /* slope table addresses are fixed */
- status = sanei_genesys_write_ahb(dev, 0x10000000 + 0x4000 * table_nr, steps * 2, table.data());
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: write to AHB failed writing slope table %d (%s)\n",
- __func__, table_nr, sane_strstatus (status));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/** @brief * Set register values of 'special' ti type frontend
- * Registers value are taken from the frontend register data
- * set.
- * @param dev device owning the AFE
- * @param set flag AFE_INIT to specify the AFE must be reset before writing data
- * */
-static SANE_Status
-gl124_set_ti_fe (Genesys_Device * dev, uint8_t set)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
-
- DBGSTART;
- if (set == AFE_INIT)
- {
- DBG (DBG_proc, "%s: setting DAC %u\n", __func__, dev->model->dac_type);
-
- dev->frontend = dev->frontend_initial;
- }
-
- /* start writing to DAC */
- status = sanei_genesys_fe_write_data (dev, 0x00, 0x80);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to write reg0: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- /* write values to analog frontend */
- for (uint16_t addr = 0x01; addr < 0x04; addr++)
- {
- status = sanei_genesys_fe_write_data(dev, addr, dev->frontend.regs.get_value(addr));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write reg %d: %s\n", __func__, addr,
- sane_strstatus(status));
- return status;
- }
- }
-
- status = sanei_genesys_fe_write_data (dev, 0x04, 0x00);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to write reg4: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- /* these are not really sign for this AFE */
- for (i = 0; i < 3; i++)
- {
- status = sanei_genesys_fe_write_data(dev, 0x05 + i, dev->frontend.regs.get_value(0x24 + i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to write reg %d: %s\n", __func__, i+5,
- sane_strstatus (status));
- return status;
- }
- }
-
- /* close writing to DAC */
- if(dev->model->dac_type == DAC_CANONLIDE120)
- {
- status = sanei_genesys_fe_write_data (dev, 0x00, 0x01);
- }
- else
- {
- status = sanei_genesys_fe_write_data (dev, 0x00, 0x11);
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to write reg0: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- DBGCOMPLETED;
-
- return status;
-}
-
-
-/* Set values of analog frontend */
-static SANE_Status
-gl124_set_fe(Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- DBG(DBG_proc, "%s (%s)\n", __func__, set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
- AFE_POWER_SAVE ? "powersave" : "huh?");
-
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
- dev->frontend = dev->frontend_initial;
- }
-
- RIE (sanei_genesys_read_register (dev, REG0A, &val));
-
- /* route to correct analog FE */
- switch ((val & REG0A_SIFSEL)>>REG0AS_SIFSEL)
- {
- case 3:
- status=gl124_set_ti_fe (dev, set);
- break;
- case 0:
- case 1:
- case 2:
- default:
- DBG(DBG_error, "%s: unsupported analog FE 0x%02x\n", __func__, val);
- status=SANE_STATUS_INVAL;
- break;
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/**@brief compute exposure to use
- * compute the sensor exposure based on target resolution
- * @param dev pointer to device description
- * @param xres sensor's required resolution
- * @param half_ccd flag for half ccd mode
- */
-static int gl124_compute_exposure(Genesys_Device *dev, int xres, int half_ccd)
-{
- Sensor_Profile* sensor_profile = get_sensor_profile(dev->model->ccd_type, xres, half_ccd);
- return sensor_profile->exposure;
-}
-
-
-static SANE_Status
-gl124_init_motor_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int scan_exposure_time,
- float scan_yres,
- int scan_step_type,
- unsigned int scan_lines,
- unsigned int scan_dummy,
- unsigned int feed_steps,
- ScanColorMode scan_mode,
- unsigned int flags)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int use_fast_fed;
- unsigned int lincnt, fast_dpi;
- uint16_t scan_table[SLOPE_TABLE_SIZE];
- uint16_t fast_table[SLOPE_TABLE_SIZE];
- int scan_steps,fast_steps,factor;
- unsigned int feedl,dist;
- uint32_t z1, z2;
- float yres;
- int min_speed;
- unsigned int linesel;
-
- DBGSTART;
- DBG(DBG_info, "%s : scan_exposure_time=%d, scan_yres=%g, scan_step_type=%d, scan_lines=%d, "
- "scan_dummy=%d, feed_steps=%d, scan_mode=%d, flags=%x\n", __func__, scan_exposure_time,
- scan_yres, scan_step_type, scan_lines, scan_dummy, feed_steps,
- static_cast<unsigned>(scan_mode), flags);
-
- /* we never use fast fed since we do manual feed for the scans */
- use_fast_fed=0;
- factor=1;
-
- /* enforce motor minimal scan speed
- * @TODO extend motor struct for this value */
- if (scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- min_speed = 900;
- }
- else
- {
- switch(dev->model->motor_type)
- {
- case MOTOR_CANONLIDE110:
- min_speed = 600;
- break;
- case MOTOR_CANONLIDE120:
- min_speed = 900;
- break;
- default:
- min_speed = 900;
- break;
- }
- }
-
- /* compute min_speed and linesel */
- if(scan_yres<min_speed)
- {
- yres=min_speed;
- linesel=yres/scan_yres-1;
- /* limit case, we need a linesel > 0 */
- if(linesel==0)
- {
- linesel=1;
- yres=scan_yres*2;
- }
- }
- else
- {
- yres=scan_yres;
- linesel=0;
- }
-
- DBG (DBG_io2, "%s: final yres=%f, linesel=%d\n", __func__, yres, linesel);
-
- lincnt=scan_lines*(linesel+1);
- sanei_genesys_set_triple(reg,REG_LINCNT,lincnt);
- DBG (DBG_io, "%s: lincnt=%d\n", __func__, lincnt);
-
- /* compute register 02 value */
- uint8_t r02 = REG02_NOTHOME;
-
- if (use_fast_fed) {
- r02 |= REG02_FASTFED;
- } else {
- r02 &= ~REG02_FASTFED;
- }
-
- if (flags & MOTOR_FLAG_AUTO_GO_HOME)
- r02 |= REG02_AGOHOME;
-
- if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
- ||(yres>=sensor.optical_res))
- {
- r02 |= REG02_ACDCDIS;
- }
-
- reg->set8(REG02, r02);
- sanei_genesys_set_motor_power(*reg, true);
-
- /* SCANFED */
- sanei_genesys_set_double(reg,REG_SCANFED,4);
-
- /* scan and backtracking slope table */
- sanei_genesys_slope_table(scan_table,
- &scan_steps,
- yres,
- scan_exposure_time,
- dev->motor.base_ydpi,
- scan_step_type,
- factor,
- dev->model->motor_type,
- motors);
- RIE(gl124_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps));
- RIE(gl124_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps));
-
- /* STEPNO */
- sanei_genesys_set_double(reg,REG_STEPNO,scan_steps);
-
- /* fast table */
- fast_dpi=yres;
-
- /*
- if (scan_mode != ScanColorMode::COLOR_SINGLE_PASS)
- {
- fast_dpi*=3;
- }
- */
- sanei_genesys_slope_table(fast_table,
- &fast_steps,
- fast_dpi,
- scan_exposure_time,
- dev->motor.base_ydpi,
- scan_step_type,
- factor,
- dev->model->motor_type,
- motors);
- RIE(gl124_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps));
- RIE(gl124_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps));
-
- /* FASTNO */
- sanei_genesys_set_double(reg,REG_FASTNO,fast_steps);
-
- /* FSHDEC */
- sanei_genesys_set_double(reg,REG_FSHDEC,fast_steps);
-
- /* FMOVNO */
- sanei_genesys_set_double(reg,REG_FMOVNO,fast_steps);
-
- /* substract acceleration distance from feedl */
- feedl=feed_steps;
- feedl<<=scan_step_type;
-
- dist = scan_steps;
- if (flags & MOTOR_FLAG_FEED)
- dist *=2;
- if (use_fast_fed)
- {
- dist += fast_steps*2;
- }
- DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
-
- /* get sure we don't use insane value */
- if(dist<feedl)
- feedl -= dist;
- else
- feedl = 0;
-
- sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
- DBG (DBG_io, "%s: feedl=%d\n", __func__, feedl);
-
- /* doesn't seem to matter that much */
- sanei_genesys_calculate_zmode2 (use_fast_fed,
- scan_exposure_time,
- scan_table,
- scan_steps,
- feedl,
- scan_steps,
- &z1,
- &z2);
-
- sanei_genesys_set_triple(reg, REG_Z1MOD,z1);
- DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
-
- sanei_genesys_set_triple(reg, REG_Z2MOD, z2);
- DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
-
- /* LINESEL */
- reg->set8_mask(REG1D, linesel, REG1D_LINESEL);
- reg->set8(REGA0, (scan_step_type << REGA0S_STEPSEL) | (scan_step_type << REGA0S_FSTPSEL));
-
- /* FMOVDEC */
- sanei_genesys_set_double(reg,REG_FMOVDEC,fast_steps);
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/** @brief copy sensor specific settings
- * Set up register set for the given sensor resolution. Values are from the device table
- * in genesys_devices.c for registers:
- * [0x16 ... 0x1d]
- * [0x52 ... 0x5e]
- * Other come from the specific device sensor table in genesys_gl124.h:
- * 0x18, 0x20, 0x61, 0x98 and
- * @param dev device to set up
- * @param regs register set to modify
- * @param dpi resolution of the sensor during scan
- * @param half_ccd flag for half ccd mode
- * */
-static void gl124_setup_sensor(Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs, int dpi, int half_ccd)
-{
- int dpihw;
- uint32_t exp;
-
- DBGSTART;
-
- // we start at 6, 0-5 is a 16 bits cache for exposure
- for (uint16_t addr = 0x16; addr < 0x1e; addr++) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- // skip writing 5d,5e which is AFE address because
- // they are not defined in register set */
- for (uint16_t addr = 0x52; addr < 0x52 + 11; addr++) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- /* set EXPDUMMY and CKxMAP */
- dpihw = sanei_genesys_compute_dpihw(dev, sensor, dpi);
- Sensor_Profile* sensor_profile = get_sensor_profile(dev->model->ccd_type, dpihw, half_ccd);
-
- regs->set8(0x18, sensor_profile->reg18);
- regs->set8(0x20, sensor_profile->reg20);
- regs->set8(0x61, sensor_profile->reg61);
- regs->set8(0x98, sensor_profile->reg98);
- if (sensor_profile->reg16 != 0) {
- regs->set8(0x16, sensor_profile->reg16);
- }
- if (sensor_profile->reg70 != 0) {
- regs->set8(0x70, sensor_profile->reg70);
- }
-
-
- sanei_genesys_set_triple(regs,REG_SEGCNT,sensor_profile->segcnt);
- sanei_genesys_set_double(regs,REG_TG0CNT,sensor_profile->tg0cnt);
- sanei_genesys_set_double(regs,REG_EXPDMY,sensor_profile->expdummy);
-
- /* if no calibration has been done, set default values for exposures */
- exp = sensor.exposure.red;
- if(exp==0)
- {
- exp=sensor_profile->expr;
- }
- sanei_genesys_set_triple(regs,REG_EXPR,exp);
-
- exp =sensor.exposure.green;
- if(exp==0)
- {
- exp=sensor_profile->expg;
- }
- sanei_genesys_set_triple(regs,REG_EXPG,exp);
-
- exp = sensor.exposure.blue;
- if(exp==0)
- {
- exp=sensor_profile->expb;
- }
- sanei_genesys_set_triple(regs,REG_EXPB,exp);
-
- sanei_genesys_set_triple(regs,REG_CK1MAP,sensor_profile->ck1map);
- sanei_genesys_set_triple(regs,REG_CK3MAP,sensor_profile->ck3map);
- sanei_genesys_set_triple(regs,REG_CK4MAP,sensor_profile->ck4map);
-
- /* order of the sub-segments */
- dev->order=sensor_profile->order;
-
- DBGCOMPLETED;
-}
-
-/** @brief setup optical related registers
- * start and pixels are expressed in optical sensor resolution coordinate
- * space.
- * @param dev scanner device to use
- * @param reg registers to set up
- * @param exposure_time exposure time to use
- * @param used_res scanning resolution used, may differ from
- * scan's one
- * @param start logical start pixel coordinate
- * @param pixels logical number of pixels to use
- * @param channels number of color channels (currently 1 or 3)
- * @param depth bit depth of the scan (1, 8 or 16)
- * @param half_ccd SANE_TRUE if sensor's timings are such that x coordinates
- * must be halved
- * @param color_filter color channel to use as gray data
- * @param flags optical flags (@see )
- * @return SANE_STATUS_GOOD if OK
- */
-static SANE_Status
-gl124_init_optical_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int exposure_time,
- int used_res,
- unsigned int start,
- unsigned int pixels,
- int channels,
- int depth,
- SANE_Bool half_ccd,
- ColorFilter color_filter,
- int flags)
-{
- unsigned int words_per_line, segcnt;
- unsigned int startx, endx, used_pixels, segnb;
- unsigned int dpiset, cksel, dpihw, factor;
- unsigned int bytes;
- GenesysRegister *r;
- SANE_Status status = SANE_STATUS_GOOD;
- uint32_t expmax, exp;
-
- DBG(DBG_proc, "%s : exposure_time=%d, used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
- "half_ccd=%d, flags=%x\n", __func__, exposure_time, used_res, start, pixels, channels, depth,
- half_ccd, flags);
-
- /* resolution is divided according to CKSEL */
- r = sanei_genesys_get_address (reg, REG18);
- cksel= (r->value & REG18_CKSEL)+1;
- DBG (DBG_io2, "%s: cksel=%d\n", __func__, cksel);
-
- /* to manage high resolution device while keeping good
- * low resolution scanning speed, we make hardware dpi vary */
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, used_res * cksel);
- factor=sensor.optical_res/dpihw;
- DBG (DBG_io2, "%s: dpihw=%d (factor=%d)\n", __func__, dpihw, factor);
-
- /* sensor parameters */
- gl124_setup_sensor(dev, sensor, reg, dpihw, half_ccd);
- dpiset = used_res * cksel;
-
- /* start and end coordinate in optical dpi coordinates */
- /* startx = start/cksel + sensor.dummy_pixel; XXX STEF XXX */
- startx = start/cksel;
- used_pixels=pixels/cksel;
- endx = startx + used_pixels;
-
- /* pixel coordinate factor correction when used dpihw is not maximal one */
- startx/=factor;
- endx/=factor;
- used_pixels=endx-startx;
-
- status = gl124_set_fe(dev, sensor, AFE_SET);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to set frontend: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- /* enable shading */
- r = sanei_genesys_get_address (reg, REG01);
- r->value &= ~REG01_SCAN;
- if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
- (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
- {
- r->value &= ~REG01_DVDSET;
- }
- else
- {
- r->value |= REG01_DVDSET;
- }
- r->value &= ~REG01_SCAN;
-
- r = sanei_genesys_get_address (reg, REG03);
- if((dev->model->ccd_type!=CIS_CANONLIDE120)&&(used_res>=600))
- {
- r->value &= ~REG03_AVEENB;
- DBG (DBG_io, "%s: disabling AVEENB\n", __func__);
- }
- else
- {
- r->value |= ~REG03_AVEENB;
- DBG (DBG_io, "%s: enabling AVEENB\n", __func__);
- }
-
- sanei_genesys_set_lamp_power(dev, sensor, *reg, !(flags & OPTICAL_FLAG_DISABLE_LAMP));
-
- /* BW threshold */
- RIE (sanei_genesys_write_register (dev, REG114, dev->settings.threshold));
- RIE (sanei_genesys_write_register (dev, REG115, dev->settings.threshold));
-
- /* monochrome / color scan */
- r = sanei_genesys_get_address (reg, REG04);
- switch (depth)
- {
- case 1:
- r->value &= ~REG04_BITSET;
- r->value |= REG04_LINEART;
- break;
- case 8:
- r->value &= ~(REG04_LINEART | REG04_BITSET);
- break;
- case 16:
- r->value &= ~REG04_LINEART;
- r->value |= REG04_BITSET;
- break;
- }
-
- r->value &= ~REG04_FILTER;
- if (channels == 1)
- {
- switch (color_filter)
- {
- case ColorFilter::RED:
- r->value |= 0x10;
- break;
- case ColorFilter::BLUE:
- r->value |= 0x30;
- break;
- case ColorFilter::GREEN:
- r->value |= 0x20;
- break;
- default:
- break; // should not happen
- }
- }
-
- /* register 05 */
- r = sanei_genesys_get_address (reg, REG05);
-
- /* set up dpihw */
- r->value &= ~REG05_DPIHW;
- switch(dpihw)
- {
- case 600:
- r->value |= REG05_DPIHW_600;
- break;
- case 1200:
- r->value |= REG05_DPIHW_1200;
- break;
- case 2400:
- r->value |= REG05_DPIHW_2400;
- break;
- case 4800:
- r->value |= REG05_DPIHW_4800;
- break;
- }
-
- /* enable gamma tables */
- if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
- r->value &= ~REG05_GMMENB;
- else
- r->value |= REG05_GMMENB;
-
- if(half_ccd)
- {
- sanei_genesys_set_double(reg,REG_DPISET,dpiset*2);
- DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset*2);
- }
- else
- {
- sanei_genesys_set_double(reg,REG_DPISET,dpiset);
- DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
- }
-
- r = sanei_genesys_get_address (reg, REG06);
- r->value |= REG06_GAIN4;
-
- /* CIS scanners can do true gray by setting LEDADD */
- /* we set up LEDADD only when asked */
- if (dev->model->is_cis == SANE_TRUE)
- {
- r = sanei_genesys_get_address (reg, REG60);
- r->value &= ~REG60_LEDADD;
- if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
- {
- r->value |= REG60_LEDADD;
- sanei_genesys_get_triple(reg,REG_EXPR,&expmax);
- sanei_genesys_get_triple(reg,REG_EXPG,&exp);
- if(exp>expmax)
- {
- expmax=exp;
- }
- sanei_genesys_get_triple(reg,REG_EXPB,&exp);
- if(exp>expmax)
- {
- expmax=exp;
- }
- sanei_genesys_set_triple(&dev->reg,REG_EXPR,expmax);
- sanei_genesys_set_triple(&dev->reg,REG_EXPG,expmax);
- sanei_genesys_set_triple(&dev->reg,REG_EXPB,expmax);
- }
- /* RGB weighting, REG_TRUER,G and B are to be set */
- r = sanei_genesys_get_address (reg, 0x01);
- r->value &= ~REG01_TRUEGRAY;
- if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
- {
- r->value |= REG01_TRUEGRAY;
- sanei_genesys_write_register (dev, REG_TRUER, 0x80);
- sanei_genesys_write_register (dev, REG_TRUEG, 0x80);
- sanei_genesys_write_register (dev, REG_TRUEB, 0x80);
- }
- }
-
- /* segment number */
- r = sanei_genesys_get_address (reg, 0x98);
- segnb = r->value & 0x0f;
-
- sanei_genesys_set_triple(reg,REG_STRPIXEL,startx/segnb);
- DBG (DBG_io2, "%s: strpixel used=%d\n", __func__, startx/segnb);
- sanei_genesys_get_triple(reg,REG_SEGCNT,&segcnt);
- if(endx/segnb==segcnt)
- {
- endx=0;
- }
- sanei_genesys_set_triple(reg,REG_ENDPIXEL,endx/segnb);
- DBG (DBG_io2, "%s: endpixel used=%d\n", __func__, endx/segnb);
-
- /* words(16bit) before gamma, conversion to 8 bit or lineart */
- words_per_line = (used_pixels * dpiset) / dpihw;
- bytes = depth / 8;
- if (depth == 1)
- {
- words_per_line = (words_per_line >> 3) + ((words_per_line & 7) ? 1 : 0);
- }
- else
- {
- words_per_line *= bytes;
- }
-
- dev->bpl = words_per_line;
- dev->cur = 0;
- dev->skip = 0;
- dev->len = dev->bpl/segnb;
- dev->dist = dev->bpl/segnb;
- dev->segnb = segnb;
- dev->line_count = 0;
- dev->line_interp = 0;
-
- DBG (DBG_io2, "%s: used_pixels =%d\n", __func__, used_pixels);
- DBG (DBG_io2, "%s: pixels =%d\n", __func__, pixels);
- DBG (DBG_io2, "%s: depth =%d\n", __func__, depth);
- DBG (DBG_io2, "%s: dev->bpl =%lu\n", __func__, (unsigned long)dev->bpl);
- DBG (DBG_io2, "%s: dev->len =%lu\n", __func__, (unsigned long)dev->len);
- DBG (DBG_io2, "%s: dev->dist =%lu\n", __func__, (unsigned long)dev->dist);
- DBG (DBG_io2, "%s: dev->line_interp=%lu\n", __func__, (unsigned long)dev->line_interp);
-
- words_per_line *= channels;
- dev->wpl = words_per_line;
-
- /* allocate buffer for odd/even pixels handling */
- dev->oe_buffer.clear();
- dev->oe_buffer.alloc(dev->wpl);
-
- /* MAXWD is expressed in 2 words unit */
- sanei_genesys_set_triple(reg,REG_MAXWD,(words_per_line));
- DBG (DBG_io2, "%s: words_per_line used=%d\n", __func__, words_per_line);
-
- sanei_genesys_set_triple(reg,REG_LPERIOD,exposure_time);
- DBG (DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time);
-
- sanei_genesys_set_double(reg,REG_DUMMY,sensor.dummy_pixel);
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** set up registers for an actual scan
- *
- * this function sets up the scanner to scan in normal or single line mode
- */
-static SANE_Status
-gl124_init_scan_regs(Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg,
- SetupParams& params)
-{
- params.assert_valid();
-
- int used_res;
- int start, used_pixels;
- int bytes_per_line;
- int move;
- unsigned int lincnt;
- unsigned int oflags, mflags; /**> optical and motor flags */
- int exposure_time;
- int stagger;
-
- int dummy = 0;
- int slope_dpi = 0;
- int scan_step_type = 1;
- int max_shift;
- size_t requested_buffer_size, read_buffer_size;
-
- SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
- unsigned optical_res;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
- half_ccd=compute_half_ccd(sensor, params.xres);
-
- /* optical_res */
- optical_res = sensor.optical_res;
- if (half_ccd)
- optical_res /= 2;
- DBG (DBG_info, "%s: optical_res=%d\n", __func__, optical_res);
-
- /* stagger */
- if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG (DBG_info, "gl124_init_scan_regs : stagger=%d lines\n", stagger);
-
- /** @brief compute used resolution */
- if (params.flags & SCAN_FLAG_USE_OPTICAL_RES)
- {
- used_res = optical_res;
- }
- else
- {
- /* resolution is choosen from a fixed list and can be used directly,
- * unless we have ydpi higher than sensor's maximum one */
- if(params.xres>optical_res)
- used_res=optical_res;
- else
- used_res = params.xres;
- }
-
- /* compute scan parameters values */
- /* pixels are allways given at full optical resolution */
- /* use detected left margin and fixed value */
- /* start */
- /* add x coordinates */
- start = params.startx;
-
- if (stagger > 0)
- start |= 1;
-
- /* compute correct pixels number */
- used_pixels = (params.pixels * optical_res) / params.xres;
- DBG (DBG_info, "%s: used_pixels=%d\n", __func__, used_pixels);
-
- /* round up pixels number if needed */
- if (used_pixels * params.xres < params.pixels * optical_res)
- used_pixels++;
-
- /* we want even number of pixels here */
- if(used_pixels & 1)
- used_pixels++;
-
- /* slope_dpi */
- /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */
- if (dev->model->is_cis)
- slope_dpi = params.yres * params.channels;
- else
- slope_dpi = params.yres;
-
- /* scan_step_type */
- if(params.flags & SCAN_FLAG_FEEDING)
- {
- scan_step_type=0;
- exposure_time=MOVE_EXPOSURE;
- }
- else
- {
- exposure_time = gl124_compute_exposure (dev, used_res, half_ccd);
- scan_step_type = sanei_genesys_compute_step_type(motors, dev->model->motor_type, exposure_time);
- }
-
- DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
- DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, scan_step_type);
-
- /*** optical parameters ***/
- /* in case of dynamic lineart, we use an internal 8 bit gray scan
- * to generate 1 lineart data */
- if (params.flags & SCAN_FLAG_DYNAMIC_LINEART) {
- params.depth = 8;
- }
-
- /* we enable true gray for cis scanners only, and just when doing
- * scan since color calibration is OK for this mode
- */
- oflags = 0;
- if (params.flags & SCAN_FLAG_DISABLE_SHADING)
- oflags |= OPTICAL_FLAG_DISABLE_SHADING;
- if (params.flags & SCAN_FLAG_DISABLE_GAMMA)
- oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
- if (params.flags & SCAN_FLAG_DISABLE_LAMP)
- oflags |= OPTICAL_FLAG_DISABLE_LAMP;
- if (params.flags & SCAN_FLAG_CALIBRATION)
- oflags |= OPTICAL_FLAG_DISABLE_DOUBLE;
-
- if (dev->model->is_cis && dev->settings.true_gray)
- {
- oflags |= OPTICAL_FLAG_ENABLE_LEDADD;
- }
-
- /* now _LOGICAL_ optical values used are known, setup registers */
- status = gl124_init_optical_regs_scan (dev,
- sensor,
- reg,
- exposure_time,
- used_res,
- start,
- used_pixels,
- params.channels,
- params.depth,
- half_ccd,
- params.color_filter,
- oflags);
- if (status != SANE_STATUS_GOOD)
- return status;
-
- /*** motor parameters ***/
-
- /* max_shift */
- max_shift=sanei_genesys_compute_max_shift(dev,params.channels,params.yres,params.flags);
-
- /* lines to scan */
- lincnt = params.lines + max_shift + stagger;
-
- /* add tl_y to base movement */
- move = params.starty;
- DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
-
- mflags=0;
- if(params.flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
- mflags|=MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
- if(params.flags & SCAN_FLAG_FEEDING)
- mflags|=MOTOR_FLAG_FEED;
-
- status = gl124_init_motor_regs_scan (dev, sensor,
- reg,
- exposure_time,
- slope_dpi,
- scan_step_type,
- dev->model->is_cis ? lincnt * params.channels : lincnt,
- dummy,
- move,
- params.scan_mode,
- mflags);
- if (status != SANE_STATUS_GOOD)
- return status;
-
- /*** prepares data reordering ***/
-
- /* words_per_line */
- bytes_per_line = (used_pixels * used_res) / optical_res;
- bytes_per_line = (bytes_per_line * params.channels * params.depth) / 8;
-
- /* since we don't have sheetfed scanners to handle,
- * use huge read buffer */
- /* TODO find the best size according to settings */
- requested_buffer_size = 16 * bytes_per_line;
-
- read_buffer_size =
- 2 * requested_buffer_size +
- ((max_shift + stagger) * used_pixels * params.channels * params.depth) / 8;
-
- dev->read_buffer.clear();
- dev->read_buffer.alloc(read_buffer_size);
-
- dev->lines_buffer.clear();
- dev->lines_buffer.alloc(read_buffer_size);
-
- dev->shrink_buffer.clear();
- dev->shrink_buffer.alloc(requested_buffer_size);
-
- dev->out_buffer.clear();
- dev->out_buffer.alloc((8 * dev->settings.pixels * params.channels * params.depth) / 8);
-
- dev->read_bytes_left = bytes_per_line * lincnt;
-
- DBG(DBG_info, "%s: physical bytes to read = %lu\n", __func__, (u_long) dev->read_bytes_left);
- dev->read_active = SANE_TRUE;
-
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
- DBG(DBG_info, "%s: current_setup.pixels=%d\n", __func__, dev->current_setup.pixels);
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
- dev->total_bytes_read = 0;
- if (params.depth == 1)
- dev->total_bytes_to_read =
- ((dev->settings.pixels * dev->settings.lines) / 8 +
- (((dev->settings.pixels * dev->settings.lines) % 8) ? 1 : 0)) *
- params.channels;
- else
- dev->total_bytes_to_read =
- dev->settings.pixels * dev->settings.lines * params.channels * (params.depth / 8);
-
- DBG(DBG_info, "%s: total bytes to send = %lu\n", __func__, (u_long) dev->total_bytes_to_read);
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static void
-gl124_calculate_current_setup (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int depth;
- int start;
-
- int used_res;
- int used_pixels;
- unsigned int lincnt;
- int exposure_time;
- int stagger;
- SANE_Bool half_ccd;
-
- int max_shift, dpihw;
-
- int optical_res;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
- /* start */
- start = SANE_UNFIX (dev->model->x_offset);
- start += dev->settings.tl_x;
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start;
- params.starty = 0; // not used
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = 0;
-
- half_ccd=compute_half_ccd(sensor, params.xres);
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
- /* optical_res */
- optical_res = sensor.optical_res;
-
- if (params.xres <= (unsigned) optical_res)
- used_res = params.xres;
- else
- used_res=optical_res;
-
- /* compute scan parameters values */
- /* pixels are allways given at half or full CCD optical resolution */
- /* use detected left margin and fixed value */
-
- /* compute correct pixels number */
- used_pixels = (params.pixels * optical_res) / params.xres;
- DBG (DBG_info, "%s: used_pixels=%d\n", __func__, used_pixels);
-
- /* exposure */
- exposure_time = gl124_compute_exposure (dev, params.xres, half_ccd);
- DBG (DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
-
- /* max_shift */
- max_shift=sanei_genesys_compute_max_shift(dev, params.channels, params.yres, 0);
-
- /* compute hw dpi for sensor */
- dpihw=sanei_genesys_compute_dpihw(dev, sensor,used_res);
-
- Sensor_Profile* sensor_profile = get_sensor_profile(dev->model->ccd_type, dpihw, half_ccd);
- dev->segnb=sensor_profile->reg98 & 0x0f;
-
- /* stagger */
- if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG (DBG_info, "%s: stagger=%d lines\n", __func__, stagger);
-
- /* lincnt */
- lincnt = params.lines + max_shift + stagger;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
- DBG (DBG_info, "%s: current_setup.pixels=%d\n", __func__, dev->current_setup.pixels);
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
- DBGCOMPLETED;
-}
-
-/**
- * for fast power saving methods only, like disabling certain amplifiers
- * @param dev device to use
- * @param enable true to set inot powersaving
- * */
-static SANE_Status
-gl124_save_power (Genesys_Device * dev, SANE_Bool enable)
-{
- DBG(DBG_proc, "%s: enable = %d\n", __func__, enable);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl124_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
-{
- GenesysRegister *r;
-
- DBG(DBG_proc, "%s (delay = %d)\n", __func__, delay);
-
- r = sanei_genesys_get_address (&dev->reg, REG03);
- r->value &= ~0xf0;
- if(delay<15)
- {
- r->value |= delay;
- }
- else
- {
- r->value |= 0x0f;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl124_start_action (Genesys_Device * dev)
-{
- return sanei_genesys_write_register (dev, 0x0f, 0x01);
-}
-
-static SANE_Status
-gl124_stop_action (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val40, val;
- unsigned int loop;
-
- DBGSTART;
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl124_homsnr_gpio(dev);
-
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- status = sanei_genesys_read_register (dev, REG100, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read reg100: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* only stop action if needed */
- if (!(val40 & REG100_DATAENB) && !(val40 & REG100_MOTMFLG))
- {
- DBG (DBG_info, "%s: already stopped\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- /* ends scan */
- val = dev->reg.get8(REG01);
- val &= ~REG01_SCAN;
- dev->reg.set8(REG01, val);
- status = sanei_genesys_write_register (dev, REG01, val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to write register 01: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
- sanei_genesys_sleep_ms(100);
-
- loop = 10;
- while (loop > 0)
- {
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- status = sanei_genesys_read_register (dev, REG100, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus (status));
- DBGCOMPLETED;
- return status;
- }
-
- /* if scanner is in command mode, we are done */
- if (!(val40 & REG100_DATAENB) && !(val40 & REG100_MOTMFLG)
- && !(val & MOTORENB))
- {
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- sanei_genesys_sleep_ms(100);
- loop--;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_IO_ERROR;
-}
-
-
-/** @brief setup GPIOs for scan
- * Setup GPIO values to drive motor (or light) needed for the
- * target resolution
- * @param *dev device to set up
- * @param resolution dpi of the target scan
- * @return SANE_STATUS_GOOD unless REG32 cannot be read
- */
-static SANE_Status
-gl124_setup_scan_gpio(Genesys_Device *dev, int resolution)
-{
-SANE_Status status = SANE_STATUS_GOOD;
-uint8_t val;
-
- DBGSTART;
- RIE (sanei_genesys_read_register (dev, REG32, &val));
-
- /* LiDE 110, 210 and 220 cases */
- if(dev->model->gpo_type != GPO_CANONLIDE120)
- {
- if(resolution>=dev->motor.base_ydpi/2)
- {
- val &= 0xf7;
- }
- else if(resolution>=dev->motor.base_ydpi/4)
- {
- val &= 0xef;
- }
- else
- {
- val |= 0x10;
- }
- }
- /* 120 : <=300 => 0x53 */
- else
- { /* base_ydpi is 4800 */
- if(resolution<=300)
- {
- val &= 0xf7;
- }
- else if(resolution<=600)
- {
- val |= 0x08;
- }
- else if(resolution<=1200)
- {
- val &= 0xef;
- val |= 0x08;
- }
- else
- {
- val &= 0xf7;
- }
- }
- val |= 0x02;
- RIE (sanei_genesys_write_register (dev, REG32, val));
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* Send the low-level scan command */
-/* todo : is this that useful ? */
-static SANE_Status
-gl124_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SANE_Bool start_motor)
-{
- (void) sensor;
-
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- DBGSTART;
- if (reg == NULL)
- return SANE_STATUS_INVAL;
-
- /* set up GPIO for scan */
- RIE(gl124_setup_scan_gpio(dev,dev->settings.yres));
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
-
- /* enable scan and motor */
- RIE (sanei_genesys_read_register (dev, REG01, &val));
- val |= REG01_SCAN;
- RIE (sanei_genesys_write_register (dev, REG01, val));
-
- if (start_motor)
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 1));
- }
- else
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 0));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/* Send the stop scan command */
-static SANE_Status
-gl124_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (check_stop = %d)\n", __func__, check_stop);
- if (reg == NULL)
- return SANE_STATUS_INVAL;
-
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- status = SANE_STATUS_GOOD;
- }
- else /* flat bed scanners */
- {
- status = gl124_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/** rewind scan
- * Move back by the same amount of distance than previous scan.
- * @param dev device to rewind
- * @returns SANE_STATUS_GOOD on success
- */
-static
-SANE_Status gl124_rewind(Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t byte;
-
- DBGSTART;
-
- /* set motor reverse */
- RIE (sanei_genesys_read_register (dev, 0x02, &byte));
- byte |= 0x04;
- RIE (sanei_genesys_write_register(dev, 0x02, byte));
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- /* and start scan, then wait completion */
- RIE (gl124_begin_scan (dev, sensor, &dev->reg, SANE_TRUE));
- do
- {
- sanei_genesys_sleep_ms(100);
- RIE (sanei_genesys_read_register (dev, REG100, &byte));
- }
- while(byte & REG100_MOTMFLG);
- RIE (gl124_end_scan (dev, &dev->reg, SANE_TRUE));
-
- /* restore direction */
- RIE (sanei_genesys_read_register (dev, 0x02, &byte));
- byte &= 0xfb;
- RIE (sanei_genesys_write_register(dev, 0x02, byte));
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/** Park head
- * Moves the slider to the home (top) position slowly
- * @param dev device to park
- * @param wait_until_home true to make the function waiting for head
- * to be home before returning, if fals returne immediately
- * @returns SANE_STATUS_GOO on success */
-static
-SANE_Status
-gl124_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- uint8_t val;
- float resolution;
- int loop = 0;
-
- DBG(DBG_proc, "%s (wait_until_home = %d)\n", __func__, wait_until_home);
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl124_homsnr_gpio(dev);
-
- /* first read gives HOME_SENSOR true */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- sanei_genesys_sleep_ms(100);
-
- /* second is reliable */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- /* is sensor at home? */
- if (val & HOMESNR)
- {
- DBG (DBG_info, "%s: already at home, completed\n", __func__);
- dev->scanhead_position_in_steps = 0;
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- /* feed a little first */
- if (dev->model->model_id == MODEL_CANON_LIDE_210)
- {
- status = gl124_feed (dev, 20, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to do initial feed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- local_reg = dev->reg;
- resolution=sanei_genesys_get_lowest_dpi(dev);
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 100;
- params.starty = 30000;
- params.pixels = 100;
- params.lines = 100;
- params.depth = 8;
- params.channels = 1;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
-
- /* set up for reverse and no scan */
- r = sanei_genesys_get_address(&local_reg, REG02);
- r->value |= REG02_MTRREV;
-
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- RIE(gl124_setup_scan_gpio(dev,resolution));
-
- try {
- status = gl124_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl124_stop_action (dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl124_stop_action (dev);
- } catch (...) {}
- /* restore original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl124_homsnr_gpio(dev);
-
- if (wait_until_home)
- {
-
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- if (val & HOMESNR) /* home sensor */
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- DBGCOMPLETED;
- dev->scanhead_position_in_steps = 0;
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl124_stop_action (dev);
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief moves the slider to steps at motor base dpi
- * @param dev device to work on
- * @param steps number of steps to move
- * @param reverse true is moving backward
- * */
-static SANE_Status
-gl124_feed (Genesys_Device * dev, unsigned int steps, int reverse)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- float resolution;
- uint8_t val;
-
- DBGSTART;
- DBG (DBG_io, "%s: steps=%d\n", __func__, steps);
-
- /* prepare local registers */
- local_reg = dev->reg;
-
- resolution=sanei_genesys_get_lowest_ydpi(dev);
- const auto& sensor = sanei_genesys_find_sensor(dev, resolution);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = steps;
- params.pixels = 100;
- params.lines = 3;
- params.depth = 8;
- params.channels = 3;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_FEEDING |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus (status));
- DBGCOMPLETED;
- return status;
- }
-
- /* set exposure to zero */
- sanei_genesys_set_triple(&local_reg,REG_EXPR,0);
- sanei_genesys_set_triple(&local_reg,REG_EXPG,0);
- sanei_genesys_set_triple(&local_reg,REG_EXPB,0);
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
-
- /* set up for no scan */
- r = sanei_genesys_get_address (&local_reg, REG01);
- r->value &= ~REG01_SCAN;
-
- /* set up for reverse if needed */
- if(reverse)
- {
- r = sanei_genesys_get_address (&local_reg, REG02);
- r->value |= REG02_MTRREV;
- }
-
- /* send registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- status = gl124_start_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus (status));
- gl124_stop_action (dev);
-
- /* restore original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- /* wait until feed count reaches the required value, but do not
- * exceed 30s */
- do
- {
- status = sanei_genesys_get_status (dev, &val);
- }
- while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
-
- /* then stop scanning */
- RIE(gl124_stop_action (dev));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
- area at 600 dpi from very top of scanner */
-static SANE_Status
-gl124_search_start_position (Genesys_Device * dev)
-{
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg = dev->reg;
- int steps;
-
- int pixels = 600;
- int dpi = 300;
-
- DBGSTART;
-
- /* sets for a 200 lines * 600 pixels */
- /* normal scan with no shading */
-
- // FIXME: the current approach of doing search only for one resolution does not work on scanners
- // whith employ different sensors with potentially different settings.
- auto& sensor = sanei_genesys_find_sensor_for_write(dev, dpi);
-
- SetupParams params;
- params.xres = dpi;
- params.yres = dpi;
- params.startx = 0;
- params.starty = 0; /*we should give a small offset here~60 steps */
- params.pixels = 600;
- params.lines = dev->model->search_lines;
- params.depth = 8;
- params.channels = 1;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::GREEN;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE;
-
- status = gl124_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status!=SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to init scan registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* send to scanner */
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- size = pixels * dev->model->search_lines;
-
- std::vector<uint8_t> data(size);
-
- status = gl124_begin_scan (dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner (dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl124_search_position.pnm", data.data(), 8, 1, pixels,
- dev->model->search_lines);
-
- status = gl124_end_scan (dev, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* update regs to copy ASIC internal state */
- dev->reg = local_reg;
-
- status =
- sanei_genesys_search_reference_point (dev, sensor, data.data(), 0, dpi, pixels,
- dev->model->search_lines);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set search reference point: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/*
- * sets up register for coarse gain calibration
- * todo: check it for scanners using it */
-static SANE_Status
-gl124_init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t channels;
- uint8_t cksel;
-
- DBGSTART;
- cksel = (regs.find_reg(0x18).value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
-
- /* set line size */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) {
- channels = 3;
- } else {
- channels = 1;
- }
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = sensor.optical_res / cksel;
- params.lines = 20;
- params.depth = 16;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_FEEDING |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_set_motor_power(regs, false);
-
- DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
- sensor.optical_res / cksel, dev->settings.xres);
-
- status = dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* init registers for shading calibration */
-/* shading calibration is done at dpihw */
-static SANE_Status
-gl124_init_regs_for_shading(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int move, resolution, dpihw, factor;
-
- DBGSTART;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- dev->calib_channels = 3;
- dev->calib_lines = dev->model->shading_lines;
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dev->settings.xres);
- if(dpihw>=2400)
- {
- dev->calib_lines *= 2;
- }
- resolution=dpihw;
-
- /* if half CCD mode, use half resolution */
- if(compute_half_ccd(sensor, dev->settings.xres)==SANE_TRUE)
- {
- resolution /= 2;
- dev->calib_lines /= 2;
- }
- dev->calib_resolution = resolution;
- dev->calib_total_bytes_to_read = 0;
- factor=sensor.optical_res/resolution;
- dev->calib_pixels = sensor.sensor_pixels/factor;
-
- /* distance to move to reach white target at high resolution */
- move=0;
- if(dev->settings.yres>=1200)
- {
- move = SANE_UNFIX (dev->model->y_offset_calib);
- move = (move * (dev->motor.base_ydpi/4)) / MM_PER_INCH;
- }
- DBG (DBG_io, "%s: move=%d steps\n", __func__, move);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = move;
- params.pixels = dev->calib_pixels;
- params.lines = dev->calib_lines;
- params.depth = 16;
- params.channels = dev->calib_channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, &regs, params);
-
- sanei_genesys_set_motor_power(regs, false);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to setup scan: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- dev->scanhead_position_in_steps += dev->calib_lines + move;
-
- status = dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to bulk write registers: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static void gl124_wait_for_motor_stop(Genesys_Device* dev)
-{
- DBG_HELPER(dbg);
- uint8_t val40, val;
-
- TIE(sanei_genesys_get_status(dev, &val));
- TIE(sanei_genesys_read_register(dev, REG100, &val40));
-
- if ((val & MOTORENB) == 0 && (val40 & REG100_MOTMFLG) == 0)
- return;
-
- do {
- sanei_genesys_sleep_ms(10);
- TIE(sanei_genesys_get_status(dev, &val));
- TIE(sanei_genesys_read_register(dev, REG100, &val40));
- } while ((val & MOTORENB) ||(val40 & REG100_MOTMFLG));
- sanei_genesys_sleep_ms(50);
-}
-
-/** @brief set up registers for the actual scan
- */
-static SANE_Status
-gl124_init_regs_for_scan (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int flags;
- int depth;
- float move;
- int move_dpi;
- float start;
-
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
- /* y (motor) distance to move to reach scanned area */
- move_dpi = dev->motor.base_ydpi/4;
- move = SANE_UNFIX (dev->model->y_offset);
- move += dev->settings.tl_y;
- move = (move * move_dpi) / MM_PER_INCH;
- DBG (DBG_info, "%s: move=%f steps\n", __func__, move);
-
- if(channels*dev->settings.yres>=600 && move>700)
- {
- status = gl124_feed (dev, move-500, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to move to scan area\n",__func__);
- return status;
- }
- move=500;
- }
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- /* start */
- start = SANE_UNFIX (dev->model->x_offset);
- start += dev->settings.tl_x;
- if(compute_half_ccd(sensor, dev->settings.xres)==SANE_TRUE)
- {
- start /=2;
- }
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- flags = 0;
-
- /* enable emulated lineart from gray data */
- if(dev->settings.scan_mode == ScanColorMode::LINEART
- && dev->settings.dynamic_lineart)
- {
- flags |= SCAN_FLAG_DYNAMIC_LINEART;
- }
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start;
- params.starty = move;
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = flags;
-
- status = gl124_init_scan_regs(dev, sensor, &dev->reg, params);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/**
- * Send shading calibration data. The buffer is considered to always hold values
- * for all the channels.
- */
-static SANE_Status
-gl124_send_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data, int size)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint32_t addr, length, strpixel ,endpixel, x, factor, segcnt, pixels, i;
- uint32_t lines, channels;
- uint16_t dpiset,dpihw;
- uint8_t val,*ptr,*src;
-
- DBGSTART;
- DBG( DBG_io2, "%s: writing %d bytes of shading data\n",__func__,size);
-
- /* logical size of a color as seen by generic code of the frontend */
- length = (uint32_t) (size / 3);
- sanei_genesys_get_triple(&dev->reg,REG_STRPIXEL,&strpixel);
- sanei_genesys_get_triple(&dev->reg,REG_ENDPIXEL,&endpixel);
- sanei_genesys_get_triple(&dev->reg,REG_SEGCNT,&segcnt);
- if(endpixel==0)
- {
- endpixel=segcnt;
- }
- DBG( DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d, PIXELS=%d, SEGCNT=%d\n",__func__,strpixel,endpixel,endpixel-strpixel,segcnt);
-
- /* compute deletion factor */
- sanei_genesys_get_double(&dev->reg,REG_DPISET,&dpiset);
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dpiset);
- factor=dpihw/dpiset;
- DBG( DBG_io2, "%s: factor=%d\n",__func__,factor);
-
- /* binary data logging */
- if(DBG_LEVEL>=DBG_data)
- {
- dev->binary=fopen("binary.pnm","wb");
- sanei_genesys_get_triple(&dev->reg, REG_LINCNT, &lines);
- channels=dev->current_setup.channels;
- if(dev->binary!=NULL)
- {
- fprintf(dev->binary,"P5\n%d %d\n%d\n",(endpixel-strpixel)/factor*channels*dev->segnb,lines/channels,255);
- }
- }
-
- /* turn pixel value into bytes 2x16 bits words */
- strpixel*=2*2; /* 2 words of 2 bytes */
- endpixel*=2*2;
- segcnt*=2*2;
- pixels=endpixel-strpixel;
-
- DBG( DBG_io2, "%s: using chunks of %d bytes (%d shading data pixels)\n",__func__,length, length/4);
- std::vector<uint8_t> buffer(pixels * dev->segnb, 0);
-
- /* write actual red data */
- for(i=0;i<3;i++)
- {
- /* copy data to work buffer and process it */
- /* coefficent destination */
- ptr = buffer.data();
-
- /* iterate on both sensor segment */
- for(x=0;x<pixels;x+=4*factor)
- {
- /* coefficient source */
- src=data+x+strpixel+i*length;
-
- /* iterate over all the segments */
- switch(dev->segnb)
- {
- case 1:
- ptr[0+pixels*0]=src[0+segcnt*0];
- ptr[1+pixels*0]=src[1+segcnt*0];
- ptr[2+pixels*0]=src[2+segcnt*0];
- ptr[3+pixels*0]=src[3+segcnt*0];
- break;
- case 2:
- ptr[0+pixels*0]=src[0+segcnt*0];
- ptr[1+pixels*0]=src[1+segcnt*0];
- ptr[2+pixels*0]=src[2+segcnt*0];
- ptr[3+pixels*0]=src[3+segcnt*0];
- ptr[0+pixels*1]=src[0+segcnt*1];
- ptr[1+pixels*1]=src[1+segcnt*1];
- ptr[2+pixels*1]=src[2+segcnt*1];
- ptr[3+pixels*1]=src[3+segcnt*1];
- break;
- case 4:
- ptr[0+pixels*0]=src[0+segcnt*0];
- ptr[1+pixels*0]=src[1+segcnt*0];
- ptr[2+pixels*0]=src[2+segcnt*0];
- ptr[3+pixels*0]=src[3+segcnt*0];
- ptr[0+pixels*1]=src[0+segcnt*2];
- ptr[1+pixels*1]=src[1+segcnt*2];
- ptr[2+pixels*1]=src[2+segcnt*2];
- ptr[3+pixels*1]=src[3+segcnt*2];
- ptr[0+pixels*2]=src[0+segcnt*1];
- ptr[1+pixels*2]=src[1+segcnt*1];
- ptr[2+pixels*2]=src[2+segcnt*1];
- ptr[3+pixels*2]=src[3+segcnt*1];
- ptr[0+pixels*3]=src[0+segcnt*3];
- ptr[1+pixels*3]=src[1+segcnt*3];
- ptr[2+pixels*3]=src[2+segcnt*3];
- ptr[3+pixels*3]=src[3+segcnt*3];
- break;
- }
-
- /* next shading coefficient */
- ptr+=4;
- }
- RIE (sanei_genesys_read_register (dev, 0xd0+i, &val));
- addr = val * 8192 + 0x10000000;
- status = sanei_genesys_write_ahb(dev, addr, pixels*dev->segnb, buffer.data());
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s; write to AHB failed (%s)\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
-
- return status;
-}
-
-
-/** @brief move to calibration area
- * This functions moves scanning head to calibration area
- * by doing a 600 dpi scan
- * @param dev scanner device
- * @return SANE_STATUS_GOOD on success, else the error code
- */
-static SANE_Status
-move_to_calibration_area (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- int pixels;
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
-
- pixels = (sensor.sensor_pixels*600)/sensor.optical_res;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- SetupParams params;
- params.xres = 600;
- params.yres = 600;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = 1;
- params.depth = 8;
- params.channels = 3;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus (status));
- return status;
- }
-
- size = pixels * 3;
- std::vector<uint8_t> line(size);
-
- /* write registers and scan data */
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- DBG (DBG_info, "%s: starting line reading\n", __func__);
- RIE(gl124_begin_scan (dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), size));
-
- /* stop scanning */
- RIE(gl124_stop_action (dev));
-
- if (DBG_LEVEL >= DBG_data)
- {
- sanei_genesys_write_pnm_file("gl124_movetocalarea.pnm", line.data(), 8, 3, pixels, 1);
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/* this function does the led calibration by scanning one line of the calibration
- area below scanner's top on white strip.
-
--needs working coarse/gain
-*/
-static SANE_Status
-gl124_led_calibration (Genesys_Device * dev, Genesys_Sensor& sensor, Genesys_Register_Set& regs)
-{
- int num_pixels;
- int total_size;
- int resolution;
- int dpihw;
- int i, j;
- SANE_Status status = SANE_STATUS_GOOD;
- int val;
- int channels, depth;
- int avg[3];
- int turn;
- uint16_t exp[3],target;
- SANE_Bool acceptable;
- SANE_Bool half_ccd;
-
- DBGSTART;
-
- /* move to calibration area */
- move_to_calibration_area(dev, sensor, regs);
-
- /* offset calibration is always done in 16 bit depth color mode */
- channels = 3;
- depth=16;
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dev->settings.xres);
- half_ccd=compute_half_ccd(sensor, dev->settings.xres);
- if(half_ccd==SANE_TRUE)
- {
- resolution = dpihw/2;
- }
- else
- {
- resolution = dpihw;
- }
- Sensor_Profile* sensor_profile = get_sensor_profile(dev->model->ccd_type, dpihw, half_ccd);
- num_pixels = (sensor.sensor_pixels*resolution)/sensor.optical_res;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = 0;
- params.pixels = num_pixels;
- params.lines = 1;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus (status));
- return status;
- }
-
- total_size = num_pixels * channels * (depth/8) * 1; /* colors * bytes_per_color * scan lines */
- std::vector<uint8_t> line(total_size);
-
- /* initial loop values and boundaries */
- exp[0]=sensor_profile->expr;
- exp[1]=sensor_profile->expg;
- exp[2]=sensor_profile->expb;
- target=sensor.gain_white_ref*256;
-
- turn = 0;
-
- /* no move during led calibration */
- sanei_genesys_set_motor_power(regs, false);
- do
- {
- /* set up exposure */
- sanei_genesys_set_triple(&regs,REG_EXPR,exp[0]);
- sanei_genesys_set_triple(&regs,REG_EXPG,exp[1]);
- sanei_genesys_set_triple(&regs,REG_EXPB,exp[2]);
-
- /* write registers and scan data */
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- DBG(DBG_info, "%s: starting line reading\n", __func__);
- RIE(gl124_begin_scan (dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, line.data(), total_size));
-
- /* stop scanning */
- RIE(gl124_stop_action (dev));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl124_led_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, line.data(), depth, channels, num_pixels, 1);
- }
-
- /* compute average */
- for (j = 0; j < channels; j++)
- {
- avg[j] = 0;
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- line[i * 2 + j * 2 * num_pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- avg[j] += val;
- }
-
- avg[j] /= num_pixels;
- }
-
- DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
-
- /* check if exposure gives average within the boundaries */
- acceptable = SANE_TRUE;
- for(i=0;i<3;i++)
- {
- /* we accept +- 2% delta from target */
- if(abs(avg[i]-target)>target/50)
- {
- exp[i]=(exp[i]*target)/avg[i];
- acceptable = SANE_FALSE;
- }
- }
-
- turn++;
- }
- while (!acceptable && turn < 100);
-
- DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
-
- /* set these values as final ones for scan */
- sanei_genesys_set_triple(&dev->reg,REG_EXPR,exp[0]);
- sanei_genesys_set_triple(&dev->reg,REG_EXPG,exp[1]);
- sanei_genesys_set_triple(&dev->reg,REG_EXPB,exp[2]);
-
- /* store in this struct since it is the one used by cache calibration */
- sensor.exposure.red = exp[0];
- sensor.exposure.green = exp[1];
- sensor.exposure.blue = exp[2];
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * average dark pixels of a 8 bits scan
- */
-static int
-dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
- unsigned int channels, unsigned int black)
-{
- unsigned int i, j, k, average, count;
- unsigned int avg[3];
- uint8_t val;
-
- /* computes average value on black margin */
- for (k = 0; k < channels; k++)
- {
- avg[k] = 0;
- count = 0;
- for (i = 0; i < lines; i++)
- {
- for (j = 0; j < black; j++)
- {
- val = data[i * channels * pixels + j + k];
- avg[k] += val;
- count++;
- }
- }
- if (count)
- avg[k] /= count;
- DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
- }
- average = 0;
- for (i = 0; i < channels; i++)
- average += avg[i];
- average /= channels;
- DBG(DBG_info, "%s: average = %d\n", __func__, average);
- return average;
-}
-
-
-static SANE_Status
-gl124_offset_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t reg0a;
- unsigned int channels, bpp;
- int pass = 0, avg, total_size;
- int topavg, bottomavg, resolution, lines;
- int top, bottom, black_pixels, pixels;
-
- DBGSTART;
-
- /* no gain nor offset for TI AFE */
- RIE (sanei_genesys_read_register (dev, REG0A, &reg0a));
- if(((reg0a & REG0A_SIFSEL)>>REG0AS_SIFSEL)==3)
- {
- DBGCOMPLETED;
- return status;
- }
-
- /* offset calibration is always done in color mode */
- channels = 3;
- resolution=sensor.optical_res;
- dev->calib_pixels = sensor.sensor_pixels;
- lines=1;
- bpp=8;
- pixels= (sensor.sensor_pixels*resolution) / sensor.optical_res;
- black_pixels = (sensor.black_pixels * resolution) / sensor.optical_res;
- DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = bpp;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_set_motor_power(regs, false);
-
- /* allocate memory for scans */
- total_size = pixels * channels * lines * (bpp/8); /* colors * bytes_per_color * scan lines */
-
- std::vector<uint8_t> first_line(total_size);
- std::vector<uint8_t> second_line(total_size);
-
- /* init gain */
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
-
- /* scan with no move */
- bottom = 10;
- dev->frontend.set_offset(0, bottom);
- dev->frontend.set_offset(1, bottom);
- dev->frontend.set_offset(2, bottom);
-
- RIE(gl124_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting first line reading\n", __func__);
- RIE(gl124_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size));
- if (DBG_LEVEL >= DBG_data)
- {
- char title[30];
- snprintf(title, 30, "gl124_offset%03d.pnm", bottom);
- sanei_genesys_write_pnm_file(title, first_line.data(), bpp, channels, pixels, lines);
- }
-
- bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
-
- /* now top value */
- top = 255;
- dev->frontend.set_offset(0, top);
- dev->frontend.set_offset(1, top);
- dev->frontend.set_offset(2, top);
- RIE(gl124_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl124_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, second_line.data(), total_size));
-
- topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
-
- /* loop until acceptable level */
- while ((pass < 32) && (top - bottom > 1))
- {
- pass++;
-
- /* settings for new scan */
- dev->frontend.set_offset(0, (top + bottom) / 2);
- dev->frontend.set_offset(1, (top + bottom) / 2);
- dev->frontend.set_offset(2, (top + bottom) / 2);
-
- /* scan with no move */
- RIE(gl124_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl124_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char title[30];
- snprintf(title, 30, "gl124_offset%03d.pnm", dev->frontend.get_offset(1));
- sanei_genesys_write_pnm_file(title, second_line.data(), bpp, channels, pixels, lines);
- }
-
- avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
-
- /* compute new boundaries */
- if (topavg == avg)
- {
- topavg = avg;
- top = dev->frontend.get_offset(1);
- }
- else
- {
- bottomavg = avg;
- bottom = dev->frontend.get_offset(1);
- }
- }
- DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* alternative coarse gain calibration
- this on uses the settings from offset_calibration and
- uses only one scanline
- */
-/*
- with offset and coarse calibration we only want to get our input range into
- a reasonable shape. the fine calibration of the upper and lower bounds will
- be done with shading.
- */
-static SANE_Status
-gl124_coarse_gain_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi)
-{
- int pixels;
- int total_size;
- uint8_t reg0a;
- int i, j, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- int max[3];
- float gain[3],coeff;
- int val, code, lines;
- int resolution;
- int bpp;
-
- DBG(DBG_proc, "%s: dpi = %d\n", __func__, dpi);
-
- /* no gain nor offset for TI AFE */
- RIE (sanei_genesys_read_register (dev, REG0A, &reg0a));
- if(((reg0a & REG0A_SIFSEL)>>REG0AS_SIFSEL)==3)
- {
- DBGCOMPLETED;
- return status;
- }
-
- /* coarse gain calibration is always done in color mode */
- channels = 3;
-
- /* follow CKSEL */
- if(dev->settings.xres<sensor.optical_res)
- {
- coeff=0.9;
- /*resolution=sensor.optical_res/2;*/
- resolution=sensor.optical_res;
- }
- else
- {
- resolution=sensor.optical_res;
- coeff=1.0;
- }
- lines=10;
- bpp=8;
- pixels = (sensor.sensor_pixels * resolution) / sensor.optical_res;
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = bpp;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- try {
- status = gl124_init_scan_regs(dev, sensor, &regs, params);
- } catch (...) {
- try {
- sanei_genesys_set_motor_power(regs, false);
- } catch (...) {}
- throw;
- }
-
- sanei_genesys_set_motor_power(regs, false);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- RIE (dev->model->cmd_set->bulk_write_register(dev, regs));
-
- total_size = pixels * channels * (16/bpp) * lines;
-
- std::vector<uint8_t> line(total_size);
-
- RIE(gl124_set_fe(dev, sensor, AFE_SET));
- RIE(gl124_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl124_gain.pnm", line.data(), bpp, channels, pixels, lines);
-
- /* average value on each channel */
- for (j = 0; j < channels; j++)
- {
- max[j] = 0;
- for (i = pixels/4; i < (pixels*3/4); i++)
- {
- if(bpp==16)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * pixels + 1] * 256 +
- line[i * 2 + j * 2 * pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- }
- else
- {
- if (dev->model->is_cis)
- val = line[i + j * pixels];
- else
- val = line[i * channels + j];
- }
-
- max[j] += val;
- }
- max[j] = max[j] / (pixels/2);
-
- gain[j] = ((float) sensor.gain_white_ref*coeff) / max[j];
-
- /* turn logical gain value into gain code, checking for overflow */
- code = 283 - 208 / gain[j];
- if (code > 255)
- code = 255;
- else if (code < 0)
- code = 0;
- dev->frontend.set_gain(j, code);
-
- DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j],
- gain[j], dev->frontend.get_gain(j));
- }
-
- if (dev->model->is_cis) {
- uint8_t gain0 = dev->frontend.get_gain(0);
- if (gain0 > dev->frontend.get_gain(1)) {
- gain0 = dev->frontend.get_gain(1);
- }
- if (gain0 > dev->frontend.get_gain(2)) {
- gain0 = dev->frontend.get_gain(2);
- }
- dev->frontend.set_gain(0, gain0);
- dev->frontend.set_gain(1, gain0);
- dev->frontend.set_gain(2, gain0);
- }
-
- if (channels == 1) {
- dev->frontend.set_gain(0, dev->frontend.get_gain(1));
- dev->frontend.set_gain(2, dev->frontend.get_gain(1));
- }
-
- RIE (gl124_stop_action (dev));
-
- status = gl124_slow_back_home (dev, SANE_TRUE);
-
- DBGCOMPLETED;
- return status;
-}
-
-/*
- * wait for lamp warmup by scanning the same line until difference
- * between 2 scans is below a threshold
- */
-static SANE_Status
-gl124_init_regs_for_warmup (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- int *channels, int *total_size)
-{
- int num_pixels;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
- if (dev == NULL || reg == NULL || channels == NULL || total_size == NULL)
- return SANE_STATUS_INVAL;
-
- *channels=3;
-
- *reg = dev->reg;
-
- SetupParams params;
- params.xres = sensor.optical_res;
- params.yres = dev->motor.base_ydpi;
- params.startx = sensor.sensor_pixels / 4;
- params.starty = 0;
- params.pixels = sensor.sensor_pixels / 2;
- params.lines = 1;
- params.depth = 8;
- params.channels = *channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl124_init_scan_regs(dev, sensor, reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- num_pixels = dev->current_setup.pixels;
-
- *total_size = num_pixels * 3 * 1; /* colors * bytes_per_color * scan lines */
-
- sanei_genesys_set_motor_power(*reg, false);
- RIE (dev->model->cmd_set->bulk_write_register(dev, *reg));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief default GPIO values
- * set up GPIO/GPOE for idle state
- * @param dev device to set up
- * @return SANE_STATUS_GOOD unless a GPIO register cannot be written
- */
-static SANE_Status
-gl124_init_gpio (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int idx;
-
- DBGSTART;
-
- /* per model GPIO layout */
- if (dev->model->model_id == MODEL_CANON_LIDE_110)
- {
- idx = 0;
- }
- else if (dev->model->model_id == MODEL_CANON_LIDE_120)
- {
- idx = 2;
- }
- else
- { /* canon LiDE 210 and 220 case */
- idx = 1;
- }
-
- RIE (sanei_genesys_write_register (dev, REG31, gpios[idx].r31));
- RIE (sanei_genesys_write_register (dev, REG32, gpios[idx].r32));
- RIE (sanei_genesys_write_register (dev, REG33, gpios[idx].r33));
- RIE (sanei_genesys_write_register (dev, REG34, gpios[idx].r34));
- RIE (sanei_genesys_write_register (dev, REG35, gpios[idx].r35));
- RIE (sanei_genesys_write_register (dev, REG36, gpios[idx].r36));
- RIE (sanei_genesys_write_register (dev, REG38, gpios[idx].r38));
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * set memory layout by filling values in dedicated registers
- */
-static SANE_Status
-gl124_init_memory_layout (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int idx = 0;
-
- DBGSTART;
-
- /* point to per model memory layout */
- if (dev->model->model_id == MODEL_CANON_LIDE_110 ||dev->model->model_id == MODEL_CANON_LIDE_120)
- {
- idx = 0;
- }
- else
- { /* canon LiDE 210 and 220 case */
- idx = 1;
- }
-
- /* setup base address for shading data. */
- /* values must be multiplied by 8192=0x4000 to give address on AHB */
- /* R-Channel shading bank0 address setting for CIS */
- sanei_genesys_write_register (dev, 0xd0, layouts[idx].rd0);
- /* G-Channel shading bank0 address setting for CIS */
- sanei_genesys_write_register (dev, 0xd1, layouts[idx].rd1);
- /* B-Channel shading bank0 address setting for CIS */
- sanei_genesys_write_register (dev, 0xd2, layouts[idx].rd2);
-
- /* setup base address for scanned data. */
- /* values must be multiplied by 1024*2=0x0800 to give address on AHB */
- /* R-Channel ODD image buffer 0x0124->0x92000 */
- /* size for each buffer is 0x16d*1k word */
- sanei_genesys_write_register (dev, 0xe0, layouts[idx].re0);
- sanei_genesys_write_register (dev, 0xe1, layouts[idx].re1);
- /* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
- sanei_genesys_write_register (dev, 0xe2, layouts[idx].re2);
- sanei_genesys_write_register (dev, 0xe3, layouts[idx].re3);
-
- /* R-Channel EVEN image buffer 0x0292 */
- sanei_genesys_write_register (dev, 0xe4, layouts[idx].re4);
- sanei_genesys_write_register (dev, 0xe5, layouts[idx].re5);
- /* R-Channel EVEN image buffer end-address 0x03ff*/
- sanei_genesys_write_register (dev, 0xe6, layouts[idx].re6);
- sanei_genesys_write_register (dev, 0xe7, layouts[idx].re7);
-
- /* same for green, since CIS, same addresses */
- sanei_genesys_write_register (dev, 0xe8, layouts[idx].re0);
- sanei_genesys_write_register (dev, 0xe9, layouts[idx].re1);
- sanei_genesys_write_register (dev, 0xea, layouts[idx].re2);
- sanei_genesys_write_register (dev, 0xeb, layouts[idx].re3);
- sanei_genesys_write_register (dev, 0xec, layouts[idx].re4);
- sanei_genesys_write_register (dev, 0xed, layouts[idx].re5);
- sanei_genesys_write_register (dev, 0xee, layouts[idx].re6);
- sanei_genesys_write_register (dev, 0xef, layouts[idx].re7);
-
-/* same for blue, since CIS, same addresses */
- sanei_genesys_write_register (dev, 0xf0, layouts[idx].re0);
- sanei_genesys_write_register (dev, 0xf1, layouts[idx].re1);
- sanei_genesys_write_register (dev, 0xf2, layouts[idx].re2);
- sanei_genesys_write_register (dev, 0xf3, layouts[idx].re3);
- sanei_genesys_write_register (dev, 0xf4, layouts[idx].re4);
- sanei_genesys_write_register (dev, 0xf5, layouts[idx].re5);
- sanei_genesys_write_register (dev, 0xf6, layouts[idx].re6);
- sanei_genesys_write_register (dev, 0xf7, layouts[idx].re7);
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * initialize backend and ASIC : registers, motor tables, and gamma tables
- * then ensure scanner's head is at home
- */
-static SANE_Status
-gl124_init(Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG_INIT ();
- DBGSTART;
-
- status=sanei_genesys_asic_init(dev, 0);
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/* *
- * initialize ASIC from power on condition
- */
-static SANE_Status
-gl124_boot (Genesys_Device * dev, SANE_Bool cold)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- DBGSTART;
-
- /* reset ASIC in case of cold boot */
- if(cold)
- {
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
- }
-
- /* enable GPOE 17 */
- RIE (sanei_genesys_write_register (dev, 0x36, 0x01));
-
- /* set GPIO 17 */
- RIE (sanei_genesys_read_register (dev, 0x33, &val));
- val |= 0x01;
- RIE (sanei_genesys_write_register (dev, 0x33, val));
-
- /* test CHKVER */
- RIE (sanei_genesys_read_register (dev, REG100, &val));
- if (val & REG100_CHKVER)
- {
- RIE (sanei_genesys_read_register (dev, 0x00, &val));
- DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
- }
-
- /* Set default values for registers */
- gl124_init_registers (dev);
-
- /* Write initial registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, dev->reg));
-
- /* tune reg 0B */
- val = REG0B_30MHZ | REG0B_ENBDRAM | REG0B_64M;
- RIE (sanei_genesys_write_register (dev, REG0B, val));
- dev->reg.remove_reg(0x0b);
-
- /* set up end access */
- RIE (sanei_genesys_write_0x8c (dev, 0x10, 0x0b));
- RIE (sanei_genesys_write_0x8c (dev, 0x13, 0x0e));
-
- /* CIS_LINE */
- SETREG (0x08, REG08_CIS_LINE);
- RIE (sanei_genesys_write_register (dev, 0x08, dev->reg.find_reg(0x08).value));
-
- /* setup gpio */
- RIE (gl124_init_gpio (dev));
-
- /* setup internal memory layout */
- RIE (gl124_init_memory_layout (dev));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-static SANE_Status
-gl124_update_hardware_sensors (Genesys_Scanner * s)
-{
- /* do what is needed to get a new set of events, but try to not loose
- any of them.
- */
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val=0;
-
- RIE (sanei_genesys_read_register (s->dev, REG31, &val));
-
- /* TODO : for the next scanner special case,
- * add another per scanner button profile struct to avoid growing
- * hard-coded button mapping here.
- */
- if((s->dev->model->gpo_type == GPO_CANONLIDE110)
- ||(s->dev->model->gpo_type == GPO_CANONLIDE120))
- {
- s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0);
- s->buttons[BUTTON_FILE_SW].write((val & 0x08) == 0);
- s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0);
- s->buttons[BUTTON_COPY_SW].write((val & 0x02) == 0);
- }
- else
- { /* LiDE 210 case */
- s->buttons[BUTTON_EXTRA_SW].write((val & 0x01) == 0);
- s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0);
- s->buttons[BUTTON_COPY_SW].write((val & 0x04) == 0);
- s->buttons[BUTTON_EMAIL_SW].write((val & 0x08) == 0);
- s->buttons[BUTTON_FILE_SW].write((val & 0x10) == 0);
- }
- return status;
-}
-
-
-/** the gl124 command set */
-static Genesys_Command_Set gl124_cmd_set = {
- "gl124-generic", /* the name of this set */
-
- [](Genesys_Device* dev) -> bool { (void) dev; return true; },
-
- gl124_init,
- gl124_init_regs_for_warmup,
- gl124_init_regs_for_coarse_calibration,
- gl124_init_regs_for_shading,
- gl124_init_regs_for_scan,
-
- gl124_get_filter_bit,
- gl124_get_lineart_bit,
- gl124_get_bitset_bit,
- gl124_get_gain4_bit,
- gl124_get_fast_feed_bit,
- gl124_test_buffer_empty_bit,
- gl124_test_motor_flag_bit,
-
- gl124_set_fe,
- gl124_set_powersaving,
- gl124_save_power,
-
- gl124_begin_scan,
- gl124_end_scan,
-
- sanei_genesys_send_gamma_table,
-
- gl124_search_start_position,
-
- gl124_offset_calibration,
- gl124_coarse_gain_calibration,
- gl124_led_calibration,
-
- gl124_wait_for_motor_stop,
- gl124_slow_back_home,
- gl124_rewind,
-
- sanei_genesys_bulk_write_register,
- NULL,
- sanei_genesys_bulk_read_data,
-
- gl124_update_hardware_sensors,
-
- /* no sheetfed support for now */
- NULL,
- NULL,
- NULL,
- NULL,
-
- sanei_genesys_is_compatible_calibration,
- NULL,
- gl124_send_shading_data,
- gl124_calculate_current_setup,
- gl124_boot
-};
-
-SANE_Status
-sanei_gl124_init_cmd_set (Genesys_Device * dev)
-{
- dev->model->cmd_set = &gl124_cmd_set;
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_gl124.h b/backend/genesys_gl124.h
deleted file mode 100644
index 751321d..0000000
--- a/backend/genesys_gl124.h
+++ /dev/null
@@ -1,489 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2010-2016 Stéphane Voltz <stef.dev@free.fr>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#include "genesys.h"
-
-#define REG01 0x01
-#define REG01_CISSET 0x80
-#define REG01_DOGENB 0x40
-#define REG01_DVDSET 0x20
-#define REG01_STAGGER 0x10
-#define REG01_COMPENB 0x08
-#define REG01_TRUEGRAY 0x04
-#define REG01_SHDAREA 0x02
-#define REG01_SCAN 0x01
-
-#define REG02 0x02
-#define REG02_NOTHOME 0x80
-#define REG02_ACDCDIS 0x40
-#define REG02_AGOHOME 0x20
-#define REG02_MTRPWR 0x10
-#define REG02_FASTFED 0x08
-#define REG02_MTRREV 0x04
-#define REG02_HOMENEG 0x02
-#define REG02_LONGCURV 0x01
-
-#define REG03 0x03
-#define REG03_LAMPDOG 0x80
-#define REG03_AVEENB 0x40
-#define REG03_XPASEL 0x20
-#define REG03_LAMPPWR 0x10
-#define REG03_LAMPTIM 0x0f
-
-#define REG04 0x04
-#define REG04_LINEART 0x80
-#define REG04_BITSET 0x40
-#define REG04_FILTER 0x30
-#define REG04_AFEMOD 0x07
-
-#define REG05 0x05
-#define REG05_DPIHW 0xc0
-#define REG05_DPIHW_600 0x00
-#define REG05_DPIHW_1200 0x40
-#define REG05_DPIHW_2400 0x80
-#define REG05_DPIHW_4800 0xc0
-#define REG05_MTLLAMP 0x30
-#define REG05_GMMENB 0x08
-#define REG05_ENB20M 0x04
-#define REG05_MTLBASE 0x03
-
-#define REG06 0x06
-#define REG06_SCANMOD 0xe0
-#define REG06S_SCANMOD 5
-#define REG06_PWRBIT 0x10
-#define REG06_GAIN4 0x08
-#define REG06_OPTEST 0x07
-
-#define REG07_LAMPSIM 0x80
-
-#define REG08_DRAM2X 0x80
-#define REG08_MPENB 0x20
-#define REG08_CIS_LINE 0x10
-#define REG08_IR2_ENB 0x08
-#define REG08_IR1_ENB 0x04
-#define REG08_ENB24M 0x01
-
-#define REG09_MCNTSET 0xc0
-#define REG09_EVEN1ST 0x20
-#define REG09_BLINE1ST 0x10
-#define REG09_BACKSCAN 0x08
-#define REG09_OUTINV 0x04
-#define REG09_SHORTTG 0x02
-
-#define REG09S_MCNTSET 6
-#define REG09S_CLKSET 4
-
-#define REG0A 0x0a
-#define REG0A_SIFSEL 0xc0
-#define REG0AS_SIFSEL 6
-#define REG0A_SHEETFED 0x20
-#define REG0A_LPWMEN 0x10
-
-#define REG0B 0x0b
-#define REG0B_DRAMSEL 0x07
-#define REG0B_16M 0x01
-#define REG0B_64M 0x02
-#define REG0B_128M 0x03
-#define REG0B_256M 0x04
-#define REG0B_512M 0x05
-#define REG0B_1G 0x06
-#define REG0B_ENBDRAM 0x08
-#define REG0B_RFHDIS 0x10
-#define REG0B_CLKSET 0xe0
-#define REG0B_24MHZ 0x00
-#define REG0B_30MHZ 0x20
-#define REG0B_40MHZ 0x40
-#define REG0B_48MHZ 0x60
-#define REG0B_60MHZ 0x80
-
-#define REG0D 0x0d
-#define REG0D_MTRP_RDY 0x80
-#define REG0D_FULLSTP 0x10
-#define REG0D_CLRMCNT 0x04
-#define REG0D_CLRDOCJM 0x02
-#define REG0D_CLRLNCNT 0x01
-
-#define REG0F 0x0f
-
-#define REG16_CTRLHI 0x80
-#define REG16_TOSHIBA 0x40
-#define REG16_TGINV 0x20
-#define REG16_CK1INV 0x10
-#define REG16_CK2INV 0x08
-#define REG16_CTRLINV 0x04
-#define REG16_CKDIS 0x02
-#define REG16_CTRLDIS 0x01
-
-#define REG17_TGMODE 0xc0
-#define REG17_SNRSYN 0x0f
-
-#define REG18 0x18
-#define REG18_CNSET 0x80
-#define REG18_DCKSEL 0x60
-#define REG18_CKTOGGLE 0x10
-#define REG18_CKDELAY 0x0c
-#define REG18_CKSEL 0x03
-
-#define REG1A_SW2SET 0x80
-#define REG1A_SW1SET 0x40
-#define REG1A_MANUAL3 0x02
-#define REG1A_MANUAL1 0x01
-#define REG1A_CK4INV 0x08
-#define REG1A_CK3INV 0x04
-#define REG1A_LINECLP 0x02
-
-#define REG1C_TBTIME 0x07
-
-#define REG1D 0x1d
-#define REG1D_CK4LOW 0x80
-#define REG1D_CK3LOW 0x40
-#define REG1D_CK1LOW 0x20
-#define REG1D_LINESEL 0x1f
-#define REG1DS_LINESEL 0
-
-#define REG1E 0x1e
-#define REG1E_WDTIME 0xf0
-#define REG1ES_WDTIME 4
-#define REG1E_WDTIME 0xf0
-
-#define REG30 0x30
-#define REG31 0x31
-#define REG32 0x32
-#define REG32_GPIO16 0x80
-#define REG32_GPIO15 0x40
-#define REG32_GPIO14 0x20
-#define REG32_GPIO13 0x10
-#define REG32_GPIO12 0x08
-#define REG32_GPIO11 0x04
-#define REG32_GPIO10 0x02
-#define REG32_GPIO9 0x01
-#define REG33 0x33
-#define REG34 0x34
-#define REG35 0x35
-#define REG36 0x36
-#define REG37 0x37
-#define REG38 0x38
-#define REG39 0x39
-
-#define REG60 0x60
-#define REG60_LED4TG 0x80
-#define REG60_YENB 0x40
-#define REG60_YBIT 0x20
-#define REG60_ACYNCNRLC 0x10
-#define REG60_ENOFFSET 0x08
-#define REG60_LEDADD 0x04
-#define REG60_CK4ADC 0x02
-#define REG60_AUTOCONF 0x01
-
-#define REG80 0x80
-#define REG81 0x81
-
-#define REGA0 0xa0
-#define REGA0_FSTPSEL 0x28
-#define REGA0S_FSTPSEL 3
-#define REGA0_STEPSEL 0x03
-#define REGA0S_STEPSEL 0
-
-#define REGA1 0xa1
-#define REGA2 0xa2
-#define REGA3 0xa3
-#define REGA4 0xa4
-#define REGA5 0xa5
-#define REGA6 0xa6
-#define REGA7 0xa7
-#define REGA8 0xa8
-#define REGA9 0xa9
-#define REGAA 0xaa
-#define REGAB 0xab
-#define REGAC 0xac
-#define REGAD 0xad
-#define REGAE 0xae
-#define REGAF 0xaf
-#define REGB0 0xb0
-#define REGB1 0xb1
-
-#define REGB2 0xb2
-#define REGB2_Z1MOD 0x1f
-#define REGB3 0xb3
-#define REGB3_Z1MOD 0xff
-#define REGB4 0xb4
-#define REGB4_Z1MOD 0xff
-
-#define REGB5 0xb5
-#define REGB5_Z2MOD 0x1f
-#define REGB6 0xb6
-#define REGB6_Z2MOD 0xff
-#define REGB7 0xb7
-#define REGB7_Z2MOD 0xff
-
-#define REG100 0x100
-#define REG100_DOCSNR 0x80
-#define REG100_ADFSNR 0x40
-#define REG100_COVERSNR 0x20
-#define REG100_CHKVER 0x10
-#define REG100_DOCJAM 0x08
-#define REG100_HISPDFLG 0x04
-#define REG100_MOTMFLG 0x02
-#define REG100_DATAENB 0x01
-
-#define REG114 0x114
-#define REG115 0x115
-
-#define REG_LINCNT 0x25
-#define REG_MAXWD 0x28
-#define REG_DPISET 0x2c
-#define REG_FEEDL 0x3d
-#define REG_CK1MAP 0x74
-#define REG_CK3MAP 0x77
-#define REG_CK4MAP 0x7a
-#define REG_LPERIOD 0x7d
-#define REG_DUMMY 0x80
-#define REG_STRPIXEL 0x82
-#define REG_ENDPIXEL 0x85
-#define REG_EXPDMY 0x88
-#define REG_EXPR 0x8a
-#define REG_EXPG 0x8d
-#define REG_EXPB 0x90
-#define REG_SEGCNT 0x93
-#define REG_TG0CNT 0x96
-#define REG_SCANFED 0xa2
-#define REG_STEPNO 0xa4
-#define REG_FWDSTEP 0xa6
-#define REG_BWDSTEP 0xa8
-#define REG_FASTNO 0xaa
-#define REG_FSHDEC 0xac
-#define REG_FMOVNO 0xae
-#define REG_FMOVDEC 0xb0
-#define REG_Z1MOD 0xb2
-#define REG_Z2MOD 0xb5
-
-#define REG_TRUER 0x110
-#define REG_TRUEG 0x111
-#define REG_TRUEB 0x112
-
-#define SETREG(adr,val) { dev->reg.init_reg(adr, val); }
-
-typedef struct
-{
- uint8_t r31;
- uint8_t r32;
- uint8_t r33;
- uint8_t r34;
- uint8_t r35;
- uint8_t r36;
- uint8_t r38;
-} Gpio_layout;
-
-/** @brief gpio layout
- * describes initial gpio settings for a given model
- * registers 0x31 to 0x38
- */
-static Gpio_layout gpios[]={
- /* LiDE 110 */
- { /* 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38 */
- 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00
- },
- /* LiDE 210 */
- {
- 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00
- },
- /* LiDE 120 */
- {
- 0x9f, 0x53, 0x01, 0x80, 0x5f, 0x01, 0x00
- },
-};
-
-typedef struct
-{
- uint8_t rd0;
- uint8_t rd1;
- uint8_t rd2;
- uint8_t re0;
- uint8_t re1;
- uint8_t re2;
- uint8_t re3;
- uint8_t re4;
- uint8_t re5;
- uint8_t re6;
- uint8_t re7;
-} Memory_layout;
-
-static Memory_layout layouts[]={
- /* LIDE 110, 120 */
- { /* 0xd0 0xd1 0xd2 */
- 0x0a, 0x15, 0x20,
- /* 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 */
- 0x00, 0xac, 0x08, 0x55, 0x08, 0x56, 0x0f, 0xff
- },
- /* LIDE 210, 220 */
- {
- 0x0a, 0x1f, 0x34,
- 0x01, 0x24, 0x08, 0x91, 0x08, 0x92, 0x0f, 0xff
- }
-};
-
-/** @brief structure for sensor settings
- * this structure describes the sensor settings to use for a given
- * exposure. Data settings are identified by
- * - sensor id
- * - sensor hardware dpi
- * - half ccd mode
- */
-typedef struct {
- int sensor_type; /**> sensor id */
- int dpi; /**> maximum dpi for which data are valid */
- int half_ccd; /**> half ccd mode */
- int exposure; /**> exposure */
- int ck1map; /**> CK1MAP */
- int ck3map; /**> CK3MAP */
- int ck4map; /**> CK4MAP */
- int segcnt; /**> SEGCNT */
- int tg0cnt; /**> TG0CNT */
- int expdummy; /**> exposure dummy */
- int expr; /**> initial red exposure */
- int expg; /**> initial green exposure */
- int expb; /**> initial blue exposure */
- size_t *order; /**> order of sub-segments */
- uint8_t reg18; /**> register 0x18 value */
- uint8_t reg20; /**> register 0x20 value */
- uint8_t reg61; /**> register 0x61 value */
- uint8_t reg98; /**> register 0x98 value */
- uint8_t reg16; /**> register 0x16 value */
- uint8_t reg70; /**> register 0x70 value */
-} Sensor_Profile;
-
-static size_t order_01[]={0,1};
-static size_t order_0213[]={0,2,1,3};
-
-/** @brief database of sensor profiles
- * database of sensor profiles giving for each sensor and a given resolution, the period, and timings
- * to setup the sensor for the scan.
- */
-static Sensor_Profile sensors[]={
- /* LiDE 110 */
- {CIS_CANONLIDE110, 600, 1, 2768, 0x1e, 0x9f, 0x55, 2584, 154, 101, 388, 574, 393, NULL , 0x00, 0x0c, 0x20, 0x21, 0x00, 0x00},
- {CIS_CANONLIDE110, 600, 0, 5360, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, NULL , 0x00, 0x0a, 0x20, 0x21, 0x00, 0x00},
- {CIS_CANONLIDE110, 1200, 0, 10528, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, order_01 , 0x00, 0x08, 0x20, 0x22, 0x00, 0x00},
- {CIS_CANONLIDE110, 2400, 0, 20864, 0x1e, 0x9f, 0x55, 5168, 163, 4679, 6839, 8401, 6859, order_0213, 0x00, 0x06, 0x20, 0x24, 0x00, 0x00},
-
- /* LiDE 120 */
- {CIS_CANONLIDE120, 600, 1, 4608, 0x0f, 0x00, 0x55, 2552, 112, 94, 894, 1044, 994, NULL , 0x00, 0x02, 0x20, 0x21, 0x15, 0x00},
- {CIS_CANONLIDE120, 600, 0, 5360, 0x0f, 0x00, 0x55, 5104, 139, 94, 1644, 1994, 1844, NULL , 0x00, 0x02, 0x20, 0x21, 0x11, 0x1f},
- {CIS_CANONLIDE120, 1200, 0, 10528, 0x0f, 0x00, 0x55,10208, 192, 94, 3194, 3794, 3594, NULL , 0x00, 0x02, 0x20, 0x21, 0x15, 0x1f},
- {CIS_CANONLIDE120, 2400, 0, 20864, 0x0f, 0x00, 0x55,20416, 298, 94, 6244, 7544, 7094, NULL , 0x00, 0x02, 0x20, 0x21, 0x11, 0x00},
-
- /* LiDE 210 */
- {CIS_CANONLIDE210, 600, 1, 2768, 0x1e, 0x9f, 0x55, 2584, 154, 101, 388, 574, 393, NULL , 0x00, 0x0c, 0x20, 0x21, 0x00, 0x00},
- {CIS_CANONLIDE210, 600, 0, 5360, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, NULL , 0x00, 0x0a, 0x20, 0x21, 0x00, 0x00},
- {CIS_CANONLIDE210, 1200, 0, 10528, 0x1e, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, order_01 , 0x00, 0x08, 0x20, 0x22, 0x00, 0x00},
- {CIS_CANONLIDE210, 2400, 0, 20864, 0x1e, 0x9f, 0x55, 5168, 163, 4679, 6839, 8401, 6859, order_0213, 0x00, 0x06, 0x20, 0x24, 0x00, 0x00},
-
- /* LiDE 220 */
- {CIS_CANONLIDE220, 600, 1, 2768, 0x0f, 0x9f, 0x55, 2584, 154, 101, 388, 574, 393, NULL , 0x00, 0x0c, 0x20, 0x21, 0x00, 0x00},
- {CIS_CANONLIDE220, 600, 0, 5360, 0x0f, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, NULL , 0x00, 0x0a, 0x20, 0x21, 0x00, 0x00},
- {CIS_CANONLIDE220, 1200, 0, 10528, 0x0f, 0x9f, 0x55, 5168, 163, 101, 388, 574, 393, order_01 , 0x00, 0x08, 0x20, 0x22, 0x00, 0x00},
- {CIS_CANONLIDE220, 2400, 0, 20864, 0x0f, 0x9f, 0x55, 5168, 163, 4679, 6839, 8401, 6859, order_0213, 0x00, 0x06, 0x20, 0x24, 0x00, 0x00},
-};
-
-
-#define MOVE_DPI 200
-#define MOVE_EXPOSURE 2304
-/** @brief reference slope tables
- * slope table directly extracted from USB logs, with a 'termination' value of 0.
- */
-static uint32_t lide210_fast[] = { 62496, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2051, 1432, 1372, 1323, 1280, 1246, 1216, 1188, 1163, 1142, 1121, 1101, 1084, 1068, 1051, 1036, 1020, 1007, 995, 983, 971, 959, 949, 938, 929, 917, 908, 900, 891, 882, 874, 866, 857, 849, 843, 835, 829, 821, 816, 808, 802, 795, 789, 784, 778, 773, 765, 760, 755, 749, 744, 739, 734, 731, 726, 721, 716, 711, 707, 702, 698, 693, 690, 685, 682, 677, 672, 669, 665, 662, 657, 654, 650, 647, 644, 639, 637, 632, 629, 626, 622, 619, 617, 614, 610, 607, 604, 601, 599, 595, 592, 589, 587, 584, 581, 579, 576, 572, 570, 567, 564, 562, 559, 557, 554, 552, 549, 547, 544, 542, 539, 538, 536, 533, 531, 529, 526, 524, 522, 519, 518, 516, 513, 511, 509, 506, 505, 503, 501, 498, 497, 495, 493, 491, 490, 487, 485, 483, 482, 480, 477, 476, 474, 472, 470, 469, 467, 465, 464, 462, 460, 458, 456, 455, 453, 451, 450, 448, 447, 445, 444, 442, 440, 439, 437, 436, 434, 433, 431, 430, 428, 427, 425, 423, 422, 420, 419, 417, 417, 415, 414, 413, 411, 410, 408, 407, 405, 404, 402, 401, 400, 399, 398, 396, 395, 393, 392, 391, 390, 389, 387, 386, 385, 383, 382, 381, 380, 379, 377, 376, 375, 374, 373, 371, 370, 369, 368, 367, 366, 364, 363, 363, 361, 360, 359, 358, 357, 356, 355, 353, 352, 352, 350, 349, 348, 347, 346, 345, 344, 343, 342, 341, 340, 339, 338, 335, 335, 0};
-static uint32_t lide110_ok[] = { 62496, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2051, 1961, 1901, 1852, 1809, 1775, 1745, 1717, 1692, 1671, 1650, 1630, 1613, 1597, 1580, 1565, 1549, 1536, 1524, 1512, 1500, 1488, 1478, 1467, 1458, 1446, 1437, 1429, 1420, 1411, 1403, 1395, 1386, 1378, 1372, 1364, 1358, 1350, 1345, 1337, 1331, 1324, 1318, 1313, 1307, 1302, 1294, 1289, 1284, 1278, 1273, 1268, 1263, 1260, 1255, 1250, 1245, 1240, 1236, 1231, 1227, 1222, 1219, 1214, 1211, 1206, 1201, 1198, 1194, 1191, 1186, 1183, 1179, 1176, 1173, 1168, 1166, 1161, 1158, 1155, 1151, 1148, 1146, 1143, 1139, 1136, 1133, 1130, 1128, 1124, 1121, 1118, 1116, 1113, 1110, 1108, 1105, 1101, 1099, 1096, 1093, 1091, 1088, 1086, 1083, 1081, 1078, 1076, 1073, 1071, 1068, 1067, 1065, 1062, 1060, 1058, 1055, 1053, 1051, 1048, 1047, 1045, 1042, 1040, 1038, 1035, 1034, 1032, 1030, 1027, 1026, 1024, 1022, 1020, 1019, 1016, 1014, 1012, 1011, 1009, 1006, 1005, 1003, 1001, 999, 998, 996, 994, 993, 991, 989, 987, 985, 984, 982, 980, 979, 977, 976, 974, 973, 971, 969, 968, 966, 965, 963, 962, 960, 959, 957, 956, 954, 952, 951, 949, 948, 946, 946, 944, 943, 942, 940, 939, 937, 936, 934, 933, 931, 930, 929, 928, 927, 925, 924, 922, 921, 920, 919, 918, 916, 915, 914, 912, 911, 910, 909, 908, 906, 905, 904, 903, 902, 900, 899, 898, 897, 896, 895, 893, 892, 892, 890, 889, 888, 887, 886, 885, 884, 882, 881, 881, 879, 878, 877, 876, 875, 874, 873, 872, 871, 870, 869, 868, 867, 864, 857, 849, 843, 835, 829, 821, 816, 808, 802, 795, 789, 784, 778, 773, 765, 760, 755, 749, 744, 739, 734, 731, 726, 721, 716, 711, 707, 702, 698, 693, 690, 685, 682, 677, 672, 669, 665, 662, 657, 654, 650, 647, 644, 639, 637, 632, 629, 626, 622, 619, 617, 614, 610, 607, 604, 601, 599, 595, 592, 589, 587, 584, 581, 579, 576, 572, 570, 567, 564, 562, 559, 557, 554, 552, 549, 547, 544, 542, 539, 538, 536, 533, 531, 529, 526, 524, 522, 519, 518, 516, 513, 511, 509, 506, 505, 503, 501, 498, 497, 495, 493, 491, 490, 487, 485, 483, 482, 480, 477, 476, 474, 472, 470, 469, 467, 465, 464, 462, 460, 458, 456, 455, 453, 451, 450, 448, 447, 445, 444, 442, 440, 439, 437, 436, 434, 433, 431, 430, 428, 427, 425, 423, 422, 420, 419, 417, 417, 415, 414, 413, 411, 410, 408, 407, 405, 404, 402, 401, 400, 399, 398, 396, 395, 393, 392, 391, 390, 389, 387, 386, 385, 383, 382, 381, 380, 379, 377, 376, 375, 374, 373, 371, 370, 369, 368, 367, 366, 364, 363, 363, 361, 360, 359, 358, 357, 356, 355, 353, 352, 352, 350, 349, 348, 347, 346, 345, 344, 343, 342, 341, 340, 339, 338, 335, 335, 0};
-static uint32_t lide120_fast[] = { 62496, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 1957, 1845, 1768, 1710, 1665, 1624, 1588, 1557, 1529, 1504, 1481, 1458, 1440, 1420, 1403, 1386, 1370, 1356, 1343, 1329, 1316, 1303, 1293, 1280, 1270, 1260, 1250, 1241, 1231, 1222, 1214, 1206, 1197, 1189, 1182, 1174, 1167, 1160, 1153, 1147, 1140, 1133, 1128, 1121, 1116, 1110, 1104, 1099, 1093, 1088, 1082, 1077, 1072, 1067, 1062, 1058, 1053, 1049, 1045, 1040, 1035, 1032, 1027, 1023, 1020, 1015, 1012, 1008, 1004, 1000, 997, 993, 989, 985, 982, 979, 975, 972, 969, 966, 963, 959, 956, 953, 950, 947, 945, 942, 939, 936, 933, 930, 928, 925, 922, 920, 917, 914, 911, 909, 907, 904, 902, 899, 897, 895, 892, 890, 888, 886, 883, 881, 879, 876, 874, 872, 870, 864, 864, 0};
-static uint32_t lide120_ok[] = { 62496, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2286, 2264, 2248, 2232, 2221, 2211, 2205, 2195, 2190, 2180, 2175, 2170, 2160, 2155, 2150, 2145, 2140, 2135, 2130, 2125, 2121, 2116, 2111, 2106, 2106, 2102, 2097, 2092, 2087, 2087, 2083, 2078, 2074, 2074, 2069, 2064, 2064, 2060, 2055, 2055, 2051, 2051, 2046, 2042, 2042, 2038, 2038, 2033, 2029, 2029, 2024, 2024, 2020, 2010, 2010, 670*2, 0};
-static uint32_t lide110_slow[] = { 62496, 7896, 2632, 0};
-static uint32_t lide120_slow[] = { 62464, 7896, 2632, 0};
-static uint32_t lide110_max[] = { 62496, 31296, 10432, 0};
-static uint32_t lide120_max[] = { 62592, 62592, 41728, 31296, 10432, 0};
-static uint32_t lide210_max[] = { 62496, 31296, 20864, 10432, 0};
-
-/* NEXT LPERIOD=PREVIOUS*2-192 */
-/** @brief database of motor profiles
- * database of motor profiles, for each exposure deigned for the sensor, gives the reference slope table to use
- * for scan.
- */
-static Motor_Profile motors[]={
- {MOTOR_CANONLIDE110, 2768, 0, lide210_fast},
- {MOTOR_CANONLIDE110, 5360, 1, lide110_ok},
- {MOTOR_CANONLIDE110, 10528, 1, lide110_slow},
- {MOTOR_CANONLIDE110, 20864, 2, lide110_max},
- {MOTOR_CANONLIDE120, 4608, 0, lide120_fast},
- {MOTOR_CANONLIDE120, 5360, 1, lide120_ok},
- {MOTOR_CANONLIDE120, 10528, 2, lide120_slow},
- {MOTOR_CANONLIDE120, 20864, 2, lide120_max},
- {MOTOR_CANONLIDE210, 2768, 0, lide210_fast},
- {MOTOR_CANONLIDE210, 5360, 1, lide110_ok},
- {MOTOR_CANONLIDE210, 10528, 1, lide110_slow},
- {MOTOR_CANONLIDE210, 20864, 2, lide210_max},
- {0, 0, 0, NULL},
-};
-
-static
-SANE_Status gl124_init_scan_regs(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg, SetupParams& params);
-
-static SANE_Status gl124_start_action (Genesys_Device * dev);
-static SANE_Status
-gl124_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SANE_Bool start_motor);
-static SANE_Status
-gl124_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop);
-static SANE_Status
-gl124_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
-static SANE_Status gl124_init(Genesys_Device * dev);
-static SANE_Status gl124_send_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data, int size);
-
-static SANE_Status gl124_feed (Genesys_Device * dev, unsigned int steps, int reverse);
-
-static SANE_Status
-gl124_stop_action (Genesys_Device * dev);
-
-static SANE_Status
-gl124_send_slope_table (Genesys_Device * dev, int table_nr,
- uint16_t * slope_table, int steps);
diff --git a/backend/genesys_gl646.cc b/backend/genesys_gl646.cc
deleted file mode 100644
index b2b9f62..0000000
--- a/backend/genesys_gl646.cc
+++ /dev/null
@@ -1,4911 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2003 Oliver Rauch
- Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
- Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
- Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
- Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
- Copyright (C) 2007 Luke <iceyfor@gmail.com>
- Copyright (C) 2011 Alexey Osipov <simba@lerlan.ru> for HP2400 description
- and tuning
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#define DEBUG_DECLARE_ONLY
-
-#include "genesys_gl646.h"
-
-#include <vector>
-
-/**
- * reads value from gpio endpoint
- */
-static void gl646_gpio_read(UsbDevice& usb_dev, uint8_t* value)
-{
- DBG_HELPER(dbg);
- usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, GPIO_READ, INDEX, 1, value);
-}
-
-/**
- * writes the given value to gpio endpoint
- */
-static void gl646_gpio_write(UsbDevice& usb_dev, uint8_t value)
-{
- DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
- usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_WRITE, INDEX, 1, &value);
-}
-
-/**
- * writes the given value to gpio output enable endpoint
- */
-static void gl646_gpio_output_enable(UsbDevice& usb_dev, uint8_t value)
-{
- DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
- usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_OUTPUT_ENABLE, INDEX, 1, &value);
-}
-
-/* Read bulk data (e.g. scanned data) */
-static SANE_Status
-gl646_bulk_read_data (Genesys_Device * dev, uint8_t addr,
- uint8_t * data, size_t len)
-{
- SANE_Status status = sanei_genesys_bulk_read_data(dev, addr, data, len);
- if (status != SANE_STATUS_GOOD) {
- return status;
- }
- if (dev->model->is_sheetfed == SANE_TRUE) {
- gl646_detect_document_end (dev);
- }
- return status;
-}
-
-static SANE_Bool
-gl646_get_fast_feed_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x02);
- if (r && (r->value & REG02_FASTFED))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl646_get_filter_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x04);
- if (r && (r->value & REG04_FILTER))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl646_get_lineart_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x04);
- if (r && (r->value & REG04_LINEART))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl646_get_bitset_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x04);
- if (r && (r->value & REG04_BITSET))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl646_get_gain4_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x06);
- if (r && (r->value & REG06_GAIN4))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl646_test_buffer_empty_bit (SANE_Byte val)
-{
- if (val & REG41_BUFEMPTY)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl646_test_motor_flag_bit (SANE_Byte val)
-{
- if (val & REG41_MOTMFLG)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-/**
- * decodes and prints content of status (0x41) register
- * @param val value read from reg41
- */
-static void
-print_status (uint8_t val)
-{
- char msg[80];
-
- sprintf (msg, "%s%s%s%s%s%s%s%s",
- val & REG41_PWRBIT ? "PWRBIT " : "",
- val & REG41_BUFEMPTY ? "BUFEMPTY " : "",
- val & REG41_FEEDFSH ? "FEEDFSH " : "",
- val & REG41_SCANFSH ? "SCANFSH " : "",
- val & REG41_HOMESNR ? "HOMESNR " : "",
- val & REG41_LAMPSTS ? "LAMPSTS " : "",
- val & REG41_FEBUSY ? "FEBUSY " : "",
- val & REG41_MOTMFLG ? "MOTMFLG" : "");
- DBG(DBG_info, "status=%s\n", msg);
-}
-
-/**
- * start scanner's motor
- * @param dev scanner's device
- */
-static SANE_Status
-gl646_start_motor (Genesys_Device * dev)
-{
- return sanei_genesys_write_register (dev, 0x0f, 0x01);
-}
-
-
-/**
- * stop scanner's motor
- * @param dev scanner's device
- */
-static SANE_Status
-gl646_stop_motor (Genesys_Device * dev)
-{
- return sanei_genesys_write_register (dev, 0x0f, 0x00);
-}
-
-
-/**
- * find the lowest resolution for the sensor in the given mode.
- * @param sensor id of the sensor
- * @param color true is color mode
- * @return the closest resolution for the sensor and mode
- */
-static int
-get_lowest_resolution(int sensor_id, unsigned channels)
-{
- int i, nb;
- int dpi;
-
- i = 0;
- dpi = 9600;
- nb = sizeof (sensor_master) / sizeof (Sensor_Master);
- while (i < nb)
- {
- /* computes distance and keep mode if it is closer than previous */
- if (sensor_id == sensor_master[i].sensor
- && sensor_master[i].channels == channels)
- {
- if (sensor_master[i].dpi < dpi)
- {
- dpi = sensor_master[i].dpi;
- }
- }
- i++;
- }
- DBG(DBG_info, "%s: %d\n", __func__, dpi);
- return dpi;
-}
-
-/**
- * find the closest match in mode tables for the given resolution and scan mode.
- * @param sensor id of the sensor
- * @param required required resolution
- * @param color true is color mode
- * @return the closest resolution for the sensor and mode
- */
-static int
-get_closest_resolution(int sensor_id, int required, unsigned channels)
-{
- int i, nb;
- int dist, dpi;
-
- i = 0;
- dpi = 0;
- dist = 9600;
- nb = sizeof (sensor_master) / sizeof (Sensor_Master);
- while (i < nb)
- {
- /* exit on perfect match */
- if (sensor_id == sensor_master[i].sensor
- && sensor_master[i].dpi == required
- && sensor_master[i].channels == channels)
- {
- DBG(DBG_info, "%s: match found for %d\n", __func__, required);
- return required;
- }
- /* computes distance and keep mode if it is closer than previous */
- if (sensor_id == sensor_master[i].sensor
- && sensor_master[i].channels == channels)
- {
- if (abs (sensor_master[i].dpi - required) < dist)
- {
- dpi = sensor_master[i].dpi;
- dist = abs (sensor_master[i].dpi - required);
- }
- }
- i++;
- }
- DBG(DBG_info, "%s: closest match for %d is %d\n", __func__, required, dpi);
- return dpi;
-}
-
-/**
- * Computes if sensor will be set up for half ccd pixels for the given
- * scan mode.
- * @param sensor id of the sensor
- * @param required required resolution
- * @param color true is color mode
- * @return SANE_TRUE if half ccd is used
- */
-static SANE_Bool is_half_ccd(int sensor_id, int required, unsigned channels)
-{
- int i, nb;
-
- i = 0;
- nb = sizeof (sensor_master) / sizeof (Sensor_Master);
- while (i < nb)
- {
- /* exit on perfect match */
- if (sensor_id == sensor_master[i].sensor
- && sensor_master[i].dpi == required
- && sensor_master[i].channels == channels)
- {
- DBG(DBG_io, "%s: match found for %d (half_ccd=%d)\n", __func__, required,
- sensor_master[i].half_ccd);
- return sensor_master[i].half_ccd;
- }
- i++;
- }
- DBG(DBG_info, "%s: failed to find match for %d dpi\n", __func__, required);
- return SANE_FALSE;
-}
-
-/**
- * Returns the cksel values used by the required scan mode.
- * @param sensor id of the sensor
- * @param required required resolution
- * @param color true is color mode
- * @return cksel value for mode
- */
-static int get_cksel(int sensor_id, int required, unsigned channels)
-{
- int i, nb;
-
- i = 0;
- nb = sizeof (sensor_master) / sizeof (Sensor_Master);
- while (i < nb)
- {
- /* exit on perfect match */
- if (sensor_id == sensor_master[i].sensor
- && sensor_master[i].dpi == required
- && sensor_master[i].channels == channels)
- {
- DBG(DBG_io, "%s: match found for %d (cksel=%d)\n", __func__, required,
- sensor_master[i].cksel);
- return sensor_master[i].cksel;
- }
- i++;
- }
- DBG(DBG_error, "%s: failed to find match for %d dpi\n", __func__, required);
- /* fail safe fallback */
- return 1;
-}
-
-/**
- * Setup register and motor tables for a scan at the
- * given resolution and color mode. TODO try to not use any filed from
- * the device.
- * @param dev pointer to a struct describing the device
- * @param regs register set to fill
- * @param slope_table1 first motor table to fill
- * @param slope_table2 second motor table to fill
- * @return SANE_STATUS_GOOD if registers could be set, SANE_STATUS_INVAL if
- * conditions can't be met.
- * @note No harcoded SENSOR or MOTOR 'names' should be present and
- * registers are set from settings tables and flags related
- * to the hardware capabilities.
- * */
-static SANE_Status
-gl646_setup_registers (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs,
- SetupParams& params,
- uint16_t * slope_table1,
- uint16_t * slope_table2,
- bool xcorrection)
-{
- int resolution = params.xres;
- uint32_t move = params.starty;
- uint32_t linecnt = params.lines;
-
- uint32_t startx = 0;
- /* pixels are allways given at full CCD optical resolution */
- /* use detected left margin and fixed value */
- if (xcorrection == SANE_TRUE) {
- if (sensor.CCD_start_xoffset > 0) {
- startx = sensor.CCD_start_xoffset;
- } else {
- startx = sensor.dummy_pixel;
- }
- } else {
- // startx cannot be below dummy pixel value
- startx = sensor.dummy_pixel;
- }
-
- /* add x coordinates : expressed in sensor max dpi */
- startx += params.startx;
-
- /* stagger works with odd start cordinates */
- if (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE) {
- startx |= 1;
- }
-
- uint32_t pixels = (params.pixels * sensor.optical_res) / params.xres;
- /* special requirement for 400 dpi on 1200 dpi sensors */
- if (params.xres == 400)
- {
- pixels = (pixels / 6) * 6;
- }
- /* TODO check for pixel width overflow */
- uint32_t endx = startx + pixels;
-
- SANE_Status status = SANE_STATUS_GOOD;
- int i, nb;
- Sensor_Master *sensor_mst = NULL;
- Motor_Master *motor = NULL;
- Sensor_Settings *settings = NULL;
- GenesysRegister *r;
- unsigned int used1, used2, vfinal;
- unsigned int bpp; /**> bytes per pixel */
- uint32_t z1, z2;
- uint16_t ex, sx;
- int stagger, words_per_line, max_shift;
- size_t requested_buffer_size;
- size_t read_buffer_size;
- SANE_Bool half_ccd = SANE_FALSE;
- SANE_Int xresolution;
- int feedl;
-
- DBG(DBG_proc, "%s: start\n", __func__);
- DBG(DBG_info, "%s: startx=%d, endx=%d, linecnt=%d\n", __func__, startx, endx, linecnt);
-
- /* x resolution is capped by sensor's capability */
- if (resolution > sensor.optical_res)
- {
- xresolution = sensor.optical_res;
- }
- else
- {
- xresolution = resolution;
- }
-
- /* for the given resolution, search for master
- * sensor mode setting */
- i = 0;
- nb = sizeof (sensor_master) / sizeof (Sensor_Master);
- while (i < nb)
- {
- if (dev->model->ccd_type == sensor_master[i].sensor
- && sensor_master[i].dpi == xresolution
- && sensor_master[i].channels == params.channels)
- {
- sensor_mst = &sensor_master[i];
- }
- i++;
- }
- if (sensor_mst == NULL)
- {
- DBG(DBG_error, "%s: unable to find settings for sensor %d at %d dpi channels=%d\n", __func__,
- dev->model->ccd_type, xresolution, params.channels);
- return SANE_STATUS_INVAL;
- }
-
- /* for the given resolution, search for master
- * motor mode setting */
- i = 0;
- nb = sizeof (motor_master) / sizeof (Motor_Master);
- while (i < nb)
- {
- if (dev->model->motor_type == motor_master[i].motor
- && motor_master[i].dpi == resolution
- && motor_master[i].channels == params.channels)
- {
- motor = &motor_master[i];
- }
- i++;
- }
- if (motor == NULL)
- {
- DBG(DBG_error, "%s: unable to find settings for motor %d at %d dpi, color=%d\n", __func__,
- dev->model->motor_type, resolution, params.channels);
- return SANE_STATUS_INVAL;
- }
-
- /* now we can search for the specific sensor settings */
- i = 0;
- nb = sizeof (sensor_settings) / sizeof (Sensor_Settings);
- while (i < nb)
- {
- if (sensor_mst->sensor == sensor_settings[i].sensor
- && sensor_mst->cksel == sensor_settings[i].cksel)
- {
- settings = &sensor_settings[i];
- }
- i++;
- }
- if (settings == NULL)
- {
- DBG(DBG_error, "%s: unable to find settings for sensor %d with '%d' ccd timing\n", __func__,
- sensor_mst->sensor, sensor_mst->cksel);
- return SANE_STATUS_INVAL;
- }
-
- /* half_ccd if manual clock programming or dpi is half dpiset */
- half_ccd = sensor_mst->half_ccd;
-
- /* now apply values from settings to registers */
- if (sensor_mst->regs_0x10_0x15 != NULL)
- {
- for (i = 0; i < 6; i++)
- {
- r = sanei_genesys_get_address (regs, 0x10 + i);
- r->value = sensor_mst->regs_0x10_0x15[i];
- }
- }
- else
- {
- for (i = 0; i < 6; i++)
- {
- r = sanei_genesys_get_address (regs, 0x10 + i);
- r->value = 0;
- }
- }
-
- for (i = 0; i < 4; i++)
- {
- r = sanei_genesys_get_address (regs, 0x08 + i);
- if (half_ccd == SANE_TRUE)
- r->value = settings->manual_0x08_0x0b[i];
- else
- r->value = settings->regs_0x08_0x0b[i];
- }
-
- for (i = 0; i < 8; i++)
- {
- r = sanei_genesys_get_address (regs, 0x16 + i);
- r->value = settings->regs_0x16_0x1d[i];
- }
-
- for (i = 0; i < 13; i++)
- {
- r = sanei_genesys_get_address (regs, 0x52 + i);
- r->value = settings->regs_0x52_0x5e[i];
- }
- if (half_ccd == SANE_TRUE)
- {
- for (i = 0; i < 7; i++)
- {
- r = sanei_genesys_get_address (regs, 0x52 + i);
- r->value = settings->manual_0x52_0x58[i];
- }
- }
-
- /* now generate slope tables : we are not using generate_slope_table3 yet */
- sanei_genesys_generate_slope_table (slope_table1, motor->steps1,
- motor->steps1 + 1, motor->vend1,
- motor->vstart1, motor->vend1,
- motor->steps1, motor->g1, &used1,
- &vfinal);
- sanei_genesys_generate_slope_table (slope_table2, motor->steps2,
- motor->steps2 + 1, motor->vend2,
- motor->vstart2, motor->vend2,
- motor->steps2, motor->g2, &used2,
- &vfinal);
-
- /* R01 */
- /* now setup other registers for final scan (ie with shading enabled) */
- /* watch dog + shading + scan enable */
- regs->find_reg(0x01).value |= REG01_DOGENB | REG01_DVDSET | REG01_SCAN;
- if (dev->model->is_cis == SANE_TRUE)
- regs->find_reg(0x01).value |= REG01_CISSET;
- else
- regs->find_reg(0x01).value &= ~REG01_CISSET;
-
- /* if device has no calibration, don't enable shading correction */
- if (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)
- {
- regs->find_reg(0x01).value &= ~REG01_DVDSET;
- }
-
- regs->find_reg(0x01).value &= ~REG01_FASTMOD;
- if (motor->fastmod)
- regs->find_reg(0x01).value |= REG01_FASTMOD;
-
- /* R02 */
- /* allow moving when buffer full by default */
- if (dev->model->is_sheetfed == SANE_FALSE)
- dev->reg.find_reg(0x02).value &= ~REG02_ACDCDIS;
- else
- dev->reg.find_reg(0x02).value |= REG02_ACDCDIS;
-
- /* setup motor power and direction */
- sanei_genesys_set_motor_power(*regs, true);
- regs->find_reg(0x02).value &= ~REG02_MTRREV;
-
- /* fastfed enabled (2 motor slope tables) */
- if (motor->fastfed)
- regs->find_reg(0x02).value |= REG02_FASTFED;
- else
- regs->find_reg(0x02).value &= ~REG02_FASTFED;
-
- /* step type */
- regs->find_reg(0x02).value &= ~REG02_STEPSEL;
- switch (motor->steptype)
- {
- case FULL_STEP:
- break;
- case HALF_STEP:
- regs->find_reg(0x02).value |= 1;
- break;
- case QUATER_STEP:
- regs->find_reg(0x02).value |= 2;
- break;
- default:
- regs->find_reg(0x02).value |= 3;
- break;
- }
-
- /* if sheetfed, no AGOHOME */
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- regs->find_reg(0x02).value &= ~REG02_AGOHOME;
- }
- else
- {
- regs->find_reg(0x02).value |= REG02_AGOHOME;
- }
-
- /* R03 */
- regs->find_reg(0x03).value &= ~REG03_AVEENB;
- /* regs->find_reg(0x03).value |= REG03_AVEENB; */
- regs->find_reg(0x03).value &= ~REG03_LAMPDOG;
-
- /* select XPA */
- regs->find_reg(0x03).value &= ~REG03_XPASEL;
- if (params.flags & SCAN_FLAG_USE_XPA) {
- regs->find_reg(0x03).value |= REG03_XPASEL;
- }
- regs->state.is_xpa_on = params.flags & SCAN_FLAG_USE_XPA;
-
- /* R04 */
- /* monochrome / color scan */
- switch (params.depth)
- {
- case 1:
- regs->find_reg(0x04).value &= ~REG04_BITSET;
- regs->find_reg(0x04).value |= REG04_LINEART;
- break;
- case 8:
- regs->find_reg(0x04).value &= ~(REG04_LINEART | REG04_BITSET);
- break;
- case 16:
- regs->find_reg(0x04).value &= ~REG04_LINEART;
- regs->find_reg(0x04).value |= REG04_BITSET;
- break;
- }
-
- /* R05 */
- regs->find_reg(0x05).value &= ~REG05_DPIHW;
- switch (sensor.optical_res)
- {
- case 600:
- regs->find_reg(0x05).value |= REG05_DPIHW_600;
- break;
- case 1200:
- regs->find_reg(0x05).value |= REG05_DPIHW_1200;
- break;
- case 2400:
- regs->find_reg(0x05).value |= REG05_DPIHW_2400;
- break;
- default:
- regs->find_reg(0x05).value |= REG05_DPIHW;
- }
-
- /* gamma enable for scans */
- if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA)
- regs->find_reg(0x05).value |= REG05_GMM14BIT;
-
- regs->find_reg(0x05).value &= ~REG05_GMMENB;
-
- /* true CIS gray if needed */
- if (dev->model->is_cis == SANE_TRUE && params.channels == 1
- && dev->settings.true_gray)
- {
- regs->find_reg(0x05).value |= REG05_LEDADD;
- }
- else
- {
- regs->find_reg(0x05).value &= ~REG05_LEDADD;
- }
-
- /* cktoggle, ckdelay and cksel at once, cktdelay=2 => half_ccd for md5345 */
- regs->find_reg(0x18).value = sensor_mst->r18;
-
- /* manual CCD/2 clock programming => half_ccd for hp2300 */
- regs->find_reg(0x1d).value = sensor_mst->r1d;
-
- /* HP2400 1200dpi mode tuning */
-
- if (dev->model->ccd_type == CCD_HP2400)
- {
- /* reset count of dummy lines to zero */
- regs->find_reg(0x1e).value &= ~REG1E_LINESEL;
- if (params.xres >= 1200)
- {
- /* there must be one dummy line */
- regs->find_reg(0x1e).value |= 1 & REG1E_LINESEL;
-
- /* GPO12 need to be set to zero */
- regs->find_reg(0x66).value &= ~0x20;
- }
- else
- {
- /* set GPO12 back to one */
- regs->find_reg(0x66).value |= 0x20;
- }
- }
-
- /* motor steps used */
- regs->find_reg(0x21).value = motor->steps1;
- regs->find_reg(0x22).value = motor->fwdbwd;
- regs->find_reg(0x23).value = motor->fwdbwd;
- regs->find_reg(0x24).value = motor->steps1;
-
- /* scanned area height must be enlarged by max color shift needed */
- max_shift=sanei_genesys_compute_max_shift(dev,params.channels, params.yres, 0);
-
- /* we adjust linecnt according to real motor dpi */
- linecnt = (linecnt * motor->ydpi) / params.yres + max_shift;
-
- /* at QUATER_STEP lines are 'staggered' and need correction */
- stagger = 0;
- if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- {
- /* for HP3670, stagger happens only at >=1200 dpi */
- if ((dev->model->motor_type != MOTOR_HP3670 && dev->model->motor_type != MOTOR_HP2400)
- || params.yres >= (unsigned) sensor.optical_res)
- {
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- }
- }
- linecnt += stagger;
-
- DBG(DBG_info, "%s : max_shift=%d, stagger=%d lines\n", __func__, max_shift, stagger);
-
- /* CIS scanners read one line per color channel
- * since gray mode use 'add' we also read 3 channels even not in
- * color mode */
- if (dev->model->is_cis == SANE_TRUE)
- {
- sanei_genesys_set_triple(regs, REG_LINCNT, linecnt * 3);
- linecnt *= params.channels;
- }
- else
- {
- sanei_genesys_set_triple(regs, REG_LINCNT, linecnt);
- }
-
- /* scanner's x coordinates are expressed in physical DPI but they must be divided by cksel */
- sx = startx / sensor_mst->cksel;
- ex = endx / sensor_mst->cksel;
- if (half_ccd == SANE_TRUE)
- {
- sx /= 2;
- ex /= 2;
- }
- sanei_genesys_set_double(regs, REG_STRPIXEL, sx);
- sanei_genesys_set_double(regs, REG_ENDPIXEL, ex);
- DBG(DBG_info, "%s: startx=%d, endx=%d, half_ccd=%d\n", __func__, sx, ex, half_ccd);
-
- /* words_per_line must be computed according to the scan's resolution */
- /* in fact, words_per_line _gives_ the actual scan resolution */
- words_per_line = (((endx - startx) * sensor_mst->xdpi) / sensor.optical_res);
- bpp=params.depth/8;
- if (params.depth == 1)
- {
- words_per_line = (words_per_line+7)/8 ;
- bpp=1;
- }
- else
- {
- words_per_line *= bpp;
- }
- dev->bpl = words_per_line;
- words_per_line *= params.channels;
- dev->wpl = words_per_line;
-
- DBG(DBG_info, "%s: wpl=%d\n", __func__, words_per_line);
- sanei_genesys_set_triple(regs, REG_MAXWD, words_per_line);
-
- sanei_genesys_set_double(regs, REG_DPISET, sensor_mst->dpiset);
- sanei_genesys_set_double(regs, REG_LPERIOD, sensor_mst->exposure);
-
- /* move distance must be adjusted to take into account the extra lines
- * read to reorder data */
- feedl = move;
- if (stagger + max_shift > 0 && feedl != 0)
- {
- if (feedl >
- ((max_shift + stagger) * dev->motor.optical_ydpi) / motor->ydpi)
- feedl =
- feedl -
- ((max_shift + stagger) * dev->motor.optical_ydpi) / motor->ydpi;
- }
-
- /* we assume all scans are done with 2 tables */
- /*
- feedl = feed_steps - fast_slope_steps*2 -
- (slow_slope_steps >> scan_step_type); */
- /* but head has moved due to shading calibration => dev->scanhead_position_in_steps */
- if (feedl > 0)
- {
- /* take into account the distance moved during calibration */
- /* feedl -= dev->scanhead_position_in_steps; */
- DBG(DBG_info, "%s: initial move=%d\n", __func__, feedl);
- DBG(DBG_info, "%s: scanhead_position_in_steps=%d\n", __func__,
- dev->scanhead_position_in_steps);
-
- /* TODO clean up this when I'll fully understand.
- * for now, special casing each motor */
- switch (dev->model->motor_type)
- {
- case MOTOR_5345:
- switch (motor->ydpi)
- {
- case 200:
- feedl -= 70;
- break;
- case 300:
- feedl -= 70;
- break;
- case 400:
- feedl += 130;
- break;
- case 600:
- feedl += 160;
- break;
- case 1200:
- feedl += 160;
- break;
- case 2400:
- feedl += 180;
- break;
- default:
- break;
- }
- break;
- case MOTOR_HP2300:
- switch (motor->ydpi)
- {
- case 75:
- feedl -= 180;
- break;
- case 150:
- feedl += 0;
- break;
- case 300:
- feedl += 30;
- break;
- case 600:
- feedl += 35;
- break;
- case 1200:
- feedl += 45;
- break;
- default:
- break;
- }
- break;
- case MOTOR_HP2400:
- switch (motor->ydpi)
- {
- case 150:
- feedl += 150;
- break;
- case 300:
- feedl += 220;
- break;
- case 600:
- feedl += 260;
- break;
- case 1200:
- feedl += 280; /* 300 */
- break;
- case 50:
- feedl += 0;
- break;
- case 100:
- feedl += 100;
- break;
- default:
- break;
- }
- break;
-
- /* theorical value */
- default:
- if (motor->fastfed)
- {
- feedl =
- feedl - 2 * motor->steps2 -
- (motor->steps1 >> motor->steptype);
- }
- else
- {
- feedl = feedl - (motor->steps1 >> motor->steptype);
- }
- break;
- }
- /* security */
- if (feedl < 0)
- feedl = 0;
- }
-
- DBG(DBG_info, "%s: final move=%d\n", __func__, feedl);
- sanei_genesys_set_triple(regs, REG_FEEDL, feedl);
-
- regs->find_reg(0x65).value = motor->mtrpwm;
-
- sanei_genesys_calculate_zmode2 (regs->find_reg(0x02).value & REG02_FASTFED,
- sensor_mst->exposure,
- slope_table1,
- motor->steps1,
- move, motor->fwdbwd, &z1, &z2);
-
- /* no z1/z2 for sheetfed scanners */
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- z1 = 0;
- z2 = 0;
- }
- sanei_genesys_set_double(regs, REG_Z1MOD, z1);
- sanei_genesys_set_double(regs, REG_Z2MOD, z2);
- regs->find_reg(0x6b).value = motor->steps2;
- regs->find_reg(0x6c).value =
- (regs->find_reg(0x6c).value & REG6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16)
- & 0x07);
-
- RIE (write_control (dev, sensor, xresolution));
-
- /* setup analog frontend */
- RIE (gl646_set_fe(dev, sensor, AFE_SET, xresolution));
-
- /* now we're done with registers setup values used by data transfer */
- /* we setup values needed for the data transfer */
-
- /* we must use a round number of words_per_line */
- requested_buffer_size = 8 * words_per_line;
- read_buffer_size =
- 2 * requested_buffer_size +
- ((max_shift + stagger) * params.pixels * params.channels * params.depth) / 8;
-
- dev->read_buffer.clear();
- dev->read_buffer.alloc(read_buffer_size);
-
- dev->lines_buffer.clear();
- dev->lines_buffer.alloc(read_buffer_size);
-
- dev->shrink_buffer.clear();
- dev->shrink_buffer.alloc(requested_buffer_size);
-
- dev->out_buffer.clear();
- dev->out_buffer.alloc(8 * params.pixels * params.channels * bpp);
-
- /* scan bytes to read */
- dev->read_bytes_left = words_per_line * linecnt;
-
- DBG(DBG_info, "%s: physical bytes to read = %lu\n", __func__, (u_long) dev->read_bytes_left);
- dev->read_active = SANE_TRUE;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels =
- ((endx - startx) * sensor_mst->xdpi) / sensor.optical_res;
- dev->current_setup.lines = linecnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = sensor_mst->exposure;
- dev->current_setup.xres = sensor_mst->xdpi;
- dev->current_setup.yres = motor->ydpi;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
- /* total_bytes_to_read is the number of byte to send to frontend
- * total_bytes_read is the number of bytes sent to frontend
- * read_bytes_left is the number of bytes to read from the scanner
- */
- dev->total_bytes_read = 0;
- if (params.depth == 1) {
- dev->total_bytes_to_read = ((params.pixels * params.lines) / 8 +
- (((params.pixels * params.lines) % 8) ? 1 : 0)) * params.channels;
- } else {
- dev->total_bytes_to_read = params.pixels * params.lines * params.channels * bpp;
- }
-
- /* select color filter based on settings */
- regs->find_reg(0x04).value &= ~REG04_FILTER;
- if (params.channels == 1) {
- switch (params.color_filter) {
- case ColorFilter::RED:
- regs->find_reg(0x04).value |= 0x04;
- break;
- case ColorFilter::GREEN:
- regs->find_reg(0x04).value |= 0x08;
- break;
- case ColorFilter::BLUE:
- regs->find_reg(0x04).value |= 0x0c;
- break;
- default:
- break;
- }
- }
-
- DBG(DBG_proc, "%s: end\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-
-/** copy sensor specific settings */
-/* *dev : device infos
- *regs : regiters to be set
- extended : do extended set up
- half_ccd: set up for half ccd resolution
- all registers 08-0B, 10-1D, 52-5E are set up. They shouldn't
- appear anywhere else but in register init
-*/
-static void
-gl646_setup_sensor (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * regs)
-{
- (void) dev;
- DBG(DBG_proc, "%s: start\n", __func__);
-
- for (const auto& reg_setting : sensor.custom_regs) {
- regs->set8(reg_setting.address, reg_setting.value);
- }
- // FIXME: all other drivers don't set exposure here
- sanei_genesys_set_exposure(*regs, sensor.exposure);
-
- DBG(DBG_proc, "%s: end\n", __func__);
-}
-
-/** Test if the ASIC works
- */
-static SANE_Status
-gl646_asic_test (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- size_t size, verify_size;
- unsigned int i;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* set and read exposure time, compare if it's the same */
- status = sanei_genesys_write_register (dev, 0x38, 0xde);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_write_register (dev, 0x39, 0xad);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_read_register (dev, 0x4e, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (val != 0xde) /* value of register 0x38 */
- {
- DBG(DBG_error, "%s: register contains invalid value\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- status = sanei_genesys_read_register (dev, 0x4f, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (val != 0xad) /* value of register 0x39 */
- {
- DBG(DBG_error, "%s: register contains invalid value\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- /* ram test: */
- size = 0x40000;
- verify_size = size + 0x80;
- /* todo: looks like the read size must be a multiple of 128?
- otherwise the read doesn't succeed the second time after the scanner has
- been plugged in. Very strange. */
-
- std::vector<uint8_t> data(size);
- std::vector<uint8_t> verify_data(verify_size);
-
- for (i = 0; i < (size - 1); i += 2)
- {
- data[i] = i / 512;
- data[i + 1] = (i / 2) % 256;
- }
-
- status = sanei_genesys_set_buffer_address (dev, 0x0000);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_bulk_write_data(dev, 0x3c, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_set_buffer_address (dev, 0x0000);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status =
- gl646_bulk_read_data (dev, 0x45, verify_data.data(), verify_size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* i + 2 is needed as the changed address goes into effect only after one
- data word is sent. */
- for (i = 0; i < size; i++)
- {
- if (verify_data[i + 2] != data[i])
- {
- DBG(DBG_error, "%s: data verification error\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
- }
-
- DBG(DBG_info, "%s: end\n", __func__);
-
- return SANE_STATUS_GOOD;
-}
-
-/**
- * Set all registers to default values after init
- * @param dev scannerr's device to set
- */
-static void
-gl646_init_regs (Genesys_Device * dev)
-{
- int addr;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- dev->reg.clear();
-
- for (addr = 1; addr <= 0x0b; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x10; addr <= 0x29; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x2c; addr <= 0x39; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x3d; addr <= 0x3f; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x52; addr <= 0x5e; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x60; addr <= 0x6d; addr++)
- dev->reg.init_reg(addr, 0);
-
- dev->reg.find_reg(0x01).value = 0x20 /*0x22 */ ; /* enable shading, CCD, color, 1M */
- dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */
- if (dev->model->motor_type == MOTOR_5345)
- dev->reg.find_reg(0x02).value |= 0x01; /* half-step */
- switch (dev->model->motor_type)
- {
- case MOTOR_5345:
- dev->reg.find_reg(0x02).value |= 0x01; /* half-step */
- break;
- case MOTOR_XP200:
- /* for this sheetfed scanner, no AGOHOME, nor backtracking */
- dev->reg.find_reg(0x02).value = 0x50;
- break;
- default:
- break;
- }
- dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ; /* lamp on */
- 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? */
- switch (dev->model->dac_type)
- {
- case DAC_AD_XP200:
- dev->reg.find_reg(0x04).value = 0x12;
- break;
- default:
- /* Wolfson frontend */
- dev->reg.find_reg(0x04).value = 0x13;
- break;
- }
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- dev->reg.find_reg(0x05).value = 0x00; /* 12 bits gamma, disable gamma, 24 clocks/pixel */
- switch (sensor.optical_res)
- {
- case 600:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_600;
- break;
- case 1200:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_1200;
- break;
- case 2400:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_2400;
- break;
- default:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW;
- break;
- }
- if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA)
- dev->reg.find_reg(0x05).value |= REG05_GMM14BIT;
- if (dev->model->dac_type == DAC_AD_XP200)
- dev->reg.find_reg(0x05).value |= 0x01; /* 12 clocks/pixel */
-
- if (dev->model->ccd_type == CCD_HP2300)
- dev->reg.find_reg(0x06).value = 0x00; /* PWRBIT off, shading gain=4, normal AFE image capture */
- else
- dev->reg.find_reg(0x06).value = 0x18; /* PWRBIT on, shading gain=8, normal AFE image capture */
-
-
- gl646_setup_sensor(dev, sensor, &dev->reg);
-
- dev->reg.find_reg(0x1e).value = 0xf0; /* watch-dog time */
-
- switch (dev->model->ccd_type)
- {
- case CCD_HP2300:
- dev->reg.find_reg(0x1e).value = 0xf0;
- dev->reg.find_reg(0x1f).value = 0x10;
- dev->reg.find_reg(0x20).value = 0x20;
- break;
- case CCD_HP2400:
- dev->reg.find_reg(0x1e).value = 0x80;
- dev->reg.find_reg(0x1f).value = 0x10;
- dev->reg.find_reg(0x20).value = 0x20;
- break;
- case CCD_HP3670:
- dev->reg.find_reg(0x19).value = 0x2a;
- dev->reg.find_reg(0x1e).value = 0x80;
- dev->reg.find_reg(0x1f).value = 0x10;
- dev->reg.find_reg(0x20).value = 0x20;
- break;
- case CIS_XP200:
- dev->reg.find_reg(0x1e).value = 0x10;
- dev->reg.find_reg(0x1f).value = 0x01;
- dev->reg.find_reg(0x20).value = 0x50;
- break;
- default:
- dev->reg.find_reg(0x1f).value = 0x01;
- dev->reg.find_reg(0x20).value = 0x50;
- break;
- }
-
- dev->reg.find_reg(0x21).value = 0x08 /*0x20 */ ; /* table one steps number for forward slope curve of the acc/dec */
- dev->reg.find_reg(0x22).value = 0x10 /*0x08 */ ; /* steps number of the forward steps for start/stop */
- dev->reg.find_reg(0x23).value = 0x10 /*0x08 */ ; /* steps number of the backward steps for start/stop */
- dev->reg.find_reg(0x24).value = 0x08 /*0x20 */ ; /* table one steps number backward slope curve of the acc/dec */
- dev->reg.find_reg(0x25).value = 0x00; /* scan line numbers (7000) */
- dev->reg.find_reg(0x26).value = 0x00 /*0x1b */ ;
- dev->reg.find_reg(0x27).value = 0xd4 /*0x58 */ ;
- dev->reg.find_reg(0x28).value = 0x01; /* PWM duty for lamp control */
- dev->reg.find_reg(0x29).value = 0xff;
-
- dev->reg.find_reg(0x2c).value = 0x02; /* set resolution (600 DPI) */
- dev->reg.find_reg(0x2d).value = 0x58;
- dev->reg.find_reg(0x2e).value = 0x78; /* set black&white threshold high level */
- dev->reg.find_reg(0x2f).value = 0x7f; /* set black&white threshold low level */
-
- dev->reg.find_reg(0x30).value = 0x00; /* begin pixel position (16) */
- dev->reg.find_reg(0x31).value = sensor.dummy_pixel /*0x10 */ ; /* TGW + 2*TG_SHLD + x */
- dev->reg.find_reg(0x32).value = 0x2a /*0x15 */ ; /* end pixel position (5390) */
- dev->reg.find_reg(0x33).value = 0xf8 /*0x0e */ ; /* TGW + 2*TG_SHLD + y */
- dev->reg.find_reg(0x34).value = sensor.dummy_pixel;
- dev->reg.find_reg(0x35).value = 0x01 /*0x00 */ ; /* set maximum word size per line, for buffer full control (10800) */
- dev->reg.find_reg(0x36).value = 0x00 /*0x2a */ ;
- dev->reg.find_reg(0x37).value = 0x00 /*0x30 */ ;
- dev->reg.find_reg(0x38).value = 0x2a; // line period (exposure time = 11000 pixels) */
- dev->reg.find_reg(0x39).value = 0xf8;
- dev->reg.find_reg(0x3d).value = 0x00; /* set feed steps number of motor move */
- dev->reg.find_reg(0x3e).value = 0x00;
- dev->reg.find_reg(0x3f).value = 0x01 /*0x00 */ ;
-
- dev->reg.find_reg(0x60).value = 0x00; /* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
- dev->reg.find_reg(0x61).value = 0x00; /* (21h+22h)/LPeriod */
- dev->reg.find_reg(0x62).value = 0x00; /* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
- dev->reg.find_reg(0x63).value = 0x00; /* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
- dev->reg.find_reg(0x64).value = 0x00; /* motor PWM frequency */
- dev->reg.find_reg(0x65).value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */
- if (dev->model->motor_type == MOTOR_5345)
- dev->reg.find_reg(0x65).value = 0x02; /* PWM duty cycle for table one motor phase (63 = max) */
- dev->reg.find_reg(0x66).value = dev->gpo.value[0];
- dev->reg.find_reg(0x67).value = dev->gpo.value[1];
- dev->reg.find_reg(0x68).value = dev->gpo.enable[0];
- dev->reg.find_reg(0x69).value = dev->gpo.enable[1];
-
- switch (dev->model->motor_type)
- {
- case MOTOR_HP2300:
- case MOTOR_HP2400:
- dev->reg.find_reg(0x6a).value = 0x7f; /* table two steps number for acc/dec */
- dev->reg.find_reg(0x6b).value = 0x78; /* table two steps number for acc/dec */
- dev->reg.find_reg(0x6d).value = 0x7f;
- break;
- case MOTOR_5345:
- dev->reg.find_reg(0x6a).value = 0x42; /* table two fast moving step type, PWM duty for table two */
- dev->reg.find_reg(0x6b).value = 0xff; /* table two steps number for acc/dec */
- dev->reg.find_reg(0x6d).value = 0x41; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
- break;
- case MOTOR_XP200:
- dev->reg.find_reg(0x6a).value = 0x7f; /* table two fast moving step type, PWM duty for table two */
- dev->reg.find_reg(0x6b).value = 0x08; /* table two steps number for acc/dec */
- dev->reg.find_reg(0x6d).value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
- break;
- case MOTOR_HP3670:
- dev->reg.find_reg(0x6a).value = 0x41; /* table two steps number for acc/dec */
- dev->reg.find_reg(0x6b).value = 0xc8; /* table two steps number for acc/dec */
- dev->reg.find_reg(0x6d).value = 0x7f;
- break;
- default:
- dev->reg.find_reg(0x6a).value = 0x40; /* table two fast moving step type, PWM duty for table two */
- dev->reg.find_reg(0x6b).value = 0xff; /* table two steps number for acc/dec */
- dev->reg.find_reg(0x6d).value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
- break;
- }
- dev->reg.find_reg(0x6c).value = 0x00; /* peroid times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
-}
-
-
-/* Send slope table for motor movement
- slope_table in machine byte order
-*/
-static SANE_Status
-gl646_send_slope_table (Genesys_Device * dev, int table_nr,
- uint16_t * slope_table, int steps)
-{
- int dpihw;
- int start_address;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (table_nr = %d, steps = %d)=%d .. %d\n", __func__, table_nr, steps,
- slope_table[0], slope_table[steps - 1]);
-
- dpihw = dev->reg.find_reg(0x05).value >> 6;
-
- if (dpihw == 0) /* 600 dpi */
- start_address = 0x08000;
- else if (dpihw == 1) /* 1200 dpi */
- start_address = 0x10000;
- else if (dpihw == 2) /* 2400 dpi */
- start_address = 0x1f800;
- else /* reserved */
- return SANE_STATUS_INVAL;
-
- std::vector<uint8_t> table(steps * 2);
- for (int i = 0; i < steps; i++)
- {
- table[i * 2] = slope_table[i] & 0xff;
- table[i * 2 + 1] = slope_table[i] >> 8;
- }
-
- status =
- sanei_genesys_set_buffer_address (dev, start_address + table_nr * 0x100);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_bulk_write_data(dev, 0x3c, table.data(), steps * 2);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send slope table: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-/* Set values of Analog Device type frontend */
-static SANE_Status
-gl646_set_ad_fe (Genesys_Device * dev, uint8_t set)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
-
- DBG(DBG_proc, "%s(): start\n", __func__);
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
-
- dev->frontend = dev->frontend_initial;
-
- /* write them to analog frontend */
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write reg0: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write reg1: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
- if (set == AFE_SET)
- {
- for (i = 0; i < 3; i++)
- {
- status = sanei_genesys_fe_write_data(dev, 0x02 + i, dev->frontend.get_gain(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write gain %d: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
- for (i = 0; i < 3; i++)
- {
- status = sanei_genesys_fe_write_data(dev, 0x05 + i, dev->frontend.get_offset(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write offset %d: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
- }
- /*
- if (set == AFE_POWER_SAVE)
- {
- status =
- sanei_genesys_fe_write_data (dev, 0x00, dev->frontend.reg[0] | 0x04);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write reg0: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- } */
- DBG(DBG_proc, "%s(): end\n", __func__);
-
- return status;
-}
-
-/** set up analog frontend
- * set up analog frontend
- * @param dev device to set up
- * @param set action from AFE_SET, AFE_INIT and AFE_POWERSAVE
- * @param dpi resolution of the scan since it affects settings
- * @return SANE_STATUS_GOOD if evrithing OK
- */
-static SANE_Status
-gl646_wm_hp3670(Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set, int dpi)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
-
- DBGSTART;
- switch (set)
- {
- case AFE_INIT:
- status = sanei_genesys_fe_write_data (dev, 0x04, 0x80);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: reset failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_sleep_ms(200);
- RIE (sanei_genesys_write_register (dev, 0x50, 0x00));
- dev->frontend = dev->frontend_initial;
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg1 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x02, dev->frontend.regs.get_value(0x02));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg2 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- gl646_gpio_output_enable(dev->usb_dev, 0x07);
- break;
- case AFE_POWER_SAVE:
- status = sanei_genesys_fe_write_data (dev, 0x01, 0x06);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg1 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data (dev, 0x06, 0x0f);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg6 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- return status;
- break;
- default: /* AFE_SET */
- /* mode setup */
- i = dev->frontend.regs.get_value(0x03);
- if (dpi > sensor.optical_res / 2)
- {
- /* fe_reg_0x03 must be 0x12 for 1200 dpi in DAC_WOLFSON_HP3670.
- * DAC_WOLFSON_HP2400 in 1200 dpi mode works well with
- * fe_reg_0x03 set to 0x32 or 0x12 but not to 0x02 */
- i = 0x12;
- }
- status = sanei_genesys_fe_write_data (dev, 0x03, i);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg3 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- /* offset and sign (or msb/lsb ?) */
- for (i = 0; i < 3; i++)
- {
- status =
- sanei_genesys_fe_write_data(dev, 0x20 + i, dev->frontend.get_offset(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing offset%d failed: %s\n", __func__, i,
- sane_strstatus (status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x24 + i,
- dev->frontend.regs.get_value(0x24 + i)); /* MSB/LSB ? */
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing sign%d failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
-
- /* gain */
- for (i = 0; i < 3; i++)
- {
- status =
- sanei_genesys_fe_write_data(dev, 0x28 + i, dev->frontend.get_gain(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing gain%d failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/** Set values of analog frontend
- * @param dev device to set
- * @param set action to execute
- * @param dpi dpi to setup the AFE
- * @return error or SANE_STATUS_GOOD */
-static SANE_Status
-gl646_set_fe(Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set, int dpi)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- uint8_t val;
-
- DBG(DBG_proc, "%s (%s,%d)\n", __func__, set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
- AFE_POWER_SAVE ? "powersave" : "huh?", dpi);
-
- /* Analog Device type frontend */
- if ((dev->reg.find_reg(0x04).value & REG04_FESET) == 0x02)
- return gl646_set_ad_fe (dev, set);
-
- /* Wolfson type frontend */
- if ((dev->reg.find_reg(0x04).value & REG04_FESET) != 0x03)
- {
- DBG(DBG_proc, "%s(): unsupported frontend type %d\n", __func__,
- dev->reg.find_reg(0x04).value & REG04_FESET);
- return SANE_STATUS_UNSUPPORTED;
- }
-
- /* per frontend function to keep code clean */
- switch (dev->model->dac_type)
- {
- case DAC_WOLFSON_HP3670:
- case DAC_WOLFSON_HP2400:
- return gl646_wm_hp3670(dev, sensor, set, dpi);
- break;
- default:
- DBG(DBG_proc, "%s(): using old method\n", __func__);
- break;
- }
-
- /* initialize analog frontend */
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
- dev->frontend = dev->frontend_initial;
-
- /* reset only done on init */
- status = sanei_genesys_fe_write_data (dev, 0x04, 0x80);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: init fe failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* enable GPIO for some models */
- if (dev->model->ccd_type == CCD_HP2300)
- {
- val = 0x07;
- gl646_gpio_output_enable(dev->usb_dev, val);
- }
- return status;
- }
-
- /* set fontend to power saving mode */
- if (set == AFE_POWER_SAVE)
- {
- status = sanei_genesys_fe_write_data (dev, 0x01, 0x02);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing data failed: %s\n", __func__, sane_strstatus(status));
- }
- return status;
- }
-
- /* here starts AFE_SET */
- /* TODO : base this test on cfg reg3 or a CCD family flag to be created */
- /* if (dev->model->ccd_type != CCD_HP2300
- && dev->model->ccd_type != CCD_HP3670
- && dev->model->ccd_type != CCD_HP2400) */
- {
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg0 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x02, dev->frontend.regs.get_value(0x02));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg2 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- /* start with reg3 */
- status = sanei_genesys_fe_write_data(dev, 0x03, dev->frontend.regs.get_value(0x03));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg3 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- switch (dev->model->ccd_type)
- {
- default:
- for (i = 0; i < 3; i++)
- {
- status =
- sanei_genesys_fe_write_data(dev, 0x24 + i,
- dev->frontend.regs.get_value(0x24 + i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing sign[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x28 + i, dev->frontend.get_gain(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing gain[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x20 + i, dev->frontend.get_offset(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing offset[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
- break;
- /* just can't have it to work ....
- case CCD_HP2300:
- case CCD_HP2400:
- case CCD_HP3670:
-
- status =
- sanei_genesys_fe_write_data(dev, 0x23, dev->frontend.get_offset(1));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing offset[1] failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x28, dev->frontend.get_gain(1));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing gain[1] failed: %s\n", __func__, sane_strstatus (status));
- return status;
- }
- break; */
- }
-
- /* end with reg1 */
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg1 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
-
- DBG(DBG_proc, "%s: end\n", __func__);
-
- return SANE_STATUS_GOOD;
-}
-
-/** Set values of analog frontend
- * this this the public interface, the gl646 as to use one more
- * parameter to work effectively, hence the redirection
- * @param dev device to set
- * @param set action to execute
- * @return error or SANE_STATUS_GOOD */
-static SANE_Status
-gl646_public_set_fe (Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set)
-{
- return gl646_set_fe(dev, sensor, set, dev->settings.yres);
-}
-
-/**
- * enters or leaves power saving mode
- * limited to AFE for now.
- * @param dev scanner's device
- * @param enable SANE_TRUE to enable power saving, SANE_FALSE to leave it
- * @return allways SANE_STATUS_GOOD
- */
-static
-SANE_Status
-gl646_save_power (Genesys_Device * dev, SANE_Bool enable)
-{
-
- DBGSTART;
- DBG(DBG_info, "%s: enable = %d\n", __func__, enable);
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- if (enable)
- {
- /* gl646_set_fe(dev, sensor, AFE_POWER_SAVE); */
- }
- else
- {
- gl646_set_fe(dev, sensor, AFE_INIT, 0);
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl646_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
- int rate, exposure_time, tgtime, time;
-
- DBG(DBG_proc, "%s (delay = %d)\n", __func__, delay);
-
- local_reg.init_reg(0x01, dev->reg.get8(0x01)); // disable fastmode
- local_reg.init_reg(0x03, dev->reg.get8(0x03)); // Lamp power control
- local_reg.init_reg(0x05, dev->reg.get8(0x05) & ~REG05_BASESEL); // 24 clocks/pixel
- local_reg.init_reg(0x38, 0x00); // line period low
- local_reg.init_reg(0x39, 0x00); //line period high
- local_reg.init_reg(0x6c, 0x00); // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE
-
- if (!delay)
- local_reg.find_reg(0x03).value &= 0xf0; /* disable lampdog and set lamptime = 0 */
- else if (delay < 20)
- local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */
- else
- local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */
-
- time = delay * 1000 * 60; /* -> msec */
- exposure_time =
- (uint32_t) (time * 32000.0 /
- (24.0 * 64.0 * (local_reg.get8(0x03) & REG03_LAMPTIM) *
- 1024.0) + 0.5);
- /* 32000 = system clock, 24 = clocks per pixel */
- rate = (exposure_time + 65536) / 65536;
- if (rate > 4)
- {
- rate = 8;
- tgtime = 3;
- }
- else if (rate > 2)
- {
- rate = 4;
- tgtime = 2;
- }
- else if (rate > 1)
- {
- rate = 2;
- tgtime = 1;
- }
- else
- {
- rate = 1;
- tgtime = 0;
- }
-
- local_reg.find_reg(0x6c).value |= tgtime << 6;
- exposure_time /= rate;
-
- if (exposure_time > 65535)
- exposure_time = 65535;
-
- local_reg.find_reg(0x38).value = exposure_time / 256;
- local_reg.find_reg(0x39).value = exposure_time & 255;
-
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: Failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-
-/**
- * loads document into scanner
- * currently only used by XP200
- * bit2 (0x04) of gpio is paper event (document in/out) on XP200
- * HOMESNR is set if no document in front of sensor, the sequence of events is
- * paper event -> document is in the sheet feeder
- * HOMESNR becomes 0 -> document reach sensor
- * HOMESNR becomes 1 ->document left sensor
- * paper event -> document is out
- */
-static SANE_Status
-gl646_load_document (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- // FIXME: sequential not really needed in this case
- Genesys_Register_Set regs(Genesys_Register_Set::SEQUENTIAL);
- unsigned int used, vfinal, count;
- uint16_t slope_table[255];
- uint8_t val;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* no need to load document is flatbed scanner */
- if (dev->model->is_sheetfed == SANE_FALSE)
- {
- DBG(DBG_proc, "%s: nothing to load\n", __func__);
- DBG(DBG_proc, "%s: end\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read status: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* HOMSNR is set if a document is inserted */
- if ((val & REG41_HOMESNR))
- {
- /* if no document, waits for a paper event to start loading */
- /* with a 60 seconde minutes timeout */
- count = 0;
- do
- {
- gl646_gpio_read(dev->usb_dev, &val);
-
- DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, val);
- if ((val & 0x04) != 0x04)
- {
- DBG(DBG_warn, "%s: no paper detected\n", __func__);
- }
- sanei_genesys_sleep_ms(200);
- count++;
- }
- while (((val & 0x04) != 0x04) && (count < 300)); /* 1 min time out */
- if (count == 300)
- {
- DBG(DBG_error, "%s: timeout waiting for document\n", __func__);
- return SANE_STATUS_NO_DOCS;
- }
- }
-
- /* set up to fast move before scan then move until document is detected */
- regs.init_reg(0x01, 0x90);
-
- /* AGOME, 2 slopes motor moving */
- regs.init_reg(0x02, 0x79);
-
- /* motor feeding steps to 0 */
- regs.init_reg(0x3d, 0);
- regs.init_reg(0x3e, 0);
- regs.init_reg(0x3f, 0);
-
- /* 50 fast moving steps */
- regs.init_reg(0x6b, 50);
-
- /* set GPO */
- regs.init_reg(0x66, 0x30);
-
- /* stesp NO */
- regs.init_reg(0x21, 4);
- regs.init_reg(0x22, 1);
- regs.init_reg(0x23, 1);
- regs.init_reg(0x24, 4);
-
- /* generate slope table 2 */
- sanei_genesys_generate_slope_table (slope_table,
- 50,
- 51,
- 2400,
- 6000, 2400, 50, 0.25, &used, &vfinal);
-/* document loading:
- * send regs
- * start motor
- * wait e1 status to become e0
- */
- status = gl646_send_slope_table (dev, 1, slope_table, 50);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send slope table 1: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl646_start_motor (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
-
- count = 0;
- do
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read status: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_sleep_ms(200);
- count++;
- }
- while ((val & REG41_MOTMFLG) && (count < 300));
- if (count == 300)
- {
- DBG(DBG_error, "%s: can't load document\n", __func__);
- return SANE_STATUS_JAMMED;
- }
-
- /* when loading OK, document is here */
- dev->document = SANE_TRUE;
-
- /* set up to idle */
- regs.set8(0x02, 0x71);
- regs.set8(0x3f, 1);
- regs.set8(0x6b, 8);
- status = sanei_genesys_bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write idle registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: end\n", __func__);
-
- return status;
-}
-
-/**
- * detects end of document and adjust current scan
- * to take it into account
- * used by sheetfed scanners
- */
-static SANE_Status
-gl646_detect_document_end (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val, gpio;
- unsigned int bytes_left, lines;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* test for document presence */
- RIE (sanei_genesys_get_status (dev, &val));
- if (DBG_LEVEL > DBG_info)
- {
- print_status (val);
- }
- gl646_gpio_read(dev->usb_dev, &gpio);
- DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
-
- /* detect document event. There one event when the document go in,
- * then another when it leaves */
- if ((dev->document == SANE_TRUE) && (gpio & 0x04)
- && (dev->total_bytes_read > 0))
- {
- DBG(DBG_info, "%s: no more document\n", __func__);
- dev->document = SANE_FALSE;
-
- /* adjust number of bytes to read:
- * total_bytes_to_read is the number of byte to send to frontend
- * total_bytes_read is the number of bytes sent to frontend
- * read_bytes_left is the number of bytes to read from the scanner
- */
- DBG(DBG_io, "%s: total_bytes_to_read=%lu\n", __func__, (u_long) dev->total_bytes_to_read);
- DBG(DBG_io, "%s: total_bytes_read =%lu\n", __func__, (u_long) dev->total_bytes_read);
- DBG(DBG_io, "%s: read_bytes_left =%lu\n", __func__, (u_long) dev->read_bytes_left);
-
- /* amount of data available from scanner is what to scan */
- status = sanei_genesys_read_valid_words (dev, &bytes_left);
-
- /* we add the number of lines needed to read the last part of the document in */
- lines =
- (SANE_UNFIX (dev->model->y_offset) * dev->current_setup.yres) /
- MM_PER_INCH;
- DBG(DBG_io, "%s: adding %d line to flush\n", __func__, lines);
- bytes_left += lines * dev->wpl;
- if (dev->current_setup.depth > 8)
- {
- bytes_left = 2 * bytes_left;
- }
- if (dev->current_setup.channels > 1)
- {
- bytes_left = 3 * bytes_left;
- }
- if (bytes_left < dev->read_bytes_left)
- {
- dev->total_bytes_to_read = dev->total_bytes_read + bytes_left;
- dev->read_bytes_left = bytes_left;
- }
- DBG(DBG_io, "%s: total_bytes_to_read=%lu\n", __func__, (u_long) dev->total_bytes_to_read);
- DBG(DBG_io, "%s: total_bytes_read =%lu\n", __func__, (u_long) dev->total_bytes_read);
- DBG(DBG_io, "%s: read_bytes_left =%lu\n", __func__, (u_long) dev->read_bytes_left);
- }
- DBG(DBG_proc, "%s: end\n", __func__);
-
- return status;
-}
-
-/**
- * eject document from the feeder
- * currently only used by XP200
- * TODO we currently rely on AGOHOME not being set for sheetfed scanners,
- * maybe check this flag in eject to let the document being eject automaticaly
- */
-static SANE_Status
-gl646_eject_document (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- // FIXME: SEQUENTIAL not really needed in this case
- Genesys_Register_Set regs((Genesys_Register_Set::SEQUENTIAL));
- unsigned int used, vfinal, count;
- uint16_t slope_table[255];
- uint8_t gpio, state;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* at the end there will be noe more document */
- dev->document = SANE_FALSE;
-
- // first check for document event
- gl646_gpio_read(dev->usb_dev, &gpio);
-
- DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
-
- /* test status : paper event + HOMESNR -> no more doc ? */
- status = sanei_genesys_get_status (dev, &state);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read status: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- DBG(DBG_info, "%s: state=0x%02x\n", __func__, state);
- if (DBG_LEVEL > DBG_info)
- {
- print_status (state);
- }
-
- /* HOMSNR=0 if no document inserted */
- if ((state & REG41_HOMESNR) != 0)
- {
- dev->document = SANE_FALSE;
- DBG(DBG_info, "%s: no more document to eject\n", __func__);
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
- }
-
- /* there is a document inserted, eject it */
- status = sanei_genesys_write_register (dev, 0x01, 0xb0);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* wait for motor to stop */
- do
- {
- sanei_genesys_sleep_ms(200);
- status = sanei_genesys_get_status (dev, &state);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read status: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
- while (state & REG41_MOTMFLG);
-
- /* set up to fast move before scan then move until document is detected */
- regs.init_reg(0x01, 0xb0);
-
- /* AGOME, 2 slopes motor moving , eject 'backward' */
- regs.init_reg(0x02, 0x5d);
-
- /* motor feeding steps to 119880 */
- regs.init_reg(0x3d, 1);
- regs.init_reg(0x3e, 0xd4);
- regs.init_reg(0x3f, 0x48);
-
- /* 60 fast moving steps */
- regs.init_reg(0x6b, 60);
-
- /* set GPO */
- regs.init_reg(0x66, 0x30);
-
- /* stesp NO */
- regs.init_reg(0x21, 4);
- regs.init_reg(0x22, 1);
- regs.init_reg(0x23, 1);
- regs.init_reg(0x24, 4);
-
- /* generate slope table 2 */
- sanei_genesys_generate_slope_table (slope_table,
- 60,
- 61,
- 1600,
- 10000, 1600, 60, 0.25, &used, &vfinal);
-/* document eject:
- * send regs
- * start motor
- * wait c1 status to become c8 : HOMESNR and ~MOTFLAG
- */
- status = gl646_send_slope_table (dev, 1, slope_table, 60);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send slope table 1: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl646_start_motor (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus (status));
- return SANE_STATUS_IO_ERROR;
- }
-
- /* loop until paper sensor tells paper is out, and till motor is running */
- /* use a 30 timeout */
- count = 0;
- do
- {
- status = sanei_genesys_get_status (dev, &state);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read status: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- print_status (state);
- sanei_genesys_sleep_ms(200);
- count++;
- }
- while (((state & REG41_HOMESNR) == 0) && (count < 150));
-
- // read GPIO on exit
- gl646_gpio_read(dev->usb_dev, &gpio);
-
- DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
-
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-/* Send the low-level scan command */
-static SANE_Status
-gl646_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SANE_Bool start_motor)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
- // FIXME: SEQUENTIAL not really needed in this case
- Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
-
- DBG(DBG_proc, "%s\n", __func__);
-
- local_reg.init_reg(0x03, sanei_genesys_read_reg_from_set(reg, 0x03));
- local_reg.init_reg(0x01, sanei_genesys_read_reg_from_set(reg, 0x01) | REG01_SCAN); /* set scan bit */
-
- if (start_motor) {
- local_reg.init_reg(0x0f, 0x01);
- } else {
- local_reg.init_reg(0x0f, 0x00); // do not start motor yet
- }
-
- status = sanei_genesys_bulk_write_register(dev, local_reg);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: end\n", __func__);
-
- return status;
-}
-
-
-/* Send the stop scan command */
-static SANE_Status
-end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop, SANE_Bool eject)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i = 0;
- uint8_t val, scanfsh = 0;
-
- DBG(DBG_proc, "%s (check_stop = %d, eject = %d)\n", __func__, check_stop, eject);
-
- /* we need to compute scanfsh before cancelling scan */
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (val & REG41_SCANFSH)
- scanfsh = 1;
- if (DBG_LEVEL > DBG_io2)
- {
- print_status (val);
- }
- }
-
- /* ends scan */
- val = sanei_genesys_read_reg_from_set (reg, 0x01);
- val &= ~REG01_SCAN;
- sanei_genesys_set_reg_from_set (reg, 0x01, val);
- status = sanei_genesys_write_register (dev, 0x01, val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register 01: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* for sheetfed scanners, we may have to eject document */
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- if (eject == SANE_TRUE && dev->document == SANE_TRUE)
- {
- status = gl646_eject_document (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to eject document\n", __func__);
- return status;
- }
- }
- if (check_stop)
- {
- for (i = 0; i < 30; i++) /* do not wait longer than wait 3 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read register: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- if (val & REG41_SCANFSH)
- scanfsh = 1;
- if (DBG_LEVEL > DBG_io2)
- {
- print_status (val);
- }
-
- if (!(val & REG41_MOTMFLG) && (val & REG41_FEEDFSH) && scanfsh)
- {
- DBG(DBG_proc, "%s: scanfeed finished\n", __func__);
- break; /* leave for loop */
- }
-
- sanei_genesys_sleep_ms(100);
- }
- }
- }
- else /* flat bed scanners */
- {
- if (check_stop)
- {
- for (i = 0; i < 300; i++) /* do not wait longer than wait 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read register: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- if (val & REG41_SCANFSH)
- scanfsh = 1;
- if (DBG_LEVEL > DBG_io)
- {
- print_status (val);
- }
-
- if (!(val & REG41_MOTMFLG) && (val & REG41_FEEDFSH) && scanfsh)
- {
- DBG(DBG_proc, "%s: scanfeed finished\n", __func__);
- break; /* leave while loop */
- }
-
- if ((!(val & REG41_MOTMFLG)) && (val & REG41_HOMESNR))
- {
- DBG(DBG_proc, "%s: head at home\n", __func__);
- break; /* leave while loop */
- }
-
- sanei_genesys_sleep_ms(100);
- }
- }
- }
-
- DBG(DBG_proc, "%s: end (i=%u)\n", __func__, i);
-
- return status;
-}
-
-/* Send the stop scan command */
-static SANE_Status
-gl646_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop)
-{
- return end_scan (dev, reg, check_stop, SANE_FALSE);
-}
-
-/**
- * parks head
- * @param dev scanner's device
- * @param wait_until_home true if the function waits until head parked
- */
-static
-SANE_Status
-gl646_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Settings settings;
- uint8_t val;
- int i;
- int loop = 0;
-
- DBG(DBG_proc, "%s: start , wait_until_home = %d\n", __func__, wait_until_home);
-
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL > DBG_io)
- {
- print_status (val);
- }
-
- dev->scanhead_position_in_steps = 0;
-
- if (val & REG41_HOMESNR) /* is sensor at home? */
- {
- DBG(DBG_info, "%s: end since already at home\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- /* stop motor if needed */
- if (val & REG41_MOTMFLG)
- {
- status = gl646_stop_motor (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop motor: %s\n", __func__, sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
- sanei_genesys_sleep_ms(200);
- }
-
- /* when scanhead is moving then wait until scanhead stops or timeout */
- DBG(DBG_info, "%s: ensuring that motor is off\n", __func__);
- val = REG41_MOTMFLG;
- for (i = 400; i > 0 && (val & REG41_MOTMFLG); i--) /* do not wait longer than 40 seconds, count down to get i = 0 when busy */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to read home sensor & motor status: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- if (((val & (REG41_MOTMFLG | REG41_HOMESNR)) == REG41_HOMESNR)) /* at home and motor is off */
- {
- DBG(DBG_info, "%s: already at home and not moving\n", __func__);
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- }
-
- if (!i) /* the loop counted down to 0, scanner still is busy */
- {
- DBG(DBG_error, "%s: motor is still on: device busy\n", __func__);
- return SANE_STATUS_DEVICE_BUSY;
- }
-
- /* setup for a backward scan of 65535 steps, with no actual data reading */
- settings.scan_method = ScanMethod::FLATBED;
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- settings.xres = get_lowest_resolution(dev->model->ccd_type, 1);
- settings.yres = settings.xres;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels = 600;
- settings.lines = 1;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres);
-
- status = setup_for_scan(dev, sensor, &dev->reg, settings, SANE_TRUE, SANE_TRUE, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup for scan: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* backward , no actual data scanned TODO more setup flags to avoid this register manipulations ? */
- dev->reg.find_reg(0x02).value |= REG02_MTRREV;
- dev->reg.find_reg(0x01).value &= ~REG01_SCAN;
- sanei_genesys_set_triple(&dev->reg, REG_FEEDL, 65535);
-
- /* sets frontend */
- status = gl646_set_fe(dev, sensor, AFE_SET, settings.xres);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set frontend: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* write scan registers */
- try {
- status = sanei_genesys_bulk_write_register(dev, dev->reg);
- } catch (...) {
- DBG(DBG_error, "%s: failed to bulk write registers\n", __func__);
- }
- if (status != SANE_STATUS_GOOD)
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
-
- /* registers are restored to an iddl state, give up if no head to park */
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- DBG(DBG_proc, "%s: end \n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- /* starts scan */
- status = gl646_begin_scan(dev, sensor, &dev->reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: \n", __func__);
- return status;
- }
-
- /* loop until head parked */
- if (wait_until_home)
- {
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to read home sensor: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- if (val & 0x08) /* home sensor */
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- DBG(DBG_proc, "%s: end\n", __func__);
- sanei_genesys_sleep_ms(500);
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl646_stop_motor (dev);
- end_scan(dev, &dev->reg, SANE_TRUE, SANE_FALSE);
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
-
- DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/**
- * Automatically set top-left edge of the scan area by scanning an
- * area at 300 dpi from very top of scanner
- * @param dev device stucture describing the scanner
- * @return SANE_STATUS_GOOD in cas of success, else failure code
- */
-static SANE_Status
-gl646_search_start_position (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Settings settings;
- unsigned int resolution, x, y;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* we scan at 300 dpi */
- resolution = get_closest_resolution(dev->model->ccd_type, 300, 1);
-
- // FIXME: the current approach of doing search only for one resolution does not work on scanners
- // whith employ different sensors with potentially different settings.
- auto& sensor = sanei_genesys_find_sensor_for_write(dev, resolution);
-
- /* fill settings for a gray level scan */
- settings.scan_method = ScanMethod::FLATBED;
- settings.scan_mode = ScanColorMode::GRAY;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels = 600;
- settings.lines = dev->model->search_lines;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* scan the desired area */
- std::vector<uint8_t> data;
- status = simple_scan(dev, sensor, settings, SANE_TRUE, SANE_TRUE, SANE_FALSE, data);
-
- /* process data if scan is OK */
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: simple_scan failed\n", __func__);
- DBGCOMPLETED;
- return status;
- }
-
-
- /* handle stagger case : reorder gray data and thus loose some lines */
- if (dev->current_setup.stagger > 0)
- {
- DBG(DBG_proc, "%s: 'un-staggering'\n", __func__);
- for (y = 0; y < settings.lines - dev->current_setup.stagger; y++)
- {
- /* one point out of 2 is 'unaligned' */
- for (x = 0; x < settings.pixels; x += 2)
- {
- data[y * settings.pixels + x] =
- data[(y + dev->current_setup.stagger) * settings.pixels +
- x];
- }
- }
- /* correct line number */
- settings.lines -= dev->current_setup.stagger;
- }
- if (DBG_LEVEL >= DBG_data)
- {
- sanei_genesys_write_pnm_file("gl646_search_position.pnm", data.data(), settings.depth, 1,
- settings.pixels, settings.lines);
- }
-
- /* now search reference points on the data */
- status =
- sanei_genesys_search_reference_point (dev, sensor, data.data(),
- sensor.CCD_start_xoffset,
- resolution, settings.pixels,
- settings.lines);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set search reference point: %s\n", __func__,
- sane_strstatus(status));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * internally overriden during effective calibration
- * sets up register for coarse gain calibration
- */
-static SANE_Status
-gl646_init_regs_for_coarse_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- (void) sensor;
- (void) regs;
-
- DBG(DBG_proc, "%s\n", __func__);
- DBG(DBG_proc, "%s: end\n", __func__);
-
- /* to make compilers happy ... */
- if (!dev)
- {
- return SANE_STATUS_INVAL;
- }
-
- return SANE_STATUS_GOOD;
-}
-
-
-/**
- * init registers for shading calibration
- * we assume that scanner's head is on an area suiting shading calibration.
- * We scan a full scan width area by the shading line number for the device
- * at either at full sensor's resolution or half depending upon half_ccd
- * @param dev scanner's device
- * @return SANE_STATUS_GOOD if success, else error code
- */
-static SANE_Status
-gl646_init_regs_for_shading(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- (void) regs;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Settings settings;
- /* 1: no half_ccd, 2: use half number of pixels */
- int half_ccd = 1;
- int cksel = 1;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* fill settings for scan : always a color scan */
- int channels = 3;
-
- if (sensor.ccd_size_divisor > 1)
- {
- // when shading all (full width) line, we must adapt to half_ccd case
- if (is_half_ccd(dev->model->ccd_type, dev->settings.xres, channels) == SANE_TRUE)
- {
- half_ccd = 2;
- }
- }
-
- settings.scan_method = dev->settings.scan_method;
- settings.scan_mode = dev->settings.scan_mode;
- if (dev->model->is_cis == SANE_FALSE)
- {
- // FIXME: always a color scan, but why don't we set scan_mode to COLOR_SINGLE_PASS always?
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- }
- settings.xres = sensor.optical_res / half_ccd;
- cksel = get_cksel(dev->model->ccd_type, dev->settings.xres, channels);
- settings.xres = settings.xres / cksel;
- settings.yres = settings.xres;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels =
- (sensor.sensor_pixels * settings.xres) / sensor.optical_res;
- dev->calib_lines = dev->model->shading_lines;
- settings.lines = dev->calib_lines * (3 - half_ccd);
- settings.depth = 16;
- settings.color_filter = dev->settings.color_filter;
-
- settings.disable_interpolation = dev->settings.disable_interpolation;
- settings.threshold = dev->settings.threshold;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* keep account of the movement for final scan move */
- dev->scanhead_position_in_steps += settings.lines;
-
- /* we don't want top offset, but we need right margin to be the same
- * than the one for the final scan */
- status = setup_for_scan(dev, sensor, &dev->reg, settings, SANE_TRUE, SANE_FALSE, SANE_FALSE);
-
- /* used when sending shading calibration data */
- dev->calib_pixels = settings.pixels;
- dev->calib_channels = dev->current_setup.channels;
- if (dev->model->is_cis == SANE_FALSE)
- {
- dev->calib_channels = 3;
- }
-
- /* no shading */
- dev->reg.find_reg(0x01).value &= ~REG01_DVDSET;
- dev->reg.find_reg(0x02).value |= REG02_ACDCDIS; /* ease backtracking */
- dev->reg.find_reg(0x02).value &= ~(REG02_FASTFED | REG02_AGOHOME);
- dev->reg.find_reg(0x05).value &= ~REG05_GMMENB;
- sanei_genesys_set_motor_power(dev->reg, false);
-
- /* TODO another flag to setup regs ? */
- /* enforce needed LINCNT, getting rid of extra lines for color reordering */
- if (dev->model->is_cis == SANE_FALSE)
- {
- sanei_genesys_set_triple(&dev->reg, REG_LINCNT, dev->calib_lines);
- }
- else
- {
- sanei_genesys_set_triple(&dev->reg, REG_LINCNT, dev->calib_lines * 3);
- }
-
- /* copy reg to calib_reg */
- dev->calib_reg = dev->reg;
-
- /* this is an hack to make calibration cache working .... */
- /* if we don't do this, cache will be identified at the shading calibration
- * dpi which is different from calibration one */
- dev->current_setup.xres = dev->settings.xres;
- DBG(DBG_info, "%s:\n\tdev->settings.xres=%d\n\tdev->settings.yres=%d\n", __func__,
- dev->settings.xres, dev->settings.yres);
-
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-static bool gl646_needs_home_before_init_regs_for_scan(Genesys_Device* dev)
-{
- return (dev->scanhead_position_in_steps > 0 &&
- dev->settings.scan_method == ScanMethod::FLATBED);
-}
-
-/**
- * set up registers for the actual scan. The scan's parameters are given
- * through the device settings. It allocates the scan buffers.
- */
-static SANE_Status
-gl646_init_regs_for_scan (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
-
- RIE(setup_for_scan(dev, sensor, &dev->reg, dev->settings, SANE_FALSE, SANE_TRUE, SANE_TRUE));
-
- /* gamma is only enabled at final scan time */
- if (dev->settings.depth < 16)
- dev->reg.find_reg(0x05).value |= REG05_GMMENB;
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * set up registers for the actual scan. The scan's parameters are given
- * through the device settings. It allocates the scan buffers.
- * @param dev scanner's device
- * @param regs registers to set up
- * @param settings settings of scan
- * @param split SANE_TRUE if move to scan area is split from scan, SANE_FALSE is
- * scan first moves to area
- * @param xcorrection take x geometry correction into account (fixed and detected offsets)
- * @param ycorrection take y geometry correction into account
- */
-static SANE_Status
-setup_for_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set *regs,
- Genesys_Settings settings,
- SANE_Bool split,
- SANE_Bool xcorrection,
- SANE_Bool ycorrection)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Int depth;
- int channels;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- if (settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- channels = 3;
- }
- else
- {
- channels = 1;
- }
-
- depth=settings.depth;
- if (settings.scan_mode == ScanColorMode::LINEART)
- {
- if (settings.dynamic_lineart == SANE_TRUE)
- {
- depth = 8;
- }
- else
- {
- /* XXX STEF XXX : why does the common layer never send depth=1 ? */
- depth = 1;
- }
- }
-
- // compute distance to move
- float move = 0;
- // XXX STEF XXX MD5345 -> optical_ydpi, other base_ydpi => half/full step ? */
- if (split == SANE_FALSE) {
- if (dev->model->is_sheetfed == SANE_FALSE) {
- if (ycorrection == SANE_TRUE) {
- move = SANE_UNFIX(dev->model->y_offset);
- }
-
- // add tl_y to base movement
- }
- move += settings.tl_y;
-
- if (move < 0) {
- DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move);
- move = 0;
- }
- }
- move = (move * dev->motor.optical_ydpi) / MM_PER_INCH;
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- float start = settings.tl_x;
- if (xcorrection) {
- if (settings.scan_method == ScanMethod::FLATBED) {
- start += SANE_UNFIX(dev->model->x_offset);
- } else {
- start += SANE_UNFIX(dev->model->x_offset_ta);
- }
- }
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- SetupParams params;
- params.xres = settings.xres;
- params.yres = settings.yres;
- params.startx = start;
- params.starty = move;
- params.pixels = settings.pixels;
- params.lines = settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = settings.scan_mode;
- params.color_filter = settings.color_filter;
- params.flags = 0;
- if (settings.scan_method == ScanMethod::TRANSPARENCY) {
- params.flags |= SCAN_FLAG_USE_XPA;
- }
-
- uint16_t slope_table0[256] = {};
- uint16_t slope_table1[256] = {};
-
- /* set up correct values for scan (gamma and shading enabled) */
- status = gl646_setup_registers(dev, sensor, regs, params, slope_table0, slope_table1,
- xcorrection);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed setup registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* send computed slope tables */
- status =
- gl646_send_slope_table (dev, 0, slope_table0,
- sanei_genesys_read_reg_from_set (regs, 0x21));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send slope table 0: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status =
- gl646_send_slope_table (dev, 1, slope_table1,
- sanei_genesys_read_reg_from_set (regs, 0x6b));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send slope table 1: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * this function sen gamm table to ASIC
- */
-static SANE_Status
-gl646_send_gamma_table (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int size;
- int address;
- SANE_Status status = SANE_STATUS_GOOD;
- int bits;
-
- DBGSTART;
-
- /* gamma table size */
- if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA)
- {
- size = 16384;
- bits = 14;
- }
- else
- {
- size = 4096;
- bits = 12;
- }
-
- /* allocate temporary gamma tables: 16 bits words, 3 channels */
- std::vector<uint8_t> gamma(size * 2 * 3);
-
- RIE(sanei_genesys_generate_gamma_buffer(dev, sensor, bits, size-1, size, gamma.data()));
-
- /* table address */
- switch (dev->reg.find_reg(0x05).value >> 6)
- {
- case 0: /* 600 dpi */
- address = 0x09000;
- break;
- case 1: /* 1200 dpi */
- address = 0x11000;
- break;
- case 2: /* 2400 dpi */
- address = 0x20000;
- break;
- default:
- return SANE_STATUS_INVAL;
- }
-
- /* send address */
- status = sanei_genesys_set_buffer_address (dev, address);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* send data */
- status = sanei_genesys_bulk_write_data(dev, 0x3c, gamma.data(), size * 2 * 3);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send gamma table: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief this function does the led calibration.
- * this function does the led calibration by scanning one line of the calibration
- * area below scanner's top on white strip. The scope of this function is
- * currently limited to the XP200
- */
-static SANE_Status
-gl646_led_calibration (Genesys_Device * dev, Genesys_Sensor& sensor, Genesys_Register_Set& regs)
-{
- (void) regs;
- int total_size;
- unsigned int i, j;
- SANE_Status status = SANE_STATUS_GOOD;
- int val;
- unsigned int channels;
- int avg[3], avga, avge;
- int turn;
- uint16_t expr, expg, expb;
- Genesys_Settings settings;
- SANE_Int resolution;
-
- SANE_Bool acceptable = SANE_FALSE;
-
- DBG(DBG_proc, "%s\n", __func__);
- if (!dev->model->is_cis)
- {
- DBG(DBG_proc, "%s: not a cis scanner, nothing to do...\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- /* get led calibration resolution */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- channels = 3;
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- }
- else
- {
- channels = 1;
- settings.scan_mode = ScanColorMode::GRAY;
- }
- resolution = get_closest_resolution(dev->model->ccd_type, sensor.optical_res, channels);
-
- /* offset calibration is always done in color mode */
- settings.scan_method = ScanMethod::FLATBED;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels =
- (sensor.sensor_pixels * resolution) / sensor.optical_res;
- settings.lines = 1;
- settings.depth = 16;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* colors * bytes_per_color * scan lines */
- total_size = settings.pixels * channels * 2 * 1;
-
- std::vector<uint8_t> line(total_size);
-
-/*
- we try to get equal bright leds here:
-
- loop:
- average per color
- adjust exposure times
-
- Sensor_Master uint8_t regs_0x10_0x15[6];
- */
- expr = sensor.exposure.red;
- expg = sensor.exposure.green;
- expb = sensor.exposure.blue;
-
- turn = 0;
-
- do
- {
- sensor.exposure.red = expr;
- sensor.exposure.green = expg;
- sensor.exposure.blue = expb;
-
- DBG(DBG_info, "%s: starting first line reading\n", __func__);
-
- status = simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl646_led_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, line.data(), 16, channels, settings.pixels, 1);
- }
-
- acceptable = SANE_TRUE;
-
- for (j = 0; j < channels; j++)
- {
- avg[j] = 0;
- for (i = 0; i < settings.pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * settings.pixels + 1] * 256 +
- line[i * 2 + j * 2 * settings.pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- avg[j] += val;
- }
-
- avg[j] /= settings.pixels;
- }
-
- DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
-
- acceptable = SANE_TRUE;
-
- if (!acceptable)
- {
- avga = (avg[0] + avg[1] + avg[2]) / 3;
- expr = (expr * avga) / avg[0];
- expg = (expg * avga) / avg[1];
- expb = (expb * avga) / avg[2];
-
- /* keep exposure time in a working window */
- avge = (expr + expg + expb) / 3;
- if (avge > 0x2000)
- {
- expr = (expr * 0x2000) / avge;
- expg = (expg * 0x2000) / avge;
- expb = (expb * 0x2000) / avge;
- }
- if (avge < 0x400)
- {
- expr = (expr * 0x400) / avge;
- expg = (expg * 0x400) / avge;
- expb = (expb * 0x400) / avge;
- }
- }
-
- turn++;
-
- }
- while (!acceptable && turn < 100);
-
- DBG(DBG_info,"%s: acceptable exposure: 0x%04x,0x%04x,0x%04x\n", __func__, expr, expg, expb);
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * average dark pixels of a scan
- */
-static int
-dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
- unsigned int channels, unsigned int black)
-{
- unsigned int i, j, k, average, count;
- unsigned int avg[3];
- uint8_t val;
-
- /* computes average value on black margin */
- for (k = 0; k < channels; k++)
- {
- avg[k] = 0;
- count = 0;
- for (i = 0; i < lines; i++)
- {
- for (j = 0; j < black; j++)
- {
- val = data[i * channels * pixels + j + k];
- avg[k] += val;
- count++;
- }
- }
- if (count)
- avg[k] /= count;
- DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
- }
- average = 0;
- for (i = 0; i < channels; i++)
- average += avg[i];
- average /= channels;
- DBG(DBG_info, "%s: average = %d\n", __func__, average);
- return average;
-}
-
-
-/** @brief calibration for AD frontend devices
- * we do simple scan until all black_pixels are higher than 0,
- * raising offset at each turn.
- */
-static SANE_Status
-ad_fe_offset_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- unsigned int channels;
- int pass = 0;
- SANE_Int resolution;
- Genesys_Settings settings;
- unsigned int x, y, adr, min;
- unsigned int bottom, black_pixels;
-
- DBG(DBG_proc, "%s: start\n", __func__);
- channels = 3;
- resolution = get_closest_resolution(dev->model->ccd_type, sensor.optical_res, channels);
- black_pixels =
- (sensor.black_pixels * resolution) / sensor.optical_res;
- DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
-
- settings.scan_method = ScanMethod::FLATBED;
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels =
- (sensor.sensor_pixels * resolution) / sensor.optical_res;
- settings.lines = CALIBRATION_LINES;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* scan first line of data with no gain */
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
-
- std::vector<uint8_t> line;
-
- /* scan with no move */
- bottom = 1;
- do
- {
- pass++;
- dev->frontend.set_offset(0, bottom);
- dev->frontend.set_offset(1, bottom);
- dev->frontend.set_offset(2, bottom);
- status =
- simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to scan first line\n", __func__);
- return status;
- }
- if (DBG_LEVEL >= DBG_data)
- {
- char title[30];
- snprintf(title, 30, "gl646_offset%03d.pnm", (int)bottom);
- sanei_genesys_write_pnm_file (title, line.data(), 8, channels,
- settings.pixels, settings.lines);
- }
-
- min = 0;
- for (y = 0; y < settings.lines; y++)
- {
- for (x = 0; x < black_pixels; x++)
- {
- adr = (x + y * settings.pixels) * channels;
- if (line[adr] > min)
- min = line[adr];
- if (line[adr + 1] > min)
- min = line[adr + 1];
- if (line[adr + 2] > min)
- min = line[adr + 2];
- }
- }
-
- DBG(DBG_io2, "%s: pass=%d, min=%d\n", __func__, pass, min);
- bottom++;
- }
- while (pass < 128 && min == 0);
- if (pass == 128)
- {
- DBG(DBG_error, "%s: failed to find correct offset\n", __func__);
- return SANE_STATUS_INVAL;
- }
-
- DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-#define DARK_TARGET 8
-/**
- * This function does the offset calibration by scanning one line of the calibration
- * area below scanner's top. There is a black margin and the remaining is white.
- * genesys_search_start() must have been called so that the offsets and margins
- * are already known.
- * @param dev scanner's device
- * @return SANE_STATUS_GOOD if success, else error code is failure
-*/
-static SANE_Status
-gl646_offset_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- (void) regs;
- SANE_Status status = SANE_STATUS_GOOD;
- unsigned int channels;
- int pass = 0, avg;
- SANE_Int resolution;
- Genesys_Settings settings;
- int topavg, bottomavg;
- int top, bottom, black_pixels;
-
- /* Analog Device fronted have a different calibration */
- if (dev->model->dac_type == DAC_AD_XP200)
- {
- return ad_fe_offset_calibration (dev, sensor);
- }
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* setup for a RGB scan, one full sensor's width line */
- /* resolution is the one from the final scan */
- channels = 3;
- if (dev->settings.xres > sensor.optical_res) {
- resolution = get_closest_resolution(dev->model->ccd_type, sensor.optical_res, channels);
- } else {
- resolution = get_closest_resolution(dev->model->ccd_type, dev->settings.xres, channels);
- }
- black_pixels =
- (sensor.black_pixels * resolution) / sensor.optical_res;
- DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
-
- settings.scan_method = ScanMethod::FLATBED;
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels =
- (sensor.sensor_pixels * resolution) / sensor.optical_res;
- settings.lines = CALIBRATION_LINES;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* scan first line of data with no gain, but with offset from
- * last calibration */
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
-
- /* scan with no move */
- bottom = 90;
- dev->frontend.set_offset(0, bottom);
- dev->frontend.set_offset(1, bottom);
- dev->frontend.set_offset(2, bottom);
-
- std::vector<uint8_t> first_line, second_line;
-
- status = simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, first_line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to scan first line\n", __func__);
- return status;
- }
- if (DBG_LEVEL >= DBG_data)
- {
- char title[30];
- snprintf(title, 30, "gl646_offset%03d.pnm", bottom);
- sanei_genesys_write_pnm_file(title, first_line.data(), 8, channels,
- settings.pixels, settings.lines);
- }
- bottomavg = dark_average(first_line.data(), settings.pixels, settings.lines, channels,
- black_pixels);
- DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
-
- /* now top value */
- top = 231;
- dev->frontend.set_offset(0, top);
- dev->frontend.set_offset(1, top);
- dev->frontend.set_offset(2, top);
- status = simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, second_line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to scan first line\n", __func__);
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- char title[30];
- snprintf(title, 30, "gl646_offset%03d.pnm", top);
- sanei_genesys_write_pnm_file (title, second_line.data(), 8, channels,
- settings.pixels, settings.lines);
- }
- topavg = dark_average(second_line.data(), settings.pixels, settings.lines, channels,
- black_pixels);
- DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
-
- /* loop until acceptable level */
- while ((pass < 32) && (top - bottom > 1))
- {
- pass++;
-
- /* settings for new scan */
- dev->frontend.set_offset(0, (top + bottom) / 2);
- dev->frontend.set_offset(1, (top + bottom) / 2);
- dev->frontend.set_offset(2, (top + bottom) / 2);
-
- /* scan with no move */
- status = simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, second_line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to scan first line\n", __func__);
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- char title[30];
- snprintf(title, 30, "gl646_offset%03d.pnm", dev->frontend.get_offset(1));
- sanei_genesys_write_pnm_file (title, second_line.data(), 8, channels,
- settings.pixels, settings.lines);
- }
-
- avg =
- dark_average (second_line.data(), settings.pixels, settings.lines, channels,
- black_pixels);
- DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
-
- /* compute new boundaries */
- if (topavg == avg)
- {
- topavg = avg;
- top = dev->frontend.get_offset(1);
- }
- else
- {
- bottomavg = avg;
- bottom = dev->frontend.get_offset(1);
- }
- }
-
- /* in case of debug do a final scan to get result */
- if (DBG_LEVEL >= DBG_data)
- {
- status = simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, second_line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to scan final line\n", __func__);
- return status;
- }
- sanei_genesys_write_pnm_file("gl646_offset-final.pnm", second_line.data(), 8, channels,
- settings.pixels, settings.lines);
- }
-
- DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-/** @brief gain calibration for Analog Device frontends
- * Alternative coarse gain calibration
- */
-static SANE_Status
-ad_fe_coarse_gain_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi)
-{
- (void) regs;
- unsigned int i, channels, val;
- unsigned int size, count, resolution, pass;
- SANE_Status status = SANE_STATUS_GOOD;
- float average;
- Genesys_Settings settings;
- char title[32];
-
- DBGSTART;
-
- /* setup for a RGB scan, one full sensor's width line */
- /* resolution is the one from the final scan */
- channels = 3;
- resolution = get_closest_resolution(dev->model->ccd_type, dpi, channels);
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
-
- settings.scan_method = ScanMethod::FLATBED;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels =
- (sensor.sensor_pixels * resolution) / sensor.optical_res;
- settings.lines = CALIBRATION_LINES;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- size = channels * settings.pixels * settings.lines;
-
- /* start gain value */
- dev->frontend.set_gain(0, 1);
- dev->frontend.set_gain(1, 1);
- dev->frontend.set_gain(2, 1);
-
- average = 0;
- pass = 0;
-
- std::vector<uint8_t> line;
-
- /* loop until each channel raises to acceptable level */
- while ((average < sensor.gain_white_ref) && (pass < 30))
- {
- /* scan with no move */
- status =
- simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to scan first line\n", __func__);
- return status;
- }
-
- /* log scanning data */
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf (title, "gl646_alternative_gain%02d.pnm", (int)pass);
- sanei_genesys_write_pnm_file(title, line.data(), 8, channels, settings.pixels,
- settings.lines);
- }
- pass++;
-
- /* computes white average */
- average = 0;
- count = 0;
- for (i = 0; i < size; i++)
- {
- val = line[i];
- average += val;
- count++;
- }
- average = average / count;
-
- uint8_t gain0 = dev->frontend.get_gain(0);
- // adjusts gain for the channel
- if (average < sensor.gain_white_ref) {
- gain0 += 1;
- }
-
- dev->frontend.set_gain(0, gain0);
- dev->frontend.set_gain(1, gain0);
- dev->frontend.set_gain(2, gain0);
-
- DBG(DBG_proc, "%s: average = %.2f, gain = %d\n", __func__, average, gain0);
- }
-
- DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__,
- dev->frontend.get_gain(0),
- dev->frontend.get_gain(1),
- dev->frontend.get_gain(2));
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * Alternative coarse gain calibration
- * this on uses the settings from offset_calibration. First scan moves so
- * we can go to calibration area for XPA.
- * @param dev device for scan
- * @param dpi resolutnio to calibrate at
- */
-static SANE_Status
-gl646_coarse_gain_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi)
-{
- unsigned int i, j, k, channels, val, maximum, idx;
- unsigned int count, resolution, pass;
- SANE_Status status = SANE_STATUS_GOOD;
- float average[3];
- Genesys_Settings settings;
- char title[32];
-
- if (dev->model->ccd_type == CIS_XP200)
- {
- return ad_fe_coarse_gain_calibration (dev, sensor, regs, sensor.optical_res);
- }
- DBGSTART;
-
- /* setup for a RGB scan, one full sensor's width line */
- /* resolution is the one from the final scan */
- channels = 3;
-
- /* we are searching a sensor resolution */
- if (dpi > sensor.optical_res) {
- resolution = sensor.optical_res;
- } else {
- resolution = get_closest_resolution(dev->model->ccd_type, dev->settings.xres, channels);
- }
-
- settings.scan_method = dev->settings.scan_method;
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_y = 0;
- if (settings.scan_method == ScanMethod::FLATBED)
- {
- settings.tl_x = 0;
- settings.pixels = (sensor.sensor_pixels * resolution) / sensor.optical_res;
- }
- else
- {
- settings.tl_x = SANE_UNFIX (dev->model->x_offset_ta);
- settings.pixels = (SANE_UNFIX (dev->model->x_size_ta) * resolution) / MM_PER_INCH;
- }
- settings.lines = CALIBRATION_LINES;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* start gain value */
- dev->frontend.set_gain(0, 1);
- dev->frontend.set_gain(1, 1);
- dev->frontend.set_gain(2, 1);
-
- if (channels > 1)
- {
- average[0] = 0;
- average[1] = 0;
- average[2] = 0;
- idx = 0;
- }
- else
- {
- average[0] = 255;
- average[1] = 255;
- average[2] = 255;
- switch (dev->settings.color_filter) {
- case ColorFilter::RED: idx = 0; break;
- case ColorFilter::GREEN: idx = 1; break;
- case ColorFilter::BLUE: idx = 2; break;
- default: idx = 0; break; // should not happen
- }
- average[idx] = 0;
- }
- pass = 0;
-
- std::vector<uint8_t> line;
-
- /* loop until each channel raises to acceptable level */
- while (((average[0] < sensor.gain_white_ref)
- || (average[1] < sensor.gain_white_ref)
- || (average[2] < sensor.gain_white_ref)) && (pass < 30))
- {
- /* scan with no move */
- status =
- simple_scan(dev, sensor, settings, SANE_FALSE, SANE_TRUE, SANE_FALSE, line);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to scan first line\n", __func__);
- return status;
- }
-
- /* log scanning data */
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf (title, "gl646_gain%02d.pnm", (int)pass);
- sanei_genesys_write_pnm_file(title, line.data(), 8, channels, settings.pixels,
- settings.lines);
- }
- pass++;
-
- /* average high level for each channel and compute gain
- to reach the target code
- we only use the central half of the CCD data */
- for (k = idx; k < idx + channels; k++)
- {
- /* we find the maximum white value, so we can deduce a threshold
- to average white values */
- maximum = 0;
- for (i = 0; i < settings.lines; i++)
- {
- for (j = 0; j < settings.pixels; j++)
- {
- val = line[i * channels * settings.pixels + j + k];
- if (val > maximum)
- maximum = val;
- }
- }
-
- /* threshold */
- maximum *= 0.9;
-
- /* computes white average */
- average[k] = 0;
- count = 0;
- for (i = 0; i < settings.lines; i++)
- {
- for (j = 0; j < settings.pixels; j++)
- {
- /* averaging only white points allow us not to care about dark margins */
- val = line[i * channels * settings.pixels + j + k];
- if (val > maximum)
- {
- average[k] += val;
- count++;
- }
- }
- }
- average[k] = average[k] / count;
-
- /* adjusts gain for the channel */
- if (average[k] < sensor.gain_white_ref)
- dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1);
-
- DBG(DBG_proc, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k],
- dev->frontend.get_gain(k));
- }
- }
-
- if (channels < 3) {
- dev->frontend.set_gain(1, dev->frontend.get_gain(0));
- dev->frontend.set_gain(2, dev->frontend.get_gain(0));
- }
-
- DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__,
- dev->frontend.get_gain(0),
- dev->frontend.get_gain(1),
- dev->frontend.get_gain(2));
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * sets up the scanner's register for warming up. We scan 2 lines without moving.
- *
- */
-static SANE_Status
-gl646_init_regs_for_warmup (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * local_reg,
- int *channels, int *total_size)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Settings settings;
- int resolution, lines;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- dev->frontend = dev->frontend_initial;
-
- resolution = get_closest_resolution(dev->model->ccd_type, 300, 1);
-
- /* set up for a half width 2 lines gray scan without moving */
- settings.scan_method = ScanMethod::FLATBED;
- settings.scan_mode = ScanColorMode::GRAY;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels =
- (sensor.sensor_pixels * resolution) / sensor.optical_res;
- settings.lines = 2;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* setup for scan */
- status = setup_for_scan(dev, sensor, &dev->reg, settings, SANE_TRUE, SANE_FALSE, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: setup_for_scan failed (%s)\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* we are not going to move, so clear these bits */
- dev->reg.find_reg(0x02).value &= ~(REG02_FASTFED | REG02_AGOHOME);
-
- /* don't enable any correction for this scan */
- dev->reg.find_reg(0x01).value &= ~REG01_DVDSET;
-
- /* copy to local_reg */
- *local_reg = dev->reg;
-
- /* turn off motor during this scan */
- sanei_genesys_set_motor_power(*local_reg, false);
-
- /* returned value to higher level warmup function */
- *channels = 1;
- uint32_t value = 0;
- sanei_genesys_get_triple(local_reg, REG_LINCNT, &value);
- lines = value + 1;
- *total_size = lines * settings.pixels;
-
- /* now registers are ok, write them to scanner */
- RIE (gl646_set_fe(dev, sensor, AFE_SET, settings.xres));
- RIE(sanei_genesys_bulk_write_register(dev, *local_reg));
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/*
- * this function moves head without scanning, forward, then backward
- * so that the head goes to park position.
- * as a by-product, also check for lock
- */
-static SANE_Status
-gl646_repark_head (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Settings settings;
- unsigned int expected, steps;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- settings.scan_method = ScanMethod::FLATBED;
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- settings.xres = get_closest_resolution(dev->model->ccd_type, 75, 1);
- settings.yres = settings.xres;
- settings.tl_x = 0;
- settings.tl_y = 5;
- settings.pixels = 600;
- settings.lines = 4;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres);
-
- status = setup_for_scan(dev, sensor, &dev->reg, settings, SANE_FALSE, SANE_FALSE, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup for scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* TODO seems wrong ... no effective scan */
- dev->reg.find_reg(0x01).value &= ~REG01_SCAN;
-
- status = sanei_genesys_bulk_write_register(dev, dev->reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* start scan */
- status = gl646_begin_scan(dev, sensor, &dev->reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: \n", __func__);
- return status;
- }
-
- uint32_t value32 = 0;
- sanei_genesys_get_triple(&dev->reg, REG_FEEDL, &value32);
- expected = value32;
- do
- {
- sanei_genesys_sleep_ms(100);
- status = sanei_genesys_read_feed_steps (dev, &steps);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read feed steps: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
- while (steps < expected);
-
- /* toggle motor flag, put an huge step number and redo move backward */
- status = gl646_slow_back_home (dev, 1);
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-/* *
- * initialize ASIC : registers, motor tables, and gamma tables
- * then ensure scanner's head is at home
- * @param dev device description of the scanner to initailize
- * @return SANE_STATUS_GOOD if success, error code if failure
- */
-static SANE_Status
-gl646_init (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- struct timeval tv;
- uint8_t cold = 0, val = 0;
- uint32_t addr = 0xdead;
- size_t len;
-
- DBG_INIT ();
- DBG(DBG_proc, "%s: start\n", __func__);
-
- /* to detect real power up condition, we write to REG41
- * with pwrbit set, then read it back. When scanner is cold (just replugged)
- * PWRBIT will be set in the returned value
- */
- RIE (sanei_genesys_get_status (dev, &cold));
- DBG(DBG_info, "%s: status=0x%02x\n", __func__, cold);
- cold = !(cold & REG41_PWRBIT);
- if (cold)
- {
- DBG(DBG_info, "%s: device is cold\n", __func__);
- }
- else
- {
- DBG(DBG_info, "%s: device is hot\n", __func__);
- }
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- /* if scanning session hasn't been initialized, set it up */
- if (!dev->already_initialized)
- {
- dev->dark_average_data.clear();
- dev->white_average_data.clear();
-
- dev->settings.color_filter = ColorFilter::GREEN;
- gettimeofday (&tv, NULL);
- dev->init_date = tv.tv_sec;
-
- /* Set default values for registers */
- gl646_init_regs (dev);
-
- /* Init shading data */
- RIE (sanei_genesys_init_shading_data(dev, sensor, sensor.sensor_pixels));
-
- /* initial calibration reg values */
- dev->calib_reg = dev->reg;
- }
-
- /* execute physical unit init only if cold */
- if (cold)
- {
- DBG(DBG_info, "%s: device is cold\n", __func__);
-
- val = 0x04;
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_INIT, INDEX, 1, &val);
-
- /* ASIC reset */
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
- sanei_genesys_sleep_ms(100);
-
- /* Write initial registers */
- RIE(sanei_genesys_bulk_write_register(dev, dev->reg));
-
- /* Test ASIC and RAM */
- if (!(dev->model->flags & GENESYS_FLAG_LAZY_INIT))
- {
- RIE (gl646_asic_test (dev));
- }
-
- /* send gamma tables if needed */
- status = gl646_send_gamma_table(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send generic gamma tables: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* Set powersaving (default = 15 minutes) */
- RIE (gl646_set_powersaving (dev, 15));
- } /* end if cold */
-
- /* Set analog frontend */
- RIE (gl646_set_fe(dev, sensor, AFE_INIT, 0));
-
- /* GPO enabling for XP200 */
- if (dev->model->ccd_type == CIS_XP200)
- {
- sanei_genesys_write_register (dev, 0x68, dev->gpo.enable[0]);
- sanei_genesys_write_register (dev, 0x69, dev->gpo.enable[1]);
-
- // enable GPIO
- gl646_gpio_output_enable(dev->usb_dev, 6);
-
- // writes 0 to GPIO
- gl646_gpio_write(dev->usb_dev, 0);
-
- // clear GPIO enable
- gl646_gpio_output_enable(dev->usb_dev, 0);
-
- sanei_genesys_write_register (dev, 0x66, 0x10);
- sanei_genesys_write_register (dev, 0x66, 0x00);
- sanei_genesys_write_register (dev, 0x66, 0x10);
- }
-
- /* MD6471/G2410 and XP200 read/write data from an undocumented memory area which
- * is after the second slope table */
- if (dev->model->gpo_type != GPO_HP3670
- && dev->model->gpo_type != GPO_HP2400)
- {
- switch (sensor.optical_res)
- {
- case 600:
- addr = 0x08200;
- break;
- case 1200:
- addr = 0x10200;
- break;
- case 2400:
- addr = 0x1fa00;
- break;
- }
- status = sanei_genesys_set_buffer_address (dev, addr);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up control address\n", __func__);
- return SANE_STATUS_INVAL;
- }
- sanei_usb_set_timeout (2 * 1000);
- len = 6;
- status = gl646_bulk_read_data (dev, 0x45, dev->control, len);
- /* for some reason, read fails here for MD6471, HP2300 and XP200
- * one time out of 2 scanimage launches
- */
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_warn, "%s: failed to read control\n", __func__);
- status = gl646_bulk_read_data (dev, 0x45, dev->control, len);
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_warn, "%s: failed to read control\n", __func__);
- return SANE_STATUS_INVAL;
- }
- else
- {
- DBG(DBG_info, "%s: control read=0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__,
- dev->control[0], dev->control[1], dev->control[2], dev->control[3], dev->control[4],
- dev->control[5]);
- }
- sanei_usb_set_timeout (30 * 1000);
- }
- else
- /* HP2400 and HP3670 case */
- {
- dev->control[0] = 0x00;
- dev->control[1] = 0x00;
- dev->control[2] = 0x01;
- dev->control[3] = 0x00;
- dev->control[4] = 0x00;
- dev->control[5] = 0x00;
- }
-
- /* ensure head is correctly parked, and check lock */
- if (dev->model->is_sheetfed == SANE_FALSE)
- {
- if (dev->model->flags & GENESYS_FLAG_REPARK)
- {
- status = gl646_repark_head (dev);
- if (status != SANE_STATUS_GOOD)
- {
- if (status == SANE_STATUS_INVAL)
- {
- DBG(DBG_error0, "Your scanner is locked. Please move the lock switch to the "
- "unlocked position\n");
- return SANE_STATUS_JAMMED;
- }
- else
- DBG(DBG_error, "%s: gl646_repark_head failed: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
- else
- {
- RIE (gl646_slow_back_home (dev, SANE_TRUE));
- }
- }
-
- /* here session and device are initialized */
- dev->already_initialized = SANE_TRUE;
-
- DBG(DBG_proc, "%s: end\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-static
-SANE_Status
-gl646_move_to_ta (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
- if (simple_move (dev, SANE_UNFIX (dev->model->y_offset_calib_ta)) !=
- SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move to calibration area\n", __func__);
- return status;
- }
- DBGCOMPLETED;
- return status;
-}
-
-
-/**
- * Does a simple scan: ie no line reordering and avanced data buffering and
- * shading correction. Memory for data is allocated in this function
- * and must be freed by caller.
- * @param dev device of the scanner
- * @param settings parameters of the scan
- * @param move SANE_TRUE if moving during scan
- * @param forward SANE_TRUE if moving forward during scan
- * @param shading SANE_TRUE to enable shading correction
- * @param data pointer for the data
- */
-static SANE_Status
-simple_scan (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Settings settings, SANE_Bool move,
- SANE_Bool forward, SANE_Bool shading, std::vector<uint8_t>& data)
-{
- SANE_Status status = SANE_STATUS_INVAL;
- unsigned int size, lines, x, y, bpp;
- SANE_Bool empty, split;
- int count;
- uint8_t val;
-
- DBG(DBG_proc, "%s: starting\n", __func__);
- DBG(DBG_io, "%s: move=%d, forward=%d, shading=%d\n", __func__, move, forward, shading);
-
- /* round up to multiple of 3 in case of CIS scanner */
- if (dev->model->is_cis == SANE_TRUE)
- {
- settings.lines = ((settings.lines + 2) / 3) * 3;
- }
-
- /* setup for move then scan */
- if (move == SANE_TRUE && settings.tl_y > 0)
- {
- split = SANE_FALSE;
- }
- else
- {
- split = SANE_TRUE;
- }
- status = setup_for_scan(dev, sensor, &dev->reg, settings, split, SANE_FALSE, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: setup_for_scan failed (%s)\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* allocate memory fo scan : LINCNT may have been adjusted for CCD reordering */
- if (dev->model->is_cis == SANE_TRUE)
- {
- uint32_t value = 0;
- sanei_genesys_get_triple(&dev->reg, REG_LINCNT, &value);
- lines = value / 3;
- }
- else
- {
- uint32_t value = 0;
- sanei_genesys_get_triple(&dev->reg, REG_LINCNT, &value);
- lines = value + 1;
- }
- size = lines * settings.pixels;
- if (settings.depth == 16)
- bpp = 2;
- else
- bpp = 1;
- size *= bpp;
- if (settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- size *= 3;
- data.clear();
- data.resize(size);
-
- DBG(DBG_io, "%s: allocated %d bytes of memory for %d lines\n", __func__, size, lines);
-
- /* put back real line number in settings */
- settings.lines = lines;
-
- /* initialize frontend */
- status = gl646_set_fe(dev, sensor, AFE_SET, settings.xres);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* no shading correction and not watch dog for simple scan */
- dev->reg.find_reg(0x01).value &= ~(REG01_DVDSET | REG01_DOGENB);
- if (shading == SANE_TRUE)
- {
- dev->reg.find_reg(0x01).value |= REG01_DVDSET;
- }
-
- /* enable gamma table for the scan */
- dev->reg.find_reg(0x05).value |= REG05_GMMENB;
-
- /* one table movement for simple scan */
- dev->reg.find_reg(0x02).value &= ~REG02_FASTFED;
-
- if (move == SANE_FALSE)
- {
- sanei_genesys_set_motor_power(dev->reg, false);
-
- /* no automatic go home if no movement */
- dev->reg.find_reg(0x02).value &= ~REG02_AGOHOME;
- }
- if (forward == SANE_FALSE)
- {
- dev->reg.find_reg(0x02).value |= REG02_MTRREV;
- }
- else
- {
- dev->reg.find_reg(0x02).value &= ~REG02_MTRREV;
- }
-
- /* no automatic go home when using XPA */
- if (settings.scan_method == ScanMethod::TRANSPARENCY)
- {
- dev->reg.find_reg(0x02).value &= ~REG02_AGOHOME;
- }
-
- /* write scan registers */
- status = sanei_genesys_bulk_write_register(dev, dev->reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* starts scan */
- status = gl646_begin_scan(dev, sensor, &dev->reg, move);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: \n", __func__);
- return status;
- }
-
- /* wait for buffers to be filled */
- count = 0;
- do
- {
- sanei_genesys_sleep_ms(10);
- RIE (sanei_genesys_get_status (dev, &val));
- if (DBG_LEVEL > DBG_info)
- {
- print_status (val);
- }
- RIE (sanei_genesys_test_buffer_empty (dev, &empty));
- count++;
- }
- while (empty && count < 1000);
- if (count == 1000)
- {
- DBG(DBG_error, "%s: failed toread data\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner (dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* in case of CIS scanner, we must reorder data */
- if (dev->model->is_cis == SANE_TRUE
- && settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- /* alloc one line sized working buffer */
- std::vector<uint8_t> buffer(settings.pixels * 3 * bpp);
-
- /* reorder one line of data and put it back to buffer */
- if (bpp == 1)
- {
- for (y = 0; y < lines; y++)
- {
- /* reorder line */
- for (x = 0; x < settings.pixels; x++)
- {
- buffer[x * 3] = data[y * settings.pixels * 3 + x];
- buffer[x * 3 + 1] = data[y * settings.pixels * 3 + settings.pixels + x];
- buffer[x * 3 + 2] = data[y * settings.pixels * 3 + 2 * settings.pixels + x];
- }
- /* copy line back */
- memcpy (data.data() + settings.pixels * 3 * y, buffer.data(),
- settings.pixels * 3);
- }
- }
- else
- {
- for (y = 0; y < lines; y++)
- {
- /* reorder line */
- for (x = 0; x < settings.pixels; x++)
- {
- buffer[x * 6] = data[y * settings.pixels * 6 + x * 2];
- buffer[x * 6 + 1] = data[y * settings.pixels * 6 + x * 2 + 1];
- buffer[x * 6 + 2] = data[y * settings.pixels * 6 + 2 * settings.pixels + x * 2];
- buffer[x * 6 + 3] = data[y * settings.pixels * 6 + 2 * settings.pixels + x * 2 + 1];
- buffer[x * 6 + 4] = data[y * settings.pixels * 6 + 4 * settings.pixels + x * 2];
- buffer[x * 6 + 5] = data[y * settings.pixels * 6 + 4 * settings.pixels + x * 2 + 1];
- }
- /* copy line back */
- memcpy (data.data() + settings.pixels * 6 * y, buffer.data(),
- settings.pixels * 6);
- }
- }
- }
-
- /* end scan , waiting the motor to stop if needed (if moving), but without ejecting doc */
- status = end_scan(dev, &dev->reg, SANE_TRUE, SANE_FALSE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: end\n", __func__);
- return status;
-}
-
-/**
- * Does a simple move of the given distance by doing a scan at lowest resolution
- * shading correction. Memory for data is allocated in this function
- * and must be freed by caller.
- * @param dev device of the scanner
- * @param distance distance to move in MM
- */
-static SANE_Status
-simple_move (Genesys_Device * dev, SANE_Int distance)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Settings settings;
-
- DBG(DBG_proc, "%s: %d mm\n", __func__, distance);
-
- int resolution = get_lowest_resolution(dev->model->ccd_type, 3);
-
- const auto& sensor = sanei_genesys_find_sensor(dev, resolution);
-
- /* TODO give a no AGOHOME flag */
- settings.scan_method = ScanMethod::TRANSPARENCY;
- settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- settings.xres = resolution;
- settings.yres = resolution;
- settings.tl_y = 0;
- settings.tl_x = 0;
- settings.pixels =
- (sensor.sensor_pixels * settings.xres) / sensor.optical_res;
- settings.lines = (distance * settings.xres) / MM_PER_INCH;
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- std::vector<uint8_t> data;
- status = simple_scan(dev, sensor, settings, SANE_TRUE, SANE_TRUE, SANE_FALSE, data);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: simple_scan failed\n", __func__);
- }
-
- DBGCOMPLETED
- return status;
-}
-
-/**
- * update the status of the required sensor in the scanner session
- * the button fileds are used to make events 'sticky'
- */
-static SANE_Status
-gl646_update_hardware_sensors (Genesys_Scanner * session)
-{
- Genesys_Device *dev = session->dev;
- uint8_t value;
-
- // do what is needed to get a new set of events, but try to not loose any of them.
- gl646_gpio_read(dev->usb_dev, &value);
- DBG(DBG_io, "%s: GPIO=0x%02x\n", __func__, value);
-
- // scan button
- if (dev->model->buttons & GENESYS_HAS_SCAN_SW) {
- switch (dev->model->gpo_type) {
- case GPO_XP200:
- session->buttons[BUTTON_SCAN_SW].write((value & 0x02) != 0);
- break;
- case GPO_5345:
- session->buttons[BUTTON_SCAN_SW].write(value == 0x16);
- break;
- case GPO_HP2300:
- session->buttons[BUTTON_SCAN_SW].write(value == 0x6c);
- break;
- case GPO_HP3670:
- case GPO_HP2400:
- session->buttons[BUTTON_SCAN_SW].write((value & 0x20) == 0);
- break;
- default:
- return SANE_STATUS_UNSUPPORTED;
- }
- }
-
- // email button
- if (dev->model->buttons & GENESYS_HAS_EMAIL_SW) {
- switch (dev->model->gpo_type) {
- case GPO_5345:
- session->buttons[BUTTON_EMAIL_SW].write(value == 0x12);
- break;
- case GPO_HP3670:
- case GPO_HP2400:
- session->buttons[BUTTON_EMAIL_SW].write((value & 0x08) == 0);
- break;
- default:
- return SANE_STATUS_UNSUPPORTED;
- }
- }
-
- // copy button
- if (dev->model->buttons & GENESYS_HAS_COPY_SW) {
- switch (dev->model->gpo_type) {
- case GPO_5345:
- session->buttons[BUTTON_COPY_SW].write(value == 0x11);
- break;
- case GPO_HP2300:
- session->buttons[BUTTON_COPY_SW].write(value == 0x5c);
- break;
- case GPO_HP3670:
- case GPO_HP2400:
- session->buttons[BUTTON_COPY_SW].write((value & 0x10) == 0);
- break;
- default:
- return SANE_STATUS_UNSUPPORTED;
- }
- }
-
- // power button
- if (dev->model->buttons & GENESYS_HAS_POWER_SW) {
- switch (dev->model->gpo_type) {
- case GPO_5345:
- session->buttons[BUTTON_POWER_SW].write(value == 0x14);
- break;
- default:
- return SANE_STATUS_UNSUPPORTED;
- }
- }
-
- // ocr button
- if (dev->model->buttons & GENESYS_HAS_OCR_SW) {
- switch (dev->model->gpo_type) {
- case GPO_5345:
- session->buttons[BUTTON_OCR_SW].write(value == 0x13);
- break;
- default:
- return SANE_STATUS_UNSUPPORTED;
- }
- }
-
- // document detection
- if (dev->model->buttons & GENESYS_HAS_PAGE_LOADED_SW) {
- switch (dev->model->gpo_type) {
- case GPO_XP200:
- session->buttons[BUTTON_PAGE_LOADED_SW].write((value & 0x04) != 0);
- break;
- default:
- return SANE_STATUS_UNSUPPORTED;
- }
- }
-
- /* XPA detection */
- if (dev->model->flags & GENESYS_FLAG_XPA)
- {
- switch (dev->model->gpo_type)
- {
- case GPO_HP3670:
- case GPO_HP2400:
- /* test if XPA is plugged-in */
- if ((value & 0x40) == 0)
- {
- DBG(DBG_io, "%s: enabling XPA\n", __func__);
- session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
- }
- else
- {
- DBG(DBG_io, "%s: disabling XPA\n", __func__);
- session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
- }
- break;
- default:
- return SANE_STATUS_UNSUPPORTED;
- }
- }
-
- return SANE_STATUS_GOOD;
-}
-
-
-static SANE_Status
-write_control (Genesys_Device * dev, const Genesys_Sensor& sensor, int resolution)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t control[4];
- uint32_t addr = 0xdead;
-
- /* 2300 does not write to 'control' */
- if (dev->model->motor_type == MOTOR_HP2300)
- return SANE_STATUS_GOOD;
-
- /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which
- * is after the second slope table */
- switch (sensor.optical_res)
- {
- case 600:
- addr = 0x08200;
- break;
- case 1200:
- addr = 0x10200;
- break;
- case 2400:
- addr = 0x1fa00;
- break;
- default:
- DBG(DBG_error, "%s: failed to compute control address\n", __func__);
- return SANE_STATUS_INVAL;
- }
-
- /* XP200 sets dpi, what other scanner put is unknown yet */
- switch (dev->model->motor_type)
- {
- case MOTOR_XP200:
- /* we put scan's dpi, not motor one */
- control[0] = LOBYTE (resolution);
- control[1] = HIBYTE (resolution);
- control[2] = dev->control[4];
- control[3] = dev->control[5];
- break;
- case MOTOR_HP3670:
- case MOTOR_HP2400:
- case MOTOR_5345:
- default:
- control[0] = dev->control[2];
- control[1] = dev->control[3];
- control[2] = dev->control[4];
- control[3] = dev->control[5];
- break;
- }
-
- DBG(DBG_info, "%s: control write=0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, control[0], control[1],
- control[2], control[3]);
- status = sanei_genesys_set_buffer_address (dev, addr);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up control address\n", __func__);
- return SANE_STATUS_INVAL;
- }
- status = sanei_genesys_bulk_write_data(dev, 0x3c, control, 4);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up control\n", __func__);
- return SANE_STATUS_INVAL;
- }
- return status;
-}
-
-/**
- * check if a stored calibration is compatible with requested scan.
- * @return true if compatible, false if not.
- * Whenever an error is met, it is returned.
- * @param dev scanner device
- * @param cache cache entry to test
- * @param for_overwrite reserved for future use ...
- */
-static bool
-gl646_is_compatible_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Calibration_Cache * cache,
- int for_overwrite)
-{
- (void) sensor;
-#ifdef HAVE_SYS_TIME_H
- struct timeval time;
-#endif
- int compatible = 1;
-
- DBG(DBG_proc, "%s: start (for_overwrite=%d)\n", __func__, for_overwrite);
-
- if (cache == NULL)
- return false;
-
- /* build minimal current_setup for calibration cache use only, it will be better
- * computed when during setup for scan
- */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- dev->current_setup.channels = 3;
- }
- else
- {
- dev->current_setup.channels = 1;
- }
- dev->current_setup.xres = dev->settings.xres;
-
- DBG(DBG_io, "%s: requested=(%d,%f), tested=(%d,%f)\n", __func__, dev->current_setup.channels,
- dev->current_setup.xres, cache->used_setup.channels, cache->used_setup.xres);
-
- /* a calibration cache is compatible if color mode and x dpi match the user
- * requested scan. In the case of CIS scanners, dpi isn't a criteria */
- if (dev->model->is_cis == SANE_FALSE)
- {
- compatible =
- ((dev->current_setup.channels == cache->used_setup.channels)
- && (((int) dev->current_setup.xres) ==
- ((int) cache->used_setup.xres)));
- }
- else
- {
- compatible =
- (dev->current_setup.channels == cache->used_setup.channels);
- }
- if (dev->current_setup.params.scan_method != cache->used_setup.params.scan_method)
- {
- DBG(DBG_io, "%s: current method=%d, used=%d\n", __func__,
- static_cast<unsigned>(dev->current_setup.params.scan_method),
- static_cast<unsigned>(cache->used_setup.params.scan_method));
- compatible = 0;
- }
- if (!compatible)
- {
- DBG(DBG_proc, "%s: completed, non compatible cache\n", __func__);
- return false;
- }
-
- /* a cache entry expires after 30 minutes for non sheetfed scanners */
- /* this is not taken into account when overwriting cache entries */
-#ifdef HAVE_SYS_TIME_H
- if(for_overwrite == SANE_FALSE)
- {
- gettimeofday (&time, NULL);
- if ((time.tv_sec - cache->last_calibration > 30 * 60)
- && (dev->model->is_sheetfed == SANE_FALSE))
- {
- DBG(DBG_proc, "%s: expired entry, non compatible cache\n", __func__);
- return false;
- }
- }
-#endif
-
- DBG(DBG_proc, "%s: completed, cache compatible\n", __func__);
- return true;
-}
-
-/**
- * search for a full width black or white strip.
- * @param dev scanner device
- * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
- * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
- * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
- */
-static SANE_Status
-gl646_search_strip(Genesys_Device * dev, const Genesys_Sensor& sensor, SANE_Bool forward, SANE_Bool black)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Bool half_ccd = SANE_FALSE;
- Genesys_Settings settings;
- int res = get_closest_resolution(dev->model->ccd_type, 75, 1);
- unsigned int pass, count, found, x, y;
- char title[80];
-
- DBG(DBG_proc, "%s: start\n", __func__);
- /* adapt to half_ccd case */
- if (sensor.ccd_size_divisor > 1)
- {
- /* walk the master mode list to find if half_ccd */
- // FIXME: possibly wrong channel count for is_half_ccd
- if (is_half_ccd (dev->model->ccd_type, res, 3) == SANE_TRUE)
- {
- half_ccd = SANE_TRUE;
- }
- }
-
- /* we set up for a lowest available resolution color grey scan, full width */
- settings.scan_method = ScanMethod::FLATBED;
- settings.scan_mode = ScanColorMode::GRAY;
- settings.xres = res;
- settings.yres = res;
- settings.tl_x = 0;
- settings.tl_y = 0;
- settings.pixels = (SANE_UNFIX (dev->model->x_size) * res) / MM_PER_INCH;
- if (half_ccd == SANE_TRUE)
- {
- settings.pixels /= 2;
- }
-
- /* 15 mm at at time */
- settings.lines = (15 * settings.yres) / MM_PER_INCH; /* may become a parameter from genesys_devices.c */
- settings.depth = 8;
- settings.color_filter = ColorFilter::RED;
-
- settings.disable_interpolation = 0;
- settings.threshold = 0;
- settings.dynamic_lineart = SANE_FALSE;
-
- /* signals if a strip of the given color has been found */
- found = 0;
-
- /* detection pass done */
- pass = 0;
-
- std::vector<uint8_t> data;
-
- /* loop until strip is found or maximum pass number done */
- while (pass < 20 && !found)
- {
- /* scan a full width strip */
- status =
- simple_scan(dev, sensor, settings, SANE_TRUE, forward, SANE_FALSE, data);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: simple_scan failed\n", __func__);
- return status;
- }
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf (title, "gl646_search_strip_%s%02d.pnm", forward ? "fwd" : "bwd",
- (int)pass);
- sanei_genesys_write_pnm_file (title, data.data(), settings.depth, 1,
- settings.pixels, settings.lines);
- }
-
- /* search data to find black strip */
- /* when searching forward, we only need one line of the searched color since we
- * will scan forward. But when doing backward search, we need all the area of the
- * same color */
- if (forward)
- {
- for (y = 0; y < settings.lines && !found; y++)
- {
- count = 0;
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < settings.pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * settings.pixels + x] > 90)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * settings.pixels + x] < 60)
- {
- count++;
- }
- }
-
- /* at end of line, if count >= 3%, line is not fully of the desired color
- * so we must go to next line of the buffer */
- /* count*100/pixels < 3 */
- if ((count * 100) / settings.pixels < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
- pass, y);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d\n", __func__, settings.pixels, count);
- }
- }
- }
- else /* since calibration scans are done forward, we need the whole area
- to be of the required color when searching backward */
- {
- count = 0;
- for (y = 0; y < settings.lines; y++)
- {
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < settings.pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * settings.pixels + x] > 60)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * settings.pixels + x] < 60)
- {
- count++;
- }
- }
- }
-
- /* at end of area, if count >= 3%, area is not fully of the desired color
- * so we must go to next buffer */
- if ((count * 100) / (settings.pixels * settings.lines) < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d\n", __func__, settings.pixels, count);
- }
- }
- pass++;
- }
- if (found)
- {
- status = SANE_STATUS_GOOD;
- DBG(DBG_info, "%s: strip found\n", __func__);
- }
- else
- {
- status = SANE_STATUS_UNSUPPORTED;
- DBG(DBG_info, "%s: strip not found\n", __func__);
- }
- return status;
-}
-
-/** the gl646 command set */
-static Genesys_Command_Set gl646_cmd_set = {
- "gl646-generic", /* the name of this set */
-
- gl646_needs_home_before_init_regs_for_scan,
-
- gl646_init,
- gl646_init_regs_for_warmup,
- gl646_init_regs_for_coarse_calibration,
- gl646_init_regs_for_shading,
- gl646_init_regs_for_scan,
-
- gl646_get_filter_bit,
- gl646_get_lineart_bit,
- gl646_get_bitset_bit,
- gl646_get_gain4_bit,
- gl646_get_fast_feed_bit,
- gl646_test_buffer_empty_bit,
- gl646_test_motor_flag_bit,
-
- gl646_public_set_fe,
- gl646_set_powersaving,
- gl646_save_power,
-
- gl646_begin_scan,
- gl646_end_scan,
-
- gl646_send_gamma_table,
-
- gl646_search_start_position,
-
- gl646_offset_calibration,
- gl646_coarse_gain_calibration,
- gl646_led_calibration,
-
- NULL,
- gl646_slow_back_home,
- NULL,
-
- sanei_genesys_bulk_write_register,
- sanei_genesys_bulk_write_data,
- gl646_bulk_read_data,
-
- gl646_update_hardware_sensors,
-
- /* sheetfed related functions */
- gl646_load_document,
- gl646_detect_document_end,
- gl646_eject_document,
- gl646_search_strip,
-
- gl646_is_compatible_calibration,
- gl646_move_to_ta,
- NULL,
- NULL,
- NULL
-};
-
-SANE_Status
-sanei_gl646_init_cmd_set (Genesys_Device * dev)
-{
- dev->model->cmd_set = &gl646_cmd_set;
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_gl646.h b/backend/genesys_gl646.h
deleted file mode 100644
index 766176a..0000000
--- a/backend/genesys_gl646.h
+++ /dev/null
@@ -1,594 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2003-2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
- Copyright (C) 2004-2005 Gerhard Jaeger <gerhard@gjaeger.de>
- Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
- Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#include "genesys.h"
-
-/*
- * Genesys Logic GL646 based scanners
- */
-/* Individual bits */
-#define REG01_CISSET 0x80
-#define REG01_DOGENB 0x40
-#define REG01_DVDSET 0x20
-#define REG01_FASTMOD 0x10
-#define REG01_COMPENB 0x08
-#define REG01_DRAMSEL 0x04
-#define REG01_SHDAREA 0x02
-#define REG01_SCAN 0x01
-
-#define REG02_NOTHOME 0x80
-#define REG02_ACDCDIS 0x40
-#define REG02_AGOHOME 0x20
-#define REG02_MTRPWR 0x10
-#define REG02_FASTFED 0x08
-#define REG02_MTRREV 0x04
-#define REG02_STEPSEL 0x03
-
-#define REG02_FULLSTEP 0x00
-#define REG02_HALFSTEP 0x01
-#define REG02_QUATERSTEP 0x02
-
-#define REG03_TG3 0x80
-#define REG03_AVEENB 0x40
-#define REG03_XPASEL 0x20
-#define REG03_LAMPPWR 0x10
-#define REG03_LAMPDOG 0x08
-#define REG03_LAMPTIM 0x07
-
-#define REG04_LINEART 0x80
-#define REG04_BITSET 0x40
-#define REG04_ADTYPE 0x30
-#define REG04_FILTER 0x0c
-#define REG04_FESET 0x03
-
-#define REG05_DPIHW 0xc0
-#define REG05_DPIHW_600 0x00
-#define REG05_DPIHW_1200 0x40
-#define REG05_DPIHW_2400 0x80
-#define REG05_DPIHW_4800 0xc0
-#define REG05_GMMTYPE 0x30
-#define REG05_GMM14BIT 0x10
-#define REG05_GMMENB 0x08
-#define REG05_LEDADD 0x04
-#define REG05_BASESEL 0x03
-
-#define REG06_PWRBIT 0x10
-#define REG06_GAIN4 0x08
-#define REG06_OPTEST 0x07
-
-#define REG07_DMASEL 0x02
-#define REG07_DMARDWR 0x01
-
-#define REG16_CTRLHI 0x80
-#define REG16_SELINV 0x40
-#define REG16_TGINV 0x20
-#define REG16_CK1INV 0x10
-#define REG16_CK2INV 0x08
-#define REG16_CTRLINV 0x04
-#define REG16_CKDIS 0x02
-#define REG16_CTRLDIS 0x01
-
-#define REG17_TGMODE 0xc0
-#define REG17_TGMODE_NO_DUMMY 0x00
-#define REG17_TGMODE_REF 0x40
-#define REG17_TGMODE_XPA 0x80
-#define REG17_TGW 0x3f
-
-#define REG18_CNSET 0x80
-#define REG18_DCKSEL 0x60
-#define REG18_CKTOGGLE 0x10
-#define REG18_CKDELAY 0x0c
-#define REG18_CKSEL 0x03
-
-#define REG1D_CKMANUAL 0x80
-
-#define REG1E_WDTIME 0xf0
-#define REG1E_LINESEL 0x0f
-
-#define REG41_PWRBIT 0x80
-#define REG41_BUFEMPTY 0x40
-#define REG41_FEEDFSH 0x20
-#define REG41_SCANFSH 0x10
-#define REG41_HOMESNR 0x08
-#define REG41_LAMPSTS 0x04
-#define REG41_FEBUSY 0x02
-#define REG41_MOTMFLG 0x01
-
-#define REG66_LOW_CURRENT 0x10
-
-#define REG6A_FSTPSEL 0xc0
-#define REG6A_FASTPWM 0x3f
-
-#define REG6C_TGTIME 0xc0
-#define REG6C_Z1MOD 0x38
-#define REG6C_Z2MOD 0x07
-
-#define REG_SCANFED 0x1f
-#define REG_BUFSEL 0x20
-#define REG_LINCNT 0x25
-#define REG_DPISET 0x2c
-#define REG_STRPIXEL 0x30
-#define REG_ENDPIXEL 0x32
-#define REG_DUMMY 0x34
-#define REG_MAXWD 0x35
-#define REG_LPERIOD 0x38
-#define REG_FEEDL 0x3d
-#define REG_VALIDWORD 0x42
-#define REG_FEDCNT 0x48
-#define REG_SCANCNT 0x4b
-#define REG_Z1MOD 0x60
-#define REG_Z2MOD 0x62
-
-
-#include "genesys.h"
-
-static SANE_Status gl646_set_fe(Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t set, int dpi);
-
-static SANE_Status gl646_public_set_fe(Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t set);
-
-static
-SANE_Status
-gl646_save_power (Genesys_Device * dev, SANE_Bool enable);
-
-static
-SANE_Status
-gl646_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
-
-static
-SANE_Status
-gl646_move_to_ta (Genesys_Device * dev);
-
-/**
- * sets up the scanner for a scan, registers, gamma tables, shading tables
- * and slope tables, based on the parameter struct.
- * @param dev device to set up
- * @param regs registers to set up
- * @param settings settings of the scan
- * @param split true if move before scan has to be done
- * @param xcorrection true if scanner's X geometry must be taken into account to
- * compute X, ie add left margins
- * @param ycorrection true if scanner's Y geometry must be taken into account to
- * compute Y, ie add top margins
- */
-static SANE_Status
-setup_for_scan (Genesys_Device *device,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set *regs,
- Genesys_Settings settings,
- SANE_Bool split,
- SANE_Bool xcorrection,
- SANE_Bool ycorrection);
-
-/**
- * sets up the registers for a scan corresponding to the settings.
- * Builds motor slope tables. Computes buffer sizes and data amount to
- * transfer. It also sets up analog frontend.
- * */
-static SANE_Status
-gl646_setup_registers (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs, SetupParams& params,
- uint16_t * slope_table1,
- uint16_t * slope_table2,
- bool xcorrection);
-
-/**
- * Does a simple move of the given distance by doing a scan at lowest resolution
- * shading correction. Memory for data is allocated in this function
- * and must be freed by caller.
- * @param dev device of the scanner
- * @param distance distance to move in MM
- */
-static SANE_Status
-simple_move (Genesys_Device * dev, SANE_Int distance);
-
-/**
- * Does a simple scan of the area given by the settings. Scanned data
- * it put in an allocated area which must be freed by the caller.
- * and slope tables, based on the parameter struct. There is no shading
- * correction while gamma correction is active.
- * @param dev device to set up
- * @param settings settings of the scan
- * @param move flag to enable scanhead to move
- * @param forward flag to tell movement direction
- * @param shading flag to tell if shading correction should be done
- * @param data pointer that will point to the scanned data
- */
-static SANE_Status
-simple_scan(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Settings settings, SANE_Bool move, SANE_Bool forward,
- SANE_Bool shading, std::vector<uint8_t>& data);
-
-/**
- * Send the stop scan command
- * */
-static SANE_Status
-end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop, SANE_Bool eject);
-/**
- * writes control data to an area behind the last motor table.
- */
-static SANE_Status write_control (Genesys_Device * dev, const Genesys_Sensor& sensor,
- int resolution);
-
-
-/**
- * initialize scanner's registers at SANE init time
- */
-static void gl646_init_regs (Genesys_Device * dev);
-
-static SANE_Status gl646_load_document (Genesys_Device * dev);
-
-static SANE_Status
-gl646_detect_document_end (Genesys_Device * dev);
-
-#define FULL_STEP 0
-#define HALF_STEP 1
-#define QUATER_STEP 2
-
-#define CALIBRATION_LINES 10
-
-/**
- * master motor settings table entry
- */
-typedef struct
-{
- /* key */
- SANE_Int motor;
- SANE_Int dpi;
- unsigned channels;
-
- /* settings */
- SANE_Int ydpi; /* real motor dpi, may be different from the resolution */
- SANE_Int steptype; /* 0=full, 1=half, 2=quarter */
- SANE_Bool fastmod; /* fast scanning 0/1 */
- SANE_Bool fastfed; /* fast fed slope tables */
- SANE_Int mtrpwm;
- SANE_Int steps1; /* table 1 informations */
- SANE_Int vstart1;
- SANE_Int vend1;
- SANE_Int steps2; /* table 2 informations */
- SANE_Int vstart2;
- SANE_Int vend2;
- float g1;
- float g2;
- SANE_Int fwdbwd; /* forward/backward steps */
-} Motor_Master;
-
-/**
- * master sensor settings table entry
- */
-typedef struct
-{
- /* key */
- SANE_Int sensor; /**< sensor identifier */
- SANE_Int dpi; /**< required dpi */
- unsigned channels; // 3 channels if color scan, 1 channel for gray scan
-
- /* settings */
- SANE_Int xdpi; /**< real sensor dpi, may be different from the required resolution */
- SANE_Int exposure; /**< exposure time */
- SANE_Int dpiset; /**< set sensor dpi */
- SANE_Int cksel; /**< dpiset 'divisor', part of reg 18h */
- SANE_Int dummy; /**< dummy exposure time */
- /* uint8_t regs_0x10_0x15[6];*/
- uint8_t *regs_0x10_0x15; /**< per color exposure time for CIS scanners */
- SANE_Bool half_ccd; /**> true if manual CCD/2 clock programming or real dpi is half dpiset */
- uint8_t r18; /**> content of register 18h */
- uint8_t r1d; /**> content of register 1dh */
-} Sensor_Master;
-
-/**
- * settings for a given resolution and DPISET
- * TODO clean up this when all scanners will have been added
- */
-typedef struct
-{
- /* key */
- SANE_Int sensor;
- SANE_Int cksel;
-
- /* values */
- uint8_t regs_0x08_0x0b[4]; /**< settings for normal CCD clock */
- uint8_t manual_0x08_0x0b[4]; /**< settings for CCD/2 clock */
- uint8_t regs_0x16_0x1d[8];
- uint8_t regs_0x52_0x5e[13];
- uint8_t manual_0x52_0x58[7];
-} Sensor_Settings;
-
-static uint8_t xp200_color[6]={0x16, 0x44, 0x0c, 0x80, 0x09, 0x2e};
-static uint8_t xp200_gray[6]={0x05, 0x0a, 0x0f, 0xa0, 0x10, 0x10};
-
-/**
- * master sensor settings, for a given sensor and dpi,
- * it gives exposure and CCD time
- */
-static Sensor_Master sensor_master[] = {
- /* HP3670 master settings */
- {CCD_HP3670, 75, 3, 75, 4879, 300, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 100, 3, 100, 4487, 400, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 150, 3, 150, 4879, 600, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 300, 3, 300, 4503, 1200, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 600, 3, 600, 10251, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x43},
- {CCD_HP3670,1200, 3, 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
- {CCD_HP3670,2400, 3, 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
- {CCD_HP3670, 75, 1, 75, 4879, 300, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 100, 1, 100, 4487, 400, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 150, 1, 150, 4879, 600, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 300, 1, 300, 4503, 1200, 4, 42, NULL, SANE_FALSE, 0x33, 0x43},
- {CCD_HP3670, 600, 1, 600, 10251, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x43},
- {CCD_HP3670,1200, 1, 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
- {CCD_HP3670,2400, 1, 1200, 12750, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x43},
-
- /* HP 2400 master settings */
- {CCD_HP2400, 50, 3, 50, 7211, 200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 100, 3, 100, 7211, 400, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 150, 3, 150, 7211, 600, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 300, 3, 300, 8751, 1200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 600, 3, 600, 18760, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x02},
- {CCD_HP2400,1200, 3, 1200, 21749, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x42},
- {CCD_HP2400, 50, 1, 50, 7211, 200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 100, 1, 100, 7211, 400, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 150, 1, 150, 7211, 600, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 300, 1, 300, 8751, 1200, 4, 42, NULL, SANE_FALSE, 0x3f, 0x02},
- {CCD_HP2400, 600, 1, 600, 18760, 1200, 2, 42, NULL, SANE_FALSE, 0x31, 0x02},
- {CCD_HP2400,1200, 1, 1200, 21749, 1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x42},
-
- /* XP 200 master settings */
- {CIS_XP200 , 75, 3, 75, 5700, 75, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 100, 3, 100, 5700, 100, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 200, 3, 200, 5700, 200, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 300, 3, 300, 9000, 300, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 600, 3, 600, 16000, 600, 1, 42, xp200_color, SANE_FALSE, 0x00, 0x11},
-
- {CIS_XP200 , 75, 1, 75, 16000, 75, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 100, 1, 100, 7800, 100, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 200, 1, 200, 11000, 200, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 300, 1, 300, 13000, 300, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
- {CIS_XP200 , 600, 1, 600, 24000, 600, 1, 42, xp200_gray, SANE_FALSE, 0x00, 0x11},
-
- /* HP 2300 master settings */
- {CCD_HP2300, 75, 3, 75, 4480, 150, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
- {CCD_HP2300, 150, 3, 150, 4350, 300, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
- {CCD_HP2300, 300, 3, 300, 4350, 600, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
- {CCD_HP2300, 600, 3, 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
- {CCD_HP2300,1200, 3, 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
- {CCD_HP2300, 75, 1, 75, 4480, 150, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
- {CCD_HP2300, 150, 1, 150, 4350, 300, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
- {CCD_HP2300, 300, 1, 300, 4350, 600, 1, 42, NULL, SANE_TRUE , 0x20, 0x85},
- {CCD_HP2300, 600, 1, 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
- {CCD_HP2300,1200, 1, 600, 8700, 600, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
- /* non half ccd 300 dpi settings
- {CCD_HP2300, 300, 3, 300, 8700, 300, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
- {CCD_HP2300, 300, 1, 300, 8700, 300, 1, 42, NULL, SANE_FALSE, 0x20, 0x05},
- */
-
- /* MD5345/6471 master settings */
- {CCD_5345 , 50, 3, 50, 12000, 100, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 75, 3, 75, 11000, 150, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 100, 3, 100, 11000, 200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 150, 3, 150, 11000, 300, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 200, 3, 200, 11000, 400, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 300, 3, 300, 11000, 600, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 400, 3, 400, 11000, 800, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 600, 3, 600, 11000,1200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 ,1200, 3, 1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
- {CCD_5345 ,2400, 3, 1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
- {CCD_5345 , 50, 1, 50, 12000, 100, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 75, 1, 75, 11000, 150, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 100, 1, 100, 11000, 200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 150, 1, 150, 11000, 300, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 200, 1, 200, 11000, 400, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 300, 1, 300, 11000, 600, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 400, 1, 400, 11000, 800, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 , 600, 1, 600, 11000,1200, 1, 42, NULL, SANE_TRUE , 0x28, 0x03},
- {CCD_5345 ,1200, 1, 1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
- {CCD_5345 ,2400, 1, 1200, 11000,1200, 1, 42, NULL, SANE_FALSE, 0x30, 0x03},
-
-};
-
-/**
- * master motor settings, for a given motor and dpi,
- * it gives steps and speed informations
- */
-static Motor_Master motor_master[] = {
- /* HP3670 motor settings */
- {MOTOR_HP3670, 75, 3, 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 1, 200, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 100, 3, 100, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 143, 2905, 187, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 150, 3, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 73, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 300, 3, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 11, 1055, 563, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 600, 3, 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 10687, 5126, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670,1200, 3,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 6375, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670,2400, 3,2400, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 12750, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 75, 1, 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 1, 200, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 100, 1, 100, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 143, 2905, 187, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 150, 1, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 73, 3429, 305, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 300, 1, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 1, 11, 1055, 563, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670, 600, 1, 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 10687, 5126, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670,1200, 1,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 6375, 192, 3399, 337, 0.3, 0.4, 192},
- {MOTOR_HP3670,2400, 3,2400, HALF_STEP, SANE_FALSE, SANE_TRUE , 0, 3, 15937, 12750, 192, 3399, 337, 0.3, 0.4, 192},
-
- /* HP2400/G2410 motor settings base motor dpi = 600 */
- {MOTOR_HP2400, 50, 3, 50, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 100, 3, 100, HALF_STEP, SANE_FALSE, SANE_TRUE, 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 150, 3, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 15902, 902, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 300, 3, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 32, 16703, 2188, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 600, 3, 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 18761, 18761, 192, 4905, 627, 0.30, 0.4, 192},
- {MOTOR_HP2400,1200, 3,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 43501, 43501, 192, 4905, 627, 0.30, 0.4, 192},
- {MOTOR_HP2400, 50, 1, 50, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 100, 1, 100, HALF_STEP, SANE_FALSE, SANE_TRUE, 63, 120, 8736, 601, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 150, 1, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 15902, 902, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 300, 1, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 32, 16703, 2188, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400, 600, 1, 600, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 18761, 18761, 192, 4905, 337, 0.30, 0.4, 192},
- {MOTOR_HP2400,1200, 1,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 43501, 43501, 192, 4905, 337, 0.30, 0.4, 192},
-
- /* XP 200 motor settings */
- {MOTOR_XP200, 75, 3, 75, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 2136, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 100, 3, 100, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 2850, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 200, 3, 200, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6999, 5700, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 250, 3, 250, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6999, 6999, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 300, 3, 300, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 13500, 13500, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 600, 3, 600, HALF_STEP, SANE_TRUE , SANE_TRUE, 0, 4, 31998, 31998, 2, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 75, 1, 75, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 2000, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 100, 1, 100, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6000, 1300, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 200, 1, 200, HALF_STEP, SANE_TRUE , SANE_TRUE, 0, 4, 6000, 3666, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 300, 1, 300, HALF_STEP, SANE_TRUE , SANE_FALSE, 0, 4, 6500, 6500, 8, 12000, 1200, 0.3, 0.5, 1},
- {MOTOR_XP200, 600, 1, 600, HALF_STEP, SANE_TRUE , SANE_TRUE, 0, 4, 24000, 24000, 2, 12000, 1200, 0.3, 0.5, 1},
-
- /* HP scanjet 2300c */
- {MOTOR_HP2300, 75, 3, 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8139, 560, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 150, 3, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 7903, 543, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 300, 3, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 2175, 1087, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 600, 3, 600, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 8700, 4350, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300,1200, 3,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 17400, 8700, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 75, 1, 75, FULL_STEP, SANE_FALSE, SANE_TRUE , 63, 120, 8139, 560, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 150, 1, 150, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 67, 7903, 543, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 300, 1, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 2175, 1087, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 600, 1, 600, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 8700, 4350, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300,1200, 1,1200, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 3, 17400, 8700, 120, 4905, 337, 0.3, 0.4, 16},
- /* non half ccd settings for 300 dpi
- {MOTOR_HP2300, 300, 3, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 44, 5386, 2175, 120, 4905, 337, 0.3, 0.4, 16},
- {MOTOR_HP2300, 300, 1, 300, HALF_STEP, SANE_FALSE, SANE_TRUE , 63, 44, 5386, 2175, 120, 4905, 337, 0.3, 0.4, 16},
- */
-
- /* MD5345/6471 motor settings */
- /* vfinal=(exposure/(1200/dpi))/step_type */
- {MOTOR_5345, 50, 3, 50, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 250, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 75, 3, 75, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 343, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 100, 3, 100, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 458, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 150, 3, 150, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 687, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 200, 3, 200, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 916, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 300, 3, 300, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 1375, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 400, 3, 400, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2000, 1833, 255, 2000, 300, 0.3, 0.4, 32},
- {MOTOR_5345, 500, 3, 500, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2291, 2291, 255, 2000, 300, 0.3, 0.4, 32},
- {MOTOR_5345, 600, 3, 600, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 32},
- {MOTOR_5345, 1200, 3,1200, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 146},
- {MOTOR_5345, 2400, 3,2400, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 5500, 5500, 255, 2000, 300, 0.3, 0.4, 146},
- {MOTOR_5345, 50, 1, 50, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 250, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 75, 1, 75, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 343, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 100, 1, 100, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 458, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 150, 1, 150, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 687, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 200, 1, 200, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 916, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 300, 1, 300, HALF_STEP , SANE_FALSE, SANE_TRUE , 2, 255, 2500, 1375, 255, 2000, 300, 0.3, 0.4, 64},
- {MOTOR_5345, 400, 1, 400, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2000, 1833, 255, 2000, 300, 0.3, 0.4, 32},
- {MOTOR_5345, 500, 1, 500, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2291, 2291, 255, 2000, 300, 0.3, 0.4, 32},
- {MOTOR_5345, 600, 1, 600, HALF_STEP , SANE_FALSE, SANE_TRUE , 0, 32, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 32},
- {MOTOR_5345, 1200, 1,1200, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 2750, 2750, 255, 2000, 300, 0.3, 0.4, 146},
- {MOTOR_5345, 2400, 1,2400, QUATER_STEP, SANE_FALSE, SANE_TRUE , 0, 16, 5500, 5500, 255, 2000, 300, 0.3, 0.4, 146}, /* 5500 guessed */
-};
-
-/**
- * sensor settings for a given sensor and timing method
- */
-static Sensor_Settings sensor_settings[] = {
- /* HP 3670 */
- {CCD_HP3670, 1,
- {0x0d, 0x0f, 0x11, 0x13},
- {0x00, 0x00, 0x00, 0x00},
- {0x2b, 0x07, 0x30, 0x2a, 0x00, 0x00, 0xc0, 0x43},
- {0x03, 0x07, 0x0b, 0x0f, 0x13, 0x17, 0x23, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, },
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
- },
- {CCD_HP3670, 2,
- {0x00, 0x05, 0x06, 0x08},
- {0x00, 0x00, 0x00, 0x00},
- {0x33, 0x07, 0x31, 0x2a, 0x02, 0x0e, 0xc0, 0x43},
- {0x0b, 0x0f, 0x13, 0x17, 0x03, 0x07, 0x63, 0x00, 0xc1, 0x02, 0x0e, 0x00, 0x00, },
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
- },
- {CCD_HP3670, 4,
- {0x00, 0x0a, 0x0b, 0x0d},
- {0x00, 0x00, 0x00, 0x00},
- {0x33, 0x07, 0x33, 0x2a, 0x02, 0x13, 0xc0, 0x43},
- {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x15, 0xc1, 0x05, 0x0a, 0x0f, 0x00, },
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
- },
- /* HP 2400 */
- {CCD_HP2400, 4,
- {0x14, 0x15, 0x00, 0x00},
- {0x00, 0x00, 0x00, 0x00},
- {0xbf, 0x08, 0x3f, 0x2a, 0x00, 0x00, 0x00, 0x02},
- {11, 15, 19, 23, 3, 7, 0x63, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00},
- {11, 15, 19, 23, 3, 7, 0x63}
- },
- {CCD_HP2400, 2,
- {14, 15, 0, 0},
- {14, 15, 0, 0},
- {0xbf, 0x08, 0x31, 0x2a, 0, 0, 0, 0x02},
- {3, 7, 11, 15, 19, 23, 0x23, 0, 0xc1, 0, 0, 0, 0},
- {3, 7, 11, 15, 19, 23, 0x23}
- },
- {CCD_HP2400, 1,
- {0x02, 0x04, 0x00, 0x00},
- {0x02, 0x04, 0x00, 0x00},
- {0xbf, 0x08, 0x30, 0x2a, 0x00, 0x00, 0xc0, 0x42},
- {0x0b, 0x0f, 0x13, 0x17, 0x03, 0x07, 0x63, 0x00, 0xc1, 0x00, 0x0e, 0x00, 0x00},
- {0x0b, 0x0f, 0x13, 0x17, 0x03, 0x07, 0x63}
- },
- {CIS_XP200, 1,
- {6, 7, 10, 4},
- {6, 7, 10, 4},
- {0x24, 0x04, 0x00, 0x2a, 0x0a, 0x0a, 0, 0x11},
- {8, 2, 0, 0, 0, 0, 0x1a, 0x51, 0, 0, 0, 0, 0},
- {8, 2, 0, 0, 0, 0, 0x1a}
- },
- {CCD_HP2300, 1,
- {0x01, 0x03, 0x04, 0x06},
- {0x16, 0x00, 0x01, 0x03},
- {0xb7, 0x0a, 0x20, 0x2a, 0x6a, 0x8a, 0x00, 0x05},
- {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83, 0x00, 0xc1, 0x06, 0x0b, 0x10, 0x16},
- {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83}
- },
- {CCD_5345, 1,
- {0x0d, 0x0f, 0x11, 0x13},
- {0x00, 0x05, 0x06, 0x08}, /* manual clock 1/2 settings or half ccd */
- {0x0b, 0x0a, 0x30, 0x2a, 0x00, 0x00, 0x00, 0x03, },
- {0x03, 0x07, 0x0b, 0x0f, 0x13, 0x17, 0x23, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00},
- {0x0f, 0x13, 0x17, 0x03, 0x07, 0x0b, 0x83} /* half ccd settings */
- },
-};
diff --git a/backend/genesys_gl841.cc b/backend/genesys_gl841.cc
deleted file mode 100644
index 9e8fc15..0000000
--- a/backend/genesys_gl841.cc
+++ /dev/null
@@ -1,5624 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2003 Oliver Rauch
- Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
- Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
- Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
- Copyright (C) 2005 Philipp Schmid <philipp8288@web.de>
- Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
- Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
- Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
- for Plustek Opticbook 3600 support
-
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#define DEBUG_DECLARE_ONLY
-
-#include "genesys_gl841.h"
-
-#include <vector>
-
-/****************************************************************************
- Low level function
- ****************************************************************************/
-
-/* ------------------------------------------------------------------------ */
-/* Read and write RAM, registers and AFE */
-/* ------------------------------------------------------------------------ */
-
-/* Set address for writing data */
-static SANE_Status
-gl841_set_buffer_address_gamma (Genesys_Device * dev, uint32_t addr)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_io, "%s: setting address to 0x%05x\n", __func__, addr & 0xfffffff0);
-
- addr = addr >> 4;
-
- status = sanei_genesys_write_register (dev, 0x5c, (addr & 0xff));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while writing low byte: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- addr = addr >> 8;
- status = sanei_genesys_write_register (dev, 0x5b, (addr & 0xff));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while writing high byte: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_io, "%s: completed\n", __func__);
-
- return status;
-}
-
-/****************************************************************************
- Mid level functions
- ****************************************************************************/
-
-static SANE_Bool
-gl841_get_fast_feed_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x02);
- if (r && (r->value & REG02_FASTFED))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl841_get_filter_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x04);
- if (r && (r->value & REG04_FILTER))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl841_get_lineart_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x04);
- if (r && (r->value & REG04_LINEART))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl841_get_bitset_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x04);
- if (r && (r->value & REG04_BITSET))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl841_get_gain4_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x06);
- if (r && (r->value & REG06_GAIN4))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl841_test_buffer_empty_bit (SANE_Byte val)
-{
- if (val & REG41_BUFEMPTY)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl841_test_motor_flag_bit (SANE_Byte val)
-{
- if (val & REG41_MOTORENB)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-/** copy sensor specific settings */
-/* *dev : device infos
- *regs : registers to be set
- extended : do extended set up
- half_ccd: set up for half ccd resolution
- all registers 08-0B, 10-1D, 52-59 are set up. They shouldn't
- appear anywhere else but in register_ini
-
-Responsible for signals to CCD/CIS:
- CCD_CK1X (CK1INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1MTGL(0x1C),CK1LOW(0x1D),CK1MAP(0x74,0x75,0x76),CK1NEG(0x7D))
- CCD_CK2X (CK2INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1LOW(0x1D),CK1NEG(0x7D))
- CCD_CK3X (MANUAL3(0x1A),CK3INV(0x1A),CK3MTGL(0x1C),CK3LOW(0x1D),CK3MAP(0x77,0x78,0x79),CK3NEG(0x7D))
- CCD_CK4X (MANUAL3(0x1A),CK4INV(0x1A),CK4MTGL(0x1C),CK4LOW(0x1D),CK4MAP(0x7A,0x7B,0x7C),CK4NEG(0x7D))
- CCD_CPX (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),CPH(0x72),CPL(0x73),CPNEG(0x7D))
- CCD_RSX (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),RSH(0x70),RSL(0x71),RSNEG(0x7D))
- CCD_TGX (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPR(0x10,0x11),TGSHLD(0x1D))
- CCD_TGG (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPG(0x12,0x13),TGSHLD(0x1D))
- CCD_TGB (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPB(0x14,0x15),TGSHLD(0x1D))
- LAMP_SW (EXPR(0x10,0x11),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29))
- XPA_SW (EXPG(0x12,0x13),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29))
- LAMP_B (EXPB(0x14,0x15),LAMP_PWR(0x03))
-
-other registers:
- CISSET(0x01),CNSET(0x18),DCKSEL(0x18),SCANMOD(0x18),EXPDMY(0x19),LINECLP(0x1A),CKAREA(0x1C),TGTIME(0x1C),LINESEL(0x1E),DUMMY(0x34)
-
-Responsible for signals to AFE:
- VSMP (VSMP(0x58),VSMPW(0x58))
- BSMP (BSMP(0x59),BSMPW(0x59))
-
-other register settings depending on this:
- RHI(0x52),RLOW(0x53),GHI(0x54),GLOW(0x55),BHI(0x56),BLOW(0x57),
-
-*/
-static void sanei_gl841_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs,
- SANE_Bool extended, SANE_Bool half_ccd)
-{
- DBG(DBG_proc, "%s\n", __func__);
-
- // that one is tricky at least
- for (uint16_t addr = 0x08; addr <= 0x0b; ++addr) {
- regs->set8(0x70 + addr - 0x08, sensor.custom_regs.get_value(addr));
- }
-
- // ignore registers in range [0x10..0x16)
- for (uint16_t addr = 0x16; addr < 0x1e; ++addr) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- // ignore registers in range [0x5b..0x5e]
- for (uint16_t addr = 0x52; addr < 0x52 + 9; ++addr) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- /* don't go any further if no extended setup */
- if (!extended)
- return;
-
- /* todo : add more CCD types if needed */
- /* we might want to expand the Sensor struct to have these
- 2 kind of settings */
- if (dev->model->ccd_type == CCD_5345)
- {
- if (half_ccd)
- {
- GenesysRegister* r;
- /* settings for CCD used at half is max resolution */
- r = sanei_genesys_get_address (regs, 0x70);
- r->value = 0x00;
- r = sanei_genesys_get_address (regs, 0x71);
- r->value = 0x05;
- r = sanei_genesys_get_address (regs, 0x72);
- r->value = 0x06;
- r = sanei_genesys_get_address (regs, 0x73);
- r->value = 0x08;
- r = sanei_genesys_get_address (regs, 0x18);
- r->value = 0x28;
- r = sanei_genesys_get_address (regs, 0x58);
- r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
- }
- else
- {
- GenesysRegister* r;
- /* swap latch times */
- r = sanei_genesys_get_address (regs, 0x18);
- r->value = 0x30;
- regs->set8(0x52, sensor.custom_regs.get_value(0x55));
- regs->set8(0x53, sensor.custom_regs.get_value(0x56));
- regs->set8(0x54, sensor.custom_regs.get_value(0x57));
- regs->set8(0x55, sensor.custom_regs.get_value(0x52));
- regs->set8(0x56, sensor.custom_regs.get_value(0x53));
- regs->set8(0x57, sensor.custom_regs.get_value(0x54));
- r = sanei_genesys_get_address (regs, 0x58);
- r->value = 0x20 | (r->value & 0x03); /* VSMP=4 */
- }
- return;
- }
-
- if (dev->model->ccd_type == CCD_HP2300)
- {
- /* settings for CCD used at half is max resolution */
- GenesysRegister* r;
- if (half_ccd)
- {
- r = sanei_genesys_get_address (regs, 0x70);
- r->value = 0x16;
- r = sanei_genesys_get_address (regs, 0x71);
- r->value = 0x00;
- r = sanei_genesys_get_address (regs, 0x72);
- r->value = 0x01;
- r = sanei_genesys_get_address (regs, 0x73);
- r->value = 0x03;
- /* manual clock programming */
- r = sanei_genesys_get_address (regs, 0x1d);
- r->value |= 0x80;
- }
- else
- {
- r = sanei_genesys_get_address (regs, 0x70);
- r->value = 1;
- r = sanei_genesys_get_address (regs, 0x71);
- r->value = 3;
- r = sanei_genesys_get_address (regs, 0x72);
- r->value = 4;
- r = sanei_genesys_get_address (regs, 0x73);
- r->value = 6;
- }
- r = sanei_genesys_get_address (regs, 0x58);
- r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
- return;
- }
-}
-
-/** Test if the ASIC works
- */
-/*TODO: make this functional*/
-static SANE_Status
-sanei_gl841_asic_test (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- size_t size, verify_size;
- unsigned int i;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- return SANE_STATUS_INVAL;
-
- /* set and read exposure time, compare if it's the same */
- status = sanei_genesys_write_register (dev, 0x38, 0xde);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_write_register (dev, 0x39, 0xad);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_read_register (dev, 0x38, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (val != 0xde) /* value of register 0x38 */
- {
- DBG(DBG_error, "%s: register contains invalid value\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- status = sanei_genesys_read_register (dev, 0x39, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (val != 0xad) /* value of register 0x39 */
- {
- DBG(DBG_error, "%s: register contains invalid value\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- /* ram test: */
- size = 0x40000;
- verify_size = size + 0x80;
- /* todo: looks like the read size must be a multiple of 128?
- otherwise the read doesn't succeed the second time after the scanner has
- been plugged in. Very strange. */
-
- std::vector<uint8_t> data(size);
- std::vector<uint8_t> verify_data(verify_size);
-
- for (i = 0; i < (size - 1); i += 2)
- {
- data[i] = i / 512;
- data[i + 1] = (i / 2) % 256;
- }
-
- status = sanei_genesys_set_buffer_address (dev, 0x0000);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
-/* status = sanei_genesys_bulk_write_data(dev, 0x3c, data, size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write data: %s\n", __func__, sane_strstatus(status));
- free (data);
- free (verify_data);
- return status;
- }*/
-
- status = sanei_genesys_set_buffer_address (dev, 0x0000);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_bulk_read_data(dev, 0x45, verify_data.data(), verify_size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* todo: why i + 2 ? */
- for (i = 0; i < size; i++)
- {
- if (verify_data[i] != data[i])
- {
- DBG(DBG_error, "%s: data verification error\n", __func__);
- DBG(DBG_info, "0x%.8x: got %.2x %.2x %.2x %.2x, expected %.2x %.2x %.2x %.2x\n",
- i,
- verify_data[i],
- verify_data[i+1],
- verify_data[i+2],
- verify_data[i+3],
- data[i],
- data[i+1],
- data[i+2],
- data[i+3]);
- return SANE_STATUS_IO_ERROR;
- }
- }
-
- DBG(DBG_info, "%s: completed\n", __func__);
-
- return SANE_STATUS_GOOD;
-}
-
-/*
- * Set all registers LiDE 80 to default values
- * (function called only once at the beginning)
- * we are doing a special case to ease development
- */
-static void
-gl841_init_lide80 (Genesys_Device * dev)
-{
- uint8_t val;
-
- INITREG (0x01, 0x82); /* 0x02 = SHDAREA and no CISSET ! */
- INITREG (0x02, 0x10);
- INITREG (0x03, 0x50);
- INITREG (0x04, 0x02);
- INITREG (0x05, 0x4c); /* 1200 DPI */
- INITREG (0x06, 0x38); /* 0x38 scanmod=1, pwrbit, GAIN4 */
- INITREG (0x07, 0x00);
- INITREG (0x08, 0x00);
- INITREG (0x09, 0x11);
- INITREG (0x0a, 0x00);
-
- INITREG (0x10, 0x40);
- INITREG (0x11, 0x00);
- INITREG (0x12, 0x40);
- INITREG (0x13, 0x00);
- INITREG (0x14, 0x40);
- INITREG (0x15, 0x00);
- INITREG (0x16, 0x00);
- INITREG (0x17, 0x01);
- INITREG (0x18, 0x00);
- INITREG (0x19, 0x06);
- INITREG (0x1a, 0x00);
- INITREG (0x1b, 0x00);
- INITREG (0x1c, 0x00);
- INITREG (0x1d, 0x04);
- INITREG (0x1e, 0x10);
- INITREG (0x1f, 0x04);
- INITREG (0x20, 0x02);
- INITREG (0x21, 0x10);
- INITREG (0x22, 0x20);
- INITREG (0x23, 0x20);
- INITREG (0x24, 0x10);
- INITREG (0x25, 0x00);
- INITREG (0x26, 0x00);
- INITREG (0x27, 0x00);
-
- INITREG (0x29, 0xff);
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
- INITREG (0x2c, sensor.optical_res>>8);
- INITREG (0x2d, sensor.optical_res & 0xff);
- INITREG (0x2e, 0x80);
- INITREG (0x2f, 0x80);
- INITREG (0x30, 0x00);
- INITREG (0x31, 0x10);
- INITREG (0x32, 0x15);
- INITREG (0x33, 0x0e);
- INITREG (0x34, 0x40);
- INITREG (0x35, 0x00);
- INITREG (0x36, 0x2a);
- INITREG (0x37, 0x30);
- INITREG (0x38, 0x2a);
- INITREG (0x39, 0xf8);
-
- INITREG (0x3d, 0x00);
- INITREG (0x3e, 0x00);
- INITREG (0x3f, 0x00);
-
- INITREG (0x52, 0x03);
- INITREG (0x53, 0x07);
- INITREG (0x54, 0x00);
- INITREG (0x55, 0x00);
- INITREG (0x56, 0x00);
- INITREG (0x57, 0x00);
- INITREG (0x58, 0x29);
- INITREG (0x59, 0x69);
- INITREG (0x5a, 0x55);
-
- INITREG (0x5d, 0x20);
- INITREG (0x5e, 0x41);
- INITREG (0x5f, 0x40);
- INITREG (0x60, 0x00);
- INITREG (0x61, 0x00);
- INITREG (0x62, 0x00);
- INITREG (0x63, 0x00);
- INITREG (0x64, 0x00);
- INITREG (0x65, 0x00);
- INITREG (0x66, 0x00);
- INITREG (0x67, 0x40);
- INITREG (0x68, 0x40);
- INITREG (0x69, 0x20);
- INITREG (0x6a, 0x20);
- INITREG (0x6c, dev->gpo.value[0]);
- INITREG (0x6d, dev->gpo.value[1]);
- INITREG (0x6e, dev->gpo.enable[0]);
- INITREG (0x6f, dev->gpo.enable[1]);
- INITREG (0x70, 0x00);
- INITREG (0x71, 0x05);
- INITREG (0x72, 0x07);
- INITREG (0x73, 0x09);
- INITREG (0x74, 0x00);
- INITREG (0x75, 0x01);
- INITREG (0x76, 0xff);
- INITREG (0x77, 0x00);
- INITREG (0x78, 0x0f);
- INITREG (0x79, 0xf0);
- INITREG (0x7a, 0xf0);
- INITREG (0x7b, 0x00);
- INITREG (0x7c, 0x1e);
- INITREG (0x7d, 0x11);
- INITREG (0x7e, 0x00);
- INITREG (0x7f, 0x50);
- INITREG (0x80, 0x00);
- INITREG (0x81, 0x00);
- INITREG (0x82, 0x0f);
- INITREG (0x83, 0x00);
- INITREG (0x84, 0x0e);
- INITREG (0x85, 0x00);
- INITREG (0x86, 0x0d);
- INITREG (0x87, 0x02);
- INITREG (0x88, 0x00);
- INITREG (0x89, 0x00);
-
- /* specific scanner settings, clock and gpio first */
- sanei_genesys_read_register (dev, REG6B, &val);
- sanei_genesys_write_register (dev, REG6B, 0x0c);
- sanei_genesys_write_register (dev, 0x06, 0x10);
- sanei_genesys_write_register (dev, REG6E, 0x6d);
- sanei_genesys_write_register (dev, REG6F, 0x80);
- sanei_genesys_write_register (dev, REG6B, 0x0e);
- sanei_genesys_read_register (dev, REG6C, &val);
- sanei_genesys_write_register (dev, REG6C, 0x00);
- sanei_genesys_read_register (dev, REG6D, &val);
- sanei_genesys_write_register (dev, REG6D, 0x8f);
- sanei_genesys_read_register (dev, REG6B, &val);
- sanei_genesys_write_register (dev, REG6B, 0x0e);
- sanei_genesys_read_register (dev, REG6B, &val);
- sanei_genesys_write_register (dev, REG6B, 0x0e);
- sanei_genesys_read_register (dev, REG6B, &val);
- sanei_genesys_write_register (dev, REG6B, 0x0a);
- sanei_genesys_read_register (dev, REG6B, &val);
- sanei_genesys_write_register (dev, REG6B, 0x02);
- sanei_genesys_read_register (dev, REG6B, &val);
- sanei_genesys_write_register (dev, REG6B, 0x06);
-
- sanei_genesys_write_0x8c (dev, 0x10, 0x94);
- sanei_genesys_write_register (dev, 0x09, 0x10);
-
- /* set up GPIO : no address, so no bulk write, doesn't written directly either ? */
- /*
- dev->reg.find_reg(0x6c).value = dev->gpo.value[0];
- dev->reg.find_reg(0x6d).value = dev->gpo.value[1];
- dev->reg.find_reg(0x6e).value = dev->gpo.enable[0];
- dev->reg.find_reg(0x6f).value = dev->gpo.enable[1]; */
-
- // FIXME: the following code originally changed 0x6b, but due to bug the 0x6c register was
- // effectively changed. The current behavior matches the old code, but should probably be fixed.
- dev->reg.find_reg(0x6c).value |= REG6B_GPO18;
- dev->reg.find_reg(0x6c).value &= ~REG6B_GPO17;
-
- sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 0);
-}
-
-/*
- * Set all registers to default values
- * (function called only once at the beginning)
- */
-static void
-gl841_init_registers (Genesys_Device * dev)
-{
- int addr;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- dev->reg.clear();
- if (dev->model->model_id == MODEL_CANON_LIDE_80)
- {
- gl841_init_lide80(dev);
- return ;
- }
-
- for (addr = 1; addr <= 0x0a; addr++) {
- dev->reg.init_reg(addr, 0);
- }
- for (addr = 0x10; addr <= 0x27; addr++) {
- dev->reg.init_reg(addr, 0);
- }
- dev->reg.init_reg(0x29, 0);
- for (addr = 0x2c; addr <= 0x39; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x3d; addr <= 0x3f; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x52; addr <= 0x5a; addr++)
- dev->reg.init_reg(addr, 0);
- for (addr = 0x5d; addr <= 0x87; addr++)
- dev->reg.init_reg(addr, 0);
-
-
- dev->reg.find_reg(0x01).value = 0x20; /* (enable shading), CCD, color, 1M */
- if (dev->model->is_cis == SANE_TRUE) {
- dev->reg.find_reg(0x01).value |= REG01_CISSET;
- } else {
- dev->reg.find_reg(0x01).value &= ~REG01_CISSET;
- }
-
- dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */
- dev->reg.find_reg(0x02).value |= REG02_AGOHOME;
- sanei_genesys_set_motor_power(dev->reg, true);
- dev->reg.find_reg(0x02).value |= REG02_FASTFED;
-
- dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ; /* lamp on */
- dev->reg.find_reg(0x03).value |= REG03_AVEENB;
-
- if (dev->model->ccd_type == CCD_PLUSTEK_3600) /* AD front end */
- {
- dev->reg.find_reg(0x04).value = (2 << REG04S_AFEMOD) | 0x02;
- }
- else /* Wolfson front end */
- {
- dev->reg.find_reg(0x04).value |= 1 << REG04S_AFEMOD;
- }
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- dev->reg.find_reg(0x05).value = 0x00; /* disable gamma, 24 clocks/pixel */
- if (sensor.sensor_pixels < 0x1500)
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_600;
- else if (sensor.sensor_pixels < 0x2a80)
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_1200;
- else if (sensor.sensor_pixels < 0x5400)
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_2400;
- else
- {
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_2400;
- DBG(DBG_warn, "%s: Cannot handle sensor pixel count %d\n", __func__,
- sensor.sensor_pixels);
- }
-
-
- dev->reg.find_reg(0x06).value |= REG06_PWRBIT;
- dev->reg.find_reg(0x06).value |= REG06_GAIN4;
-
- /* XP300 CCD needs different clock and clock/pixels values */
- if (dev->model->ccd_type != CCD_XP300 && dev->model->ccd_type != CCD_DP685
- && dev->model->ccd_type != CCD_PLUSTEK_3600)
- {
- dev->reg.find_reg(0x06).value |= 0 << REG06S_SCANMOD;
- dev->reg.find_reg(0x09).value |= 1 << REG09S_CLKSET;
- }
- else
- {
- dev->reg.find_reg(0x06).value |= 0x05 << REG06S_SCANMOD; /* 15 clocks/pixel */
- dev->reg.find_reg(0x09).value = 0; /* 24 MHz CLKSET */
- }
-
- dev->reg.find_reg(0x1e).value = 0xf0; /* watch-dog time */
-
- dev->reg.find_reg(0x17).value |= 1 << REG17S_TGW;
-
- dev->reg.find_reg(0x19).value = 0x50;
-
- dev->reg.find_reg(0x1d).value |= 1 << REG1DS_TGSHLD;
-
- dev->reg.find_reg(0x1e).value |= 1 << REG1ES_WDTIME;
-
-/*SCANFED*/
- dev->reg.find_reg(0x1f).value = 0x01;
-
-/*BUFSEL*/
- dev->reg.find_reg(0x20).value = 0x20;
-
-/*LAMPPWM*/
- dev->reg.find_reg(0x29).value = 0xff;
-
-/*BWHI*/
- dev->reg.find_reg(0x2e).value = 0x80;
-
-/*BWLOW*/
- dev->reg.find_reg(0x2f).value = 0x80;
-
-/*LPERIOD*/
- dev->reg.find_reg(0x38).value = 0x4f;
- dev->reg.find_reg(0x39).value = 0xc1;
-
-/*VSMPW*/
- dev->reg.find_reg(0x58).value |= 3 << REG58S_VSMPW;
-
-/*BSMPW*/
- dev->reg.find_reg(0x59).value |= 3 << REG59S_BSMPW;
-
-/*RLCSEL*/
- dev->reg.find_reg(0x5a).value |= REG5A_RLCSEL;
-
-/*STOPTIM*/
- dev->reg.find_reg(0x5e).value |= 0x2 << REG5ES_STOPTIM;
-
- sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 0);
-
- /* set up GPIO */
- dev->reg.find_reg(0x6c).value = dev->gpo.value[0];
- dev->reg.find_reg(0x6d).value = dev->gpo.value[1];
- dev->reg.find_reg(0x6e).value = dev->gpo.enable[0];
- dev->reg.find_reg(0x6f).value = dev->gpo.enable[1];
-
- /* TODO there is a switch calling to be written here */
- if (dev->model->gpo_type == GPO_CANONLIDE35)
- {
- dev->reg.find_reg(0x6b).value |= REG6B_GPO18;
- dev->reg.find_reg(0x6b).value &= ~REG6B_GPO17;
- }
-
- if (dev->model->gpo_type == GPO_XP300)
- {
- dev->reg.find_reg(0x6b).value |= REG6B_GPO17;
- }
-
- if (dev->model->gpo_type == GPO_DP685)
- {
- /* REG6B_GPO18 lights on green led */
- dev->reg.find_reg(0x6b).value |= REG6B_GPO17|REG6B_GPO18;
- }
-
- DBG(DBG_proc, "%s complete\n", __func__);
-}
-
-/* Send slope table for motor movement
- slope_table in machine byte order
- */
-static SANE_Status
-gl841_send_slope_table (Genesys_Device * dev, int table_nr,
- uint16_t * slope_table, int steps)
-{
- int dpihw;
- int start_address;
- SANE_Status status = SANE_STATUS_GOOD;
- char msg[4000];
-/*#ifdef WORDS_BIGENDIAN*/
- int i;
-/*#endif*/
-
- DBG(DBG_proc, "%s (table_nr = %d, steps = %d)\n", __func__, table_nr, steps);
-
- dpihw = dev->reg.find_reg(0x05).value >> 6;
-
- if (dpihw == 0) /* 600 dpi */
- start_address = 0x08000;
- else if (dpihw == 1) /* 1200 dpi */
- start_address = 0x10000;
- else if (dpihw == 2) /* 2400 dpi */
- start_address = 0x20000;
- else /* reserved */
- return SANE_STATUS_INVAL;
-
- std::vector<uint8_t> table(steps * 2);
- for(i = 0; i < steps; i++) {
- table[i * 2] = slope_table[i] & 0xff;
- table[i * 2 + 1] = slope_table[i] >> 8;
- }
-
- if (DBG_LEVEL >= DBG_io)
- {
- sprintf (msg, "write slope %d (%d)=", table_nr, steps);
- for (i = 0; i < steps; i++)
- {
- sprintf (msg+strlen(msg), ",%d", slope_table[i]);
- }
- DBG(DBG_io, "%s: %s\n", __func__, msg);
- }
-
- status =
- sanei_genesys_set_buffer_address (dev, start_address + table_nr * 0x200);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_bulk_write_data(dev, 0x3c, table.data(), steps * 2);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send slope table: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
-}
-
-static SANE_Status
-gl841_set_lide80_fe (Genesys_Device * dev, uint8_t set)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
-
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
-
- dev->frontend = dev->frontend_initial;
-
- /* write them to analog frontend */
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x00 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x03, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x03 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x06, dev->frontend.regs.get_value(0x02));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x06 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- if (set == AFE_SET)
- {
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x00 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x06, dev->frontend.regs.get_value(0x20));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing offset failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x03, dev->frontend.regs.get_value(0x28));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing gain failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- return status;
- DBGCOMPLETED;
-}
-
-/* Set values of Analog Device type frontend */
-static SANE_Status
-gl841_set_ad_fe (Genesys_Device * dev, uint8_t set)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
-
- /* special case for LiDE 80 analog frontend */
- if(dev->model->dac_type==DAC_CANONLIDE80)
- {
- return gl841_set_lide80_fe(dev, set);
- }
-
- DBG(DBG_proc, "%s(): start\n", __func__);
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
-
- dev->frontend = dev->frontend_initial;
-
- /* write them to analog frontend */
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x00 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x01 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- for (i = 0; i < 6; i++)
- {
- status =
- sanei_genesys_fe_write_data (dev, 0x02 + i, 0x00);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing sign[%d] failed: %s\n", __func__, 0x02 + i,
- sane_strstatus(status));
- return status;
- }
- }
- }
- if (set == AFE_SET)
- {
- /* write them to analog frontend */
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x00 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg 0x01 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* Write fe 0x02 (red gain)*/
- status = sanei_genesys_fe_write_data(dev, 0x02, dev->frontend.get_gain(0));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing fe 0x02 (gain r) fail: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* Write fe 0x03 (green gain)*/
- status = sanei_genesys_fe_write_data(dev, 0x03, dev->frontend.get_gain(1));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing fe 0x03 (gain g) fail: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* Write fe 0x04 (blue gain)*/
- status = sanei_genesys_fe_write_data(dev, 0x04, dev->frontend.get_gain(2));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing fe 0x04 (gain b) fail: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* Write fe 0x05 (red offset)*/
- status =
- sanei_genesys_fe_write_data(dev, 0x05, dev->frontend.get_offset(0));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: write fe 0x05 (offset r) fail: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* Write fe 0x06 (green offset)*/
- status =
- sanei_genesys_fe_write_data(dev, 0x06, dev->frontend.get_offset(1));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: write fe 0x06 (offset g) fail: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* Write fe 0x07 (blue offset)*/
- status =
- sanei_genesys_fe_write_data(dev, 0x07, dev->frontend.get_offset(2));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: write fe 0x07 (offset b) fail: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
- DBG(DBG_proc, "%s(): end\n", __func__);
-
- return status;
-}
-
-/* Set values of analog frontend */
-static SANE_Status
-gl841_set_fe(Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
-
- DBG(DBG_proc, "%s (%s)\n", __func__,
- set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
- AFE_POWER_SAVE ? "powersave" : "huh?");
-
- /* Analog Device type frontend */
- if ((dev->reg.find_reg(0x04).value & REG04_FESET) == 0x02)
- {
- return gl841_set_ad_fe (dev, set);
- }
-
- if ((dev->reg.find_reg(0x04).value & REG04_FESET) != 0x00)
- {
- DBG(DBG_proc, "%s(): unsupported frontend type %d\n", __func__,
- dev->reg.find_reg(0x04).value & REG04_FESET);
- return SANE_STATUS_UNSUPPORTED;
- }
-
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
- dev->frontend = dev->frontend_initial;
-
- /* reset only done on init */
- status = sanei_genesys_fe_write_data (dev, 0x04, 0x80);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: reset fe failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- DBG(DBG_proc, "%s(): frontend reset complete\n", __func__);
- }
-
-
- if (set == AFE_POWER_SAVE)
- {
- status = sanei_genesys_fe_write_data (dev, 0x01, 0x02);
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: writing data failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- return status;
- }
-
- /* todo : base this test on cfg reg3 or a CCD family flag to be created */
- /*if (dev->model->ccd_type!=CCD_HP2300 && dev->model->ccd_type!=CCD_HP2400) */
- {
-
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg0 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x02, dev->frontend.regs.get_value(0x02));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg2 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg1 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_fe_write_data(dev, 0x03, dev->frontend.regs.get_value(0x03));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg3 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_fe_write_data (dev, 0x06, dev->frontend.reg2[0]);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg6 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_fe_write_data (dev, 0x08, dev->frontend.reg2[1]);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg8 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_fe_write_data (dev, 0x09, dev->frontend.reg2[2]);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg9 failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- for (i = 0; i < 3; i++)
- {
- status =
- sanei_genesys_fe_write_data(dev, 0x24 + i, dev->frontend.regs.get_value(0x24 + i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing sign[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x28 + i, dev->frontend.get_gain(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing gain[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
-
- status =
- sanei_genesys_fe_write_data(dev, 0x20 + i,
- dev->frontend.get_offset(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing offset[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
-
-
- DBG(DBG_proc, "%s: completed\n", __func__);
-
- return SANE_STATUS_GOOD;
-}
-
-#define MOTOR_ACTION_FEED 1
-#define MOTOR_ACTION_GO_HOME 2
-#define MOTOR_ACTION_HOME_FREE 3
-
-/** @brief turn off motor
- *
- */
-static SANE_Status
-gl841_init_motor_regs_off(Genesys_Register_Set * reg,
- unsigned int scan_lines)
-{
- unsigned int feedl;
- GenesysRegister* r;
-
- DBG(DBG_proc, "%s : scan_lines=%d\n", __func__, scan_lines);
-
- feedl = 2;
-
- r = sanei_genesys_get_address (reg, 0x3d);
- r->value = (feedl >> 16) & 0xf;
- r = sanei_genesys_get_address (reg, 0x3e);
- r->value = (feedl >> 8) & 0xff;
- r = sanei_genesys_get_address (reg, 0x3f);
- r->value = feedl & 0xff;
- r = sanei_genesys_get_address (reg, 0x5e);
- r->value &= ~0xe0;
-
- r = sanei_genesys_get_address (reg, 0x25);
- r->value = (scan_lines >> 16) & 0xf;
- r = sanei_genesys_get_address (reg, 0x26);
- r->value = (scan_lines >> 8) & 0xff;
- r = sanei_genesys_get_address (reg, 0x27);
- r->value = scan_lines & 0xff;
-
- r = sanei_genesys_get_address (reg, 0x02);
- r->value &= ~0x01; /*LONGCURV OFF*/
- r->value &= ~0x80; /*NOT_HOME OFF*/
-
- r->value &= ~0x10;
-
- r->value &= ~0x06;
-
- r->value &= ~0x08;
-
- r->value &= ~0x20;
-
- r->value &= ~0x40;
-
- r = sanei_genesys_get_address (reg, 0x67);
- r->value = 0x3f;
-
- r = sanei_genesys_get_address (reg, 0x68);
- r->value = 0x3f;
-
- r = sanei_genesys_get_address (reg, REG_STEPNO);
- r->value = 0;
-
- r = sanei_genesys_get_address (reg, REG_FASTNO);
- r->value = 0;
-
- r = sanei_genesys_get_address (reg, 0x69);
- r->value = 0;
-
- r = sanei_genesys_get_address (reg, 0x6a);
- r->value = 0;
-
- r = sanei_genesys_get_address (reg, 0x5f);
- r->value = 0;
-
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief write motor table frequency
- * Write motor frequency data table.
- * @param dev device to set up motor
- * @param ydpi motor target resolution
- * @return SANE_STATUS_GOOD on success
- */
-static SANE_Status gl841_write_freq(Genesys_Device *dev, unsigned int ydpi)
-{
-SANE_Status status = SANE_STATUS_GOOD;
-/**< fast table */
-uint8_t tdefault[] = {0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0x36,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xb6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0xf6,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76,0x18,0x76};
-uint8_t t1200[] = {0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc7,0x31,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc0,0x11,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0xc7,0xb1,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0x07,0xe0,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc7,0xf1,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc0,0x51,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0xc7,0x71,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20,0x07,0x20};
-uint8_t t300[] = {0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x08,0x32,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x08,0xb2,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x0c,0xa0,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x08,0xf2,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x00,0xd3,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x08,0x72,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60,0x0c,0x60};
-uint8_t t150[] = {0x0c,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0xcf,0x33,0x40,0x14,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x80,0x15,0x0c,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0xcf,0xb3,0x11,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x16,0xa0,0x0c,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0xcf,0xf3,0x40,0xd4,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x80,0xd5,0x0c,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0xcf,0x73,0x11,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60,0x16,0x60};
-
-uint8_t *table;
-
- DBGSTART;
- if(dev->model->motor_type == MOTOR_CANONLIDE80)
- {
- switch(ydpi)
- {
- case 3600:
- case 1200:
- table=t1200;
- break;
- case 900:
- case 300:
- table=t300;
- break;
- case 450:
- case 150:
- table=t150;
- break;
- default:
- table=tdefault;
- }
- RIE(sanei_genesys_write_register(dev, 0x66, 0x00));
- RIE(sanei_genesys_write_register(dev, 0x5b, 0x0c));
- RIE(sanei_genesys_write_register(dev, 0x5c, 0x00));
- RIE(sanei_genesys_bulk_write_data(dev, 0x28, table, 128));
- RIE(sanei_genesys_write_register(dev, 0x5b, 0x00));
- RIE(sanei_genesys_write_register(dev, 0x5c, 0x00));
- }
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-static SANE_Status
-gl841_init_motor_regs(Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int feed_steps,/*1/base_ydpi*/
-/*maybe float for half/quarter step resolution?*/
- unsigned int action,
- unsigned int flags)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- unsigned int fast_exposure;
- int scan_power_mode;
- int use_fast_fed = 0;
- uint16_t fast_slope_table[256];
- unsigned int fast_slope_steps = 0;
- unsigned int feedl;
- GenesysRegister* r;
-/*number of scan lines to add in a scan_lines line*/
-
- DBG(DBG_proc, "%s : feed_steps=%d, action=%d, flags=%x\n", __func__, feed_steps, action, flags);
-
- memset(fast_slope_table,0xff,512);
-
- gl841_send_slope_table (dev, 0, fast_slope_table, 256);
- gl841_send_slope_table (dev, 1, fast_slope_table, 256);
- gl841_send_slope_table (dev, 2, fast_slope_table, 256);
- gl841_send_slope_table (dev, 3, fast_slope_table, 256);
- gl841_send_slope_table (dev, 4, fast_slope_table, 256);
-
- gl841_write_freq(dev, dev->motor.base_ydpi / 4);
-
- fast_slope_steps = 256;
- if (action == MOTOR_ACTION_FEED || action == MOTOR_ACTION_GO_HOME)
- {
- /* FEED and GO_HOME can use fastest slopes available */
- fast_exposure = gl841_exposure_time(dev, sensor,
- dev->motor.base_ydpi / 4,
- 0,
- 0,
- 0,
- &scan_power_mode);
- DBG(DBG_info, "%s : fast_exposure=%d pixels\n", __func__, fast_exposure);
- }
-
- if (action == MOTOR_ACTION_HOME_FREE) {
-/* HOME_FREE must be able to stop in one step, so do not try to get faster */
- fast_exposure = dev->motor.slopes[0][0].maximum_start_speed;
- }
-
- sanei_genesys_create_slope_table3 (
- dev,
- fast_slope_table,
- 256,
- fast_slope_steps,
- 0,
- fast_exposure,
- dev->motor.base_ydpi / 4,
- &fast_slope_steps,
- &fast_exposure, 0);
-
- feedl = feed_steps - fast_slope_steps*2;
- use_fast_fed = 1;
-
-/* all needed slopes available. we did even decide which mode to use.
- what next?
- - transfer slopes
-SCAN:
-flags \ use_fast_fed ! 0 1
-------------------------\--------------------
- 0 ! 0,1,2 0,1,2,3
-MOTOR_FLAG_AUTO_GO_HOME ! 0,1,2,4 0,1,2,3,4
-OFF: none
-FEED: 3
-GO_HOME: 3
-HOME_FREE: 3
- - setup registers
- * slope specific registers (already done)
- * DECSEL for HOME_FREE/GO_HOME/SCAN
- * FEEDL
- * MTRREV
- * MTRPWR
- * FASTFED
- * STEPSEL
- * MTRPWM
- * FSTPSEL
- * FASTPWM
- * HOMENEG
- * BWDSTEP
- * FWDSTEP
- * Z1
- * Z2
- */
-
- r = sanei_genesys_get_address(reg, 0x3d);
- r->value = (feedl >> 16) & 0xf;
- r = sanei_genesys_get_address(reg, 0x3e);
- r->value = (feedl >> 8) & 0xff;
- r = sanei_genesys_get_address(reg, 0x3f);
- r->value = feedl & 0xff;
- r = sanei_genesys_get_address(reg, 0x5e);
- r->value &= ~0xe0;
-
- r = sanei_genesys_get_address(reg, 0x25);
- r->value = 0;
- r = sanei_genesys_get_address(reg, 0x26);
- r->value = 0;
- r = sanei_genesys_get_address(reg, 0x27);
- r->value = 0;
-
- r = sanei_genesys_get_address(reg, 0x02);
- r->value &= ~0x01; /*LONGCURV OFF*/
- r->value &= ~0x80; /*NOT_HOME OFF*/
-
- r->value |= 0x10;
-
- if (action == MOTOR_ACTION_GO_HOME)
- r->value |= 0x06;
- else
- r->value &= ~0x06;
-
- if (use_fast_fed)
- r->value |= 0x08;
- else
- r->value &= ~0x08;
-
- if (flags & MOTOR_FLAG_AUTO_GO_HOME)
- r->value |= 0x20;
- else
- r->value &= ~0x20;
-
- r->value &= ~0x40;
-
- status = gl841_send_slope_table (dev, 3, fast_slope_table, 256);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- r = sanei_genesys_get_address(reg, 0x67);
- r->value = 0x3f;
-
- r = sanei_genesys_get_address(reg, 0x68);
- r->value = 0x3f;
-
- r = sanei_genesys_get_address(reg, REG_STEPNO);
- r->value = 0;
-
- r = sanei_genesys_get_address(reg, REG_FASTNO);
- r->value = 0;
-
- r = sanei_genesys_get_address(reg, 0x69);
- r->value = 0;
-
- r = sanei_genesys_get_address(reg, 0x6a);
- r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
-
- r = sanei_genesys_get_address(reg, 0x5f);
- r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
-
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl841_init_motor_regs_scan(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int scan_exposure_time,/*pixel*/
- float scan_yres,/*dpi, motor resolution*/
- int scan_step_type,/*0: full, 1: half, 2: quarter*/
- unsigned int scan_lines,/*lines, scan resolution*/
- unsigned int scan_dummy,
-/*number of scan lines to add in a scan_lines line*/
- unsigned int feed_steps,/*1/base_ydpi*/
-/*maybe float for half/quarter step resolution?*/
- int scan_power_mode,
- unsigned int flags)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- unsigned int fast_exposure;
- int use_fast_fed = 0;
- int dummy_power_mode;
- unsigned int fast_time;
- unsigned int slow_time;
- uint16_t slow_slope_table[256];
- uint16_t fast_slope_table[256];
- uint16_t back_slope_table[256];
- unsigned int slow_slope_time;
- unsigned int fast_slope_time;
- unsigned int slow_slope_steps = 0;
- unsigned int fast_slope_steps = 0;
- unsigned int back_slope_steps = 0;
- unsigned int feedl;
- GenesysRegister* r;
- unsigned int min_restep = 0x20;
- uint32_t z1, z2;
-
- DBG(DBG_proc, "%s : scan_exposure_time=%d, scan_yres=%g, scan_step_type=%d, scan_lines=%d,"
- " scan_dummy=%d, feed_steps=%d, scan_power_mode=%d, flags=%x\n", __func__,
- scan_exposure_time,
- scan_yres,
- scan_step_type,
- scan_lines,
- scan_dummy,
- feed_steps,
- scan_power_mode,
- flags);
-
- fast_exposure = gl841_exposure_time(dev, sensor,
- dev->motor.base_ydpi / 4,
- 0,
- 0,
- 0,
- &dummy_power_mode);
-
- DBG(DBG_info, "%s : fast_exposure=%d pixels\n", __func__, fast_exposure);
-
- memset(slow_slope_table,0xff,512);
-
- gl841_send_slope_table (dev, 0, slow_slope_table, 256);
- gl841_send_slope_table (dev, 1, slow_slope_table, 256);
- gl841_send_slope_table (dev, 2, slow_slope_table, 256);
- gl841_send_slope_table (dev, 3, slow_slope_table, 256);
- gl841_send_slope_table (dev, 4, slow_slope_table, 256);
-
- /* motor frequency table */
- gl841_write_freq(dev, scan_yres);
-
-/*
- we calculate both tables for SCAN. the fast slope step count depends on
- how many steps we need for slow acceleration and how much steps we are
- allowed to use.
- */
- slow_slope_time = sanei_genesys_create_slope_table3 (
- dev,
- slow_slope_table, 256,
- 256,
- scan_step_type,
- scan_exposure_time,
- scan_yres,
- &slow_slope_steps,
- NULL,
- scan_power_mode);
-
- sanei_genesys_create_slope_table3 (
- dev,
- back_slope_table, 256,
- 256,
- scan_step_type,
- 0,
- scan_yres,
- &back_slope_steps,
- NULL,
- scan_power_mode);
-
- if (feed_steps < (slow_slope_steps >> scan_step_type)) {
- /*TODO: what should we do here?? go back to exposure calculation?*/
- feed_steps = slow_slope_steps >> scan_step_type;
- }
-
- if (feed_steps > fast_slope_steps*2 -
- (slow_slope_steps >> scan_step_type))
- fast_slope_steps = 256;
- else
-/* we need to shorten fast_slope_steps here. */
- fast_slope_steps = (feed_steps -
- (slow_slope_steps >> scan_step_type))/2;
-
- DBG(DBG_info, "%s: Maximum allowed slope steps for fast slope: %d\n", __func__,
- fast_slope_steps);
-
- fast_slope_time = sanei_genesys_create_slope_table3 (
- dev,
- fast_slope_table, 256,
- fast_slope_steps,
- 0,
- fast_exposure,
- dev->motor.base_ydpi / 4,
- &fast_slope_steps,
- &fast_exposure,
- scan_power_mode);
-
- /* fast fed special cases handling */
- if (dev->model->gpo_type == GPO_XP300
- || dev->model->gpo_type == GPO_DP685)
- {
- /* quirk: looks like at least this scanner is unable to use
- 2-feed mode */
- use_fast_fed = 0;
- }
- else if (feed_steps < fast_slope_steps*2 + (slow_slope_steps >> scan_step_type)) {
- use_fast_fed = 0;
- DBG(DBG_info, "%s: feed too short, slow move forced.\n", __func__);
- } else {
-/* for deciding whether we should use fast mode we need to check how long we
- need for (fast)accelerating, moving, decelerating, (TODO: stopping?)
- (slow)accelerating again versus (slow)accelerating and moving. we need
- fast and slow tables here.
-*/
-/*NOTE: scan_exposure_time is per scan_yres*/
-/*NOTE: fast_exposure is per base_ydpi/4*/
-/*we use full steps as base unit here*/
- fast_time =
- fast_exposure / 4 *
- (feed_steps - fast_slope_steps*2 -
- (slow_slope_steps >> scan_step_type))
- + fast_slope_time*2 + slow_slope_time;
- slow_time =
- (scan_exposure_time * scan_yres) / dev->motor.base_ydpi *
- (feed_steps - (slow_slope_steps >> scan_step_type))
- + slow_slope_time;
-
- DBG(DBG_info, "%s: Time for slow move: %d\n", __func__, slow_time);
- DBG(DBG_info, "%s: Time for fast move: %d\n", __func__, fast_time);
-
- use_fast_fed = fast_time < slow_time;
- }
-
- if (use_fast_fed)
- feedl = feed_steps - fast_slope_steps*2 -
- (slow_slope_steps >> scan_step_type);
- else
- if ((feed_steps << scan_step_type) < slow_slope_steps)
- feedl = 0;
- else
- feedl = (feed_steps << scan_step_type) - slow_slope_steps;
- DBG(DBG_info, "%s: Decided to use %s mode\n", __func__, use_fast_fed?"fast feed":"slow feed");
-
-/* all needed slopes available. we did even decide which mode to use.
- what next?
- - transfer slopes
-SCAN:
-flags \ use_fast_fed ! 0 1
-------------------------\--------------------
- 0 ! 0,1,2 0,1,2,3
-MOTOR_FLAG_AUTO_GO_HOME ! 0,1,2,4 0,1,2,3,4
-OFF: none
-FEED: 3
-GO_HOME: 3
-HOME_FREE: 3
- - setup registers
- * slope specific registers (already done)
- * DECSEL for HOME_FREE/GO_HOME/SCAN
- * FEEDL
- * MTRREV
- * MTRPWR
- * FASTFED
- * STEPSEL
- * MTRPWM
- * FSTPSEL
- * FASTPWM
- * HOMENEG
- * BWDSTEP
- * FWDSTEP
- * Z1
- * Z2
- */
-
- r = sanei_genesys_get_address (reg, 0x3d);
- r->value = (feedl >> 16) & 0xf;
- r = sanei_genesys_get_address (reg, 0x3e);
- r->value = (feedl >> 8) & 0xff;
- r = sanei_genesys_get_address (reg, 0x3f);
- r->value = feedl & 0xff;
- r = sanei_genesys_get_address (reg, 0x5e);
- r->value &= ~0xe0;
-
- r = sanei_genesys_get_address (reg, 0x25);
- r->value = (scan_lines >> 16) & 0xf;
- r = sanei_genesys_get_address (reg, 0x26);
- r->value = (scan_lines >> 8) & 0xff;
- r = sanei_genesys_get_address (reg, 0x27);
- r->value = scan_lines & 0xff;
-
- r = sanei_genesys_get_address (reg, 0x02);
- r->value &= ~0x01; /*LONGCURV OFF*/
- r->value &= ~0x80; /*NOT_HOME OFF*/
- r->value |= 0x10;
-
- r->value &= ~0x06;
-
- if (use_fast_fed)
- r->value |= 0x08;
- else
- r->value &= ~0x08;
-
- if (flags & MOTOR_FLAG_AUTO_GO_HOME)
- r->value |= 0x20;
- else
- r->value &= ~0x20;
-
- if (flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
- r->value |= 0x40;
- else
- r->value &= ~0x40;
-
- status = gl841_send_slope_table (dev, 0, slow_slope_table, 256);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- status = gl841_send_slope_table (dev, 1, back_slope_table, 256);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- status = gl841_send_slope_table (dev, 2, slow_slope_table, 256);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- if (use_fast_fed) {
- status = gl841_send_slope_table (dev, 3, fast_slope_table, 256);
-
- if (status != SANE_STATUS_GOOD)
- return status;
- }
-
- if (flags & MOTOR_FLAG_AUTO_GO_HOME){
- status = gl841_send_slope_table (dev, 4, fast_slope_table, 256);
-
- if (status != SANE_STATUS_GOOD)
- return status;
- }
-
-
-/* now reg 0x21 and 0x24 are available, we can calculate reg 0x22 and 0x23,
- reg 0x60-0x62 and reg 0x63-0x65
- rule:
- 2*STEPNO+FWDSTEP=2*FASTNO+BWDSTEP
-*/
-/* steps of table 0*/
- if (min_restep < slow_slope_steps*2+2)
- min_restep = slow_slope_steps*2+2;
-/* steps of table 1*/
- if (min_restep < back_slope_steps*2+2)
- min_restep = back_slope_steps*2+2;
-/* steps of table 0*/
- r = sanei_genesys_get_address (reg, REG_FWDSTEP);
- r->value = min_restep - slow_slope_steps*2;
-/* steps of table 1*/
- r = sanei_genesys_get_address (reg, REG_BWDSTEP);
- r->value = min_restep - back_slope_steps*2;
-
-/*
- for z1/z2:
- in dokumentation mentioned variables a-d:
- a = time needed for acceleration, table 1
- b = time needed for reg 0x1f... wouldn't that be reg0x1f*exposure_time?
- c = time needed for acceleration, table 1
- d = time needed for reg 0x22... wouldn't that be reg0x22*exposure_time?
- z1 = (c+d-1) % exposure_time
- z2 = (a+b-1) % exposure_time
-*/
-/* i don't see any effect of this. i can only guess that this will enhance
- sub-pixel accuracy
- z1 = (slope_0_time-1) % exposure_time;
- z2 = (slope_0_time-1) % exposure_time;
-*/
- z1 = z2 = 0;
-
- DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
- DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
- r = sanei_genesys_get_address (reg, 0x60);
- r->value = ((z1 >> 16) & 0xff);
- r = sanei_genesys_get_address (reg, 0x61);
- r->value = ((z1 >> 8) & 0xff);
- r = sanei_genesys_get_address (reg, 0x62);
- r->value = (z1 & 0xff);
- r = sanei_genesys_get_address (reg, 0x63);
- r->value = ((z2 >> 16) & 0xff);
- r = sanei_genesys_get_address (reg, 0x64);
- r->value = ((z2 >> 8) & 0xff);
- r = sanei_genesys_get_address (reg, 0x65);
- r->value = (z2 & 0xff);
-
- r = sanei_genesys_get_address (reg, REG1E);
- r->value &= REG1E_WDTIME;
- r->value |= scan_dummy;
-
- r = sanei_genesys_get_address (reg, 0x67);
- r->value = 0x3f | (scan_step_type << 6);
-
- r = sanei_genesys_get_address (reg, 0x68);
- r->value = 0x3f;
-
- r = sanei_genesys_get_address (reg, REG_STEPNO);
- r->value = (slow_slope_steps >> 1) + (slow_slope_steps & 1);
-
- r = sanei_genesys_get_address (reg, REG_FASTNO);
- r->value = (back_slope_steps >> 1) + (back_slope_steps & 1);
-
- r = sanei_genesys_get_address (reg, 0x69);
- r->value = (slow_slope_steps >> 1) + (slow_slope_steps & 1);
-
- r = sanei_genesys_get_address (reg, 0x6a);
- r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
-
- r = sanei_genesys_get_address (reg, 0x5f);
- r->value = (fast_slope_steps >> 1) + (fast_slope_steps & 1);
-
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static int
-gl841_get_dpihw(Genesys_Device * dev)
-{
- GenesysRegister* r;
- r = sanei_genesys_get_address(&dev->reg, 0x05);
- if ((r->value & REG05_DPIHW) == REG05_DPIHW_600)
- return 600;
- if ((r->value & REG05_DPIHW) == REG05_DPIHW_1200)
- return 1200;
- if ((r->value & REG05_DPIHW) == REG05_DPIHW_2400)
- return 2400;
- return 0;
-}
-
-static SANE_Status
-gl841_init_optical_regs_off(Genesys_Register_Set * reg)
-{
- GenesysRegister* r;
-
- DBGSTART;
-
- r = sanei_genesys_get_address(reg, 0x01);
- r->value &= ~REG01_SCAN;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl841_init_optical_regs_scan(Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int exposure_time,
- unsigned int used_res,
- unsigned int start,
- unsigned int pixels,
- int channels,
- int depth,
- SANE_Bool half_ccd,
- ColorFilter color_filter,
- int flags
- )
-{
- unsigned int words_per_line;
- unsigned int end;
- unsigned int dpiset;
- GenesysRegister* r;
- SANE_Status status = SANE_STATUS_GOOD;
- uint16_t expavg, expr, expb, expg;
-
- DBG(DBG_proc, "%s : exposure_time=%d, used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
- "half_ccd=%d, flags=%x\n", __func__,
- exposure_time,
- used_res,
- start,
- pixels,
- channels,
- depth,
- half_ccd,
- flags);
-
- end = start + pixels;
-
- status = gl841_set_fe(dev, sensor, AFE_SET);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* adjust used_res for chosen dpihw */
- used_res = used_res * gl841_get_dpihw(dev) / sensor.optical_res;
-
-/*
- with half_ccd the optical resolution of the ccd is halved. We don't apply this
- to dpihw, so we need to double dpiset.
-
- For the scanner only the ratio of dpiset and dpihw is of relevance to scale
- down properly.
-*/
- if (half_ccd)
- dpiset = used_res * 2;
- else
- dpiset = used_res;
-
- /* gpio part.*/
- if (dev->model->gpo_type == GPO_CANONLIDE35)
- {
- r = sanei_genesys_get_address (reg, REG6C);
- if (half_ccd)
- r->value &= ~0x80;
- else
- r->value |= 0x80;
- }
- if (dev->model->gpo_type == GPO_CANONLIDE80)
- {
- r = sanei_genesys_get_address (reg, REG6C);
- if (half_ccd)
- {
- r->value &= ~0x40;
- r->value |= 0x20;
- }
- else
- {
- r->value &= ~0x20;
- r->value |= 0x40;
- }
- }
-
- /* enable shading */
- r = sanei_genesys_get_address (reg, 0x01);
- r->value |= REG01_SCAN;
- if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
- (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
- r->value &= ~REG01_DVDSET;
- else
- r->value |= REG01_DVDSET;
-
- /* average looks better than deletion, and we are already set up to
- use one of the average enabled resolutions
- */
- r = sanei_genesys_get_address (reg, 0x03);
- r->value |= REG03_AVEENB;
- sanei_genesys_set_lamp_power(dev, sensor, *reg, !(flags & OPTICAL_FLAG_DISABLE_LAMP));
-
- /* BW threshold */
- r = sanei_genesys_get_address (reg, 0x2e);
- r->value = dev->settings.threshold;
- r = sanei_genesys_get_address (reg, 0x2f);
- r->value = dev->settings.threshold;
-
-
- /* monochrome / color scan */
- r = sanei_genesys_get_address (reg, 0x04);
- switch (depth) {
- case 1:
- r->value &= ~REG04_BITSET;
- r->value |= REG04_LINEART;
- break;
- case 8:
- r->value &= ~(REG04_LINEART | REG04_BITSET);
- break;
- case 16:
- r->value &= ~REG04_LINEART;
- r->value |= REG04_BITSET;
- break;
- }
-
- /* AFEMOD should depend on FESET, and we should set these
- * bits separately */
- r->value &= ~(REG04_FILTER | REG04_AFEMOD);
- if (flags & OPTICAL_FLAG_ENABLE_LEDADD)
- {
- r->value |= 0x10; /* no filter */
- }
- else if (channels == 1)
- {
- switch (color_filter)
- {
- case ColorFilter::RED:
- r->value |= 0x14;
- break;
- case ColorFilter::GREEN:
- r->value |= 0x18;
- break;
- case ColorFilter::BLUE:
- r->value |= 0x1c;
- break;
- default:
- r->value |= 0x10;
- break;
- }
- }
- else
- {
- if (dev->model->ccd_type == CCD_PLUSTEK_3600)
- {
- r->value |= 0x22; /* slow color pixel by pixel */
- }
- else
- {
- r->value |= 0x10; /* color pixel by pixel */
- }
- }
-
- /* CIS scanners can do true gray by setting LEDADD */
- r = sanei_genesys_get_address (reg, 0x87);
- r->value &= ~REG87_LEDADD;
- if (flags & OPTICAL_FLAG_ENABLE_LEDADD)
- {
- r->value |= REG87_LEDADD;
- sanei_genesys_get_double (reg, REG_EXPR, &expr);
- sanei_genesys_get_double (reg, REG_EXPG, &expg);
- sanei_genesys_get_double (reg, REG_EXPB, &expb);
-
- /* use minimal exposure for best image quality */
- expavg = expg;
- if (expr < expg)
- expavg = expr;
- if (expb < expavg)
- expavg = expb;
-
- sanei_genesys_set_double(&dev->reg, REG_EXPR, expavg);
- sanei_genesys_set_double(&dev->reg, REG_EXPG, expavg);
- sanei_genesys_set_double(&dev->reg, REG_EXPB, expavg);
- }
-
- /* enable gamma tables */
- r = sanei_genesys_get_address (reg, 0x05);
- if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
- r->value &= ~REG05_GMMENB;
- else
- r->value |= REG05_GMMENB;
-
- /* sensor parameters */
- sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 1, half_ccd);
-
- r = sanei_genesys_get_address (reg, 0x29);
- r->value = 255; /*<<<"magic" number, only suitable for cis*/
-
- sanei_genesys_set_double(reg, REG_DPISET, dpiset);
- sanei_genesys_set_double(reg, REG_STRPIXEL, start);
- sanei_genesys_set_double(reg, REG_ENDPIXEL, end);
- DBG(DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d\n", __func__, start, end);
-
- /* words(16bit) before gamma, conversion to 8 bit or lineart*/
- words_per_line = (pixels * dpiset) / gl841_get_dpihw(dev);
-
- words_per_line *= channels;
-
- if (depth == 1)
- words_per_line = (words_per_line >> 3) + ((words_per_line & 7)?1:0);
- else
- words_per_line *= depth / 8;
-
- dev->wpl = words_per_line;
- dev->bpl = words_per_line;
-
- r = sanei_genesys_get_address (reg, 0x35);
- r->value = LOBYTE (HIWORD (words_per_line));
- r = sanei_genesys_get_address (reg, 0x36);
- r->value = HIBYTE (LOWORD (words_per_line));
- r = sanei_genesys_get_address (reg, 0x37);
- r->value = LOBYTE (LOWORD (words_per_line));
-
- sanei_genesys_set_double(reg, REG_LPERIOD, exposure_time);
-
- r = sanei_genesys_get_address (reg, 0x34);
- r->value = sensor.dummy_pixel;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static int
-gl841_get_led_exposure(Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int d,r,g,b,m;
- if (!dev->model->is_cis)
- return 0;
- d = dev->reg.find_reg(0x19).value;
-
- r = sensor.exposure.red;
- g = sensor.exposure.green;
- b = sensor.exposure.blue;
-
- m = r;
- if (m < g)
- m = g;
- if (m < b)
- m = b;
-
- return m + d;
-}
-
-/** @brief compute exposure time
- * Compute exposure time for the device and the given scan resolution,
- * also compute scan_power_mode
- */
-static int
-gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor,
- float slope_dpi,
- int scan_step_type,
- int start,
- int used_pixels,
- int *scan_power_mode)
-{
-int exposure_time = 0;
-int exposure_time2 = 0;
-int led_exposure;
-
- *scan_power_mode=0;
- led_exposure=gl841_get_led_exposure(dev, sensor);
- exposure_time = sanei_genesys_exposure_time2(
- dev,
- slope_dpi,
- scan_step_type,
- start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/
- led_exposure,
- *scan_power_mode);
-
- while(*scan_power_mode + 1 < dev->motor.power_mode_count) {
- exposure_time2 = sanei_genesys_exposure_time2(
- dev,
- slope_dpi,
- scan_step_type,
- start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/
- led_exposure,
- *scan_power_mode + 1);
- if (exposure_time < exposure_time2)
- break;
- exposure_time = exposure_time2;
- (*scan_power_mode)++;
- }
-
- return exposure_time;
-}
-
-/**@brief compute scan_step_type
- * Try to do at least 4 steps per line. if that is impossible we will have to
- * live with that.
- * @param dev device
- * @param yres motor resolution
- */
-static int
-gl841_scan_step_type(Genesys_Device *dev, int yres)
-{
-int scan_step_type=0;
-
- /* TODO : check if there is a bug around the use of max_step_type */
- /* should be <=1, need to chek all devices entry in genesys_devices */
- if (yres*4 < dev->motor.base_ydpi || dev->motor.max_step_type <= 0)
- {
- scan_step_type = 0;
- }
- else if (yres*4 < dev->motor.base_ydpi*2 || dev->motor.max_step_type <= 1)
- {
- scan_step_type = 1;
- }
- else
- {
- scan_step_type = 2;
- }
-
- /* this motor behaves differently */
- if (dev->model->motor_type==MOTOR_CANONLIDE80)
- {
- /* driven by 'frequency' tables ? */
- scan_step_type = 0;
- }
-
- return scan_step_type;
-}
-
-/* set up registers for an actual scan
- *
- * this function sets up the scanner to scan in normal or single line mode
- */
-static
-SANE_Status
-gl841_init_scan_regs(Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SetupParams& params)
-{
- params.assert_valid();
-
- int used_res;
- int start, used_pixels;
- int bytes_per_line;
- int move;
- unsigned int lincnt;
- int exposure_time;
- int scan_power_mode;
- int i;
- int stagger;
- int avg;
-
- int slope_dpi = 0;
- int dummy = 0;
- int scan_step_type = 1;
- int max_shift;
- size_t requested_buffer_size, read_buffer_size;
-
- SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
- int optical_res;
- SANE_Status status = SANE_STATUS_GOOD;
- unsigned int oflags; /**> optical flags */
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
-/*
-results:
-
-for scanner:
-half_ccd
-start
-end
-dpiset
-exposure_time
-dummy
-z1
-z2
-
-for ordered_read:
- dev->words_per_line
- dev->read_factor
- dev->requested_buffer_size
- dev->read_buffer_size
- dev->read_pos
- dev->read_bytes_in_buffer
- dev->read_bytes_left
- dev->max_shift
- dev->stagger
-
-independent of our calculated values:
- dev->total_bytes_read
- dev->bytes_to_read
- */
-
-/* half_ccd */
- /* we have 2 domains for ccd: xres below or above half ccd max dpi */
- if (sensor.get_ccd_size_divisor_for_dpi(params.xres) > 1) {
- half_ccd = SANE_TRUE;
- } else {
- half_ccd = SANE_FALSE;
- }
-
-/* optical_res */
-
- optical_res = sensor.optical_res;
- if (half_ccd)
- optical_res /= 2;
-
-/* stagger */
-
- if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG(DBG_info, "%s : stagger=%d lines\n", __func__, stagger);
-
-/* used_res */
- i = optical_res / params.xres;
-
-/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
-
- if (i < 2 || (params.flags & SCAN_FLAG_USE_OPTICAL_RES)) /* optical_res >= xres > optical_res/2 */
- used_res = optical_res;
- else if (i < 3) /* optical_res/2 >= xres > optical_res/3 */
- used_res = optical_res/2;
- else if (i < 4) /* optical_res/3 >= xres > optical_res/4 */
- used_res = optical_res/3;
- else if (i < 5) /* optical_res/4 >= xres > optical_res/5 */
- used_res = optical_res/4;
- else if (i < 6) /* optical_res/5 >= xres > optical_res/6 */
- used_res = optical_res/5;
- else if (i < 8) /* optical_res/6 >= xres > optical_res/8 */
- used_res = optical_res/6;
- else if (i < 10) /* optical_res/8 >= xres > optical_res/10 */
- used_res = optical_res/8;
- else if (i < 12) /* optical_res/10 >= xres > optical_res/12 */
- used_res = optical_res/10;
- else if (i < 15) /* optical_res/12 >= xres > optical_res/15 */
- used_res = optical_res/12;
- else
- used_res = optical_res/15;
-
- /* compute scan parameters values */
- /* pixels are allways given at half or full CCD optical resolution */
- /* use detected left margin and fixed value */
- /* start */
- /* add x coordinates */
- start = ((sensor.CCD_start_xoffset + params.startx) * used_res) / sensor.optical_res;
-
- /* needs to be aligned for used_res */
- start = (start * optical_res) / used_res;
-
- start += sensor.dummy_pixel + 1;
-
- if (stagger > 0)
- start |= 1;
-
- /* in case of SHDAREA, we need to align start
- * on pixel average factor, startx is different of
- * 0 only when calling for function to setup for
- * scan, where shading data needs to be align */
- if((dev->reg.find_reg(0x01).value & REG01_SHDAREA) != 0)
- {
- avg=optical_res/used_res;
- start=(start/avg)*avg;
- }
-
- /* compute correct pixels number */
- /* pixels */
- used_pixels = (params.pixels * optical_res) / params.xres;
-
- /* round up pixels number if needed */
- if (used_pixels * params.xres < params.pixels * optical_res)
- used_pixels++;
-
-/* dummy */
- /* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1
- dummy line. Maybe the dummy line adds correctness since the motor runs
- slower (higher dpi)
- */
-/* for cis this creates better aligned color lines:
-dummy \ scanned lines
- 0: R G B R ...
- 1: R G B - R ...
- 2: R G B - - R ...
- 3: R G B - - - R ...
- 4: R G B - - - - R ...
- 5: R G B - - - - - R ...
- 6: R G B - - - - - - R ...
- 7: R G B - - - - - - - R ...
- 8: R G B - - - - - - - - R ...
- 9: R G B - - - - - - - - - R ...
- 10: R G B - - - - - - - - - - R ...
- 11: R G B - - - - - - - - - - - R ...
- 12: R G B - - - - - - - - - - - - R ...
- 13: R G B - - - - - - - - - - - - - R ...
- 14: R G B - - - - - - - - - - - - - - R ...
- 15: R G B - - - - - - - - - - - - - - - R ...
- -- pierre
- */
- dummy = 0;
-
-/* slope_dpi */
-/* cis color scan is effectively a gray scan with 3 gray lines per color
- line and a FILTER of 0 */
- if (dev->model->is_cis)
- slope_dpi = params.yres* params.channels;
- else
- slope_dpi = params.yres;
-
- slope_dpi = slope_dpi * (1 + dummy);
-
- scan_step_type = gl841_scan_step_type(dev, params.yres);
- exposure_time = gl841_exposure_time(dev, sensor,
- slope_dpi,
- scan_step_type,
- start,
- used_pixels,
- &scan_power_mode);
- DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
-
- /*** optical parameters ***/
- /* in case of dynamic lineart, we use an internal 8 bit gray scan
- * to generate 1 lineart data */
- if(params.flags & SCAN_FLAG_DYNAMIC_LINEART)
- {
- params.depth=8;
- }
-
- oflags=0;
- if (params.flags & SCAN_FLAG_DISABLE_SHADING)
- {
- oflags |= OPTICAL_FLAG_DISABLE_SHADING;
- }
- if ((params.flags & SCAN_FLAG_DISABLE_GAMMA) || (params.depth==16))
- {
- oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
- }
- if (params.flags & SCAN_FLAG_DISABLE_LAMP)
- {
- oflags |= OPTICAL_FLAG_DISABLE_LAMP;
- }
- if (params.flags & SCAN_FLAG_ENABLE_LEDADD)
- {
- oflags |= OPTICAL_FLAG_ENABLE_LEDADD;
- }
-
- status = gl841_init_optical_regs_scan(dev, sensor,
- reg,
- exposure_time,
- used_res,
- start,
- used_pixels,
- params.channels,
- params.depth,
- half_ccd,
- params.color_filter,
- oflags);
- if (status != SANE_STATUS_GOOD)
- {
- return status;
- }
-
-/*** motor parameters ***/
-
- /* scanned area must be enlarged by max color shift needed */
- max_shift=sanei_genesys_compute_max_shift(dev, params.channels,params.yres,params.flags);
-
- /* lincnt */
- lincnt = params.lines + max_shift + stagger;
-
- /* add tl_y to base movement */
- move = params.starty;
- DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
-
- /* subtract current head position */
- move -= dev->scanhead_position_in_steps;
- DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
-
- if (move < 0)
- move = 0;
-
- /* round it */
-/* the move is not affected by dummy -- pierre */
-/* move = ((move + dummy) / (dummy + 1)) * (dummy + 1);
- DBG(DBG_info, "%s: move=%d steps\n", __func__, move);*/
-
- if (params.flags & SCAN_FLAG_SINGLE_LINE)
- status = gl841_init_motor_regs_off(reg, dev->model->is_cis?lincnt* params.channels:lincnt);
- else
- status = gl841_init_motor_regs_scan(dev, sensor,
- reg,
- exposure_time,
- slope_dpi,
- scan_step_type,
- dev->model->is_cis?lincnt* params.channels:lincnt,
- dummy,
- move,
- scan_power_mode,
- (params.flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)?
- MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE:0
- );
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
-
- /*** prepares data reordering ***/
-
-/* words_per_line */
- bytes_per_line = (used_pixels * used_res) / optical_res;
- bytes_per_line = (bytes_per_line * params.channels * params.depth) / 8;
-
- requested_buffer_size = 8 * bytes_per_line;
- /* we must use a round number of bytes_per_line */
- if (requested_buffer_size > sanei_genesys_get_bulk_max_size(dev))
- requested_buffer_size =
- (sanei_genesys_get_bulk_max_size(dev) / bytes_per_line) * bytes_per_line;
-
- read_buffer_size =
- 2 * requested_buffer_size +
- ((max_shift + stagger) * used_pixels * params.channels * params.depth) / 8;
-
- dev->read_buffer.clear();
- dev->read_buffer.alloc(read_buffer_size);
-
- dev->lines_buffer.clear();
- dev->lines_buffer.alloc(read_buffer_size);
-
- dev->shrink_buffer.clear();
- dev->shrink_buffer.alloc(requested_buffer_size);
-
- dev->out_buffer.clear();
- dev->out_buffer.alloc((8 * dev->settings.pixels * params.channels * params.depth) / 8);
-
- dev->read_bytes_left = bytes_per_line * lincnt;
-
- DBG(DBG_info, "%s: physical bytes to read = %lu\n", __func__, (u_long) dev->read_bytes_left);
- dev->read_active = SANE_TRUE;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res)/optical_res;
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
-/* TODO: should this be done elsewhere? */
- /* scan bytes to send to the frontend */
- /* theory :
- target_size =
- (dev->settings.pixels * dev->settings.lines * channels * depth) / 8;
- but it suffers from integer overflow so we do the following:
-
- 1 bit color images store color data byte-wise, eg byte 0 contains
- 8 bits of red data, byte 1 contains 8 bits of green, byte 2 contains
- 8 bits of blue.
- This does not fix the overflow, though.
- 644mp*16 = 10gp, leading to an overflow
- -- pierre
- */
-
- dev->total_bytes_read = 0;
- if (params.depth == 1)
- dev->total_bytes_to_read =
- ((dev->settings.pixels * dev->settings.lines) / 8 +
- (((dev->settings.pixels * dev->settings.lines)%8)?1:0)
- ) * params.channels;
- else
- dev->total_bytes_to_read =
- dev->settings.pixels * dev->settings.lines * params.channels * (params.depth / 8);
-
- DBG(DBG_info, "%s: total bytes to send = %lu\n", __func__, (u_long) dev->total_bytes_to_read);
-/* END TODO */
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-static void gl841_calculate_current_setup(Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int depth;
- int start;
-
- int used_res;
- int used_pixels;
- unsigned int lincnt;
- int exposure_time;
- int scan_power_mode;
- int i;
- int stagger;
-
- int slope_dpi = 0;
- int dummy = 0;
- int scan_step_type = 1;
- int max_shift;
-
- SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
- int optical_res;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
-/* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
-/* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
-/* start */
- start = SANE_UNFIX (dev->model->x_offset);
-
- start += dev->settings.tl_x;
-
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start;
- params.starty = 0; // not used
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = 0;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
-/* half_ccd */
- /* we have 2 domains for ccd: xres below or above half ccd max dpi */
- if (sensor.get_ccd_size_divisor_for_dpi(params.xres) > 1) {
- half_ccd = SANE_TRUE;
- } else {
- half_ccd = SANE_FALSE;
- }
-
-/* optical_res */
-
- optical_res = sensor.optical_res;
- if (half_ccd)
- optical_res /= 2;
-
-/* stagger */
-
- if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG(DBG_info, "%s: stagger=%d lines\n", __func__, stagger);
-
-/* used_res */
- i = optical_res / params.xres;
-
-/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
-
- if (i < 2) /* optical_res >= xres > optical_res/2 */
- used_res = optical_res;
- else if (i < 3) /* optical_res/2 >= xres > optical_res/3 */
- used_res = optical_res/2;
- else if (i < 4) /* optical_res/3 >= xres > optical_res/4 */
- used_res = optical_res/3;
- else if (i < 5) /* optical_res/4 >= xres > optical_res/5 */
- used_res = optical_res/4;
- else if (i < 6) /* optical_res/5 >= xres > optical_res/6 */
- used_res = optical_res/5;
- else if (i < 8) /* optical_res/6 >= xres > optical_res/8 */
- used_res = optical_res/6;
- else if (i < 10) /* optical_res/8 >= xres > optical_res/10 */
- used_res = optical_res/8;
- else if (i < 12) /* optical_res/10 >= xres > optical_res/12 */
- used_res = optical_res/10;
- else if (i < 15) /* optical_res/12 >= xres > optical_res/15 */
- used_res = optical_res/12;
- else
- used_res = optical_res/15;
-
- /* compute scan parameters values */
- /* pixels are allways given at half or full CCD optical resolution */
- /* use detected left margin and fixed value */
- start = ((sensor.CCD_start_xoffset + params.startx) * used_res) / sensor.optical_res;
-
-/* needs to be aligned for used_res */
- start = (start * optical_res) / used_res;
-
- start += sensor.dummy_pixel + 1;
-
- if (stagger > 0) {
- start |= 1;
- }
-
- used_pixels = (params.pixels * optical_res) / params.xres;
-
- // round up pixels number if needed
- if (used_pixels * params.xres < params.pixels * optical_res) {
- used_pixels++;
- }
-
- /* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1
- dummy line. Maybe the dummy line adds correctness since the motor runs
- slower (higher dpi)
- */
-/* for cis this creates better aligned color lines:
-dummy \ scanned lines
- 0: R G B R ...
- 1: R G B - R ...
- 2: R G B - - R ...
- 3: R G B - - - R ...
- 4: R G B - - - - R ...
- 5: R G B - - - - - R ...
- 6: R G B - - - - - - R ...
- 7: R G B - - - - - - - R ...
- 8: R G B - - - - - - - - R ...
- 9: R G B - - - - - - - - - R ...
- 10: R G B - - - - - - - - - - R ...
- 11: R G B - - - - - - - - - - - R ...
- 12: R G B - - - - - - - - - - - - R ...
- 13: R G B - - - - - - - - - - - - - R ...
- 14: R G B - - - - - - - - - - - - - - R ...
- 15: R G B - - - - - - - - - - - - - - - R ...
- -- pierre
- */
- dummy = 0;
-
-/* cis color scan is effectively a gray scan with 3 gray lines per color
- line and a FILTER of 0 */
- if (dev->model->is_cis) {
- slope_dpi = params.yres * params.channels;
- } else {
- slope_dpi = params.yres;
- }
-
- slope_dpi = slope_dpi * (1 + dummy);
-
- scan_step_type = gl841_scan_step_type(dev, params.yres);
- exposure_time = gl841_exposure_time(dev, sensor,
- slope_dpi,
- scan_step_type,
- start,
- used_pixels,
- &scan_power_mode);
- DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
-
- /* scanned area must be enlarged by max color shift needed */
- max_shift = sanei_genesys_compute_max_shift(dev, params.channels, params.yres, 0);
-
- lincnt = params.lines + max_shift + stagger;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res)/optical_res;
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
- DBGCOMPLETED;
-}
-
-/*for fast power saving methods only, like disabling certain amplifiers*/
-static SANE_Status gl841_save_power(Genesys_Device * dev, SANE_Bool enable)
-{
- uint8_t val;
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- DBG(DBG_proc, "%s: enable = %d\n", __func__, enable);
-
- if (enable)
- {
- if (dev->model->gpo_type == GPO_CANONLIDE35)
- {
-/* expect GPIO17 to be enabled, and GPIO9 to be disabled,
- while GPIO8 is disabled*/
-/* final state: GPIO8 disabled, GPIO9 enabled, GPIO17 disabled,
- GPIO18 disabled*/
-
- sanei_genesys_read_register(dev, REG6D, &val);
- sanei_genesys_write_register(dev, REG6D, val | 0x80);
-
- sanei_genesys_sleep_ms(1);
-
- /*enable GPIO9*/
- sanei_genesys_read_register(dev, REG6C, &val);
- sanei_genesys_write_register(dev, REG6C, val | 0x01);
-
- /*disable GPO17*/
- sanei_genesys_read_register(dev, REG6B, &val);
- sanei_genesys_write_register(dev, REG6B, val & ~REG6B_GPO17);
-
- /*disable GPO18*/
- sanei_genesys_read_register(dev, REG6B, &val);
- sanei_genesys_write_register(dev, REG6B, val & ~REG6B_GPO18);
-
- sanei_genesys_sleep_ms(1);
-
- sanei_genesys_read_register(dev, REG6D, &val);
- sanei_genesys_write_register(dev, REG6D, val & ~0x80);
-
- }
- if (dev->model->gpo_type == GPO_DP685)
- {
- sanei_genesys_read_register(dev, REG6B, &val);
- sanei_genesys_write_register(dev, REG6B, val & ~REG6B_GPO17);
- dev->reg.find_reg(0x6b).value &= ~REG6B_GPO17;
- dev->calib_reg.find_reg(0x6b).value &= ~REG6B_GPO17;
- }
-
- gl841_set_fe(dev, sensor, AFE_POWER_SAVE);
-
- }
- else
- {
- if (dev->model->gpo_type == GPO_CANONLIDE35)
- {
-/* expect GPIO17 to be enabled, and GPIO9 to be disabled,
- while GPIO8 is disabled*/
-/* final state: GPIO8 enabled, GPIO9 disabled, GPIO17 enabled,
- GPIO18 enabled*/
-
- sanei_genesys_read_register(dev, REG6D, &val);
- sanei_genesys_write_register(dev, REG6D, val | 0x80);
-
- sanei_genesys_sleep_ms(10);
-
- /*disable GPIO9*/
- sanei_genesys_read_register(dev, REG6C, &val);
- sanei_genesys_write_register(dev, REG6C, val & ~0x01);
-
- /*enable GPIO10*/
- sanei_genesys_read_register(dev, REG6C, &val);
- sanei_genesys_write_register(dev, REG6C, val | 0x02);
-
- /*enable GPO17*/
- sanei_genesys_read_register(dev, REG6B, &val);
- sanei_genesys_write_register(dev, REG6B, val | REG6B_GPO17);
- dev->reg.find_reg(0x6b).value |= REG6B_GPO17;
- dev->calib_reg.find_reg(0x6b).value |= REG6B_GPO17;
-
- /*enable GPO18*/
- sanei_genesys_read_register(dev, REG6B, &val);
- sanei_genesys_write_register(dev, REG6B, val | REG6B_GPO18);
- dev->reg.find_reg(0x6b).value |= REG6B_GPO18;
- dev->calib_reg.find_reg(0x6b).value |= REG6B_GPO18;
-
- }
- if (dev->model->gpo_type == GPO_DP665
- || dev->model->gpo_type == GPO_DP685)
- {
- sanei_genesys_read_register(dev, REG6B, &val);
- sanei_genesys_write_register(dev, REG6B, val | REG6B_GPO17);
- dev->reg.find_reg(0x6b).value |= REG6B_GPO17;
- dev->calib_reg.find_reg(0x6b).value |= REG6B_GPO17;
- }
-
- }
-
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl841_set_powersaving (Genesys_Device * dev,
- int delay /* in minutes */ )
-{
- SANE_Status status = SANE_STATUS_GOOD;
- // FIXME: SEQUENTIAL not really needed in this case
- Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
- int rate, exposure_time, tgtime, time;
-
- DBG(DBG_proc, "%s (delay = %d)\n", __func__, delay);
-
- local_reg.init_reg(0x01, dev->reg.get8(0x01)); /* disable fastmode */
- local_reg.init_reg(0x03, dev->reg.get8(0x03)); /* Lamp power control */
- local_reg.init_reg(0x05, dev->reg.get8(0x05)); /*& ~REG05_BASESEL*/; /* 24 clocks/pixel */
- local_reg.init_reg(0x18, 0x00); // Set CCD type
- local_reg.init_reg(0x38, 0x00);
- local_reg.init_reg(0x39, 0x00);
-
- // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE
- local_reg.init_reg(0x1c, dev->reg.get8(0x05) & ~REG1C_TGTIME);
-
- if (!delay) {
- local_reg.find_reg(0x03).value = local_reg.find_reg(0x03).value & 0xf0; /* disable lampdog and set lamptime = 0 */
- } else if (delay < 20) {
- local_reg.find_reg(0x03).value = (local_reg.find_reg(0x03).value & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */
- } else {
- local_reg.find_reg(0x03).value = (local_reg.find_reg(0x03).value & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */
- }
-
- time = delay * 1000 * 60; /* -> msec */
- exposure_time =
- (uint32_t) (time * 32000.0 /
- (24.0 * 64.0 * (local_reg.find_reg(0x03).value & REG03_LAMPTIM) *
- 1024.0) + 0.5);
- /* 32000 = system clock, 24 = clocks per pixel */
- rate = (exposure_time + 65536) / 65536;
- if (rate > 4)
- {
- rate = 8;
- tgtime = 3;
- }
- else if (rate > 2)
- {
- rate = 4;
- tgtime = 2;
- }
- else if (rate > 1)
- {
- rate = 2;
- tgtime = 1;
- }
- else
- {
- rate = 1;
- tgtime = 0;
- }
-
- local_reg.find_reg(0x1c).value |= tgtime;
- exposure_time /= rate;
-
- if (exposure_time > 65535)
- exposure_time = 65535;
-
- local_reg.set8(0x38, exposure_time >> 8);
- local_reg.set8(0x39, exposure_time & 255); /* lowbyte */
-
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
-}
-
-static SANE_Status
-gl841_start_action (Genesys_Device * dev)
-{
- return sanei_genesys_write_register (dev, 0x0f, 0x01);
-}
-
-static SANE_Status
-gl841_stop_action (Genesys_Device * dev)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val40, val;
- unsigned int loop;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- status = sanei_genesys_read_register(dev, 0x40, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* only stop action if needed */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
- {
- DBG(DBG_info, "%s: already stopped\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- local_reg = dev->reg;
-
- gl841_init_optical_regs_off(&local_reg);
-
- gl841_init_motor_regs_off(&local_reg,0);
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* looks like writing the right registers to zero is enough to get the chip
- out of scan mode into command mode, actually triggering(writing to
- register 0x0f) seems to be unnecessary */
-
- loop = 10;
- while (loop > 0)
- {
- status = sanei_genesys_read_register(dev, 0x40, &val40);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* if scanner is in command mode, we are done */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
- {
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- sanei_genesys_sleep_ms(100);
- loop--;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_IO_ERROR;
-}
-
-static SANE_Status
-gl841_get_paper_sensor(Genesys_Device * dev, SANE_Bool * paper_loaded)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- status = sanei_genesys_read_register(dev, REG6D, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read gpio: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- *paper_loaded = (val & 0x1) == 0;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl841_eject_document (Genesys_Device * dev)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- SANE_Bool paper_loaded;
- unsigned int init_steps;
- float feed_mm;
- int loop;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- if (dev->model->is_sheetfed == SANE_FALSE)
- {
- DBG(DBG_proc, "%s: there is no \"eject sheet\"-concept for non sheet fed\n", __func__);
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
-
- local_reg.clear();
- val = 0;
-
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read status register: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl841_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop motor: %s\n", __func__, sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
-
- local_reg = dev->reg;
-
- gl841_init_optical_regs_off(&local_reg);
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
- gl841_init_motor_regs(dev, sensor, &local_reg,
- 65536,MOTOR_ACTION_FEED,0);
-
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- try {
- status = gl841_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl841_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- sanei_genesys_bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl841_stop_action (dev);
- /* send original registers */
- sanei_genesys_bulk_write_register(dev, dev->reg);
- return status;
- }
-
- RIE(gl841_get_paper_sensor(dev, &paper_loaded));
- if (paper_loaded)
- {
- DBG(DBG_info, "%s: paper still loaded\n", __func__);
- /* force document TRUE, because it is definitely present */
- dev->document = SANE_TRUE;
- dev->scanhead_position_in_steps = 0;
-
- loop = 300;
- while (loop > 0) /* do not wait longer then 30 seconds */
- {
-
- RIE(gl841_get_paper_sensor(dev, &paper_loaded));
-
- if (!paper_loaded)
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- DBG(DBG_proc, "%s: finished\n", __func__);
- break;
- }
- sanei_genesys_sleep_ms(100);
- --loop;
- }
-
- if (loop == 0)
- {
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl841_stop_action (dev);
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
- }
-
- feed_mm = SANE_UNFIX(dev->model->eject_feed);
- if (dev->document)
- {
- feed_mm += SANE_UNFIX(dev->model->post_scan);
- }
-
- status = sanei_genesys_read_feed_steps(dev, &init_steps);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read feed steps: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* now feed for extra <number> steps */
- loop = 0;
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- unsigned int steps;
-
- status = sanei_genesys_read_feed_steps(dev, &steps);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read feed steps: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_info, "%s: init_steps: %d, steps: %d\n", __func__, init_steps, steps);
-
- if (steps > init_steps + (feed_mm * dev->motor.base_ydpi) / MM_PER_INCH)
- {
- break;
- }
-
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- status = gl841_stop_action(dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop motor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- dev->document = SANE_FALSE;
-
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-
-static SANE_Status
-gl841_load_document (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Bool paper_loaded;
- int loop = 300;
- DBG(DBG_proc, "%s\n", __func__);
- while (loop > 0) /* do not wait longer then 30 seconds */
- {
-
- RIE(gl841_get_paper_sensor(dev, &paper_loaded));
-
- if (paper_loaded)
- {
- DBG(DBG_info, "%s: document inserted\n", __func__);
-
- /* when loading OK, document is here */
- dev->document = SANE_TRUE;
-
- // give user some time to place document correctly
- sanei_genesys_sleep_ms(1000);
- break;
- }
- sanei_genesys_sleep_ms(100);
- --loop;
- }
-
- if (loop == 0)
- {
- /* when we come here then the user needed to much time for this */
- DBG(DBG_error, "%s: timeout while waiting for document\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/**
- * detects end of document and adjust current scan
- * to take it into account
- * used by sheetfed scanners
- */
-static SANE_Status
-gl841_detect_document_end (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Bool paper_loaded;
- unsigned int scancnt = 0, lincnt, postcnt;
- uint8_t val;
- size_t total_bytes_to_read;
-
- DBG(DBG_proc, "%s: begin\n", __func__);
-
- RIE (gl841_get_paper_sensor (dev, &paper_loaded));
-
- /* sheetfed scanner uses home sensor as paper present */
- if ((dev->document == SANE_TRUE) && !paper_loaded)
- {
- DBG(DBG_info, "%s: no more document\n", __func__);
- dev->document = SANE_FALSE;
-
- /* we can't rely on total_bytes_to_read since the frontend
- * might have been slow to read data, so we re-evaluate the
- * amount of data to scan form the hardware settings
- */
- try {
- status = sanei_genesys_read_scancnt(dev, &scancnt);
- } catch (...) {
- dev->total_bytes_to_read = dev->total_bytes_read;
- dev->read_bytes_left = 0;
- throw;
- }
-
- if(status!=SANE_STATUS_GOOD)
- {
- dev->total_bytes_to_read = dev->total_bytes_read;
- dev->read_bytes_left = 0;
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
- }
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS && dev->model->is_cis)
- {
- scancnt/=3;
- }
- DBG(DBG_io, "%s: scancnt=%u lines\n", __func__, scancnt);
-
- RIE(sanei_genesys_read_register(dev, 0x25, &val));
- lincnt=65536*val;
- RIE(sanei_genesys_read_register(dev, 0x26, &val));
- lincnt+=256*val;
- RIE(sanei_genesys_read_register(dev, 0x27, &val));
- lincnt+=val;
- DBG(DBG_io, "%s: lincnt=%u lines\n", __func__, lincnt);
- postcnt=(SANE_UNFIX(dev->model->post_scan)/MM_PER_INCH)*dev->settings.yres;
- DBG(DBG_io, "%s: postcnt=%u lines\n", __func__, postcnt);
-
- /* the current scancnt is also the final one, so we use it to
- * compute total bytes to read. We also add the line count to eject document */
- total_bytes_to_read=(scancnt+postcnt)*dev->wpl;
-
- DBG(DBG_io, "%s: old total_bytes_to_read=%u\n", __func__,
- (unsigned int)dev->total_bytes_to_read);
- DBG(DBG_io, "%s: new total_bytes_to_read=%u\n", __func__, (unsigned int)total_bytes_to_read);
-
- /* assign new end value */
- if(dev->total_bytes_to_read>total_bytes_to_read)
- {
- DBG(DBG_io, "%s: scan shorten\n", __func__);
- dev->total_bytes_to_read=total_bytes_to_read;
- }
- }
-
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/* Send the low-level scan command */
-/* todo : is this that useful ? */
-static SANE_Status
-gl841_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SANE_Bool start_motor)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
- // FIXME: SEQUENTIAL not really needed in this case
- Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
- uint8_t val;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- if (dev->model->gpo_type == GPO_CANONLIDE80)
- {
- RIE (sanei_genesys_read_register (dev, REG6B, &val));
- val = REG6B_GPO18;
- RIE (sanei_genesys_write_register (dev, REG6B, val));
- }
-
- if (dev->model->ccd_type != CCD_PLUSTEK_3600) {
- local_reg.init_reg(0x03, sanei_genesys_read_reg_from_set(reg, 0x03) | REG03_LAMPPWR);
- } else {
- // TODO PLUSTEK_3600: why ??
- local_reg.init_reg(0x03, sanei_genesys_read_reg_from_set(reg, 0x03));
- }
-
- local_reg.init_reg(0x01, sanei_genesys_read_reg_from_set(reg, 0x01) | REG01_SCAN); /* set scan bit */
- local_reg.init_reg(0x0d, 0x01);
-
- if (start_motor) {
- local_reg.init_reg(0x0f, 0x01);
- } else {
- // do not start motor yet
- local_reg.init_reg(0x0f, 0x00);
- }
-
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
-
- return status;
-}
-
-
-/* Send the stop scan command */
-static SANE_Status
-gl841_end_scan (Genesys_Device * dev, Genesys_Register_Set __sane_unused__ * reg,
- SANE_Bool check_stop)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (check_stop = %d)\n", __func__, check_stop);
-
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- status = SANE_STATUS_GOOD;
- }
- else /* flat bed scanners */
- {
- status = gl841_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
-
- return status;
-}
-
-/* Moves the slider to steps */
-static SANE_Status
-gl841_feed (Genesys_Device * dev, int steps)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- int loop;
-
- DBG(DBG_proc, "%s (steps = %d)\n", __func__, steps);
-
- status = gl841_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop action: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- // FIXME: we should pick sensor according to the resolution scanner is currently operating on
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- local_reg = dev->reg;
-
- gl841_init_optical_regs_off(&local_reg);
-
- gl841_init_motor_regs(dev, sensor, &local_reg, steps,MOTOR_ACTION_FEED,0);
-
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- try {
- status = gl841_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl841_stop_action (dev);
- } catch (...) {}
- try {
- // send original registers
- sanei_genesys_bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl841_stop_action (dev);
- /* send original registers */
- sanei_genesys_bulk_write_register(dev, dev->reg);
- return status;
- }
-
- loop = 0;
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (!(val & REG41_MOTORENB)) /* motor enabled */
- {
- DBG(DBG_proc, "%s: finished\n", __func__);
- dev->scanhead_position_in_steps += steps;
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl841_stop_action (dev);
-
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
-}
-
-/* Moves the slider to the home (top) position slowly */
-static SANE_Status
-gl841_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- uint8_t val;
- int loop = 0;
-
- DBG(DBG_proc, "%s (wait_until_home = %d)\n", __func__, wait_until_home);
-
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- DBG(DBG_proc, "%s: there is no \"home\"-concept for sheet fed\n", __func__);
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- /* reset gpio pin */
- if (dev->model->gpo_type == GPO_CANONLIDE35)
- {
- RIE (sanei_genesys_read_register (dev, REG6C, &val));
- val = dev->gpo.value[0];
- RIE (sanei_genesys_write_register (dev, REG6C, val));
- }
- if (dev->model->gpo_type == GPO_CANONLIDE80)
- {
- RIE (sanei_genesys_read_register (dev, REG6B, &val));
- val = REG6B_GPO18 | REG6B_GPO17;
- RIE (sanei_genesys_write_register (dev, REG6B, val));
- }
- gl841_save_power(dev, SANE_FALSE);
-
- /* first read gives HOME_SENSOR true */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- sanei_genesys_sleep_ms(100);
-
- /* second is reliable */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- dev->scanhead_position_in_steps = 0;
-
- if (val & REG41_HOMESNR) /* is sensor at home? */
- {
- DBG(DBG_info, "%s: already at home, completed\n", __func__);
- dev->scanhead_position_in_steps = 0;
- return SANE_STATUS_GOOD;
- }
-
- /* end previous scan if any */
- r = sanei_genesys_get_address(&dev->reg, REG01);
- r->value &= ~REG01_SCAN;
- status = sanei_genesys_write_register (dev, REG01, r->value);
-
- /* if motor is on, stop current action */
- if (val & REG41_MOTORENB)
- {
- status = gl841_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop motor: %s\n", __func__, sane_strstatus(status));
- return SANE_STATUS_IO_ERROR;
- }
- }
-
- local_reg = dev->reg;
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- gl841_init_motor_regs(dev, sensor, &local_reg, 65536,MOTOR_ACTION_GO_HOME,0);
-
- /* set up for reverse and no scan */
- r = sanei_genesys_get_address(&local_reg, REG02);
- r->value |= REG02_MTRREV;
- r = sanei_genesys_get_address(&local_reg, REG01);
- r->value &= ~REG01_SCAN;
-
- RIE (sanei_genesys_bulk_write_register(dev, local_reg));
-
- try {
- status = gl841_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl841_stop_action(dev);
- } catch (...) {}
- try {
- sanei_genesys_bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl841_stop_action (dev);
- /* send original registers */
- sanei_genesys_bulk_write_register(dev, dev->reg);
- return status;
- }
-
- if (wait_until_home)
- {
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- if (val & REG41_HOMESNR) /* home sensor */
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl841_stop_action (dev);
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
- area at 600 dpi from very top of scanner */
-static SANE_Status
-gl841_search_start_position (Genesys_Device * dev)
-{
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- int steps;
-
- int pixels = 600;
- int dpi = 300;
-
- DBGSTART;
-
- local_reg = dev->reg;
-
- /* sets for a 200 lines * 600 pixels */
- /* normal scan with no shading */
-
- // FIXME: the current approach of doing search only for one resolution does not work on scanners
- // whith employ different sensors with potentially different settings.
- auto& sensor = sanei_genesys_find_sensor_for_write(dev, dpi);
-
- SetupParams params;
- params.xres = dpi;
- params.yres = dpi;
- params.startx = 0;
- params.starty = 0; /*we should give a small offset here~60 steps*/
- params.pixels = 600;
- params.lines = dev->model->search_lines;
- params.depth = 8;
- params.channels = 1;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::GREEN;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE;
-
- status = gl841_init_scan_regs(dev, sensor, &local_reg, params);
-
- if(status!=SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to init scan registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* send to scanner */
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- size = pixels * dev->model->search_lines;
-
- std::vector<uint8_t> data(size);
-
- status = gl841_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl841_search_position.pnm", data.data(), 8, 1, pixels,
- dev->model->search_lines);
-
- status = gl841_end_scan(dev, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* update regs to copy ASIC internal state */
- dev->reg = local_reg;
-
-/*TODO: find out where sanei_genesys_search_reference_point
- stores information, and use that correctly*/
- status =
- sanei_genesys_search_reference_point (dev, sensor, data.data(), 0, dpi, pixels,
- dev->model->search_lines);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set search reference point: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/*
- * sets up register for coarse gain calibration
- * todo: check it for scanners using it */
-static SANE_Status
-gl841_init_regs_for_coarse_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t channels;
- uint8_t cksel;
-
- DBGSTART;
-
- cksel = (regs.find_reg(0x18).value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
-
- /* set line size */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else {
- channels = 1;
- }
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = sensor.optical_res / cksel; /* XXX STEF XXX !!! */
- params.lines = 20;
- params.depth = 16;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl841_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
- sensor.optical_res / cksel, dev->settings.xres);
-
- status = sanei_genesys_bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
-
-/* if (DBG_LEVEL >= DBG_info)
- sanei_gl841_print_registers (regs);*/
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* init registers for shading calibration */
-static SANE_Status
-gl841_init_regs_for_shading(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Int ydpi;
- float starty=0;
-
- DBGSTART;
- DBG(DBG_proc, "%s: lines = %d\n", __func__, (int)(dev->calib_lines));
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- ydpi = dev->motor.base_ydpi;
- if (dev->model->motor_type == MOTOR_PLUSTEK_3600) /* TODO PLUSTEK_3600: 1200dpi not yet working, produces dark bar */
- {
- ydpi = 600;
- }
- if (dev->model->motor_type == MOTOR_CANONLIDE80)
- {
- ydpi = gl841_get_dpihw(dev);
- /* get over extra dark area for this model.
- It looks like different devices have dark areas of different width
- due to manufacturing variability. The initial value of starty was 140,
- but it moves the sensor almost past the dark area completely in places
- on certain devices.
-
- On a particular device the black area starts at roughly position
- 160 to 230 depending on location (the dark area is not completely
- parallel to the frame).
- */
- starty = 70;
- }
-
- dev->calib_channels = 3;
- dev->calib_lines = dev->model->shading_lines;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = ydpi;
- params.startx = 0;
- params.starty = starty;
- params.pixels = (sensor.sensor_pixels * dev->settings.xres) / sensor.optical_res;
- params.lines = dev->calib_lines;
- params.depth = 16;
- params.channels = dev->calib_channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_USE_OPTICAL_RES |
- /*SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |*/
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl841_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- dev->calib_pixels = dev->current_setup.pixels;
- dev->scanhead_position_in_steps += dev->calib_lines + starty;
-
- status = sanei_genesys_bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* set up registers for the actual scan
- */
-static SANE_Status
-gl841_init_regs_for_scan (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int flags;
- int depth;
- float move;
- int move_dpi;
- float start;
-
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
-/* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
-/* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
-
- /* steps to move to reach scanning area:
- - first we move to physical start of scanning
- either by a fixed steps amount from the black strip
- or by a fixed amount from parking position,
- minus the steps done during shading calibration
- - then we move by the needed offset whitin physical
- scanning area
-
- assumption: steps are expressed at maximum motor resolution
-
- we need:
- SANE_Fixed y_offset;
- SANE_Fixed y_size;
- SANE_Fixed y_offset_calib;
- mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
-
- /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
- relative from origin, else, it is from parking position */
-
- move_dpi = dev->motor.base_ydpi;
-
- move = 0;
- if (dev->model->flags & GENESYS_FLAG_SEARCH_START)
- {
- move += SANE_UNFIX (dev->model->y_offset_calib);
- }
-
- DBG(DBG_info, "%s move=%f steps\n", __func__, move);
-
- move += SANE_UNFIX (dev->model->y_offset);
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- move += dev->settings.tl_y;
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- move = (move * move_dpi) / MM_PER_INCH;
-
-/* start */
- start = SANE_UNFIX (dev->model->x_offset);
-
- start += dev->settings.tl_x;
-
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- flags=0;
-
- /* we enable true gray for cis scanners only, and just when doing
- * scan since color calibration is OK for this mode
- */
- flags = 0;
-
- /* true gray (led add for cis scanners) */
- if(dev->model->is_cis && dev->settings.true_gray
- && dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS
- && dev->model->ccd_type != CIS_CANONLIDE80)
- {
- // on Lide 80 the LEDADD bit results in only red LED array being lit
- DBG(DBG_io, "%s: activating LEDADD\n", __func__);
- flags |= SCAN_FLAG_ENABLE_LEDADD;
- }
-
- /* enable emulated lineart from gray data */
- if(dev->settings.scan_mode == ScanColorMode::LINEART
- && dev->settings.dynamic_lineart)
- {
- flags |= SCAN_FLAG_DYNAMIC_LINEART;
- }
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start;
- params.starty = move;
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = flags;
-
- status = gl841_init_scan_regs(dev, sensor, &dev->reg, params);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/*
- * this function sends generic gamma table (ie linear ones)
- * or the Sensor specific one if provided
- */
-static SANE_Status
-gl841_send_gamma_table(Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
-
- size = 256;
-
- /* allocate temporary gamma tables: 16 bits words, 3 channels */
- std::vector<uint8_t> gamma(size * 2 * 3);
-
- RIE(sanei_genesys_generate_gamma_buffer(dev, sensor, 16, 65535, size, gamma.data()));
-
- /* send address */
- status = gl841_set_buffer_address_gamma (dev, 0x00000);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* send data */
- status = sanei_genesys_bulk_write_data(dev, 0x28, gamma.data(), size * 2 * 3);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send gamma table: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* this function does the led calibration by scanning one line of the calibration
- area below scanner's top on white strip.
-
--needs working coarse/gain
-*/
-static SANE_Status
-gl841_led_calibration (Genesys_Device * dev, Genesys_Sensor& sensor, Genesys_Register_Set& regs)
-{
- int num_pixels;
- int total_size;
- int i, j;
- SANE_Status status = SANE_STATUS_GOOD;
- int val;
- int channels;
- int avg[3], avga, avge;
- int turn;
- uint16_t exp[3], target;
- int move;
-
- SANE_Bool acceptable = SANE_FALSE;
-
- /* these 2 boundaries should be per sensor */
- uint16_t min_exposure=500;
- uint16_t max_exposure;
-
- DBGSTART;
-
- /* feed to white strip if needed */
- if (dev->model->y_offset_calib>0)
- {
- move = SANE_UNFIX (dev->model->y_offset_calib);
- move = (move * (dev->motor.base_ydpi)) / MM_PER_INCH;
- DBG(DBG_io, "%s: move=%d lines\n", __func__, move);
- status = gl841_feed(dev, move);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to feed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- /* offset calibration is always done in color mode */
- channels = 3;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = (sensor.sensor_pixels*dev->settings.xres) / sensor.optical_res;
- params.lines = 1;
- params.depth = 16;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_USE_OPTICAL_RES;
-
- status = gl841_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- RIE(sanei_genesys_bulk_write_register(dev, regs));
-
- num_pixels = dev->current_setup.pixels;
-
- total_size = num_pixels * channels * 2 * 1; /* colors * bytes_per_color * scan lines */
-
- std::vector<uint8_t> line(total_size);
-
-/*
- we try to get equal bright leds here:
-
- loop:
- average per color
- adjust exposure times
- */
-
- exp[0] = sensor.exposure.red;
- exp[1] = sensor.exposure.green;
- exp[2] = sensor.exposure.blue;
-
- turn = 0;
- /* max exposure is set to ~2 time initial average
- * exposure, or 2 time last calibration exposure */
- max_exposure=((exp[0]+exp[1]+exp[2])/3)*2;
- target=sensor.gain_white_ref*256;
-
- do {
- sensor.exposure.red = exp[0];
- sensor.exposure.green = exp[1];
- sensor.exposure.blue = exp[2];
-
- sanei_genesys_set_exposure(regs, sensor.exposure);
- RIE(sanei_genesys_write_register(dev, 0x10, (sensor.exposure.red >> 8) & 0xff));
- RIE(sanei_genesys_write_register(dev, 0x11, sensor.exposure.red & 0xff));
- RIE(sanei_genesys_write_register(dev, 0x12, (sensor.exposure.green >> 8) & 0xff));
- RIE(sanei_genesys_write_register(dev, 0x13, sensor.exposure.green & 0xff));
- RIE(sanei_genesys_write_register(dev, 0x14, (sensor.exposure.blue >> 8) & 0xff));
- RIE(sanei_genesys_write_register(dev, 0x15, sensor.exposure.blue & 0xff));
-
- RIE(sanei_genesys_bulk_write_register(dev, regs));
-
- DBG(DBG_info, "%s: starting line reading\n", __func__);
- RIE(gl841_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data) {
- char fn[30];
- snprintf(fn, 30, "gl841_led_%d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, line.data(), 16, channels, num_pixels, 1);
- }
-
- /* compute average */
- for (j = 0; j < channels; j++)
- {
- avg[j] = 0;
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- line[i * 2 + j * 2 * num_pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- avg[j] += val;
- }
-
- avg[j] /= num_pixels;
- }
-
- DBG(DBG_info,"%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
-
- acceptable = SANE_TRUE;
-
- /* exposure is acceptable if each color is in the %5 range
- * of other color channels */
- if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
- avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
- avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
- {
- acceptable = SANE_FALSE;
- }
-
- /* led exposure is not acceptable if white level is too low
- * ~80 hardcoded value for white level */
- if(avg[0]<20000 || avg[1]<20000 || avg[2]<20000)
- {
- acceptable = SANE_FALSE;
- }
-
- /* for scanners using target value */
- if(target>0)
- {
- acceptable = SANE_TRUE;
- for(i=0;i<3;i++)
- {
- /* we accept +- 2% delta from target */
- if(abs(avg[i]-target)>target/50)
- {
- exp[i]=(exp[i]*target)/avg[i];
- acceptable = SANE_FALSE;
- }
- }
- }
- else
- {
- if (!acceptable)
- {
- avga = (avg[0]+avg[1]+avg[2])/3;
- exp[0] = (exp[0] * avga) / avg[0];
- exp[1] = (exp[1] * avga) / avg[1];
- exp[2] = (exp[2] * avga) / avg[2];
- /*
- keep the resulting exposures below this value.
- too long exposure drives the ccd into saturation.
- we may fix this by relying on the fact that
- we get a striped scan without shading, by means of
- statistical calculation
- */
- avge = (exp[0] + exp[1] + exp[2]) / 3;
-
- if (avge > max_exposure) {
- exp[0] = (exp[0] * max_exposure) / avge;
- exp[1] = (exp[1] * max_exposure) / avge;
- exp[2] = (exp[2] * max_exposure) / avge;
- }
- if (avge < min_exposure) {
- exp[0] = (exp[0] * min_exposure) / avge;
- exp[1] = (exp[1] * min_exposure) / avge;
- exp[2] = (exp[2] * min_exposure) / avge;
- }
-
- }
- }
-
- RIE (gl841_stop_action (dev));
-
- turn++;
-
- } while (!acceptable && turn < 100);
-
- DBG(DBG_info,"%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
-
- gl841_slow_back_home(dev, SANE_TRUE);
-
- DBGCOMPLETED;
- return status;
-}
-
-/** @brief calibration for AD frontend devices
- * offset calibration assumes that the scanning head is on a black area
- * For LiDE80 analog frontend
- * 0x0003 : is gain and belongs to [0..63]
- * 0x0006 : is offset
- * We scan a line with no gain until average offset reaches the target
- */
-static SANE_Status
-ad_fe_offset_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int num_pixels;
- int total_size;
- int i;
- int average;
- int turn;
- int top;
- int bottom;
- int target;
-
- DBGSTART;
-
- /* don't impact 3600 behavior since we can't test it */
- if (dev->model->ccd_type == CCD_PLUSTEK_3600)
- {
- DBGCOMPLETED;
- return status;
- }
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = (sensor.sensor_pixels*dev->settings.xres) / sensor.optical_res;
- params.lines = 1;
- params.depth = 8;
- params.channels = 3;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_USE_OPTICAL_RES;
-
- status = gl841_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- num_pixels = dev->current_setup.pixels;
- total_size = num_pixels * 3 * 2 * 1;
-
- std::vector<uint8_t> line(total_size);
-
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
-
- /* loop on scan until target offset is reached */
- turn=0;
- target=24;
- bottom=0;
- top=255;
- do {
- /* set up offset mid range */
- dev->frontend.set_offset(0, (top + bottom) / 2);
- dev->frontend.set_offset(1, (top + bottom) / 2);
- dev->frontend.set_offset(2, (top + bottom) / 2);
-
- /* scan line */
- DBG(DBG_info, "%s: starting line reading\n", __func__);
- sanei_genesys_bulk_write_register(dev, regs);
- gl841_set_fe(dev, sensor, AFE_SET);
- gl841_begin_scan(dev, sensor, &regs, SANE_TRUE);
- sanei_genesys_read_data_from_scanner(dev, line.data(), total_size);
- gl841_stop_action (dev);
- if (DBG_LEVEL >= DBG_data) {
- char fn[30];
- snprintf(fn, 30, "gl841_offset_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, line.data(), 8, 3, num_pixels, 1);
- }
-
- /* search for minimal value */
- average=0;
- for(i=0;i<total_size;i++)
- {
- average+=line[i];
- }
- average/=total_size;
- DBG(DBG_data, "%s: average=%d\n", __func__, average);
-
- /* if min value is above target, the current value becomes the new top
- * else it is the new bottom */
- if(average>target)
- {
- top=(top+bottom)/2;
- }
- else
- {
- bottom=(top+bottom)/2;
- }
- turn++;
- } while ((top-bottom)>1 && turn < 100);
-
- // FIXME: don't overwrite the calibrated values
- dev->frontend.set_offset(0, 0);
- dev->frontend.set_offset(1, 0);
- dev->frontend.set_offset(2, 0);
- DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
- DBGCOMPLETED;
- return status;
-}
-
-/* this function does the offset calibration by scanning one line of the calibration
- area below scanner's top. There is a black margin and the remaining is white.
- sanei_genesys_search_start() must have been called so that the offsets and margins
- are allready known.
-
-this function expects the slider to be where?
-*/
-static SANE_Status
-gl841_offset_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- int num_pixels;
- int total_size;
- int i, j;
- SANE_Status status = SANE_STATUS_GOOD;
- int val;
- int channels;
- int off[3],offh[3],offl[3],off1[3],off2[3];
- int min1[3],min2[3];
- int cmin[3],cmax[3];
- int turn;
- SANE_Bool acceptable = SANE_FALSE;
- int mintgt = 0x400;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- /* Analog Device fronted have a different calibration */
- if ((dev->reg.find_reg(0x04).value & REG04_FESET) == 0x02)
- {
- return ad_fe_offset_calibration(dev, sensor, regs);
- }
-
- /* offset calibration is always done in color mode */
- channels = 3;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = (sensor.sensor_pixels*dev->settings.xres) / sensor.optical_res;
- params.lines = 1;
- params.depth = 16;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_USE_OPTICAL_RES |
- SCAN_FLAG_DISABLE_LAMP;
-
- status = gl841_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- num_pixels = dev->current_setup.pixels;
-
- total_size = num_pixels * channels * 2 * 1; /* colors * bytes_per_color * scan lines */
-
- std::vector<uint8_t> first_line(total_size);
- std::vector<uint8_t> second_line(total_size);
-
- /* scan first line of data with no offset nor gain */
-/*WM8199: gain=0.73; offset=-260mV*/
-/*okay. the sensor black level is now at -260mV. we only get 0 from AFE...*/
-/* we should probably do real calibration here:
- * -detect acceptable offset with binary search
- * -calculate offset from this last version
- *
- * acceptable offset means
- * - few completely black pixels(<10%?)
- * - few completely white pixels(<10%?)
- *
- * final offset should map the minimum not completely black
- * pixel to 0(16 bits)
- *
- * this does account for dummy pixels at the end of ccd
- * this assumes slider is at black strip(which is not quite as black as "no
- * signal").
- *
- */
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
- offh[0] = 0xff;
- offh[1] = 0xff;
- offh[2] = 0xff;
- offl[0] = 0x00;
- offl[1] = 0x00;
- offl[2] = 0x00;
- turn = 0;
-
- do {
-
- RIE(sanei_genesys_bulk_write_register(dev, regs));
-
- for (j=0; j < channels; j++) {
- off[j] = (offh[j]+offl[j])/2;
- dev->frontend.set_offset(j, off[j]);
- }
-
- status = gl841_set_fe(dev, sensor, AFE_SET);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_info, "%s: starting first line reading\n", __func__);
- RIE(gl841_begin_scan(dev, sensor, &regs, SANE_TRUE));
-
- RIE(sanei_genesys_read_data_from_scanner (dev, first_line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data) {
- char fn[30];
- snprintf(fn, 30, "gl841_offset1_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, first_line.data(), 16, channels, num_pixels, 1);
- }
-
- acceptable = SANE_TRUE;
-
- for (j = 0; j < channels; j++)
- {
- cmin[j] = 0;
- cmax[j] = 0;
-
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- first_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- first_line[i * 2 + j * 2 * num_pixels];
- else
- val =
- first_line[i * 2 * channels + 2 * j + 1] * 256 +
- first_line[i * 2 * channels + 2 * j];
- if (val < 10)
- cmin[j]++;
- if (val > 65525)
- cmax[j]++;
- }
-
- /* TODO the DP685 has a black strip in the middle of the sensor
- * should be handled in a more elegant way , could be a bug */
- if (dev->model->ccd_type == CCD_DP685)
- cmin[j] -= 20;
-
- if (cmin[j] > num_pixels/100) {
- acceptable = SANE_FALSE;
- if (dev->model->is_cis)
- offl[0] = off[0];
- else
- offl[j] = off[j];
- }
- if (cmax[j] > num_pixels/100) {
- acceptable = SANE_FALSE;
- if (dev->model->is_cis)
- offh[0] = off[0];
- else
- offh[j] = off[j];
- }
- }
-
- DBG(DBG_info,"%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0],
- cmin[1], cmax[1], cmin[2], cmax[2]);
-
- if (dev->model->is_cis) {
- offh[2] = offh[1] = offh[0];
- offl[2] = offl[1] = offl[0];
- }
-
- RIE(gl841_stop_action(dev));
-
- turn++;
- } while (!acceptable && turn < 100);
-
- DBG(DBG_info,"%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]);
-
-
- for (j = 0; j < channels; j++)
- {
- off1[j] = off[j];
-
- min1[j] = 65536;
-
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- first_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- first_line[i * 2 + j * 2 * num_pixels];
- else
- val =
- first_line[i * 2 * channels + 2 * j + 1] * 256 +
- first_line[i * 2 * channels + 2 * j];
- if (min1[j] > val && val >= 10)
- min1[j] = val;
- }
- }
-
-
- offl[0] = off[0];
- offl[1] = off[0];
- offl[2] = off[0];
- turn = 0;
-
- do {
-
- for (j=0; j < channels; j++) {
- off[j] = (offh[j]+offl[j])/2;
- dev->frontend.set_offset(j, off[j]);
- }
-
- status = gl841_set_fe(dev, sensor, AFE_SET);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(sanei_genesys_bulk_write_register(dev, regs));
- RIE(gl841_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, second_line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data) {
- char fn[30];
- snprintf(fn, 30, "gl841_offset2_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, second_line.data(), 16, channels, num_pixels, 1);
- }
-
- acceptable = SANE_TRUE;
-
- for (j = 0; j < channels; j++)
- {
- cmin[j] = 0;
- cmax[j] = 0;
-
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- second_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- second_line[i * 2 + j * 2 * num_pixels];
- else
- val =
- second_line[i * 2 * channels + 2 * j + 1] * 256 +
- second_line[i * 2 * channels + 2 * j];
- if (val < 10)
- cmin[j]++;
- if (val > 65525)
- cmax[j]++;
- }
-
- if (cmin[j] > num_pixels/100) {
- acceptable = SANE_FALSE;
- if (dev->model->is_cis)
- offl[0] = off[0];
- else
- offl[j] = off[j];
- }
- if (cmax[j] > num_pixels/100) {
- acceptable = SANE_FALSE;
- if (dev->model->is_cis)
- offh[0] = off[0];
- else
- offh[j] = off[j];
- }
- }
-
- DBG(DBG_info, "%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0],
- cmin[1], cmax[1], cmin[2], cmax[2]);
-
- if (dev->model->is_cis) {
- offh[2] = offh[1] = offh[0];
- offl[2] = offl[1] = offl[0];
- }
-
- RIE(gl841_stop_action (dev));
-
- turn++;
-
- } while (!acceptable && turn < 100);
-
- DBG(DBG_info, "%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]);
-
-
- for (j = 0; j < channels; j++)
- {
- off2[j] = off[j];
-
- min2[j] = 65536;
-
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- second_line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- second_line[i * 2 + j * 2 * num_pixels];
- else
- val =
- second_line[i * 2 * channels + 2 * j + 1] * 256 +
- second_line[i * 2 * channels + 2 * j];
- if (min2[j] > val && val != 0)
- min2[j] = val;
- }
- }
-
- DBG(DBG_info, "%s: first set: %d/%d,%d/%d,%d/%d\n", __func__, off1[0], min1[0], off1[1], min1[1],
- off1[2], min1[2]);
-
- DBG(DBG_info, "%s: second set: %d/%d,%d/%d,%d/%d\n", __func__, off2[0], min2[0], off2[1], min2[1],
- off2[2], min2[2]);
-
-/*
- calculate offset for each channel
- based on minimal pixel value min1 at offset off1 and minimal pixel value min2
- at offset off2
-
- to get min at off, values are linearly interpolated:
- min=real+off*fact
- min1=real+off1*fact
- min2=real+off2*fact
-
- fact=(min1-min2)/(off1-off2)
- real=min1-off1*(min1-min2)/(off1-off2)
-
- off=(min-min1+off1*(min1-min2)/(off1-off2))/((min1-min2)/(off1-off2))
-
- off=(min*(off1-off2)+min1*off2-off1*min2)/(min1-min2)
-
- */
- for (j = 0; j < channels; j++)
- {
- if (min2[j]-min1[j] == 0) {
-/*TODO: try to avoid this*/
- DBG(DBG_warn, "%s: difference too small\n", __func__);
- if (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j] >= 0)
- off[j] = 0x0000;
- else
- off[j] = 0xffff;
- } else
- off[j] = (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j])/(min1[j]-min2[j]);
- if (off[j] > 255)
- off[j] = 255;
- if (off[j] < 0)
- off[j] = 0;
- dev->frontend.set_offset(j, off[j]);
- }
-
- DBG(DBG_info, "%s: final offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]);
-
- if (dev->model->is_cis) {
- if (off[0] < off[1])
- off[0] = off[1];
- if (off[0] < off[2])
- off[0] = off[2];
- dev->frontend.set_offset(0, off[0]);
- dev->frontend.set_offset(1, off[0]);
- dev->frontend.set_offset(2, off[0]);
- }
-
- if (channels == 1)
- {
- dev->frontend.set_offset(1, dev->frontend.get_offset(0));
- dev->frontend.set_offset(2, dev->frontend.get_offset(0));
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
-}
-
-
-/* alternative coarse gain calibration
- this on uses the settings from offset_calibration and
- uses only one scanline
- */
-/*
- with offset and coarse calibration we only want to get our input range into
- a reasonable shape. the fine calibration of the upper and lower bounds will
- be done with shading.
- */
-static SANE_Status
-gl841_coarse_gain_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi)
-{
- int num_pixels;
- int total_size;
- int i, j, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- int max[3];
- float gain[3];
- int val;
- int lines=1;
- int move;
-
- DBG(DBG_proc, "%s: dpi=%d\n", __func__, dpi);
-
- /* feed to white strip if needed */
- if (dev->model->y_offset_calib>0)
- {
- move = SANE_UNFIX (dev->model->y_offset_calib);
- move = (move * (dev->motor.base_ydpi)) / MM_PER_INCH;
- DBG(DBG_io, "%s: move=%d lines\n", __func__, move);
- status = gl841_feed(dev, move);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to feed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- /* coarse gain calibration is allways done in color mode */
- channels = 3;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = (sensor.sensor_pixels*dev->settings.xres) / sensor.optical_res;
- params.lines = lines;
- params.depth = 16;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_USE_OPTICAL_RES;
-
- status = gl841_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- RIE(sanei_genesys_bulk_write_register(dev, regs));
-
- num_pixels = dev->current_setup.pixels;
-
- total_size = num_pixels * channels * 2 * lines; /* colors * bytes_per_color * scan lines */
-
- std::vector<uint8_t> line(total_size);
-
- RIE(gl841_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl841_gain.pnm", line.data(), 16, channels, num_pixels, lines);
-
- /* average high level for each channel and compute gain
- to reach the target code
- we only use the central half of the CCD data */
- for (j = 0; j < channels; j++)
- {
- max[j] = 0;
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- line[i * 2 + j * 2 * num_pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
-
- if (val > max[j])
- max[j] = val;
- }
-
- gain[j] = 65535.0/max[j];
-
- uint8_t out_gain = 0;
-
- if (dev->model->dac_type == DAC_CANONLIDE35 ||
- dev->model->dac_type == DAC_WOLFSON_XP300 ||
- dev->model->dac_type == DAC_WOLFSON_DSM600)
- {
- gain[j] *= 0.69;/*seems we don't get the real maximum. empirically derived*/
- if (283 - 208/gain[j] > 255)
- out_gain = 255;
- else if (283 - 208/gain[j] < 0)
- out_gain = 0;
- else
- out_gain = 283 - 208/gain[j];
- }
- else if (dev->model->dac_type == DAC_CANONLIDE80)
- {
- out_gain = gain[j]*12;
- }
- dev->frontend.set_gain(j, out_gain);
-
- DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j],
- out_gain);
- }
-
- for (j = 0; j < channels; j++)
- {
- if(gain[j] > 10)
- {
- DBG (DBG_error0, "**********************************************\n");
- DBG (DBG_error0, "**********************************************\n");
- DBG (DBG_error0, "**** ****\n");
- DBG (DBG_error0, "**** Extremely low Brightness detected. ****\n");
- DBG (DBG_error0, "**** Check the scanning head is ****\n");
- DBG (DBG_error0, "**** unlocked and moving. ****\n");
- DBG (DBG_error0, "**** ****\n");
- DBG (DBG_error0, "**********************************************\n");
- DBG (DBG_error0, "**********************************************\n");
- return SANE_STATUS_JAMMED;
- }
-
- }
-
- if (dev->model->is_cis) {
- uint8_t gain0 = dev->frontend.get_gain(0);
- if (gain0 > dev->frontend.get_gain(1)) {
- gain0 = dev->frontend.get_gain(1);
- }
- if (gain0 > dev->frontend.get_gain(2)) {
- gain0 = dev->frontend.get_gain(2);
- }
- dev->frontend.set_gain(0, gain0);
- dev->frontend.set_gain(1, gain0);
- dev->frontend.set_gain(2, gain0);
- }
-
- if (channels == 1) {
- dev->frontend.set_gain(0, dev->frontend.get_gain(1));
- dev->frontend.set_gain(2, dev->frontend.get_gain(1));
- }
-
- DBG(DBG_info, "%s: gain=(%d,%d,%d)\n", __func__,
- dev->frontend.get_gain(0),
- dev->frontend.get_gain(1),
- dev->frontend.get_gain(2));
-
- RIE (gl841_stop_action (dev));
-
- gl841_slow_back_home(dev, SANE_TRUE);
-
- DBGCOMPLETED;
- return status;
-}
-
-/*
- * wait for lamp warmup by scanning the same line until difference
- * between 2 scans is below a threshold
- */
-static SANE_Status
-gl841_init_regs_for_warmup (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * local_reg,
- int *channels, int *total_size)
-{
- int num_pixels = (int) (4 * 300);
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- *local_reg = dev->reg;
-
-/* okay.. these should be defaults stored somewhere */
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
- dev->frontend.set_offset(0, 0x80);
- dev->frontend.set_offset(1, 0x80);
- dev->frontend.set_offset(2, 0x80);
-
- SetupParams params;
- params.xres = sensor.optical_res;
- params.yres = dev->settings.yres;
- params.startx = sensor.dummy_pixel;
- params.starty = 0;
- params.pixels = num_pixels;
- params.lines = 1;
- params.depth = 16;
- params.channels = *channels;
- params.scan_method = dev->settings.scan_method;
- if (*channels == 3) {
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- } else {
- params.scan_mode = ScanColorMode::GRAY;
- }
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_USE_OPTICAL_RES;
-
- status = gl841_init_scan_regs(dev, sensor, local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- num_pixels = dev->current_setup.pixels;
-
- *total_size = num_pixels * 3 * 2 * 1; /* colors * bytes_per_color * scan lines */
-
- RIE(sanei_genesys_bulk_write_register(dev, *local_reg));
-
- return status;
-}
-
-
-/*
- * this function moves head without scanning, forward, then backward
- * so that the head goes to park position.
- * as a by-product, also check for lock
- */
-static SANE_Status
-sanei_gl841_repark_head (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- status = gl841_feed(dev,232);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to feed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* toggle motor flag, put an huge step number and redo move backward */
- status = gl841_slow_back_home (dev, SANE_TRUE);
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
-}
-
-static bool
-gl841_is_compatible_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Calibration_Cache *cache,
- int for_overwrite)
-{
-#ifdef HAVE_SYS_TIME_H
- struct timeval time;
-#endif
-
- DBGSTART;
-
- /* calibration cache not working yet for this model */
- if (dev->model->ccd_type == CCD_PLUSTEK_3600)
- {
- return false;
- }
-
- gl841_calculate_current_setup (dev, sensor);
-
- DBG(DBG_proc, "%s: checking\n", __func__);
-
- if (dev->current_setup.ccd_size_divisor != cache->used_setup.ccd_size_divisor)
- return false;
-
- /* a cache entry expires after 30 minutes for non sheetfed scanners */
- /* this is not taken into account when overwriting cache entries */
-#ifdef HAVE_SYS_TIME_H
- if(for_overwrite == SANE_FALSE)
- {
- gettimeofday (&time, NULL);
- if ((time.tv_sec - cache->last_calibration > 30 * 60)
- && (dev->model->is_sheetfed == SANE_FALSE))
- {
- DBG(DBG_proc, "%s: expired entry, non compatible cache\n", __func__);
- return false;
- }
- }
-#endif
-
- DBGCOMPLETED;
- return true;
-}
-
-/*
- * initialize ASIC : registers, motor tables, and gamma tables
- * then ensure scanner's head is at home
- */
-static SANE_Status
-gl841_init (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- size_t size;
-
- DBG_INIT ();
- DBGSTART;
-
- dev->scanhead_position_in_steps = 0;
-
- /* Check if the device has already been initialized and powered up */
- if (dev->already_initialized)
- {
- RIE (sanei_genesys_get_status (dev, &val));
- if (val & REG41_PWRBIT)
- {
- DBG(DBG_info, "%s: already initialized\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
- }
-
- dev->dark_average_data.clear();
- dev->white_average_data.clear();
-
- dev->settings.color_filter = ColorFilter::RED;
-
- /* ASIC reset */
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
-
- /* Set default values for registers */
- gl841_init_registers (dev);
-
- /* Write initial registers */
- RIE(sanei_genesys_bulk_write_register(dev, dev->reg));
-
- /* Test ASIC and RAM */
- if (!(dev->model->flags & GENESYS_FLAG_LAZY_INIT))
- {
- RIE (sanei_gl841_asic_test (dev));
- }
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- /* Set analog frontend */
- RIE (gl841_set_fe(dev, sensor, AFE_INIT));
-
- /* Move home */
- RIE (gl841_slow_back_home (dev, SANE_TRUE));
-
- /* Init shading data */
- RIE (sanei_genesys_init_shading_data(dev, sensor, sensor.sensor_pixels));
-
- /* ensure head is correctly parked, and check lock */
- if (dev->model->flags & GENESYS_FLAG_REPARK)
- {
- status = sanei_gl841_repark_head (dev);
- if (status != SANE_STATUS_GOOD)
- {
- if (status == SANE_STATUS_INVAL)
- DBG(DBG_error0, "Your scanner is locked. Please move the lock switch to the unlocked "
- "position\n");
- else
- DBG(DBG_error, "%s: sanei_gl841_repark_head failed: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- }
-
- /* send gamma tables */
- status = gl841_send_gamma_table(dev, sensor);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send initial gamma tables: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* initial calibration reg values */
- Genesys_Register_Set& regs = dev->calib_reg;
- regs = dev->reg;
-
- SetupParams params;
- params.xres = 300;
- params.yres = 300;
- params.startx = 0;
- params.starty = 0;
- params.pixels = (16 * 300) / sensor.optical_res;
- params.lines = 1;
- params.depth = 16;
- params.channels = 3;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_USE_OPTICAL_RES;
-
- status = gl841_init_scan_regs(dev, sensor, &regs, params);
-
- RIE(sanei_genesys_bulk_write_register(dev, regs));
-
- size = dev->current_setup.pixels * 3 * 2 * 1; /* colors * bytes_per_color * scan lines */
-
- std::vector<uint8_t> line(size);
-
- DBG(DBG_info, "%s: starting dummy data reading\n", __func__);
- RIE(gl841_begin_scan(dev, sensor, &regs, SANE_TRUE));
-
- sanei_usb_set_timeout(1000);/* 1 second*/
-
-/*ignore errors. next read will succeed*/
- sanei_genesys_read_data_from_scanner(dev, line.data(), size);
-
- sanei_usb_set_timeout(30 * 1000);/* 30 seconds*/
-
- RIE (gl841_end_scan(dev, &regs, SANE_TRUE));
-
- regs = dev->reg;
-
- /* Set powersaving (default = 15 minutes) */
- RIE (gl841_set_powersaving (dev, 15));
- dev->already_initialized = SANE_TRUE;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl841_update_hardware_sensors (Genesys_Scanner * s)
-{
- /* do what is needed to get a new set of events, but try to not lose
- any of them.
- */
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- if (s->dev->model->gpo_type == GPO_CANONLIDE35
- || s->dev->model->gpo_type == GPO_CANONLIDE80)
- {
- RIE(sanei_genesys_read_register(s->dev, REG6D, &val));
- s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0);
- s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0);
- s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0);
- s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0);
- }
-
- if (s->dev->model->gpo_type == GPO_XP300 ||
- s->dev->model->gpo_type == GPO_DP665 ||
- s->dev->model->gpo_type == GPO_DP685)
- {
- RIE(sanei_genesys_read_register(s->dev, REG6D, &val));
-
- s->buttons[BUTTON_PAGE_LOADED_SW].write((val & 0x01) == 0);
- s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0);
- }
-
- return status;
-}
-
-/** @brief search for a full width black or white strip.
- * This function searches for a black or white stripe across the scanning area.
- * When searching backward, the searched area must completely be of the desired
- * color since this area will be used for calibration which scans forward.
- * @param dev scanner device
- * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
- * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
- * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
- */
-static SANE_Status
-gl841_search_strip(Genesys_Device * dev, const Genesys_Sensor& sensor,
- SANE_Bool forward, SANE_Bool black)
-{
- unsigned int pixels, lines, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- size_t size;
- int steps, depth, dpi;
- unsigned int pass, count, found, x, y, length;
- char title[80];
- GenesysRegister *r;
- uint8_t white_level=90; /**< default white level to detect white dots */
- uint8_t black_level=60; /**< default black level to detect black dots */
-
- DBG(DBG_proc, "%s %s %s\n", __func__, black ? "black" : "white", forward ? "forward" : "reverse");
-
- /* use maximum gain when doing forward white strip detection
- * since we don't have calibrated the sensor yet */
- if(!black && forward)
- {
- dev->frontend.set_gain(0, 0xff);
- dev->frontend.set_gain(1, 0xff);
- dev->frontend.set_gain(2, 0xff);
- }
-
- gl841_set_fe(dev, sensor, AFE_SET);
- status = gl841_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* set up for a gray scan at lowest dpi */
- dpi = 9600;
- for (x = 0; x < MAX_RESOLUTIONS; x++)
- {
- if (dev->model->xdpi_values[x] > 0 && dev->model->xdpi_values[x] < dpi)
- dpi = dev->model->xdpi_values[x];
- }
- channels = 1;
-
- /* shading calibation is done with dev->motor.base_ydpi */
- /* lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; */
- lines = (10*dpi)/MM_PER_INCH;
-
- depth = 8;
- pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res;
- size = pixels * channels * lines * (depth / 8);
- std::vector<uint8_t> data(size);
-
- /* 20 cm max length for calibration sheet */
- length = ((200 * dpi) / MM_PER_INCH)/lines;
-
- dev->scanhead_position_in_steps = 0;
-
- local_reg = dev->reg;
-
- SetupParams params;
- params.xres = dpi;
- params.yres = dpi;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA;
-
- status = gl841_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup for scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* set up for reverse or forward */
- r = sanei_genesys_get_address(&local_reg, 0x02);
- if (forward)
- r->value &= ~4;
- else
- r->value |= 4;
-
-
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl841_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl841_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl841_stop_action failed\n", __func__);
- return status;
- }
-
- pass = 0;
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf(title, "gl841_search_strip_%s_%s%02u.pnm", black ? "black" : "white",
- forward ? "fwd" : "bwd", pass);
- sanei_genesys_write_pnm_file(title, data.data(), depth, channels, pixels, lines);
- }
-
- /* loop until strip is found or maximum pass number done */
- found = 0;
- while (pass < length && !found)
- {
- status = sanei_genesys_bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* now start scan */
- status = gl841_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error,"%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner (dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "g%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl841_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl841_stop_action failed\n", __func__);
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf(title, "gl841_search_strip_%s_%s%02u.pnm",
- black ? "black" : "white", forward ? "fwd" : "bwd", pass);
- sanei_genesys_write_pnm_file(title, data.data(), depth, channels, pixels, lines);
- }
-
- /* search data to find black strip */
- /* when searching forward, we only need one line of the searched color since we
- * will scan forward. But when doing backward search, we need all the area of the
- * same color */
- if (forward)
- {
- for (y = 0; y < lines && !found; y++)
- {
- count = 0;
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > white_level)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < black_level)
- {
- count++;
- }
- }
-
- /* at end of line, if count >= 3%, line is not fully of the desired color
- * so we must go to next line of the buffer */
- /* count*100/pixels < 3 */
- if ((count * 100) / pixels < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
- pass, y);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- }
- else /* since calibration scans are done forward, we need the whole area
- to be of the required color when searching backward */
- {
- count = 0;
- for (y = 0; y < lines; y++)
- {
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > white_level)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < black_level)
- {
- count++;
- }
- }
- }
-
- /* at end of area, if count >= 3%, area is not fully of the desired color
- * so we must go to next buffer */
- if ((count * 100) / (pixels * lines) < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- pass++;
- }
-
- if (found)
- {
- status = SANE_STATUS_GOOD;
- DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
- }
- else
- {
- status = SANE_STATUS_UNSUPPORTED;
- DBG(DBG_info, "%s: %s strip not found\n", __func__, black ? "black" : "white");
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
-}
-
-/**
- * Send shading calibration data. The buffer is considered to always hold values
- * for all the channels.
- */
-static
-SANE_Status
-gl841_send_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data, int size)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint32_t length, x, factor, pixels, i;
- uint32_t lines, channels;
- uint16_t dpiset, dpihw, strpixel ,endpixel, beginpixel;
- uint8_t *ptr,*src;
-
- DBGSTART;
- DBG(DBG_io2, "%s: writing %d bytes of shading data\n", __func__, size);
-
- /* old method if no SHDAREA */
- if((dev->reg.find_reg(0x01).value & REG01_SHDAREA) == 0)
- {
- /* start address */
- status = sanei_genesys_set_buffer_address (dev, 0x0000);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* shading data whole line */
- status = dev->model->cmd_set->bulk_write_data (dev, 0x3c, data, size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading table: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- DBGCOMPLETED;
- return status;
- }
-
- /* data is whole line, we extract only the part for the scanned area */
- length = (uint32_t) (size / 3);
- sanei_genesys_get_double(&dev->reg,REG_STRPIXEL,&strpixel);
- sanei_genesys_get_double(&dev->reg,REG_ENDPIXEL,&endpixel);
- DBG(DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d, PIXELS=%d\n", __func__, strpixel, endpixel,
- endpixel-strpixel);
-
- /* compute deletion/average factor */
- sanei_genesys_get_double(&dev->reg,REG_DPISET,&dpiset);
- dpihw = gl841_get_dpihw(dev);
- unsigned ccd_size_divisor = dev->current_setup.ccd_size_divisor;
- factor=dpihw/dpiset;
- DBG(DBG_io2, "%s: dpihw=%d, dpiset=%d, ccd_size_divisor=%d, factor=%d\n", __func__, dpihw, dpiset,
- ccd_size_divisor, factor);
-
- /* binary data logging */
- if(DBG_LEVEL>=DBG_data)
- {
- dev->binary=fopen("binary.pnm","wb");
- sanei_genesys_get_triple(&dev->reg, REG_LINCNT, &lines);
- channels=dev->current_setup.channels;
- if(dev->binary!=NULL)
- {
- fprintf(dev->binary,"P5\n%d %d\n%d\n",(endpixel-strpixel)/factor*channels,lines/channels,255);
- }
- }
-
- /* turn pixel value into bytes 2x16 bits words */
- strpixel*=2*2; /* 2 words of 2 bytes */
- endpixel*=2*2;
- pixels=endpixel-strpixel;
-
- /* shading pixel begin is start pixel minus start pixel during shading
- * calibration. Currently only cases handled are full and half ccd resolution.
- */
- beginpixel = sensor.CCD_start_xoffset / ccd_size_divisor;
- beginpixel += sensor.dummy_pixel + 1;
- DBG(DBG_io2, "%s: ORIGIN PIXEL=%d\n", __func__, beginpixel);
- beginpixel = (strpixel-beginpixel*2*2)/factor;
- DBG(DBG_io2, "%s: BEGIN PIXEL=%d\n", __func__, beginpixel/4);
-
- DBG(DBG_io2, "%s: using chunks of %d bytes (%d shading data pixels)\n", __func__, length,
- length/4);
- std::vector<uint8_t> buffer(pixels, 0);
-
- /* write actual shading data contigously
- * channel by channel, starting at addr 0x0000
- * */
- for(i=0;i<3;i++)
- {
- /* copy data to work buffer and process it */
- /* coefficent destination */
- ptr=buffer.data();
-
- /* iterate on both sensor segment, data has been averaged,
- * so is in the right order and we only have to copy it */
- for(x=0;x<pixels;x+=4)
- {
- /* coefficient source */
- src=data+x+beginpixel+i*length;
- ptr[0]=src[0];
- ptr[1]=src[1];
- ptr[2]=src[2];
- ptr[3]=src[3];
-
- /* next shading coefficient */
- ptr+=4;
- }
-
- /* 0x5400 alignment for LIDE80 internal memory */
- RIE(sanei_genesys_set_buffer_address(dev, 0x5400*i));
- RIE(dev->model->cmd_set->bulk_write_data(dev, 0x3c, buffer.data(), pixels));
- }
-
- DBGCOMPLETED;
-
- return status;
-}
-
-
-/** the gl841 command set */
-static Genesys_Command_Set gl841_cmd_set = {
- "gl841-generic", /* the name of this set */
-
- [](Genesys_Device* dev) -> bool { (void) dev; return true; },
-
- gl841_init,
- gl841_init_regs_for_warmup,
- gl841_init_regs_for_coarse_calibration,
- gl841_init_regs_for_shading,
- gl841_init_regs_for_scan,
-
- gl841_get_filter_bit,
- gl841_get_lineart_bit,
- gl841_get_bitset_bit,
- gl841_get_gain4_bit,
- gl841_get_fast_feed_bit,
- gl841_test_buffer_empty_bit,
- gl841_test_motor_flag_bit,
-
- gl841_set_fe,
- gl841_set_powersaving,
- gl841_save_power,
-
- gl841_begin_scan,
- gl841_end_scan,
-
- gl841_send_gamma_table,
-
- gl841_search_start_position,
-
- gl841_offset_calibration,
- gl841_coarse_gain_calibration,
- gl841_led_calibration,
-
- NULL,
- gl841_slow_back_home,
- NULL,
-
- sanei_genesys_bulk_write_register,
- sanei_genesys_bulk_write_data,
- sanei_genesys_bulk_read_data,
-
- gl841_update_hardware_sensors,
-
- gl841_load_document,
- gl841_detect_document_end,
- gl841_eject_document,
- gl841_search_strip,
-
- gl841_is_compatible_calibration,
- NULL,
- gl841_send_shading_data,
- gl841_calculate_current_setup,
- NULL
-};
-
-SANE_Status
-sanei_gl841_init_cmd_set (Genesys_Device * dev)
-{
- dev->model->cmd_set = &gl841_cmd_set;
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_gl841.h b/backend/genesys_gl841.h
deleted file mode 100644
index 3dbfc80..0000000
--- a/backend/genesys_gl841.h
+++ /dev/null
@@ -1,265 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2011-2013 Stéphane Voltz <stef.dev@free.fr>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#include "genesys.h"
-
-/* Individual bits */
-#define REG01 0x01
-#define REG01_CISSET 0x80
-#define REG01_DOGENB 0x40
-#define REG01_DVDSET 0x20
-#define REG01_M16DRAM 0x08
-#define REG01_DRAMSEL 0x04
-#define REG01_SHDAREA 0x02
-#define REG01_SCAN 0x01
-
-#define REG02 0x02
-#define REG02_NOTHOME 0x80
-#define REG02_ACDCDIS 0x40
-#define REG02_AGOHOME 0x20
-#define REG02_MTRPWR 0x10
-#define REG02_FASTFED 0x08
-#define REG02_MTRREV 0x04
-#define REG02_HOMENEG 0x02
-#define REG02_LONGCURV 0x01
-
-#define REG03_LAMPDOG 0x80
-#define REG03_AVEENB 0x40
-#define REG03_XPASEL 0x20
-#define REG03_LAMPPWR 0x10
-#define REG03_LAMPTIM 0x0f
-
-#define REG04_LINEART 0x80
-#define REG04_BITSET 0x40
-#define REG04_AFEMOD 0x30
-#define REG04_FILTER 0x0c
-#define REG04_FESET 0x03
-
-#define REG04S_AFEMOD 4
-
-#define REG05_DPIHW 0xc0
-#define REG05_DPIHW_600 0x00
-#define REG05_DPIHW_1200 0x40
-#define REG05_DPIHW_2400 0x80
-#define REG05_MTLLAMP 0x30
-#define REG05_GMMENB 0x08
-#define REG05_MTLBASE 0x03
-
-#define REG06_SCANMOD 0xe0
-#define REG06S_SCANMOD 5
-#define REG06_PWRBIT 0x10
-#define REG06_GAIN4 0x08
-#define REG06_OPTEST 0x07
-
-#define REG07_SRAMSEL 0x08
-#define REG07_FASTDMA 0x04
-#define REG07_DMASEL 0x02
-#define REG07_DMARDWR 0x01
-
-#define REG08_DECFLAG 0x40
-#define REG08_GMMFFR 0x20
-#define REG08_GMMFFG 0x10
-#define REG08_GMMFFB 0x08
-#define REG08_GMMZR 0x04
-#define REG08_GMMZG 0x02
-#define REG08_GMMZB 0x01
-
-#define REG09_MCNTSET 0xc0
-#define REG09_CLKSET 0x30
-#define REG09_BACKSCAN 0x08
-#define REG09_ENHANCE 0x04
-#define REG09_SHORTTG 0x02
-#define REG09_NWAIT 0x01
-
-#define REG09S_MCNTSET 6
-#define REG09S_CLKSET 4
-
-
-#define REG0A_SRAMBUF 0x01
-
-#define REG0D 0x0d
-#define REG0D_CLRLNCNT 0x01
-
-#define REG16_CTRLHI 0x80
-#define REG16_TOSHIBA 0x40
-#define REG16_TGINV 0x20
-#define REG16_CK1INV 0x10
-#define REG16_CK2INV 0x08
-#define REG16_CTRLINV 0x04
-#define REG16_CKDIS 0x02
-#define REG16_CTRLDIS 0x01
-
-#define REG17_TGMODE 0xc0
-#define REG17_TGMODE_NO_DUMMY 0x00
-#define REG17_TGMODE_REF 0x40
-#define REG17_TGMODE_XPA 0x80
-#define REG17_TGW 0x3f
-#define REG17S_TGW 0
-
-#define REG18_CNSET 0x80
-#define REG18_DCKSEL 0x60
-#define REG18_CKTOGGLE 0x10
-#define REG18_CKDELAY 0x0c
-#define REG18_CKSEL 0x03
-
-#define REG1A_MANUAL3 0x02
-#define REG1A_MANUAL1 0x01
-#define REG1A_CK4INV 0x08
-#define REG1A_CK3INV 0x04
-#define REG1A_LINECLP 0x02
-
-#define REG1C_TGTIME 0x07
-
-#define REG1D_CK4LOW 0x80
-#define REG1D_CK3LOW 0x40
-#define REG1D_CK1LOW 0x20
-#define REG1D_TGSHLD 0x1f
-#define REG1DS_TGSHLD 0
-
-
-#define REG1E 0x1e
-#define REG1E_WDTIME 0xf0
-#define REG1ES_WDTIME 4
-#define REG1E_LINESEL 0x0f
-#define REG1ES_LINESEL 0
-
-#define REG_EXPR 0x10
-#define REG_EXPG 0x12
-#define REG_EXPB 0x14
-#define REG_STEPNO 0x21
-#define REG_FWDSTEP 0x22
-#define REG_BWDSTEP 0x23
-#define REG_FASTNO 0x24
-#define REG_LINCNT 0x25
-#define REG_DPISET 0x2c
-#define REG_STRPIXEL 0x30
-#define REG_ENDPIXEL 0x32
-#define REG_LPERIOD 0x38
-
-#define REG40_HISPDFLG 0x04
-#define REG40_MOTMFLG 0x02
-#define REG40_DATAENB 0x01
-
-#define REG41_PWRBIT 0x80
-#define REG41_BUFEMPTY 0x40
-#define REG41_FEEDFSH 0x20
-#define REG41_SCANFSH 0x10
-#define REG41_HOMESNR 0x08
-#define REG41_LAMPSTS 0x04
-#define REG41_FEBUSY 0x02
-#define REG41_MOTORENB 0x01
-
-#define REG58_VSMP 0xf8
-#define REG58S_VSMP 3
-#define REG58_VSMPW 0x07
-#define REG58S_VSMPW 0
-
-#define REG59_BSMP 0xf8
-#define REG59S_BSMP 3
-#define REG59_BSMPW 0x07
-#define REG59S_BSMPW 0
-
-#define REG5A_ADCLKINV 0x80
-#define REG5A_RLCSEL 0x40
-#define REG5A_CDSREF 0x30
-#define REG5AS_CDSREF 4
-#define REG5A_RLC 0x0f
-#define REG5AS_RLC 0
-
-#define REG5E_DECSEL 0xe0
-#define REG5ES_DECSEL 5
-#define REG5E_STOPTIM 0x1f
-#define REG5ES_STOPTIM 0
-
-#define REG60_ZIMOD 0x1f
-#define REG61_Z1MOD 0xff
-#define REG62_Z1MOD 0xff
-
-#define REG63_Z2MOD 0x1f
-#define REG64_Z2MOD 0xff
-#define REG65_Z2MOD 0xff
-
-#define REG67_STEPSEL 0xc0
-#define REG67_FULLSTEP 0x00
-#define REG67_HALFSTEP 0x40
-#define REG67_QUATERSTEP 0x80
-#define REG67_MTRPWM 0x3f
-
-#define REG68_FSTPSEL 0xc0
-#define REG68_FULLSTEP 0x00
-#define REG68_HALFSTEP 0x40
-#define REG68_QUATERSTEP 0x80
-#define REG68_FASTPWM 0x3f
-
-#define REG6B_MULTFILM 0x80
-#define REG6B_GPOM13 0x40
-#define REG6B_GPOM12 0x20
-#define REG6B_GPOM11 0x10
-#define REG6B_GPO18 0x02
-#define REG6B_GPO17 0x01
-
-#define REG6B 0x6b
-
-#define REG6C 0x6c
-#define REG6C_GPIOH 0xff
-#define REG6C_GPIOL 0xff
-
-#define REG6D 0x6d
-#define REG6E 0x6e
-#define REG6F 0x6f
-
-#define REG87_LEDADD 0x04
-
-#define INITREG(adr,val) {dev->reg.init_reg(adr, val); }
-
-/**
- * prototypes declaration in case of unit testing
- */
-
-static
-int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor,
- float slope_dpi,
- int scan_step_type,
- int start,
- int used_pixels,
- int *scan_power_mode);
diff --git a/backend/genesys_gl843.cc b/backend/genesys_gl843.cc
deleted file mode 100644
index a72dc5a..0000000
--- a/backend/genesys_gl843.cc
+++ /dev/null
@@ -1,4415 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
-
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#define DEBUG_DECLARE_ONLY
-
-#include "genesys_gl843.h"
-
-#include <string>
-#include <vector>
-
-/****************************************************************************
- Low level function
- ****************************************************************************/
-
-/* ------------------------------------------------------------------------ */
-/* Read and write RAM, registers and AFE */
-/* ------------------------------------------------------------------------ */
-
-/* Set address for writing data */
-static SANE_Status
-gl843_set_buffer_address (Genesys_Device * dev, uint32_t addr)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_io, "%s: setting address to 0x%05x\n", __func__, addr & 0xffff);
-
- status = sanei_genesys_write_register (dev, 0x5b, ((addr >> 8) & 0xff));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while writing high byte: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = sanei_genesys_write_register (dev, 0x5c, (addr & 0xff));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while writing low byte: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_io, "%s: completed\n", __func__);
-
- return status;
-}
-
-/**
- * writes a block of data to RAM
- * @param dev USB device
- * @param addr RAM address to write to
- * @param size size of the chunk of data
- * @param data pointer to the data to write
- */
-static SANE_Status
-write_data (Genesys_Device * dev, uint32_t addr, uint32_t size,
- uint8_t * data)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
-
- status = gl843_set_buffer_address (dev, addr);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while setting address for bulk write data: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* write actual data */
- status = sanei_genesys_bulk_write_data(dev, 0x28, data, size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while writing bulk write data: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* set back address to 0 */
- status = gl843_set_buffer_address (dev, 0);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed setting to default RAM address: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- DBGCOMPLETED;
- return status;
-}
-
-/****************************************************************************
- Mid level functions
- ****************************************************************************/
-
-static SANE_Bool
-gl843_get_fast_feed_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG02);
- if (r && (r->value & REG02_FASTFED))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl843_get_filter_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_FILTER))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl843_get_lineart_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_LINEART))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl843_get_bitset_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_BITSET))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl843_get_gain4_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG06);
- if (r && (r->value & REG06_GAIN4))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-/**
- * compute the step multiplier used
- */
-static int
-gl843_get_step_multiplier (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
- int value = 1;
-
- r = sanei_genesys_get_address (regs, REG9D);
- if (r != NULL)
- {
- switch (r->value & 0x0c)
- {
- case 0x04:
- value = 2;
- break;
- case 0x08:
- value = 4;
- break;
- default:
- value = 1;
- }
- }
- DBG(DBG_io, "%s: step multiplier is %d\n", __func__, value);
- return value;
-}
-
-static SANE_Bool
-gl843_test_buffer_empty_bit (SANE_Byte val)
-{
- if (val & REG41_BUFEMPTY)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl843_test_motor_flag_bit (SANE_Byte val)
-{
- if (val & REG41_MOTORENB)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-/** copy sensor specific settings */
-static void
-gl843_setup_sensor (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs, int dpi,int flags)
-{
- (void) dpi;
- (void) flags;
-
- DBGSTART;
-
- for (const auto& custom_reg : sensor.custom_regs) {
- regs->set8(custom_reg.address, custom_reg.value);
- }
- if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE)) {
- regs->set8(0x7d, 0x90);
- }
-
- DBGCOMPLETED;
-}
-
-
-/** @brief set all registers to default values .
- * This function is called only once at the beginning and
- * fills register startup values for registers reused across scans.
- * Those that are rarely modified or not modified are written
- * individually.
- * @param dev device structure holding register set to initialize
- */
-static void
-gl843_init_registers (Genesys_Device * dev)
-{
- // Within this function SENSOR_DEF marker documents that a register is part
- // of the sensors definition and the actual value is set in
- // gl843_setup_sensor().
-
- DBGSTART;
-
- dev->reg.clear();
-
- /* default to KV-SS080 */
- SETREG (0xa2, 0x0f);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0xa2, 0x1f);
- }
- SETREG (0x01, 0x00);
- SETREG (0x02, 0x78);
- SETREG (0x03, 0x1f);
- SETREG (0x04, 0x10);
-
- // fine tune upon device description
- SETREG (0x05, 0x80);
- if (dev->model->model_id == MODEL_HP_SCANJET_G4010 ||
- dev->model->model_id == MODEL_HP_SCANJET_G4050 ||
- dev->model->model_id == MODEL_HP_SCANJET_4850C)
- {
- SETREG (0x05, 0x08);
- }
-
- dev->reg.find_reg(0x05).value &= ~REG05_DPIHW;
- switch (sanei_genesys_find_sensor_any(dev).optical_res)
- {
- case 600:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_600;
- break;
- case 1200:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_1200;
- break;
- case 2400:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_2400;
- break;
- case 4800:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_4800;
- break;
- }
-
- // TODO: on 8600F the windows driver turns off GAIN4 which is recommended
- SETREG (0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */
- SETREG (0x08, 0x00);
- SETREG (0x09, 0x00);
- SETREG (0x0a, 0x00);
-
- // This register controls clock and RAM settings and is further modified in
- // gl843_boot
- SETREG (0x0b, 0x6a);
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x0b, 0x89);
- }
-
- // EXPR[0:15], EXPG[0:15], EXPB[0:15]: Exposure time settings.
- SETREG(0x10, 0x00); // SENSOR_DEF
- SETREG(0x11, 0x00); // SENSOR_DEF
- SETREG(0x12, 0x00); // SENSOR_DEF
- SETREG(0x13, 0x00); // SENSOR_DEF
- SETREG(0x14, 0x00); // SENSOR_DEF
- SETREG(0x15, 0x00); // SENSOR_DEF
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- dev->reg.set16(REG_EXPR, 0x9c40);
- dev->reg.set16(REG_EXPG, 0x9c40);
- dev->reg.set16(REG_EXPB, 0x9c40);
- }
- // CCD signal settings.
- SETREG(0x16, 0x33); // SENSOR_DEF
- SETREG(0x17, 0x1c); // SENSOR_DEF
- SETREG(0x18, 0x10); // SENSOR_DEF
-
- // EXPDMY[0:7]: Exposure time of dummy lines.
- SETREG(0x19, 0x2a); // SENSOR_DEF
-
- // Various CCD clock settings.
- SETREG(0x1a, 0x04); // SENSOR_DEF
- SETREG(0x1b, 0x00); // SENSOR_DEF
- SETREG(0x1c, 0x20); // SENSOR_DEF
- SETREG(0x1d, 0x04); // SENSOR_DEF
-
- SETREG (0x1e, 0x10);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x1e, 0x20);
- }
-
- SETREG (0x1f, 0x01);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x1f, 0xff);
- }
-
- SETREG (0x20, 0x10);
- SETREG (0x21, 0x04);
- SETREG (0x22, 0x01);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x22, 0xc8);
- }
-
- SETREG (0x23, 0x01);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x23, 0xc8);
- }
-
- SETREG (0x24, 0x04);
- SETREG (0x25, 0x00);
- SETREG (0x26, 0x00);
- SETREG (0x27, 0x00);
- SETREG (0x2c, 0x02);
- SETREG (0x2d, 0x58);
- // BWHI[0:7]: high level of black and white threshold
- SETREG (0x2e, 0x80);
- // BWLOW[0:7]: low level of black and white threshold
- SETREG (0x2f, 0x80);
- SETREG (0x30, 0x00);
- SETREG (0x31, 0x14);
- SETREG (0x32, 0x27);
- SETREG (0x33, 0xec);
-
- // DUMMY: CCD dummy and optically black pixel count
- SETREG (0x34, 0x24);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x34, 0x14);
- }
-
- // MAXWD: If available buffer size is less than 2*MAXWD words, then
- // "buffer full" state will be set.
- SETREG (0x35, 0x00);
- SETREG (0x36, 0xff);
- SETREG (0x37, 0xff);
-
- // LPERIOD: Line period or exposure time for CCD or CIS.
- SETREG(0x38, 0x55); // SENSOR_DEF
- SETREG(0x39, 0xf0); // SENSOR_DEF
-
- // FEEDL[0:24]: The number of steps of motor movement.
- SETREG(0x3d, 0x00);
- SETREG (0x3e, 0x00);
- SETREG (0x3f, 0x01);
-
- // Latch points for high and low bytes of R, G and B channels of AFE. If
- // multiple clocks per pixel are consumed, then the setting defines during
- // which clock the corresponding value will be read.
- // RHI[0:4]: The latch point for high byte of R channel.
- // RLOW[0:4]: The latch point for low byte of R channel.
- // GHI[0:4]: The latch point for high byte of G channel.
- // GLOW[0:4]: The latch point for low byte of G channel.
- // BHI[0:4]: The latch point for high byte of B channel.
- // BLOW[0:4]: The latch point for low byte of B channel.
- SETREG(0x52, 0x01); // SENSOR_DEF
- SETREG(0x53, 0x04); // SENSOR_DEF
- SETREG(0x54, 0x07); // SENSOR_DEF
- SETREG(0x55, 0x0a); // SENSOR_DEF
- SETREG(0x56, 0x0d); // SENSOR_DEF
- SETREG(0x57, 0x10); // SENSOR_DEF
-
- // VSMP[0:4]: The position of the image sampling pulse for AFE in cycles.
- // VSMPW[0:2]: The length of the image sampling pulse for AFE in cycles.
- SETREG(0x58, 0x1b); // SENSOR_DEF
-
- SETREG(0x59, 0x00); // SENSOR_DEF
- SETREG(0x5a, 0x40); // SENSOR_DEF
-
- // 0x5b-0x5c: GMMADDR[0:15] address for gamma or motor tables download
- // SENSOR_DEF
-
- // DECSEL[0:2]: The number of deceleratino steps after touching home sensor
- // STOPTIM[0:4]: The stop duration between change of directions in
- // backtracking
- SETREG (0x5e, 0x23);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x5e, 0x1f);
- }
-
- // FMOVDEC: The number of deceleration steps in table 5 for auto-go-home
- SETREG (0x5f, 0x01);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x5f, 0xf0);
- }
-
- // Z1MOD[0:20]
- SETREG (0x60, 0x00);
- SETREG (0x61, 0x00);
- SETREG (0x62, 0x00);
-
- // Z2MOD[0:20]
- SETREG (0x63, 0x00);
- SETREG (0x64, 0x00);
- SETREG (0x65, 0x00);
-
- // STEPSEL[0:1]. Motor movement step mode selection for tables 1-3 in
- // scanning mode.
- // MTRPWM[0:5]. Motor phase PWM duty cycle setting for tables 1-3
- SETREG (0x67, 0x7f);
- // FSTPSEL[0:1]: Motor movement step mode selection for tables 4-5 in
- // command mode.
- // FASTPWM[5:0]: Motor phase PWM duty cycle setting for tables 4-5
- SETREG (0x68, 0x7f);
-
- // FSHDEC[0:7]: The number of deceleration steps after scanning is finished
- // (table 3)
- SETREG (0x69, 0x01);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x69, 64);
- }
-
- // FMOVNO[0:7] The number of acceleration or deceleration steps for fast
- // moving (table 4)
- SETREG (0x6a, 0x04);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x69, 64);
- }
-
- // GPIO-related register bits
- SETREG (0x6b, 0x30);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x6b, 0x72);
- }
-
- // 0x6c, 0x6d, 0x6e, 0x6f are set according to gpio tables. See
- // gl843_init_gpio.
-
- // RSH[0:4]: The position of rising edge of CCD RS signal in cycles
- // RSL[0:4]: The position of falling edge of CCD RS signal in cycles
- // CPH[0:4]: The position of rising edge of CCD CP signal in cycles.
- // CPL[0:4]: The position of falling edge of CCD CP signal in cycles
- SETREG(0x70, 0x01); // SENSOR_DEF
- SETREG(0x71, 0x03); // SENSOR_DEF
- SETREG (0x72, 0x04);
- SETREG (0x73, 0x05);
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x70, 0x00);
- SETREG(0x71, 0x02);
- SETREG(0x72, 0x02);
- SETREG(0x73, 0x04);
- }
-
- // CK1MAP[0:17], CK3MAP[0:17], CK4MAP[0:17]: CCD clock bit mapping setting.
- SETREG(0x74, 0x00); // SENSOR_DEF
- SETREG(0x75, 0x00); // SENSOR_DEF
- SETREG(0x76, 0x3c); // SENSOR_DEF
- SETREG(0x77, 0x00); // SENSOR_DEF
- SETREG(0x78, 0x00); // SENSOR_DEF
- SETREG(0x79, 0x9f); // SENSOR_DEF
- SETREG(0x7a, 0x00); // SENSOR_DEF
- SETREG(0x7b, 0x00); // SENSOR_DEF
- SETREG(0x7c, 0x55); // SENSOR_DEF
-
- // various AFE settings
- SETREG(0x7d, 0x00);
-
- // GPOLED[x]: LED vs GPIO settings
- SETREG(0x7e, 0x00);
-
- // BSMPDLY, VSMPDLY
- // LEDCNT[0:1]: Controls led blinking and its period
- SETREG (0x7f, 0x00);
-
- // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for
- // moving in various situations.
- SETREG (0x80, 0x00);
-
- if (dev->model->model_id != MODEL_CANON_CANOSCAN_4400F)
- {
- // NOTE: Historical code. None of the following 6 registers are
- // documented in the datasheet. Their default value is 0, so probably it's
- // not a bad idea to leave this here.
- SETREG (0x81, 0x00);
- SETREG (0x82, 0x00);
- SETREG (0x83, 0x00);
- SETREG (0x84, 0x00);
- SETREG (0x85, 0x00);
- SETREG (0x86, 0x00);
- }
-
- SETREG (0x87, 0x00);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x87, 0x02);
- }
-
- // MTRPLS[0:7]: The width of the ADF motor trigger signal pulse.
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x94, 0xff);
- }
-
- // 0x95-0x97: SCANLEN[0:19]: Controls when paper jam bit is set in sheetfed
- // scanners.
-
- // ONDUR[0:15]: The duration of PWM ON phase for LAMP control
- // OFFDUR[0:15]: The duration of PWM OFF phase for LAMP control
- // both of the above are in system clocks
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x98, 0x00);
- SETREG(0x99, 0x00);
- SETREG(0x9a, 0x00);
- SETREG(0x9b, 0x00);
- }
-
- // RMADLY[0:1], MOTLAG, CMODE, STEPTIM, MULDMYLN, IFRS
- SETREG(0x9d, 0x04);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0x9d, 0x08); // additionally sets the multiplier for slope tables
- }
-
-
- // SEL3INV, TGSTIME[0:2], TGWTIME[0:2]
- SETREG (0x9e, 0x00); // SENSOR_DEF
-
- // RFHSET[0:4]: Refresh time of SDRAM in units of 2us
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0xa2, 0x1f);
- }
-
- // 0xa6-0xa9: controls gpio, see gl843_gpio_init
-
- // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK.
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0xab, 0x00);
- }
-
- // VRHOME[3:2], VRMOVE[3:2], VRBACK[3:2]: Vref setting of the motor driver IC
- // for various situations.
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- SETREG(0xac, 0x00);
- }
-
- if (dev->model->model_id != MODEL_CANON_CANOSCAN_8400F)
- {
- SETREG (0x0c, 0x00);
- SETREG (0x94, 0xff);
- SETREG (0xab, 0x50);
- }
-
- if (dev->model->model_id != MODEL_CANON_CANOSCAN_4400F
- && dev->model->model_id != MODEL_CANON_CANOSCAN_8400F)
- {
- SETREG (0xaa, 0x00);
- }
-
- /* G4050 values */
- if (dev->model->model_id == MODEL_HP_SCANJET_G4010 ||
- dev->model->model_id == MODEL_HP_SCANJET_G4050 ||
- dev->model->model_id == MODEL_HP_SCANJET_4850C)
- {
- SETREG (0x03, 0x1d);
- SETREG (0x06, 0xd0); /* SCANMOD=110, PWRBIT and no GAIN4 */
- SETREG (0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */
- SETREG (0x0a, 0x18);
- SETREG (0x0b, 0x69);
-
- /* CIS exposure is used for XPA lamp movement */
- SETREG (0x10, 0x2c);
- SETREG (0x11, 0x09);
- SETREG (0x12, 0x22);
- SETREG (0x13, 0xb8);
- SETREG (0x14, 0x10);
- SETREG (0x15, 0xf0);
-
- SETREG (0x6b, 0xf4);
-
- SETREG (0x70, 0x00);
- SETREG (0x71, 0x02);
- SETREG (0x72, 0x00);
- SETREG (0x73, 0x00);
-
- SETREG (0x80, 0x50);
- SETREG (0x9d, 0x08);
- SETREG (0xab, 0x40);
-
- /* XXX STEF XXX TODO move to set for scan */
- SETREG (0x98, 0x03);
- SETREG (0x99, 0x30);
- SETREG (0x9a, 0x01);
- SETREG (0x9b, 0x80);
- SETREG (0xac, 0x00);
- }
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_4400F)
- {
- SETREG (0x06, 0xf0); /* SCANMOD=111, PWRBIT and no GAIN4 */
- SETREG (0x0b, 0x69); /* 16M only */
- SETREG (0x1e, 0x20);
- SETREG (0x22, 0xc8);
- SETREG (0x23, 0xc8);
- SETREG (0x5e, 0x3f);
- SETREG (0x5f, 0xf0);
- SETREG (0x6b, 0x72);
- SETREG (0x72, 0x01);
- SETREG (0x73, 0x03);
- SETREG (0x80, 0x0c);
- SETREG (0x87, 0x02); /* MCLOCK -> CK4MAP */
- SETREG (0x9d, 0x08); /* STEPTIM=2 */
- SETREG (0xa2, 0x1f);
- SETREG (0xab, 0x00);
- sanei_genesys_set_double(&dev->reg,REG_EXPR,0x9c40);
- sanei_genesys_set_double(&dev->reg,REG_EXPG,0x9c40);
- sanei_genesys_set_double(&dev->reg,REG_EXPB,0x9c40);
- }
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8400F)
- {
- SETREG (0x03, 0x1c);
- SETREG (0x06, 0xd0); /* SCANMOD=110, PWRBIT and no GAIN4 */
- SETREG (0x0a, 0x10);
- SETREG (0x22, 0x50);
- SETREG (0x23, 0x50);
- SETREG (0x5e, 0x85);
- SETREG (0x6b, 0xb1);
- SETREG (0x1e, 0xa0);
- SETREG (0x72, 0x03);
- SETREG (0x73, 0x04);
- SETREG (0x7d, 0x20);
- SETREG (0x80, 0x28);
- SETREG (0x87, 0x02); /* MCLOCK -> CK4MAP */
- SETREG (0x9d, 0x08); /* STEPTIM=2 */
- }
-
- dev->calib_reg = dev->reg;
-
- DBGCOMPLETED;
-}
-
-/* Send slope table for motor movement
- slope_table in machine byte order
- */
-static SANE_Status
-gl843_send_slope_table (Genesys_Device * dev, int table_nr,
- uint16_t * slope_table, int steps)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- char msg[10000];
-
- DBG(DBG_proc, "%s (table_nr = %d, steps = %d)\n", __func__, table_nr, steps);
-
- std::vector<uint8_t> table(steps * 2);
- for (i = 0; i < steps; i++)
- {
- table[i * 2] = slope_table[i] & 0xff;
- table[i * 2 + 1] = slope_table[i] >> 8;
- }
-
- if (DBG_LEVEL >= DBG_io)
- {
- sprintf (msg, "write slope %d (%d)=", table_nr, steps);
- for (i = 0; i < steps; i++)
- {
- sprintf (msg+strlen(msg), "%d", slope_table[i]);
- }
- DBG(DBG_io, "%s: %s\n", __func__, msg);
- }
-
-
- /* slope table addresses are fixed : 0x4000, 0x4800, 0x5000, 0x5800, 0x6000 */
- /* XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14); */
- status = write_data (dev, 0x4000 + 0x800 * table_nr, steps * 2, table.data());
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: write data failed writing slope table %d (%s)\n", __func__, table_nr,
- sane_strstatus(status));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/* Set values of analog frontend */
-static SANE_Status
-gl843_set_fe (Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- int i;
-
- DBG(DBG_proc, "%s (%s)\n", __func__, set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
- AFE_POWER_SAVE ? "powersave" : "huh?");
-
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
- dev->frontend = dev->frontend_initial;
- }
-
- /* check analog frontend type */
- // FIXME: looks like we write to that register with initial data
- RIE (sanei_genesys_read_register (dev, REG04, &val));
- if ((val & REG04_FESET) != 0x00)
- {
- /* for now there is no support for AD fe */
- DBG(DBG_proc, "%s(): unsupported frontend type %d\n", __func__,
- dev->reg.find_reg(0x04).value & REG04_FESET);
- return SANE_STATUS_UNSUPPORTED;
- }
-
- DBG(DBG_proc, "%s(): frontend reset complete\n", __func__);
-
- for (i = 1; i <= 3; i++)
- {
- // FIXME: BUG: we should initialize dev->frontend before first use. Right now it's
- // initialized during genesys_coarse_calibration()
- if (dev->frontend.regs.empty()) {
- status = sanei_genesys_fe_write_data(dev, i, 0x00);
- } else {
- status = sanei_genesys_fe_write_data(dev, i, dev->frontend.regs.get_value(0x00 + i));
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing reg[%d] failed: %s\n", __func__, i, sane_strstatus(status));
- return status;
- }
- }
- for (const auto& reg : sensor.custom_fe_regs) {
- status = sanei_genesys_fe_write_data(dev, reg.address, reg.value);
- if (status != SANE_STATUS_GOOD) {
- DBG(DBG_error, "%s: writing reg[%d] failed: %s\n", __func__, i, sane_strstatus(status));
- return status;
- }
- }
-
- for (i = 0; i < 3; i++)
- {
- // FIXME: BUG: see above
- if (dev->frontend.regs.empty()) {
- status = sanei_genesys_fe_write_data(dev, 0x20 + i, 0x00);
- } else {
- status = sanei_genesys_fe_write_data(dev, 0x20 + i, dev->frontend.get_offset(i));
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing offset[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
-
- if (dev->model->ccd_type == CCD_KVSS080)
- {
- for (i = 0; i < 3; i++)
- {
- // FIXME: BUG: see above
- if (dev->frontend.regs.empty()) {
- status = sanei_genesys_fe_write_data(dev, 0x24 + i, 0x00);
- } else {
- status = sanei_genesys_fe_write_data(dev, 0x24 + i,
- dev->frontend.regs.get_value(0x24 + i));
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing sign[%d] failed: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
- }
-
- for (i = 0; i < 3; i++)
- {
- // FIXME: BUG: see above
- if (dev->frontend.regs.empty()) {
- status = sanei_genesys_fe_write_data(dev, 0x28 + i, 0x00);
- } else {
- status = sanei_genesys_fe_write_data(dev, 0x28 + i, dev->frontend.get_gain(i));
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: writing gain[%d] failed: %s\n", __func__, i, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-static SANE_Status
-gl843_init_motor_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int exposure,
- float scan_yres,
- int scan_step_type,
- unsigned int scan_lines,
- unsigned int scan_dummy,
- unsigned int feed_steps,
- int scan_power_mode,
- unsigned int flags)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int use_fast_fed, coeff;
- unsigned int lincnt;
- uint16_t scan_table[1024];
- uint16_t fast_table[1024];
- int scan_steps,fast_steps, fast_step_type;
- unsigned int feedl,factor,dist;
- GenesysRegister *r;
- uint32_t z1, z2;
-
- DBGSTART;
- DBG(DBG_info, "%s : exposure=%d, scan_yres=%g, scan_step_type=%d, scan_lines=%d, scan_dummy=%d, "
- "feed_steps=%d, scan_power_mode=%d, flags=%x\n", __func__, exposure, scan_yres,
- scan_step_type, scan_lines, scan_dummy, feed_steps, scan_power_mode, flags);
-
- /* get step multiplier */
- factor = gl843_get_step_multiplier (reg);
-
- use_fast_fed = 0;
-
- if((scan_yres>=300 && feed_steps>900) || (flags & MOTOR_FLAG_FEED))
- use_fast_fed=1;
-
- lincnt=scan_lines;
- sanei_genesys_set_triple(reg,REG_LINCNT,lincnt);
- DBG(DBG_io, "%s: lincnt=%d\n", __func__, lincnt);
-
- /* compute register 02 value */
- r = sanei_genesys_get_address (reg, REG02);
- r->value = 0x00;
- sanei_genesys_set_motor_power(*reg, true);
-
- if (use_fast_fed)
- r->value |= REG02_FASTFED;
- else
- r->value &= ~REG02_FASTFED;
-
- /* in case of automatic go home, move until home sensor */
- if (flags & MOTOR_FLAG_AUTO_GO_HOME)
- r->value |= REG02_AGOHOME | REG02_NOTHOME;
-
- /* disable backtracking */
- if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
- ||(scan_yres>=2400)
- ||(scan_yres>=sensor.optical_res))
- r->value |= REG02_ACDCDIS;
-
- /* scan and backtracking slope table */
- sanei_genesys_slope_table(scan_table,
- &scan_steps,
- scan_yres,
- exposure,
- dev->motor.base_ydpi,
- scan_step_type,
- factor,
- dev->model->motor_type,
- gl843_motors);
- RIE(gl843_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps*factor));
- RIE(gl843_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps*factor));
-
- /* STEPNO */
- r = sanei_genesys_get_address (reg, REG_STEPNO);
- r->value = scan_steps;
-
- /* FSHDEC */
- r = sanei_genesys_get_address (reg, REG_FSHDEC);
- r->value = scan_steps;
-
- /* fast table */
- fast_step_type=0;
- if(scan_step_type<=fast_step_type)
- {
- fast_step_type=scan_step_type;
- }
- sanei_genesys_slope_table(fast_table,
- &fast_steps,
- sanei_genesys_get_lowest_ydpi(dev),
- exposure,
- dev->motor.base_ydpi,
- fast_step_type,
- factor,
- dev->model->motor_type,
- gl843_motors);
- RIE(gl843_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps*factor));
- RIE(gl843_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps*factor));
- RIE(gl843_send_slope_table (dev, HOME_TABLE, fast_table, fast_steps*factor));
-
- /* FASTNO */
- r = sanei_genesys_get_address (reg, REG_FASTNO);
- r->value = fast_steps;
-
- /* FMOVNO */
- r = sanei_genesys_get_address (reg, REG_FMOVNO);
- r->value = fast_steps;
-
- /* substract acceleration distance from feedl */
- feedl=feed_steps;
- feedl<<=scan_step_type;
-
- dist = scan_steps;
- if (use_fast_fed)
- {
- dist += fast_steps*2;
- }
- DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
-
- /* get sure when don't insane value : XXX STEF XXX in this case we should
- * fall back to single table move */
- if(dist<feedl)
- feedl -= dist;
- else
- feedl = 1;
-
- sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
- DBG(DBG_io, "%s: feedl=%d\n", __func__, feedl);
-
- /* doesn't seem to matter that much */
- sanei_genesys_calculate_zmode2 (use_fast_fed,
- exposure,
- scan_table,
- scan_steps,
- feedl,
- scan_steps,
- &z1,
- &z2);
- if(scan_yres>600)
- {
- z1=0;
- z2=0;
- }
-
- sanei_genesys_set_triple(reg,REG_Z1MOD,z1);
- DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
-
- sanei_genesys_set_triple(reg,REG_Z2MOD,z2);
- DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
-
- r = sanei_genesys_get_address (reg, REG1E);
- r->value &= 0xf0; /* 0 dummy lines */
- r->value |= scan_dummy; /* dummy lines */
-
- r = sanei_genesys_get_address (reg, REG67);
- r->value = 0x3f | (scan_step_type << REG67S_STEPSEL);
-
- r = sanei_genesys_get_address (reg, REG68);
- r->value = 0x3f | (scan_step_type << REG68S_FSTPSEL);
-
- /* steps for STOP table */
- r = sanei_genesys_get_address (reg, REG_FMOVDEC);
- r->value = fast_steps;
-
- /* Vref XXX STEF XXX : optical divider or step type ? */
- r = sanei_genesys_get_address (reg, 0x80);
- if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE))
- {
- r->value = 0x50;
- coeff=sensor.optical_res/sanei_genesys_compute_dpihw(dev, sensor, scan_yres);
- if (dev->model->motor_type == MOTOR_KVSS080)
- {
- if(coeff>=1)
- {
- r->value |= 0x05;
- }
- }
- else {
- switch(coeff)
- {
- case 4:
- r->value |= 0x0a;
- break;
- case 2:
- r->value |= 0x0f;
- break;
- case 1:
- r->value |= 0x0f;
- break;
- }
- }
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/** @brief setup optical related registers
- * start and pixels are expressed in optical sensor resolution coordinate
- * space.
- * @param dev device to use
- * @param reg registers to set up
- * @param exposure exposure time to use
- * @param used_res scanning resolution used, may differ from
- * scan's one
- * @param start logical start pixel coordinate
- * @param pixels logical number of pixels to use
- * @param channels number of color channles used (1 or 3)
- * @param depth bit depth of the scan (1, 8 or 16 bits)
- * @param ccd_size_divisor SANE_TRUE specifies how much x coordinates must be shrunk
- * @param color_filter to choose the color channel used in gray scans
- * @param flags to drive specific settings such no calibration, XPA use ...
- * @return SANE_STATUS_GOOD if OK
- */
-static SANE_Status
-gl843_init_optical_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int exposure,
- int used_res,
- unsigned int start,
- unsigned int pixels,
- int channels,
- int depth,
- unsigned ccd_size_divisor,
- ColorFilter color_filter,
- int flags)
-{
- unsigned int words_per_line;
- unsigned int startx, endx, used_pixels;
- unsigned int dpiset, dpihw, factor;
- unsigned int bytes;
- unsigned int tgtime; /**> exposure time multiplier */
- unsigned int cksel; /**> clock per system pixel time in capturing image */
- GenesysRegister *r;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s : exposure=%d, used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
- "ccd_size_divisor=%d, flags=%x\n", __func__, exposure, used_res, start, pixels, channels,
- depth, ccd_size_divisor, flags);
-
- /* tgtime */
- tgtime = exposure / 65536 + 1;
- DBG(DBG_io2, "%s: tgtime=%d\n", __func__, tgtime);
-
- /* to manage high resolution device while keeping good
- * low resolution scanning speed, we make hardware dpi vary */
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, used_res);
- factor=sensor.optical_res/dpihw;
- DBG(DBG_io2, "%s: dpihw=%d (factor=%d)\n", __func__, dpihw, factor);
-
- /* sensor parameters */
- gl843_setup_sensor (dev, sensor, reg, dpihw, flags);
-
- /* resolution is divided according to CKSEL which is known once sensor is set up */
- r = sanei_genesys_get_address (reg, REG18);
- cksel= (r->value & REG18_CKSEL)+1;
- DBG(DBG_io2, "%s: cksel=%d\n", __func__, cksel);
- dpiset = used_res * cksel;
-
- /* start and end coordinate in optical dpi coordinates */
- startx = (start + sensor.dummy_pixel)/cksel;
-
- used_pixels=pixels/cksel;
- endx = startx + used_pixels;
-
- /* pixel coordinate factor correction when used dpihw is not maximal one */
- startx/=factor;
- endx/=factor;
- used_pixels=endx-startx;
-
- /* in case of stagger we have to start at an odd coordinate */
- if ((flags & OPTICAL_FLAG_STAGGER)
- &&((startx & 1)==0))
- {
- startx++;
- endx++;
- }
-
- status = gl843_set_fe(dev, sensor, AFE_SET);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* enable shading */
- r = sanei_genesys_get_address (reg, REG01);
- r->value &= ~REG01_SCAN;
- if ((flags & OPTICAL_FLAG_DISABLE_SHADING) || (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
- {
- r->value &= ~REG01_DVDSET;
- }
- else
- {
- r->value |= REG01_DVDSET;
- }
- if(dpihw>600)
- {
- r->value |= REG01_SHDAREA;
- }
- else
- {
- r->value &= ~REG01_SHDAREA;
- }
-
- r = sanei_genesys_get_address (reg, REG03);
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- r->value |= REG03_AVEENB;
- else {
- r->value &= ~REG03_AVEENB;
- }
-
- // FIXME: we probably don't need to set exposure to registers at this point. It was this way
- // before a refactor.
- sanei_genesys_set_lamp_power(dev, sensor, *reg, !(flags & OPTICAL_FLAG_DISABLE_LAMP));
-
- /* select XPA */
- r->value &= ~REG03_XPASEL;
- if (flags & OPTICAL_FLAG_USE_XPA)
- {
- r->value |= REG03_XPASEL;
- }
- reg->state.is_xpa_on = flags & OPTICAL_FLAG_USE_XPA;
-
- /* BW threshold */
- r = sanei_genesys_get_address (reg, REG2E);
- r->value = dev->settings.threshold;
- r = sanei_genesys_get_address (reg, REG2F);
- r->value = dev->settings.threshold;
-
- /* monochrome / color scan */
- r = sanei_genesys_get_address (reg, REG04);
- switch (depth)
- {
- case 1:
- r->value &= ~REG04_BITSET;
- r->value |= REG04_LINEART;
- break;
- case 8:
- r->value &= ~(REG04_LINEART | REG04_BITSET);
- break;
- case 16:
- r->value &= ~REG04_LINEART;
- r->value |= REG04_BITSET;
- break;
- }
-
- r->value &= ~(REG04_FILTER | REG04_AFEMOD);
- if (channels == 1)
- {
- switch (color_filter)
- {
- case ColorFilter::RED:
- r->value |= 0x14;
- break;
- case ColorFilter::BLUE:
- r->value |= 0x1c;
- break;
- case ColorFilter::GREEN:
- r->value |= 0x18;
- break;
- default:
- break; // should not happen
- }
- }
- else
- r->value |= 0x10; /* mono */
-
- /* register 05 */
- r = sanei_genesys_get_address (reg, REG05);
-
- /* set up dpihw */
- r->value &= ~REG05_DPIHW;
- switch(dpihw)
- {
- case 600:
- r->value |= REG05_DPIHW_600;
- break;
- case 1200:
- r->value |= REG05_DPIHW_1200;
- break;
- case 2400:
- r->value |= REG05_DPIHW_2400;
- break;
- case 4800:
- r->value |= REG05_DPIHW_4800;
- break;
- }
-
- /* enable gamma tables */
- if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
- r->value &= ~REG05_GMMENB;
- else
- r->value |= REG05_GMMENB;
-
- sanei_genesys_set_double(reg, REG_DPISET, dpiset * ccd_size_divisor);
- DBG(DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset * ccd_size_divisor);
-
- sanei_genesys_set_double(reg, REG_STRPIXEL, startx);
- sanei_genesys_set_double(reg, REG_ENDPIXEL, endx);
-
- /* words(16bit) before gamma, conversion to 8 bit or lineart */
- words_per_line = (used_pixels * dpiset) / dpihw;
- bytes = depth / 8;
- if (depth == 1)
- {
- words_per_line = (words_per_line >> 3) + ((words_per_line & 7) ? 1 : 0);
- }
- else
- {
- words_per_line *= bytes;
- }
-
- dev->wpl = words_per_line;
- dev->bpl = words_per_line;
-
- DBG(DBG_io2, "%s: used_pixels=%d\n", __func__, used_pixels);
- DBG(DBG_io2, "%s: pixels =%d\n", __func__, pixels);
- DBG(DBG_io2, "%s: depth =%d\n", __func__, depth);
- DBG(DBG_io2, "%s: dev->bpl =%lu\n", __func__, (unsigned long) dev->bpl);
- DBG(DBG_io2, "%s: dev->len =%lu\n", __func__, (unsigned long)dev->len);
- DBG(DBG_io2, "%s: dev->dist =%lu\n", __func__, (unsigned long)dev->dist);
-
- words_per_line *= channels;
-
- /* MAXWD is expressed in 2 words unit */
- /* nousedspace = (mem_bank_range * 1024 / 256 -1 ) * 4; */
- sanei_genesys_set_triple(reg,REG_MAXWD,(words_per_line)>>1);
- DBG(DBG_io2, "%s: words_per_line used=%d\n", __func__, words_per_line);
-
- sanei_genesys_set_double(reg,REG_LPERIOD,exposure/tgtime);
- DBG(DBG_io2, "%s: exposure used=%d\n", __func__, exposure/tgtime);
-
- r = sanei_genesys_get_address (reg, REG_DUMMY);
- r->value = sensor.dummy_pixel;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-struct ScanSession {
- SetupParams params;
-
- // whether the session setup has been computed via gl843_compute_session()
- bool computed = false;
-
- // whether CCD operates as half-resolution or full resolution at a specific resolution
- unsigned ccd_size_divisor = 1;
-
- // the optical resolution of the scanner.
- unsigned optical_resolution = 0;
-
- // the number of pixels at the optical resolution.
- unsigned optical_pixels = 0;
-
- // the number of bytes in the output of a single line directly from scanner
- unsigned optical_line_bytes = 0;
-
- // the resolution of the output data.
- unsigned output_resolution = 0;
-
- // the number of pixels in output data
- unsigned output_pixels = 0;
-
- // the number of bytes in the output of a single line
- unsigned output_line_bytes = 0;
-
- // the number of lines in the output of the scanner. This must be larger than the user
- // requested number due to line staggering and color channel shifting.
- unsigned output_line_count = 0;
-
- // the number of staggered lines (i.e. lines that overlap during scanning due to line being
- // thinner than the CCD element)
- unsigned num_staggered_lines = 0;
-
- // the number of lines that color channels shift due to different physical positions of
- // different color channels
- unsigned max_color_shift_lines = 0;
-
- void assert_computed() const
- {
- if (!computed) {
- throw std::runtime_error("ScanSession is not computed");
- }
- }
-};
-
-static unsigned align_int_up(unsigned num, unsigned alignment)
-{
- unsigned mask = alignment - 1;
- if (num & mask)
- num = (num & ~mask) + alignment;
- return num;
-}
-
-// computes physical parameters for specific scan setup
-static void gl843_compute_session(Genesys_Device* dev, ScanSession& s,
- const Genesys_Sensor& sensor)
-{
- s.params.assert_valid();
- s.ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(s.params.xres);
-
- s.optical_resolution = sensor.optical_res / s.ccd_size_divisor;
-
- if (s.params.flags & SCAN_FLAG_USE_OPTICAL_RES) {
- s.output_resolution = s.optical_resolution;
- } else {
- // resolution is choosen from a fixed list and can be used directly
- // unless we have ydpi higher than sensor's maximum one
- if (s.params.xres > s.optical_resolution)
- s.output_resolution = s.optical_resolution;
- else
- s.output_resolution = s.params.xres;
- }
-
- // compute rounded up number of optical pixels
- s.optical_pixels = (s.params.pixels * s.optical_resolution) / s.params.xres;
- if (s.optical_pixels * s.params.xres < s.params.pixels * s.optical_resolution)
- s.optical_pixels++;
-
- // ensure the number of optical pixels is divisible by 2.
- // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number
- s.optical_pixels = align_int_up(s.optical_pixels, 2 * s.ccd_size_divisor);
-
- s.output_pixels =
- (s.optical_pixels * s.output_resolution) / s.optical_resolution;
-
- // Note: staggering is not applied for calibration. Staggering starts at 2400 dpi
- s.num_staggered_lines = 0;
- if ((s.params.yres > 1200) &&
- ((s.params.flags & SCAN_FLAG_IGNORE_LINE_DISTANCE) == 0) &&
- (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- {
- s.num_staggered_lines = (4 * s.params.yres) / dev->motor.base_ydpi;
- }
-
- s.max_color_shift_lines = sanei_genesys_compute_max_shift(dev, s.params.channels,
- s.params.yres, s.params.flags);
-
- s.output_line_count = s.params.lines + s.max_color_shift_lines + s.num_staggered_lines;
-
- s.optical_line_bytes = (s.optical_pixels * s.params.channels * s.params.depth) / 8;
- s.output_line_bytes = (s.output_pixels * s.params.channels * s.params.depth) / 8;
- s.computed = true;
-}
-
-/* set up registers for an actual scan
- *
- * this function sets up the scanner to scan in normal or single line mode
- */
-static SANE_Status gl843_init_scan_regs(Genesys_Device* dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set* reg,
- ScanSession& session)
-{
- session.assert_computed();
-
- int start;
- int move;
- unsigned int oflags, mflags; /**> optical and motor flags */
- int exposure;
-
- int slope_dpi = 0;
- int dummy = 0;
- int scan_step_type = 1;
- int scan_power_mode = 0;
- size_t requested_buffer_size, read_buffer_size;
-
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, session.params);
-
- DBG(DBG_info, "%s : stagger=%d lines\n", __func__, session.num_staggered_lines);
-
- /* we enable true gray for cis scanners only, and just when doing
- * scan since color calibration is OK for this mode
- */
- oflags = 0;
- if (session.params.flags & SCAN_FLAG_DISABLE_SHADING)
- oflags |= OPTICAL_FLAG_DISABLE_SHADING;
- if (session.params.flags & SCAN_FLAG_DISABLE_GAMMA)
- oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
- if (session.params.flags & SCAN_FLAG_DISABLE_LAMP)
- oflags |= OPTICAL_FLAG_DISABLE_LAMP;
- if (session.params.flags & SCAN_FLAG_CALIBRATION)
- oflags |= OPTICAL_FLAG_DISABLE_DOUBLE;
- if (session.num_staggered_lines)
- oflags |= OPTICAL_FLAG_STAGGER;
- if (session.params.flags & SCAN_FLAG_USE_XPA)
- oflags |= OPTICAL_FLAG_USE_XPA;
-
-
- /* compute scan parameters values */
- /* pixels are allways given at full optical resolution */
- /* use detected left margin and fixed value */
- /* start */
- start = session.params.startx;
-
- dummy = 0;
- /* dummy = 1; XXX STEF XXX */
-
- /* slope_dpi */
- /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */
- if (dev->model->is_cis)
- slope_dpi = session.params.yres * session.params.channels;
- else
- slope_dpi = session.params.yres;
- slope_dpi = slope_dpi * (1 + dummy);
-
- /* scan_step_type */
- exposure = sensor.exposure_lperiod;
- if (exposure < 0) {
- throw std::runtime_error("Exposure not defined in sensor definition");
- }
- scan_step_type = sanei_genesys_compute_step_type(gl843_motors, dev->model->motor_type, exposure);
-
- DBG(DBG_info, "%s : exposure=%d pixels\n", __func__, exposure);
- DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, scan_step_type);
-
- /*** optical parameters ***/
- /* in case of dynamic lineart, we use an internal 8 bit gray scan
- * to generate 1 lineart data */
- if (session.params.flags & SCAN_FLAG_DYNAMIC_LINEART) {
- session.params.depth = 8;
- }
-
- /* no 16 bit gamma for this ASIC */
- if (session.params.depth == 16)
- {
- session.params.flags |= SCAN_FLAG_DISABLE_GAMMA;
- oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
- }
-
- /* now _LOGICAL_ optical values used are known, setup registers */
- status = gl843_init_optical_regs_scan (dev, sensor,
- reg,
- exposure,
- session.output_resolution,
- start,
- session.optical_pixels,
- session.params.channels,
- session.params.depth,
- session.ccd_size_divisor,
- session.params.color_filter,
- oflags);
- if (status != SANE_STATUS_GOOD)
- return status;
-
- /*** motor parameters ***/
-
- /* it seems base_dpi of the G4050 motor is changed above 600 dpi*/
- if (dev->model->motor_type == MOTOR_G4050 && session.params.yres>600)
- {
- dev->ld_shift_r = (dev->model->ld_shift_r*3800)/dev->motor.base_ydpi;
- dev->ld_shift_g = (dev->model->ld_shift_g*3800)/dev->motor.base_ydpi;
- dev->ld_shift_b = (dev->model->ld_shift_b*3800)/dev->motor.base_ydpi;
- }
- else
- {
- dev->ld_shift_r = dev->model->ld_shift_r;
- dev->ld_shift_g = dev->model->ld_shift_g;
- dev->ld_shift_b = dev->model->ld_shift_b;
- }
-
- /* add tl_y to base movement */
- move = session.params.starty;
- DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
-
-
- mflags=0;
- if(session.params.flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
- mflags|=MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
- if(session.params.flags & SCAN_FLAG_FEEDING)
- mflags|=MOTOR_FLAG_FEED;
- if (session.params.flags & SCAN_FLAG_USE_XPA)
- mflags |= MOTOR_FLAG_USE_XPA;
-
- status = gl843_init_motor_regs_scan (dev, sensor,
- reg,
- exposure,
- slope_dpi,
- scan_step_type,
- dev->model->is_cis ? session.output_line_count * session.params.channels
- : session.output_line_count,
- dummy,
- move,
- scan_power_mode,
- mflags);
- if (status != SANE_STATUS_GOOD)
- return status;
-
- /* since we don't have sheetfed scanners to handle,
- * use huge read buffer */
- /* TODO find the best size according to settings */
- requested_buffer_size = 16 * session.output_line_bytes;
-
- read_buffer_size =
- 2 * requested_buffer_size +
- (session.max_color_shift_lines + session.num_staggered_lines) * session.optical_line_bytes;
-
- dev->read_buffer.clear();
- dev->read_buffer.alloc(read_buffer_size);
-
- dev->lines_buffer.clear();
- dev->lines_buffer.alloc(read_buffer_size);
-
- dev->shrink_buffer.clear();
- dev->shrink_buffer.alloc(requested_buffer_size);
-
- dev->out_buffer.clear();
- dev->out_buffer.alloc((8 * session.params.pixels * session.params.channels *
- session.params.depth) / 8);
-
- dev->read_bytes_left = session.output_line_bytes * session.output_line_count;
-
- DBG(DBG_info, "%s: physical bytes to read = %lu\n", __func__, (u_long) dev->read_bytes_left);
- dev->read_active = SANE_TRUE;
-
- dev->current_setup.params = session.params;
- dev->current_setup.pixels = session.output_pixels;
- DBG(DBG_info, "%s: current_setup.pixels=%d\n", __func__, dev->current_setup.pixels);
- dev->current_setup.lines = session.output_line_count;
- dev->current_setup.depth = session.params.depth;
- dev->current_setup.channels = session.params.channels;
- dev->current_setup.exposure_time = exposure;
- dev->current_setup.xres = session.output_resolution;
- dev->current_setup.yres = session.params.yres;
- dev->current_setup.ccd_size_divisor = session.ccd_size_divisor;
- dev->current_setup.stagger = session.num_staggered_lines;
- dev->current_setup.max_shift = session.max_color_shift_lines + session.num_staggered_lines;
-
- dev->total_bytes_read = 0;
- if (session.params.depth == 1) {
- dev->total_bytes_to_read = ((session.params.pixels * session.params.lines) / 8 +
- (((session.params.pixels * session.params.lines) % 8) ? 1 : 0)) *
- session.params.channels;
- } else {
- dev->total_bytes_to_read = session.params.pixels * session.params.lines *
- session.params.channels * (session.params.depth / 8);
- }
-
- DBG(DBG_info, "%s: total bytes to send = %lu\n", __func__, (u_long) dev->total_bytes_to_read);
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-static void
-gl843_calculate_current_setup(Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int depth;
- int start;
-
- int used_res;
- int used_pixels;
- unsigned int lincnt;
- int exposure;
- int stagger;
-
- int max_shift;
-
- int optical_res;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* we have 2 domains for ccd: xres below or above half ccd max dpi */
- unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
- /* start */
- if(dev->settings.scan_method==ScanMethod::TRANSPARENCY)
- start = SANE_UNFIX (dev->model->x_offset_ta);
- else
- start = SANE_UNFIX (dev->model->x_offset);
-
- start /= ccd_size_divisor;
-
- start += dev->settings.tl_x;
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start; // not used
- params.starty = 0; // not used
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = 0;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
- /* optical_res */
- optical_res = sensor.optical_res / ccd_size_divisor;
-
- /* stagger */
- if (ccd_size_divisor == 1 && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG(DBG_info, "%s: stagger=%d lines\n", __func__, stagger);
-
- if (params.xres <= (unsigned) optical_res) {
- used_res = params.xres;
- } else {
- used_res = optical_res;
- }
-
- /* compute scan parameters values */
- /* pixels are allways given at half or full CCD optical resolution */
- /* use detected left margin and fixed value */
-
- /* compute correct pixels number */
- used_pixels = (params.pixels * optical_res) / params.xres;
- DBG(DBG_info, "%s: used_pixels=%d\n", __func__, used_pixels);
-
- /* exposure */
- exposure = sensor.exposure_lperiod;
- if (exposure < 0) {
- throw std::runtime_error("Exposure not defined in sensor definition");
- }
- DBG(DBG_info, "%s : exposure=%d pixels\n", __func__, exposure);
-
- /* it seems base_dpi of the G4050 motor is changed above 600 dpi*/
- if (dev->model->motor_type == MOTOR_G4050 && params.yres>600)
- {
- dev->ld_shift_r = (dev->model->ld_shift_r*3800)/dev->motor.base_ydpi;
- dev->ld_shift_g = (dev->model->ld_shift_g*3800)/dev->motor.base_ydpi;
- dev->ld_shift_b = (dev->model->ld_shift_b*3800)/dev->motor.base_ydpi;
- }
- else
- {
- dev->ld_shift_r = dev->model->ld_shift_r;
- dev->ld_shift_g = dev->model->ld_shift_g;
- dev->ld_shift_b = dev->model->ld_shift_b;
- }
-
- /* scanned area must be enlarged by max color shift needed */
- max_shift = sanei_genesys_compute_max_shift(dev, params.channels, params.yres, 0);
-
- /* lincnt */
- lincnt = params.lines + max_shift + stagger;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
- DBG(DBG_info, "%s: current_setup.pixels=%d\n", __func__, dev->current_setup.pixels);
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = ccd_size_divisor;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
- DBG(DBG_proc, "%s: completed\n", __func__);
-}
-
-/**
- * for fast power saving methods only, like disabling certain amplifiers
- * @param dev device to use
- * @param enable true to set inot powersaving
- * */
-static SANE_Status
-gl843_save_power (Genesys_Device * dev, SANE_Bool enable)
-{
- uint8_t val;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s: enable = %d\n", __func__, enable);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
-
- /* switch KV-SS080 lamp off */
- if (dev->model->gpo_type == GPO_KVSS080)
- {
- RIE(sanei_genesys_read_register (dev, REG6C, &val));
- if(enable)
- val &= 0xef;
- else
- val |= 0x10;
- RIE(sanei_genesys_write_register(dev,REG6C,val));
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl843_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (delay = %d)\n", __func__, delay);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
-
- DBGCOMPLETED;
- return status;
-}
-
-static SANE_Status
-gl843_start_action (Genesys_Device * dev)
-{
- return sanei_genesys_write_register (dev, 0x0f, 0x01);
-}
-
-static SANE_Status
-gl843_stop_action_no_move(Genesys_Device* dev, Genesys_Register_Set* reg)
-{
- uint8_t val = sanei_genesys_read_reg_from_set(reg, REG01);
- val &= ~REG01_SCAN;
- sanei_genesys_set_reg_from_set(reg, REG01, val);
- SANE_Status ret = sanei_genesys_write_register(dev, REG01, val);
- sanei_genesys_sleep_ms(100);
- return ret;
-}
-
-static SANE_Status
-gl843_stop_action (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val40, val;
- unsigned int loop;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- val40 = 0;
- status = sanei_genesys_read_register (dev, REG40, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
- }
-
- /* only stop action if needed */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
- {
- DBG(DBG_info, "%s: already stopped\n", __func__);
- DBG(DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- /* ends scan 646 */
- val = dev->reg.get8(REG01);
- val &= ~REG01_SCAN;
- dev->reg.set8(REG01, val);
- status = sanei_genesys_write_register (dev, REG01, val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register 01: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_sleep_ms(100);
-
- loop = 10;
- while (loop > 0)
- {
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- val40 = 0;
- status = sanei_genesys_read_register (dev, 0x40, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* if scanner is in command mode, we are done */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG)
- && !(val & REG41_MOTORENB))
- {
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- sanei_genesys_sleep_ms(100);
- loop--;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_IO_ERROR;
-}
-
-static SANE_Status
-gl843_get_paper_sensor (Genesys_Device * dev, SANE_Bool * paper_loaded)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- status = sanei_genesys_read_register (dev, REG6D, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read gpio: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- *paper_loaded = (val & 0x1) == 0;
- return SANE_STATUS_GOOD;
-
- return SANE_STATUS_INVAL;
-}
-
-static SANE_Status
-gl843_eject_document (Genesys_Device * dev)
-{
- DBG(DBG_proc, "%s: not implemented \n", __func__);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
- return SANE_STATUS_GOOD;
-}
-
-
-static SANE_Status
-gl843_load_document (Genesys_Device * dev)
-{
- DBG(DBG_proc, "%s: not implemented \n", __func__);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
- return SANE_STATUS_GOOD;
-}
-
-/**
- * detects end of document and adjust current scan
- * to take it into account
- * used by sheetfed scanners
- */
-static SANE_Status
-gl843_detect_document_end (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Bool paper_loaded;
- unsigned int scancnt = 0;
- int flines, channels, depth, bytes_remain, sublines,
- bytes_to_flush, lines, sub_bytes, tmp, read_bytes_left;
- DBG(DBG_proc, "%s: begin\n", __func__);
-
- RIE (gl843_get_paper_sensor (dev, &paper_loaded));
-
- /* sheetfed scanner uses home sensor as paper present */
- if ((dev->document == SANE_TRUE) && !paper_loaded)
- {
- DBG(DBG_info, "%s: no more document\n", __func__);
- dev->document = SANE_FALSE;
-
- channels = dev->current_setup.channels;
- depth = dev->current_setup.depth;
- read_bytes_left = (int) dev->read_bytes_left;
- DBG(DBG_io, "%s: read_bytes_left=%d\n", __func__, read_bytes_left);
-
- /* get lines read */
- try {
- status = sanei_genesys_read_scancnt(dev, &scancnt);
- } catch (...) {
- flines = 0;
- }
- if (status != SANE_STATUS_GOOD)
- {
- flines = 0;
- }
- else
- {
- /* compute number of line read */
- tmp = (int) dev->total_bytes_read;
- if (depth == 1 || dev->settings.scan_mode == ScanColorMode::LINEART)
- flines = tmp * 8 / dev->settings.pixels / channels;
- else
- flines = tmp / (depth / 8) / dev->settings.pixels / channels;
-
- /* number of scanned lines, but no read yet */
- flines = scancnt - flines;
-
- DBG(DBG_io, "%s: %d scanned but not read lines\n", __func__, flines);
- }
-
- /* adjust number of bytes to read
- * we need to read the final bytes which are word per line * number of last lines
- * to have doc leaving feeder */
- lines =
- (SANE_UNFIX (dev->model->post_scan) * dev->current_setup.yres) /
- MM_PER_INCH + flines;
- DBG(DBG_io, "%s: adding %d line to flush\n", __func__, lines);
-
- /* number of bytes to read from scanner to get document out of it after
- * end of document dectected by hardware sensor */
- bytes_to_flush = lines * dev->wpl;
-
- /* if we are already close to end of scan, flushing isn't needed */
- if (bytes_to_flush < read_bytes_left)
- {
- /* we take all these step to work around an overflow on some plateforms */
- tmp = (int) dev->total_bytes_read;
- DBG (DBG_io, "%s: tmp=%d\n", __func__, tmp);
- bytes_remain = (int) dev->total_bytes_to_read;
- DBG(DBG_io, "%s: bytes_remain=%d\n", __func__, bytes_remain);
- bytes_remain = bytes_remain - tmp;
- DBG(DBG_io, "%s: bytes_remain=%d\n", __func__, bytes_remain);
-
- /* remaining lines to read by frontend for the current scan */
- if (depth == 1 || dev->settings.scan_mode == ScanColorMode::LINEART)
- {
- flines = bytes_remain * 8 / dev->settings.pixels / channels;
- }
- else
- flines = bytes_remain / (depth / 8)
- / dev->settings.pixels / channels;
- DBG(DBG_io, "%s: flines=%d\n", __func__, flines);
-
- if (flines > lines)
- {
- /* change the value controlling communication with the frontend :
- * total bytes to read is current value plus the number of remaining lines
- * multiplied by bytes per line */
- sublines = flines - lines;
-
- if (depth == 1 || dev->settings.scan_mode == ScanColorMode::LINEART)
- sub_bytes =
- ((dev->settings.pixels * sublines) / 8 +
- (((dev->settings.pixels * sublines) % 8) ? 1 : 0)) *
- channels;
- else
- sub_bytes =
- dev->settings.pixels * sublines * channels * (depth / 8);
-
- dev->total_bytes_to_read -= sub_bytes;
-
- /* then adjust the physical bytes to read */
- if (read_bytes_left > sub_bytes)
- {
- dev->read_bytes_left -= sub_bytes;
- }
- else
- {
- dev->total_bytes_to_read = dev->total_bytes_read;
- dev->read_bytes_left = 0;
- }
-
- DBG(DBG_io, "%s: sublines=%d\n", __func__, sublines);
- DBG(DBG_io, "%s: subbytes=%d\n", __func__, sub_bytes);
- DBG(DBG_io, "%s: total_bytes_to_read=%lu\n", __func__,
- (unsigned long) dev->total_bytes_to_read);
- DBG(DBG_io, "%s: read_bytes_left=%d\n", __func__, read_bytes_left);
- }
- }
- else
- {
- DBG(DBG_io, "%s: no flushing needed\n", __func__);
- }
- }
-
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-// enables or disables XPA slider motor
-static SANE_Status gl843_set_xpa_motor_power(Genesys_Device *dev, bool set)
-{
- uint8_t val;
- SANE_Status status=SANE_STATUS_GOOD;
-
- DBGSTART;
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F) {
-
- if (set) {
- RIE(sanei_genesys_read_register(dev, REG6C, &val));
- val &= ~REG6C_GPIO14;
- if (dev->current_setup.xres >= 2400) {
- val |= REG6C_GPIO10;
- }
- RIE(sanei_genesys_write_register(dev, REG6C, val));
-
- RIE(sanei_genesys_read_register(dev, REGA6, &val));
- val |= REGA6_GPIO17;
- RIE(sanei_genesys_write_register(dev, REGA6,val));
- } else {
- RIE(sanei_genesys_read_register(dev, REG6C, &val));
- val |= REG6C_GPIO14;
- val &= ~REG6C_GPIO10;
- RIE(sanei_genesys_write_register(dev, REG6C, val));
-
- RIE(sanei_genesys_read_register(dev, REGA6, &val));
- val &= ~REGA6_GPIO17;
- RIE(sanei_genesys_write_register(dev, REGA6,val));
- }
- DBGCOMPLETED;
- return status;
- }
-
- if (dev->model->model_id == MODEL_HP_SCANJET_G4050) {
-
- if (set) {
- /* set MULTFILM et GPOADF */
- RIE (sanei_genesys_read_register (dev, REG6B, &val));
- val |=REG6B_MULTFILM|REG6B_GPOADF;
- RIE (sanei_genesys_write_register (dev, REG6B, val));
-
- RIE (sanei_genesys_read_register (dev, REG6C, &val));
- val &= ~REG6C_GPIO15;
- RIE (sanei_genesys_write_register (dev, REG6C, val));
-
- /* Motor power ? No move at all without this one */
- RIE (sanei_genesys_read_register (dev, REGA6, &val));
- val |= REGA6_GPIO20;
- RIE (sanei_genesys_write_register(dev,REGA6,val));
-
- RIE (sanei_genesys_read_register (dev, REGA8, &val));
- val &= ~REGA8_GPO27;
- RIE (sanei_genesys_write_register (dev, REGA8, val));
-
- RIE (sanei_genesys_read_register (dev, REGA9, &val));
- val |= REGA9_GPO32|REGA9_GPO31;
- RIE (sanei_genesys_write_register (dev, REGA9, val));
- } else {
- /* unset GPOADF */
- RIE (sanei_genesys_read_register (dev, REG6B, &val));
- val &= ~REG6B_GPOADF;
- RIE (sanei_genesys_write_register (dev, REG6B, val));
-
- RIE (sanei_genesys_read_register (dev, REGA8, &val));
- val |= REGA8_GPO27;
- RIE (sanei_genesys_write_register (dev, REGA8, val));
-
- RIE (sanei_genesys_read_register (dev, REGA9, &val));
- val &= ~REGA9_GPO31;
- RIE (sanei_genesys_write_register (dev, REGA9, val));
- }
- DBGCOMPLETED;
- return status;
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/** @brief light XPA lamp
- * toggle gpios to switch off regular lamp and light on the
- * XPA light
- * @param dev device to set up
- */
-static SANE_Status gl843_set_xpa_lamp_power(Genesys_Device *dev, bool set)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val = 0;
- DBGSTART;
-
- if (set) {
- RIE(sanei_genesys_read_register(dev, REGA6, &val));
-
- // cut regular lamp power
- val &= ~(REGA6_GPIO24 | REGA6_GPIO23);
-
- // set XPA lamp power
- val |= REGA6_GPIO22 | REGA6_GPIO21 | REGA6_GPIO19;
-
- RIE(sanei_genesys_write_register(dev, REGA6, val));
-
- RIE(sanei_genesys_read_register(dev, REGA7, &val));
- val|=REGA7_GPOE24; /* lamp 1 off GPOE 24 */
- val|=REGA7_GPOE23; /* lamp 2 off GPOE 23 */
- val|=REGA7_GPOE22; /* full XPA lamp power */
- RIE(sanei_genesys_write_register(dev, REGA7, val));
- } else {
- RIE(sanei_genesys_read_register(dev, REGA6, &val));
-
- // switch on regular lamp
- val |= REGA6_GPIO23;
-
- // no XPA lamp power (2 bits for level: __11 ____)
- val &= ~(REGA6_GPIO22 | REGA6_GPIO21);
-
- RIE(sanei_genesys_write_register(dev, REGA6, val));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/* Send the low-level scan command */
-static SANE_Status
-gl843_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SANE_Bool start_motor)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- uint16_t dpiset, dpihw;
-
- DBGSTART;
-
- /* get back the target dpihw */
- sanei_genesys_get_double (reg, REG_DPISET, &dpiset);
- dpihw = sanei_genesys_compute_dpihw(dev, sensor, dpiset);
-
- /* set up GPIO for scan */
- switch(dev->model->gpo_type)
- {
- /* KV case */
- case GPO_KVSS080:
- RIE (sanei_genesys_write_register (dev, REGA9, 0x00));
- RIE (sanei_genesys_write_register (dev, REGA6, 0xf6));
- /* blinking led */
- RIE (sanei_genesys_write_register (dev, 0x7e, 0x04));
- break;
- case GPO_G4050:
- RIE (sanei_genesys_write_register (dev, REGA7, 0xfe));
- RIE (sanei_genesys_write_register (dev, REGA8, 0x3e));
- RIE (sanei_genesys_write_register (dev, REGA9, 0x06));
- switch (dpihw)
- {
- case 1200:
- case 2400:
- case 4800:
- RIE (sanei_genesys_write_register (dev, REG6C, 0x60));
- RIE (sanei_genesys_write_register (dev, REGA6, 0x46));
- break;
- default: /* 600 dpi case */
- RIE (sanei_genesys_write_register (dev, REG6C, 0x20));
- RIE (sanei_genesys_write_register (dev, REGA6, 0x44));
- }
-
- if (reg->state.is_xpa_on && reg->state.is_lamp_on) {
- RIE(gl843_set_xpa_lamp_power(dev, true));
- }
-
- if (reg->state.is_xpa_on) {
- dev->needs_home_ta = SANE_TRUE;
- RIE(gl843_set_xpa_motor_power(dev, true));
- }
-
- /* blinking led */
- RIE (sanei_genesys_write_register (dev, REG7E, 0x01));
- break;
- case GPO_CS8600F:
- if (reg->state.is_xpa_on) {
- dev->needs_home_ta = SANE_TRUE;
- RIE(gl843_set_xpa_motor_power(dev, true));
- }
- break;
- case GPO_CS4400F:
- case GPO_CS8400F:
- default:
- break;
- }
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register
- (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
-
- /* enable scan and motor */
- RIE (sanei_genesys_read_register (dev, REG01, &val));
- val |= REG01_SCAN;
- RIE (sanei_genesys_write_register (dev, REG01, val));
-
- if (start_motor)
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 1));
- }
- else
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 0));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/* Send the stop scan command */
-static SANE_Status
-gl843_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (check_stop = %d)\n", __func__, check_stop);
- if (reg == NULL)
- return SANE_STATUS_INVAL;
-
- /* post scan gpio */
- RIE(sanei_genesys_write_register(dev,0x7e,0x00));
-
- // turn off XPA lamp if needed
- // BUG: the if condition below probably shouldn't be enabled when XPA is off
- if (reg->state.is_xpa_on || reg->state.is_lamp_on) {
- gl843_set_xpa_lamp_power(dev, false);
- }
-
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- status = SANE_STATUS_GOOD;
- }
- else /* flat bed scanners */
- {
- status = gl843_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/** @brief park XPA lamp
- * park the XPA lamp if needed
- */
-static SANE_Status gl843_park_xpa_lamp (Genesys_Device * dev)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- uint8_t val;
- int loop = 0;
-
- DBGSTART;
-
- /* copy scan settings */
- local_reg = dev->reg;
-
- /* set a huge feedl and reverse direction */
- sanei_genesys_set_triple(&local_reg,REG_FEEDL,0xbdcd);
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
-
- /* set up for reverse and no scan */
- r = sanei_genesys_get_address (&local_reg, REG02);
- r->value |= REG02_MTRREV;
- r = sanei_genesys_get_address (&local_reg, REG01);
- r->value &= ~REG01_SCAN;
-
- /* write to scanner and start action */
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
- RIE(gl843_set_xpa_motor_power(dev, true));
- try {
- status = gl843_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl843_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl843_stop_action (dev);
- /* restore original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- while (loop < 600) /* do not wait longer then 60 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io2)
- {
- sanei_genesys_print_status (val);
- }
-
- if (val & REG41_HOMESNR) /* home sensor */
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- DBG(DBG_proc, "%s: finished\n", __func__);
-
- gl843_set_xpa_motor_power(dev, false);
- dev->needs_home_ta = SANE_FALSE;
-
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* we are not parked here.... should we fail ? */
- DBG(DBG_info, "%s: XPA lamp is not parked\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief Moves the slider to the home (top) position slowly
- * */
-static SANE_Status
-gl843_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- uint8_t val;
- float resolution;
- int loop = 0;
-
- DBG(DBG_proc, "%s (wait_until_home = %d)\n", __func__, wait_until_home);
-
- if (dev->needs_home_ta) {
- RIE(gl843_park_xpa_lamp(dev));
- }
-
- /* regular slow back home */
- dev->scanhead_position_in_steps = 0;
-
- /* first read gives HOME_SENSOR true */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_sleep_ms(100);
-
- /* second is reliable */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- if (val & HOMESNR) /* is sensor at home? */
- {
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- local_reg = dev->reg;
- resolution=sanei_genesys_get_lowest_ydpi(dev);
-
- const auto& sensor = sanei_genesys_find_sensor(dev, resolution);
-
- ScanSession session;
- session.params.xres = resolution;
- session.params.yres = resolution;
- session.params.startx = 100;
- session.params.starty = 40000;
- session.params.pixels = 100;
- session.params.lines = 100;
- session.params.depth = 8;
- session.params.channels = 1;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::LINEART;
- session.params.color_filter = dev->settings.color_filter;
- session.params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
- gl843_compute_session(dev, session, sensor);
-
- status = gl843_init_scan_regs(dev, sensor, &local_reg, session);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
-
- /* set up for reverse and no scan */
- r = sanei_genesys_get_address(&local_reg, REG02);
- r->value |= REG02_MTRREV;
- r = sanei_genesys_get_address(&local_reg, REG01);
- r->value &= ~REG01_SCAN;
-
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- try {
- status = gl843_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl843_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl843_stop_action (dev);
- /* restore original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- if (wait_until_home)
- {
-
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io2)
- {
- sanei_genesys_print_status (val);
- }
-
- if (val & REG41_HOMESNR) /* home sensor */
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl843_stop_action (dev);
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
- area at 600 dpi from very top of scanner */
-static SANE_Status
-gl843_search_start_position (Genesys_Device * dev)
-{
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- int steps;
-
- int pixels = 600;
- int dpi = 300;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- local_reg = dev->reg;
-
- /* sets for a 200 lines * 600 pixels */
- /* normal scan with no shading */
-
- // FIXME: the current approach of doing search only for one resolution does not work on scanners
- // whith employ different sensors with potentially different settings.
- auto& sensor = sanei_genesys_find_sensor_for_write(dev, dpi);
-
- ScanSession session;
- session.params.xres = dpi;
- session.params.yres = dpi;
- session.params.startx = 0;
- session.params.starty = 0; // we should give a small offset here - ~60 steps
- session.params.pixels = 600;
- session.params.lines = dev->model->search_lines;
- session.params.depth = 8;
- session.params.channels = 1;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::GRAY;
- session.params.color_filter = ColorFilter::GREEN;
- session.params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE;
- gl843_compute_session(dev, session, sensor);
-
- status = gl843_init_scan_regs(dev, sensor, &local_reg, session);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk setup registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* send to scanner */
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- size = dev->read_bytes_left;
-
- std::vector<uint8_t> data(size);
-
- status = gl843_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- RIE(gl843_stop_action_no_move(dev, &local_reg));
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl843_search_position.pnm", data.data(), 8, 1, pixels,
- dev->model->search_lines);
-
- status = gl843_end_scan(dev, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* update regs to copy ASIC internal state */
- dev->reg = local_reg;
-
- status =
- sanei_genesys_search_reference_point (dev, sensor, data.data(), 0, dpi, pixels,
- dev->model->search_lines);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set search reference point: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/*
- * sets up register for coarse gain calibration
- * todo: check it for scanners using it */
-static SANE_Status
-gl843_init_regs_for_coarse_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t channels;
- uint8_t cksel;
-
- DBGSTART;
- cksel = (regs.find_reg(0x18).value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
-
- /* set line size */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- int flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
- flags |= SCAN_FLAG_USE_XPA;
- }
-
- ScanSession session;
- session.params.xres = dev->settings.xres;
- session.params.yres = dev->settings.yres;
- session.params.startx = 0;
- session.params.starty = 0;
- session.params.pixels = sensor.optical_res / cksel; // XXX STEF XXX dpi instead of pixels!
- session.params.lines = 20;
- session.params.depth = 16;
- session.params.channels = channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = dev->settings.scan_mode;
- session.params.color_filter = dev->settings.color_filter;
- session.params.flags = flags;
- gl843_compute_session(dev, session, sensor);
-
- status = gl843_init_scan_regs(dev, sensor, &regs, session);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_set_motor_power(regs, false);
-
- DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
- sensor.optical_res / cksel, dev->settings.xres);
-
- status = dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief moves the slider to steps at motor base dpi
- * @param dev device to work on
- * @param steps number of steps to move
- * */
-static SANE_Status
-gl843_feed (Genesys_Device * dev, unsigned int steps)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- float resolution;
- uint8_t val;
-
- DBGSTART;
-
- /* prepare local registers */
- local_reg = dev->reg;
-
- resolution=sanei_genesys_get_lowest_ydpi(dev);
-
- const auto& sensor = sanei_genesys_find_sensor(dev, resolution);
-
- ScanSession session;
- session.params.xres = resolution;
- session.params.yres = resolution;
- session.params.startx = 0;
- session.params.starty = steps;
- session.params.pixels = 100;
- session.params.lines = 3;
- session.params.depth = 8;
- session.params.channels = 3;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- session.params.color_filter = ColorFilter::RED;
- session.params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_FEEDING |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
- gl843_compute_session(dev, session, sensor);
-
- status = gl843_init_scan_regs(dev, sensor, &local_reg, session);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
-
- /* set up for no scan */
- r = sanei_genesys_get_address(&local_reg, REG01);
- r->value &= ~REG01_SCAN;
-
- /* send registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- try {
- status = gl843_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl843_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl843_stop_action (dev);
-
- /* restore original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- /* wait until feed count reaches the required value, but do not
- * exceed 30s */
- do
- {
- status = sanei_genesys_get_status (dev, &val);
- }
- while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
-
- // looks like the scanner locks up if we scan immediately after feeding
- sanei_genesys_sleep_ms(100);
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status gl843_move_to_ta (Genesys_Device * dev);
-
-/* init registers for shading calibration */
-/* shading calibration is done at dpihw */
-static SANE_Status
-gl843_init_regs_for_shading(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int move, resolution, dpihw, factor;
- uint16_t strpixel;
-
- DBGSTART;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- dev->calib_channels = 3;
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY)
- dev->calib_lines = dev->model->shading_ta_lines;
- else
- dev->calib_lines = dev->model->shading_lines;
- dpihw = sanei_genesys_compute_dpihw_calibration(dev, sensor, dev->settings.xres);
- factor=sensor.optical_res/dpihw;
- resolution=dpihw;
-
- const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution,
- dev->settings.scan_method);
-
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY &&
- dev->model->model_id == MODEL_CANON_CANOSCAN_8600F &&
- dev->settings.xres == 4800)
- {
- float offset = SANE_UNFIX(dev->model->x_offset_ta);
- offset /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
- offset = (offset * calib_sensor.optical_res) / MM_PER_INCH;
-
- unsigned size = SANE_UNFIX(dev->model->x_size_ta);
- size /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
- size = (size * calib_sensor.optical_res) / MM_PER_INCH;
-
- dev->calib_pixels_offset = offset;
- dev->calib_pixels = size;
- }
- else
- {
- dev->calib_pixels_offset = 0;
- dev->calib_pixels = calib_sensor.sensor_pixels / factor;
- }
-
- dev->calib_resolution = resolution;
-
- int flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY)
- {
- // note: move_to_ta() function has already been called and the sensor is at the
- // transparency adapter
- move = 0; // already at dev->model->y_offset_calib_ta implicitly
- flags |= SCAN_FLAG_USE_XPA;
- }
- else
- move = SANE_UNFIX(dev->model->y_offset_calib);
-
- move = (move * resolution) / MM_PER_INCH;
-
- ScanSession session;
- session.params.xres = resolution;
- session.params.yres = resolution;
- session.params.startx = dev->calib_pixels_offset;
- session.params.starty = move;
- session.params.pixels = dev->calib_pixels;
- session.params.lines = dev->calib_lines;
- session.params.depth = 16;
- session.params.channels = dev->calib_channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = dev->settings.scan_mode;
- session.params.color_filter = dev->settings.color_filter;
- session.params.flags = flags;
- gl843_compute_session(dev, session, calib_sensor);
-
- status = gl843_init_scan_regs(dev, calib_sensor, &regs, session);
-
- // the pixel number may be updated to conform to scanner constraints
- dev->calib_pixels = dev->current_setup.pixels;
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- dev->calib_total_bytes_to_read = dev->read_bytes_left;
-
- dev->scanhead_position_in_steps += dev->calib_lines + move;
- sanei_genesys_get_double(&regs,REG_STRPIXEL,&strpixel);
- DBG(DBG_info, "%s: STRPIXEL=%d\n", __func__, strpixel);
-
- status = dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief set up registers for the actual scan
- */
-static SANE_Status
-gl843_init_regs_for_scan (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int flags;
- int depth;
- float move;
- int move_dpi;
- float start;
-
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
- move_dpi = dev->motor.base_ydpi;
-
- flags = 0;
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY)
- {
- // note: move_to_ta() function has already been called and the sensor is at the
- // transparency adapter
- move = SANE_UNFIX(dev->model->y_offset_ta) - SANE_UNFIX(dev->model->y_offset_calib_ta);
- flags |= SCAN_FLAG_USE_XPA;
- }
- else
- move = SANE_UNFIX(dev->model->y_offset);
-
- move += dev->settings.tl_y;
- move = (move * move_dpi) / MM_PER_INCH;
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- /* start */
- if(dev->settings.scan_method==ScanMethod::TRANSPARENCY)
- start = SANE_UNFIX (dev->model->x_offset_ta);
- else
- start = SANE_UNFIX (dev->model->x_offset);
-
- start /= sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres);
- start += dev->settings.tl_x;
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- /* enable emulated lineart from gray data */
- if(dev->settings.scan_mode == ScanColorMode::LINEART
- && dev->settings.dynamic_lineart)
- {
- flags |= SCAN_FLAG_DYNAMIC_LINEART;
- }
-
- ScanSession session;
- session.params.xres = dev->settings.xres;
- session.params.yres = dev->settings.yres;
- session.params.startx = start;
- session.params.starty = move;
- session.params.pixels = dev->settings.pixels;
- session.params.lines = dev->settings.lines;
- session.params.depth = depth;
- session.params.channels = channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = dev->settings.scan_mode;
- session.params.color_filter = dev->settings.color_filter;
- session.params.flags = flags;
- gl843_compute_session(dev, session, sensor);
-
- status = gl843_init_scan_regs(dev, sensor, &dev->reg, session);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/**
- * This function sends gamma tables to ASIC
- */
-static SANE_Status
-gl843_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor)
-{
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
-
- DBGSTART;
-
- size = 256;
-
- /* allocate temporary gamma tables: 16 bits words, 3 channels */
- std::vector<uint8_t> gamma(size * 2 * 3);
-
- std::vector<uint16_t> rgamma = get_gamma_table(dev, sensor, GENESYS_RED);
- std::vector<uint16_t> ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN);
- std::vector<uint16_t> bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE);
-
- // copy sensor specific's gamma tables
- for (i = 0; i < size; i++) {
- gamma[i * 2 + size * 0 + 0] = rgamma[i] & 0xff;
- gamma[i * 2 + size * 0 + 1] = (rgamma[i] >> 8) & 0xff;
- gamma[i * 2 + size * 2 + 0] = ggamma[i] & 0xff;
- gamma[i * 2 + size * 2 + 1] = (ggamma[i] >> 8) & 0xff;
- gamma[i * 2 + size * 4 + 0] = bgamma[i] & 0xff;
- gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff;
- }
-
- /* send address */
- status = gl843_set_buffer_address (dev, 0x0000);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* send data */
- status = sanei_genesys_bulk_write_data(dev, 0x28, gamma.data(), size * 2 * 3);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send gamma table: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/* this function does the led calibration by scanning one line of the calibration
- area below scanner's top on white strip.
-
--needs working coarse/gain
-*/
-static SANE_Status
-gl843_led_calibration (Genesys_Device * dev, Genesys_Sensor& sensor, Genesys_Register_Set& regs)
-{
- int num_pixels;
- int total_size;
- int used_res;
- int i, j;
- SANE_Status status = SANE_STATUS_GOOD;
- int val;
- int channels, depth;
- int avg[3], avga, avge;
- int turn;
- uint16_t expr, expg, expb;
-
- SANE_Bool acceptable = SANE_FALSE;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- /* offset calibration is always done in color mode */
- channels = 3;
- depth = 16;
- used_res = sensor.optical_res;
-
- auto& calib_sensor = sanei_genesys_find_sensor_for_write(dev, used_res,
- dev->settings.scan_method);
- num_pixels =
- (calib_sensor.sensor_pixels * used_res) / calib_sensor.optical_res;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- ScanSession session;
- session.params.xres = used_res;
- session.params.yres = dev->motor.base_ydpi;
- session.params.startx = 0;
- session.params.starty = 0;
- session.params.pixels = num_pixels;
- session.params.lines = 1;
- session.params.depth = depth;
- session.params.channels = channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- session.params.color_filter = dev->settings.color_filter;
- session.params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
- gl843_compute_session(dev, session, calib_sensor);
-
- status = gl843_init_scan_regs(dev, calib_sensor, &regs, session);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- total_size = dev->read_bytes_left;
-
- std::vector<uint8_t> line(total_size);
-
-/*
- we try to get equal bright leds here:
-
- loop:
- average per color
- adjust exposure times
- */
-
- expr = calib_sensor.exposure.red;
- expg = calib_sensor.exposure.green;
- expb = calib_sensor.exposure.blue;
-
- turn = 0;
-
- do
- {
-
- calib_sensor.exposure.red = expr;
- calib_sensor.exposure.green = expg;
- calib_sensor.exposure.blue = expb;
-
- sanei_genesys_set_exposure(regs, calib_sensor.exposure);
-
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- DBG(DBG_info, "%s: starting first line reading\n", __func__);
- RIE (gl843_begin_scan(dev, calib_sensor, &regs, SANE_TRUE));
- RIE (sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
- RIE(gl843_stop_action_no_move(dev, &regs));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl843_led_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, line.data(), depth, channels, num_pixels, 1);
- }
-
- acceptable = SANE_TRUE;
-
- for (j = 0; j < channels; j++)
- {
- avg[j] = 0;
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- line[i * 2 + j * 2 * num_pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- avg[j] += val;
- }
-
- avg[j] /= num_pixels;
- }
-
- DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
-
- acceptable = SANE_TRUE;
-
- if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
- avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
- avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
- acceptable = SANE_FALSE;
-
- if (!acceptable)
- {
- avga = (avg[0] + avg[1] + avg[2]) / 3;
- expr = (expr * avga) / avg[0];
- expg = (expg * avga) / avg[1];
- expb = (expb * avga) / avg[2];
-/*
- keep the resulting exposures below this value.
- too long exposure drives the ccd into saturation.
- we may fix this by relying on the fact that
- we get a striped scan without shading, by means of
- statistical calculation
-*/
- avge = (expr + expg + expb) / 3;
-
- /* don't overflow max exposure */
- if (avge > 3000)
- {
- expr = (expr * 2000) / avge;
- expg = (expg * 2000) / avge;
- expb = (expb * 2000) / avge;
- }
- if (avge < 50)
- {
- expr = (expr * 50) / avge;
- expg = (expg * 50) / avge;
- expb = (expb * 50) / avge;
- }
-
- }
-
- RIE (gl843_stop_action (dev));
-
- turn++;
-
- }
- while (!acceptable && turn < 100);
-
- DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, expr, expg, expb);
-
- sensor.exposure = calib_sensor.exposure;
-
- gl843_slow_back_home (dev, SANE_TRUE);
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
-}
-
-
-
-/**
- * average dark pixels of a 8 bits scan of a given channel
- */
-static int
-dark_average_channel (uint8_t * data, unsigned int pixels, unsigned int lines,
- unsigned int channels, unsigned int black, int channel)
-{
- unsigned int i, j, k, count;
- unsigned int avg[3];
- uint8_t val;
-
- /* computes average values on black margin */
- for (k = 0; k < channels; k++)
- {
- avg[k] = 0;
- count = 0;
- // FIXME: start with the second line because the black pixels often have noise on the first
- // line; the cause is probably incorrectly cleaned up previous scan
- for (i = 1; i < lines; i++)
- {
- for (j = 0; j < black; j++)
- {
- val = data[i * channels * pixels + j*channels + k];
- avg[k] += val;
- count++;
- }
- }
- if (count)
- avg[k] /= count;
- DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
- }
- DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
- return avg[channel];
-}
-
-/** @brief calibrate AFE offset
- * Iterate doing scans at target dpi until AFE offset if correct. One
- * color line is scanned at a time. Scanning head doesn't move.
- * @param dev device to calibrate
- */
-static SANE_Status
-gl843_offset_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- unsigned int channels, bpp;
- int pass, total_size, i, resolution, lines;
- int topavg[3], bottomavg[3], avg[3];
- int top[3], bottom[3], black_pixels, pixels, factor, dpihw;
-
- DBGSTART;
-
- /* offset calibration is always done in color mode */
- channels = 3;
- lines = 8;
- bpp = 8;
-
- /* compute divider factor to compute final pixels number */
- dpihw = sanei_genesys_compute_dpihw_calibration(dev, sensor, dev->settings.xres);
- factor = sensor.optical_res / dpihw;
- resolution = dpihw;
-
- const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution,
- dev->settings.scan_method);
-
- int target_pixels = calib_sensor.sensor_pixels / factor;
- int start_pixel = 0;
- black_pixels = calib_sensor.black_pixels / factor;
-
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY &&
- dev->model->model_id == MODEL_CANON_CANOSCAN_8600F &&
- dev->settings.xres == 4800)
- {
- start_pixel = SANE_UNFIX(dev->model->x_offset_ta);
- start_pixel /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
- start_pixel = (start_pixel * calib_sensor.optical_res) / MM_PER_INCH;
-
- target_pixels = SANE_UNFIX(dev->model->x_size_ta);
- target_pixels /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
- target_pixels = (target_pixels * calib_sensor.optical_res) / MM_PER_INCH;
- }
-
- int flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY)
- {
- flags |= SCAN_FLAG_USE_XPA;
- }
-
- ScanSession session;
- session.params.xres = resolution;
- session.params.yres = resolution;
- session.params.startx = start_pixel;
- session.params.starty = 0;
- session.params.pixels = target_pixels;
- session.params.lines = lines;
- session.params.depth = bpp;
- session.params.channels = channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- session.params.color_filter = ColorFilter::RED;
- session.params.flags = flags;
- gl843_compute_session(dev, session, calib_sensor);
- pixels = session.output_pixels;
-
- DBG(DBG_io, "%s: dpihw =%d\n", __func__, dpihw);
- DBG(DBG_io, "%s: factor =%d\n", __func__, factor);
- DBG(DBG_io, "%s: resolution =%d\n", __func__, resolution);
- DBG(DBG_io, "%s: pixels =%d\n", __func__, pixels);
- DBG(DBG_io, "%s: black_pixels=%d\n", __func__, black_pixels);
- status = gl843_init_scan_regs(dev, calib_sensor, &regs, session);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_set_motor_power(regs, false);
-
- /* allocate memory for scans */
- total_size = dev->read_bytes_left;
-
- std::vector<uint8_t> first_line(total_size);
- std::vector<uint8_t> second_line(total_size);
-
- /* init gain and offset */
- for (i = 0; i < 3; i++)
- {
- bottom[i] = 10;
- dev->frontend.set_offset(i, bottom[i]);
- dev->frontend.set_gain(i, 0);
- }
- RIE(gl843_set_fe(dev, calib_sensor, AFE_SET));
-
- /* scan with obttom AFE settings */
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting first line reading\n", __func__);
- RIE(gl843_begin_scan(dev, calib_sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size));
- RIE(gl843_stop_action_no_move(dev, &regs));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[40];
- snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.pnm",
- bottom[0], bottom[1], bottom[2]);
- sanei_genesys_write_pnm_file(fn, first_line.data(), bpp, channels, pixels, lines);
- }
-
- for (i = 0; i < 3; i++)
- {
- bottomavg[i] = dark_average_channel(first_line.data(), pixels, lines, channels, black_pixels, i);
- DBG(DBG_io2, "%s: bottom avg %d=%d\n", __func__, i, bottomavg[i]);
- }
-
- /* now top value */
- for (i = 0; i < 3; i++)
- {
- top[i] = 255;
- dev->frontend.set_offset(i, top[i]);
- }
- RIE(gl843_set_fe(dev, calib_sensor, AFE_SET));
-
- /* scan with top AFE values */
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl843_begin_scan(dev, calib_sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size));
- RIE(gl843_stop_action_no_move(dev, &regs));
-
- for (i = 0; i < 3; i++)
- {
- topavg[i] = dark_average_channel(second_line.data(), pixels, lines, channels, black_pixels, i);
- DBG(DBG_io2, "%s: top avg %d=%d\n", __func__, i, topavg[i]);
- }
-
- pass = 0;
-
- std::vector<uint8_t> debug_image;
- size_t debug_image_lines = 0;
- std::string debug_image_info;
-
- /* loop until acceptable level */
- while ((pass < 32)
- && ((top[0] - bottom[0] > 1)
- || (top[1] - bottom[1] > 1) || (top[2] - bottom[2] > 1)))
- {
- pass++;
-
- /* settings for new scan */
- for (i = 0; i < 3; i++)
- {
- if (top[i] - bottom[i] > 1)
- {
- dev->frontend.set_offset(i, (top[i] + bottom[i]) / 2);
- }
- }
- RIE(gl843_set_fe(dev, calib_sensor, AFE_SET));
-
- /* scan with no move */
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl843_begin_scan(dev, calib_sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size));
- RIE(gl843_stop_action_no_move(dev, &regs));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char title[100];
- snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
- lines, pixels,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
- debug_image_info += title;
- std::copy(second_line.begin(), second_line.end(), std::back_inserter(debug_image));
- debug_image_lines += lines;
- }
-
- for (i = 0; i < 3; i++)
- {
- avg[i] = dark_average_channel(second_line.data(), pixels, lines, channels, black_pixels, i);
- DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, i, avg[i],
- dev->frontend.get_offset(i));
- }
-
- /* compute new boundaries */
- for (i = 0; i < 3; i++)
- {
- if (topavg[i] >= avg[i])
- {
- topavg[i] = avg[i];
- top[i] = dev->frontend.get_offset(i);
- }
- else
- {
- bottomavg[i] = avg[i];
- bottom[i] = dev->frontend.get_offset(i);
- }
- }
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- sanei_genesys_write_file("gl843_offset_all_desc.txt",
- (uint8_t*) debug_image_info.data(), debug_image_info.size());
- sanei_genesys_write_pnm_file("gl843_offset_all.pnm",
- debug_image.data(), bpp, channels, pixels, debug_image_lines);
- }
-
- DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* alternative coarse gain calibration
- this on uses the settings from offset_calibration and
- uses only one scanline
- */
-/*
- with offset and coarse calibration we only want to get our input range into
- a reasonable shape. the fine calibration of the upper and lower bounds will
- be done with shading.
- */
-static SANE_Status
-gl843_coarse_gain_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi)
-{
- int pixels, factor, dpihw;
- int total_size;
- int i, j, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- int max[3];
- float coeff;
- int val, lines;
- int resolution;
- int bpp;
-
- DBG(DBG_proc, "%s: dpi = %d\n", __func__, dpi);
- dpihw=sanei_genesys_compute_dpihw_calibration(dev, sensor, dpi);
- factor=sensor.optical_res/dpihw;
-
- /* coarse gain calibration is always done in color mode */
- channels = 3;
-
- /* follow CKSEL */
- if (dev->model->ccd_type == CCD_KVSS080)
- {
- if(dev->settings.xres<sensor.optical_res)
- {
- coeff=0.9;
- }
- else
- {
- coeff=1.0;
- }
- }
- else
- {
- coeff=1.0;
- }
- resolution=dpihw;
- lines=10;
- bpp=8;
- int target_pixels = sensor.sensor_pixels / factor;
-
- int flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY)
- {
- flags |= SCAN_FLAG_USE_XPA;
- }
-
- const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution,
- dev->settings.scan_method);
-
- ScanSession session;
- session.params.xres = resolution;
- session.params.yres = resolution;
- session.params.startx = 0;
- session.params.starty = 0;
- session.params.pixels = target_pixels;
- session.params.lines = lines;
- session.params.depth = bpp;
- session.params.channels = channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- session.params.color_filter = dev->settings.color_filter;
- session.params.flags = flags;
- gl843_compute_session(dev, session, calib_sensor);
- pixels = session.output_pixels;
-
- try {
- status = gl843_init_scan_regs(dev, calib_sensor, &regs, session);
- } catch (...) {
- try {
- sanei_genesys_set_motor_power(regs, false);
- } catch (...) {}
- throw;
- }
-
- sanei_genesys_set_motor_power(regs, false);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- total_size = dev->read_bytes_left;
-
- std::vector<uint8_t> line(total_size);
-
- RIE(gl843_set_fe(dev, calib_sensor, AFE_SET));
- RIE(gl843_begin_scan(dev, calib_sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, line.data(), total_size));
- RIE(gl843_stop_action_no_move(dev, &regs));
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl843_gain.pnm", line.data(), bpp, channels, pixels, lines);
-
- /* average value on each channel */
- for (j = 0; j < channels; j++)
- {
- max[j] = 0;
- // FIXME: start from the second line because the first line often has artifacts. Probably
- // caused by unclean cleanup of previous scans
- for (i = pixels/4 + pixels; i < (pixels*3/4) + pixels; i++)
- {
- if(bpp==16)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * pixels + 1] * 256 +
- line[i * 2 + j * 2 * pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- }
- else
- {
- if (dev->model->is_cis)
- val = line[i + j * pixels];
- else
- val = line[i * channels + j];
- }
-
- max[j] += val;
- }
- max[j] = max[j] / (pixels/2);
-
- /* the flow of data through the frontend ADC is as follows (see e.g. VM8192 datasheet)
- input
- -> apply offset (o = i + 260mV * (DAC[7:0]-127.5)/127.5) ->
- -> apply gain (o = i * 208/(283-PGA[7:0])
- -> ADC
-
- Here we have some input data that was acquired with zero gain (PGA==0).
- We want to compute gain such that the output would approach full ADC range (controlled by
- gain_white_ref).
-
- We want to solve the following for {PGA}:
-
- {input} * 208 / (283 - 0) = {output}
- {input} * 208 / (283 - {PGA}) = {target output}
-
- The solution is the following equation:
-
- {PGA} = 283 * (1 - {output} / {target output})
- */
- float gain = ((float) max[j] / (calib_sensor.gain_white_ref*coeff));
- int code = 283 * (1 - gain);
- if (code > 255)
- code = 255;
- else if (code < 0)
- code = 0;
- dev->frontend.set_gain(j, code);
-
- DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain,
- code);
- }
-
- if (dev->model->is_cis) {
- uint8_t gain0 = dev->frontend.get_gain(0);
- if (gain0 > dev->frontend.get_gain(1)) {
- gain0 = dev->frontend.get_gain(1);
- }
- if (gain0 > dev->frontend.get_gain(2)) {
- gain0 = dev->frontend.get_gain(2);
- }
- dev->frontend.set_gain(0, gain0);
- dev->frontend.set_gain(1, gain0);
- dev->frontend.set_gain(2, gain0);
- }
-
- if (channels == 1) {
- dev->frontend.set_gain(0, dev->frontend.get_gain(1));
- dev->frontend.set_gain(2, dev->frontend.get_gain(1));
- }
-
- RIE (gl843_stop_action (dev));
-
- status=gl843_slow_back_home (dev, SANE_TRUE);
-
- DBGCOMPLETED;
- return status;
-}
-
-/*
- * wait for lamp warmup by scanning the same line until difference
- * between 2 scans is below a threshold
- */
-static SANE_Status
-gl843_init_regs_for_warmup (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- int *channels, int *total_size)
-{
- int num_pixels;
- SANE_Status status = SANE_STATUS_GOOD;
- int dpihw;
- int resolution;
- int factor;
-
- DBGSTART;
- if (dev == NULL || reg == NULL || channels == NULL || total_size == NULL)
- return SANE_STATUS_INVAL;
-
- /* setup scan */
- *channels=3;
- resolution=600;
- dpihw=sanei_genesys_compute_dpihw_calibration(dev, sensor, resolution);
- resolution=dpihw;
-
- const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution,
- dev->settings.scan_method);
- factor = calib_sensor.optical_res/dpihw;
- num_pixels = calib_sensor.sensor_pixels/(factor*2);
- *total_size = num_pixels * 3 * 1;
-
- *reg = dev->reg;
-
- ScanSession session;
- session.params.xres = resolution;
- session.params.yres = resolution;
- session.params.startx = num_pixels/2;
- session.params.starty = 0;
- session.params.pixels = num_pixels;
- session.params.lines = 1;
- session.params.depth = 8;
- session.params.channels = *channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- session.params.color_filter = dev->settings.color_filter;
- session.params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
- gl843_compute_session(dev, session, calib_sensor);
-
- status = gl843_init_scan_regs(dev, calib_sensor, reg, session);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- sanei_genesys_set_motor_power(*reg, false);
- RIE(dev->model->cmd_set->bulk_write_register(dev, *reg));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/**
- * set up GPIO/GPOE for idle state
-WRITE GPIO[17-21]= GPIO19
-WRITE GPOE[17-21]= GPOE21 GPOE20 GPOE19 GPOE18
-genesys_write_register(0xa8,0x3e)
-GPIO(0xa8)=0x3e
- */
-static SANE_Status
-gl843_init_gpio (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int idx;
-
- DBGSTART;
-
- RIE (sanei_genesys_write_register (dev, REG6E, dev->gpo.enable[0]));
- RIE (sanei_genesys_write_register (dev, REG6F, dev->gpo.enable[1]));
- RIE (sanei_genesys_write_register (dev, REG6C, dev->gpo.value[0]));
- RIE (sanei_genesys_write_register (dev, REG6D, dev->gpo.value[1]));
-
- idx=0;
- while(dev->model->gpo_type != gpios[idx].gpo_type && gpios[idx].gpo_type!=0)
- {
- idx++;
- }
- if (gpios[idx].gpo_type!=0)
- {
- RIE (sanei_genesys_write_register (dev, REGA6, gpios[idx].ra6));
- RIE (sanei_genesys_write_register (dev, REGA7, gpios[idx].ra7));
- RIE (sanei_genesys_write_register (dev, REGA8, gpios[idx].ra8));
- RIE (sanei_genesys_write_register (dev, REGA9, gpios[idx].ra9));
- }
- else
- {
- status=SANE_STATUS_INVAL;
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/* *
- * initialize ASIC from power on condition
- */
-static SANE_Status
-gl843_boot (Genesys_Device * dev, SANE_Bool cold)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- DBGSTART;
-
- if(cold)
- {
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
- }
-
- if(dev->usb_mode == 1)
- {
- val = 0x14;
- }
- else
- {
- val = 0x11;
- }
- RIE (sanei_genesys_write_0x8c (dev, 0x0f, val));
-
- /* test CHKVER */
- RIE (sanei_genesys_read_register (dev, REG40, &val));
- if (val & REG40_CHKVER)
- {
- RIE (sanei_genesys_read_register (dev, 0x00, &val));
- DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
- }
-
- /* Set default values for registers */
- gl843_init_registers (dev);
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- // turns on vref control for maximum current of the motor driver
- RIE(sanei_genesys_write_register (dev, REG6B, 0x72));
- }
- else
- {
- RIE(sanei_genesys_write_register (dev, REG6B, 0x02));
- }
-
- /* Write initial registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, dev->reg));
-
- // Enable DRAM by setting a rising edge on bit 3 of reg 0x0b
- val = dev->reg.find_reg(0x0b).value & REG0B_DRAMSEL;
- val = (val | REG0B_ENBDRAM);
- RIE (sanei_genesys_write_register (dev, REG0B, val));
- dev->reg.find_reg(0x0b).value = val;
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- RIE (sanei_genesys_write_0x8c (dev, 0x10, 0xc8));
- }
- else
- {
- RIE (sanei_genesys_write_0x8c (dev, 0x10, 0xb4));
- }
-
- /* CLKSET */
- int clock_freq = REG0B_48MHZ;
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- clock_freq = REG0B_60MHZ;
-
- val = (dev->reg.find_reg(0x0b).value & ~REG0B_CLKSET) | clock_freq;
-
- RIE (sanei_genesys_write_register (dev, REG0B, val));
- dev->reg.find_reg(0x0b).value = val;
-
- /* prevent further writings by bulk write register */
- dev->reg.remove_reg(0x0b);
-
- if (dev->model->model_id != MODEL_CANON_CANOSCAN_8600F)
- {
- // set up end access
- // FIXME: this is overwritten in gl843_init_gpio
- sanei_genesys_write_register(dev, REGA7, 0x04);
- sanei_genesys_write_register(dev, REGA9, 0x00);
- }
-
- /* set RAM read address */
- RIE (sanei_genesys_write_register (dev, REG29, 0x00));
- RIE (sanei_genesys_write_register (dev, REG2A, 0x00));
- RIE (sanei_genesys_write_register (dev, REG2B, 0x00));
-
- /* setup gpio */
- RIE (gl843_init_gpio (dev));
-
- gl843_feed (dev, 300);
- sanei_genesys_sleep_ms(100);
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* *
- * initialize backend and ASIC : registers, motor tables, and gamma tables
- * then ensure scanner's head is at home
- */
-static SANE_Status
-gl843_init (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG_INIT ();
- DBGSTART;
-
- status=sanei_genesys_asic_init(dev, 0);
-
- DBGCOMPLETED;
- return status;
-}
-
-static SANE_Status
-gl843_update_hardware_sensors (Genesys_Scanner * s)
-{
- /* do what is needed to get a new set of events, but try to not lose
- any of them.
- */
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- RIE (sanei_genesys_read_register (s->dev, REG6D, &val));
-
- switch (s->dev->model->gpo_type)
- {
- case GPO_KVSS080:
- s->buttons[BUTTON_SCAN_SW].write((val & 0x04) == 0);
- break;
- case GPO_G4050:
- s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0);
- s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0);
- s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0);
- s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0);
- break;
- case GPO_CS4400F:
- case GPO_CS8400F:
- default:
- break;
- }
-
- return status;
-}
-
-/** @brief move sensor to transparency adaptor
- * Move sensor to the calibration of the transparency adapator (XPA).
- * @param dev device to use
- */
-static SANE_Status
-gl843_move_to_ta (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- float resolution;
- unsigned int feed;
-
- DBGSTART;
-
- resolution=sanei_genesys_get_lowest_ydpi(dev);
- feed = 16*(SANE_UNFIX (dev->model->y_offset_calib_ta) * resolution) / MM_PER_INCH;
- status = gl843_feed (dev, feed);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move to XPA calibration area\n", __func__);
- return status;
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/** @brief search for a full width black or white strip.
- * This function searches for a black or white stripe across the scanning area.
- * When searching backward, the searched area must completely be of the desired
- * color since this area will be used for calibration which scans forward.
- * @param dev scanner device
- * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
- * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
- * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
- */
-static SANE_Status
-gl843_search_strip (Genesys_Device * dev, const Genesys_Sensor& sensor,
- SANE_Bool forward, SANE_Bool black)
-{
- unsigned int pixels, lines, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- size_t size;
- int steps, depth, dpi;
- unsigned int pass, count, found, x, y;
- GenesysRegister *r;
-
- DBG(DBG_proc, "%s %s %s\n", __func__, black ? "black" : "white", forward ? "forward" : "reverse");
-
- gl843_set_fe(dev, sensor, AFE_SET);
- status = gl843_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* set up for a gray scan at lowest dpi */
- dpi = sanei_genesys_get_lowest_dpi(dev);
- channels = 1;
-
- const auto& calib_sensor = sanei_genesys_find_sensor(dev, dpi, dev->settings.scan_method);
-
- /* 10 MM */
- /* lines = (10 * dpi) / MM_PER_INCH; */
- /* shading calibation is done with dev->motor.base_ydpi */
- lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
- depth = 8;
- pixels = (calib_sensor.sensor_pixels * dpi) / calib_sensor.optical_res;
-
- dev->scanhead_position_in_steps = 0;
-
- local_reg = dev->reg;
-
- ScanSession session;
- session.params.xres = dpi;
- session.params.yres = dpi;
- session.params.startx = 0;
- session.params.starty = 0;
- session.params.pixels = pixels;
- session.params.lines = lines;
- session.params.depth = depth;
- session.params.channels = channels;
- session.params.scan_method = dev->settings.scan_method;
- session.params.scan_mode = ScanColorMode::GRAY;
- session.params.color_filter = ColorFilter::RED;
- session.params.flags = SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_SHADING;
- gl843_compute_session(dev, session, calib_sensor);
-
- status = gl843_init_scan_regs(dev, calib_sensor, &local_reg, session);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup for scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- size = dev->read_bytes_left;
- std::vector<uint8_t> data(size);
-
- /* set up for reverse or forward */
- r = sanei_genesys_get_address(&local_reg, REG02);
- if (forward)
- r->value &= ~REG02_MTRREV;
- else
- r->value |= REG02_MTRREV;
-
-
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl843_begin_scan(dev, calib_sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl843_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl843_stop_action failed\n", __func__);
- return status;
- }
-
- pass = 0;
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[40];
- snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm",
- black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
- sanei_genesys_write_pnm_file(fn, data.data(), depth, channels, pixels, lines);
- }
-
- /* loop until strip is found or maximum pass number done */
- found = 0;
- while (pass < 20 && !found)
- {
- status =
- dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* now start scan */
- status = gl843_begin_scan(dev, calib_sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl843_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl843_stop_action failed\n", __func__);
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[40];
- snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm",
- black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
- sanei_genesys_write_pnm_file(fn, data.data(), depth, channels, pixels, lines);
- }
-
- /* search data to find black strip */
- /* when searching forward, we only need one line of the searched color since we
- * will scan forward. But when doing backward search, we need all the area of the
- * same color */
- if (forward)
- {
- for (y = 0; y < lines && !found; y++)
- {
- count = 0;
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > 90)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < 60)
- {
- count++;
- }
- }
-
- /* at end of line, if count >= 3%, line is not fully of the desired color
- * so we must go to next line of the buffer */
- /* count*100/pixels < 3 */
- if ((count * 100) / pixels < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
- pass, y);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- }
- else /* since calibration scans are done forward, we need the whole area
- to be of the required color when searching backward */
- {
- count = 0;
- for (y = 0; y < lines; y++)
- {
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > 90)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < 60)
- {
- count++;
- }
- }
- }
-
- /* at end of area, if count >= 3%, area is not fully of the desired color
- * so we must go to next buffer */
- if ((count * 100) / (pixels * lines) < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- pass++;
- }
- if (found)
- {
- status = SANE_STATUS_GOOD;
- DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
- }
- else
- {
- status = SANE_STATUS_UNSUPPORTED;
- DBG(DBG_info, "%s: %s strip not found\n", __func__, black ? "black" : "white");
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return status;
-}
-
-/**
- * Send shading calibration data. The buffer is considered to always hold values
- * for all the channels.
- */
-static SANE_Status
-gl843_send_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data, int size)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint32_t final_size, length, i;
- uint8_t *buffer;
- int count,offset;
- unsigned int cksel;
- GenesysRegister *r;
- uint16_t dpiset, strpixel, endpixel, startx, factor;
-
- DBGSTART;
-
- offset=0;
- length=size;
- r = sanei_genesys_get_address(&dev->reg, REG01);
- if(r->value & REG01_SHDAREA)
- {
- /* recompute STRPIXEL used shading calibration so we can
- * compute offset within data for SHDAREA case */
- r = sanei_genesys_get_address(&dev->reg, REG18);
- cksel= (r->value & REG18_CKSEL)+1;
- sanei_genesys_get_double(&dev->reg,REG_DPISET,&strpixel);
- sanei_genesys_get_double(&dev->reg,REG_DPISET,&dpiset);
- factor=sensor.optical_res/sanei_genesys_compute_dpihw(dev, sensor, dpiset);
-
- /* start coordinate in optical dpi coordinates */
- startx = (sensor.dummy_pixel / cksel) / factor;
-
- /* current scan coordinates */
- sanei_genesys_get_double(&dev->reg,REG_STRPIXEL,&strpixel);
- sanei_genesys_get_double(&dev->reg,REG_ENDPIXEL,&endpixel);
-
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- int optical_res = sensor.optical_res / dev->current_setup.ccd_size_divisor;
- int dpiset_real = dpiset / dev->current_setup.ccd_size_divisor;
- int half_ccd_factor = optical_res /
- sanei_genesys_compute_dpihw_calibration(dev, sensor, dpiset_real);
- strpixel /= half_ccd_factor;
- endpixel /= half_ccd_factor;
- }
-
- /* 16 bit words, 2 words per color, 3 color channels */
- offset=(strpixel-startx)*2*2*3;
- length=(endpixel-strpixel)*2*2*3;
- DBG(DBG_info, "%s: STRPIXEL=%d, ENDPIXEL=%d, startx=%d\n", __func__, strpixel, endpixel,
- startx);
- }
-
- /* compute and allocate size for final data */
- final_size = ((length+251) / 252) * 256;
- DBG(DBG_io, "%s: final shading size=%04x (length=%d)\n", __func__, final_size, length);
- std::vector<uint8_t> final_data(final_size, 0);
-
- /* copy regular shading data to the expected layout */
- buffer = final_data.data();
- count = 0;
-
- /* loop over calibration data */
- for (i = 0; i < length; i++)
- {
- buffer[count] = data[offset+i];
- count++;
- if ((count % (256*2)) == (252*2))
- {
- count += 4*2;
- }
- }
-
- /* send data */
- status = sanei_genesys_set_buffer_address (dev, 0);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set buffer address: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = dev->model->cmd_set->bulk_write_data (dev, 0x3c, final_data.data(), count);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to send shading table: %s\n", __func__, sane_strstatus(status));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/** the gl843 command set */
-static Genesys_Command_Set gl843_cmd_set = {
- "gl843-generic", /* the name of this set */
-
- [](Genesys_Device* dev) -> bool { (void) dev; return true; },
-
- gl843_init,
- gl843_init_regs_for_warmup,
- gl843_init_regs_for_coarse_calibration,
- gl843_init_regs_for_shading,
- gl843_init_regs_for_scan,
-
- gl843_get_filter_bit,
- gl843_get_lineart_bit,
- gl843_get_bitset_bit,
- gl843_get_gain4_bit,
- gl843_get_fast_feed_bit,
- gl843_test_buffer_empty_bit,
- gl843_test_motor_flag_bit,
-
- gl843_set_fe,
- gl843_set_powersaving,
- gl843_save_power,
-
- gl843_begin_scan,
- gl843_end_scan,
-
- gl843_send_gamma_table,
-
- gl843_search_start_position,
-
- gl843_offset_calibration,
- gl843_coarse_gain_calibration,
- gl843_led_calibration,
-
- NULL,
- gl843_slow_back_home,
- NULL,
-
- sanei_genesys_bulk_write_register,
- sanei_genesys_bulk_write_data,
- sanei_genesys_bulk_read_data,
-
- gl843_update_hardware_sensors,
-
- gl843_load_document,
- gl843_detect_document_end,
- gl843_eject_document,
- gl843_search_strip,
-
- sanei_genesys_is_compatible_calibration,
- gl843_move_to_ta,
- gl843_send_shading_data,
- gl843_calculate_current_setup,
- gl843_boot
-};
-
-SANE_Status
-sanei_gl843_init_cmd_set (Genesys_Device * dev)
-{
- dev->model->cmd_set = &gl843_cmd_set;
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_gl843.h b/backend/genesys_gl843.h
deleted file mode 100644
index 7651cc9..0000000
--- a/backend/genesys_gl843.h
+++ /dev/null
@@ -1,472 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#include "genesys.h"
-
-#define REG01 0x01
-#define REG01_CISSET 0x80
-#define REG01_DOGENB 0x40
-#define REG01_DVDSET 0x20
-#define REG01_STAGGER 0x10
-#define REG01_COMPENB 0x08
-#define REG01_TRUEGRAY 0x04
-#define REG01_SHDAREA 0x02
-#define REG01_SCAN 0x01
-
-#define REG02 0x02
-#define REG02_NOTHOME 0x80
-#define REG02_ACDCDIS 0x40
-#define REG02_AGOHOME 0x20
-#define REG02_MTRPWR 0x10
-#define REG02_FASTFED 0x08
-#define REG02_MTRREV 0x04
-#define REG02_HOMENEG 0x02
-#define REG02_LONGCURV 0x01
-
-#define REG03 0x03
-#define REG03_LAMPDOG 0x80
-#define REG03_AVEENB 0x40
-#define REG03_XPASEL 0x20
-#define REG03_LAMPPWR 0x10
-#define REG03_LAMPTIM 0x0f
-
-#define REG04 0x04
-#define REG04_LINEART 0x80
-#define REG04_BITSET 0x40
-#define REG04_AFEMOD 0x30
-#define REG04_FILTER 0x0c
-#define REG04_FESET 0x03
-
-#define REG04S_AFEMOD 4
-
-#define REG05 0x05
-#define REG05_DPIHW 0xc0
-#define REG05_DPIHW_600 0x00
-#define REG05_DPIHW_1200 0x40
-#define REG05_DPIHW_2400 0x80
-#define REG05_DPIHW_4800 0xc0
-#define REG05_MTLLAMP 0x30
-#define REG05_GMMENB 0x08
-#define REG05_MTLBASE 0x03
-
-#define REG06 0x06
-#define REG06_SCANMOD 0xe0
-#define REG06S_SCANMOD 5
-#define REG06_PWRBIT 0x10
-#define REG06_GAIN4 0x08
-#define REG06_OPTEST 0x07
-
-#define REG07_LAMPSIM 0x80
-
-#define REG08_DECFLAG 0x40
-#define REG08_GMMFFR 0x20
-#define REG08_GMMFFG 0x10
-#define REG08_GMMFFB 0x08
-#define REG08_GMMZR 0x04
-#define REG08_GMMZG 0x02
-#define REG08_GMMZB 0x01
-
-#define REG09_MCNTSET 0xc0
-#define REG09_EVEN1ST 0x20
-#define REG09_BLINE1ST 0x10
-#define REG09_BACKSCAN 0x08
-#define REG09_ENHANCE 0x04
-#define REG09_SHORTTG 0x02
-#define REG09_NWAIT 0x01
-
-#define REG09S_MCNTSET 6
-#define REG09S_CLKSET 4
-
-#define REG0B 0x0b
-#define REG0B_DRAMSEL 0x07
-#define REG0B_ENBDRAM 0x08
-#define REG0B_ENBDRAM 0x08
-#define REG0B_RFHDIS 0x10
-#define REG0B_CLKSET 0xe0
-#define REG0B_24MHZ 0x00
-#define REG0B_30MHZ 0x20
-#define REG0B_40MHZ 0x40
-#define REG0B_48MHZ 0x60
-#define REG0B_60MHZ 0x80
-
-#define REG0D 0x0d
-#define REG0D_JAMPCMD 0x80
-#define REG0D_DOCCMD 0x40
-#define REG0D_CCDCMD 0x20
-#define REG0D_FULLSTP 0x10
-#define REG0D_SEND 0x08
-#define REG0D_CLRMCNT 0x04
-#define REG0D_CLRDOCJM 0x02
-#define REG0D_CLRLNCNT 0x01
-
-#define REG0F 0x0f
-
-#define REG_EXPR 0x10
-#define REG_EXPG 0x12
-#define REG_EXPB 0x14
-
-#define REG16_CTRLHI 0x80
-#define REG16_TOSHIBA 0x40
-#define REG16_TGINV 0x20
-#define REG16_CK1INV 0x10
-#define REG16_CK2INV 0x08
-#define REG16_CTRLINV 0x04
-#define REG16_CKDIS 0x02
-#define REG16_CTRLDIS 0x01
-
-#define REG17_TGMODE 0xc0
-#define REG17_TGMODE_NO_DUMMY 0x00
-#define REG17_TGMODE_REF 0x40
-#define REG17_TGMODE_XPA 0x80
-#define REG17_TGW 0x3f
-#define REG17S_TGW 0
-
-#define REG18 0x18
-#define REG18_CNSET 0x80
-#define REG18_DCKSEL 0x60
-#define REG18_CKTOGGLE 0x10
-#define REG18_CKDELAY 0x0c
-#define REG18_CKSEL 0x03
-
-#define REG_EXPDMY 0x19
-
-#define REG1A_TGLSW2 0x80
-#define REG1A_TGLSW1 0x40
-#define REG1A_MANUAL3 0x02
-#define REG1A_MANUAL1 0x01
-#define REG1A_CK4INV 0x08
-#define REG1A_CK3INV 0x04
-#define REG1A_LINECLP 0x02
-
-#define REG1C 0x1c
-#define REG1C_TGTIME 0x07
-
-#define REG1D_CK4LOW 0x80
-#define REG1D_CK3LOW 0x40
-#define REG1D_CK1LOW 0x20
-#define REG1D_TGSHLD 0x1f
-#define REG1DS_TGSHLD 0
-
-
-#define REG1E 0x1e
-#define REG1E_WDTIME 0xf0
-#define REG1ES_WDTIME 4
-#define REG1E_LINESEL 0x0f
-#define REG1ES_LINESEL 0
-
-#define REG21 0x21
-#define REG_STEPNO 0x21
-#define REG_FWDSTEP 0x22
-#define REG_BWDSTEP 0x23
-#define REG_FASTNO 0x24
-#define REG_LINCNT 0x25
-
-#define REG29 0x29
-#define REG2A 0x2a
-#define REG2B 0x2b
-#define REG_DPISET 0x2c
-#define REG2E 0x2e
-#define REG2F 0x2f
-
-#define REG_STRPIXEL 0x30
-#define REG_ENDPIXEL 0x32
-#define REG_DUMMY 0x34
-#define REG_MAXWD 0x35
-#define REG_LPERIOD 0x38
-#define REG_FEEDL 0x3d
-
-#define REG40 0x40
-#define REG40_DOCSNR 0x80
-#define REG40_ADFSNR 0x40
-#define REG40_COVERSNR 0x20
-#define REG40_CHKVER 0x10
-#define REG40_DOCJAM 0x08
-#define REG40_HISPDFLG 0x04
-#define REG40_MOTMFLG 0x02
-#define REG40_DATAENB 0x01
-
-#define REG41_PWRBIT 0x80
-#define REG41_BUFEMPTY 0x40
-#define REG41_FEEDFSH 0x20
-#define REG41_SCANFSH 0x10
-#define REG41_HOMESNR 0x08
-#define REG41_LAMPSTS 0x04
-#define REG41_FEBUSY 0x02
-#define REG41_MOTORENB 0x01
-
-#define REG58_VSMP 0xf8
-#define REG58S_VSMP 3
-#define REG58_VSMPW 0x07
-#define REG58S_VSMPW 0
-
-#define REG59_BSMP 0xf8
-#define REG59S_BSMP 3
-#define REG59_BSMPW 0x07
-#define REG59S_BSMPW 0
-
-#define REG5A_ADCLKINV 0x80
-#define REG5A_RLCSEL 0x40
-#define REG5A_CDSREF 0x30
-#define REG5AS_CDSREF 4
-#define REG5A_RLC 0x0f
-#define REG5AS_RLC 0
-
-#define REG5E 0x5e
-#define REG5E_DECSEL 0xe0
-#define REG5ES_DECSEL 5
-#define REG5E_STOPTIM 0x1f
-#define REG5ES_STOPTIM 0
-
-#define REG_FMOVDEC 0x5f
-
-#define REG60 0x60
-#define REG60_Z1MOD 0x1f
-#define REG61 0x61
-#define REG61_Z1MOD 0xff
-#define REG62 0x62
-#define REG62_Z1MOD 0xff
-
-#define REG63 0x63
-#define REG63_Z2MOD 0x1f
-#define REG64 0x64
-#define REG64_Z2MOD 0xff
-#define REG65 0x65
-#define REG65_Z2MOD 0xff
-
-#define REG67 0x67
-
-#define REG68 0x68
-
-#define REG67S_STEPSEL 6
-#define REG67_STEPSEL 0xc0
-#define REG67_FULLSTEP 0x00
-#define REG67_HALFSTEP 0x20
-#define REG67_EIGHTHSTEP 0x60
-#define REG67_16THSTEP 0x80
-
-#define REG68S_FSTPSEL 6
-#define REG68_FSTPSEL 0xc0
-#define REG68_FULLSTEP 0x00
-#define REG68_HALFSTEP 0x20
-#define REG68_EIGHTHSTEP 0x60
-#define REG68_16THSTEP 0x80
-
-#define REG_FSHDEC 0x69
-#define REG_FMOVNO 0x6a
-
-#define REG6B 0x6b
-#define REG6B_MULTFILM 0x80
-#define REG6B_GPOM13 0x40
-#define REG6B_GPOM12 0x20
-#define REG6B_GPOM11 0x10
-#define REG6B_GPOCK4 0x08
-#define REG6B_GPOCP 0x04
-#define REG6B_GPOLEDB 0x02
-#define REG6B_GPOADF 0x01
-
-#define REG6C 0x6c
-#define REG6C_GPIO16 0x80
-#define REG6C_GPIO15 0x40
-#define REG6C_GPIO14 0x20
-#define REG6C_GPIO13 0x10
-#define REG6C_GPIO12 0x08
-#define REG6C_GPIO11 0x04
-#define REG6C_GPIO10 0x02
-#define REG6C_GPIO9 0x01
-#define REG6C_GPIOH 0xff
-#define REG6C_GPIOL 0xff
-
-#define REG_Z1MOD 0x60
-#define REG_Z2MOD 0x63
-
-#define REG6D 0x6d
-#define REG6E 0x6e
-#define REG6F 0x6f
-
-#define REG_CK1MAP 0x74
-#define REG_CK3MAP 0x77
-#define REG_CK4MAP 0x7a
-
-#define REG7E 0x7e
-
-#define REG9D 0x9d
-#define REG9DS_STEPTIM 2
-
-#define REG87_LEDADD 0x04
-
-#define REGA6 0xa6
-#define REGA6_GPIO24 0x80
-#define REGA6_GPIO23 0x40
-#define REGA6_GPIO22 0x20
-#define REGA6_GPIO21 0x10
-#define REGA6_GPIO20 0x08
-#define REGA6_GPIO19 0x04
-#define REGA6_GPIO18 0x02
-#define REGA6_GPIO17 0x01
-#define REGA7 0xa7
-#define REGA7_GPOE24 0x80
-#define REGA7_GPOE23 0x40
-#define REGA7_GPOE22 0x20
-#define REGA7_GPOE21 0x10
-#define REGA7_GPOE20 0x08
-#define REGA7_GPOE19 0x04
-#define REGA7_GPOE18 0x02
-#define REGA7_GPOE17 0x01
-#define REGA8 0xa8
-#define REGA8_GPOE27 0x20
-#define REGA8_GPOE26 0x10
-#define REGA8_GPOE25 0x08
-#define REGA8_GPO27 0x04
-#define REGA8_GPO26 0x02
-#define REGA8_GPO25 0x01
-#define REGA9 0xa9
-#define REGA9_GPO33 0x20
-#define REGA9_GPO32 0x10
-#define REGA9_GPO31 0x08
-#define REGA9_GPO30 0x04
-#define REGA9_GPO29 0x02
-#define REGA9_GPO28 0x01
-
-#define SCAN_TABLE 0 /* table 1 at 0x4000 */
-#define BACKTRACK_TABLE 1 /* table 2 at 0x4800 */
-#define STOP_TABLE 2 /* table 3 at 0x5000 */
-#define FAST_TABLE 3 /* table 4 at 0x5800 */
-#define HOME_TABLE 4 /* table 5 at 0x6000 */
-
-#define SCAN_FLAG_SINGLE_LINE 0x001
-#define SCAN_FLAG_DISABLE_SHADING 0x002
-#define SCAN_FLAG_DISABLE_GAMMA 0x004
-#define SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE 0x008
-#define SCAN_FLAG_IGNORE_LINE_DISTANCE 0x010
-#define SCAN_FLAG_USE_OPTICAL_RES 0x020
-#define SCAN_FLAG_DISABLE_LAMP 0x040
-#define SCAN_FLAG_DYNAMIC_LINEART 0x080
-
-#define SETREG(adr,val) { dev->reg.init_reg(adr, val); }
-
-typedef struct
-{
- SANE_Int gpo_type;
- uint8_t ra6;
- uint8_t ra7;
- uint8_t ra8;
- uint8_t ra9;
-} Gpio_layout;
-
-static Gpio_layout gpios[]={
- /* G4050 */
- {
- GPO_G4050, 0x08, 0x1e, 0x3e, 0x06
- },
- /* KV-SS080 */
- {
- GPO_KVSS080, 0x06, 0x0f, 0x00, 0x08
- },
- /* 4400F */
- {
- GPO_CS4400F, 0x00, 0xff, 0x07, 0x00
- },
- /* 8400F */
- {
- GPO_CS8400F, 0x00, 0x03, 0x00, 0x02
- },
- {
- GPO_CS8600F, 0x00, 0xff, 0x00, 0x00,
- },
- /* end marker */
- {
- 0, 0, 0, 0, 0
- },
-};
-
-
-static uint32_t kvss080[]={44444, 34188, 32520, 29630, 26666, 24242, 22222, 19048, 16666, 15686, 14814, 14034, 12402, 11110, 8888, 7618, 6666, 5926, 5228, 4678, 4172, 3682, 3336, 3074, 2866, 2702, 2566, 2450, 2352, 2266, 2188, 2118, 2056, 2002, 1950, 1904, 1860, 1820, 1784, 1748, 1716, 1684, 1656, 1628, 1600, 1576, 1552, 1528, 1506, 1486, 1466, 1446, 1428, 1410, 1394, 1376, 1360, 1346, 1330, 1316, 1302, 1288, 1276, 1264, 1250, 1238, 1228, 1216, 1206, 1194, 1184, 1174, 1164, 1154, 1146, 1136, 1128, 1120, 1110, 1102, 1094, 1088, 1080, 1072, 1064, 1058, 1050, 1044, 1038, 1030, 1024, 1018, 1012, 1006, 1000, 994, 988, 984, 978, 972, 968, 962, 958, 952, 948, 942, 938, 934, 928, 924, 920, 916, 912, 908, 904, 900, 896, 892, 888, 884, 882, 878, 874, 870, 868, 864, 860, 858, 854, 850, 848, 844, 842, 838, 836, 832, 830, 826, 824, 822, 820, 816, 814, 812, 808, 806, 804, 802, 800, 796, 794, 792, 790, 788, 786, 784, 782, 778, 776, 774, 772, 770, 768, 766, 764, 762, 760, 758, 756, 754, 752, 750, 750, 748, 746, 744, 742, 740, 738, 736, 734, 734, 732, 730, 728, 726, 724, 724, 722, 720, 718, 716, 716, 714, 712, 710, 710, 708, 706, 704, 704, 702, 700, 698, 698, 696, 694, 694, 692, 690, 690, 688, 686, 686, 684, 682, 682, 680, 678, 678, 676, 674, 674, 672, 672, 670, 668, 668, 666, 666, 664, 662, 662, 660, 660, 658, 656, 656, 654, 654, 652, 652, 650, 650, 648, 646, 646, 644, 644, 642, 642, 640, 640, 638, 638, 636, 636, 636, 634, 634, 632, 632, 630, 630, 628, 628, 626, 626, 624, 624, 624, 622, 622, 620, 620, 618, 618, 618, 616, 616, 614, 614, 612, 612, 612, 610, 610, 608, 608, 608, 606, 606, 606, 604, 604, 602, 602, 602, 600, 600, 600, 598, 598, 596, 596, 596, 594, 594, 594, 592, 592, 592, 590, 590, 590, 588, 588, 588, 586, 586, 586, 584, 584, 584, 582, 582, 582, 590, 590, 590, 588, 588, 588, 586, 586, 586, 584, 584, 584, 582, 582, 582, 580, 580, 580, 578, 578, 578, 576, 576, 576, 576, 574, 574, 574, 572, 572, 572, 570, 570, 570, 568, 568, 568, 568, 566, 566, 566, 564, 564, 564, 562, 562, 562, 562, 560, 560, 560, 558, 558, 558, 558, 556, 556, 556, 554, 554, 554, 552, 552, 552, 552, 550, 550, 550, 548, 548, 548, 548, 546, 546, 546, 546, 544, 544, 544, 542, 542, 542, 542, 540, 540, 540, 538, 538, 538, 538, 536, 536, 536, 536, 534, 534, 534, 534, 532, 532, 532, 530, 530, 530, 530, 528, 528, 528, 528, 526, 526, 526, 526, 524, 524, 524, 524, 522, 522, 522, 522, 520, 520, 520, 520, 518, 518, 518, 516, 516, 516, 516, 514, 514, 514, 514, 514, 512, 512, 512, 512, 510, 510, 510, 510, 508, 508, 508, 508, 506, 506, 506, 506, 504, 504, 504, 504, 502, 502, 502, 502, 500, 500, 500, 500, 0};
-static uint32_t g4050_fast[]={7842,5898,4384,4258,4152,4052,3956,3864,3786,3714,3632,3564,3498,3444,3384,3324,3276,3228,3174,3128,3086,3044,3002,2968,2930,2892,2860,2824,2794,2760,2732,2704,2676,2650,2618,2594,2568,2548,2524,2500,2478,2454,2436,2414,2392,2376,2354,2338,2318,2302,2282,2266,2252,2232,2218,2202,2188,2174,2160,2142,2128,2116,2102,2088,2076,2062,2054,2040,2028,2020,2014,2008,2004,2002,2002,2002,1946,1882,1826,1770,1716,1662,1612,1568,1526,1488,1454,1422,1390,1362,1336,1310,1288,1264,1242,1222,1204,1184,1166,1150,1134,1118,1104,1090,1076,1064,1050,1038,1026,1016,1004,994,984,972,964,954,944,936,928,920,910,902,896,888,880,874,866,860,854,848,840,834,828,822,816,812,806,800,796,790,784,780,776,770,766,760,756,752,748,744,740,736,732,728,724,720,716,712,708,704,702,698,694,690,688,684,682,678,674,672,668,666,662,660,656,654,650,648,646,644,640,638,636,632,630,628,624,622,620,618,616,614,610,608,606,604,602,600,598,596,594,592,590,588,586,584,582,580,578,576,574,572,570,568,566,564,564,562,560,558,556,554,552,552,550,548,546,546,544,542,540,538,538,536,534,532,532,530,528,528,526,524,522,522,520,518,518,516,514,514,512,512,510,508,508,506,504,504,502,502,500,498,498,496,496,494,494,492,490,490,488,488,486,486,484,484,482,480,480,478,478,476,476,474,474,472,472,470,470,468,468,468,466,466,464,464,462,462,460,460,458,458,456,456,456,454,454,452,452,450,450,450,448,448,446,446,444,444,444,442,442,440,440,440,438,438,438,436,436,434,434,434,432,432,432,430,430,428,428,428,426,426,426,424,424,424,422,422,422,420,420,420,418,418,418,416,416,416,414,414,414,412,412,412,410,410,410,408,408,408,406,406,406,404,404,404,404,402,402,402,400,400,400,400,398,398,398,396,396,396,396,394,394,394,392,392,392,392,390,390,390,388,388,388,388,386,386,386,386,384,384,384,384,382,382,382,382,380,380,380,380,378,378,378,378,376,376,376,376,376,374,374,374,374,374,372,372,372,372,372,370,370,370,370,370,368,368,368,368,368,366,366,366,366,366,364,364,364,364,364,364,362,362,362,362,362,360,360,360,360,360,360,358,358,358,358,358,358,356,356,356,356,356,356,354,354,354,354,354,352,352,352,352,352,352,350,350,350,350,350,350,350,348,348,348,348,348,348,346,346,346,346,346,346,344,344,344,344,344,344,344,342,342,342,342,342,342,340,340,340,340,340,340,340,338,338,338,338,338,338,338,336,336,336,336,336,336,336,334,334,334,334,334,334,334,332,332,332,332,332,332,332,332,330,330,330,330,330,330,330,328,328,328,328,328,328,328,328,326,326,326,326,326,326,326,324,324,324,324,324,324,324,324,322,322,322,322,322,322,322,322,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320, 0};
-static uint32_t g4050_high[]={28032,28032,28032,28032,28032,28032,28032,28032, 27668,27024,26479,25975,25402,24926,24465,24087,23667,23248,22912,22576,22198,21877,21583,21289,20996,20758,20492,20226,20002,19751,19541,19303,19107,18911,18715,18534,18310,18142,17960,17820,17652,17485,17331,17163,17037,16883,16729,16617,16463,16352,16212,16100,15960,15848,15750,15610,15512,15400,15302,15204,15107,14981,14883,14799,14701,14603,14519,14421,14365,14267,14183,14127,14085,14043,14016,14002,14002,14002,13610,13162,12771,12379,12001,11624,11274,10966,10672,10407,10169,9945,9721,9525,9344,9162,9008,8840,8686,8546,8420,8280,8155,8043,7931,7819,7721,7623,7525,7441,7343,7259,7175,7105,7021,6952,6882,6798,6742,6672,6602,6546,6490,6434,6364,6308,6266,6210,6154,6112,6056,6014,5972,5930,5874,5833,5791,5749,5707,5679,5637,5595,5567,5525,5483,5455,5427,5385,5357,5315,5287,5259,5231,5203,5175,5147,5119,5091,5063,5035,5007,4979,4951,4923,4909,4881,4853,4825,4811,4783,4769,4741,4713,4699,4672,4658,4630,4616,4588,4574,4546,4532,4518,4504,4476,4462,4448,4420,4406,4392,4364,4350,4336,4322,4308,4294,4266,4252,4238,4224,4210,4196,4182,4168,4154,4140,4126,4112,4098,4084,4070,4056,4042,4028,4014,4000,3986,3972,3958,3944,3944,3930,3916,3902,3888,3874,3860,3860,3846,3832,3818,3818,3804,3790,3776,3762,3762,3748,3734,3720,3720,3706,3692,3692,3678,3664,3650,3650,3636,3622,3622,3608,3594,3594,3580,3580,3566,3552,3552,3538,3524,3524,3510,3510,3497,3483,3483,3469,3469,3455,3455,3441,3427,3427,3413,3413,3399,3399,3385,3385,3371,3357,3357,3343,3343,3329,3329,3315,3315,3301,3301,3287,3287,3273,3273,3273,3259,3259,3245,3245,3231,3231,3217,3217,3203,3203,3189,3189,3189,3175,3175,3161,3161,3147,3147,3147,3133,3133,3119,3119,3105,3105,3105,3091,3091,3077,3077,3077,3063,3063,3063,3049,3049,3035,3035,3035,3021,3021,3021,3007,3007,2993,2993,2993,2979,2979,2979,2965,2965,2965,2951,2951,2951,2937,2937,2937,2923,2923,2923,2909,2909,2909,2895,2895,2895,2881,2881,2881,2867,2867,2867,2853,2853,2853,2839,2839,2839,2825,2825,2825,2825,2811,2811,2811,2797,2797,2797,2797,2783,2783,2783,2769,2769,2769,2769,2755,2755,2755,2741,2741,2741,2741,2727,2727,2727,2713,2713,2713,2713,2699,2699,2699,2699,2685,2685,2685,2685,2671,2671,2671,2671,2657,2657,2657,2657,2643,2643,2643,2643,2629,2629,2629,2629,2629,2615,2615,2615,2615,2615,2601,2601,2601,2601,2601,2587,2587,2587,2587,2587,2573,2573,2573,2573,2573,2559,2559,2559,2559,2559,2545,2545,2545,2545,2545,2545,2531,2531,2531,2531,2531,2517,2517,2517,2517,2517,2517,2503,2503,2503,2503,2503,2503,2489,2489,2489,2489,2489,2489,2475,2475,2475,2475,2475,2461,2461,2461,2461,2461,2461,2447,2447,2447,2447,2447,2447,2447,2433,2433,2433,2433,2433,2433,2419,2419,2419,2419,2419,2419,2405,2405,2405,2405,2405,2405,2405,2391,2391,2391,2391,2391,2391,2377,2377,2377,2377,2377,2377,2377,2363,2363,2363,2363,2363,2363,2363,2349,2349,2349,2349,2349,2349,2349,2336,2336,2336,2336,2336,2336,2336,2322,2322,2322,2322,2322,2322,2322,2322,2308,2308,2308,2308,2308,2308,2308,2294,2294,2294,2294,2294,2294,2294,2294,2280,2280,2280,2280,2280,2280,2280,2266,2266,2266,2266,2266,2266,2266,2266,2252,2252,2252,2252,2252,2252,2252,2252,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238,2238, 0};
-static uint32_t g4050_max[]={42752,42752,42752,42752,42752,42752,42752,42752, 41824,31456,23381,22709,22144,21610,21098,20608,20192,19808,19370,19008,18656,18368,18048,17728,17472,17216,16928,16682,16458,16234,16010,15829,15626,15424,15253,15061,14901,14720,14570,14421,14272,14133,13962,13834,13696,13589,13461,13333,13216,13088,12992,12874,12757,12672,12554,12469,12362,12277,12170,12085,12010,11904,11829,11744,11669,11594,11520,11424,11349,11285,11210,11136,11072,10997,10954,10880,10816,10773,10741,10709,10688,10677,10677,10677,10378,10037,9738,9440,9152,8864,8597,8362,8138,7936,7754,7584,7413,7264,7125,6986,6869,6741,6624,6517,6421,6314,6218,6133,6048,5962,5888,5813,5738,5674,5600,5536,5472,5418,5354,5301,5248,5184,5141,5088,5034,4992,4949,4906,4853,4810,4778,4736,4693,4661,4618,4586,4554,4522,4480,4448,4416,4384,4352,4330,4298,4266,4245,4213,4181,4160,4138,4106,4085,4053,4032,4010,3989,3968,3946,3925,3904,3882,3861,3840,3818,3797,3776,3754,3744,3722,3701,3680,3669,3648,3637,3616,3594,3584,3562,3552,3530,3520,3498,3488,3466,3456,3445,3434,3413,3402,3392,3370,3360,3349,3328,3317,3306,3296,3285,3274,3253,3242,3232,3221,3210,3200,3189,3178,3168,3157,3146,3136,3125,3114,3104,3093,3082,3072,3061,3050,3040,3029,3018,3008,3008,2997,2986,2976,2965,2954,2944,2944,2933,2922,2912,2912,2901,2890,2880,2869,2869,2858,2848,2837,2837,2826,2816,2816,2805,2794,2784,2784,2773,2762,2762,2752,2741,2741,2730,2730,2720,2709,2709,2698,2688,2688,2677,2677,2666,2656,2656,2645,2645,2634,2634,2624,2613,2613,2602,2602,2592,2592,2581,2581,2570,2560,2560,2549,2549,2538,2538,2528,2528,2517,2517,2506,2506,2496,2496,2496,2485,2485,2474,2474,2464,2464,2453,2453,2442,2442,2432,2432,2432,2421,2421,2410,2410,2400,2400,2400,2389,2389,2378,2378,2368,2368,2368,2357,2357,2346,2346,2346,2336,2336,2336,2325,2325,2314,2314,2314,2304,2304,2304,2293,2293,2282,2282,2282,2272,2272,2272,2261,2261,2261,2250,2250,2250,2240,2240,2240,2229,2229,2229,2218,2218,2218,2208,2208,2208,2197,2197,2197,2186,2186,2186,2176,2176,2176,2165,2165,2165,2154,2154,2154,2154,2144,2144,2144,2133,2133,2133,2133,2122,2122,2122,2112,2112,2112,2112,2101,2101,2101,2090,2090,2090,2090,2080,2080,2080,2069,2069,2069,2069,2058,2058,2058,2058,2048,2048,2048,2048,2037,2037,2037,2037,2026,2026,2026,2026,2016,2016,2016,2016,2005,2005,2005,2005,2005,1994,1994,1994,1994,1994,1984,1984,1984,1984,1984,1973,1973,1973,1973,1973,1962,1962,1962,1962,1962,1952,1952,1952,1952,1952,1941,1941,1941,1941,1941,1941,1930,1930,1930,1930,1930,1920,1920,1920,1920,1920,1920,1909,1909,1909,1909,1909,1909,1898,1898,1898,1898,1898,1898,1888,1888,1888,1888,1888,1877,1877,1877,1877,1877,1877,1866,1866,1866,1866,1866,1866,1866,1856,1856,1856,1856,1856,1856,1845,1845,1845,1845,1845,1845,1834,1834,1834,1834,1834,1834,1834,1824,1824,1824,1824,1824,1824,1813,1813,1813,1813,1813,1813,1813,1802,1802,1802,1802,1802,1802,1802,1792,1792,1792,1792,1792,1792,1792,1781,1781,1781,1781,1781,1781,1781,1770,1770,1770,1770,1770,1770,1770,1770,1760,1760,1760,1760,1760,1760,1760,1749,1749,1749,1749,1749,1749,1749,1749,1738,1738,1738,1738,1738,1738,1738,1728,1728,1728,1728,1728,1728,1728,1728,1717,1717,1717,1717,1717,1717,1717,1717,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,1706,0};
-static uint32_t g4050_xpa[]={9422,5978,4736,4028,3560,3220,2914,2756,2588,2448,2328,2224,2132,2052,1978,1914,1854,1800,1752,1706,1664,1626,1588,1554,1522,1492,1464,1438,1412,1388,1366,1344,1324,1304,1284,1268,1250,1232,1218,1202,1188,1172,1160,1146,1134,1120,1110,1098,1086,1076,1066,1056,1046,1036,1026,1018,1008,1000,992,984,976,968,960,952,946,938,932,924,918,912,906,898,892,888,882,876,870,864,860,854,848,844,838,834,828,824,820,814,810,806,802,798,794,790,786,782,778,774,770,766,762,758,754,752,748,744,740,738,734,730,728,724,722,718,716,712,710,706,704,700,698,696,692,690,686,684,682,678,676,674,672,668,666,664,662,660,656,654,652,650,648,646,644,642,638,636,634,632,630,628,626,624,622,620,618,616,614,612,612,610,608,606,604,602,600,598,596,594,594,592,590,588,586,584,584,582,580,578,576,576,574,572,570,570,568,566,564,564,562,560,560,558,556,554,554,552,550,550,548,546,546,544,542,542,540,540,538,536,536,534,532,532,530,530,528,526,526,524,524,522,522,520,518,518,516,516,514,514,512,512,510,508,508,506,506,504,504,502,502,500,500,498,498,496,496,494,494,492,492,492,490,490,488,488,486,486,484,484,482,482,480,480,480,478,478,476,476,474,474,474,472,472,470,470,468,468,468,466,466,464,464,464,462,462,462,460,460,458,458,458,456,456,454,454,454,452,452,452,450,450,450,448,448,446,446,446,444,444,444,442,442,442,440,440,440,438,438,438,436,436,436,434,434,434,432,432,432,430,430,430,430,428,428,428,426,426,426,424,424,424,424,422,422,422,420,420,420,418,418,418,418,416,416,416,414,414,414,414,412,412,412,412,410,410,410,408,408,408,408,406,406,406,406,404,404,404,404,402,402,402,402,400,400,400,400,398,398,398,398,396,396,396,396,394,394,394,394,392,392,392,392,392,390,390,390,390,388,388,388,388,386,386,386,386,386,384,384,384,384,384,382,382,382,382,380,380,380,380,380,378,378,378,378,378,376,376,376,376,376,374,374,374,374,374,372,372,372,372,372,370,370,370,370,370,368,368,368,368,368,366,366,366,366,366,364,364,364,364,364,364,362,362,362,362,362,360,360,360,360,360,360,358,358,358,358,358,358,356,356,356,356,356,354,354,354,354,354,354,352,352,352,352,352,352,350,350,350,350,350,350,350,348,348,348,348,348,348,346,346,346,346,346,346,344,344,344,344,344,344,344,342,342,342,342,342,342,340,340,340,340,340,340,340,338,338,338,338,338,338,338,336,336,336,336,336,336,336,334,334,334,334,334,334,334,332,332,332,332,332,332,332,330,330,330,330,330,330,330,330,328,328,328,328,328,328,328,326,326,326,326,326,326,326,326,324,324,324,324,324,324,324,324,322,322,322,322,322,322,322,322,320,320,320,320,320,320,320,320,318,318,318,318,318,318,318,318,318,316,316,316,316,316,316,316,316,314,314,314,314,314,314,314,314,314,312,312,312,312,312,312,312,312,312,310,310,310,310,310,310,310,310,310,308,308,308,308,308,308,308,308,308,306,306,306,306,306,306,306,306,306,306,304,304,304,304,304,304,304,304,304,302,302,302,302,302,302,302,302,302,302,300,300,300,300,300,300,300,300,300,300,298,298,298,298,298,298,298,298,298,298,298,296,296,296,296,296,296,296,296,296,296,294,294,294,294,294,294,294,294,294,294,294,292,292,292,292,292,292,292,292,292,292,292,290,290,290,290,290,290,290,290,290,290,290,288,288,288,288,288,288,288,288,288,288,288,288,286,286,286,286,286,286,286,286,286,286,286,286,284,284,284,284,284,284,284,284,284,284,284,284,282,282,282,282,282,282,282,282,282,282,282,282,280,280,280,280,280,280,280,280,280,280,280,280,280,278,278,278,278,278,278,278,278,278,278,278,278,278,276,276,276,276,276,276,276,276,276,276,276,276,276,274,274,274,274,274,274,274,274,274,274,274,274,274,274,272,272,272,272,272,272,272,272,272,272,272,272,272,272,270,270,270,270,270,270,270,270,270,270,270,270,270,270,268,268,268,268,268,268,268,268,268,268,268,268,268,268,268,266,266,266,266,266,266,266,266,266,266,266,266,266,266,266,264,264,264,264,264,264,264,264,264,264,264,264,264,264,264,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,260,260,260,260,260,260,260,260,260,260,260,260,260,260,260,260,258,258,258,258,258,258,258,258,258,258,258,258,258,258,258,258,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,0};
-static uint32_t cs4400f_fast[]={49152, 49152, 31144, 23652, 19538, 16822, 14908, 13442, 12288, 11356, 10590, 9922, 9362, 8886, 8456, 8064, 7728, 7418, 7148, 6882, 6664, 6446, 6252, 6060, 5890, 5740, 5586, 5450, 5322, 5198, 5080, 4968, 4868, 4766, 4674, 4584, 4500, 4418, 4338, 4262, 4194, 4122, 4058, 3996, 3932, 3874, 3816, 3766, 3712, 3662, 3610, 3566, 3518, 3474, 3430, 3388, 3350, 3310, 3272, 3236, 3200, 3164, 3130, 3096, 3062, 3032, 3000, 2972, 2942, 2914, 2884, 2858, 2832, 2806, 2780, 2756, 2732, 2708, 2686, 2662, 2640, 2618, 2596, 2576, 2554, 2536, 2516, 2496, 2478, 2458, 2440, 2422, 2404, 2388, 2370, 2354, 2338, 2320, 2306, 2290, 2276, 2258, 2244, 2230, 2216, 2202, 2188, 2174, 2162, 2148, 2134, 2122, 2108, 2096, 2084, 2072, 2060, 2048, 2036, 2026, 2014, 2002, 1992, 1982, 1970, 1960, 1950, 1940, 1928, 1920, 1908, 1900, 1890, 1880, 1872, 1862, 1852, 1844, 1834, 1826, 1818, 1808, 1800, 1792, 1784, 1776, 1768, 1760, 1752, 1742, 1736, 1728, 1720, 1712, 1704, 1698, 1690, 1684, 1676, 1670, 1662, 1656, 1648, 1642, 1634, 1628, 1622, 1616, 1608, 1602, 1596, 1590, 1584, 1578, 1572, 1566, 1560, 1554, 1548, 1542, 1536, 1532, 1526, 1520, 1514, 1508, 1504, 1498, 1492, 1488, 1482, 1478, 1472, 1466, 1462, 1456, 1452, 1446, 1442, 1438, 1432, 1428, 1424, 1418, 1414, 1410, 1404, 1400, 1396, 1390, 1386, 1382, 1378, 1374, 1370, 1364, 1360, 1356, 1352, 1348, 1344, 1340, 1336, 1332, 1328, 1324, 1320, 1316, 1312, 1308, 1304, 1302, 1298, 1294, 1290, 1286, 1282, 1278, 1276, 1272, 1268, 1264, 1262, 1258, 1254, 1250, 1248, 1244, 1240, 1238, 1234, 1230, 1228, 1224, 1222, 1218, 1214, 1212, 1208, 1206, 1202, 1200, 1196, 1194, 1190, 1186, 1184, 1182, 1178, 1176, 1172, 1170, 1166, 1164, 1162, 1158, 1156, 1152, 1150, 1148, 1144, 1142, 1138, 1136, 1134, 1130, 1128, 1126, 1122, 1120, 1118, 1116, 1112, 1110, 1108, 1106, 1102, 1100, 1098, 1096, 1092, 1090, 1088, 1086, 1082, 1080, 1078, 1076, 1074, 1072, 1068, 1066, 1064, 1062, 1060, 1058, 1056, 1054, 1052, 1048, 1046, 1044, 1042, 1040, 1038, 1036, 1034, 1032, 1030, 1028, 1026, 1024, 1022, 1020, 1018, 1016, 1014, 1012, 1010, 1008, 1006, 1004, 1002, 1000, 998, 996, 994, 992, 990, 988, 986, 984, 982, 980, 978, 976, 974, 972, 972, 970, 968, 966, 964, 962, 960, 958, 956, 956, 954, 952, 950, 948, 946, 944, 944, 942, 940, 938, 936, 934, 934, 932, 930, 928, 926, 926, 924, 922, 920, 918, 918, 916, 914, 912, 912, 910, 908, 906, 904, 904, 902, 900, 898, 898, 896, 894, 892, 892, 890, 888, 888, 886, 884, 882, 882, 880, 878, 878, 876, 874, 874, 872, 870, 868, 868, 866, 864, 864, 862, 860, 860, 858, 856, 856, 854, 852, 852, 850, 848, 848, 846, 846, 844, 842, 842, 840, 838, 838, 836, 834, 834, 832, 832, 830, 828, 828, 826, 826, 824, 822, 822, 820, 820, 818, 816, 816, 814, 814, 812, 812, 810, 808, 808, 806, 806, 804, 802, 802, 800, 800, 798, 798, 796, 796, 794, 792, 792, 790, 790, 788, 788, 786, 786, 784, 784, 782, 782, 780, 780, 778, 778, 776, 774, 774, 772, 772, 770, 770, 768, 768, 766, 766, 764, 764, 762, 762, 760, 760, 758, 758, 758, 756, 756, 754, 754, 752, 752, 750, 750, 748, 748, 746, 746, 744, 744, 742, 742, 740, 740, 738, 738, 738, 736, 736, 734, 734, 732, 732, 730, 730, 730, 728, 728, 726, 726, 724, 724, 722, 722, 722, 720, 720, 718, 718, 718, 716, 716, 714, 714, 712, 712, 712, 710, 710, 708, 708, 708, 706, 706, 704, 704, 702, 702, 702, 700, 700, 698, 698, 698, 696, 696, 694, 694, 694, 692, 692, 692, 690, 690, 688, 688, 688, 686, 686, 684, 684, 684, 682, 682, 682, 680, 680, 680, 678, 678, 676, 676, 676, 674, 674, 674, 672, 672, 670, 670, 670, 668, 668, 668, 666, 666, 666, 664, 664, 664, 662, 662, 660, 660, 660, 658, 658, 658, 656, 656, 656, 654, 654, 654, 652, 652, 652, 650, 650, 650, 648, 648, 648, 646, 646, 646, 644, 644, 644, 642, 642, 642, 640, 640, 640, 640, 638, 638, 638, 636, 636, 636, 634, 634, 634, 632, 632, 632, 630, 630, 630, 630, 628, 628, 628, 626, 626, 626, 624, 624, 624, 624, 622, 622, 622, 620, 620, 620, 618, 618, 618, 618, 616, 616, 616, 614, 614, 614, 614, 612, 612, 612, 610, 610, 610, 610, 608, 608, 608, 606, 606, 606, 606, 604, 604, 604, 604, 602, 602, 602, 600, 600, 600, 600, 598, 598, 598, 598, 596, 596, 596, 594, 594, 594, 594, 592, 592, 592, 592, 590, 590, 590, 590, 588, 588, 588, 586, 586, 586, 586, 584, 584, 584, 584, 582, 582, 582, 582, 580, 580, 580, 580, 578, 578, 578, 578, 576, 576, 576, 576, 574, 574, 574, 574, 574, 572, 572, 572, 572, 570, 570, 570, 570, 568, 568, 568, 568, 566, 566, 566, 566, 564, 564, 564, 564, 564, 562, 562, 562, 562, 560, 560, 560, 560, 558, 558, 558, 558, 558, 556, 556, 556, 556, 554, 554, 554, 554, 554, 552, 552, 552, 552, 550, 550, 550, 550, 550, 548, 548, 548, 548, 546, 546, 546, 546, 546, 544, 544, 544, 544, 544, 542, 542, 542, 542, 540, 540, 540, 540, 540, 538, 538, 538, 538, 538, 536, 536, 536, 536, 536, 534, 534, 534, 534, 534, 532, 532, 532, 532, 532, 530, 530, 530, 530, 530, 528, 528, 528, 528, 528, 526, 526, 526, 526, 526, 524, 524, 524, 524, 524, 522, 522, 522, 522, 522, 520, 520, 520, 520, 520, 520, 518, 518, 518, 518, 518, 516, 516, 516, 516, 516, 514, 514, 514, 514, 514, 514, 512, 512, 512, 512, 512, 510, 510, 510, 510, 510, 510, 508, 508, 508, 508, 508, 506, 506, 506, 506, 506, 506, 504, 504, 504, 504, 504, 502, 502, 502, 502, 502, 502, 500, 500, 500, 500, 500, 500, 498, 498, 498, 498, 498, 498, 496, 496, 496, 496, 496, 496, 494, 494, 494, 494, 494, 494, 492, 492, 492, 492, 492, 492, 490, 490, 490, 490, 490, 490, 488, 488, 488, 488, 488, 488, 486, 486, 486, 486, 486, 486, 484, 484, 484, 484, 484, 484, 484, 0, 0, 0, 0, 0};
-static uint32_t cs8400f_fast[]={8743, 8205, 7017, 6201, 4938, 4016, 3371, 2966, 2682, 2469, 2296, 2159, 2041, 1942, 1857, 1782, 1716, 1656, 1602, 1554, 1510, 1470, 1432, 1398, 1366, 1336, 1309, 1282, 1258, 1235, 1213, 1193, 1173, 1154, 1137, 1120, 1104, 1089, 1074, 1060, 1047, 1034, 1022, 1010, 998, 987, 976, 966, 956, 946, 937, 928, 919, 911, 902, 894, 887, 879, 872, 864, 858, 851, 844, 838, 832, 825, 819, 814, 808, 802, 797, 792, 786, 781, 776, 771, 766, 762, 757, 753, 748, 744, 740, 736, 731, 728, 724, 720, 716, 712, 709, 705, 701, 698, 695, 691, 688, 685, 682, 679, 675, 672, 669, 666, 664, 661, 658, 655, 652, 650, 647, 644, 642, 639, 637, 634, 632, 629, 627, 625, 622, 620, 618, 616, 613, 611, 609, 607, 605, 603, 601, 599, 597, 595, 593, 591, 589, 587, 585, 583, 581, 580, 578, 576, 574, 573, 571, 569, 567, 566, 564, 563, 561, 559, 558, 556, 555, 553, 552, 550, 549, 547, 546, 544, 543, 542, 540, 539, 537, 536, 535, 533, 532, 531, 529, 528, 527, 526, 524, 523, 522, 521, 519, 518, 517, 516, 515, 514, 512, 511, 510, 509, 508, 507, 506, 505, 504, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 488, 487, 486, 485, 484, 483, 482, 481, 480, 479, 478, 477, 477, 476, 475, 474, 473, 472, 472, 471, 470, 469, 468, 467, 467, 466, 465, 464, 464, 463, 462, 461, 460, 460, 459, 458, 457, 457, 456, 455, 454, 454, 453, 452, 452, 451, 450, 449, 449, 448, 448, 447, 446, 446, 445, 444, 444, 443, 442, 442, 441, 440, 440, 439, 438, 438, 437, 437, 436, 435, 435, 434, 434, 433, 432, 432, 431, 431, 430, 429, 429, 428, 428, 427, 427, 426, 425, 425, 424, 424, 423, 423, 422, 422, 421, 421, 420, 420, 419, 419, 418, 417, 417, 416, 416, 416, 415, 414, 414, 414, 413, 412, 412, 412, 411, 411, 410, 410, 409, 409, 408, 408, 407, 407, 406, 406, 406, 405, 405, 404, 404, 403, 403, 402, 402, 402, 401, 401, 400, 400, 399, 399, 398, 398, 398, 397, 397, 396, 396, 396, 395, 395, 394, 394, 394, 393, 393, 392, 392, 392, 391, 391, 391, 390, 390, 389, 389, 389, 388, 388, 387, 387, 387, 386, 386, 385, 385, 385, 384, 384, 384, 383, 383, 383, 382, 382, 382, 381, 381, 381, 380, 380, 380, 379, 379, 379, 378, 378, 378, 377, 377, 377, 376, 376, 376, 375, 375, 375, 374, 374, 374, 373, 373, 373, 372, 372, 372, 371, 371, 371, 370, 370, 370, 370, 369, 369, 369, 368, 368, 368, 368, 367, 367, 367, 367, 366, 366, 366, 365, 365, 365, 365, 364, 364, 364, 363, 363, 363, 363, 362, 362, 362, 362, 361, 361, 361, 360, 360, 360, 360, 359, 359, 359, 358, 358, 358, 358, 357, 357, 357, 356, 356, 356, 356, 355, 355, 355, 355, 354, 354, 354, 354, 354, 353, 353, 353, 353, 352, 352, 352, 352, 352, 351, 351, 351, 351, 350, 350, 350, 350, 350, 349, 349, 349, 349, 348, 348, 348, 348, 348, 347, 347, 347, 347, 346, 346, 346, 346, 346, 345, 345, 345, 345, 344, 344, 344, 344, 344, 343, 343, 343, 343, 342, 342, 342, 342, 342, 341, 341, 341, 341, 340, 340, 340, 340, 340, 339, 339, 339, 339, 338, 338, 338, 338, 338, 337, 337, 337, 337, 337, 337, 336, 336, 336, 336, 336, 336, 335, 335, 335, 335, 335, 335, 334, 334, 334, 334, 334, 334, 333, 333, 333, 333, 333, 333, 332, 332, 332, 332, 332, 332, 331, 331, 331, 331, 331, 331, 330, 330, 330, 330, 330, 330, 329, 329, 329, 329, 329, 329, 328, 328, 328, 328, 328, 328, 327, 327, 327, 327, 327, 327, 326, 326, 326, 326, 326, 326, 325, 325, 325, 325, 325, 325, 324, 324, 324, 324, 324, 324, 323, 323, 323, 323, 323, 323, 322, 322, 322, 322, 322, 322, 321, 321, 321, 321, 321, 321, 320, 320, 320, 320, 320, 320, 319, 319, 319, 319, 319, 319, 318, 318, 318, 318, 318, 318, 317, 317, 317, 317, 317, 317, 316, 316, 316, 316, 316, 316, 315, 315, 315, 315, 315, 315, 314, 314, 314, 314, 314, 314, 313, 313, 313, 313, 313, 313, 312, 312, 312, 312, 312, 312, 311, 311, 311, 311, 311, 311, 310, 310, 310, 310, 310, 310, 309, 309, 309, 309, 309, 309, 308, 308, 308, 308, 308, 308, 307, 307, 307, 307, 307, 307, 306, 306, 306, 306, 306, 306, 305, 305, 305, 305, 305, 305, 304, 304, 304, 304, 304, 304, 303, 303, 303, 303, 303, 303, 302, 302, 302, 302, 302, 302, 302, 301, 301, 301, 301, 301, 301, 301, 301, 301, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 0, 0, 0, 0, 0 };
-static uint32_t motor_speeds_cs8600f[] = {
- 54612, 54612, 34604, 26280, 21708, 18688, 16564, 14936, 13652, 12616,
- 11768, 11024, 10400, 9872, 9392, 8960, 8584, 8240, 7940, 7648,
- 7404, 7160, 6948, 6732, 6544, 6376, 6208, 6056, 5912, 5776,
- 5644, 5520, 5408, 5292, 5192, 5092, 5000, 4908, 4820, 4736,
- 4660, 4580, 4508, 4440, 4368, 4304, 4240, 4184, 4124, 4068,
- 4012, 3960, 3908, 3860, 3808, 3764, 3720, 3676, 3636, 3592,
- 3552, 3516, 3476, 3440, 3400, 3368, 3332, 3300, 3268, 3236,
- 3204, 3176, 3148, 3116, 3088, 3060, 3036, 3008, 2984, 2956,
- 2932, 2908, 2884, 2860, 2836, 2816, 2796, 2772, 2752, 2732,
- 2708, 2692, 2672, 2652, 2632, 2616, 2596, 2576, 2560, 2544,
- 2528, 2508, 2492, 2476, 2460, 2444, 2432, 2416, 2400, 2384,
- 2372, 2356, 2344, 2328, 2316, 2304, 2288, 2276, 2260, 2252,
- 2236, 2224, 2212, 2200, 2188, 2176, 2164, 2156, 2144, 2132,
- 2120, 2108, 2100, 2088, 2080, 2068, 2056, 2048, 2036, 2028,
- 2020, 2008, 2000, 1988, 1980, 1972, 1964, 1952, 1944, 1936,
- 1928, 1920, 1912, 1900, 1892, 1884, 1876, 1868, 1860, 1856,
- 1848, 1840, 1832, 1824, 1816, 1808, 1800, 1796, 1788, 1780,
- 1772, 1764, 1760, 1752, 1744, 1740, 1732, 1724, 1720, 1712,
- 1708, 1700, 1692, 1688, 1680, 1676, 1668, 1664, 1656, 1652,
- 1644, 1640, 1636, 1628, 1624, 1616, 1612, 1608, 1600, 1596,
- 1592, 1584, 1580, 1576, 1568, 1564, 1560, 1556, 1548, 1544,
- 1540, 1536, 1528, 1524, 1520, 1516, 1512, 1508, 1500, 0
-};
-
-/**
- * database of motor profiles
- */
-
-static Motor_Profile gl843_motors[]={
- /* KV-SS080 */
- {MOTOR_KVSS080, 8000, 1, kvss080},
- /* G4010/G4050/CS4400F */
- {MOTOR_G4050, 8016, 1, g4050_fast},
- {MOTOR_G4050, 11640, 1, cs4400f_fast},
- {MOTOR_G4050, 15624, 1, g4050_xpa},
- {MOTOR_G4050, 42752, 2, g4050_max},
- {MOTOR_G4050, 56064, 1, g4050_high},
- /* CS8400F */
- {MOTOR_CS8400F, 7200, 0, cs8400f_fast},
- { MOTOR_CS8600F, 0x59d8, 2, motor_speeds_cs8600f }, // FIXME: if the exposure is lower then we'll select another motor
- { 0, 0, 0, NULL },
-};
diff --git a/backend/genesys_gl846.cc b/backend/genesys_gl846.cc
deleted file mode 100644
index c5294b8..0000000
--- a/backend/genesys_gl846.cc
+++ /dev/null
@@ -1,3393 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2012-2013 Stéphane Voltz <stef.dev@free.fr>
-
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-/** @file
- *
- * This file handles GL846 and GL845 ASICs since they are really close to each other.
- */
-
-#define DEBUG_DECLARE_ONLY
-
-#include "genesys_gl846.h"
-
-#include <vector>
-
-/****************************************************************************
- Mid level functions
- ****************************************************************************/
-
-static SANE_Bool
-gl846_get_fast_feed_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG02);
- if (r && (r->value & REG02_FASTFED))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl846_get_filter_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_FILTER))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl846_get_lineart_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_LINEART))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl846_get_bitset_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_BITSET))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl846_get_gain4_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x06);
- if (r && (r->value & REG06_GAIN4))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl846_test_buffer_empty_bit (SANE_Byte val)
-{
- if (val & REG41_BUFEMPTY)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl846_test_motor_flag_bit (SANE_Byte val)
-{
- if (val & REG41_MOTORENB)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-/**
- * compute the step multiplier used
- */
-static int
-gl846_get_step_multiplier (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
- int value = 1;
-
- r = sanei_genesys_get_address (regs, 0x9d);
- if (r != NULL)
- {
- value = (r->value & 0x0f)>>1;
- value = 1 << value;
- }
- DBG (DBG_io, "%s: step multiplier is %d\n", __func__, value);
- return value;
-}
-
-/** @brief sensor profile
- * search for the database of motor profiles and get the best one. Each
- * profile is at a specific dpihw. Use LiDE 110 table by default.
- * @param sensor_type sensor id
- * @param dpi hardware dpi for the scan
- * @return a pointer to a Sensor_Profile struct
- */
-static Sensor_Profile *get_sensor_profile(int sensor_type, int dpi)
-{
- unsigned int i;
- int idx;
-
- i=0;
- idx=-1;
- while(i<sizeof(sensors)/sizeof(Sensor_Profile))
- {
- /* exact match */
- if(sensors[i].sensor_type==sensor_type && sensors[i].dpi==dpi)
- {
- return &(sensors[i]);
- }
-
- /* closest match */
- if(sensors[i].sensor_type==sensor_type)
- {
- if(idx<0)
- {
- idx=i;
- }
- else
- {
- if(sensors[i].dpi>=dpi
- && sensors[i].dpi<sensors[idx].dpi)
- {
- idx=i;
- }
- }
- }
- i++;
- }
-
- /* default fallback */
- if(idx<0)
- {
- DBG (DBG_warn,"%s: using default sensor profile\n",__func__);
- idx=0;
- }
-
- return &(sensors[idx]);
-}
-
-/**@brief compute exposure to use
- * compute the sensor exposure based on target resolution
- */
-static int gl846_compute_exposure(Genesys_Device *dev, int xres)
-{
- Sensor_Profile* sensor_profile=get_sensor_profile(dev->model->ccd_type, xres);
- return sensor_profile->exposure;
-}
-
-
-/** @brief sensor specific settings
-*/
-static void gl846_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs, int dpi)
-{
- GenesysRegister *r;
- int dpihw;
- uint16_t exp;
-
- DBGSTART;
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dpi);
-
- for (uint16_t addr = 0x16; addr < 0x1e; addr++) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- for (uint16_t addr = 0x52; addr < 0x52 + 9; addr++) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- /* set EXPDUMMY and CKxMAP */
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dpi);
- Sensor_Profile* sensor_profile = get_sensor_profile(dev->model->ccd_type, dpihw);
-
- sanei_genesys_set_reg_from_set(regs,REG_EXPDMY,(uint8_t)((sensor_profile->expdummy) & 0xff));
-
- /* if no calibration has been done, set default values for exposures */
- exp = sensor.exposure.red;
- if(exp==0)
- {
- exp=sensor_profile->expr;
- }
- sanei_genesys_set_double(regs,REG_EXPR,exp);
-
- exp = sensor.exposure.green;
- if(exp==0)
- {
- exp=sensor_profile->expg;
- }
- sanei_genesys_set_double(regs,REG_EXPG,exp);
-
- exp = sensor.exposure.blue;
- if(exp==0)
- {
- exp=sensor_profile->expb;
- }
- sanei_genesys_set_double(regs,REG_EXPB,exp);
-
- sanei_genesys_set_triple(regs,REG_CK1MAP,sensor_profile->ck1map);
- sanei_genesys_set_triple(regs,REG_CK3MAP,sensor_profile->ck3map);
- sanei_genesys_set_triple(regs,REG_CK4MAP,sensor_profile->ck4map);
-
- /* order of the sub-segments */
- dev->order=sensor_profile->order;
-
- r = sanei_genesys_get_address (regs, 0x17);
- r->value = sensor_profile->r17;
-
- DBGCOMPLETED;
-}
-
-
-/** @brief set all registers to default values .
- * This function is called only once at the beginning and
- * fills register startup values for registers reused across scans.
- * Those that are rarely modified or not modified are written
- * individually.
- * @param dev device structure holding register set to initialize
- */
-static void
-gl846_init_registers (Genesys_Device * dev)
-{
- DBGSTART;
-
- dev->reg.clear();
-
- SETREG (0x01,0x60);
- SETREG (0x02,0x38);
- SETREG (0x03,0x03);
- SETREG (0x04,0x22);
- SETREG (0x05,0x60);
- SETREG (0x06,0x10);
- SETREG (0x08,0x60);
- SETREG (0x09,0x00);
- SETREG (0x0a,0x00);
- SETREG (0x0b,0x8b);
- SETREG (0x0c,0x00);
- SETREG (0x0d,0x00);
- SETREG (0x10,0x00);
- SETREG (0x11,0x00);
- SETREG (0x12,0x00);
- SETREG (0x13,0x00);
- SETREG (0x14,0x00);
- SETREG (0x15,0x00);
- SETREG (0x16,0xbb);
- SETREG (0x17,0x13);
- SETREG (0x18,0x10);
- SETREG (0x19,0x2a);
- SETREG (0x1a,0x34);
- SETREG (0x1b,0x00);
- SETREG (0x1c,0x20);
- SETREG (0x1d,0x06);
- SETREG (0x1e,0xf0);
- SETREG (0x1f,0x01);
- SETREG (0x20,0x03);
- SETREG (0x21,0x10);
- SETREG (0x22,0x60);
- SETREG (0x23,0x60);
- SETREG (0x24,0x60);
- SETREG (0x25,0x00);
- SETREG (0x26,0x00);
- SETREG (0x27,0x00);
- SETREG (0x2c,0x00);
- SETREG (0x2d,0x00);
- SETREG (0x2e,0x80);
- SETREG (0x2f,0x80);
- SETREG (0x30,0x00);
- SETREG (0x31,0x00);
- SETREG (0x32,0x00);
- SETREG (0x33,0x00);
- SETREG (0x34,0x1f);
- SETREG (0x35,0x00);
- SETREG (0x36,0x40);
- SETREG (0x37,0x00);
- SETREG (0x38,0x2a);
- SETREG (0x39,0xf8);
- SETREG (0x3d,0x00);
- SETREG (0x3e,0x00);
- SETREG (0x3f,0x01);
- SETREG (0x52,0x02);
- SETREG (0x53,0x04);
- SETREG (0x54,0x06);
- SETREG (0x55,0x08);
- SETREG (0x56,0x0a);
- SETREG (0x57,0x00);
- SETREG (0x58,0x59);
- SETREG (0x59,0x31);
- SETREG (0x5a,0x40);
- SETREG (0x5e,0x1f);
- SETREG (0x5f,0x01);
- SETREG (0x60,0x00);
- SETREG (0x61,0x00);
- SETREG (0x62,0x00);
- SETREG (0x63,0x00);
- SETREG (0x64,0x00);
- SETREG (0x65,0x00);
- SETREG (0x67,0x7f);
- SETREG (0x68,0x7f);
- SETREG (0x69,0x01);
- SETREG (0x6a,0x01);
- SETREG (0x70,0x01);
- SETREG (0x71,0x00);
- SETREG (0x72,0x02);
- SETREG (0x73,0x01);
- SETREG (0x74,0x00);
- SETREG (0x75,0x00);
- SETREG (0x76,0x00);
- SETREG (0x77,0x00);
- SETREG (0x78,0x00);
- SETREG (0x79,0x3f);
- SETREG (0x7a,0x00);
- SETREG (0x7b,0x09);
- SETREG (0x7c,0x99);
- SETREG (0x7d,0x20);
- SETREG (0x7f,0x05);
- SETREG (0x80,0x4f);
- SETREG (0x87,0x02);
- SETREG (0x94,0xff);
- SETREG (0x9d,0x04);
- SETREG (0x9e,0x00);
- SETREG (0xa1,0xe0);
- SETREG (0xa2,0x1f);
- SETREG (0xab,0xc0);
- SETREG (0xbb,0x00);
- SETREG (0xbc,0x0f);
- SETREG (0xdb,0xff);
- SETREG (0xfe,0x08);
- SETREG (0xff,0x02);
- SETREG (0x98,0x20);
- SETREG (0x99,0x00);
- SETREG (0x9a,0x90);
- SETREG (0x9b,0x00);
- SETREG (0xf8,0x05);
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- /* fine tune upon device description */
- dev->reg.find_reg(0x05).value &= ~REG05_DPIHW;
- switch (sensor.optical_res)
- {
- case 600:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_600;
- break;
- case 1200:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_1200;
- break;
- case 2400:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_2400;
- break;
- case 4800:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_4800;
- break;
- }
-
- /* initalize calibration reg */
- dev->calib_reg = dev->reg;
-
- DBGCOMPLETED;
-}
-
-/**@brief send slope table for motor movement
- * Send slope_table in machine byte order
- * @param dev device to send slope table
- * @param table_nr index of the slope table in ASIC memory
- * Must be in the [0-4] range.
- * @param slope_table pointer to 16 bit values array of the slope table
- * @param steps number of elements in the slope table
- */
-static SANE_Status
-gl846_send_slope_table (Genesys_Device * dev, int table_nr,
- uint16_t * slope_table, int steps)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- char msg[10000];
-
- DBG (DBG_proc, "%s (table_nr = %d, steps = %d)\n", __func__,
- table_nr, steps);
-
- /* sanity check */
- if(table_nr<0 || table_nr>4)
- {
- DBG (DBG_error, "%s: invalid table number %d!\n", __func__, table_nr);
- return SANE_STATUS_INVAL;
- }
-
- std::vector<uint8_t> table(steps * 2);
- for (i = 0; i < steps; i++)
- {
- table[i * 2] = slope_table[i] & 0xff;
- table[i * 2 + 1] = slope_table[i] >> 8;
- }
-
- if (DBG_LEVEL >= DBG_io)
- {
- sprintf (msg, "write slope %d (%d)=", table_nr, steps);
- for (i = 0; i < steps; i++)
- {
- sprintf (msg+strlen(msg), "%d", slope_table[i]);
- }
- DBG (DBG_io, "%s: %s\n", __func__, msg);
- }
-
- /* slope table addresses are fixed */
- status = sanei_genesys_write_ahb(dev, 0x10000000 + 0x4000 * table_nr, steps * 2, table.data());
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: write to AHB failed writing slope table %d (%s)\n", __func__, table_nr,
- sane_strstatus(status));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * Set register values of Analog Device type frontend
- * */
-static SANE_Status
-gl846_set_adi_fe (Genesys_Device * dev, uint8_t set)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- uint8_t val8;
-
- DBGSTART;
-
- /* wait for FE to be ready */
- status = sanei_genesys_get_status (dev, &val8);
- while (val8 & REG41_FEBUSY)
- {
- sanei_genesys_sleep_ms(10);
- status = sanei_genesys_get_status (dev, &val8);
- };
-
- if (set == AFE_INIT)
- {
- DBG (DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
- dev->frontend = dev->frontend_initial;
- }
-
- /* write them to analog frontend */
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to write reg0: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to write reg1: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- for (i = 0; i < 3; i++)
- {
- status = sanei_genesys_fe_write_data(dev, 0x02 + i, dev->frontend.get_gain(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to write gain %d: %s\n", __func__, i,
- sane_strstatus (status));
- return status;
- }
- }
- for (i = 0; i < 3; i++)
- {
- status = sanei_genesys_fe_write_data(dev, 0x05 + i, dev->frontend.get_offset(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to write offset %d: %s\n", __func__, i,
- sane_strstatus (status));
- return status;
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-static SANE_Status
-gl846_homsnr_gpio(Genesys_Device *dev)
-{
-uint8_t val;
-SANE_Status status=SANE_STATUS_GOOD;
-
- RIE (sanei_genesys_read_register (dev, REG6C, &val));
- val |= 0x41;
- RIE (sanei_genesys_write_register (dev, REG6C, val));
-
- return status;
-}
-
-/* Set values of analog frontend */
-static SANE_Status
-gl846_set_fe(Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (%s)\n", __func__, set == AFE_INIT ? "init" : set == AFE_SET ? "set" :
- set == AFE_POWER_SAVE ? "powersave" : "huh?");
-
- /* route to specific analog frontend setup */
- switch (dev->reg.find_reg(0x04).value & REG04_FESET)
- {
- case 0x02: /* ADI FE */
- status = gl846_set_adi_fe(dev, set);
- break;
- default:
- DBG(DBG_proc, "%s(): unsupported frontend type %d\n", __func__,
- dev->reg.find_reg(0x04).value & REG04_FESET);
- status = SANE_STATUS_UNSUPPORTED;
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/** @brief set up motor related register for scan
- */
-static SANE_Status
-gl846_init_motor_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int scan_exposure_time,
- float scan_yres,
- int scan_step_type,
- unsigned int scan_lines,
- unsigned int scan_dummy,
- unsigned int feed_steps,
- int scan_power_mode,
- unsigned int flags)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int use_fast_fed;
- unsigned int fast_dpi;
- uint16_t scan_table[SLOPE_TABLE_SIZE];
- uint16_t fast_table[SLOPE_TABLE_SIZE];
- int scan_steps, fast_steps, factor;
- unsigned int feedl, dist;
- GenesysRegister *r;
- uint32_t z1, z2;
- unsigned int min_restep = 0x20;
- uint8_t val;
- int fast_step_type;
- unsigned int ccdlmt,tgtime;
-
- DBGSTART;
- DBG(DBG_proc, "%s : scan_exposure_time=%d, scan_yres=%g, scan_step_type=%d, scan_lines=%d, "
- "scan_dummy=%d, feed_steps=%d, scan_power_mode=%d, flags=%x\n", __func__, scan_exposure_time,
- scan_yres, scan_step_type, scan_lines, scan_dummy, feed_steps, scan_power_mode, flags);
-
- /* get step multiplier */
- factor = gl846_get_step_multiplier (reg);
-
- use_fast_fed=0;
- /* no fast fed since feed works well */
- if(dev->settings.yres==4444 && feed_steps>100
- && ((flags & MOTOR_FLAG_FEED)==0))
- {
- use_fast_fed=1;
- }
- DBG (DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
-
- sanei_genesys_set_triple(reg, REG_LINCNT, scan_lines);
- DBG (DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
-
- /* compute register 02 value */
- r = sanei_genesys_get_address (reg, REG02);
- r->value = 0x00;
- sanei_genesys_set_motor_power(*reg, true);
-
- if (use_fast_fed)
- r->value |= REG02_FASTFED;
- else
- r->value &= ~REG02_FASTFED;
-
- if (flags & MOTOR_FLAG_AUTO_GO_HOME)
- r->value |= REG02_AGOHOME | REG02_NOTHOME;
-
- if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
- ||(scan_yres>=sensor.optical_res))
- {
- r->value |= REG02_ACDCDIS;
- }
-
- /* scan and backtracking slope table */
- sanei_genesys_slope_table(scan_table,
- &scan_steps,
- scan_yres,
- scan_exposure_time,
- dev->motor.base_ydpi,
- scan_step_type,
- factor,
- dev->model->motor_type,
- gl846_motors);
- RIE(gl846_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps*factor));
- RIE(gl846_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps*factor));
-
- /* fast table */
- fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
- fast_step_type=scan_step_type;
- if(scan_step_type>=2)
- {
- fast_step_type=2;
- }
-
- sanei_genesys_slope_table(fast_table,
- &fast_steps,
- fast_dpi,
- scan_exposure_time,
- dev->motor.base_ydpi,
- fast_step_type,
- factor,
- dev->model->motor_type,
- gl846_motors);
-
- /* manual override of high start value */
- fast_table[0]=fast_table[1];
-
- RIE(gl846_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps*factor));
- RIE(gl846_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps*factor));
- RIE(gl846_send_slope_table (dev, HOME_TABLE, fast_table, fast_steps*factor));
-
- /* correct move distance by acceleration and deceleration amounts */
- feedl=feed_steps;
- if (use_fast_fed)
- {
- feedl<<=fast_step_type;
- dist=(scan_steps+2*fast_steps)*factor;
- /* TODO read and decode REGAB */
- r = sanei_genesys_get_address (reg, 0x5e);
- dist += (r->value & 31);
- /* FEDCNT */
- r = sanei_genesys_get_address (reg, REG_FEDCNT);
- dist += r->value;
- }
- else
- {
- feedl<<=scan_step_type;
- dist=scan_steps*factor;
- if (flags & MOTOR_FLAG_FEED)
- dist *=2;
- }
- DBG (DBG_io2, "%s: scan steps=%d\n", __func__, scan_steps);
- DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
-
- /* check for overflow */
- if(dist<feedl)
- feedl -= dist;
- else
- feedl = 0;
-
- sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
- DBG (DBG_io ,"%s: feedl=%d\n",__func__,feedl);
-
- r = sanei_genesys_get_address (reg, REG0C);
- ccdlmt=(r->value & REG0C_CCDLMT)+1;
-
- r = sanei_genesys_get_address (reg, REG1C);
- tgtime=1<<(r->value & REG1C_TGTIME);
-
- /* hi res motor speed GPIO */
- /*
- RIE (sanei_genesys_read_register (dev, REG6C, &effective));
- */
-
- /* if quarter step, bipolar Vref2 */
- /* XXX STEF XXX GPIO
- if (scan_step_type > 1)
- {
- if (scan_step_type < 3)
- {
- val = effective & ~REG6C_GPIO13;
- }
- else
- {
- val = effective | REG6C_GPIO13;
- }
- }
- else
- {
- val = effective;
- }
- RIE (sanei_genesys_write_register (dev, REG6C, val));
- */
-
- /* effective scan */
- /*
- RIE (sanei_genesys_read_register (dev, REG6C, &effective));
- val = effective | REG6C_GPIO10;
- RIE (sanei_genesys_write_register (dev, REG6C, val));
- */
-
- if(dev->model->gpo_type==GPO_IMG101)
- {
- if(scan_yres==sanei_genesys_compute_dpihw(dev, sensor,scan_yres))
- {
- val=1;
- }
- else
- {
- val=0;
- }
- RIE (sanei_genesys_write_register (dev, REG7E, val));
- }
-
- min_restep=scan_steps/2-1;
- if (min_restep < 1)
- min_restep = 1;
- r = sanei_genesys_get_address (reg, REG_FWDSTEP);
- r->value = min_restep;
- r = sanei_genesys_get_address (reg, REG_BWDSTEP);
- r->value = min_restep;
-
- sanei_genesys_calculate_zmode2(use_fast_fed,
- scan_exposure_time*ccdlmt*tgtime,
- scan_table,
- scan_steps*factor,
- feedl,
- min_restep*factor,
- &z1,
- &z2);
-
- DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
- sanei_genesys_set_triple(reg, REG60, z1 | (scan_step_type << (16+REG60S_STEPSEL)));
-
- DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
- sanei_genesys_set_triple(reg, REG63, z2 | (scan_step_type << (16+REG63S_FSTPSEL)));
-
- r = sanei_genesys_get_address (reg, 0x1e);
- r->value &= 0xf0; /* 0 dummy lines */
- r->value |= scan_dummy; /* dummy lines */
-
- r = sanei_genesys_get_address (reg, REG67);
- r->value = 0x7f;
-
- r = sanei_genesys_get_address (reg, REG68);
- r->value = 0x7f;
-
- r = sanei_genesys_get_address (reg, REG_STEPNO);
- r->value = scan_steps;
-
- r = sanei_genesys_get_address (reg, REG_FASTNO);
- r->value = scan_steps;
-
- r = sanei_genesys_get_address (reg, REG_FSHDEC);
- r->value = scan_steps;
-
- r = sanei_genesys_get_address (reg, REG_FMOVNO);
- r->value = fast_steps;
-
- r = sanei_genesys_get_address (reg, REG_FMOVDEC);
- r->value = fast_steps;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/** @brief set up registers related to sensor
- * Set up the following registers
- 0x01
- 0x03
- 0x10-0x015 R/G/B exposures
- 0x19 EXPDMY
- 0x2e BWHI
- 0x2f BWLO
- 0x04
- 0x87
- 0x05
- 0x2c,0x2d DPISET
- 0x30,0x31 STRPIXEL
- 0x32,0x33 ENDPIXEL
- 0x35,0x36,0x37 MAXWD [25:2] (>>2)
- 0x38,0x39 LPERIOD
- 0x34 DUMMY
- */
-static SANE_Status
-gl846_init_optical_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int exposure_time,
- int used_res,
- unsigned int start,
- unsigned int pixels,
- int channels,
- int depth,
- SANE_Bool half_ccd, ColorFilter color_filter, int flags)
-{
- unsigned int words_per_line;
- unsigned int startx, endx, used_pixels;
- unsigned int dpiset, dpihw,segnb,cksel,factor;
- unsigned int bytes;
- GenesysRegister *r;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s : exposure_time=%d, used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
- "half_ccd=%d, flags=%x\n", __func__, exposure_time, used_res, start, pixels, channels, depth,
- half_ccd, flags);
-
- /* resolution is divided according to CKSEL */
- r = sanei_genesys_get_address (reg, REG18);
- cksel= (r->value & REG18_CKSEL)+1;
- DBG(DBG_io2, "%s: cksel=%d\n", __func__, cksel);
-
- /* to manage high resolution device while keeping good
- * low resolution scanning speed, we make hardware dpi vary */
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, used_res * cksel);
- factor=sensor.optical_res/dpihw;
- DBG(DBG_io2, "%s: dpihw=%d (factor=%d)\n", __func__, dpihw, factor);
-
- /* sensor parameters */
- Sensor_Profile* sensor_profile = get_sensor_profile(dev->model->ccd_type, dpihw);
- gl846_setup_sensor(dev, sensor, reg, dpihw);
- dpiset = used_res * cksel;
-
- /* start and end coordinate in optical dpi coordinates */
- startx = start/cksel+sensor.CCD_start_xoffset;
- used_pixels=pixels/cksel;
-
- /* end of sensor window */
- endx = startx + used_pixels;
-
- /* sensors are built from 600 dpi segments for LiDE 100/200
- * and 1200 dpi for the 700F */
- if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR)
- {
- segnb=dpihw/600;
- }
- else
- {
- segnb=1;
- }
-
- /* compute pixel coordinate in the given dpihw space,
- * taking segments into account */
- startx/=factor*segnb;
- endx/=factor*segnb;
- dev->len=endx-startx;
- dev->dist=0;
- dev->skip=0;
-
- /* in cas of multi-segments sensor, we have to add the witdh
- * of the sensor crossed by the scan area */
- if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR && segnb>1)
- {
- dev->dist = sensor_profile->segcnt;
- }
-
- /* use a segcnt rounded to next even number */
- endx += ((dev->dist+1)&0xfffe)*(segnb-1);
- used_pixels=endx-startx;
-
- status = gl846_set_fe(dev, sensor, AFE_SET);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* enable shading */
- r = sanei_genesys_get_address (reg, REG01);
- r->value &= ~REG01_SCAN;
- r->value |= REG01_SHDAREA;
- if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
- (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
- {
- r->value &= ~REG01_DVDSET;
- }
- else
- {
- r->value |= REG01_DVDSET;
- }
-
- r = sanei_genesys_get_address (reg, REG03);
- r->value &= ~REG03_AVEENB;
-
- sanei_genesys_set_lamp_power(dev, sensor, *reg, !(flags & OPTICAL_FLAG_DISABLE_LAMP));
-
- /* BW threshold */
- r = sanei_genesys_get_address (reg, 0x2e);
- r->value = dev->settings.threshold;
- r = sanei_genesys_get_address (reg, 0x2f);
- r->value = dev->settings.threshold;
-
- /* monochrome / color scan */
- r = sanei_genesys_get_address (reg, REG04);
- switch (depth)
- {
- case 1:
- r->value &= ~REG04_BITSET;
- r->value |= REG04_LINEART;
- break;
- case 8:
- r->value &= ~(REG04_LINEART | REG04_BITSET);
- break;
- case 16:
- r->value &= ~REG04_LINEART;
- r->value |= REG04_BITSET;
- break;
- }
-
- r->value &= ~(REG04_FILTER | REG04_AFEMOD);
- if (channels == 1)
- {
- switch (color_filter)
- {
- case ColorFilter::RED:
- r->value |= 0x24;
- break;
- case ColorFilter::BLUE:
- r->value |= 0x2c;
- break;
- case ColorFilter::GREEN:
- r->value |= 0x28;
- break;
- default:
- break; // should not happen
- }
- }
- else
- r->value |= 0x20; /* mono */
-
- /* register 05 */
- r = sanei_genesys_get_address (reg, REG05);
-
- /* set up dpihw */
- r->value &= ~REG05_DPIHW;
- switch(dpihw)
- {
- case 600:
- r->value |= REG05_DPIHW_600;
- break;
- case 1200:
- r->value |= REG05_DPIHW_1200;
- break;
- case 2400:
- r->value |= REG05_DPIHW_2400;
- break;
- case 4800:
- r->value |= REG05_DPIHW_4800;
- break;
- }
-
- /* enable gamma tables */
- if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
- r->value &= ~REG05_GMMENB;
- else
- r->value |= REG05_GMMENB;
-
- /* CIS scanners can do true gray by setting LEDADD */
- /* we set up LEDADD only when asked */
- if (dev->model->is_cis == SANE_TRUE)
- {
- r = sanei_genesys_get_address (reg, 0x87);
- r->value &= ~REG87_LEDADD;
- if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
- {
- r->value |= REG87_LEDADD;
- }
- /* RGB weighting
- r = sanei_genesys_get_address (reg, 0x01);
- r->value &= ~REG01_TRUEGRAY;
- if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
- {
- r->value |= REG01_TRUEGRAY;
- }*/
- }
-
- /* words(16bit) before gamma, conversion to 8 bit or lineart*/
- words_per_line = (used_pixels * dpiset) / dpihw;
- bytes=depth/8;
- if (depth == 1)
- {
- words_per_line = (words_per_line+7)/8 ;
- dev->len = (dev->len >> 3) + ((dev->len & 7) ? 1 : 0);
- dev->dist = (dev->dist >> 3) + ((dev->dist & 7) ? 1 : 0);
- }
- else
- {
- words_per_line *= bytes;
- dev->dist *= bytes;
- dev->len *= bytes;
- }
-
- dev->bpl = words_per_line;
- dev->cur=0;
- dev->segnb=segnb;
- dev->line_interp = 0;
-
- sanei_genesys_set_double(reg,REG_DPISET,dpiset);
- DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
-
- sanei_genesys_set_double(reg,REG_STRPIXEL,startx);
- sanei_genesys_set_double(reg,REG_ENDPIXEL,endx);
- DBG (DBG_io2, "%s: startx=%d\n", __func__, startx);
- DBG (DBG_io2, "%s: endx =%d\n", __func__, endx);
-
- DBG (DBG_io2, "%s: used_pixels=%d\n", __func__, used_pixels);
- DBG (DBG_io2, "%s: pixels =%d\n", __func__, pixels);
- DBG (DBG_io2, "%s: depth =%d\n", __func__, depth);
- DBG (DBG_io2, "%s: dev->bpl =%lu\n", __func__, (unsigned long)dev->bpl);
- DBG (DBG_io2, "%s: dev->len =%lu\n", __func__, (unsigned long)dev->len);
- DBG (DBG_io2, "%s: dev->dist =%lu\n", __func__, (unsigned long)dev->dist);
- DBG (DBG_io2, "%s: dev->segnb =%lu\n", __func__, (unsigned long)dev->segnb);
-
- words_per_line *= channels;
- dev->wpl = words_per_line;
-
- dev->oe_buffer.clear();
- dev->oe_buffer.alloc(dev->wpl);
-
- /* MAXWD is expressed in 4 words unit */
- sanei_genesys_set_triple(reg, REG_MAXWD, (words_per_line >> 2));
- DBG (DBG_io2, "%s: words_per_line used=%d\n", __func__, words_per_line);
-
- sanei_genesys_set_double(reg, REG_LPERIOD, exposure_time);
- DBG (DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time);
-
- r = sanei_genesys_get_address (reg, 0x34);
- r->value = sensor.dummy_pixel;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* set up registers for an actual scan
- *
- * this function sets up the scanner to scan in normal or single line mode
- */
-static SANE_Status
-gl846_init_scan_regs(Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SetupParams& params)
-{
- params.assert_valid();
-
- int used_res;
- int start, used_pixels;
- int bytes_per_line;
- int move;
- unsigned int lincnt;
- unsigned int oflags; /**> optical flags */
- unsigned int mflags; /**> motor flags */
- int exposure_time;
- int stagger;
-
- int slope_dpi = 0;
- int dummy = 0;
- int scan_step_type = 1;
- int scan_power_mode = 0;
- int max_shift;
- size_t requested_buffer_size, read_buffer_size;
-
- SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
- int optical_res;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
- /* we may have 2 domains for ccd: xres below or above half ccd max dpi */
- if (sensor.get_ccd_size_divisor_for_dpi(params.xres) > 1)
- {
- half_ccd = SANE_TRUE;
- }
- else
- {
- half_ccd = SANE_FALSE;
- }
-
- /* optical_res */
- optical_res = sensor.optical_res;
- if (half_ccd)
- optical_res /= 2;
-
- /* stagger */
- if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG(DBG_info, "%s : stagger=%d lines\n", __func__, stagger);
-
- /* used_res */
- if (params.flags & SCAN_FLAG_USE_OPTICAL_RES)
- {
- used_res = optical_res;
- }
- else
- {
- /* resolution is choosen from a list */
- used_res = params.xres;
- }
-
- /* compute scan parameters values */
- /* pixels are allways given at full optical resolution */
- /* use detected left margin and fixed value */
- /* start */
- /* add x coordinates */
- start = params.startx;
-
- if (stagger > 0)
- start |= 1;
-
- /* compute correct pixels number */
- /* pixels */
- used_pixels = (params.pixels * optical_res) / params.xres;
-
- /* round up pixels number if needed */
- if (used_pixels * params.xres < params.pixels * optical_res)
- used_pixels++;
-
- dummy = 3-params.channels;
-
-/* slope_dpi */
-/* cis color scan is effectively a gray scan with 3 gray lines per color
- line and a FILTER of 0 */
- if (dev->model->is_cis)
- slope_dpi = params.yres * params.channels;
- else
- slope_dpi = params.yres;
-
- slope_dpi = slope_dpi * (1 + dummy);
-
- exposure_time = gl846_compute_exposure (dev, used_res);
- scan_step_type = sanei_genesys_compute_step_type(gl846_motors, dev->model->motor_type, exposure_time);
-
- DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
- DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, scan_step_type);
-
-/*** optical parameters ***/
- /* in case of dynamic lineart, we use an internal 8 bit gray scan
- * to generate 1 lineart data */
- if (params.flags & SCAN_FLAG_DYNAMIC_LINEART) {
- params.depth = 8;
- }
-
- /* we enable true gray for cis scanners only, and just when doing
- * scan since color calibration is OK for this mode
- */
- oflags = 0;
- if(params.flags & SCAN_FLAG_DISABLE_SHADING)
- oflags |= OPTICAL_FLAG_DISABLE_SHADING;
- if(params.flags & SCAN_FLAG_DISABLE_GAMMA)
- oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
- if(params.flags & SCAN_FLAG_DISABLE_LAMP)
- oflags |= OPTICAL_FLAG_DISABLE_LAMP;
-
- if (dev->model->is_cis && dev->settings.true_gray)
- {
- oflags |= OPTICAL_FLAG_ENABLE_LEDADD;
- }
-
- status = gl846_init_optical_regs_scan (dev, sensor,
- reg,
- exposure_time,
- used_res,
- start,
- used_pixels,
- params.channels,
- params.depth,
- half_ccd,
- params.color_filter,
- oflags);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
-/*** motor parameters ***/
-
- /* max_shift */
- max_shift=sanei_genesys_compute_max_shift(dev,params.channels,params.yres,params.flags);
-
- /* lincnt */
- lincnt = params.lines + max_shift + stagger;
-
- /* add tl_y to base movement */
- move = params.starty;
- DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
-
- mflags=0;
- if(params.flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
- mflags |= MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
- if(params.flags & SCAN_FLAG_FEEDING)
- mflags |= MOTOR_FLAG_FEED;
-
- status = gl846_init_motor_regs_scan (dev, sensor,
- reg,
- exposure_time,
- slope_dpi,
- scan_step_type,
- dev->model->is_cis ? lincnt *
- params.channels : lincnt, dummy, move,
- scan_power_mode,
- mflags);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
-
- /*** prepares data reordering ***/
-
-/* words_per_line */
- bytes_per_line = (used_pixels * used_res) / optical_res;
- bytes_per_line = (bytes_per_line * params.channels * params.depth) / 8;
-
- requested_buffer_size = 8 * bytes_per_line;
-
- read_buffer_size =
- 2 * requested_buffer_size +
- ((max_shift + stagger) * used_pixels * params.channels * params.depth) / 8;
-
- dev->read_buffer.clear();
- dev->read_buffer.alloc(read_buffer_size);
-
- dev->lines_buffer.clear();
- dev->lines_buffer.alloc(read_buffer_size);
-
- dev->shrink_buffer.clear();
- dev->shrink_buffer.alloc(requested_buffer_size);
-
- dev->out_buffer.clear();
- dev->out_buffer.alloc((8 * params.pixels * params.channels * params.depth) / 8);
-
- dev->read_bytes_left = bytes_per_line * lincnt;
-
- DBG(DBG_info, "%s: physical bytes to read = %lu\n", __func__, (u_long) dev->read_bytes_left);
- dev->read_active = SANE_TRUE;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
-/* TODO: should this be done elsewhere? */
- /* scan bytes to send to the frontend */
- /* theory :
- target_size =
- (params.pixels * params.lines * channels * depth) / 8;
- but it suffers from integer overflow so we do the following:
-
- 1 bit color images store color data byte-wise, eg byte 0 contains
- 8 bits of red data, byte 1 contains 8 bits of green, byte 2 contains
- 8 bits of blue.
- This does not fix the overflow, though.
- 644mp*16 = 10gp, leading to an overflow
- -- pierre
- */
-
- dev->total_bytes_read = 0;
- if (params.depth == 1)
- dev->total_bytes_to_read =
- ((params.pixels * params.lines) / 8 +
- (((params.pixels * params.lines) % 8) ? 1 : 0)) *
- params.channels;
- else
- dev->total_bytes_to_read =
- params.pixels * params.lines * params.channels * (params.depth / 8);
-
- DBG(DBG_info, "%s: total bytes to send = %lu\n", __func__, (u_long) dev->total_bytes_to_read);
-/* END TODO */
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static void
-gl846_calculate_current_setup(Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int depth;
- int start;
-
- int used_res;
- int used_pixels;
- unsigned int lincnt;
- int exposure_time;
- int stagger;
-
- int slope_dpi;
- int dummy = 0;
- int max_shift;
-
- SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
- int optical_res;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
- /* start */
- start = SANE_UNFIX (dev->model->x_offset);
- start += dev->settings.tl_x;
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start; // not used
- params.starty = 0; // not used
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = 0;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
-/* half_ccd */
- /* we have 2 domains for ccd: xres below or above half ccd max dpi */
- if (sensor.get_ccd_size_divisor_for_dpi(params.xres) > 1) {
- half_ccd = SANE_TRUE;
- } else {
- half_ccd = SANE_FALSE;
- }
-
- /* optical_res */
- optical_res = sensor.optical_res;
-
- /* stagger */
- if (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE)
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG(DBG_info, "%s: stagger=%d lines\n", __func__, stagger);
-
- /* resolution is choosen from a fixed list */
- used_res = params.xres;
-
- /* compute scan parameters values */
- /* pixels are allways given at half or full CCD optical resolution */
- /* use detected left margin and fixed value */
-
- /* compute correct pixels number */
- used_pixels = (params.pixels * optical_res) / used_res;
- dummy = 3 - params.channels;
-
- /* cis color scan is effectively a gray scan with 3 gray lines per color
- line and a FILTER of 0 */
- if (dev->model->is_cis) {
- slope_dpi = params.yres * params.channels;
- } else {
- slope_dpi = params.yres;
- }
-
- slope_dpi = slope_dpi * (1 + dummy);
-
- exposure_time = gl846_compute_exposure (dev, used_res);
- DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
-
- max_shift = sanei_genesys_compute_max_shift(dev, params.channels, params.yres, 0);
-
- /* lincnt */
- lincnt = params.lines + max_shift + stagger;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
- DBGCOMPLETED;
-}
-
-/*for fast power saving methods only, like disabling certain amplifiers*/
-static SANE_Status
-gl846_save_power (Genesys_Device * dev, SANE_Bool enable)
-{
- DBG(DBG_proc, "%s: enable = %d\n", __func__, enable);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl846_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
-{
- DBG(DBG_proc, "%s (delay = %d)\n", __func__, delay);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl846_start_action (Genesys_Device * dev)
-{
- return sanei_genesys_write_register (dev, 0x0f, 0x01);
-}
-
-static SANE_Status
-gl846_stop_action (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val40, val;
- unsigned int loop;
-
- DBGSTART;
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl846_homsnr_gpio(dev);
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- status = sanei_genesys_read_register (dev, REG40, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* only stop action if needed */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
- {
- DBG(DBG_info, "%s: already stopped\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- /* ends scan */
- val = dev->reg.get8(REG01);
- val &= ~REG01_SCAN;
- dev->reg.set8(REG01, val);
- status = sanei_genesys_write_register (dev, REG01, val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register 01: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_sleep_ms(100);
-
- loop = 10;
- while (loop > 0)
- {
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- status = sanei_genesys_read_register (dev, REG40, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* if scanner is in command mode, we are done */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG)
- && !(val & REG41_MOTORENB))
- {
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- sanei_genesys_sleep_ms(100);
- loop--;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_IO_ERROR;
-}
-
-/* Send the low-level scan command */
-static SANE_Status
-gl846_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SANE_Bool start_motor)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- GenesysRegister *r;
-
- DBGSTART;
-
- /* XXX STEF XXX SCAN GPIO */
- /*
- RIE (sanei_genesys_read_register (dev, REG6C, &val));
- RIE (sanei_genesys_write_register (dev, REG6C, val));
- */
-
- val = REG0D_CLRLNCNT;
- RIE (sanei_genesys_write_register (dev, REG0D, val));
- val = REG0D_CLRMCNT;
- RIE (sanei_genesys_write_register (dev, REG0D, val));
-
- RIE (sanei_genesys_read_register (dev, REG01, &val));
- val |= REG01_SCAN;
- RIE (sanei_genesys_write_register (dev, REG01, val));
- r = sanei_genesys_get_address (reg, REG01);
- r->value = val;
-
- if (start_motor)
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 1));
- }
- else
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 0));
- }
-
- DBGCOMPLETED;
-
- return status;
-}
-
-
-/* Send the stop scan command */
-static SANE_Status
-gl846_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (check_stop = %d)\n", __func__, check_stop);
- if (reg == NULL)
- return SANE_STATUS_INVAL;
-
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- status = SANE_STATUS_GOOD;
- }
- else /* flat bed scanners */
- {
- status = gl846_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/* Moves the slider to the home (top) postion slowly */
-static SANE_Status
-gl846_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- float resolution;
- uint8_t val;
- int loop = 0;
- ScanColorMode scan_mode;
-
- DBG(DBG_proc, "%s (wait_until_home = %d)\n", __func__, wait_until_home);
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl846_homsnr_gpio(dev);
-
- /* first read gives HOME_SENSOR true */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- sanei_genesys_sleep_ms(100);
-
- /* second is reliable */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- /* is sensor at home? */
- if (val & HOMESNR)
- {
- DBG(DBG_info, "%s: already at home, completed\n", __func__);
- dev->scanhead_position_in_steps = 0;
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- local_reg = dev->reg;
-
- resolution=sanei_genesys_get_lowest_ydpi(dev);
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- /* TODO add scan_mode to the API */
- scan_mode= dev->settings.scan_mode;
- dev->settings.scan_mode = ScanColorMode::LINEART;
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 100;
- params.starty = 30000;
- params.pixels = 100;
- params.lines = 100;
- params.depth = 8;
- params.channels = 1;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl846_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
- dev->settings.scan_mode=scan_mode;
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
-
- /* set up for reverse */
- r = sanei_genesys_get_address(&local_reg, REG02);
- r->value |= REG02_MTRREV;
-
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- try {
- status = gl846_start_action(dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl846_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl846_stop_action (dev);
- /* send original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl846_homsnr_gpio(dev);
-
- if (wait_until_home)
- {
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- if (val & HOMESNR) /* home sensor */
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- gl846_stop_action (dev);
- dev->scanhead_position_in_steps = 0;
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl846_stop_action (dev);
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
- area at 600 dpi from very top of scanner */
-static SANE_Status
-gl846_search_start_position (Genesys_Device * dev)
-{
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- int steps;
-
- int pixels = 600;
- int dpi = 300;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- local_reg = dev->reg;
-
- /* sets for a 200 lines * 600 pixels */
- /* normal scan with no shading */
-
- // FIXME: the current approach of doing search only for one resolution does not work on scanners
- // whith employ different sensors with potentially different settings.
- auto& sensor = sanei_genesys_find_sensor_for_write(dev, dpi);
-
- SetupParams params;
- params.xres = dpi;
- params.yres = dpi;
- params.startx = 0;
- params.starty = 0; /*we should give a small offset here~60 steps */
- params.pixels = 600;
- params.lines = dev->model->search_lines;
- params.depth = 8;
- params.channels = 1;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::GREEN;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl846_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* send to scanner */
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- size = pixels * dev->model->search_lines;
-
- std::vector<uint8_t> data(size);
-
- status = gl846_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl846_search_position.pnm", data.data(), 8, 1, pixels,
- dev->model->search_lines);
-
- status = gl846_end_scan (dev, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* update regs to copy ASIC internal state */
- dev->reg = local_reg;
-
-/*TODO: find out where sanei_genesys_search_reference_point
- stores information, and use that correctly*/
- status =
- sanei_genesys_search_reference_point (dev, sensor, data.data(), 0, dpi, pixels,
- dev->model->search_lines);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set search reference point: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/*
- * sets up register for coarse gain calibration
- * todo: check it for scanners using it */
-static SANE_Status
-gl846_init_regs_for_coarse_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t channels;
- uint8_t cksel;
-
- DBG(DBG_proc, "%s\n", __func__);
-
-
- cksel = (regs.find_reg(0x18).value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
-
- /* set line size */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else {
- channels = 1;
- }
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = sensor.optical_res / cksel;
- params.lines = 20;
- params.depth = 16;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl846_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
- sensor.optical_res / cksel, dev->settings.xres);
-
- status = dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief moves the slider to steps at motor base dpi
- * @param dev device to work on
- * @param steps number of steps to move in base_dpi line count
- * */
-static SANE_Status
-gl846_feed (Genesys_Device * dev, unsigned int steps)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- float resolution;
- uint8_t val;
-
- DBGSTART;
- DBG(DBG_io, "%s: steps=%d\n", __func__, steps);
-
- /* prepare local registers */
- local_reg = dev->reg;
-
- resolution=sanei_genesys_get_lowest_ydpi(dev);
- const auto& sensor = sanei_genesys_find_sensor(dev, resolution);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = steps;
- params.pixels = 100;
- params.lines = 3;
- params.depth = 8;
- params.channels = 3;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_FEEDING |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl846_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* set exposure to zero */
- sanei_genesys_set_triple(&local_reg,REG_EXPR,0);
- sanei_genesys_set_triple(&local_reg,REG_EXPG,0);
- sanei_genesys_set_triple(&local_reg,REG_EXPB,0);
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
-
- /* set up for no scan */
- r = sanei_genesys_get_address(&local_reg, REG01);
- r->value &= ~REG01_SCAN;
-
- /* send registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- try {
- status = gl846_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl846_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl846_stop_action (dev);
-
- /* restore original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- /* wait until feed count reaches the required value, but do not
- * exceed 30s */
- do
- {
- status = sanei_genesys_get_status (dev, &val);
- }
- while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
-
- /* then stop scanning */
- RIE(gl846_stop_action (dev));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* init registers for shading calibration */
-static SANE_Status
-gl846_init_regs_for_shading(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- float move;
-
- DBGSTART;
- dev->calib_channels = 3;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- dev->calib_resolution = sanei_genesys_compute_dpihw(dev, sensor, dev->settings.xres);
- dev->calib_total_bytes_to_read = 0;
- dev->calib_lines = dev->model->shading_lines;
- if(dev->calib_resolution==4800)
- dev->calib_lines *= 2;
- dev->calib_pixels = (sensor.sensor_pixels*dev->calib_resolution)/sensor.optical_res;
- DBG(DBG_io, "%s: calib_lines = %d\n", __func__, (unsigned int)dev->calib_lines);
- DBG(DBG_io, "%s: calib_pixels = %d\n", __func__, (unsigned int)dev->calib_pixels);
-
- /* this is aworkaround insufficent distance for slope
- * motor acceleration TODO special motor slope for shading */
- move=1;
- if(dev->calib_resolution<1200)
- {
- move=40;
- }
-
- SetupParams params;
- params.xres = dev->calib_resolution;
- params.yres = dev->calib_resolution;
- params.startx = 0;
- params.starty = move;
- params.pixels = dev->calib_pixels;
- params.lines = dev->calib_lines;
- params.depth = 16;
- params.channels = dev->calib_channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl846_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* we use GENESYS_FLAG_SHADING_REPARK */
- dev->scanhead_position_in_steps = 0;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief set up registers for the actual scan
- */
-static SANE_Status
-gl846_init_regs_for_scan (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int flags;
- int depth;
- float move;
- int move_dpi;
- float start;
-
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
-
- /* steps to move to reach scanning area:
- - first we move to physical start of scanning
- either by a fixed steps amount from the black strip
- or by a fixed amount from parking position,
- minus the steps done during shading calibration
- - then we move by the needed offset whitin physical
- scanning area
-
- assumption: steps are expressed at maximum motor resolution
-
- we need:
- SANE_Fixed y_offset;
- SANE_Fixed y_size;
- SANE_Fixed y_offset_calib;
- mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
-
- /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
- relative from origin, else, it is from parking position */
-
- move_dpi = dev->motor.base_ydpi;
-
- move = SANE_UNFIX (dev->model->y_offset);
- move += dev->settings.tl_y;
- move = (move * move_dpi) / MM_PER_INCH;
- move -= dev->scanhead_position_in_steps;
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- /* fast move to scan area */
- /* we don't move fast the whole distance since it would involve
- * computing acceleration/deceleration distance for scan
- * resolution. So leave a remainder for it so scan makes the final
- * move tuning */
- if(channels*dev->settings.yres>=600 && move>700)
- {
- status = gl846_feed (dev, move-500);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move to scan area\n", __func__);
- return status;
- }
- move=500;
- }
-
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- /* start */
- start = SANE_UNFIX (dev->model->x_offset);
- start += dev->settings.tl_x;
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- flags = 0;
-
- /* emulated lineart from gray data is required for now */
- if(dev->settings.scan_mode == ScanColorMode::LINEART
- && dev->settings.dynamic_lineart)
- {
- flags |= SCAN_FLAG_DYNAMIC_LINEART;
- }
-
- /* backtracking isn't handled well, so don't enable it */
- flags |= SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start;
- params.starty = move;
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = flags;
-
- status = gl846_init_scan_regs(dev, sensor, &dev->reg, params);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/**
- * Send shading calibration data. The buffer is considered to always hold values
- * for all the channels.
- */
-static SANE_Status
-gl846_send_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data, int size)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint32_t addr, length, i, x, factor, pixels;
- uint32_t dpiset, dpihw, strpixel, endpixel;
- uint16_t tempo;
- uint32_t lines, channels;
- uint8_t val,*ptr,*src;
-
- DBGSTART;
- DBG(DBG_io2, "%s: writing %d bytes of shading data\n", __func__, size);
-
- /* shading data is plit in 3 (up to 5 with IR) areas
- write(0x10014000,0x00000dd8)
- URB 23429 bulk_out len 3544 wrote 0x33 0x10 0x....
- write(0x1003e000,0x00000dd8)
- write(0x10068000,0x00000dd8)
- */
- length = (uint32_t) (size / 3);
- sanei_genesys_get_double(&dev->reg,REG_STRPIXEL,&tempo);
- strpixel=tempo;
- sanei_genesys_get_double(&dev->reg,REG_ENDPIXEL,&tempo);
- endpixel=tempo;
-
- /* compute deletion factor */
- sanei_genesys_get_double(&dev->reg,REG_DPISET,&tempo);
- dpiset=tempo;
- DBG(DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d, PIXELS=%d, DPISET=%d\n", __func__, strpixel, endpixel,
- endpixel-strpixel, dpiset);
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dpiset);
- factor=dpihw/dpiset;
- DBG(DBG_io2, "%s: factor=%d\n", __func__, factor);
-
- if(DBG_LEVEL>=DBG_data)
- {
- dev->binary=fopen("binary.pnm","wb");
- sanei_genesys_get_triple(&dev->reg, REG_LINCNT, &lines);
- channels=dev->current_setup.channels;
- if(dev->binary!=NULL)
- {
- fprintf(dev->binary,"P5\n%d %d\n%d\n",(endpixel-strpixel)/factor*channels,lines/channels,255);
- }
- }
-
- pixels=endpixel-strpixel;
-
- /* since we're using SHDAREA, substract startx coordinate from shading */
- strpixel-=((sensor.CCD_start_xoffset*600)/sensor.optical_res);
-
- /* turn pixel value into bytes 2x16 bits words */
- strpixel*=2*2;
- pixels*=2*2;
-
- std::vector<uint8_t> buffer(pixels, 0);
-
- DBG(DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n", __func__, pixels, pixels);
-
- /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address
- * is 8192*reg value */
-
- /* write actual color channel data */
- for(i=0;i<3;i++)
- {
- /* build up actual shading data by copying the part from the full width one
- * to the one corresponding to SHDAREA */
- ptr = buffer.data();
-
- /* iterate on both sensor segment */
- for(x=0;x<pixels;x+=4*factor)
- {
- /* coefficient source */
- src=(data+strpixel+i*length)+x;
-
- /* coefficient copy */
- ptr[0]=src[0];
- ptr[1]=src[1];
- ptr[2]=src[2];
- ptr[3]=src[3];
-
- /* next shading coefficient */
- ptr+=4;
- }
-
- RIE(sanei_genesys_read_register(dev, 0xd0+i, &val));
- addr = val * 8192 + 0x10000000;
- status = sanei_genesys_write_ahb(dev, addr, pixels, buffer.data());
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s; write to AHB failed (%s)\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
-
- return status;
-}
-
-/** @brief calibrates led exposure
- * Calibrate exposure by scanning a white area until the used exposure gives
- * data white enough.
- * @param dev device to calibrate
- */
-static SANE_Status
-gl846_led_calibration (Genesys_Device * dev, Genesys_Sensor& sensor, Genesys_Register_Set& regs)
-{
- int num_pixels;
- int total_size;
- int used_res;
- int i, j;
- SANE_Status status = SANE_STATUS_GOOD;
- int val;
- int channels, depth;
- int avg[3], top[3], bottom[3];
- int turn;
- uint16_t exp[3];
- float move;
- SANE_Bool acceptable;
-
- DBGSTART;
-
- move = SANE_UNFIX (dev->model->y_offset_calib);
- move = (move * (dev->motor.base_ydpi/4)) / MM_PER_INCH;
- if(move>20)
- {
- RIE(gl846_feed (dev, move));
- }
- DBG(DBG_io, "%s: move=%f steps\n", __func__, move);
-
- /* offset calibration is always done in color mode */
- channels = 3;
- depth=16;
- used_res=sanei_genesys_compute_dpihw(dev, sensor, dev->settings.xres);
- Sensor_Profile* sensor_profile = get_sensor_profile(dev->model->ccd_type, used_res);
- num_pixels = (sensor.sensor_pixels*used_res)/sensor.optical_res;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- SetupParams params;
- params.xres = used_res;
- params.yres = used_res;
- params.startx = 0;
- params.starty = 0;
- params.pixels = num_pixels;
- params.lines = 1;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl846_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- total_size = num_pixels * channels * (depth/8) * 1; /* colors * bytes_per_color * scan lines */
- std::vector<uint8_t> line(total_size);
-
- /* initial loop values and boundaries */
- exp[0]=sensor_profile->expr;
- exp[1]=sensor_profile->expg;
- exp[2]=sensor_profile->expb;
-
- bottom[0]=29000;
- bottom[1]=29000;
- bottom[2]=29000;
-
- top[0]=41000;
- top[1]=51000;
- top[2]=51000;
-
- turn = 0;
-
- /* no move during led calibration */
- sanei_genesys_set_motor_power(regs, false);
- do
- {
- /* set up exposure */
- sanei_genesys_set_double(&regs,REG_EXPR,exp[0]);
- sanei_genesys_set_double(&regs,REG_EXPG,exp[1]);
- sanei_genesys_set_double(&regs,REG_EXPB,exp[2]);
-
- /* write registers and scan data */
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- DBG(DBG_info, "%s: starting line reading\n", __func__);
- RIE(gl846_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
-
- /* stop scanning */
- RIE(gl846_stop_action(dev));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl846_led_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, line.data(), depth, channels, num_pixels, 1);
- }
-
- /* compute average */
- for (j = 0; j < channels; j++)
- {
- avg[j] = 0;
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- line[i * 2 + j * 2 * num_pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- avg[j] += val;
- }
-
- avg[j] /= num_pixels;
- }
-
- DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
-
- /* check if exposure gives average within the boundaries */
- acceptable = SANE_TRUE;
- for(i=0;i<3;i++)
- {
- if(avg[i]<bottom[i])
- {
- exp[i]=(exp[i]*bottom[i])/avg[i];
- acceptable = SANE_FALSE;
- }
- if(avg[i]>top[i])
- {
- exp[i]=(exp[i]*top[i])/avg[i];
- acceptable = SANE_FALSE;
- }
- }
-
- turn++;
- }
- while (!acceptable && turn < 100);
-
- DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
-
- /* set these values as final ones for scan */
- sanei_genesys_set_double(&dev->reg,REG_EXPR,exp[0]);
- sanei_genesys_set_double(&dev->reg,REG_EXPG,exp[1]);
- sanei_genesys_set_double(&dev->reg,REG_EXPB,exp[2]);
-
- /* store in this struct since it is the one used by cache calibration */
- sensor.exposure.red = exp[0];
- sensor.exposure.green = exp[1];
- sensor.exposure.blue = exp[2];
-
- /* go back home */
- if(move>20)
- {
- status=gl846_slow_back_home (dev, SANE_TRUE);
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * set up GPIO/GPOE for idle state
- */
-static SANE_Status
-gl846_init_gpio (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int idx=0;
-
- DBGSTART;
-
- /* search GPIO profile */
- while(gpios[idx].sensor_id!=0 && dev->model->gpo_type!=gpios[idx].sensor_id)
- {
- idx++;
- }
- if(gpios[idx].sensor_id==0)
- {
- DBG(DBG_error, "%s: failed to find GPIO profile for sensor_id=%d\n", __func__,
- dev->model->ccd_type);
- return SANE_STATUS_INVAL;
- }
-
- RIE (sanei_genesys_write_register (dev, REGA7, gpios[idx].ra7));
- RIE (sanei_genesys_write_register (dev, REGA6, gpios[idx].ra6));
-
- RIE (sanei_genesys_write_register (dev, REG6B, gpios[idx].r6b));
- RIE (sanei_genesys_write_register (dev, REG6C, gpios[idx].r6c));
- RIE (sanei_genesys_write_register (dev, REG6D, gpios[idx].r6d));
- RIE (sanei_genesys_write_register (dev, REG6E, gpios[idx].r6e));
- RIE (sanei_genesys_write_register (dev, REG6F, gpios[idx].r6f));
-
- RIE (sanei_genesys_write_register (dev, REGA8, gpios[idx].ra8));
- RIE (sanei_genesys_write_register (dev, REGA9, gpios[idx].ra9));
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * set memory layout by filling values in dedicated registers
- */
-static SANE_Status
-gl846_init_memory_layout (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int idx = 0, i;
- uint8_t val;
-
- DBGSTART
-
- /* point to per model memory layout */
- idx = 0;
- while(layouts[idx].model!=NULL && strcmp(dev->model->name,layouts[idx].model)!=0)
- {
- if(strcmp(dev->model->name,layouts[idx].model)!=0)
- idx++;
- }
- if(layouts[idx].model==NULL)
- {
- DBG(DBG_error, "%s: failed to find memory layout for model %s!\n", __func__, dev->model->name);
- return SANE_STATUS_INVAL;
- }
-
- /* CLKSET and DRAMSEL */
- val = layouts[idx].dramsel;
- RIE (sanei_genesys_write_register (dev, REG0B, val));
- dev->reg.find_reg(0x0b).value = val;
-
- /* prevent further writings by bulk write register */
- dev->reg.remove_reg(0x0b);
-
- /* setup base address for shading and scanned data. */
- for(i=0;i<10;i++)
- {
- sanei_genesys_write_register (dev, 0xe0+i, layouts[idx].rx[i]);
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/* *
- * initialize ASIC from power on condition
- */
-static SANE_Status
-gl846_boot (Genesys_Device * dev, SANE_Bool cold)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- DBGSTART;
-
- /* reset ASIC if cold boot */
- if(cold)
- {
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
- }
-
- if(dev->usb_mode == 1)
- {
- val = 0x14;
- }
- else
- {
- val = 0x11;
- }
- RIE (sanei_genesys_write_0x8c (dev, 0x0f, val));
-
- /* test CHKVER */
- RIE (sanei_genesys_read_register (dev, REG40, &val));
- if (val & REG40_CHKVER)
- {
- RIE (sanei_genesys_read_register (dev, 0x00, &val));
- DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
- }
-
- /* Set default values for registers */
- gl846_init_registers (dev);
-
- /* Write initial registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, dev->reg));
-
- /* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
- val = dev->reg.find_reg(0x0b).value & REG0B_DRAMSEL;
- val = (val | REG0B_ENBDRAM);
- RIE (sanei_genesys_write_register (dev, REG0B, val));
- dev->reg.find_reg(0x0b).value = val;
-
- /* CIS_LINE */
- if (dev->model->is_cis)
- {
- SETREG (0x08, REG08_CIS_LINE);
- RIE (sanei_genesys_write_register (dev, 0x08, dev->reg.find_reg(0x08).value));
- }
-
- /* set up clocks */
- RIE (sanei_genesys_write_0x8c (dev, 0x10, 0x0e));
- RIE (sanei_genesys_write_0x8c (dev, 0x13, 0x0e));
-
- /* setup gpio */
- RIE (gl846_init_gpio (dev));
-
- /* setup internal memory layout */
- RIE (gl846_init_memory_layout (dev));
-
- SETREG (0xf8, 0x05);
- RIE (sanei_genesys_write_register (dev, 0xf8, dev->reg.find_reg(0xf8).value));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/**
- * initialize backend and ASIC : registers, motor tables, and gamma tables
- * then ensure scanner's head is at home
- */
-static SANE_Status gl846_init(Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG_INIT ();
- DBGSTART;
-
- status=sanei_genesys_asic_init(dev, 0);
-
- DBGCOMPLETED;
- return status;
-}
-
-static SANE_Status
-gl846_update_hardware_sensors (Genesys_Scanner * s)
-{
- /* do what is needed to get a new set of events, but try to not lose
- any of them.
- */
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- uint8_t scan, file, email, copy;
- switch(s->dev->model->gpo_type)
- {
- default:
- scan=0x01;
- file=0x02;
- email=0x04;
- copy=0x08;
- }
- RIE (sanei_genesys_read_register (s->dev, REG6D, &val));
-
- s->buttons[BUTTON_SCAN_SW].write((val & scan) == 0);
- s->buttons[BUTTON_FILE_SW].write((val & file) == 0);
- s->buttons[BUTTON_EMAIL_SW].write((val & email) == 0);
- s->buttons[BUTTON_COPY_SW].write((val & copy) == 0);
-
- return status;
-}
-
-/** @brief search for a full width black or white strip.
- * This function searches for a black or white stripe across the scanning area.
- * When searching backward, the searched area must completely be of the desired
- * color since this area will be used for calibration which scans forward.
- * @param dev scanner device
- * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
- * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
- * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
- */
-static SANE_Status
-gl846_search_strip(Genesys_Device * dev, const Genesys_Sensor& sensor,
- SANE_Bool forward, SANE_Bool black)
-{
- unsigned int pixels, lines, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- size_t size;
- int steps, depth, dpi;
- unsigned int pass, count, found, x, y;
- char title[80];
- GenesysRegister *r;
-
- DBG(DBG_proc, "%s %s %s\n", __func__, black ? "black" : "white", forward ? "forward" : "reverse");
-
- status = gl846_set_fe(dev, sensor, AFE_SET);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl846_set_fe() failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl846_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* set up for a gray scan at lowest dpi */
- dpi = 9600;
- for (x = 0; x < MAX_RESOLUTIONS; x++)
- {
- if (dev->model->xdpi_values[x] > 0 && dev->model->xdpi_values[x] < dpi)
- dpi = dev->model->xdpi_values[x];
- }
- channels = 1;
- /* 10 MM */
- /* lines = (10 * dpi) / MM_PER_INCH; */
- /* shading calibation is done with dev->motor.base_ydpi */
- lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
- depth = 8;
- pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res;
- size = pixels * channels * lines * (depth / 8);
- std::vector<uint8_t> data(size);
-
- dev->scanhead_position_in_steps = 0;
-
- local_reg = dev->reg;
-
- SetupParams params;
- params.xres = dpi;
- params.yres = dpi;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA;
-
- status = gl846_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup for scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* set up for reverse or forward */
- r = sanei_genesys_get_address (&local_reg, REG02);
- if (forward)
- r->value &= ~REG02_MTRREV;
- else
- r->value |= REG02_MTRREV;
-
-
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl846_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl846_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl846_stop_action failed\n", __func__);
- return status;
- }
-
- pass = 0;
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf(title, "gl846_search_strip_%s_%s%02d.pnm",
- black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
- sanei_genesys_write_pnm_file(title, data.data(), depth, channels, pixels, lines);
- }
-
- /* loop until strip is found or maximum pass number done */
- found = 0;
- while (pass < 20 && !found)
- {
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to bulk write registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* now start scan */
- status = gl846_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl846_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl846_stop_action failed\n", __func__);
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf(title, "gl846_search_strip_%s_%s%02d.pnm",
- black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
- sanei_genesys_write_pnm_file(title, data.data(), depth, channels, pixels, lines);
- }
-
- /* search data to find black strip */
- /* when searching forward, we only need one line of the searched color since we
- * will scan forward. But when doing backward search, we need all the area of the
- * same color */
- if (forward)
- {
- for (y = 0; y < lines && !found; y++)
- {
- count = 0;
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > 90)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < 60)
- {
- count++;
- }
- }
-
- /* at end of line, if count >= 3%, line is not fully of the desired color
- * so we must go to next line of the buffer */
- /* count*100/pixels < 3 */
- if ((count * 100) / pixels < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
- pass, y);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- }
- else /* since calibration scans are done forward, we need the whole area
- to be of the required color when searching backward */
- {
- count = 0;
- for (y = 0; y < lines; y++)
- {
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > 90)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < 60)
- {
- count++;
- }
- }
- }
-
- /* at end of area, if count >= 3%, area is not fully of the desired color
- * so we must go to next buffer */
- if ((count * 100) / (pixels * lines) < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- pass++;
- }
-
- if (found)
- {
- status = SANE_STATUS_GOOD;
- DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
- }
- else
- {
- status = SANE_STATUS_UNSUPPORTED;
- DBG(DBG_info, "%s: %s strip not found\n", __func__, black ? "black" : "white");
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * average dark pixels of a 8 bits scan
- */
-static int
-dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
- unsigned int channels, unsigned int black)
-{
- unsigned int i, j, k, average, count;
- unsigned int avg[3];
- uint8_t val;
-
- /* computes average value on black margin */
- for (k = 0; k < channels; k++)
- {
- avg[k] = 0;
- count = 0;
- for (i = 0; i < lines; i++)
- {
- for (j = 0; j < black; j++)
- {
- val = data[i * channels * pixels + j + k];
- avg[k] += val;
- count++;
- }
- }
- if (count)
- avg[k] /= count;
- DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
- }
- average = 0;
- for (i = 0; i < channels; i++)
- average += avg[i];
- average /= channels;
- DBG(DBG_info, "%s: average = %d\n", __func__, average);
- return average;
-}
-
-static SANE_Status
-gl846_offset_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t reg04;
- unsigned int channels, bpp;
- int pass = 0, avg, total_size;
- int topavg, bottomavg, resolution, lines;
- int top, bottom, black_pixels, pixels;
-
- DBGSTART;
-
- /* no gain nor offset for AKM AFE */
- RIE (sanei_genesys_read_register (dev, REG04, &reg04));
- if ((reg04 & REG04_FESET) == 0x02)
- {
- DBGCOMPLETED;
- return status;
- }
-
- /* offset calibration is always done in color mode */
- channels = 3;
- resolution=sensor.optical_res;
- dev->calib_pixels = sensor.sensor_pixels;
- lines=1;
- bpp=8;
- pixels= (sensor.sensor_pixels*resolution) / sensor.optical_res;
- black_pixels = (sensor.black_pixels * resolution) / sensor.optical_res;
- DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = bpp;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl846_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_set_motor_power(regs, false);
-
- /* allocate memory for scans */
- total_size = pixels * channels * lines * (bpp/8); /* colors * bytes_per_color * scan lines */
-
- std::vector<uint8_t> first_line(total_size);
- std::vector<uint8_t> second_line(total_size);
-
- /* init gain */
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
-
- /* scan with no move */
- bottom = 10;
- dev->frontend.set_offset(0, bottom);
- dev->frontend.set_offset(1, bottom);
- dev->frontend.set_offset(2, bottom);
-
- RIE(gl846_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting first line reading\n", __func__);
- RIE(gl846_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, first_line.data(), total_size));
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl846_offset%03d.pnm", bottom);
- sanei_genesys_write_pnm_file(fn, first_line.data(), bpp, channels, pixels, lines);
- }
-
- bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
-
- /* now top value */
- top = 255;
- dev->frontend.set_offset(0, top);
- dev->frontend.set_offset(1, top);
- dev->frontend.set_offset(2, top);
- RIE(gl846_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl846_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, second_line.data(), total_size));
-
- topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
-
- /* loop until acceptable level */
- while ((pass < 32) && (top - bottom > 1))
- {
- pass++;
-
- /* settings for new scan */
- dev->frontend.set_offset(0, (top + bottom) / 2);
- dev->frontend.set_offset(1, (top + bottom) / 2);
- dev->frontend.set_offset(2, (top + bottom) / 2);
-
- /* scan with no move */
- RIE(gl846_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl846_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, second_line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl846_offset%03d.pnm", dev->frontend.get_offset(1));
- sanei_genesys_write_pnm_file(fn, second_line.data(), bpp, channels, pixels, lines);
- }
-
- avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
-
- /* compute new boundaries */
- if (topavg == avg)
- {
- topavg = avg;
- top = dev->frontend.get_offset(1);
- }
- else
- {
- bottomavg = avg;
- bottom = dev->frontend.get_offset(1);
- }
- }
- DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl846_coarse_gain_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi)
-{
- int pixels;
- int total_size;
- uint8_t reg04;
- int i, j, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- int max[3];
- float gain[3],coeff;
- int val, code, lines;
- int resolution;
- int bpp;
-
- DBG(DBG_proc, "%s: dpi = %d\n", __func__, dpi);
-
- /* no gain nor offset for AKM AFE */
- RIE (sanei_genesys_read_register (dev, REG04, &reg04));
- if ((reg04 & REG04_FESET) == 0x02)
- {
- DBGCOMPLETED;
- return status;
- }
-
- /* coarse gain calibration is always done in color mode */
- channels = 3;
-
- /* follow CKSEL */
- if(dev->settings.xres<sensor.optical_res)
- {
- coeff=0.9;
- /*resolution=sensor.optical_res/2;*/
- resolution=sensor.optical_res;
- }
- else
- {
- resolution=sensor.optical_res;
- coeff=1.0;
- }
- lines=10;
- bpp=8;
- pixels = (sensor.sensor_pixels * resolution) / sensor.optical_res;
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = bpp;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- try {
- status = gl846_init_scan_regs(dev, sensor, &regs, params);
- } catch (...) {
- try {
- sanei_genesys_set_motor_power(regs, false);
- } catch (...) {}
- throw;
- }
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- RIE (dev->model->cmd_set->bulk_write_register(dev, regs));
-
- total_size = pixels * channels * (16/bpp) * lines;
-
- std::vector<uint8_t> line(total_size);
-
- RIE(gl846_set_fe(dev, sensor, AFE_SET));
- RIE(gl846_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl846_gain.pnm", line.data(), bpp, channels, pixels, lines);
-
- /* average value on each channel */
- for (j = 0; j < channels; j++)
- {
- max[j] = 0;
- for (i = pixels/4; i < (pixels*3/4); i++)
- {
- if (dev->model->is_cis)
- val = line[i + j * pixels];
- else
- val = line[i * channels + j];
-
- max[j] += val;
- }
- max[j] = max[j] / (pixels/2);
-
- gain[j] = ((float) sensor.gain_white_ref*coeff) / max[j];
-
- /* turn logical gain value into gain code, checking for overflow */
- code = 283 - 208 / gain[j];
- if (code > 255)
- code = 255;
- else if (code < 0)
- code = 0;
- dev->frontend.set_gain(j, code);
-
- DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j],
- dev->frontend.get_gain(j));
- }
-
- if (dev->model->is_cis) {
- uint8_t gain0 = dev->frontend.get_gain(0);
- if (gain0 > dev->frontend.get_gain(1)) {
- gain0 = dev->frontend.get_gain(1);
- }
- if (gain0 > dev->frontend.get_gain(2)) {
- gain0 = dev->frontend.get_gain(2);
- }
- dev->frontend.set_gain(0, gain0);
- dev->frontend.set_gain(1, gain0);
- dev->frontend.set_gain(2, gain0);
- }
-
- RIE (gl846_stop_action (dev));
-
- status=gl846_slow_back_home (dev, SANE_TRUE);
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/** the gl846 command set */
-static Genesys_Command_Set gl846_cmd_set = {
- "gl846-generic", /* the name of this set */
-
- nullptr,
-
- gl846_init,
- NULL,
- gl846_init_regs_for_coarse_calibration,
- gl846_init_regs_for_shading,
- gl846_init_regs_for_scan,
-
- gl846_get_filter_bit,
- gl846_get_lineart_bit,
- gl846_get_bitset_bit,
- gl846_get_gain4_bit,
- gl846_get_fast_feed_bit,
- gl846_test_buffer_empty_bit,
- gl846_test_motor_flag_bit,
-
- gl846_set_fe,
- gl846_set_powersaving,
- gl846_save_power,
-
- gl846_begin_scan,
- gl846_end_scan,
-
- sanei_genesys_send_gamma_table,
-
- gl846_search_start_position,
-
- gl846_offset_calibration,
- gl846_coarse_gain_calibration,
- gl846_led_calibration,
-
- NULL,
- gl846_slow_back_home,
- NULL,
-
- sanei_genesys_bulk_write_register,
- NULL,
- sanei_genesys_bulk_read_data,
-
- gl846_update_hardware_sensors,
-
- NULL,
- NULL,
- NULL,
- gl846_search_strip,
-
- sanei_genesys_is_compatible_calibration,
- NULL,
- gl846_send_shading_data,
- gl846_calculate_current_setup,
- gl846_boot
-};
-
-SANE_Status
-sanei_gl846_init_cmd_set (Genesys_Device * dev)
-{
- dev->model->cmd_set = &gl846_cmd_set;
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_gl846.h b/backend/genesys_gl846.h
deleted file mode 100644
index 797c605..0000000
--- a/backend/genesys_gl846.h
+++ /dev/null
@@ -1,498 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2012-2013 Stéphane Voltz <stef.dev@free.fr>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#include "genesys.h"
-
-#define REG01 0x01
-#define REG01_CISSET 0x80
-#define REG01_DOGENB 0x40
-#define REG01_DVDSET 0x20
-#define REG01_STAGGER 0x10
-#define REG01_COMPENB 0x08
-#define REG01_TRUEGRAY 0x04
-#define REG01_SHDAREA 0x02
-#define REG01_SCAN 0x01
-
-#define REG02 0x02
-#define REG02_NOTHOME 0x80
-#define REG02_ACDCDIS 0x40
-#define REG02_AGOHOME 0x20
-#define REG02_MTRPWR 0x10
-#define REG02_FASTFED 0x08
-#define REG02_MTRREV 0x04
-#define REG02_HOMENEG 0x02
-#define REG02_LONGCURV 0x01
-
-#define REG03 0x03
-#define REG03_LAMPDOG 0x80
-#define REG03_AVEENB 0x40
-#define REG03_XPASEL 0x20
-#define REG03_LAMPPWR 0x10
-#define REG03_LAMPTIM 0x0f
-
-#define REG04 0x04
-#define REG04_LINEART 0x80
-#define REG04_BITSET 0x40
-#define REG04_AFEMOD 0x30
-#define REG04_FILTER 0x0c
-#define REG04_FESET 0x03
-
-#define REG04S_AFEMOD 4
-
-#define REG05 0x05
-#define REG05_DPIHW 0xc0
-#define REG05_DPIHW_600 0x00
-#define REG05_DPIHW_1200 0x40
-#define REG05_DPIHW_2400 0x80
-#define REG05_DPIHW_4800 0xc0
-#define REG05_MTLLAMP 0x30
-#define REG05_GMMENB 0x08
-#define REG05_MTLBASE 0x03
-
-#define REG06_SCANMOD 0xe0
-#define REG06S_SCANMOD 5
-#define REG06_PWRBIT 0x10
-#define REG06_GAIN4 0x08
-#define REG06_OPTEST 0x07
-
-#define REG07_LAMPSIM 0x80
-
-#define REG08_DRAM2X 0x80
-#define REG08_MPENB 0x20
-#define REG08_CIS_LINE 0x10
-#define REG08_IR1ENB 0x08
-#define REG08_IR2ENB 0x04
-#define REG08_ENB24M 0x01
-
-#define REG09_MCNTSET 0xc0
-#define REG09_EVEN1ST 0x20
-#define REG09_BLINE1ST 0x10
-#define REG09_BACKSCAN 0x08
-#define REG09_ENHANCE 0x04
-#define REG09_SHORTTG 0x02
-#define REG09_NWAIT 0x01
-
-#define REG09S_MCNTSET 6
-#define REG09S_CLKSET 4
-
-
-#define REG0A_LPWMEN 0x10
-
-#define REG0B 0x0b
-#define REG0B_DRAMSEL 0x07
-#define REG0B_ENBDRAM 0x08
-#define REG0B_ENBDRAM 0x08
-#define REG0B_RFHDIS 0x10
-#define REG0B_CLKSET 0xe0
-#define REG0B_24MHZ 0x00
-#define REG0B_30MHZ 0x20
-#define REG0B_40MHZ 0x40
-#define REG0B_48MHZ 0x60
-#define REG0B_60MHZ 0x80
-
-#define REG0C 0x0c
-#define REG0C_CCDLMT 0x0f
-
-#define REG0D 0x0d
-#define REG0D_SCSYNC 0x40
-#define REG0D_CLRERR 0x20
-#define REG0D_FULLSTP 0x10
-#define REG0D_SEND 0x80
-#define REG0D_CLRMCNT 0x04
-#define REG0D_CLRDOCJM 0x02
-#define REG0D_CLRLNCNT 0x01
-
-#define REG0F 0x0f
-
-#define REG16_CTRLHI 0x80
-#define REG16_TOSHIBA 0x40
-#define REG16_TGINV 0x20
-#define REG16_CK1INV 0x10
-#define REG16_CK2INV 0x08
-#define REG16_CTRLINV 0x04
-#define REG16_CKDIS 0x02
-#define REG16_CTRLDIS 0x01
-
-#define REG17_TGMODE 0xc0
-#define REG17_TGMODE_NO_DUMMY 0x00
-#define REG17_TGMODE_REF 0x40
-#define REG17_TGMODE_XPA 0x80
-#define REG17_TGW 0x3f
-#define REG17S_TGW 0
-
-#define REG18 0x18
-#define REG18_CNSET 0x80
-#define REG18_DCKSEL 0x60
-#define REG18_CKTOGGLE 0x10
-#define REG18_CKDELAY 0x0c
-#define REG18_CKSEL 0x03
-
-#define REG1A_SW2SET 0x80
-#define REG1A_SW1SET 0x40
-#define REG1A_MANUAL3 0x02
-#define REG1A_MANUAL1 0x01
-#define REG1A_CK4INV 0x08
-#define REG1A_CK3INV 0x04
-#define REG1A_LINECLP 0x02
-
-#define REG1C 0x1c
-#define REG1C_TGTIME 0x07
-
-#define REG1D_CK4LOW 0x80
-#define REG1D_CK3LOW 0x40
-#define REG1D_CK1LOW 0x20
-#define REG1D_TGSHLD 0x1f
-#define REG1DS_TGSHLD 0
-
-
-#define REG1E_WDTIME 0xf0
-#define REG1ES_WDTIME 4
-#define REG1E_LINESEL 0x0f
-#define REG1ES_LINESEL 0
-
-#define REG_FEDCNT 0x1f
-
-#define REG24 0x1c
-#define REG40 0x40
-#define REG40_DOCSNR 0x80
-#define REG40_ADFSNR 0x40
-#define REG40_COVERSNR 0x20
-#define REG40_CHKVER 0x10
-#define REG40_DOCJAM 0x08
-#define REG40_HISPDFLG 0x04
-#define REG40_MOTMFLG 0x02
-#define REG40_DATAENB 0x01
-
-#define REG41_PWRBIT 0x80
-#define REG41_BUFEMPTY 0x40
-#define REG41_FEEDFSH 0x20
-#define REG41_SCANFSH 0x10
-#define REG41_HOMESNR 0x08
-#define REG41_LAMPSTS 0x04
-#define REG41_FEBUSY 0x02
-#define REG41_MOTORENB 0x01
-
-#define REG58_VSMP 0xf8
-#define REG58S_VSMP 3
-#define REG58_VSMPW 0x07
-#define REG58S_VSMPW 0
-
-#define REG59_BSMP 0xf8
-#define REG59S_BSMP 3
-#define REG59_BSMPW 0x07
-#define REG59S_BSMPW 0
-
-#define REG5A_ADCLKINV 0x80
-#define REG5A_RLCSEL 0x40
-#define REG5A_CDSREF 0x30
-#define REG5AS_CDSREF 4
-#define REG5A_RLC 0x0f
-#define REG5AS_RLC 0
-
-#define REG5E_DECSEL 0xe0
-#define REG5ES_DECSEL 5
-#define REG5E_STOPTIM 0x1f
-#define REG5ES_STOPTIM 0
-
-#define REG60 0x60
-#define REG60_Z1MOD 0x1f
-#define REG61 0x61
-#define REG61_Z1MOD 0xff
-#define REG62 0x62
-#define REG62_Z1MOD 0xff
-
-#define REG63 0x63
-#define REG63_Z2MOD 0x1f
-#define REG64 0x64
-#define REG64_Z2MOD 0xff
-#define REG65 0x65
-#define REG65_Z2MOD 0xff
-
-#define REG60S_STEPSEL 5
-#define REG60_STEPSEL 0xe0
-#define REG60_FULLSTEP 0x00
-#define REG60_HALFSTEP 0x20
-#define REG60_EIGHTHSTEP 0x60
-#define REG60_16THSTEP 0x80
-
-#define REG63S_FSTPSEL 5
-#define REG63_FSTPSEL 0xe0
-#define REG63_FULLSTEP 0x00
-#define REG63_HALFSTEP 0x20
-#define REG63_EIGHTHSTEP 0x60
-#define REG63_16THSTEP 0x80
-
-#define REG67 0x67
-#define REG67_MTRPWM 0x80
-
-#define REG68 0x68
-#define REG68_FASTPWM 0x80
-
-#define REG6B 0x6b
-#define REG6B_MULTFILM 0x80
-#define REG6B_GPOM13 0x40
-#define REG6B_GPOM12 0x20
-#define REG6B_GPOM11 0x10
-#define REG6B_GPO18 0x02
-#define REG6B_GPO17 0x01
-
-#define REG6C 0x6c
-#define REG6C_GPIO16 0x80
-#define REG6C_GPIO15 0x40
-#define REG6C_GPIO14 0x20
-#define REG6C_GPIO13 0x10
-#define REG6C_GPIO12 0x08
-#define REG6C_GPIO11 0x04
-#define REG6C_GPIO10 0x02
-#define REG6C_GPIO9 0x01
-#define REG6C_GPIOH 0xff
-#define REG6C_GPIOL 0xff
-
-#define REG6D 0x6d
-#define REG6E 0x6e
-#define REG6F 0x6f
-#define REG7E 0x7e
-
-#define REG87_ACYCNRLC 0x10
-#define REG87_ENOFFSET 0x08
-#define REG87_LEDADD 0x04
-#define REG87_CK4ADC 0x02
-#define REG87_AUTOCONF 0x01
-
-#define REG9E 0x9e
-#define REG9F 0x9f
-
-#define REGA6 0xa6
-#define REGA7 0xa7
-#define REGA8 0xa8
-#define REGA9 0xa9
-#define REGAB 0xab
-
-#define REG_EXPR 0x10
-#define REG_EXPG 0x12
-#define REG_EXPB 0x14
-#define REG_EXPDMY 0x19
-#define REG_STEPNO 0x21
-#define REG_FWDSTEP 0x22
-#define REG_BWDSTEP 0x23
-#define REG_FASTNO 0x24
-#define REG_DPISET 0x2c
-#define REG_STRPIXEL 0x30
-#define REG_ENDPIXEL 0x32
-#define REG_LINCNT 0x25
-#define REG_MAXWD 0x35
-#define REG_LPERIOD 0x38
-#define REG_FEEDL 0x3d
-#define REG_FMOVDEC 0x5f
-#define REG_FSHDEC 0x69
-#define REG_FMOVNO 0x6a
-#define REG_CK1MAP 0x74
-#define REG_CK3MAP 0x77
-#define REG_CK4MAP 0x7a
-
-#define REGF8 0xf8
-#define REGF8_MAXSEL 0xf0
-#define REGF8_SMAXSEL 4
-#define REGF8_MINSEL 0x0f
-
-#define SETREG(adr,val) { dev->reg.init_reg(adr, val); }
-
-
-/** set up registers for an actual scan
- *
- * this function sets up the scanner to scan in normal or single line mode
- */
-static SANE_Status gl846_init_scan_regs(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg, SetupParams& params);
-
-/* Send the low-level scan command */
-static SANE_Status gl846_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg, SANE_Bool start_motor);
-
-/* Send the stop scan command */
-static SANE_Status gl846_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool check_stop);
-
-static SANE_Status gl846_init (Genesys_Device * dev);
-
-/** @brief moves the slider to steps at motor base dpi
- * @param dev device to work on
- * @param steps number of steps to move
- * */
-static SANE_Status
-gl846_feed (Genesys_Device * dev, unsigned int steps);
-
-static SANE_Status
-gl846_stop_action (Genesys_Device * dev);
-
-static SANE_Status
-gl846_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home);
-
-static SANE_Status
-gl846_boot (Genesys_Device * dev, SANE_Bool cold);
-
-
-
-typedef struct
-{
- uint8_t sensor_id;
- uint8_t r6b;
- uint8_t r6c;
- uint8_t r6d;
- uint8_t r6e;
- uint8_t r6f;
- uint8_t ra6;
- uint8_t ra7;
- uint8_t ra8;
- uint8_t ra9;
-} Gpio_Profile;
-
-static Gpio_Profile gpios[]={
- { GPO_IMG101, 0x72, 0x1f, 0xa4, 0x13, 0xa7, 0x11, 0xff, 0x19, 0x05},
- { GPO_PLUSTEK3800, 0x30, 0x01, 0x80, 0x2d, 0x80, 0x0c, 0x8f, 0x08, 0x04},
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-};
-
-typedef struct
-{
- const char *model;
- uint8_t dramsel;
- /* shading data address */
- uint8_t rd0;
- uint8_t rd1;
- uint8_t rd2;
- /* scanned data address */
- uint8_t rx[24];
-} Memory_layout;
-
-static Memory_layout layouts[]={
- /* Image formula 101 */
- {
- "canon-image-formula-101",
- 0x8b,
- 0x0a, 0x1b, 0x00,
- { /* RED ODD START / RED ODD END */
- 0x00, 0xb0, 0x05, 0xe7, /* [0x00b0, 0x05e7] 1336*4000w */
- /* RED EVEN START / RED EVEN END */
- 0x05, 0xe8, 0x0b, 0x1f, /* [0x05e8, 0x0b1f] */
- /* GREEN ODD START / GREEN ODD END */
- 0x0b, 0x20, 0x10, 0x57, /* [0x0b20, 0x1057] */
- /* GREEN EVEN START / GREEN EVEN END */
- 0x10, 0x58, 0x15, 0x8f, /* [0x1058, 0x158f] */
- /* BLUE ODD START / BLUE ODD END */
- 0x15, 0x90, 0x1a, 0xc7, /* [0x1590,0x1ac7] */
- /* BLUE EVEN START / BLUE EVEN END */
- 0x1a, 0xc8, 0x1f, 0xff /* [0x1ac8,0x1fff] */
- }
- },
- /* OpticBook 3800 */
- {
- "plustek-opticbook-3800",
- 0x2a,
- 0x0a, 0x0a, 0x0a,
- { /* RED ODD START / RED ODD END */
- 0x00, 0x68, 0x03, 0x00,
- /* RED EVEN START / RED EVEN END */
- 0x03, 0x01, 0x05, 0x99,
- /* GREEN ODD START / GREEN ODD END */
- 0x05, 0x9a, 0x08, 0x32,
- /* GREEN EVEN START / GREEN EVEN END */
- 0x08, 0x33, 0x0a, 0xcb,
- /* BLUE ODD START / BLUE ODD END */
- 0x0a, 0xcc, 0x0d, 0x64,
- /* BLUE EVEN START / BLUE EVEN END */
- 0x0d, 0x65, 0x0f, 0xfd
- }
- },
- /* list terminating entry */
- { NULL, 0, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }
-};
-
-/** @brief structure for sensor settings
- * this structure describes the sensor settings to use for a given
- * exposure.
- */
-typedef struct {
- int sensor_type; /**> sensor id */
- int dpi; /**> maximum dpi for which data are valid */
- int exposure; /**> exposure */
- int ck1map; /**> CK1MAP */
- int ck3map; /**> CK3MAP */
- int ck4map; /**> CK4MAP */
- int segcnt; /**> SEGCNT */
- int expdummy; /**> exposure dummy */
- int expr; /**> initial red exposure */
- int expg; /**> initial green exposure */
- int expb; /**> initial blue exposure */
- size_t *order; /**> order of sub-segments */
- uint8_t r17; /**> TG width */
-} Sensor_Profile;
-
-/**
- * order of the scanned pixel
- */
-static size_t order_01[]={0,1};
-
-/**
- * database of sensor profiles
- */
-static Sensor_Profile sensors[]={
- {CCD_IMG101, 1200, 11000, 60, 159, 85, 5136, 255, 0, 0, 0, order_01 , 0x13},
- {CCD_PLUSTEK3800, 1200, 11000, 60, 159, 85, 5136, 255, 0, 0, 0, order_01 , 0x13},
-};
-
-/* base motor slopes in full step unit */
-/* target=((exposure * dpi) / base_dpi)>>step_type; */
-static uint32_t img101_high[] = {22000, 22000, 22000, 18450, 15974, 14284, 13054, 12076, 11286, 10660, 10100, 9632, 9224, 8864, 8532, 8250, 7986, 7750, 7530, 7330, 7142, 6972, 6810, 6656, 6518, 6384, 6264, 6150, 6038, 5930, 5834, 5732, 5642, 5560, 5476, 5398, 5324, 5252, 5180, 5112, 5050, 4990, 4926, 4868, 4816, 4760, 4708, 4658, 4608, 4562, 4516, 4472, 4428, 4384, 4344, 4306, 4266, 4230, 4194, 4156, 4122, 4088, 4054, 4022, 3990, 3960, 3930, 3900, 3872, 3842, 3816, 3790, 3762, 3736, 3710, 3686, 3662, 3638, 3614, 3590, 3570, 3548, 3526, 3506, 3484, 3462, 3442, 3424, 3402, 3384, 3366, 3346, 3328, 3310, 3292, 3276, 3258, 3242, 3224, 3208, 3192, 3176, 3162, 3146, 3130, 3114, 3100, 3086, 3072, 3058, 3044, 3030, 3016, 3002, 2990, 2976, 2964, 2950, 2938, 2926, 2914, 2902, 2890, 2878, 2866, 2854, 2844, 2832, 2820, 2810, 2800, 2790, 2778, 2768, 2756, 2748, 2738, 2726, 2716, 2708, 2698, 2688, 2678, 2668, 2660, 2650, 2642, 2632, 2624, 2616, 2606, 2598, 2588, 2580, 2572, 2564, 2556, 2548, 2540, 2530, 2522, 2516, 2508, 2500, 2492, 2486, 2476, 2470, 2462, 2454, 2448, 2440, 2434, 2426, 2420, 2412, 2406, 2400, 2392, 2386, 2378, 2372, 2366, 2360, 2354, 2346, 2340, 2334, 2328, 2322, 2314, 2310, 2304, 2296, 2292, 2286, 2280, 2274, 2268, 2262, 2256, 2252, 2246, 2240, 2234, 2230, 2224, 2218, 2214, 2208, 2202, 2196, 2192, 2188, 2182, 2176, 2172, 2166, 2162, 2156, 2152, 2146, 2142, 2136, 2132, 2128, 2124, 2118, 2114, 2108, 2104, 2100, 2096, 2092, 2086, 2082, 2078, 2072, 2068, 2064, 2060, 2056, 2052, 2048, 2044, 2038, 2034, 2030, 2026, 2022, 2018, 2014, 2010, 2006, 2002, 1998, 1994, 1990, 1988, 1984, 1980, 1976, 1972, 1968, 1964, 1960, 1956, 1952, 1950, 1946, 1942, 1938, 1934, 1932, 1928, 1924, 1920, 1918, 1914, 1910, 1908, 1904, 1900, 1898, 1894, 1890, 1888, 1884, 1880, 1878, 1874, 1870, 1868, 1864, 1862, 1858, 1854, 1852, 1848, 1846, 1842, 1840, 1836, 1834, 1830, 1828, 1824, 1822, 1818, 1816, 1812, 1810, 1806, 1804, 1800, 1798, 1794, 1792, 1790, 1786, 1784, 1780, 1778, 1776, 1772, 1770, 1768, 1764, 1762, 1758, 1756, 1754, 1752, 1748, 1746, 1744, 1740, 1738, 1736, 1734, 1730, 1728, 1726, 1724, 1720, 1718, 1716, 1714, 1710, 1708, 1706, 1704, 1700, 1698, 1696, 1694, 1692, 1688, 1686, 1684, 1682, 1680, 1676, 1674, 1672, 1670, 1668, 1666, 1664, 1662, 1660, 1656, 1654, 1652, 1650, 1648, 1646, 1644, 1642, 1638, 1636, 1634, 1632, 1630, 1628, 1626, 1624, 1622, 1620, 1618, 1616, 1614, 1612, 1610, 1608, 1606, 1604, 1602, 1600, 1598, 1596, 1594, 1592, 1590, 1588, 1586, 1584, 1582, 1580, 1578, 1576, 1574, 1572, 1570, 1568, 1566, 1564, 1562, 1560, 1558, 1556, 1556, 1554, 1552, 1550, 1548, 1546, 1544, 1542, 1540, 1538, 1536, 1536, 1534, 1532, 1530, 1528, 1526, 1524, 1522, 1522, 1520, 1518, 1516, 1514, 1512, 1510, 1510, 1508, 1506, 1504, 1502, 1500, 1500, 1498, 1496, 1494, 1492, 1490, 1490, 1488, 1486, 1484, 1482, 1482, 1480, 1478, 1476, 1474, 1474, 1472, 1470, 1468, 1466, 1466, 1464, 1462, 1460, 1460, 1458, 1456, 1454, 1454, 1452, 1450, 1448, 1448, 1446, 1444, 1442, 1442, 1440, 1438, 1438, 1436, 1434, 1432, 1432, 1430, 1428, 1426, 1426, 1424, 1422, 1422, 1420, 1418, 1418, 1416, 1414, 1412, 1412, 1410, 1408, 1408, 1406, 1404, 1404, 1402, 1400, 1400, 1398, 1396, 1394, 1394, 1392, 1390, 1390, 1388, 1388, 1386, 1384, 1384, 1382, 1380, 1380, 1378, 1376, 1376, 1374, 1374, 1372, 1370, 1370, 1368, 1366, 1366, 1364, 1362, 1362, 1360, 1360, 1358, 1356, 1356, 1354, 1352, 1352, 1350, 1350, 1348, 1348, 1346, 1344, 1344, 1342, 1340, 1340, 1338, 1338, 1336, 1336, 1334, 1332, 1332, 1330, 1330, 1328, 1328, 1326, 1324, 1324, 1322, 1322, 1320, 1318, 1318, 1316, 1316, 1314, 1314, 1312, 1310, 1310, 1308, 1308, 1306, 1306, 1304, 1304, 1302, 1302, 1300, 1300, 1298, 1298, 1296, 1294, 1294, 1292, 1292, 1290, 1290, 1288, 1288, 1286, 1286, 1284, 1284, 1282, 1282, 1280, 1280, 1278, 1278, 1276, 1276, 1274, 1272, 1272, 1270, 1270, 1270, 1268, 1268, 1266, 1264, 1264, 1262, 1262, 1260, 1260, 1260, 1258, 1258, 1256, 1254, 1254, 1254, 1252, 1252, 1250, 1250, 1248, 1248, 1246, 1246, 1244, 1244, 1242, 1242, 1240, 1240, 1238, 1238, 1236, 1236, 1236, 1234, 1234, 1232, 1232, 1230, 1230, 1228, 1228, 1226, 1226, 1226, 1224, 1224, 1222, 1222, 1220, 1220, 1218, 1218, 1218, 1216, 1216, 1214, 1214, 1212, 1212, 1210, 1210, 1210, 1208, 1208, 1206, 1206, 1204, 1204, 1204, 1202, 1202, 1200, 1200, 1198, 1198, 1198, 1196, 1196, 1194, 1194, 1192, 1192, 1192, 1190, 1190, 1188, 1188, 1188, 1186, 1186, 1184, 1184, 1182, 1182, 1182, 1180, 1180, 1180, 1178, 1178, 1176, 1176, 1174, 1174, 1174, 1172, 1172, 1172, 1170, 1170, 1168, 1168, 1168, 1166, 1166, 1164, 1164, 1164, 1162, 1162, 1160, 1160, 1160, 1158, 1158, 1156, 1156, 1156, 1154, 1154, 1154, 1152, 1152, 1152, 1150, 1150, 1148, 1148, 1148, 1146, 1146, 1146, 1144, 1144, 1142, 1142, 1142, 1140, 1140, 1140, 1138, 1138, 1136, 1136, 1136, 1134, 1134, 1134, 1132, 1132, 1132, 1130, 1130, 1130, 1128, 1128, 1126, 1126, 1126, 1124, 1124, 1124, 1122, 1122, 1122, 1120, 1120, 1120, 1118, 1118, 1118, 1116, 1116, 1116, 1114, 1114, 1114, 1112, 1112, 1112, 1110, 1110, 1110, 1108, 1108, 1108, 1106, 1106, 1106, 1104, 1104, 1104, 1102, 1102, 1102, 1100, 1100, 1100, 1098, 1098, 1098, 1096, 1096, 1096, 1094, 1094, 1094, 1092, 1092, 1092, 1090, 1090, 1090, 1088, 1088, 1088, 1086, 1086, 1086, 1086, 1084, 1084, 1084, 1082, 1082, 1082, 1080, 1080, 1080, 1078, 1078, 1078, 1078, 1076, 1076, 1076, 1074, 1074, 1074, 1072, 1072, 1072, 1072, 1070, 1070, 1070, 1068, 1068, 1068, 1066, 1066, 1066, 1064, 1064, 1064, 1064, 1062, 1062, 1062, 1060, 1060, 1060, 1058, 1058, 1058, 1058, 1056, 1056, 1056, 1054, 1054, 1054, 1054, 1052, 1052, 1052, 1050, 1050, 1050, 1050, 1048, 1048, 1048, 1046, 1046, 1046, 1046, 1044, 1044, 1044, 1044, 1042, 1042, 1042, 1040, 1040, 1040, 1040, 1038, 1038, 1038, 1036, 1036, 1036, 1036, 1034, 1034, 1034, 1034, 1032, 1032, 1032, 1030, 1030, 1030, 1030, 1028, 1028, 1028, 1028, 1026, 1026, 1026, 1026, 1024, 1024, 1024, 1022, 1022, 1022, 1022, 1020, 1020, 1020, 1020, 1018, 1018, 1018, 1018, 1016, 1016, 1016, 1016, 1014, 1014, 1014, 1014, 1012, 1012, 1012, 1012, 1010, 1010, 1010, 1010, 1008, 1008, 1008, 1008, 1006, 1006, 1006, 1006, 1004, 1004, 1004, 1004, 1002, 1002, 1002, 1002, 1000, 1000, 1000, 1000, 0 };
-
-/**
- * database of motor profiles
- */
-
-static Motor_Profile gl846_motors[]={
- /* Image Formula 101 */
- {MOTOR_IMG101, 11000, HALF_STEP , img101_high},
- {MOTOR_PLUSTEK3800, 11000, HALF_STEP , img101_high},
-
- /* end of database entry */
- {0, 0, 0, NULL},
-};
diff --git a/backend/genesys_gl847.cc b/backend/genesys_gl847.cc
deleted file mode 100644
index b5748a5..0000000
--- a/backend/genesys_gl847.cc
+++ /dev/null
@@ -1,3517 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
-
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#define DEBUG_DECLARE_ONLY
-
-#include "genesys_gl847.h"
-
-#include <vector>
-
-/****************************************************************************
- Mid level functions
- ****************************************************************************/
-
-static SANE_Bool
-gl847_get_fast_feed_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG02);
- if (r && (r->value & REG02_FASTFED))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl847_get_filter_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_FILTER))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl847_get_lineart_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_LINEART))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl847_get_bitset_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, REG04);
- if (r && (r->value & REG04_BITSET))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl847_get_gain4_bit (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
-
- r = sanei_genesys_get_address (regs, 0x06);
- if (r && (r->value & REG06_GAIN4))
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl847_test_buffer_empty_bit (SANE_Byte val)
-{
- if (val & REG41_BUFEMPTY)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-static SANE_Bool
-gl847_test_motor_flag_bit (SANE_Byte val)
-{
- if (val & REG41_MOTORENB)
- return SANE_TRUE;
- return SANE_FALSE;
-}
-
-/**
- * compute the step multiplier used
- */
-static int
-gl847_get_step_multiplier (Genesys_Register_Set * regs)
-{
- GenesysRegister *r = NULL;
- int value = 1;
-
- r = sanei_genesys_get_address (regs, 0x9d);
- if (r != NULL)
- {
- value = (r->value & 0x0f)>>1;
- value = 1 << value;
- }
- DBG (DBG_io, "%s: step multiplier is %d\n", __func__, value);
- return value;
-}
-
-/** @brief sensor profile
- * search for the database of motor profiles and get the best one. Each
- * profile is at a specific dpihw. Use LiDE 110 table by default.
- * @param sensor_type sensor id
- * @param dpi hardware dpi for the scan
- * @return a pointer to a Sensor_Profile struct
- */
-static Sensor_Profile *get_sensor_profile(int sensor_type, int dpi)
-{
- unsigned int i;
- int idx;
-
- i=0;
- idx=-1;
- while(i<sizeof(sensors)/sizeof(Sensor_Profile))
- {
- /* exact match */
- if(sensors[i].sensor_type==sensor_type && sensors[i].dpi==dpi)
- {
- return &(sensors[i]);
- }
-
- /* closest match */
- if(sensors[i].sensor_type==sensor_type)
- {
- if(idx<0)
- {
- idx=i;
- }
- else
- {
- if(sensors[i].dpi>=dpi
- && sensors[i].dpi<sensors[idx].dpi)
- {
- idx=i;
- }
- }
- }
- i++;
- }
-
- /* default fallback */
- if(idx<0)
- {
- DBG (DBG_warn,"%s: using default sensor profile\n",__func__);
- idx=0;
- }
-
- return &(sensors[idx]);
-}
-
-/**@brief compute exposure to use
- * compute the sensor exposure based on target resolution
- */
-static int gl847_compute_exposure(Genesys_Device *dev, int xres)
-{
- Sensor_Profile* sensor_profile=get_sensor_profile(dev->model->ccd_type, xres);
- return sensor_profile->exposure;
-}
-
-
-/** @brief sensor specific settings
-*/
-static void gl847_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs, int dpi)
-{
- GenesysRegister *r;
- int dpihw;
- uint16_t exp;
-
- DBGSTART;
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dpi);
-
- for (uint16_t addr = 0x16; addr < 0x1e; addr++) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- for (uint16_t addr = 0x52; addr < 0x52 + 9; addr++) {
- regs->set8(addr, sensor.custom_regs.get_value(addr));
- }
-
- /* set EXPDUMMY and CKxMAP */
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dpi);
- Sensor_Profile* sensor_profile=get_sensor_profile(dev->model->ccd_type, dpihw);
-
- sanei_genesys_set_reg_from_set(regs,REG_EXPDMY,(uint8_t)((sensor_profile->expdummy) & 0xff));
-
- /* if no calibration has been done, set default values for exposures */
- exp = sensor.exposure.red;
- if(exp==0)
- {
- exp=sensor_profile->expr;
- }
- sanei_genesys_set_double(regs,REG_EXPR,exp);
-
- exp = sensor.exposure.green;
- if(exp==0)
- {
- exp=sensor_profile->expg;
- }
- sanei_genesys_set_double(regs,REG_EXPG,exp);
-
- exp = sensor.exposure.blue;
- if(exp==0)
- {
- exp=sensor_profile->expb;
- }
- sanei_genesys_set_double(regs,REG_EXPB,exp);
-
- sanei_genesys_set_triple(regs,REG_CK1MAP,sensor_profile->ck1map);
- sanei_genesys_set_triple(regs,REG_CK3MAP,sensor_profile->ck3map);
- sanei_genesys_set_triple(regs,REG_CK4MAP,sensor_profile->ck4map);
-
- /* order of the sub-segments */
- dev->order=sensor_profile->order;
-
- r = sanei_genesys_get_address (regs, 0x17);
- r->value = sensor_profile->r17;
-
- DBGCOMPLETED;
-}
-
-
-/** @brief set all registers to default values .
- * This function is called only once at the beginning and
- * fills register startup values for registers reused across scans.
- * Those that are rarely modified or not modified are written
- * individually.
- * @param dev device structure holding register set to initialize
- */
-static void
-gl847_init_registers (Genesys_Device * dev)
-{
- int lide700=0;
- uint8_t val;
-
- DBGSTART;
- /* 700F class needs some different initial settings */
- if (dev->model->model_id == MODEL_CANON_LIDE_700F)
- {
- lide700 = 1;
- }
-
- dev->reg.clear();
-
- SETREG (0x01, 0x82);
- SETREG (0x02, 0x18);
- SETREG (0x03, 0x50);
- SETREG (0x04, 0x12);
- SETREG (0x05, 0x80);
- SETREG (0x06, 0x50); /* FASTMODE + POWERBIT */
- SETREG (0x08, 0x10);
- SETREG (0x09, 0x01);
- SETREG (0x0a, 0x00);
- SETREG (0x0b, 0x01);
- SETREG (0x0c, 0x02);
-
- /* LED exposures */
- SETREG (0x10, 0x00);
- SETREG (0x11, 0x00);
- SETREG (0x12, 0x00);
- SETREG (0x13, 0x00);
- SETREG (0x14, 0x00);
- SETREG (0x15, 0x00);
-
- SETREG (0x16, 0x10);
- SETREG (0x17, 0x08);
- SETREG (0x18, 0x00);
-
- /* EXPDMY */
- SETREG (0x19, 0x50);
-
- SETREG (0x1a, 0x34);
- SETREG (0x1b, 0x00);
- SETREG (0x1c, 0x02);
- SETREG (0x1d, 0x04);
- SETREG (0x1e, 0x10);
- SETREG (0x1f, 0x04);
- SETREG (0x20, 0x02);
- SETREG (0x21, 0x10);
- SETREG (0x22, 0x7f);
- SETREG (0x23, 0x7f);
- SETREG (0x24, 0x10);
- SETREG (0x25, 0x00);
- SETREG (0x26, 0x00);
- SETREG (0x27, 0x00);
- SETREG (0x2c, 0x09);
- SETREG (0x2d, 0x60);
- SETREG (0x2e, 0x80);
- SETREG (0x2f, 0x80);
- SETREG (0x30, 0x00);
- SETREG (0x31, 0x10);
- SETREG (0x32, 0x15);
- SETREG (0x33, 0x0e);
- SETREG (0x34, 0x40);
- SETREG (0x35, 0x00);
- SETREG (0x36, 0x2a);
- SETREG (0x37, 0x30);
- SETREG (0x38, 0x2a);
- SETREG (0x39, 0xf8);
- SETREG (0x3d, 0x00);
- SETREG (0x3e, 0x00);
- SETREG (0x3f, 0x00);
- SETREG (0x52, 0x03);
- SETREG (0x53, 0x07);
- SETREG (0x54, 0x00);
- SETREG (0x55, 0x00);
- SETREG (0x56, 0x00);
- SETREG (0x57, 0x00);
- SETREG (0x58, 0x2a);
- SETREG (0x59, 0xe1);
- SETREG (0x5a, 0x55);
- SETREG (0x5e, 0x41);
- SETREG (0x5f, 0x40);
- SETREG (0x60, 0x00);
- SETREG (0x61, 0x21);
- SETREG (0x62, 0x40);
- SETREG (0x63, 0x00);
- SETREG (0x64, 0x21);
- SETREG (0x65, 0x40);
- SETREG (0x67, 0x80);
- SETREG (0x68, 0x80);
- SETREG (0x69, 0x20);
- SETREG (0x6a, 0x20);
-
- /* CK1MAP */
- SETREG (0x74, 0x00);
- SETREG (0x75, 0x00);
- SETREG (0x76, 0x3c);
-
- /* CK3MAP */
- SETREG (0x77, 0x00);
- SETREG (0x78, 0x00);
- SETREG (0x79, 0x9f);
-
- /* CK4MAP */
- SETREG (0x7a, 0x00);
- SETREG (0x7b, 0x00);
- SETREG (0x7c, 0x55);
-
- SETREG (0x7d, 0x00);
-
- /* NOTE: autoconf is a non working option */
- SETREG (0x87, 0x02);
- SETREG (0x9d, 0x06);
- SETREG (0xa2, 0x0f);
- SETREG (0xbd, 0x18);
- SETREG (0xfe, 0x08);
-
- /* gamma[0] and gamma[256] values */
- SETREG (0xbe, 0x00);
- SETREG (0xc5, 0x00);
- SETREG (0xc6, 0x00);
- SETREG (0xc7, 0x00);
- SETREG (0xc8, 0x00);
- SETREG (0xc9, 0x00);
- SETREG (0xca, 0x00);
-
- /* LiDE 700 fixups */
- if(lide700)
- {
- SETREG (0x5f, 0x04);
- SETREG (0x7d, 0x80);
-
- /* we write to these registers only once */
- val=0;
- sanei_genesys_write_register (dev, REG7E, val);
- sanei_genesys_write_register (dev, REG9E, val);
- sanei_genesys_write_register (dev, REG9F, val);
- sanei_genesys_write_register (dev, REGAB, val);
- }
-
- /* fine tune upon device description */
- dev->reg.find_reg(0x05).value &= ~REG05_DPIHW;
- switch (sanei_genesys_find_sensor_any(dev).optical_res)
- {
- case 600:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_600;
- break;
- case 1200:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_1200;
- break;
- case 2400:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_2400;
- break;
- case 4800:
- dev->reg.find_reg(0x05).value |= REG05_DPIHW_4800;
- break;
- }
-
- /* initalize calibration reg */
- dev->calib_reg = dev->reg;
-
- DBGCOMPLETED;
-}
-
-/**@brief send slope table for motor movement
- * Send slope_table in machine byte order
- * @param dev device to send slope table
- * @param table_nr index of the slope table in ASIC memory
- * Must be in the [0-4] range.
- * @param slope_table pointer to 16 bit values array of the slope table
- * @param steps number of elements in the slope table
- */
-static SANE_Status
-gl847_send_slope_table (Genesys_Device * dev, int table_nr,
- uint16_t * slope_table, int steps)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- char msg[10000];
-
- DBG (DBG_proc, "%s (table_nr = %d, steps = %d)\n", __func__,
- table_nr, steps);
-
- /* sanity check */
- if(table_nr<0 || table_nr>4)
- {
- DBG (DBG_error, "%s: invalid table number %d!\n", __func__, table_nr);
- return SANE_STATUS_INVAL;
- }
-
- std::vector<uint8_t> table(steps * 2);
- for (i = 0; i < steps; i++)
- {
- table[i * 2] = slope_table[i] & 0xff;
- table[i * 2 + 1] = slope_table[i] >> 8;
- }
-
- if (DBG_LEVEL >= DBG_io)
- {
- sprintf (msg, "write slope %d (%d)=", table_nr, steps);
- for (i = 0; i < steps; i++)
- {
- sprintf (msg+strlen(msg), "%d", slope_table[i]);
- }
- DBG (DBG_io, "%s: %s\n", __func__, msg);
- }
-
- /* slope table addresses are fixed */
- status = sanei_genesys_write_ahb(dev, 0x10000000 + 0x4000 * table_nr, steps * 2, table.data());
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: write to AHB failed writing slope table %d (%s)\n", __func__, table_nr,
- sane_strstatus(status));
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * Set register values of Analog Device type frontend
- * */
-static SANE_Status
-gl847_set_ad_fe (Genesys_Device * dev, uint8_t set)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- uint8_t val8;
-
- DBGSTART;
-
- /* wait for FE to be ready */
- status = sanei_genesys_get_status (dev, &val8);
- while (val8 & REG41_FEBUSY)
- {
- sanei_genesys_sleep_ms(10);
- status = sanei_genesys_get_status (dev, &val8);
- };
-
- if (set == AFE_INIT)
- {
- DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, dev->model->dac_type);
-
- dev->frontend = dev->frontend_initial;
- }
-
- /* reset DAC */
- status = sanei_genesys_fe_write_data (dev, 0x00, 0x80);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write reg0: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* write them to analog frontend */
- status = sanei_genesys_fe_write_data(dev, 0x00, dev->frontend.regs.get_value(0x00));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write reg0: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- status = sanei_genesys_fe_write_data(dev, 0x01, dev->frontend.regs.get_value(0x01));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write reg1: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- for (i = 0; i < 3; i++)
- {
- status = sanei_genesys_fe_write_data(dev, 0x02 + i, dev->frontend.get_gain(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write gain %d: %s\n", __func__, i, sane_strstatus(status));
- return status;
- }
- }
- for (i = 0; i < 3; i++)
- {
- status = sanei_genesys_fe_write_data(dev, 0x05 + i, dev->frontend.get_offset(i));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write offset %d: %s\n", __func__, i,
- sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-static SANE_Status
-gl847_homsnr_gpio(Genesys_Device *dev)
-{
-uint8_t val;
-SANE_Status status=SANE_STATUS_GOOD;
-
- if (dev->model->gpo_type == GPO_CANONLIDE700)
- {
- RIE (sanei_genesys_read_register (dev, REG6C, &val));
- val &= ~REG6C_GPIO10;
- RIE (sanei_genesys_write_register (dev, REG6C, val));
- }
- else
- {
- RIE (sanei_genesys_read_register (dev, REG6C, &val));
- val |= REG6C_GPIO10;
- RIE (sanei_genesys_write_register (dev, REG6C, val));
- }
- return status;
-}
-
-/* Set values of analog frontend */
-static SANE_Status
-gl847_set_fe(Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set)
-{
- (void) sensor;
-
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- DBG(DBG_proc, "%s (%s)\n", __func__, set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
- AFE_POWER_SAVE ? "powersave" : "huh?");
-
- RIE (sanei_genesys_read_register (dev, REG04, &val));
-
- /* route to AD devices */
- if ((val & REG04_FESET) == 0x02)
- {
- return gl847_set_ad_fe (dev, set);
- }
-
- /* for now there is no support yet for wolfson fe */
- DBG(DBG_proc, "%s(): unsupported frontend type %d\n", __func__,
- dev->reg.find_reg(0x04).value & REG04_FESET);
-
- DBGCOMPLETED;
- return SANE_STATUS_UNSUPPORTED;
-}
-
-
-/** @brief set up motor related register for scan
- */
-static SANE_Status
-gl847_init_motor_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int scan_exposure_time,
- float scan_yres,
- int scan_step_type,
- unsigned int scan_lines,
- unsigned int scan_dummy,
- unsigned int feed_steps,
- int scan_power_mode,
- unsigned int flags)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int use_fast_fed;
- unsigned int fast_dpi;
- uint16_t scan_table[SLOPE_TABLE_SIZE];
- uint16_t fast_table[SLOPE_TABLE_SIZE];
- int scan_steps, fast_steps, factor;
- unsigned int feedl, dist;
- GenesysRegister *r;
- uint32_t z1, z2;
- unsigned int min_restep = 0x20;
- uint8_t val, effective;
- int fast_step_type;
- unsigned int ccdlmt,tgtime;
-
- DBGSTART;
- DBG(DBG_proc, "%s : scan_exposure_time=%d, can_yres=%g, scan_step_type=%d, scan_lines=%d, "
- "scan_dummy=%d, feed_steps=%d, scan_power_mode=%d, flags=%x\n", __func__, scan_exposure_time,
- scan_yres, scan_step_type, scan_lines, scan_dummy, feed_steps, scan_power_mode, flags);
-
- /* get step multiplier */
- factor = gl847_get_step_multiplier (reg);
-
- use_fast_fed=0;
- /* no fast fed since feed works well */
- if(dev->settings.yres==4444 && feed_steps>100
- && ((flags & MOTOR_FLAG_FEED)==0))
- {
- use_fast_fed=1;
- }
- DBG(DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
-
- sanei_genesys_set_triple(reg, REG_LINCNT, scan_lines);
- DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
-
- /* compute register 02 value */
- r = sanei_genesys_get_address (reg, REG02);
- r->value = 0x00;
- sanei_genesys_set_motor_power(*reg, true);
-
- if (use_fast_fed)
- r->value |= REG02_FASTFED;
- else
- r->value &= ~REG02_FASTFED;
-
- if (flags & MOTOR_FLAG_AUTO_GO_HOME)
- r->value |= REG02_AGOHOME | REG02_NOTHOME;
-
- if ((flags & MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE)
- ||(scan_yres>=sensor.optical_res))
- {
- r->value |= REG02_ACDCDIS;
- }
-
- /* scan and backtracking slope table */
- sanei_genesys_slope_table(scan_table,
- &scan_steps,
- scan_yres,
- scan_exposure_time,
- dev->motor.base_ydpi,
- scan_step_type,
- factor,
- dev->model->motor_type,
- gl847_motors);
- RIE(gl847_send_slope_table (dev, SCAN_TABLE, scan_table, scan_steps*factor));
- RIE(gl847_send_slope_table (dev, BACKTRACK_TABLE, scan_table, scan_steps*factor));
-
- /* fast table */
- fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
- fast_step_type=scan_step_type;
- if(scan_step_type>=2)
- {
- fast_step_type=2;
- }
-
- sanei_genesys_slope_table(fast_table,
- &fast_steps,
- fast_dpi,
- scan_exposure_time,
- dev->motor.base_ydpi,
- fast_step_type,
- factor,
- dev->model->motor_type,
- gl847_motors);
-
- /* manual override of high start value */
- fast_table[0]=fast_table[1];
-
- RIE(gl847_send_slope_table (dev, STOP_TABLE, fast_table, fast_steps*factor));
- RIE(gl847_send_slope_table (dev, FAST_TABLE, fast_table, fast_steps*factor));
- RIE(gl847_send_slope_table (dev, HOME_TABLE, fast_table, fast_steps*factor));
-
- /* correct move distance by acceleration and deceleration amounts */
- feedl=feed_steps;
- if (use_fast_fed)
- {
- feedl<<=fast_step_type;
- dist=(scan_steps+2*fast_steps)*factor;
- /* TODO read and decode REGAB */
- r = sanei_genesys_get_address (reg, 0x5e);
- dist += (r->value & 31);
- /* FEDCNT */
- r = sanei_genesys_get_address (reg, REG_FEDCNT);
- dist += r->value;
- }
- else
- {
- feedl<<=scan_step_type;
- dist=scan_steps*factor;
- if (flags & MOTOR_FLAG_FEED)
- dist *=2;
- }
- DBG(DBG_io2, "%s: scan steps=%d\n", __func__, scan_steps);
- DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
-
- /* check for overflow */
- if(dist<feedl)
- feedl -= dist;
- else
- feedl = 0;
-
- sanei_genesys_set_triple(reg,REG_FEEDL,feedl);
- DBG(DBG_io ,"%s: feedl=%d\n", __func__, feedl);
-
- r = sanei_genesys_get_address (reg, REG0C);
- ccdlmt=(r->value & REG0C_CCDLMT)+1;
-
- r = sanei_genesys_get_address (reg, REG1C);
- tgtime=1<<(r->value & REG1C_TGTIME);
-
- /* hi res motor speed GPIO */
- RIE (sanei_genesys_read_register (dev, REG6C, &effective));
-
- /* if quarter step, bipolar Vref2 */
- if (scan_step_type > 1)
- {
- if (scan_step_type < 3)
- {
- val = effective & ~REG6C_GPIO13;
- }
- else
- {
- val = effective | REG6C_GPIO13;
- }
- }
- else
- {
- val = effective;
- }
- RIE (sanei_genesys_write_register (dev, REG6C, val));
-
- /* effective scan */
- RIE (sanei_genesys_read_register (dev, REG6C, &effective));
- val = effective | REG6C_GPIO10;
- RIE (sanei_genesys_write_register (dev, REG6C, val));
-
- min_restep=scan_steps/2-1;
- if (min_restep < 1)
- min_restep = 1;
- r = sanei_genesys_get_address (reg, REG_FWDSTEP);
- r->value = min_restep;
- r = sanei_genesys_get_address (reg, REG_BWDSTEP);
- r->value = min_restep;
-
- sanei_genesys_calculate_zmode2(use_fast_fed,
- scan_exposure_time*ccdlmt*tgtime,
- scan_table,
- scan_steps*factor,
- feedl,
- min_restep*factor,
- &z1,
- &z2);
-
- DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
- sanei_genesys_set_triple(reg, REG60, z1 | (scan_step_type << (16+REG60S_STEPSEL)));
-
- DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
- sanei_genesys_set_triple(reg, REG63, z2 | (scan_step_type << (16+REG63S_FSTPSEL)));
-
- r = sanei_genesys_get_address (reg, 0x1e);
- r->value &= 0xf0; /* 0 dummy lines */
- r->value |= scan_dummy; /* dummy lines */
-
- r = sanei_genesys_get_address (reg, REG67);
- r->value = REG67_MTRPWM;
-
- r = sanei_genesys_get_address (reg, REG68);
- r->value = REG68_FASTPWM;
-
- r = sanei_genesys_get_address (reg, REG_STEPNO);
- r->value = scan_steps;
-
- r = sanei_genesys_get_address (reg, REG_FASTNO);
- r->value = scan_steps;
-
- r = sanei_genesys_get_address (reg, REG_FSHDEC);
- r->value = scan_steps;
-
- r = sanei_genesys_get_address (reg, REG_FMOVNO);
- r->value = fast_steps;
-
- r = sanei_genesys_get_address (reg, REG_FMOVDEC);
- r->value = fast_steps;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/** @brief set up registers related to sensor
- * Set up the following registers
- 0x01
- 0x03
- 0x10-0x015 R/G/B exposures
- 0x19 EXPDMY
- 0x2e BWHI
- 0x2f BWLO
- 0x04
- 0x87
- 0x05
- 0x2c,0x2d DPISET
- 0x30,0x31 STRPIXEL
- 0x32,0x33 ENDPIXEL
- 0x35,0x36,0x37 MAXWD [25:2] (>>2)
- 0x38,0x39 LPERIOD
- 0x34 DUMMY
- */
-static SANE_Status
-gl847_init_optical_regs_scan (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg,
- unsigned int exposure_time,
- int used_res,
- unsigned int start,
- unsigned int pixels,
- int channels,
- int depth,
- SANE_Bool half_ccd, ColorFilter color_filter, int flags)
-{
- unsigned int words_per_line;
- unsigned int startx, endx, used_pixels;
- unsigned int dpiset, dpihw,segnb,cksel,factor;
- unsigned int bytes;
- GenesysRegister *r;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s : exposure_time=%d, used_res=%d, start=%d, pixels=%d, channels=%d, depth=%d, "
- "half_ccd=%d, flags=%x\n", __func__, exposure_time, used_res, start, pixels, channels, depth,
- half_ccd, flags);
-
- /* resolution is divided according to CKSEL */
- r = sanei_genesys_get_address (reg, REG18);
- cksel= (r->value & REG18_CKSEL)+1;
- DBG(DBG_io2, "%s: cksel=%d\n", __func__, cksel);
-
- /* to manage high resolution device while keeping good
- * low resolution scanning speed, we make hardware dpi vary */
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, used_res * cksel);
- factor=sensor.optical_res/dpihw;
- DBG(DBG_io2, "%s: dpihw=%d (factor=%d)\n", __func__, dpihw, factor);
-
- /* sensor parameters */
- Sensor_Profile* sensor_profile=get_sensor_profile(dev->model->ccd_type, dpihw);
- gl847_setup_sensor(dev, sensor, reg, dpihw);
- dpiset = used_res * cksel;
-
- /* start and end coordinate in optical dpi coordinates */
- startx = start/cksel+sensor.CCD_start_xoffset;
- used_pixels=pixels/cksel;
-
- /* end of sensor window */
- endx = startx + used_pixels;
-
- /* sensors are built from 600 dpi segments for LiDE 100/200
- * and 1200 dpi for the 700F */
- if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR)
- {
- segnb=dpihw/600;
- }
- else
- {
- segnb=1;
- }
-
- /* compute pixel coordinate in the given dpihw space,
- * taking segments into account */
- startx/=factor*segnb;
- endx/=factor*segnb;
- dev->len=endx-startx;
- dev->dist=0;
- dev->skip=0;
-
- /* in cas of multi-segments sensor, we have to add the witdh
- * of the sensor crossed by the scan area */
- if (dev->model->flags & GENESYS_FLAG_SIS_SENSOR && segnb>1)
- {
- dev->dist = sensor_profile->segcnt;
- }
-
- /* use a segcnt rounded to next even number */
- endx += ((dev->dist+1)&0xfffe)*(segnb-1);
- used_pixels=endx-startx;
-
- status = gl847_set_fe(dev, sensor, AFE_SET);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set frontend: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* enable shading */
- r = sanei_genesys_get_address (reg, REG01);
- r->value &= ~REG01_SCAN;
- r->value |= REG01_SHDAREA;
- if ((flags & OPTICAL_FLAG_DISABLE_SHADING) ||
- (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
- {
- r->value &= ~REG01_DVDSET;
- }
- else
- {
- r->value |= REG01_DVDSET;
- }
-
- r = sanei_genesys_get_address (reg, REG03);
- r->value &= ~REG03_AVEENB;
-
- sanei_genesys_set_lamp_power(dev, sensor, *reg, !(flags & OPTICAL_FLAG_DISABLE_LAMP));
-
- /* BW threshold */
- r = sanei_genesys_get_address (reg, 0x2e);
- r->value = dev->settings.threshold;
- r = sanei_genesys_get_address (reg, 0x2f);
- r->value = dev->settings.threshold;
-
- /* monochrome / color scan */
- r = sanei_genesys_get_address (reg, REG04);
- switch (depth)
- {
- case 1:
- r->value &= ~REG04_BITSET;
- r->value |= REG04_LINEART;
- break;
- case 8:
- r->value &= ~(REG04_LINEART | REG04_BITSET);
- break;
- case 16:
- r->value &= ~REG04_LINEART;
- r->value |= REG04_BITSET;
- break;
- }
-
- r->value &= ~(REG04_FILTER | REG04_AFEMOD);
- if (channels == 1)
- {
- switch (color_filter)
- {
-
- case ColorFilter::RED:
- r->value |= 0x14;
- break;
- case ColorFilter::BLUE:
- r->value |= 0x1c;
- break;
- case ColorFilter::GREEN:
- r->value |= 0x18;
- break;
- default:
- break; // should not happen
- }
- }
- else
- r->value |= 0x10; /* mono */
-
- /* register 05 */
- r = sanei_genesys_get_address (reg, REG05);
-
- /* set up dpihw */
- r->value &= ~REG05_DPIHW;
- switch(dpihw)
- {
- case 600:
- r->value |= REG05_DPIHW_600;
- break;
- case 1200:
- r->value |= REG05_DPIHW_1200;
- break;
- case 2400:
- r->value |= REG05_DPIHW_2400;
- break;
- case 4800:
- r->value |= REG05_DPIHW_4800;
- break;
- }
-
- /* enable gamma tables */
- if (flags & OPTICAL_FLAG_DISABLE_GAMMA)
- r->value &= ~REG05_GMMENB;
- else
- r->value |= REG05_GMMENB;
-
- /* CIS scanners can do true gray by setting LEDADD */
- /* we set up LEDADD only when asked */
- if (dev->model->is_cis == SANE_TRUE)
- {
- r = sanei_genesys_get_address (reg, 0x87);
- r->value &= ~REG87_LEDADD;
- if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
- {
- r->value |= REG87_LEDADD;
- }
- /* RGB weighting
- r = sanei_genesys_get_address (reg, 0x01);
- r->value &= ~REG01_TRUEGRAY;
- if (channels == 1 && (flags & OPTICAL_FLAG_ENABLE_LEDADD))
- {
- r->value |= REG01_TRUEGRAY;
- }*/
- }
-
- /* words(16bit) before gamma, conversion to 8 bit or lineart*/
- words_per_line = (used_pixels * dpiset) / dpihw;
- bytes=depth/8;
- if (depth == 1)
- {
- words_per_line = (words_per_line+7)/8 ;
- dev->len = (dev->len >> 3) + ((dev->len & 7) ? 1 : 0);
- dev->dist = (dev->dist >> 3) + ((dev->dist & 7) ? 1 : 0);
- }
- else
- {
- words_per_line *= bytes;
- dev->dist *= bytes;
- dev->len *= bytes;
- }
-
- dev->bpl = words_per_line;
- dev->cur=0;
- dev->segnb=segnb;
- dev->line_interp = 0;
-
- sanei_genesys_set_double(reg,REG_DPISET,dpiset);
- DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset);
-
- sanei_genesys_set_double(reg,REG_STRPIXEL,startx);
- sanei_genesys_set_double(reg,REG_ENDPIXEL,endx);
- DBG (DBG_io2, "%s: startx=%d\n", __func__, startx);
- DBG (DBG_io2, "%s: endx =%d\n", __func__, endx);
-
- DBG (DBG_io2, "%s: used_pixels=%d\n", __func__, used_pixels);
- DBG (DBG_io2, "%s: pixels =%d\n", __func__, pixels);
- DBG (DBG_io2, "%s: depth =%d\n", __func__, depth);
- DBG (DBG_io2, "%s: dev->bpl =%lu\n", __func__, (unsigned long)dev->bpl);
- DBG (DBG_io2, "%s: dev->len =%lu\n", __func__, (unsigned long)dev->len);
- DBG (DBG_io2, "%s: dev->dist =%lu\n", __func__, (unsigned long)dev->dist);
- DBG (DBG_io2, "%s: dev->segnb =%lu\n", __func__, (unsigned long)dev->segnb);
-
- words_per_line *= channels;
- dev->wpl = words_per_line;
-
- dev->oe_buffer.clear();
- dev->oe_buffer.alloc(dev->wpl);
-
- /* MAXWD is expressed in 4 words unit */
- sanei_genesys_set_triple(reg, REG_MAXWD, (words_per_line >> 2));
- DBG(DBG_io2, "%s: words_per_line used=%d\n", __func__, words_per_line);
-
- sanei_genesys_set_double(reg, REG_LPERIOD, exposure_time);
- DBG(DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time);
-
- r = sanei_genesys_get_address (reg, 0x34);
- r->value = sensor.dummy_pixel;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* set up registers for an actual scan
- *
- * this function sets up the scanner to scan in normal or single line mode
- */
-static SANE_Status
-gl847_init_scan_regs(Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SetupParams& params)
-
-{
- params.assert_valid();
-
- int used_res;
- int start, used_pixels;
- int bytes_per_line;
- int move;
- unsigned int lincnt;
- unsigned int oflags; /**> optical flags */
- unsigned int mflags; /**> motor flags */
- int exposure_time;
- int stagger;
-
- int slope_dpi = 0;
- int dummy = 0;
- int scan_step_type = 1;
- int scan_power_mode = 0;
- int max_shift;
- size_t requested_buffer_size, read_buffer_size;
-
- SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
- int optical_res;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
- /* we may have 2 domains for ccd: xres below or above half ccd max dpi */
- if (sensor.get_ccd_size_divisor_for_dpi(params.xres) > 1)
- {
- half_ccd = SANE_TRUE;
- }
- else
- {
- half_ccd = SANE_FALSE;
- }
-
- /* optical_res */
- optical_res = sensor.optical_res;
- if (half_ccd)
- optical_res /= 2;
-
- /* stagger */
- if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG(DBG_info, "%s : stagger=%d lines\n", __func__, stagger);
-
- /* used_res */
- if (params.flags & SCAN_FLAG_USE_OPTICAL_RES)
- {
- used_res = optical_res;
- }
- else
- {
- /* resolution is choosen from a list */
- used_res = params.xres;
- }
-
- /* compute scan parameters values */
- /* pixels are allways given at full optical resolution */
- /* use detected left margin and fixed value */
- /* start */
- /* add x coordinates */
- start = params.startx;
-
- if (stagger > 0)
- start |= 1;
-
- /* compute correct pixels number */
- /* pixels */
- used_pixels = (params.pixels * optical_res) / params.xres;
-
- /* round up pixels number if needed */
- if (used_pixels * params.xres < params.pixels * optical_res)
- used_pixels++;
-
- dummy = 3-params.channels;
-
-/* slope_dpi */
-/* cis color scan is effectively a gray scan with 3 gray lines per color
- line and a FILTER of 0 */
- if (dev->model->is_cis)
- slope_dpi = params.yres * params.channels;
- else
- slope_dpi = params.yres;
-
- slope_dpi = slope_dpi * (1 + dummy);
-
- exposure_time = gl847_compute_exposure (dev, used_res);
- scan_step_type = sanei_genesys_compute_step_type(gl847_motors, dev->model->motor_type, exposure_time);
-
- DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
- DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, scan_step_type);
-
-/*** optical parameters ***/
- /* in case of dynamic lineart, we use an internal 8 bit gray scan
- * to generate 1 lineart data */
- if (params.flags & SCAN_FLAG_DYNAMIC_LINEART) {
- params.depth = 8;
- }
-
- /* we enable true gray for cis scanners only, and just when doing
- * scan since color calibration is OK for this mode
- */
- oflags = 0;
- if(params.flags & SCAN_FLAG_DISABLE_SHADING)
- oflags |= OPTICAL_FLAG_DISABLE_SHADING;
- if(params.flags & SCAN_FLAG_DISABLE_GAMMA)
- oflags |= OPTICAL_FLAG_DISABLE_GAMMA;
- if(params.flags & SCAN_FLAG_DISABLE_LAMP)
- oflags |= OPTICAL_FLAG_DISABLE_LAMP;
-
- if (dev->model->is_cis && dev->settings.true_gray)
- {
- oflags |= OPTICAL_FLAG_ENABLE_LEDADD;
- }
-
- status = gl847_init_optical_regs_scan (dev, sensor,
- reg,
- exposure_time,
- used_res,
- start,
- used_pixels,
- params.channels,
- params.depth,
- half_ccd,
- params.color_filter,
- oflags);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
-/*** motor parameters ***/
-
- /* max_shift */
- max_shift=sanei_genesys_compute_max_shift(dev,params.channels,params.yres,params.flags);
-
- /* lincnt */
- lincnt = params.lines + max_shift + stagger;
-
- /* add tl_y to base movement */
- move = params.starty;
- DBG(DBG_info, "%s: move=%d steps\n", __func__, move);
-
- mflags=0;
- if(params.flags & SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE)
- mflags |= MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE;
- if(params.flags & SCAN_FLAG_FEEDING)
- mflags |= MOTOR_FLAG_FEED;
-
- status = gl847_init_motor_regs_scan (dev, sensor,
- reg,
- exposure_time,
- slope_dpi,
- scan_step_type,
- dev->model->is_cis ? lincnt *
- params.channels : lincnt, dummy, move,
- scan_power_mode,
- mflags);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
-
- /*** prepares data reordering ***/
-
-/* words_per_line */
- bytes_per_line = (used_pixels * used_res) / optical_res;
- bytes_per_line = (bytes_per_line * params.channels * params.depth) / 8;
-
- requested_buffer_size = 8 * bytes_per_line;
-
- read_buffer_size =
- 2 * requested_buffer_size +
- ((max_shift + stagger) * used_pixels * params.channels * params.depth) / 8;
-
- dev->read_buffer.clear();
- dev->read_buffer.alloc(read_buffer_size);
-
- dev->lines_buffer.clear();
- dev->lines_buffer.alloc(read_buffer_size);
-
- dev->shrink_buffer.clear();
- dev->shrink_buffer.alloc(requested_buffer_size);
-
- dev->out_buffer.clear();
- dev->out_buffer.alloc((8 * params.pixels * params.channels * params.depth) / 8);
-
- dev->read_bytes_left = bytes_per_line * lincnt;
-
- DBG(DBG_info, "%s: physical bytes to read = %lu\n", __func__, (u_long) dev->read_bytes_left);
- dev->read_active = SANE_TRUE;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
-/* TODO: should this be done elsewhere? */
- /* scan bytes to send to the frontend */
- /* theory :
- target_size =
- (params.pixels * params.lines * channels * depth) / 8;
- but it suffers from integer overflow so we do the following:
-
- 1 bit color images store color data byte-wise, eg byte 0 contains
- 8 bits of red data, byte 1 contains 8 bits of green, byte 2 contains
- 8 bits of blue.
- This does not fix the overflow, though.
- 644mp*16 = 10gp, leading to an overflow
- -- pierre
- */
-
- dev->total_bytes_read = 0;
- if (params.depth == 1)
- dev->total_bytes_to_read =
- ((params.pixels * params.lines) / 8 +
- (((params.pixels * params.lines) % 8) ? 1 : 0)) *
- params.channels;
- else
- dev->total_bytes_to_read =
- params.pixels * params.lines * params.channels * (params.depth / 8);
-
- DBG(DBG_info, "%s: total bytes to send = %lu\n", __func__, (u_long) dev->total_bytes_to_read);
-/* END TODO */
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static void
-gl847_calculate_current_setup(Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int depth;
- int start;
-
- int used_res;
- int used_pixels;
- unsigned int lincnt;
- int exposure_time;
- int stagger;
-
- int slope_dpi = 0;
- int dummy = 0;
- int max_shift;
-
- SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
- int optical_res;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
- /* start */
- start = SANE_UNFIX (dev->model->x_offset);
- start += dev->settings.tl_x;
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start; // not used
- params.starty = 0; // not used
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = 0;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, params);
-
-/* half_ccd */
- /* we have 2 domains for ccd: xres below or above half ccd max dpi */
- if (sensor.get_ccd_size_divisor_for_dpi(params.xres) > 1) {
- half_ccd = SANE_TRUE;
- } else {
- half_ccd = SANE_FALSE;
- }
-
- /* optical_res */
- optical_res = sensor.optical_res;
-
- /* stagger */
- if (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE)
- stagger = (4 * params.yres) / dev->motor.base_ydpi;
- else
- stagger = 0;
- DBG(DBG_info, "%s: stagger=%d lines\n", __func__, stagger);
-
- /* resolution is choosen from a fixed list */
- used_res = params.xres;
-
- /* compute scan parameters values */
- /* pixels are allways given at half or full CCD optical resolution */
- /* use detected left margin and fixed value */
-
- /* compute correct pixels number */
- used_pixels = (params.pixels * optical_res) / used_res;
- dummy = 3 - params.channels;
-
- /* slope_dpi */
- /* cis color scan is effectively a gray scan with 3 gray lines per color
- line and a FILTER of 0 */
- if (dev->model->is_cis) {
- slope_dpi = params.yres * params.channels;
- } else {
- slope_dpi = params.yres;
- }
-
- slope_dpi = slope_dpi * (1 + dummy);
-
- exposure_time = gl847_compute_exposure (dev, used_res);
- DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);
-
- /* max_shift */
- max_shift = sanei_genesys_compute_max_shift(dev, params.channels, params.yres, 0);
-
- /* lincnt */
- lincnt = params.lines + max_shift + stagger;
-
- dev->current_setup.params = params;
- dev->current_setup.pixels = (used_pixels * used_res) / optical_res;
- dev->current_setup.lines = lincnt;
- dev->current_setup.depth = params.depth;
- dev->current_setup.channels = params.channels;
- dev->current_setup.exposure_time = exposure_time;
- dev->current_setup.xres = used_res;
- dev->current_setup.yres = params.yres;
- dev->current_setup.ccd_size_divisor = half_ccd ? 2 : 1;
- dev->current_setup.stagger = stagger;
- dev->current_setup.max_shift = max_shift + stagger;
-
- DBGCOMPLETED;
-}
-
-/*for fast power saving methods only, like disabling certain amplifiers*/
-static SANE_Status
-gl847_save_power (Genesys_Device * dev, SANE_Bool enable)
-{
- DBG(DBG_proc, "%s: enable = %d\n", __func__, enable);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl847_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
-{
- DBG(DBG_proc, "%s (delay = %d)\n", __func__, delay);
- if (dev == NULL)
- return SANE_STATUS_INVAL;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl847_start_action (Genesys_Device * dev)
-{
- return sanei_genesys_write_register (dev, 0x0f, 0x01);
-}
-
-static SANE_Status
-gl847_stop_action (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val40, val;
- unsigned int loop;
-
- DBGSTART;
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl847_homsnr_gpio(dev);
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- status = sanei_genesys_read_register (dev, REG40, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* only stop action if needed */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG))
- {
- DBG(DBG_info, "%s: already stopped\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- /* ends scan */
- val = dev->reg.get8(REG01);
- val &= ~REG01_SCAN;
- sanei_genesys_set_reg_from_set(&dev->reg, REG01, val);
- status = sanei_genesys_write_register (dev, REG01, val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to write register 01: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_sleep_ms(100);
-
- loop = 10;
- while (loop > 0)
- {
- status = sanei_genesys_get_status (dev, &val);
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- status = sanei_genesys_read_register (dev, REG40, &val40);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* if scanner is in command mode, we are done */
- if (!(val40 & REG40_DATAENB) && !(val40 & REG40_MOTMFLG)
- && !(val & REG41_MOTORENB))
- {
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- sanei_genesys_sleep_ms(100);
- loop--;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_IO_ERROR;
-}
-
-/* Send the low-level scan command */
-static SANE_Status
-gl847_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * reg,
- SANE_Bool start_motor)
-{
- (void) sensor;
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- GenesysRegister *r;
-
- DBGSTART;
-
- /* clear GPIO 10 */
- if (dev->model->gpo_type != GPO_CANONLIDE700)
- {
- RIE (sanei_genesys_read_register (dev, REG6C, &val));
- val &= ~REG6C_GPIO10;
- RIE (sanei_genesys_write_register (dev, REG6C, val));
- }
-
- val = REG0D_CLRLNCNT;
- RIE (sanei_genesys_write_register (dev, REG0D, val));
- val = REG0D_CLRMCNT;
- RIE (sanei_genesys_write_register (dev, REG0D, val));
-
- RIE (sanei_genesys_read_register (dev, REG01, &val));
- val |= REG01_SCAN;
- RIE (sanei_genesys_write_register (dev, REG01, val));
- r = sanei_genesys_get_address (reg, REG01);
- r->value = val;
-
- if (start_motor)
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 1));
- }
- else
- {
- RIE (sanei_genesys_write_register (dev, REG0F, 0));
- }
-
- DBGCOMPLETED;
-
- return status;
-}
-
-
-/* Send the stop scan command */
-static SANE_Status
-gl847_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
- SANE_Bool check_stop)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_proc, "%s (check_stop = %d)\n", __func__, check_stop);
- if (reg == NULL)
- return SANE_STATUS_INVAL;
-
- if (dev->model->is_sheetfed == SANE_TRUE)
- {
- status = SANE_STATUS_GOOD;
- }
- else /* flat bed scanners */
- {
- status = gl847_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/** rewind scan
- * Move back by the same amount of distance than previous scan.
- * @param dev device to rewind
- * @returns SANE_STATUS_GOOD on success
- */
-#if 0 /* disabled to fix #7 */
-static
-SANE_Status gl847_rewind(Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t byte;
-
- DBGSTART;
-
- /* set motor reverse */
- RIE (sanei_genesys_read_register (dev, 0x02, &byte));
- byte |= 0x04;
- RIE (sanei_genesys_write_register(dev, 0x02, byte));
-
- /* and start scan, then wait completion */
- RIE (gl847_begin_scan (dev, dev->reg, SANE_TRUE));
- do
- {
- sanei_genesys_sleep_ms(100);
- RIE (sanei_genesys_read_register (dev, REG40, &byte));
- }
- while(byte & REG40_MOTMFLG);
- RIE (gl847_end_scan (dev, dev->reg, SANE_TRUE));
-
- /* restore direction */
- RIE (sanei_genesys_read_register (dev, 0x02, &byte));
- byte &= 0xfb;
- RIE (sanei_genesys_write_register(dev, 0x02, byte));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-#endif
-
-/** Park head
- * Moves the slider to the home (top) position slowly
- * @param dev device to park
- * @param wait_until_home true to make the function waiting for head
- * to be home before returning, if fals returne immediately
- * @returns SANE_STATUS_GOO on success */
-static
-SANE_Status
-gl847_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- float resolution;
- uint8_t val;
- int loop = 0;
- ScanColorMode scan_mode;
-
- DBG(DBG_proc, "%s (wait_until_home = %d)\n", __func__, wait_until_home);
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl847_homsnr_gpio(dev);
-
- /* first read gives HOME_SENSOR true */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
- sanei_genesys_sleep_ms(100);
-
- /* second is reliable */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io)
- {
- sanei_genesys_print_status (val);
- }
-
- /* is sensor at home? */
- if (val & HOMESNR)
- {
- DBG(DBG_info, "%s: already at home, completed\n", __func__);
- dev->scanhead_position_in_steps = 0;
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
-
- local_reg = dev->reg;
-
- resolution=sanei_genesys_get_lowest_ydpi(dev);
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- /* TODO add scan_mode to the API */
- scan_mode = dev->settings.scan_mode;
- dev->settings.scan_mode = ScanColorMode::LINEART;
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 100;
- params.starty = 30000;
- params.pixels = 100;
- params.lines = 100;
- params.depth = 8;
- params.channels = 1;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl847_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- dev->settings.scan_mode = scan_mode;
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT | REG0D_CLRMCNT));
-
- /* set up for reverse */
- r = sanei_genesys_get_address (&local_reg, REG02);
- r->value |= REG02_MTRREV;
-
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- try {
- status = gl847_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl847_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl847_stop_action (dev);
- /* send original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- /* post scan gpio : without that HOMSNR is unreliable */
- gl847_homsnr_gpio(dev);
-
- if (wait_until_home)
- {
- while (loop < 300) /* do not wait longer then 30 seconds */
- {
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- if (val & HOMESNR) /* home sensor */
- {
- DBG(DBG_info, "%s: reached home position\n", __func__);
- gl847_stop_action (dev);
- dev->scanhead_position_in_steps = 0;
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
- }
- sanei_genesys_sleep_ms(100);
- ++loop;
- }
-
- /* when we come here then the scanner needed too much time for this, so we better stop the motor */
- gl847_stop_action (dev);
- DBG(DBG_error, "%s: timeout while waiting for scanhead to go home\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
- area at 600 dpi from very top of scanner */
-static SANE_Status
-gl847_search_start_position (Genesys_Device * dev)
-{
- int size;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- int steps;
-
- int pixels = 600;
- int dpi = 300;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- local_reg = dev->reg;
-
- /* sets for a 200 lines * 600 pixels */
- /* normal scan with no shading */
-
- // FIXME: the current approach of doing search only for one resolution does not work on scanners
- // whith employ different sensors with potentially different settings.
- auto& sensor = sanei_genesys_find_sensor_for_write(dev, dpi);
-
- SetupParams params;
- params.xres = dpi;
- params.yres = dpi;
- params.startx = 0;
- params.starty = 0; /*we should give a small offset here~60 steps */
- params.pixels = 600;
- params.lines = dev->model->search_lines;
- params.depth = 8;
- params.channels = 1;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::GREEN;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl847_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* send to scanner */
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- size = pixels * dev->model->search_lines;
-
- std::vector<uint8_t> data(size);
-
- status = gl847_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl847_search_position.pnm", data.data(), 8, 1, pixels,
- dev->model->search_lines);
-
- status = gl847_end_scan(dev, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to end scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* update regs to copy ASIC internal state */
- dev->reg = local_reg;
-
-/*TODO: find out where sanei_genesys_search_reference_point
- stores information, and use that correctly*/
- status =
- sanei_genesys_search_reference_point(dev, sensor, data.data(), 0, dpi, pixels,
- dev->model->search_lines);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set search reference point: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/*
- * sets up register for coarse gain calibration
- * todo: check it for scanners using it */
-static SANE_Status
-gl847_init_regs_for_coarse_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t channels;
- uint8_t cksel;
-
- DBG(DBG_proc, "%s\n", __func__);
-
-
- cksel = (regs.find_reg(0x18).value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
-
- /* set line size */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else {
- channels = 1;
- }
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = 0;
- params.starty = 0;
- params.pixels = sensor.optical_res / cksel;
- params.lines = 20;
- params.depth = 16;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl847_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__,
- sensor.optical_res / cksel, dev->settings.xres);
-
- status =
- dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief moves the slider to steps at motor base dpi
- * @param dev device to work on
- * @param steps number of steps to move in base_dpi line count
- * */
-static SANE_Status
-gl847_feed (Genesys_Device * dev, unsigned int steps)
-{
- Genesys_Register_Set local_reg;
- SANE_Status status = SANE_STATUS_GOOD;
- GenesysRegister *r;
- float resolution;
- uint8_t val;
-
- DBGSTART;
- DBG(DBG_io, "%s: steps=%d\n", __func__, steps);
-
- local_reg = dev->reg;
-
- resolution=sanei_genesys_get_lowest_ydpi(dev);
- const auto& sensor = sanei_genesys_find_sensor(dev, resolution);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = steps;
- params.pixels = 100;
- params.lines = 3;
- params.depth = 8;
- params.channels = 3;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_FEEDING |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl847_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to set up registers: %s\n", __func__, sane_strstatus(status));
- DBGCOMPLETED;
- return status;
- }
-
- /* set exposure to zero */
- sanei_genesys_set_triple(&local_reg,REG_EXPR,0);
- sanei_genesys_set_triple(&local_reg,REG_EXPG,0);
- sanei_genesys_set_triple(&local_reg,REG_EXPB,0);
-
- /* clear scan and feed count */
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRLNCNT));
- RIE (sanei_genesys_write_register (dev, REG0D, REG0D_CLRMCNT));
-
- /* set up for no scan */
- r = sanei_genesys_get_address(&local_reg, REG01);
- r->value &= ~REG01_SCAN;
-
- /* send registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, local_reg));
-
- try {
- status = gl847_start_action (dev);
- } catch (...) {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- try {
- gl847_stop_action(dev);
- } catch (...) {}
- try {
- // restore original registers
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- } catch (...) {}
- throw;
- }
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to start motor: %s\n", __func__, sane_strstatus(status));
- gl847_stop_action (dev);
-
- /* restore original registers */
- dev->model->cmd_set->bulk_write_register(dev, dev->reg);
- return status;
- }
-
- /* wait until feed count reaches the required value, but do not
- * exceed 30s */
- do
- {
- status = sanei_genesys_get_status (dev, &val);
- }
- while (status == SANE_STATUS_GOOD && !(val & FEEDFSH));
-
- /* then stop scanning */
- RIE(gl847_stop_action (dev));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/* init registers for shading calibration */
-static SANE_Status
-gl847_init_regs_for_shading(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- float move;
-
- DBGSTART;
- dev->calib_channels = 3;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- dev->calib_resolution = sanei_genesys_compute_dpihw(dev, sensor, dev->settings.xres);
- dev->calib_total_bytes_to_read = 0;
- dev->calib_lines = dev->model->shading_lines;
- if(dev->calib_resolution==4800)
- dev->calib_lines *= 2;
- dev->calib_pixels = (sensor.sensor_pixels*dev->calib_resolution)/sensor.optical_res;
- DBG(DBG_io, "%s: calib_lines = %d\n", __func__, (int)dev->calib_lines);
- DBG(DBG_io, "%s: calib_pixels = %d\n", __func__, (int)dev->calib_pixels);
-
- /* this is aworkaround insufficent distance for slope
- * motor acceleration TODO special motor slope for shading */
- move=1;
- if(dev->calib_resolution<1200)
- {
- move=40;
- }
-
- SetupParams params;
- params.xres = dev->calib_resolution;
- params.yres = dev->calib_resolution;
- params.startx = 0;
- params.starty = move;
- params.pixels = dev->calib_pixels;
- params.lines = dev->calib_lines;
- params.depth = 16;
- params.channels = dev->calib_channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl847_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = dev->model->cmd_set->bulk_write_register(dev, regs);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* we use GENESYS_FLAG_SHADING_REPARK */
- dev->scanhead_position_in_steps = 0;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** @brief set up registers for the actual scan
- */
-static SANE_Status
-gl847_init_regs_for_scan (Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int channels;
- int flags;
- int depth;
- float move;
- int move_dpi;
- float start;
-
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG(DBG_info, "%s ", __func__);
- debug_dump(DBG_info, dev->settings);
-
- /* channels */
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- channels = 3;
- else
- channels = 1;
-
- /* depth */
- depth = dev->settings.depth;
- if (dev->settings.scan_mode == ScanColorMode::LINEART)
- depth = 1;
-
-
- /* steps to move to reach scanning area:
- - first we move to physical start of scanning
- either by a fixed steps amount from the black strip
- or by a fixed amount from parking position,
- minus the steps done during shading calibration
- - then we move by the needed offset whitin physical
- scanning area
-
- assumption: steps are expressed at maximum motor resolution
-
- we need:
- SANE_Fixed y_offset;
- SANE_Fixed y_size;
- SANE_Fixed y_offset_calib;
- mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
-
- /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
- relative from origin, else, it is from parking position */
-
- move_dpi = dev->motor.base_ydpi;
-
- move = SANE_UNFIX (dev->model->y_offset);
- move += dev->settings.tl_y;
- move = (move * move_dpi) / MM_PER_INCH;
- move -= dev->scanhead_position_in_steps;
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- /* fast move to scan area */
- /* we don't move fast the whole distance since it would involve
- * computing acceleration/deceleration distance for scan
- * resolution. So leave a remainder for it so scan makes the final
- * move tuning */
- if(channels*dev->settings.yres>=600 && move>700)
- {
- status = gl847_feed (dev, move-500);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to move to scan area\n", __func__);
- return status;
- }
- move=500;
- }
-
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
- DBG(DBG_info, "%s: move=%f steps\n", __func__, move);
-
- /* start */
- start = SANE_UNFIX (dev->model->x_offset);
- start += dev->settings.tl_x;
- start = (start * sensor.optical_res) / MM_PER_INCH;
-
- flags = 0;
-
- /* emulated lineart from gray data is required for now */
- if(dev->settings.scan_mode == ScanColorMode::LINEART
- && dev->settings.dynamic_lineart)
- {
- flags |= SCAN_FLAG_DYNAMIC_LINEART;
- }
-
- /* backtracking isn't handled well, so don't enable it */
- flags |= SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE;
-
- SetupParams params;
- params.xres = dev->settings.xres;
- params.yres = dev->settings.yres;
- params.startx = start;
- params.starty = move;
- params.pixels = dev->settings.pixels;
- params.lines = dev->settings.lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = dev->settings.scan_mode;
- params.color_filter = dev->settings.color_filter;
- params.flags = flags;
-
- status = gl847_init_scan_regs(dev, sensor, &dev->reg, params);
-
- if (status != SANE_STATUS_GOOD)
- return status;
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-
-/**
- * Send shading calibration data. The buffer is considered to always hold values
- * for all the channels.
- */
-static SANE_Status
-gl847_send_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data, int size)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint32_t addr, length, i, x, factor, pixels;
- uint32_t dpiset, dpihw, strpixel, endpixel;
- uint16_t tempo;
- uint32_t lines, channels;
- uint8_t val,*ptr,*src;
-
- DBGSTART;
- DBG(DBG_io2, "%s: writing %d bytes of shading data\n", __func__, size);
-
- /* shading data is plit in 3 (up to 5 with IR) areas
- write(0x10014000,0x00000dd8)
- URB 23429 bulk_out len 3544 wrote 0x33 0x10 0x....
- write(0x1003e000,0x00000dd8)
- write(0x10068000,0x00000dd8)
- */
- length = (uint32_t) (size / 3);
- sanei_genesys_get_double(&dev->reg,REG_STRPIXEL,&tempo);
- strpixel=tempo;
- sanei_genesys_get_double(&dev->reg,REG_ENDPIXEL,&tempo);
- endpixel=tempo;
-
- /* compute deletion factor */
- sanei_genesys_get_double(&dev->reg,REG_DPISET,&tempo);
- dpiset=tempo;
- DBG(DBG_io2, "%s: STRPIXEL=%d, ENDPIXEL=%d, PIXELS=%d, DPISET=%d\n", __func__, strpixel, endpixel,
- endpixel-strpixel, dpiset);
- dpihw=sanei_genesys_compute_dpihw(dev, sensor, dpiset);
- factor=dpihw/dpiset;
- DBG(DBG_io2, "%s: factor=%d\n", __func__, factor);
-
- if(DBG_LEVEL>=DBG_data)
- {
- dev->binary=fopen("binary.pnm","wb");
- sanei_genesys_get_triple(&dev->reg, REG_LINCNT, &lines);
- channels=dev->current_setup.channels;
- if(dev->binary!=NULL)
- {
- fprintf(dev->binary,"P5\n%d %d\n%d\n",(endpixel-strpixel)/factor*channels,lines/channels,255);
- }
- }
-
- pixels=endpixel-strpixel;
-
- /* since we're using SHDAREA, substract startx coordinate from shading */
- strpixel-=((sensor.CCD_start_xoffset*600)/sensor.optical_res);
-
- /* turn pixel value into bytes 2x16 bits words */
- strpixel*=2*2;
- pixels*=2*2;
-
- std::vector<uint8_t> buffer(pixels, 0);
-
- DBG(DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n", __func__, pixels, pixels);
-
- /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address
- * is 8192*reg value */
-
- /* write actual color channel data */
- for(i=0;i<3;i++)
- {
- /* build up actual shading data by copying the part from the full width one
- * to the one corresponding to SHDAREA */
- ptr = buffer.data();
-
- /* iterate on both sensor segment */
- for(x=0;x<pixels;x+=4*factor)
- {
- /* coefficient source */
- src=(data+strpixel+i*length)+x;
-
- /* coefficient copy */
- ptr[0]=src[0];
- ptr[1]=src[1];
- ptr[2]=src[2];
- ptr[3]=src[3];
-
- /* next shading coefficient */
- ptr+=4;
- }
-
- RIE (sanei_genesys_read_register (dev, 0xd0+i, &val));
- addr = val * 8192 + 0x10000000;
- status = sanei_genesys_write_ahb(dev, addr, pixels, buffer.data());
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s; write to AHB failed (%s)\n", __func__, sane_strstatus(status));
- return status;
- }
- }
-
- DBGCOMPLETED;
-
- return status;
-}
-
-/** @brief calibrates led exposure
- * Calibrate exposure by scanning a white area until the used exposure gives
- * data white enough.
- * @param dev device to calibrate
- */
-static SANE_Status
-gl847_led_calibration (Genesys_Device * dev, Genesys_Sensor& sensor, Genesys_Register_Set& regs)
-{
- int num_pixels;
- int total_size;
- int used_res;
- int i, j;
- SANE_Status status = SANE_STATUS_GOOD;
- int val;
- int channels, depth;
- int avg[3], top[3], bottom[3];
- int turn;
- uint16_t exp[3];
- float move;
- SANE_Bool acceptable;
-
- DBGSTART;
-
- move = SANE_UNFIX (dev->model->y_offset_calib);
- move = (move * (dev->motor.base_ydpi/4)) / MM_PER_INCH;
- if(move>20)
- {
- RIE(gl847_feed (dev, move));
- }
- DBG(DBG_io, "%s: move=%f steps\n", __func__, move);
-
- /* offset calibration is always done in color mode */
- channels = 3;
- depth=16;
- used_res=sanei_genesys_compute_dpihw(dev, sensor, dev->settings.xres);
- Sensor_Profile* sensor_profile=get_sensor_profile(dev->model->ccd_type, used_res);
- num_pixels = (sensor.sensor_pixels*used_res)/sensor.optical_res;
-
- /* initial calibration reg values */
- regs = dev->reg;
-
- SetupParams params;
- params.xres = used_res;
- params.yres = used_res;
- params.startx = 0;
- params.starty = 0;
- params.pixels = num_pixels;
- params.lines = 1;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl847_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- total_size = num_pixels * channels * (depth/8) * 1; /* colors * bytes_per_color * scan lines */
- std::vector<uint8_t> line(total_size);
-
- /* initial loop values and boundaries */
- exp[0]=sensor_profile->expr;
- exp[1]=sensor_profile->expg;
- exp[2]=sensor_profile->expb;
-
- bottom[0]=29000;
- bottom[1]=29000;
- bottom[2]=29000;
-
- top[0]=41000;
- top[1]=51000;
- top[2]=51000;
-
- turn = 0;
-
- /* no move during led calibration */
- sanei_genesys_set_motor_power(regs, false);
- do
- {
- /* set up exposure */
- sanei_genesys_set_double(&regs,REG_EXPR,exp[0]);
- sanei_genesys_set_double(&regs,REG_EXPG,exp[1]);
- sanei_genesys_set_double(&regs,REG_EXPB,exp[2]);
-
- /* write registers and scan data */
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- DBG(DBG_info, "%s: starting line reading\n", __func__);
- RIE(gl847_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
-
- /* stop scanning */
- RIE(gl847_stop_action (dev));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl847_led_%02d.pnm", turn);
- sanei_genesys_write_pnm_file(fn, line.data(), depth, channels, num_pixels, 1);
- }
-
- /* compute average */
- for (j = 0; j < channels; j++)
- {
- avg[j] = 0;
- for (i = 0; i < num_pixels; i++)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * num_pixels + 1] * 256 +
- line[i * 2 + j * 2 * num_pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- avg[j] += val;
- }
-
- avg[j] /= num_pixels;
- }
-
- DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
-
- /* check if exposure gives average within the boundaries */
- acceptable = SANE_TRUE;
- for(i=0;i<3;i++)
- {
- if(avg[i]<bottom[i])
- {
- exp[i]=(exp[i]*bottom[i])/avg[i];
- acceptable = SANE_FALSE;
- }
- if(avg[i]>top[i])
- {
- exp[i]=(exp[i]*top[i])/avg[i];
- acceptable = SANE_FALSE;
- }
- }
-
- turn++;
- }
- while (!acceptable && turn < 100);
-
- DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]);
-
- /* set these values as final ones for scan */
- sanei_genesys_set_double(&dev->reg,REG_EXPR,exp[0]);
- sanei_genesys_set_double(&dev->reg,REG_EXPG,exp[1]);
- sanei_genesys_set_double(&dev->reg,REG_EXPB,exp[2]);
-
- /* store in this struct since it is the one used by cache calibration */
- sensor.exposure.red = exp[0];
- sensor.exposure.green = exp[1];
- sensor.exposure.blue = exp[2];
-
- /* go back home */
- if(move>20)
- {
- status=gl847_slow_back_home (dev, SANE_TRUE);
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * set up GPIO/GPOE for idle state
- */
-static SANE_Status
-gl847_init_gpio (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int idx=0;
-
- DBGSTART;
-
- /* search GPIO profile */
- while(gpios[idx].sensor_id!=0 && dev->model->gpo_type!=gpios[idx].sensor_id)
- {
- idx++;
- }
- if(gpios[idx].sensor_id==0)
- {
- DBG(DBG_error, "%s: failed to find GPIO profile for sensor_id=%d\n", __func__,
- dev->model->ccd_type);
- return SANE_STATUS_INVAL;
- }
-
- RIE (sanei_genesys_write_register (dev, REGA7, gpios[idx].ra7));
- RIE (sanei_genesys_write_register (dev, REGA6, gpios[idx].ra6));
-
- RIE (sanei_genesys_write_register (dev, REG6E, gpios[idx].r6e));
- RIE (sanei_genesys_write_register (dev, REG6C, 0x00));
-
- RIE (sanei_genesys_write_register (dev, REG6B, gpios[idx].r6b));
- RIE (sanei_genesys_write_register (dev, REG6C, gpios[idx].r6c));
- RIE (sanei_genesys_write_register (dev, REG6D, gpios[idx].r6d));
- RIE (sanei_genesys_write_register (dev, REG6E, gpios[idx].r6e));
- RIE (sanei_genesys_write_register (dev, REG6F, gpios[idx].r6f));
-
- RIE (sanei_genesys_write_register (dev, REGA8, gpios[idx].ra8));
- RIE (sanei_genesys_write_register (dev, REGA9, gpios[idx].ra9));
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * set memory layout by filling values in dedicated registers
- */
-static SANE_Status
-gl847_init_memory_layout (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int idx = 0;
- uint8_t val;
-
- DBG(DBG_proc, "%s\n" , __func__);
-
- /* point to per model memory layout */
- idx = 0;
- if (dev->model->model_id == MODEL_CANON_LIDE_100)
- {
- idx = 0;
- }
- if (dev->model->model_id == MODEL_CANON_LIDE_200)
- {
- idx = 1;
- }
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_5600F)
- {
- idx = 2;
- }
- if (dev->model->model_id == MODEL_CANON_LIDE_700F)
- {
- idx = 3;
- }
-
- /* CLKSET nd DRAMSEL */
- val = layouts[idx].dramsel;
- RIE (sanei_genesys_write_register (dev, REG0B, val));
- dev->reg.find_reg(0x0b).value = val;
-
- /* prevent further writings by bulk write register */
- dev->reg.remove_reg(0x0b);
-
- /* setup base address for shading data. */
- /* values must be multiplied by 8192=0x4000 to give address on AHB */
- /* R-Channel shading bank0 address setting for CIS */
- sanei_genesys_write_register (dev, 0xd0, layouts[idx].rd0);
- /* G-Channel shading bank0 address setting for CIS */
- sanei_genesys_write_register (dev, 0xd1, layouts[idx].rd1);
- /* B-Channel shading bank0 address setting for CIS */
- sanei_genesys_write_register (dev, 0xd2, layouts[idx].rd2);
-
- /* setup base address for scanned data. */
- /* values must be multiplied by 1024*2=0x0800 to give address on AHB */
- /* R-Channel ODD image buffer 0x0124->0x92000 */
- /* size for each buffer is 0x16d*1k word */
- sanei_genesys_write_register (dev, 0xe0, layouts[idx].re0);
- sanei_genesys_write_register (dev, 0xe1, layouts[idx].re1);
- /* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
- sanei_genesys_write_register (dev, 0xe2, layouts[idx].re2);
- sanei_genesys_write_register (dev, 0xe3, layouts[idx].re3);
-
- /* R-Channel EVEN image buffer 0x0292 */
- sanei_genesys_write_register (dev, 0xe4, layouts[idx].re4);
- sanei_genesys_write_register (dev, 0xe5, layouts[idx].re5);
- /* R-Channel EVEN image buffer end-address 0x03ff*/
- sanei_genesys_write_register (dev, 0xe6, layouts[idx].re6);
- sanei_genesys_write_register (dev, 0xe7, layouts[idx].re7);
-
- /* same for green, since CIS, same addresses */
- sanei_genesys_write_register (dev, 0xe8, layouts[idx].re0);
- sanei_genesys_write_register (dev, 0xe9, layouts[idx].re1);
- sanei_genesys_write_register (dev, 0xea, layouts[idx].re2);
- sanei_genesys_write_register (dev, 0xeb, layouts[idx].re3);
- sanei_genesys_write_register (dev, 0xec, layouts[idx].re4);
- sanei_genesys_write_register (dev, 0xed, layouts[idx].re5);
- sanei_genesys_write_register (dev, 0xee, layouts[idx].re6);
- sanei_genesys_write_register (dev, 0xef, layouts[idx].re7);
-
-/* same for blue, since CIS, same addresses */
- sanei_genesys_write_register (dev, 0xf0, layouts[idx].re0);
- sanei_genesys_write_register (dev, 0xf1, layouts[idx].re1);
- sanei_genesys_write_register (dev, 0xf2, layouts[idx].re2);
- sanei_genesys_write_register (dev, 0xf3, layouts[idx].re3);
- sanei_genesys_write_register (dev, 0xf4, layouts[idx].re4);
- sanei_genesys_write_register (dev, 0xf5, layouts[idx].re5);
- sanei_genesys_write_register (dev, 0xf6, layouts[idx].re6);
- sanei_genesys_write_register (dev, 0xf7, layouts[idx].re7);
-
- DBGCOMPLETED;
- return status;
-}
-
-/* *
- * initialize ASIC from power on condition
- */
-static SANE_Status
-gl847_boot (Genesys_Device * dev, SANE_Bool cold)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
-
- DBGSTART;
-
- /* reset ASIC if cold boot */
- if(cold)
- {
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x01));
- RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
- }
-
- /* test CHKVER */
- RIE (sanei_genesys_read_register (dev, REG40, &val));
- if (val & REG40_CHKVER)
- {
- RIE (sanei_genesys_read_register (dev, 0x00, &val));
- DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
- }
-
- /* Set default values for registers */
- gl847_init_registers (dev);
-
- /* Write initial registers */
- RIE (dev->model->cmd_set->bulk_write_register(dev, dev->reg));
-
- /* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
- val = dev->reg.find_reg(0x0b).value & REG0B_DRAMSEL;
- val = (val | REG0B_ENBDRAM);
- RIE (sanei_genesys_write_register (dev, REG0B, val));
- dev->reg.find_reg(0x0b).value = val;
-
- /* CIS_LINE */
- SETREG (0x08, REG08_CIS_LINE);
- RIE (sanei_genesys_write_register (dev, 0x08, dev->reg.find_reg(0x08).value));
-
- /* set up end access */
- RIE (sanei_genesys_write_0x8c (dev, 0x10, 0x0b));
- RIE (sanei_genesys_write_0x8c (dev, 0x13, 0x0e));
-
- /* setup gpio */
- RIE (gl847_init_gpio (dev));
-
- /* setup internal memory layout */
- RIE (gl847_init_memory_layout (dev));
-
- SETREG (0xf8, 0x01);
- RIE (sanei_genesys_write_register (dev, 0xf8, dev->reg.find_reg(0xf8).value));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/**
- * initialize backend and ASIC : registers, motor tables, and gamma tables
- * then ensure scanner's head is at home
- */
-static SANE_Status gl847_init (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBG_INIT ();
- DBGSTART;
-
- status=sanei_genesys_asic_init(dev, 0);
-
- DBGCOMPLETED;
- return status;
-}
-
-static SANE_Status
-gl847_update_hardware_sensors (Genesys_Scanner * s)
-{
- /* do what is needed to get a new set of events, but try to not lose
- any of them.
- */
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- uint8_t scan, file, email, copy;
- switch(s->dev->model->gpo_type)
- {
- case GPO_CANONLIDE700:
- scan=0x04;
- file=0x02;
- email=0x01;
- copy=0x08;
- break;
- default:
- scan=0x01;
- file=0x02;
- email=0x04;
- copy=0x08;
- }
- RIE (sanei_genesys_read_register (s->dev, REG6D, &val));
-
- s->buttons[BUTTON_SCAN_SW].write((val & scan) == 0);
- s->buttons[BUTTON_FILE_SW].write((val & file) == 0);
- s->buttons[BUTTON_EMAIL_SW].write((val & email) == 0);
- s->buttons[BUTTON_COPY_SW].write((val & copy) == 0);
-
- return status;
-}
-
-/** @brief search for a full width black or white strip.
- * This function searches for a black or white stripe across the scanning area.
- * When searching backward, the searched area must completely be of the desired
- * color since this area will be used for calibration which scans forward.
- * @param dev scanner device
- * @param forward SANE_TRUE if searching forward, SANE_FALSE if searching backward
- * @param black SANE_TRUE if searching for a black strip, SANE_FALSE for a white strip
- * @return SANE_STATUS_GOOD if a matching strip is found, SANE_STATUS_UNSUPPORTED if not
- */
-static SANE_Status
-gl847_search_strip (Genesys_Device * dev, const Genesys_Sensor& sensor,
- SANE_Bool forward, SANE_Bool black)
-{
- unsigned int pixels, lines, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set local_reg;
- size_t size;
- int steps, depth, dpi;
- unsigned int pass, count, found, x, y;
- char title[80];
- GenesysRegister *r;
-
- DBG(DBG_proc, "%s %s %s\n", __func__, black ? "black" : "white", forward ? "forward" : "reverse");
-
- gl847_set_fe(dev, sensor, AFE_SET);
- status = gl847_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to stop: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* set up for a gray scan at lowest dpi */
- dpi = 9600;
- for (x = 0; x < MAX_RESOLUTIONS; x++)
- {
- if (dev->model->xdpi_values[x] > 0 && dev->model->xdpi_values[x] < dpi)
- dpi = dev->model->xdpi_values[x];
- }
- channels = 1;
- /* 10 MM */
- /* lines = (10 * dpi) / MM_PER_INCH; */
- /* shading calibation is done with dev->motor.base_ydpi */
- lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi;
- depth = 8;
- pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res;
- size = pixels * channels * lines * (depth / 8);
- std::vector<uint8_t> data(size);
- dev->scanhead_position_in_steps = 0;
-
- local_reg = dev->reg;
-
- SetupParams params;
- params.xres = dpi;
- params.yres = dpi;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = depth;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::GRAY;
- params.color_filter = ColorFilter::RED;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA;
-
- status = gl847_init_scan_regs(dev, sensor, &local_reg, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup for scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* set up for reverse or forward */
- r = sanei_genesys_get_address(&local_reg, REG02);
- if (forward)
- r->value &= ~REG02_MTRREV;
- else
- r->value |= REG02_MTRREV;
-
-
- status = dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to bulk write registers: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl847_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl847_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl847_stop_action failed\n", __func__);
- return status;
- }
-
- pass = 0;
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf(title, "gl847_search_strip_%s_%s%02d.pnm",
- black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
- sanei_genesys_write_pnm_file(title, data.data(), depth, channels, pixels, lines);
- }
-
- /* loop until strip is found or maximum pass number done */
- found = 0;
- while (pass < 20 && !found)
- {
- status =
- dev->model->cmd_set->bulk_write_register(dev, local_reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: Failed to bulk write registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* now start scan */
- status = gl847_begin_scan(dev, sensor, &local_reg, SANE_TRUE);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to begin scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- /* waits for valid data */
- do
- sanei_genesys_test_buffer_empty (dev, &steps);
- while (steps);
-
- /* now we're on target, we can read data */
- status = sanei_genesys_read_data_from_scanner(dev, data.data(), size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read data: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- status = gl847_stop_action (dev);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: gl847_stop_action failed\n", __func__);
- return status;
- }
-
- if (DBG_LEVEL >= DBG_data)
- {
- sprintf(title, "gl847_search_strip_%s_%s%02d.pnm",
- black ? "black" : "white", forward ? "fwd" : "bwd", (int)pass);
- sanei_genesys_write_pnm_file(title, data.data(), depth, channels, pixels, lines);
- }
-
- /* search data to find black strip */
- /* when searching forward, we only need one line of the searched color since we
- * will scan forward. But when doing backward search, we need all the area of the
- * same color */
- if (forward)
- {
- for (y = 0; y < lines && !found; y++)
- {
- count = 0;
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > 90)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < 60)
- {
- count++;
- }
- }
-
- /* at end of line, if count >= 3%, line is not fully of the desired color
- * so we must go to next line of the buffer */
- /* count*100/pixels < 3 */
- if ((count * 100) / pixels < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__,
- pass, y);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- }
- else /* since calibration scans are done forward, we need the whole area
- to be of the required color when searching backward */
- {
- count = 0;
- for (y = 0; y < lines; y++)
- {
- /* count of white/black pixels depending on the color searched */
- for (x = 0; x < pixels; x++)
- {
- /* when searching for black, detect white pixels */
- if (black && data[y * pixels + x] > 90)
- {
- count++;
- }
- /* when searching for white, detect black pixels */
- if (!black && data[y * pixels + x] < 60)
- {
- count++;
- }
- }
- }
-
- /* at end of area, if count >= 3%, area is not fully of the desired color
- * so we must go to next buffer */
- if ((count * 100) / (pixels * lines) < 3)
- {
- found = 1;
- DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
- }
- else
- {
- DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count,
- (100 * count) / pixels);
- }
- }
- pass++;
- }
-
- if (found)
- {
- status = SANE_STATUS_GOOD;
- DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
- }
- else
- {
- status = SANE_STATUS_UNSUPPORTED;
- DBG(DBG_info, "%s: %s strip not found\n", __func__, black ? "black" : "white");
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**
- * average dark pixels of a 8 bits scan
- */
-static int
-dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
- unsigned int channels, unsigned int black)
-{
- unsigned int i, j, k, average, count;
- unsigned int avg[3];
- uint8_t val;
-
- /* computes average value on black margin */
- for (k = 0; k < channels; k++)
- {
- avg[k] = 0;
- count = 0;
- for (i = 0; i < lines; i++)
- {
- for (j = 0; j < black; j++)
- {
- val = data[i * channels * pixels + j + k];
- avg[k] += val;
- count++;
- }
- }
- if (count)
- avg[k] /= count;
- DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
- }
- average = 0;
- for (i = 0; i < channels; i++)
- average += avg[i];
- average /= channels;
- DBG(DBG_info, "%s: average = %d\n", __func__, average);
- return average;
-}
-
-static SANE_Status
-gl847_offset_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t reg04;
- unsigned int channels, bpp;
- int pass = 0, avg, total_size;
- int topavg, bottomavg, resolution, lines;
- int top, bottom, black_pixels, pixels;
-
- DBGSTART;
-
- /* no gain nor offset for AKM AFE */
- RIE (sanei_genesys_read_register (dev, REG04, &reg04));
- if ((reg04 & REG04_FESET) == 0x02)
- {
- DBGCOMPLETED;
- return status;
- }
-
- /* offset calibration is always done in color mode */
- channels = 3;
- resolution=sensor.optical_res;
- dev->calib_pixels = sensor.sensor_pixels;
- lines=1;
- bpp=8;
- pixels= (sensor.sensor_pixels*resolution) / sensor.optical_res;
- black_pixels = (sensor.black_pixels * resolution) / sensor.optical_res;
- DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = bpp;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- status = gl847_init_scan_regs(dev, sensor, &regs, params);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
- sanei_genesys_set_motor_power(regs, false);
-
- /* allocate memory for scans */
- total_size = pixels * channels * lines * (bpp/8); /* colors * bytes_per_color * scan lines */
-
- std::vector<uint8_t> first_line(total_size);
- std::vector<uint8_t> second_line(total_size);
-
- /* init gain */
- dev->frontend.set_gain(0, 0);
- dev->frontend.set_gain(1, 0);
- dev->frontend.set_gain(2, 0);
-
- /* scan with no move */
- bottom = 10;
- dev->frontend.set_offset(0, bottom);
- dev->frontend.set_offset(1, bottom);
- dev->frontend.set_offset(2, bottom);
-
- RIE(gl847_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting first line reading\n", __func__);
- RIE(gl847_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size));
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl847_offset%03d.pnm", bottom);
- sanei_genesys_write_pnm_file(fn, first_line.data(), bpp, channels, pixels, lines);
- }
-
- bottomavg = dark_average (first_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
-
- /* now top value */
- top = 255;
- dev->frontend.set_offset(0, top);
- dev->frontend.set_offset(1, top);
- dev->frontend.set_offset(2, top);
- RIE(gl847_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl847_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, second_line.data(), total_size));
-
- topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
-
- /* loop until acceptable level */
- while ((pass < 32) && (top - bottom > 1))
- {
- pass++;
-
- /* settings for new scan */
- dev->frontend.set_offset(0, (top + bottom) / 2);
- dev->frontend.set_offset(1, (top + bottom) / 2);
- dev->frontend.set_offset(2, (top + bottom) / 2);
-
- /* scan with no move */
- RIE(gl847_set_fe(dev, sensor, AFE_SET));
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
- DBG(DBG_info, "%s: starting second line reading\n", __func__);
- RIE(gl847_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner (dev, second_line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data)
- {
- char fn[30];
- snprintf(fn, 30, "gl847_offset%03d.pnm", dev->frontend.get_offset(1));
- sanei_genesys_write_pnm_file(fn, second_line.data(), bpp, channels, pixels, lines);
- }
-
- avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
- DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
-
- /* compute new boundaries */
- if (topavg == avg)
- {
- topavg = avg;
- top = dev->frontend.get_offset(1);
- }
- else
- {
- bottomavg = avg;
- bottom = dev->frontend.get_offset(1);
- }
- }
- DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
-
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-static SANE_Status
-gl847_coarse_gain_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi)
-{
- int pixels;
- int total_size;
- uint8_t reg04;
- int i, j, channels;
- SANE_Status status = SANE_STATUS_GOOD;
- int max[3];
- float gain[3],coeff;
- int val, code, lines;
- int resolution;
- int bpp;
-
- DBG(DBG_proc, "%s: dpi = %d\n", __func__, dpi);
-
- /* no gain nor offset for AKM AFE */
- RIE (sanei_genesys_read_register (dev, REG04, &reg04));
- if ((reg04 & REG04_FESET) == 0x02)
- {
- DBGCOMPLETED;
- return status;
- }
-
- /* coarse gain calibration is always done in color mode */
- channels = 3;
-
- /* follow CKSEL */
- if(dev->settings.xres<sensor.optical_res)
- {
- coeff=0.9;
- /*resolution=sensor.optical_res/2; */
- resolution=sensor.optical_res;
- }
- else
- {
- resolution=sensor.optical_res;
- coeff=1.0;
- }
- lines=10;
- bpp=8;
- pixels = (sensor.sensor_pixels * resolution) / sensor.optical_res;
-
- SetupParams params;
- params.xres = resolution;
- params.yres = resolution;
- params.startx = 0;
- params.starty = 0;
- params.pixels = pixels;
- params.lines = lines;
- params.depth = bpp;
- params.channels = channels;
- params.scan_method = dev->settings.scan_method;
- params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- params.color_filter = dev->settings.color_filter;
- params.flags = SCAN_FLAG_DISABLE_SHADING |
- SCAN_FLAG_DISABLE_GAMMA |
- SCAN_FLAG_SINGLE_LINE |
- SCAN_FLAG_IGNORE_LINE_DISTANCE;
-
- try {
- status = gl847_init_scan_regs(dev, sensor, &regs, params);
- } catch (...) {
- try {
- sanei_genesys_set_motor_power(regs, false);
- } catch (...) {}
- throw;
- }
-
- sanei_genesys_set_motor_power(regs, false);
-
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to setup scan: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- RIE(dev->model->cmd_set->bulk_write_register(dev, regs));
-
- total_size = pixels * channels * (16/bpp) * lines;
-
- std::vector<uint8_t> line(total_size);
-
- RIE(gl847_set_fe(dev, sensor, AFE_SET));
- RIE(gl847_begin_scan(dev, sensor, &regs, SANE_TRUE));
- RIE(sanei_genesys_read_data_from_scanner(dev, line.data(), total_size));
-
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl847_gain.pnm", line.data(), bpp, channels, pixels, lines);
-
- /* average value on each channel */
- for (j = 0; j < channels; j++)
- {
- max[j] = 0;
- for (i = pixels/4; i < (pixels*3/4); i++)
- {
- if(bpp==16)
- {
- if (dev->model->is_cis)
- val =
- line[i * 2 + j * 2 * pixels + 1] * 256 +
- line[i * 2 + j * 2 * pixels];
- else
- val =
- line[i * 2 * channels + 2 * j + 1] * 256 +
- line[i * 2 * channels + 2 * j];
- }
- else
- {
- if (dev->model->is_cis)
- val = line[i + j * pixels];
- else
- val = line[i * channels + j];
- }
-
- max[j] += val;
- }
- max[j] = max[j] / (pixels/2);
-
- gain[j] = ((float) sensor.gain_white_ref*coeff) / max[j];
-
- /* turn logical gain value into gain code, checking for overflow */
- code = 283 - 208 / gain[j];
- if (code > 255)
- code = 255;
- else if (code < 0)
- code = 0;
- dev->frontend.set_gain(j, code);
-
- DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j],
- dev->frontend.get_gain(j));
- }
-
- if (dev->model->is_cis) {
- uint8_t gain0 = dev->frontend.get_gain(0);
- if (gain0 > dev->frontend.get_gain(1)) {
- gain0 = dev->frontend.get_gain(1);
- }
- if (gain0 > dev->frontend.get_gain(2)) {
- gain0 = dev->frontend.get_gain(2);
- }
- dev->frontend.set_gain(0, gain0);
- dev->frontend.set_gain(1, gain0);
- dev->frontend.set_gain(2, gain0);
- }
-
- if (channels == 1) {
- dev->frontend.set_gain(0, dev->frontend.get_gain(1));
- dev->frontend.set_gain(2, dev->frontend.get_gain(1));
- }
-
- RIE (gl847_stop_action (dev));
-
- status=gl847_slow_back_home (dev, SANE_TRUE);
-
- DBGCOMPLETED;
- return status;
-}
-
-
-/** the gl847 command set */
-static Genesys_Command_Set gl847_cmd_set = {
- "gl847-generic", /* the name of this set */
-
- nullptr,
-
- gl847_init,
- NULL, /*gl847_init_regs_for_warmup*/
- gl847_init_regs_for_coarse_calibration,
- gl847_init_regs_for_shading,
- gl847_init_regs_for_scan,
-
- gl847_get_filter_bit,
- gl847_get_lineart_bit,
- gl847_get_bitset_bit,
- gl847_get_gain4_bit,
- gl847_get_fast_feed_bit,
- gl847_test_buffer_empty_bit,
- gl847_test_motor_flag_bit,
-
- gl847_set_fe,
- gl847_set_powersaving,
- gl847_save_power,
-
- gl847_begin_scan,
- gl847_end_scan,
-
- sanei_genesys_send_gamma_table,
-
- gl847_search_start_position,
-
- gl847_offset_calibration,
- gl847_coarse_gain_calibration,
- gl847_led_calibration,
-
- NULL,
- gl847_slow_back_home,
- NULL, /* disable gl847_rewind, see #7 */
-
- sanei_genesys_bulk_write_register,
- NULL,
- sanei_genesys_bulk_read_data,
-
- gl847_update_hardware_sensors,
-
- NULL, /* no known gl847 sheetfed scanner */
- NULL, /* no known gl847 sheetfed scanner */
- NULL, /* no known gl847 sheetfed scanner */
- gl847_search_strip,
-
- sanei_genesys_is_compatible_calibration,
- NULL,
- gl847_send_shading_data,
- gl847_calculate_current_setup,
- gl847_boot
-};
-
-SANE_Status
-sanei_gl847_init_cmd_set (Genesys_Device * dev)
-{
- dev->model->cmd_set = &gl847_cmd_set;
- return SANE_STATUS_GOOD;
-}
diff --git a/backend/genesys_gl847.h b/backend/genesys_gl847.h
deleted file mode 100644
index 7af9c36..0000000
--- a/backend/genesys_gl847.h
+++ /dev/null
@@ -1,510 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#include "genesys.h"
-
-#define REG01 0x01
-#define REG01_CISSET 0x80
-#define REG01_DOGENB 0x40
-#define REG01_DVDSET 0x20
-#define REG01_STAGGER 0x10
-#define REG01_COMPENB 0x08
-#define REG01_TRUEGRAY 0x04
-#define REG01_SHDAREA 0x02
-#define REG01_SCAN 0x01
-
-#define REG02 0x02
-#define REG02_NOTHOME 0x80
-#define REG02_ACDCDIS 0x40
-#define REG02_AGOHOME 0x20
-#define REG02_MTRPWR 0x10
-#define REG02_FASTFED 0x08
-#define REG02_MTRREV 0x04
-#define REG02_HOMENEG 0x02
-#define REG02_LONGCURV 0x01
-
-#define REG03 0x03
-#define REG03_LAMPDOG 0x80
-#define REG03_AVEENB 0x40
-#define REG03_XPASEL 0x20
-#define REG03_LAMPPWR 0x10
-#define REG03_LAMPTIM 0x0f
-
-#define REG04 0x04
-#define REG04_LINEART 0x80
-#define REG04_BITSET 0x40
-#define REG04_AFEMOD 0x30
-#define REG04_FILTER 0x0c
-#define REG04_FESET 0x03
-
-#define REG04S_AFEMOD 4
-
-#define REG05 0x05
-#define REG05_DPIHW 0xc0
-#define REG05_DPIHW_600 0x00
-#define REG05_DPIHW_1200 0x40
-#define REG05_DPIHW_2400 0x80
-#define REG05_DPIHW_4800 0xc0
-#define REG05_MTLLAMP 0x30
-#define REG05_GMMENB 0x08
-#define REG05_MTLBASE 0x03
-
-#define REG06_SCANMOD 0xe0
-#define REG06S_SCANMOD 5
-#define REG06_PWRBIT 0x10
-#define REG06_GAIN4 0x08
-#define REG06_OPTEST 0x07
-
-#define REG07_LAMPSIM 0x80
-
-#define REG08_DRAM2X 0x80
-#define REG08_MPENB 0x20
-#define REG08_CIS_LINE 0x10
-#define REG08_IR1ENB 0x08
-#define REG08_IR2ENB 0x04
-#define REG08_ENB24M 0x01
-
-#define REG09_MCNTSET 0xc0
-#define REG09_EVEN1ST 0x20
-#define REG09_BLINE1ST 0x10
-#define REG09_BACKSCAN 0x08
-#define REG09_ENHANCE 0x04
-#define REG09_SHORTTG 0x02
-#define REG09_NWAIT 0x01
-
-#define REG09S_MCNTSET 6
-#define REG09S_CLKSET 4
-
-
-#define REG0A_LPWMEN 0x10
-
-#define REG0B 0x0b
-#define REG0B_DRAMSEL 0x07
-#define REG0B_ENBDRAM 0x08
-#define REG0B_ENBDRAM 0x08
-#define REG0B_RFHDIS 0x10
-#define REG0B_CLKSET 0xe0
-#define REG0B_24MHZ 0x00
-#define REG0B_30MHZ 0x20
-#define REG0B_40MHZ 0x40
-#define REG0B_48MHZ 0x60
-#define REG0B_60MHZ 0x80
-
-#define REG0C 0x0c
-#define REG0C_CCDLMT 0x0f
-
-#define REG0D 0x0d
-#define REG0D_FULLSTP 0x10
-#define REG0D_SEND 0x80
-#define REG0D_CLRMCNT 0x04
-#define REG0D_CLRDOCJM 0x02
-#define REG0D_CLRLNCNT 0x01
-
-#define REG0F 0x0f
-
-#define REG16_CTRLHI 0x80
-#define REG16_TOSHIBA 0x40
-#define REG16_TGINV 0x20
-#define REG16_CK1INV 0x10
-#define REG16_CK2INV 0x08
-#define REG16_CTRLINV 0x04
-#define REG16_CKDIS 0x02
-#define REG16_CTRLDIS 0x01
-
-#define REG17_TGMODE 0xc0
-#define REG17_TGMODE_NO_DUMMY 0x00
-#define REG17_TGMODE_REF 0x40
-#define REG17_TGMODE_XPA 0x80
-#define REG17_TGW 0x3f
-#define REG17S_TGW 0
-
-#define REG18 0x18
-#define REG18_CNSET 0x80
-#define REG18_DCKSEL 0x60
-#define REG18_CKTOGGLE 0x10
-#define REG18_CKDELAY 0x0c
-#define REG18_CKSEL 0x03
-
-#define REG1A_SW2SET 0x80
-#define REG1A_SW1SET 0x40
-#define REG1A_MANUAL3 0x02
-#define REG1A_MANUAL1 0x01
-#define REG1A_CK4INV 0x08
-#define REG1A_CK3INV 0x04
-#define REG1A_LINECLP 0x02
-
-#define REG1C 0x1c
-#define REG1C_TGTIME 0x07
-
-#define REG1D_CK4LOW 0x80
-#define REG1D_CK3LOW 0x40
-#define REG1D_CK1LOW 0x20
-#define REG1D_TGSHLD 0x1f
-#define REG1DS_TGSHLD 0
-
-
-#define REG1E_WDTIME 0xf0
-#define REG1ES_WDTIME 4
-#define REG1E_LINESEL 0x0f
-#define REG1ES_LINESEL 0
-
-#define REG_FEDCNT 0x1f
-
-#define REG24 0x1c
-#define REG40 0x40
-#define REG40_CHKVER 0x10
-#define REG40_HISPDFLG 0x04
-#define REG40_MOTMFLG 0x02
-#define REG40_DATAENB 0x01
-
-#define REG41_PWRBIT 0x80
-#define REG41_BUFEMPTY 0x40
-#define REG41_FEEDFSH 0x20
-#define REG41_SCANFSH 0x10
-#define REG41_HOMESNR 0x08
-#define REG41_LAMPSTS 0x04
-#define REG41_FEBUSY 0x02
-#define REG41_MOTORENB 0x01
-
-#define REG58_VSMP 0xf8
-#define REG58S_VSMP 3
-#define REG58_VSMPW 0x07
-#define REG58S_VSMPW 0
-
-#define REG59_BSMP 0xf8
-#define REG59S_BSMP 3
-#define REG59_BSMPW 0x07
-#define REG59S_BSMPW 0
-
-#define REG5A_ADCLKINV 0x80
-#define REG5A_RLCSEL 0x40
-#define REG5A_CDSREF 0x30
-#define REG5AS_CDSREF 4
-#define REG5A_RLC 0x0f
-#define REG5AS_RLC 0
-
-#define REG5E_DECSEL 0xe0
-#define REG5ES_DECSEL 5
-#define REG5E_STOPTIM 0x1f
-#define REG5ES_STOPTIM 0
-
-#define REG60 0x60
-#define REG60_Z1MOD 0x1f
-#define REG61 0x61
-#define REG61_Z1MOD 0xff
-#define REG62 0x62
-#define REG62_Z1MOD 0xff
-
-#define REG63 0x63
-#define REG63_Z2MOD 0x1f
-#define REG64 0x64
-#define REG64_Z2MOD 0xff
-#define REG65 0x65
-#define REG65_Z2MOD 0xff
-
-#define REG60S_STEPSEL 5
-#define REG60_STEPSEL 0xe0
-#define REG60_FULLSTEP 0x00
-#define REG60_HALFSTEP 0x20
-#define REG60_EIGHTHSTEP 0x60
-#define REG60_16THSTEP 0x80
-
-#define REG63S_FSTPSEL 5
-#define REG63_FSTPSEL 0xe0
-#define REG63_FULLSTEP 0x00
-#define REG63_HALFSTEP 0x20
-#define REG63_EIGHTHSTEP 0x60
-#define REG63_16THSTEP 0x80
-
-#define REG67 0x67
-#define REG67_MTRPWM 0x80
-
-#define REG68 0x68
-#define REG68_FASTPWM 0x80
-
-#define REG6B 0x6b
-#define REG6B_MULTFILM 0x80
-#define REG6B_GPOM13 0x40
-#define REG6B_GPOM12 0x20
-#define REG6B_GPOM11 0x10
-#define REG6B_GPO18 0x02
-#define REG6B_GPO17 0x01
-
-#define REG6C 0x6c
-#define REG6C_GPIO16 0x80
-#define REG6C_GPIO15 0x40
-#define REG6C_GPIO14 0x20
-#define REG6C_GPIO13 0x10
-#define REG6C_GPIO12 0x08
-#define REG6C_GPIO11 0x04
-#define REG6C_GPIO10 0x02
-#define REG6C_GPIO9 0x01
-#define REG6C_GPIOH 0xff
-#define REG6C_GPIOL 0xff
-
-#define REG6D 0x6d
-#define REG6E 0x6e
-#define REG6F 0x6f
-#define REG7E 0x7e
-
-#define REG87_LEDADD 0x04
-
-#define REG9E 0x9e
-#define REG9F 0x9f
-
-#define REGA6 0xa6
-#define REGA7 0xa7
-#define REGA8 0xa8
-#define REGA9 0xa9
-#define REGAB 0xab
-
-#define REG_EXPR 0x10
-#define REG_EXPG 0x12
-#define REG_EXPB 0x14
-#define REG_EXPDMY 0x19
-#define REG_STEPNO 0x21
-#define REG_FWDSTEP 0x22
-#define REG_BWDSTEP 0x23
-#define REG_FASTNO 0x24
-#define REG_DPISET 0x2c
-#define REG_STRPIXEL 0x30
-#define REG_ENDPIXEL 0x32
-#define REG_LINCNT 0x25
-#define REG_MAXWD 0x35
-#define REG_LPERIOD 0x38
-#define REG_FEEDL 0x3d
-#define REG_FMOVDEC 0x5f
-#define REG_FSHDEC 0x69
-#define REG_FMOVNO 0x6a
-#define REG_CK1MAP 0x74
-#define REG_CK3MAP 0x77
-#define REG_CK4MAP 0x7a
-
-#define SETREG(adr,val) { dev->reg.init_reg(adr, val); }
-
-/** set up registers for an actual scan
- *
- * this function sets up the scanner to scan in normal or single line mode
- */
-static SANE_Status gl847_init_scan_regs(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg, SetupParams& params);
-
-/* Send the low-level scan command */
-static SANE_Status gl847_begin_scan (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set * reg, SANE_Bool start_motor);
-
-/* Send the stop scan command */
-static SANE_Status gl847_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg, SANE_Bool check_stop);
-
-static SANE_Status gl847_init (Genesys_Device * dev);
-
-/** @brief moves the slider to steps at motor base dpi
- * @param dev device to work on
- * @param steps number of steps to move
- * */
-static SANE_Status
-gl847_feed (Genesys_Device * dev, unsigned int steps);
-
-typedef struct
-{
- uint8_t sensor_id;
- uint8_t r6b;
- uint8_t r6c;
- uint8_t r6d;
- uint8_t r6e;
- uint8_t r6f;
- uint8_t ra6;
- uint8_t ra7;
- uint8_t ra8;
- uint8_t ra9;
-} Gpio_Profile;
-
-static Gpio_Profile gpios[]={
- { GPO_CANONLIDE200, 0x02, 0xf9, 0x20, 0xff, 0x00, 0x04, 0x04, 0x00, 0x00},
- { GPO_CANONLIDE700, 0x06, 0xdb, 0xff, 0xff, 0x80, 0x15, 0x07, 0x20, 0x10},
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-};
-
-typedef struct
-{
- uint8_t dramsel;
- uint8_t rd0;
- uint8_t rd1;
- uint8_t rd2;
- uint8_t re0;
- uint8_t re1;
- uint8_t re2;
- uint8_t re3;
- uint8_t re4;
- uint8_t re5;
- uint8_t re6;
- uint8_t re7;
-} Memory_layout;
-
-static Memory_layout layouts[]={
- /* LIDE 100 */
- {
- 0x29,
- 0x0a, 0x15, 0x20,
- 0x00, 0xac, 0x02, 0x55, 0x02, 0x56, 0x03, 0xff
- },
- /* LIDE 200 */
- {
- 0x29,
- 0x0a, 0x1f, 0x34,
- 0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
- },
- /* 5600F */
- {
- 0x29,
- 0x0a, 0x1f, 0x34,
- 0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
- },
- /* LIDE 700F */
- {
- 0x2a,
- 0x0a, 0x33, 0x5c,
- 0x02, 0x14, 0x09, 0x09, 0x09, 0x0a, 0x0f, 0xff
- }
-};
-
-/** @brief structure for sensor settings
- * this structure describes the sensor settings to use for a given
- * exposure.
- */
-typedef struct {
- int sensor_type; /**> sensor id */
- int dpi; /**> maximum dpi for which data are valid */
- int exposure; /**> exposure */
- int ck1map; /**> CK1MAP */
- int ck3map; /**> CK3MAP */
- int ck4map; /**> CK4MAP */
- int segcnt; /**> SEGCNT */
- int expdummy; /**> exposure dummy */
- int expr; /**> initial red exposure */
- int expg; /**> initial green exposure */
- int expb; /**> initial blue exposure */
- size_t *order; /**> order of sub-segments */
- uint8_t r17; /**> TG width */
-} Sensor_Profile;
-
-static size_t order_01[]={0,1};
-static size_t order_0213[]={0,2,1,3};
-static size_t order_0246[]={0,2,4,6,1,3,5,7};
-
-static size_t new_order[]={0,1,2,3};
-static size_t order_0145[]={0,1,4,5,2,3,6,7};
-
-/**
- * database of sensor profiles
- */
-static Sensor_Profile sensors[]={
- {CIS_CANONLIDE100, 200, 2848, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
- {CIS_CANONLIDE100, 300, 1424, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
- {CIS_CANONLIDE100, 600, 1432, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
- {CIS_CANONLIDE100, 1200, 2712, 60, 159, 85, 5136, 255, 746, 478, 353, order_01 , 0x08},
- {CIS_CANONLIDE100, 2400, 5280, 60, 159, 85, 5136, 255, 1417, 909, 643, order_0213, 0x06},
- /*
- {CIS_CANONLIDE200, 150, 2848, 240, 636, 340, 5144, 0, 255, 637, 637, 637},
- {CIS_CANONLIDE200, 300, 1424, 240, 636, 340, 5144, 0, 255, 637, 637, 637},
- */
- {CIS_CANONLIDE200, 200, 2848, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
- {CIS_CANONLIDE200, 300, 1424, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
- {CIS_CANONLIDE200, 600, 1432, 60, 159, 85, 5136, 255, 410, 275, 203, NULL , 0x0a},
- {CIS_CANONLIDE200, 1200, 2712, 60, 159, 85, 5136, 255, 746, 478, 353, order_01 , 0x08},
- {CIS_CANONLIDE200, 2400, 5280, 60, 159, 85, 5136, 255, 1417, 909, 643, order_0213, 0x06},
- {CIS_CANONLIDE200, 4800, 10416, 60, 159, 85, 5136, 255, 2692, 1728, 1221, order_0246, 0x04},
-
- /* LiDE 700F */
- {CIS_CANONLIDE700, 150, 2848, 135, 249, 85, 5187, 255, 465, 310, 239, NULL , 0x0c},
- {CIS_CANONLIDE700, 300, 1424, 135, 249, 85, 5187, 255, 465, 310, 239, NULL , 0x0c},
- {CIS_CANONLIDE700, 600, 1504, 135, 249, 85, 5187, 255, 465, 310, 239, NULL , 0x0c},
- {CIS_CANONLIDE700, 1200, 2696, 135, 249, 85, 5187, 255, 1464, 844, 555, order_01 , 0x0a},
- {CIS_CANONLIDE700, 2400, 10576, 135, 249, 85, 5187, 255, 2798, 1558, 972, new_order , 0x08},
- {CIS_CANONLIDE700, 4800, 10576, 135, 249, 85, 5187, 255, 2798, 1558, 972, order_0145, 0x06},
-};
-
-/* base motor sopes in full step unit */
-/* target=((exposure * dpi) / base_dpi)>>step_type; */
-static uint32_t lide200_base[] = { 46876, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2343, 2336, 2329, 2322, 2314, 2307, 2300,2292,2285,2278,2271,2263,2256,2249,2241,2234,2227,2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
-static uint32_t lide200_medium[] = { 46876, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136, 8136,2343, 2336, 2329, 2322, 2314, 2307, 2300,2292,2285,2278,2271,2263,2256,2249,2241,2234,2227,2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
-static uint32_t lide200_high[] = { 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 31680, 2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
-static uint32_t lide700_medium[] = { 46876,2342,2342,2342,2342,2342,2342,2342,2342,2302,2286,2274,2266,2258,2252,2244,2240,2234,2228,2224,2218,2216,2210,2208,2202,2200,2194,2192,2190,2186,2182,2180,2176,2174,2172,2170,2166,2162,2160,2156,2154,2152,2150,2150,2146,2144,2142,2140,2136,2134,2132,2130,2130,2128,2124,2122,2120,2120,2118,2116,2112,2112,2110,2108,2106,2106,2104,2102,2102,2098,2096,2094,2094,2092,2090,2090,2086,2084,2084,2082,2082,2080,2078,2078,2076,2074,2074,2070,2070,2068,2066,2066,2064,2064,2062,2062,2060,2058,2058,2054,2054,2052,2052,2050,2050,2048,2048,2046,2046,2044,2042,2042,2040,2040,2038,2038,2034,2034,2032,2032,2030,2030,2028,2028,2026,2026,2022,2022};
-static uint32_t lide700_high[] = { 46876,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864,15864};
-/* 5190 trop
- * 5186 pas assez
- */
-/*
-static uint32_t lide200_max[] = { 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 124992, 2219,2212,2205,2198,2190,2183,2176,2168,2161,2154,2146,2139,2132,2125,2117,2110,2103,2095,2088,2081,2073,2066,2059,2052,2044,2037,2030,2022,2015,2008,2001,1993,1986,1979,1971,1964,1957,1949,1942,1935,1928,1920,1913,1906,1898,1891,1884,1876,1869,1862,1855,1847,1840,1833,1825,1818,1811,1803,1796,1789,1782,1774,1767,1760,1752,1745,1738,1731,1723,1716,1709,1701,1694,1687,1679,1672,1665,1658,1650,1643,1636,1628,1621,1614,1606,1599,1592,1585,1577,1570,1563,1555,1548,1541,1533,1526,1519,1512,1504,1497,1490,1482,1475,1468,1461,1453,1446,1439,1431,1424,1417,1409,1402,1395,1388,1380,1373,1366,1358,1351,1344,1336,1329,1322,1315,1307,1300,1293,1285,1278,1271,1263,1256,1249,1242,1234,1227,1220,1212,1205,1198,1191,1183,1176,1169,1161,1154,1147,1139,1132,1125,1118,1110,1103,1096,1088,1081,1074,1066,1059,1052,1045,1037,1030,1023,1015,1008,1001,993,986,979,972,964,957,950,942,935,928,921,913,906,899,891,884,877,869,862,855,848,840,833,826,818,811,804,796,789,782,775,767,760,753,745,738,731,723,716,709,702,694,687,680,672,665,658,651,643,636,629,621,614,607,599,592,585,578,570,563,556,534,534, 0};
-*/
-
-/**
- * database of motor profiles
- */
-
-static Motor_Profile gl847_motors[]={
- /* LiDE 100 */
- {MOTOR_CANONLIDE100, 2848, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE100, 1424, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE100, 1432, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE100, 2712, QUARTER_STEP, lide200_medium},
- {MOTOR_CANONLIDE100, 5280, EIGHTH_STEP , lide200_high},
-
- /* LiDE 200 */
- {MOTOR_CANONLIDE200, 2848, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE200, 1424, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE200, 1432, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE200, 2712, QUARTER_STEP, lide200_medium},
- {MOTOR_CANONLIDE200, 5280, EIGHTH_STEP , lide200_high},
- {MOTOR_CANONLIDE200, 10416, EIGHTH_STEP , lide200_high},
-
- /* LiDE 700F */
- {MOTOR_CANONLIDE700, 2848, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE700, 1424, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE700, 1504, HALF_STEP , lide200_base},
- {MOTOR_CANONLIDE700, 2696, HALF_STEP , lide700_medium}, /* 2696 , 2838 */
- {MOTOR_CANONLIDE700, 10576, EIGHTH_STEP, lide700_high},
-
- /* end of database entry */
- {0, 0, 0, NULL},
-};
diff --git a/backend/genesys_low.cc b/backend/genesys_low.cc
deleted file mode 100644
index 097375f..0000000
--- a/backend/genesys_low.cc
+++ /dev/null
@@ -1,2059 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
-
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#define DEBUG_DECLARE_ONLY
-
-#include "genesys_low.h"
-#include "assert.h"
-
-#include <vector>
-
-
-Genesys_Device::~Genesys_Device()
-{
- clear();
-
- if (file_name != nullptr)
- free(file_name);
-}
-
-void Genesys_Device::clear()
-{
- read_buffer.clear();
- lines_buffer.clear();
- shrink_buffer.clear();
- out_buffer.clear();
- binarize_buffer.clear();
- local_buffer.clear();
-
- calib_file.clear();
-
- calibration_cache.clear();
-
- white_average_data.clear();
- dark_average_data.clear();
-}
-
-/* ------------------------------------------------------------------------ */
-/* functions calling ASIC specific functions */
-/* ------------------------------------------------------------------------ */
-
-/**
- * setup the hardware dependent functions
- */
-SANE_Status
-sanei_genesys_init_cmd_set (Genesys_Device * dev)
-{
- DBG_INIT ();
- switch (dev->model->asic_type)
- {
- case GENESYS_GL646:
- return sanei_gl646_init_cmd_set (dev);
- case GENESYS_GL841:
- return sanei_gl841_init_cmd_set (dev);
- case GENESYS_GL843:
- return sanei_gl843_init_cmd_set (dev);
- case GENESYS_GL845: /* since only a few reg bits differs
- we handle both together */
- case GENESYS_GL846:
- return sanei_gl846_init_cmd_set (dev);
- case GENESYS_GL847:
- return sanei_gl847_init_cmd_set (dev);
- case GENESYS_GL124:
- return sanei_gl124_init_cmd_set (dev);
- default:
- return SANE_STATUS_INVAL;
- }
-}
-
-/* ------------------------------------------------------------------------ */
-/* General IO and debugging functions */
-/* ------------------------------------------------------------------------ */
-
-SANE_Status sanei_genesys_write_file(const char *filename, uint8_t * data, size_t length)
-{
- FILE *out;
-
- out = fopen (filename, "w");
- if (!out) {
- DBG(DBG_error, "%s: could nor open %s for writing: %s\n", __func__, filename,
- strerror(errno));
- return SANE_STATUS_INVAL;
- }
- fwrite(data, 1, length, out);
- fclose(out);
-
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/* Write data to a pnm file (e.g. calibration). For debugging only */
-/* data is RGB or grey, with little endian byte order */
-SANE_Status
-sanei_genesys_write_pnm_file (const char *filename, uint8_t * data, int depth,
- int channels, int pixels_per_line, int lines)
-{
- FILE *out;
- int count;
-
- DBG(DBG_info, "%s: depth=%d, channels=%d, ppl=%d, lines=%d\n", __func__,depth, channels,
- pixels_per_line, lines);
-
- out = fopen (filename, "w");
- if (!out)
- {
- DBG(DBG_error, "%s: could nor open %s for writing: %s\n", __func__, filename,
- strerror(errno));
- return SANE_STATUS_INVAL;
- }
- if(depth==1)
- {
- fprintf (out, "P4\n%d\n%d\n", pixels_per_line, lines);
- }
- else
- {
- fprintf (out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6',
- pixels_per_line, lines, (int) pow (2, depth) - 1);
- }
- if (channels == 3)
- {
- for (count = 0; count < (pixels_per_line * lines * 3); count++)
- {
- if (depth == 16)
- fputc (*(data + 1), out);
- fputc (*(data++), out);
- if (depth == 16)
- data++;
- }
- }
- else
- {
- if (depth==1)
- {
- pixels_per_line/=8;
- }
- for (count = 0; count < (pixels_per_line * lines); count++)
- {
- switch (depth)
- {
- case 8:
- fputc (*(data + count), out);
- break;
- case 16:
- fputc (*(data + 1), out);
- fputc (*(data), out);
- data += 2;
- break;
- default:
- fputc(data[count], out);
- break;
- }
- }
- }
- fclose (out);
-
- DBG(DBG_proc, "%s: finished\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-/* ------------------------------------------------------------------------ */
-/* Read and write RAM, registers and AFE */
-/* ------------------------------------------------------------------------ */
-
-extern unsigned sanei_genesys_get_bulk_max_size(Genesys_Device * dev)
-{
- /* Genesys supports 0xFE00 maximum size in general, wheraus GL646 supports
- 0xFFC0. We use 0xF000 because that's the packet limit in the Linux usbmon
- USB capture stack. By default it limits packet size to b_size / 5 where
- b_size is the size of the ring buffer. By default it's 300*1024, so the
- packet is limited 61440 without any visibility to acquiring software.
- */
- if (dev->model->asic_type == GENESYS_GL124 ||
- dev->model->asic_type == GENESYS_GL846 ||
- dev->model->asic_type == GENESYS_GL847) {
- return 0xeff0;
- }
- return 0xf000;
-}
-
-void sanei_genesys_bulk_read_data_send_header(Genesys_Device* dev, size_t len)
-{
- DBG_HELPER(dbg);
-
- uint8_t outdata[8];
- if (dev->model->asic_type == GENESYS_GL124 ||
- dev->model->asic_type == GENESYS_GL846 ||
- dev->model->asic_type == GENESYS_GL847)
- {
- // hard coded 0x10000000 address
- outdata[0] = 0;
- outdata[1] = 0;
- outdata[2] = 0;
- outdata[3] = 0x10;
- } else if (dev->model->asic_type == GENESYS_GL841 ||
- dev->model->asic_type == GENESYS_GL843) {
- outdata[0] = BULK_IN;
- outdata[1] = BULK_RAM;
- outdata[2] = VALUE_BUFFER & 0xff;
- outdata[3] = (VALUE_BUFFER >> 8) & 0xff;
- } else {
- outdata[0] = BULK_IN;
- outdata[1] = BULK_RAM;
- outdata[2] = 0x00;
- outdata[3] = 0x00;
- }
-
- /* data size to transfer */
- outdata[4] = (len & 0xff);
- outdata[5] = ((len >> 8) & 0xff);
- outdata[6] = ((len >> 16) & 0xff);
- outdata[7] = ((len >> 24) & 0xff);
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00,
- sizeof(outdata), outdata);
-}
-
-SANE_Status sanei_genesys_bulk_read_data(Genesys_Device * dev, uint8_t addr, uint8_t* data,
- size_t len)
-{
- DBG_HELPER(dbg);
-
- // currently supported: GL646, GL841, GL843, GL846, GL847, GL124
- size_t size, target;
- uint8_t *buffer;
-
- unsigned is_addr_used = 1;
- unsigned has_header_before_each_chunk = 0;
- if (dev->model->asic_type == GENESYS_GL124 ||
- dev->model->asic_type == GENESYS_GL846 ||
- dev->model->asic_type == GENESYS_GL847)
- {
- is_addr_used = 0;
- has_header_before_each_chunk = 1;
- }
-
- if (is_addr_used) {
- DBG(DBG_io, "%s: requesting %lu bytes from 0x%02x addr\n", __func__, (u_long) len, addr);
- } else {
- DBG(DBG_io, "%s: requesting %lu bytes\n", __func__, (u_long) len);
- }
-
- if (len == 0)
- return SANE_STATUS_GOOD;
-
- if (is_addr_used) {
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, 0x00,
- 1, &addr);
- }
-
- target = len;
- buffer = data;
-
- size_t max_in_size = sanei_genesys_get_bulk_max_size(dev);
-
- if (!has_header_before_each_chunk) {
- sanei_genesys_bulk_read_data_send_header(dev, len);
- }
-
- // loop until computed data size is read
- while (target) {
- if (target > max_in_size) {
- size = max_in_size;
- } else {
- size = target;
- }
-
- if (has_header_before_each_chunk) {
- sanei_genesys_bulk_read_data_send_header(dev, size);
- }
-
- DBG(DBG_io2, "%s: trying to read %lu bytes of data\n", __func__, (u_long) size);
-
- dev->usb_dev.bulk_read(data, &size);
-
- DBG(DBG_io2, "%s: read %lu bytes, %lu remaining\n", __func__,
- (u_long) size, (u_long) (target - size));
-
- target -= size;
- data += size;
- }
-
- if (DBG_LEVEL >= DBG_data && dev->binary!=NULL) {
- fwrite(buffer, len, 1, dev->binary);
- }
-
- return SANE_STATUS_GOOD;
-}
-
-SANE_Status sanei_genesys_bulk_write_data(Genesys_Device * dev, uint8_t addr, uint8_t* data,
- size_t len)
-{
- DBG_HELPER(dbg);
-
- // supported: GL646, GL841, GL843
- size_t size;
- uint8_t outdata[8];
-
- DBG(DBG_io, "%s writing %lu bytes\n", __func__, (u_long) len);
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX,
- 1, &addr);
-
-
- size_t max_out_size = sanei_genesys_get_bulk_max_size(dev);
-
- while (len) {
- if (len > max_out_size)
- size = max_out_size;
- else
- size = len;
-
- if (dev->model->asic_type == GENESYS_GL841) {
- outdata[0] = BULK_OUT;
- outdata[1] = BULK_RAM;
- outdata[2] = VALUE_BUFFER & 0xff;
- outdata[3] = (VALUE_BUFFER >> 8) & 0xff;
- } else {
- outdata[0] = BULK_OUT;
- outdata[1] = BULK_RAM;
- outdata[2] = 0x00;
- outdata[3] = 0x00;
- }
-
- outdata[4] = (size & 0xff);
- outdata[5] = ((size >> 8) & 0xff);
- outdata[6] = ((size >> 16) & 0xff);
- outdata[7] = ((size >> 24) & 0xff);
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00,
- sizeof(outdata), outdata);
-
- dev->usb_dev.bulk_write(data, &size);
-
- DBG(DBG_io2, "%s: wrote %lu bytes, %lu remaining\n", __func__, (u_long) size,
- (u_long) (len - size));
-
- len -= size;
- data += size;
- }
-
- return SANE_STATUS_GOOD;
-}
-
-/** @brief write to one high (addr >= 0x100) register
- * write to a register which address is higher than 0xff.
- * @param dev opened device to write to
- * @param reg LSB of register address
- * @param val value to write
- */
-SANE_Status
-sanei_genesys_write_hregister (Genesys_Device * dev, uint16_t reg, uint8_t val)
-{
- DBG_HELPER(dbg);
-
- uint8_t buffer[2];
-
- buffer[0]=reg & 0xff;
- buffer[1]=val;
-
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, 0x100 | VALUE_SET_REGISTER, INDEX,
- 2, buffer);
-
- DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, reg, val);
-
- return SANE_STATUS_GOOD;
-}
-
-/** @brief read from one high (addr >= 0x100) register
- * Read to a register which address is higher than 0xff. Second byte is check to detect
- * physical link errors.
- * @param dev opened device to read from
- * @param reg LSB of register address
- * @param val value to write
- */
-SANE_Status
-sanei_genesys_read_hregister (Genesys_Device * dev, uint16_t reg, uint8_t * val)
-{
- DBG_HELPER(dbg);
-
- SANE_Byte value[2];
-
- dev->usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_BUFFER, 0x100 | VALUE_GET_REGISTER,
- 0x22+((reg & 0xff)<<8), 2, value);
-
- *val=value[0];
- DBG(DBG_io2, "%s(0x%02x)=0x%02x\n", __func__, reg, *val);
-
- /* check usb link status */
- if((value[1] & 0xff) != 0x55)
- {
- DBG(DBG_error,"%s: invalid read, scanner unplugged ?\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
- return SANE_STATUS_GOOD;
-}
-
-/**
- * Write to one GL847 ASIC register
-URB 10 control 0x40 0x04 0x83 0x00 len 2 wrote 0xa6 0x04
- */
-static SANE_Status
-sanei_genesys_write_gl847_register (Genesys_Device * dev, uint8_t reg, uint8_t val)
-{
- DBG_HELPER(dbg);
-
- uint8_t buffer[2];
-
- buffer[0]=reg;
- buffer[1]=val;
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_SET_REGISTER, INDEX,
- 2, buffer);
-
- DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, reg, val);
-
- return SANE_STATUS_GOOD;
-}
-
-/**
- * Write to one ASIC register
- */
-SANE_Status
-sanei_genesys_write_register (Genesys_Device * dev, uint16_t reg, uint8_t val)
-{
- DBG_HELPER(dbg);
-
- SANE_Byte reg8;
-
- /* 16 bit register address space */
- if(reg>255)
- {
- return sanei_genesys_write_hregister(dev, reg, val);
- }
-
- /* route to gl847 function if needed */
- if(dev->model->asic_type==GENESYS_GL847
- || dev->model->asic_type==GENESYS_GL845
- || dev->model->asic_type==GENESYS_GL846
- || dev->model->asic_type==GENESYS_GL124)
- {
- return sanei_genesys_write_gl847_register(dev, reg, val);
- }
-
- reg8=reg & 0xff;
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX,
- 1, &reg8);
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_WRITE_REGISTER, INDEX,
- 1, &val);
-
- DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, reg, val);
-
- return SANE_STATUS_GOOD;
-}
-
-/**
- * @brief write command to 0x8c endpoint
- * Write a value to 0x8c end point (end access), for USB firmware related operations
- * Known values are 0x0f, 0x11 for USB 2.0 data transfer and 0x0f,0x14 for USB1.1
- * @param dev device to write to
- * @param index index of the command
- * @param val value to write
- */
-SANE_Status
-sanei_genesys_write_0x8c(Genesys_Device * dev, uint8_t index, uint8_t val)
-{
- DBG_HELPER_ARGS(dbg, "0x%02x,0x%02x", index, val);
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_BUF_ENDACCESS, index,
- 1, &val);
- return SANE_STATUS_GOOD;
-}
-
-/* read reg 0x41:
- * URB 164 control 0xc0 0x04 0x8e 0x4122 len 2 read 0xfc 0x55
- */
-static SANE_Status
-sanei_genesys_read_gl847_register (Genesys_Device * dev, uint16_t reg, uint8_t * val)
-{
- DBG_HELPER(dbg);
- SANE_Status status = SANE_STATUS_GOOD;
- SANE_Byte value[2];
-
- dev->usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_BUFFER, VALUE_GET_REGISTER, 0x22+(reg<<8),
- 2, value);
-
- *val=value[0];
- DBG(DBG_io2, "%s(0x%02x)=0x%02x\n", __func__, reg, *val);
-
- /* check usb link status */
- if((value[1] & 0xff) != 0x55)
- {
- DBG(DBG_error,"%s: invalid read, scanner unplugged ?\n", __func__);
- status=SANE_STATUS_IO_ERROR;
- }
- return status;
-}
-
-/* Read from one register */
-SANE_Status
-sanei_genesys_read_register (Genesys_Device * dev, uint16_t reg, uint8_t * val)
-{
- DBG_HELPER(dbg);
-
- SANE_Byte reg8;
-
- /* 16 bit register address space */
- if(reg>255)
- {
- return sanei_genesys_read_hregister(dev, reg, val);
- }
-
- /* route to gl847 function if needed */
- if(dev->model->asic_type==GENESYS_GL847
- || dev->model->asic_type==GENESYS_GL845
- || dev->model->asic_type==GENESYS_GL846
- || dev->model->asic_type==GENESYS_GL124)
- return sanei_genesys_read_gl847_register(dev, reg, val);
-
- /* 8 bit register address space */
- reg8=(SANE_Byte)(reg& 0Xff);
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX,
- 1, &reg8);
-
- *val = 0;
-
- dev->usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_READ_REGISTER, INDEX,
- 1, val);
-
- DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, reg, *val);
-
- return SANE_STATUS_GOOD;
-}
-
-/* Set address for writing data */
-SANE_Status
-sanei_genesys_set_buffer_address (Genesys_Device * dev, uint32_t addr)
-{
- SANE_Status status = SANE_STATUS_GOOD;
-
- if(dev->model->asic_type==GENESYS_GL847
- || dev->model->asic_type==GENESYS_GL845
- || dev->model->asic_type==GENESYS_GL846
- || dev->model->asic_type==GENESYS_GL124)
- {
- DBG(DBG_warn, "%s: shouldn't be used for GL846+ ASICs\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- DBG(DBG_io, "%s: setting address to 0x%05x\n", __func__, addr & 0xfffffff0);
-
- addr = addr >> 4;
-
- status = sanei_genesys_write_register (dev, 0x2b, (addr & 0xff));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while writing low byte: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- addr = addr >> 8;
- status = sanei_genesys_write_register (dev, 0x2a, (addr & 0xff));
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while writing high byte: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_io, "%s: completed\n", __func__);
-
- return status;
-}
-
-/**@brief read data from analog frontend (AFE)
- * @param dev device owning the AFE
- * @param addr register address to read
- * @param data placeholder for the result
- * @return SANE_STATUS_GOOD is OK, else the error code
- */
-SANE_Status
-sanei_genesys_fe_read_data (Genesys_Device * dev, uint8_t addr,
- uint16_t *data)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t value;
- Genesys_Register_Set reg;
-
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- reg.init_reg(0x50, addr);
-
- /* set up read address */
- status = dev->model->cmd_set->bulk_write_register(dev, reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while bulk writing registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- /* read data */
- RIE (sanei_genesys_read_register (dev, 0x46, &value));
- *data=256*value;
- RIE (sanei_genesys_read_register (dev, 0x47, &value));
- *data+=value;
-
- DBG(DBG_io, "%s (0x%02x, 0x%04x)\n", __func__, addr, *data);
- DBG(DBG_proc, "%s: completed\n", __func__);
-
- return status;
-}
-
-/*@brief write data to analog frontend
- * writes data to analog frontend to set it up accordingly
- * to the sensor settings (exposure, timings, color, bit depth, ...)
- * @param dev devie owning the AFE to write to
- * @param addr AFE rister address
- * @param data value to write to AFE register
- **/
-SANE_Status
-sanei_genesys_fe_write_data (Genesys_Device * dev, uint8_t addr,
- uint16_t data)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- Genesys_Register_Set reg(Genesys_Register_Set::SEQUENTIAL);
-
- DBG(DBG_io, "%s (0x%02x, 0x%04x)\n", __func__, addr, data);
-
- reg.init_reg(0x51, addr);
- if (dev->model->asic_type == GENESYS_GL124) {
- reg.init_reg(0x5d, (data / 256) & 0xff);
- reg.init_reg(0x5e, data & 0xff);
- } else {
- reg.init_reg(0x3a, (data / 256) & 0xff);
- reg.init_reg(0x3b, data & 0xff);
- }
-
- status = dev->model->cmd_set->bulk_write_register(dev, reg);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed while bulk writing registers: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_io, "%s: completed\n", __func__);
-
- return status;
-}
-
-/* ------------------------------------------------------------------------ */
-/* Medium level functions */
-/* ------------------------------------------------------------------------ */
-
-/** read the status register
- */
-SANE_Status
-sanei_genesys_get_status (Genesys_Device * dev, uint8_t * status)
-{
- if(dev->model->asic_type==GENESYS_GL124)
- return sanei_genesys_read_hregister(dev, 0x101, status);
- return sanei_genesys_read_register (dev, 0x41, status);
-}
-
-/**
- * decodes and prints content of status register
- * @param val value read from status register
- */
-void sanei_genesys_print_status (uint8_t val)
-{
- char msg[80];
-
- sprintf (msg, "%s%s%s%s%s%s%s%s",
- val & PWRBIT ? "PWRBIT " : "",
- val & BUFEMPTY ? "BUFEMPTY " : "",
- val & FEEDFSH ? "FEEDFSH " : "",
- val & SCANFSH ? "SCANFSH " : "",
- val & HOMESNR ? "HOMESNR " : "",
- val & LAMPSTS ? "LAMPSTS " : "",
- val & FEBUSY ? "FEBUSY " : "",
- val & MOTORENB ? "MOTORENB" : "");
- DBG(DBG_info, "status=%s\n", msg);
-}
-
-#if 0
-/* returns pixels per line from register set */
-/*candidate for moving into chip specific files?*/
-static int
-genesys_pixels_per_line (Genesys_Register_Set * reg)
-{
- int pixels_per_line;
-
- pixels_per_line =
- sanei_genesys_read_reg_from_set (reg,
- 0x32) * 256 +
- sanei_genesys_read_reg_from_set (reg, 0x33);
- pixels_per_line -=
- (sanei_genesys_read_reg_from_set (reg, 0x30) * 256 +
- sanei_genesys_read_reg_from_set (reg, 0x31));
-
- return pixels_per_line;
-}
-
-/* returns dpiset from register set */
-/*candidate for moving into chip specific files?*/
-static int
-genesys_dpiset (Genesys_Register_Set * reg)
-{
- int dpiset;
-
- dpiset =
- sanei_genesys_read_reg_from_set (reg,
- 0x2c) * 256 +
- sanei_genesys_read_reg_from_set (reg, 0x2d);
-
- return dpiset;
-}
-#endif
-
-/** read the number of valid words in scanner's RAM
- * ie registers 42-43-44
- */
-/*candidate for moving into chip specific files?*/
-SANE_Status
-sanei_genesys_read_valid_words (Genesys_Device * dev, unsigned int *words)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t value;
-
- DBGSTART;
- switch (dev->model->asic_type)
- {
- case GENESYS_GL124:
- RIE (sanei_genesys_read_hregister (dev, 0x102, &value));
- *words = (value & 0x03);
- RIE (sanei_genesys_read_hregister (dev, 0x103, &value));
- *words = *words * 256 + value;
- RIE (sanei_genesys_read_hregister (dev, 0x104, &value));
- *words = *words * 256 + value;
- RIE (sanei_genesys_read_hregister (dev, 0x105, &value));
- *words = *words * 256 + value;
- break;
-
- case GENESYS_GL845:
- case GENESYS_GL846:
- RIE (sanei_genesys_read_register (dev, 0x42, &value));
- *words = (value & 0x02);
- RIE (sanei_genesys_read_register (dev, 0x43, &value));
- *words = *words * 256 + value;
- RIE (sanei_genesys_read_register (dev, 0x44, &value));
- *words = *words * 256 + value;
- RIE (sanei_genesys_read_register (dev, 0x45, &value));
- *words = *words * 256 + value;
- break;
-
- case GENESYS_GL847:
- RIE (sanei_genesys_read_register (dev, 0x42, &value));
- *words = (value & 0x03);
- RIE (sanei_genesys_read_register (dev, 0x43, &value));
- *words = *words * 256 + value;
- RIE (sanei_genesys_read_register (dev, 0x44, &value));
- *words = *words * 256 + value;
- RIE (sanei_genesys_read_register (dev, 0x45, &value));
- *words = *words * 256 + value;
- break;
-
- default:
- RIE (sanei_genesys_read_register (dev, 0x44, &value));
- *words = value;
- RIE (sanei_genesys_read_register (dev, 0x43, &value));
- *words += (value * 256);
- RIE (sanei_genesys_read_register (dev, 0x42, &value));
- if (dev->model->asic_type == GENESYS_GL646)
- *words += ((value & 0x03) * 256 * 256);
- else
- *words += ((value & 0x0f) * 256 * 256);
- }
-
- DBG(DBG_proc, "%s: %d words\n", __func__, *words);
- DBGCOMPLETED;
- return SANE_STATUS_GOOD;
-}
-
-/** read the number of lines scanned
- * ie registers 4b-4c-4d
- */
-SANE_Status
-sanei_genesys_read_scancnt (Genesys_Device * dev, unsigned int *words)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t value;
-
- DBG(DBG_proc, "%s: start\n", __func__);
-
- if (dev->model->asic_type == GENESYS_GL124)
- {
- RIE (sanei_genesys_read_hregister (dev, 0x10b, &value));
- *words = (value & 0x0f) << 16;
- RIE (sanei_genesys_read_hregister (dev, 0x10c, &value));
- *words += (value << 8);
- RIE (sanei_genesys_read_hregister (dev, 0x10d, &value));
- *words += value;
- }
- else
- {
- RIE (sanei_genesys_read_register (dev, 0x4d, &value));
- *words = value;
- RIE (sanei_genesys_read_register (dev, 0x4c, &value));
- *words += (value * 256);
- RIE (sanei_genesys_read_register (dev, 0x4b, &value));
- if (dev->model->asic_type == GENESYS_GL646)
- *words += ((value & 0x03) * 256 * 256);
- else
- *words += ((value & 0x0f) * 256 * 256);
- }
-
- DBG(DBG_proc, "%s: %d lines\n", __func__, *words);
- return SANE_STATUS_GOOD;
-}
-
-/** @brief Check if the scanner's internal data buffer is empty
- * @param *dev device to test for data
- * @param *empty return value
- * @return empty will be set to SANE_TRUE if there is no scanned data.
- **/
-SANE_Status
-sanei_genesys_test_buffer_empty (Genesys_Device * dev, SANE_Bool * empty)
-{
- uint8_t val = 0;
- SANE_Status status = SANE_STATUS_GOOD;
-
- sanei_genesys_sleep_ms(1);
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: failed to read buffer status: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- if (dev->model->cmd_set->test_buffer_empty_bit (val))
- {
- /* fix timing issue on USB3 (or just may be too fast) hardware
- * spotted by John S. Weber <jweber53@gmail.com>
- */
- sanei_genesys_sleep_ms(1);
- DBG(DBG_io2, "%s: buffer is empty\n", __func__);
- *empty = SANE_TRUE;
- return SANE_STATUS_GOOD;
- }
-
- *empty = SANE_FALSE;
-
- DBG(DBG_io, "%s: buffer is filled\n", __func__);
- return SANE_STATUS_GOOD;
-}
-
-
-/* Read data (e.g scanned image) from scan buffer */
-SANE_Status
-sanei_genesys_read_data_from_scanner (Genesys_Device * dev, uint8_t * data,
- size_t size)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- int time_count = 0;
- unsigned int words = 0;
-
- DBG(DBG_proc, "%s (size = %lu bytes)\n", __func__, (u_long) size);
-
- if (size & 1)
- DBG(DBG_info, "WARNING %s: odd number of bytes\n", __func__);
-
- /* wait until buffer not empty for up to 5 seconds */
- do
- {
- status = sanei_genesys_read_valid_words (dev, &words);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: checking for empty buffer failed: %s\n", __func__,
- sane_strstatus(status));
- return status;
- }
- if (words == 0)
- {
- sanei_genesys_sleep_ms(10);
- time_count++;
- }
- }
- while ((time_count < 2500*2) && (words == 0));
-
- if (words == 0) /* timeout, buffer does not get filled */
- {
- DBG(DBG_error, "%s: timeout, buffer does not get filled\n", __func__);
- return SANE_STATUS_IO_ERROR;
- }
-
- status = dev->model->cmd_set->bulk_read_data (dev, 0x45, data, size);
- if (status != SANE_STATUS_GOOD)
- {
- DBG(DBG_error, "%s: reading bulk data failed: %s\n", __func__, sane_strstatus(status));
- return status;
- }
-
- DBG(DBG_proc, "%s: completed\n", __func__);
- return SANE_STATUS_GOOD;
-}
-SANE_Status
-sanei_genesys_read_feed_steps (Genesys_Device * dev, unsigned int *steps)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t value;
-
- DBG(DBG_proc, "%s\n", __func__);
-
- if (dev->model->asic_type == GENESYS_GL124)
- {
- RIE (sanei_genesys_read_hregister (dev, 0x108, &value));
- *steps = (value & 0x1f) << 16;
- RIE (sanei_genesys_read_hregister (dev, 0x109, &value));
- *steps += (value << 8);
- RIE (sanei_genesys_read_hregister (dev, 0x10a, &value));
- *steps += value;
- }
- else
- {
- RIE (sanei_genesys_read_register (dev, 0x4a, &value));
- *steps = value;
- RIE (sanei_genesys_read_register (dev, 0x49, &value));
- *steps += (value * 256);
- RIE (sanei_genesys_read_register (dev, 0x48, &value));
- if (dev->model->asic_type == GENESYS_GL646)
- *steps += ((value & 0x03) * 256 * 256);
- else if (dev->model->asic_type == GENESYS_GL841)
- *steps += ((value & 0x0f) * 256 * 256);
- else
- *steps += ((value & 0x1f) * 256 * 256);
- }
-
- DBG(DBG_proc, "%s: %d steps\n", __func__, *steps);
- return SANE_STATUS_GOOD;
-}
-
-void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, bool set)
-{
- static const uint8_t REG03_LAMPPWR = 0x10;
-
- if (set) {
- regs.find_reg(0x03).value |= REG03_LAMPPWR;
-
- if (dev->model->asic_type == GENESYS_GL841) {
- sanei_genesys_set_exposure(regs, sanei_genesys_fixup_exposure(sensor.exposure));
- regs.set8(0x19, 0x50);
- }
-
- if (dev->model->asic_type == GENESYS_GL843) {
- sanei_genesys_set_exposure(regs, sensor.exposure);
- }
- } else {
- regs.find_reg(0x03).value &= ~REG03_LAMPPWR;
-
- if (dev->model->asic_type == GENESYS_GL841) {
- sanei_genesys_set_exposure(regs, {0x0101, 0x0101, 0x0101});
- regs.set8(0x19, 0xff);
- }
-
- if (dev->model->asic_type == GENESYS_GL843) {
- if (dev->model->model_id != MODEL_CANON_CANOSCAN_8600F) {
- // BUG: datasheet says we shouldn't set exposure to zero
- sanei_genesys_set_exposure(regs, {0, 0, 0});
- }
- }
- }
- regs.state.is_lamp_on = set;
-}
-
-void sanei_genesys_set_motor_power(Genesys_Register_Set& regs, bool set)
-{
- static const uint8_t REG02_MTRPWR = 0x10;
-
- if (set) {
- regs.find_reg(0x02).value |= REG02_MTRPWR;
- } else {
- regs.find_reg(0x02).value &= ~REG02_MTRPWR;
- }
-}
-
-/**
- * Write to many registers at once
- * Note: sequential call to write register, no effective
- * bulk write implemented.
- * @param dev device to write to
- * @param reg pointer to an array of registers
- * @param elems size of the array
- */
-SANE_Status sanei_genesys_bulk_write_register(Genesys_Device * dev, Genesys_Register_Set& reg)
-{
- DBG_HELPER(dbg);
-
- SANE_Status status = SANE_STATUS_GOOD;
-
- if (dev->model->asic_type == GENESYS_GL646 ||
- dev->model->asic_type == GENESYS_GL841)
- {
- uint8_t outdata[8];
- std::vector<uint8_t> buffer;
- buffer.reserve(reg.size() * 2);
-
- /* copy registers and values in data buffer */
- for (const auto& r : reg) {
- buffer.push_back(r.address);
- buffer.push_back(r.value);
- }
-
- DBG(DBG_io, "%s (elems= %lu, size = %lu)\n", __func__, (u_long) reg.size(),
- (u_long) buffer.size());
-
- if (dev->model->asic_type == GENESYS_GL646) {
- outdata[0] = BULK_OUT;
- outdata[1] = BULK_REGISTER;
- outdata[2] = 0x00;
- outdata[3] = 0x00;
- outdata[4] = (buffer.size() & 0xff);
- outdata[5] = ((buffer.size() >> 8) & 0xff);
- outdata[6] = ((buffer.size() >> 16) & 0xff);
- outdata[7] = ((buffer.size() >> 24) & 0xff);
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, INDEX,
- sizeof(outdata), outdata);
-
- size_t write_size = buffer.size();
-
- dev->usb_dev.bulk_write(buffer.data(), &write_size);
- } else {
- for (size_t i = 0; i < reg.size();) {
- size_t c = reg.size() - i;
- if (c > 32) /*32 is max on GL841. checked that.*/
- c = 32;
-
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_SET_REGISTER,
- INDEX, c * 2, buffer.data() + i * 2);
-
- i += c;
- }
- }
- } else {
- for (const auto& r : reg) {
- status = sanei_genesys_write_register (dev, r.address, r.value);
- if (status != SANE_STATUS_GOOD)
- return status;
- }
- }
-
- DBG (DBG_io, "%s: wrote %lu registers\n", __func__, (u_long) reg.size());
- return status;
-}
-
-
-
-/**
- * writes a block of data to AHB
- * @param dn USB device index
- * @param usb_mode usb mode : 1 usb 1.1, 2 usb 2.0
- * @param addr AHB address to write to
- * @param size size of the chunk of data
- * @param data pointer to the data to write
- */
-SANE_Status
-sanei_genesys_write_ahb(Genesys_Device* dev, uint32_t addr, uint32_t size, uint8_t * data)
-{
- DBG_HELPER(dbg);
-
- uint8_t outdata[8];
- size_t written,blksize;
- SANE_Status status = SANE_STATUS_GOOD;
- int i;
- char msg[100]="AHB=";
-
- outdata[0] = addr & 0xff;
- outdata[1] = ((addr >> 8) & 0xff);
- outdata[2] = ((addr >> 16) & 0xff);
- outdata[3] = ((addr >> 24) & 0xff);
- outdata[4] = (size & 0xff);
- outdata[5] = ((size >> 8) & 0xff);
- outdata[6] = ((size >> 16) & 0xff);
- outdata[7] = ((size >> 24) & 0xff);
-
- if (DBG_LEVEL >= DBG_io)
- {
- for (i = 0; i < 8; i++)
- {
- sprintf (msg+strlen(msg), " 0x%02x", outdata[i]);
- }
- DBG (DBG_io, "%s: write(0x%08x,0x%08x)\n", __func__, addr,size);
- DBG (DBG_io, "%s: %s\n", __func__, msg);
- }
-
- // write addr and size for AHB
- dev->usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x01, 8, outdata);
-
- size_t max_out_size = sanei_genesys_get_bulk_max_size(dev);
-
- /* write actual data */
- written = 0;
- do
- {
- if (size - written > max_out_size)
- {
- blksize = max_out_size;
- }
- else
- {
- blksize = size - written;
- }
- dev->usb_dev.bulk_write(data + written, &blksize);
-
- written += blksize;
- }
- while (written < size);
-
- return status;
-}
-
-
-std::vector<uint16_t> get_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor,
- int color)
-{
- if (!dev->gamma_override_tables[color].empty()) {
- return dev->gamma_override_tables[color];
- } else {
- std::vector<uint16_t> ret;
- sanei_genesys_create_default_gamma_table(dev, ret, sensor.gamma[color]);
- return ret;
- }
-}
-
-/** @brief generates gamma buffer to transfer
- * Generates gamma table buffer to send to ASIC. Applies
- * contrast and brightness if set.
- * @param dev device to set up
- * @param bits number of bits used by gamma
- * @param max value for gamma
- * @param size of the gamma table
- * @param gamma allocated gamma buffer to fill
- * @returns SANE_STATUS_GOOD or SANE_STATUS_NO_MEM
- */
-SANE_Status sanei_genesys_generate_gamma_buffer(Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- int bits,
- int max,
- int size,
- uint8_t *gamma)
-{
- std::vector<uint16_t> rgamma = get_gamma_table(dev, sensor, GENESYS_RED);
- std::vector<uint16_t> ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN);
- std::vector<uint16_t> bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE);
-
- if(dev->settings.contrast!=0 || dev->settings.brightness!=0)
- {
- std::vector<uint16_t> lut(65536);
- sanei_genesys_load_lut((unsigned char *)lut.data(),
- bits,
- bits,
- 0,
- max,
- dev->settings.contrast,
- dev->settings.brightness);
- for (int i = 0; i < size; i++)
- {
- uint16_t value=rgamma[i];
- value=lut[value];
- gamma[i * 2 + size * 0 + 0] = value & 0xff;
- gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
-
- value=ggamma[i];
- value=lut[value];
- gamma[i * 2 + size * 2 + 0] = value & 0xff;
- gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
-
- value=bgamma[i];
- value=lut[value];
- gamma[i * 2 + size * 4 + 0] = value & 0xff;
- gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
- }
- }
- else
- {
- for (int i = 0; i < size; i++)
- {
- uint16_t value=rgamma[i];
- gamma[i * 2 + size * 0 + 0] = value & 0xff;
- gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
-
- value=ggamma[i];
- gamma[i * 2 + size * 2 + 0] = value & 0xff;
- gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
-
- value=bgamma[i];
- gamma[i * 2 + size * 4 + 0] = value & 0xff;
- gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
- }
- }
-
- return SANE_STATUS_GOOD;
-}
-
-
-/** @brief send gamma table to scanner
- * This function sends generic gamma table (ie ones built with
- * provided gamma) or the user defined one if provided by
- * fontend. Used by gl846+ ASICs
- * @param dev device to write to
- */
-SANE_Status
-sanei_genesys_send_gamma_table(Genesys_Device * dev, const Genesys_Sensor& sensor)
-{
- int size;
- int i;
- uint8_t val;
- SANE_Status status = SANE_STATUS_GOOD;
-
- DBGSTART;
-
- size = 256 + 1;
-
- /* allocate temporary gamma tables: 16 bits words, 3 channels */
- std::vector<uint8_t> gamma(size * 2 * 3, 255);
-
- RIE(sanei_genesys_generate_gamma_buffer(dev, sensor, 16, 65535, size, gamma.data()));
-
- /* loop sending gamma tables NOTE: 0x01000000 not 0x10000000 */
- for (i = 0; i < 3; i++)
- {
- /* clear corresponding GMM_N bit */
- RIE(sanei_genesys_read_register(dev, 0xbd, &val));
- val &= ~(0x01 << i);
- RIE(sanei_genesys_write_register(dev, 0xbd, val));
-
- /* clear corresponding GMM_F bit */
- RIE(sanei_genesys_read_register(dev, 0xbe, &val));
- val &= ~(0x01 << i);
- RIE(sanei_genesys_write_register(dev, 0xbe, val));
-
- // FIXME: currently the last word of each gamma table is not initialied, so to work around
- // unstable data, just set it to 0 which is the most likely value of uninitialized memory
- // (proper value is probably 0xff)
- gamma[size * 2 * i + size * 2 - 2] = 0;
- gamma[size * 2 * i + size * 2 - 1] = 0;
-
- /* set GMM_Z */
- RIE(sanei_genesys_write_register (dev, 0xc5+2*i, gamma[size*2*i+1]));
- RIE(sanei_genesys_write_register (dev, 0xc6+2*i, gamma[size*2*i]));
-
- status = sanei_genesys_write_ahb(dev, 0x01000000 + 0x200 * i, (size-1) * 2, gamma.data() + i * size * 2+2);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: write to AHB failed writing table %d (%s)\n", __func__,
- i, sane_strstatus (status));
- break;
- }
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/** @brief initialize device
- * Initialize backend and ASIC : registers, motor tables, and gamma tables
- * then ensure scanner's head is at home. Designed for gl846+ ASICs.
- * Detects cold boot (ie first boot since device plugged) in this case
- * an extensice setup up is done at hardware level.
- *
- * @param dev device to initialize
- * @param max_regs umber of maximum used registers
- * @return SANE_STATUS_GOOD in case of success
- */
-SANE_Status
-sanei_genesys_asic_init(Genesys_Device* dev, int /*max_regs*/)
-{
- DBG_HELPER(dbg);
-
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- SANE_Bool cold = SANE_TRUE;
-
- DBGSTART;
-
- // URB 16 control 0xc0 0x0c 0x8e 0x0b len 1 read 0x00 */
- dev->usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_GET_REGISTER, 0x00, 1, &val);
-
- DBG (DBG_io2, "%s: value=0x%02x\n", __func__, val);
- DBG (DBG_info, "%s: device is %s\n", __func__, (val & 0x08) ? "USB 1.0" : "USB2.0");
- if (val & 0x08)
- {
- dev->usb_mode = 1;
- }
- else
- {
- dev->usb_mode = 2;
- }
-
- /* check if the device has already been initialized and powered up
- * we read register 6 and check PWRBIT, if reset scanner has been
- * freshly powered up. This bit will be set to later so that following
- * reads can detect power down/up cycle*/
- RIE (sanei_genesys_read_register (dev, 0x06, &val));
- /* test for POWER bit */
- if (val & 0x10)
- {
- cold = SANE_FALSE;
- }
- DBG (DBG_info, "%s: device is %s\n", __func__, cold ? "cold" : "warm");
-
- /* don't do anything if backend is initialized and hardware hasn't been
- * replug */
- if (dev->already_initialized && !cold)
- {
- DBG (DBG_info, "%s: already initialized, nothing to do\n", __func__);
- return SANE_STATUS_GOOD;
- }
-
- /* set up hardware and registers */
- RIE (dev->model->cmd_set->asic_boot (dev, cold));
-
- /* now hardware part is OK, set up device struct */
- dev->white_average_data.clear();
- dev->dark_average_data.clear();
-
- dev->settings.color_filter = ColorFilter::RED;
-
- /* duplicate initial values into calibration registers */
- dev->calib_reg = dev->reg;
-
- const auto& sensor = sanei_genesys_find_sensor_any(dev);
-
- /* Set analog frontend */
- RIE (dev->model->cmd_set->set_fe(dev, sensor, AFE_INIT));
-
- dev->already_initialized = SANE_TRUE;
-
- /* Move to home if needed */
- RIE (dev->model->cmd_set->slow_back_home (dev, SANE_TRUE));
- dev->scanhead_position_in_steps = 0;
-
- /* Set powersaving (default = 15 minutes) */
- RIE (dev->model->cmd_set->set_powersaving (dev, 15));
-
- return status;
-}
-
-/**
- * Wait for the scanning head to park
- */
-SANE_Status
-sanei_genesys_wait_for_home (Genesys_Device * dev)
-{
- SANE_Status status = SANE_STATUS_GOOD;
- uint8_t val;
- int loop;
- int max=300;
-
- DBGSTART;
-
- /* clear the parking status whatever the outcome of the function */
- dev->parking=SANE_FALSE;
-
- /* read initial status, if head isn't at home and motor is on
- * we are parking, so we wait.
- * gl847/gl124 need 2 reads for reliable results */
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
- sanei_genesys_sleep_ms(10);
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
-
- /* if at home, return */
- if(val & HOMESNR)
- {
- DBG (DBG_info,
- "%s: already at home\n", __func__);
- return status;
- }
-
- /* loop for 30 s max, polling home sensor */
- loop = 0;
- do
- {
- sanei_genesys_sleep_ms(100);
- status = sanei_genesys_get_status (dev, &val);
- if (status != SANE_STATUS_GOOD)
- {
- DBG (DBG_error,
- "%s: failed to read home sensor: %s\n", __func__,
- sane_strstatus (status));
- return status;
- }
- if (DBG_LEVEL >= DBG_io2)
- {
- sanei_genesys_print_status (val);
- }
- ++loop;
- }
- while (loop < max && !(val & HOMESNR) && status == SANE_STATUS_GOOD);
-
- /* if after the timeout, head is still not parked, error out */
- if(loop >= max && !(val & HOMESNR) && status == SANE_STATUS_GOOD)
- {
- DBG (DBG_error, "%s: failed to reach park position %ds\n", __func__, max/10);
- return SANE_STATUS_IO_ERROR;
- }
-
- DBGCOMPLETED;
- return status;
-}
-
-/**@brief compute hardware sensor dpi to use
- * compute the sensor hardware dpi based on target resolution.
- * A lower dpihw enable faster scans.
- * @param dev device used for the scan
- * @param xres x resolution of the scan
- * @return the hardware dpi to use
- */
-int sanei_genesys_compute_dpihw(Genesys_Device *dev, const Genesys_Sensor& sensor, int xres)
-{
- /* some scanners use always hardware dpi for sensor */
- if (dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE)
- {
- return sensor.optical_res;
- }
-
- /* can't be below 600 dpi */
- if (xres <= 600)
- {
- return 600;
- }
- if (xres <= sensor.optical_res / 4)
- {
- return sensor.optical_res / 4;
- }
- if (xres <= sensor.optical_res / 2)
- {
- return sensor.optical_res / 2;
- }
- return sensor.optical_res;
-}
-
-// sanei_genesys_compute_dpihw returns the dpihw that is written to register.
-// However the number of pixels depends on half_ccd mode
-int sanei_genesys_compute_dpihw_calibration(Genesys_Device *dev, const Genesys_Sensor& sensor,
- int xres)
-{
- if (dev->model->model_id == MODEL_CANON_CANOSCAN_8600F)
- {
- // real resolution is half of the "official" resolution - half_ccd mode
- int hwres = sensor.optical_res / sensor.get_ccd_size_divisor_for_dpi(xres);
-
- if (xres <= hwres / 4)
- {
- return hwres / 4;
- }
- if (xres <= hwres / 2)
- {
- return hwres / 2;
- }
- return hwres;
- }
-
- return sanei_genesys_compute_dpihw(dev, sensor, xres);
-}
-
-/** @brief motor profile
- * search for the database of motor profiles and get the best one. Each
- * profile is at full step and at a reference exposure. Use first entry
- * by default.
- * @param motors motor profile database
- * @param motor_type motor id
- * @param exposure exposure time
- * @return a pointer to a Motor_Profile struct
- */
-Motor_Profile *sanei_genesys_get_motor_profile(Motor_Profile *motors, int motor_type, int exposure)
-{
- unsigned int i;
- int idx;
-
- i=0;
- idx=-1;
- while(motors[i].exposure!=0)
- {
- /* exact match */
- if(motors[i].motor_type==motor_type && motors[i].exposure==exposure)
- {
- return &(motors[i]);
- }
-
- /* closest match */
- if(motors[i].motor_type==motor_type)
- {
- /* if profile exposure is higher than the required one,
- * the entry is a candidate for the closest match */
- if(motors[i].exposure>=exposure)
- {
- if(idx<0)
- {
- /* no match found yet */
- idx=i;
- }
- else
- {
- /* test for better match */
- if(motors[i].exposure<motors[idx].exposure)
- {
- idx=i;
- }
- }
- }
- }
- i++;
- }
-
- /* default fallback */
- if(idx<0)
- {
- DBG (DBG_warn,"%s: using default motor profile\n",__func__);
- idx=0;
- }
-
- return &(motors[idx]);
-}
-
-/**@brief compute motor step type to use
- * compute the step type (full, half, quarter, ...) to use based
- * on target resolution
- * @param motors motor profile database
- * @param motor_type motor id
- * @param exposure sensor exposure
- * @return 0 for full step
- * 1 for half step
- * 2 for quarter step
- * 3 for eighth step
- */
-int sanei_genesys_compute_step_type(Motor_Profile *motors,
- int motor_type,
- int exposure)
-{
-Motor_Profile *profile;
-
- profile=sanei_genesys_get_motor_profile(motors, motor_type, exposure);
- return profile->step_type;
-}
-
-/** @brief generate slope table
- * Generate the slope table to use for the scan using a reference slope
- * table.
- * @param slope pointer to the slope table to fill
- * @param steps pointer to return used step number
- * @param dpi desired motor resolution
- * @param exposure exposure used
- * @param base_dpi base resolution of the motor
- * @param step_type step type used for scan
- * @param factor shrink factor for the slope
- * @param motor_type motor id
- * @param motors motor profile database
- */
-int sanei_genesys_slope_table(uint16_t *slope,
- int *steps,
- int dpi,
- int exposure,
- int base_dpi,
- int step_type,
- int factor,
- int motor_type,
- Motor_Profile *motors)
-{
-int sum, i;
-uint16_t target,current;
-Motor_Profile *profile;
-
- /* required speed */
- target=((exposure * dpi) / base_dpi)>>step_type;
- DBG (DBG_io2, "%s: exposure=%d, dpi=%d, target=%d\n", __func__, exposure, dpi, target);
-
- /* fill result with target speed */
- for(i=0;i<SLOPE_TABLE_SIZE;i++)
- slope[i]=target;
-
- profile=sanei_genesys_get_motor_profile(motors, motor_type, exposure);
-
- /* use profile to build table */
- i=0;
- sum=0;
-
- /* first step is always used unmodified */
- current=profile->table[0];
-
- /* loop on profile copying and apply step type */
- while(profile->table[i]!=0 && current>=target)
- {
- slope[i]=current;
- sum+=slope[i];
- i++;
- current=profile->table[i]>>step_type;
- }
-
- /* ensure last step is required speed in case profile doesn't contain it */
- if(current!=0 && current<target)
- {
- slope[i]=target;
- sum+=slope[i];
- i++;
- }
-
- /* range checking */
- if(profile->table[i]==0 && DBG_LEVEL >= DBG_warn && current>target)
- {
- DBG (DBG_warn,"%s: short slope table, failed to reach %d. target too low ?\n",__func__,target);
- }
- if(i<3 && DBG_LEVEL >= DBG_warn)
- {
- DBG (DBG_warn,"%s: short slope table, failed to reach %d. target too high ?\n",__func__,target);
- }
-
- /* align on factor */
- while(i%factor!=0)
- {
- slope[i+1]=slope[i];
- sum+=slope[i];
- i++;
- }
-
- /* ensure minimal slope size */
- while(i<2*factor)
- {
- slope[i+1]=slope[i];
- sum+=slope[i];
- i++;
- }
-
- // return used steps and taken time
- *steps=i/factor;
- return sum;
-}
-
-/** @brief returns the lowest possible ydpi for the device
- * Parses device entry to find lowest motor dpi.
- * @param dev device description
- * @return lowest motor resolution
- */
-int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev)
-{
- int min=20000;
- int i=0;
-
- while(dev->model->ydpi_values[i]!=0)
- {
- if(dev->model->ydpi_values[i]<min)
- {
- min=dev->model->ydpi_values[i];
- }
- i++;
- }
- return min;
-}
-
-/** @brief returns the lowest possible dpi for the device
- * Parses device entry to find lowest motor or sensor dpi.
- * @param dev device description
- * @return lowest motor resolution
- */
-int sanei_genesys_get_lowest_dpi(Genesys_Device *dev)
-{
- int min=20000;
- int i=0;
-
- while(dev->model->ydpi_values[i]!=0)
- {
- if(dev->model->ydpi_values[i]<min)
- {
- min=dev->model->ydpi_values[i];
- }
- i++;
- }
- i=0;
- while(dev->model->xdpi_values[i]!=0)
- {
- if(dev->model->xdpi_values[i]<min)
- {
- min=dev->model->xdpi_values[i];
- }
- i++;
- }
- return min;
-}
-
-/** @brief check is a cache entry may be used
- * Compares current settings with the cache entry and return
- * SANE_TRUE if they are compatible.
- * A calibration cache is compatible if color mode and x dpi match the user
- * requested scan. In the case of CIS scanners, dpi isn't a criteria.
- * flatbed cache entries are considred too old and then expires if they
- * are older than the expiration time option, forcing calibration at least once
- * then given time. */
-bool sanei_genesys_is_compatible_calibration(Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Calibration_Cache * cache, int for_overwrite)
-{
-#ifdef HAVE_SYS_TIME_H
- struct timeval time;
-#endif
- int compatible = 1, resolution;
-
- DBGSTART;
-
- if(dev->model->cmd_set->calculate_current_setup==NULL)
- {
- DBG (DBG_proc, "%s: no calculate_setup, non compatible cache\n", __func__);
- return false;
- }
-
- dev->model->cmd_set->calculate_current_setup(dev, sensor);
-
- DBG (DBG_proc, "%s: checking\n", __func__);
-
- /* a calibration cache is compatible if color mode and x dpi match the user
- * requested scan. In the case of CIS scanners, dpi isn't a criteria */
- if (dev->model->is_cis == SANE_FALSE)
- {
- resolution = dev->settings.xres;
- if(resolution>sensor.optical_res)
- {
- resolution=sensor.optical_res;
- }
- compatible = (resolution == ((int) cache->used_setup.xres));
- }
- else
- {
- resolution=sanei_genesys_compute_dpihw(dev, sensor, dev->settings.xres);
- compatible = (resolution == ((int) sanei_genesys_compute_dpihw(dev, sensor,cache->used_setup.xres)));
- }
- DBG (DBG_io, "%s: after resolution check current compatible=%d\n", __func__, compatible);
- if (dev->current_setup.ccd_size_divisor != cache->used_setup.ccd_size_divisor)
- {
- DBG (DBG_io, "%s: half_ccd=%d, used=%d\n", __func__,
- dev->current_setup.ccd_size_divisor, cache->used_setup.ccd_size_divisor);
- compatible = 0;
- }
- if (dev->current_setup.params.scan_method != cache->used_setup.params.scan_method)
- {
- DBG (DBG_io, "%s: current method=%d, used=%d\n", __func__,
- static_cast<unsigned>(dev->current_setup.params.scan_method),
- static_cast<unsigned>(cache->used_setup.params.scan_method));
- compatible = 0;
- }
- if (!compatible)
- {
- DBG (DBG_proc, "%s: completed, non compatible cache\n", __func__);
- return false;
- }
-
- /* a cache entry expires after afetr expiration time for non sheetfed scanners */
- /* this is not taken into account when overwriting cache entries */
-#ifdef HAVE_SYS_TIME_H
- if(for_overwrite == SANE_FALSE && dev->settings.expiration_time >=0)
- {
- gettimeofday (&time, NULL);
- if ((time.tv_sec - cache->last_calibration > dev->settings.expiration_time*60)
- && (dev->model->is_sheetfed == SANE_FALSE)
- && (dev->settings.scan_method == ScanMethod::FLATBED))
- {
- DBG (DBG_proc, "%s: expired entry, non compatible cache\n", __func__);
- return false;
- }
- }
-#endif
-
- DBGCOMPLETED;
- return true;
-}
-
-
-/** @brief compute maximum line distance shift
- * compute maximum line distance shift for the motor and sensor
- * combination. Line distance shift is the distance between different
- * color component of CCD sensors. Since these components aren't at
- * the same physical place, they scan diffrent lines. Software must
- * take this into account to accurately mix color data.
- * @param dev device session to compute max_shift for
- * @param channels number of color channels for the scan
- * @param yres motor resolution used for the scan
- * @param flags scan flags
- * @return 0 or line distance shift
- */
-int sanei_genesys_compute_max_shift(Genesys_Device *dev,
- int channels,
- int yres,
- int flags)
-{
- int max_shift;
-
- max_shift=0;
- if (channels > 1 && !(flags & SCAN_FLAG_IGNORE_LINE_DISTANCE))
- {
- max_shift = dev->ld_shift_r;
- if (dev->ld_shift_b > max_shift)
- max_shift = dev->ld_shift_b;
- if (dev->ld_shift_g > max_shift)
- max_shift = dev->ld_shift_g;
- max_shift = (max_shift * yres) / dev->motor.base_ydpi;
- }
- return max_shift;
-}
-
-/** @brief build lookup table for digital enhancements
- * Function to build a lookup table (LUT), often
- used by scanners to implement brightness/contrast/gamma
- or by backends to speed binarization/thresholding
-
- offset and slope inputs are -127 to +127
-
- slope rotates line around central input/output val,
- 0 makes horizontal line
-
- pos zero neg
- . x . . x
- . x . . x
- out . x .xxxxxxxxxxx . x
- . x . . x
- ....x....... ............ .......x....
- in in in
-
- offset moves line vertically, and clamps to output range
- 0 keeps the line crossing the center of the table
-
- high low
- . xxxxxxxx .
- . x .
- out x . x
- . . x
- ............ xxxxxxxx....
- in in
-
- out_min/max provide bounds on output values,
- useful when building thresholding lut.
- 0 and 255 are good defaults otherwise.
- * @param lut pointer where to store the generated lut
- * @param in_bits number of bits for in values
- * @param out_bits number of bits of out values
- * @param out_min minimal out value
- * @param out_max maximal out value
- * @param slope slope of the generated data
- * @param offset offset of the generated data
- */
-SANE_Status
-sanei_genesys_load_lut (unsigned char * lut,
- int in_bits,
- int out_bits,
- int out_min,
- int out_max,
- int slope,
- int offset)
-{
- SANE_Status ret = SANE_STATUS_GOOD;
- int i, j;
- double shift, rise;
- int max_in_val = (1 << in_bits) - 1;
- int max_out_val = (1 << out_bits) - 1;
- uint8_t *lut_p8 = lut;
- uint16_t *lut_p16 = (uint16_t *) lut;
-
- DBGSTART;
-
- /* slope is converted to rise per unit run:
- * first [-127,127] to [-.999,.999]
- * then to [-PI/4,PI/4] then [0,PI/2]
- * then take the tangent (T.O.A)
- * then multiply by the normal linear slope
- * because the table may not be square, i.e. 1024x256*/
- rise = tan ((double) slope / 128 * M_PI_4 + M_PI_4) * max_out_val / max_in_val;
-
- /* line must stay vertically centered, so figure
- * out vertical offset at central input value */
- shift = (double) max_out_val / 2 - (rise * max_in_val / 2);
-
- /* convert the user offset setting to scale of output
- * first [-127,127] to [-1,1]
- * then to [-max_out_val/2,max_out_val/2]*/
- shift += (double) offset / 127 * max_out_val / 2;
-
- for (i = 0; i <= max_in_val; i++)
- {
- j = rise * i + shift;
-
- /* cap data to required range */
- if (j < out_min)
- {
- j = out_min;
- }
- else if (j > out_max)
- {
- j = out_max;
- }
-
- /* copy result according to bit depth */
- if (out_bits <= 8)
- {
- *lut_p8 = j;
- lut_p8++;
- }
- else
- {
- *lut_p16 = j;
- lut_p16++;
- }
- }
-
- DBGCOMPLETED;
- return ret;
-}
-
-void sanei_genesys_usleep(unsigned int useconds)
-{
- usleep(useconds);
-}
-
-void sanei_genesys_sleep_ms(unsigned int milliseconds)
-{
- sanei_genesys_usleep(milliseconds * 1000);
-}
-
-static std::unique_ptr<std::vector<std::function<void()>>> s_functions_run_at_backend_exit;
-
-void add_function_to_run_at_backend_exit(std::function<void()> function)
-{
- if (!s_functions_run_at_backend_exit)
- s_functions_run_at_backend_exit.reset(new std::vector<std::function<void()>>());
- s_functions_run_at_backend_exit->push_back(std::move(function));
-}
-
-void run_functions_at_backend_exit()
-{
- for (auto it = s_functions_run_at_backend_exit->rbegin();
- it != s_functions_run_at_backend_exit->rend(); ++it)
- {
- (*it)();
- }
- s_functions_run_at_backend_exit.release();
-}
-
-void debug_dump(unsigned level, const Genesys_Settings& settings)
-{
- DBG(level, "settings:\n"
- "Resolution X/Y : %u / %u dpi\n"
- "Lines : %u\n"
- "Pixels per line : %u\n"
- "Depth : %u\n"
- "Start position X/Y : %.3f/%.3f\n"
- "Scan mode : %d\n\n",
- settings.xres, settings.yres,
- settings.lines, settings.pixels, settings.depth,
- settings.tl_x, settings.tl_y,
- static_cast<unsigned>(settings.scan_mode));
-}
-
-void debug_dump(unsigned level, const SetupParams& params)
-{
- DBG(level, "settings:\n"
- "Resolution X/Y : %u / %u dpi\n"
- "Lines : %u\n"
- "Pixels per line : %u\n"
- "Depth : %u\n"
- "Channels : %u\n"
- "Start position X/Y : %g / %g\n"
- "Scan mode : %d\n"
- "Color filter : %d\n"
- "Flags : %x\n",
- params.xres, params.yres,
- params.lines, params.pixels,
- params.depth, params.channels,
- params.startx, params.starty,
- static_cast<unsigned>(params.scan_mode),
- static_cast<unsigned>(params.color_filter),
- params.flags);
-}
-
-void debug_dump(unsigned level, const Genesys_Current_Setup& setup)
-{
- DBG(level, "current_setup:\n"
- "Pixels: %d\n"
- "Lines: %d\n"
- "Depth: %d\n"
- "Channels: %d\n"
- "exposure_time: %d\n"
- "Resolution X/Y: %g %g\n"
- "ccd_size_divisor: %d\n"
- "stagger: %d\n"
- "max_shift: %d\n",
- setup.pixels,
- setup.lines,
- setup.depth,
- setup.channels,
- setup.exposure_time,
- setup.xres, setup.yres,
- setup.ccd_size_divisor,
- setup.stagger,
- setup.max_shift);
-}
diff --git a/backend/genesys_low.h b/backend/genesys_low.h
deleted file mode 100644
index e750808..0000000
--- a/backend/genesys_low.h
+++ /dev/null
@@ -1,2042 +0,0 @@
-/* sane - Scanner Access Now Easy.
-
- Copyright (C) 2003 Oliver Rauch
- Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
- Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
- Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
- Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
- Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
- Parts of the structs have been taken from the gt68xx backend by
- Sergey Vlasov <vsu@altlinux.ru> et al.
-
- This file is part of the SANE package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- As a special exception, the authors of SANE give permission for
- additional uses of the libraries contained in this release of SANE.
-
- The exception is that, if you link a SANE library with other files
- to produce an executable, this does not by itself cause the
- resulting executable to be covered by the GNU General Public
- License. Your use of that executable is in no way restricted on
- account of linking the SANE library code into it.
-
- This exception does not, however, invalidate any other reasons why
- the executable file might be covered by the GNU General Public
- License.
-
- If you submit changes to SANE to the maintainers to be included in
- a subsequent release, you agree by submitting the changes that
- those changes may be distributed with this exception intact.
-
- If you write modifications of your own for SANE, it is your choice
- whether to permit this exception to apply to your modifications.
- If you do not wish that, delete this exception notice.
-*/
-
-#ifndef GENESYS_LOW_H
-#define GENESYS_LOW_H
-
-
-#include "../include/sane/config.h"
-
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <math.h>
-#include <stddef.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_MKDIR
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
-#include "../include/sane/sane.h"
-#include "../include/sane/sanei.h"
-#include "../include/sane/saneopts.h"
-
-#include "../include/sane/sanei_backend.h"
-#include "../include/sane/sanei_usb.h"
-
-#include "../include/_stdint.h"
-
-#include "genesys_error.h"
-#include "genesys_sanei.h"
-#include "genesys_serialize.h"
-
-#include <algorithm>
-#include <array>
-#include <cstring>
-#include <functional>
-#include <iostream>
-#include <limits>
-#include <memory>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-#define FREE_IFNOT_NULL(x) if(x!=NULL) { free(x); x=NULL;}
-
-#define GENESYS_RED 0
-#define GENESYS_GREEN 1
-#define GENESYS_BLUE 2
-
-/* Flags */
-#define GENESYS_FLAG_UNTESTED (1 << 0) /**< Print a warning for these scanners */
-#define GENESYS_FLAG_14BIT_GAMMA (1 << 1) /**< use 14bit Gamma table instead of 12 */
-#define GENESYS_FLAG_LAZY_INIT (1 << 2) /**< skip extensive ASIC test at init */
-#define GENESYS_FLAG_XPA (1 << 3)
-#define GENESYS_FLAG_SKIP_WARMUP (1 << 4) /**< skip genesys_warmup() */
-/** @brief offset calibration flag
- * signals that the scanner does offset calibration. In this case off_calibration() and
- * coarse_gain_calibration() functions must be implemented
- */
-#define GENESYS_FLAG_OFFSET_CALIBRATION (1 << 5)
-#define GENESYS_FLAG_SEARCH_START (1 << 6) /**< do start search before scanning */
-#define GENESYS_FLAG_REPARK (1 << 7) /**< repark head (and check for lock) by
- moving without scanning */
-#define GENESYS_FLAG_DARK_CALIBRATION (1 << 8) /**< do dark calibration */
-#define GENESYS_FLAG_STAGGERED_LINE (1 << 9) /**< pixel columns are shifted vertically for hi-res modes */
-
-#define GENESYS_FLAG_MUST_WAIT (1 << 10) /**< tells wether the scanner must wait for the head when parking */
-
-
-#define GENESYS_FLAG_HAS_UTA (1 << 11) /**< scanner has a transparency adapter */
-
-#define GENESYS_FLAG_DARK_WHITE_CALIBRATION (1 << 12) /**< yet another calibration method. does white and dark shading in one run, depending on a black and a white strip*/
-#define GENESYS_FLAG_CUSTOM_GAMMA (1 << 13) /**< allow custom gamma tables */
-#define GENESYS_FLAG_NO_CALIBRATION (1 << 14) /**< allow scanners to use skip the calibration, needed for sheetfed scanners */
-#define GENESYS_FLAG_SIS_SENSOR (1 << 16) /**< handling of multi-segments sensors in software */
-#define GENESYS_FLAG_SHADING_NO_MOVE (1 << 17) /**< scanner doesn't move sensor during shading calibration */
-#define GENESYS_FLAG_SHADING_REPARK (1 << 18) /**< repark head between shading scans */
-#define GENESYS_FLAG_FULL_HWDPI_MODE (1 << 19) /**< scanner always use maximum hw dpi to setup the sensor */
-// scanner has infrared transparency scanning capability
-#define GENESYS_FLAG_HAS_UTA_INFRARED (1 << 20)
-
-#define GENESYS_HAS_NO_BUTTONS 0 /**< scanner has no supported button */
-#define GENESYS_HAS_SCAN_SW (1 << 0) /**< scanner has SCAN button */
-#define GENESYS_HAS_FILE_SW (1 << 1) /**< scanner has FILE button */
-#define GENESYS_HAS_COPY_SW (1 << 2) /**< scanner has COPY button */
-#define GENESYS_HAS_EMAIL_SW (1 << 3) /**< scanner has EMAIL button */
-#define GENESYS_HAS_PAGE_LOADED_SW (1 << 4) /**< scanner has paper in detection */
-#define GENESYS_HAS_OCR_SW (1 << 5) /**< scanner has OCR button */
-#define GENESYS_HAS_POWER_SW (1 << 6) /**< scanner has power button */
-#define GENESYS_HAS_CALIBRATE (1 << 7) /**< scanner has 'calibrate' software button to start calibration */
-#define GENESYS_HAS_EXTRA_SW (1 << 8) /**< scanner has extra function button */
-
-/* USB control message values */
-#define REQUEST_TYPE_IN (USB_TYPE_VENDOR | USB_DIR_IN)
-#define REQUEST_TYPE_OUT (USB_TYPE_VENDOR | USB_DIR_OUT)
-#define REQUEST_REGISTER 0x0c
-#define REQUEST_BUFFER 0x04
-#define VALUE_BUFFER 0x82
-#define VALUE_SET_REGISTER 0x83
-#define VALUE_READ_REGISTER 0x84
-#define VALUE_WRITE_REGISTER 0x85
-#define VALUE_INIT 0x87
-#define GPIO_OUTPUT_ENABLE 0x89
-#define GPIO_READ 0x8a
-#define GPIO_WRITE 0x8b
-#define VALUE_BUF_ENDACCESS 0x8c
-#define VALUE_GET_REGISTER 0x8e
-#define INDEX 0x00
-
-/* todo: used?
-#define VALUE_READ_STATUS 0x86
-*/
-
-/* Read/write bulk data/registers */
-#define BULK_OUT 0x01
-#define BULK_IN 0x00
-#define BULK_RAM 0x00
-#define BULK_REGISTER 0x11
-
-#define BULKOUT_MAXSIZE 0xF000
-
-/* AFE values */
-#define AFE_INIT 1
-#define AFE_SET 2
-#define AFE_POWER_SAVE 4
-
-#define LOWORD(x) ((uint16_t)((x) & 0xffff))
-#define HIWORD(x) ((uint16_t)((x) >> 16))
-#define LOBYTE(x) ((uint8_t)((x) & 0xFF))
-#define HIBYTE(x) ((uint8_t)((x) >> 8))
-
-/* Global constants */
-/* TODO: emove this leftover of early backend days */
-#define MOTOR_SPEED_MAX 350
-#define DARK_VALUE 0
-
-#define PWRBIT 0x80
-#define BUFEMPTY 0x40
-#define FEEDFSH 0x20
-#define SCANFSH 0x10
-#define HOMESNR 0x08
-#define LAMPSTS 0x04
-#define FEBUSY 0x02
-#define MOTORENB 0x01
-
-#define GENESYS_MAX_REGS 256
-
-enum class ScanMethod : unsigned {
- // normal scan method
- FLATBED = 0,
- // scan using transparency adaptor
- TRANSPARENCY = 1,
- // scan using transparency adaptor via infrared channel
- TRANSPARENCY_INFRARED = 2
-};
-
-inline void serialize(std::istream& str, ScanMethod& x)
-{
- unsigned value;
- serialize(str, value);
- x = static_cast<ScanMethod>(value);
-}
-
-inline void serialize(std::ostream& str, ScanMethod& x)
-{
- unsigned value = static_cast<unsigned>(x);
- serialize(str, value);
-}
-
-enum class ScanColorMode : unsigned {
- LINEART = 0,
- HALFTONE,
- GRAY,
- COLOR_SINGLE_PASS
-};
-
-inline void serialize(std::istream& str, ScanColorMode& x)
-{
- unsigned value;
- serialize(str, value);
- x = static_cast<ScanColorMode>(value);
-}
-
-inline void serialize(std::ostream& str, ScanColorMode& x)
-{
- unsigned value = static_cast<unsigned>(x);
- serialize(str, value);
-}
-
-enum class ColorFilter : unsigned {
- RED = 0,
- GREEN,
- BLUE,
- NONE
-};
-
-inline void serialize(std::istream& str, ColorFilter& x)
-{
- unsigned value;
- serialize(str, value);
- x = static_cast<ColorFilter>(value);
-}
-
-inline void serialize(std::ostream& str, ColorFilter& x)
-{
- unsigned value = static_cast<unsigned>(x);
- serialize(str, value);
-}
-
-struct GenesysRegister {
- uint16_t address = 0;
- uint8_t value = 0;
-};
-
-inline bool operator<(const GenesysRegister& lhs, const GenesysRegister& rhs)
-{
- return lhs.address < rhs.address;
-}
-
-struct GenesysRegisterSetState {
- bool is_lamp_on = false;
- bool is_xpa_on = false;
-};
-
-class Genesys_Register_Set {
-public:
- using container = std::vector<GenesysRegister>;
- using iterator = typename container::iterator;
- using const_iterator = typename container::const_iterator;
-
- // FIXME: this shouldn't live here, but in a separate struct that contains Genesys_Register_Set
- GenesysRegisterSetState state;
-
- enum Options {
- SEQUENTIAL = 1
- };
-
- Genesys_Register_Set()
- {
- registers_.reserve(GENESYS_MAX_REGS);
- }
-
- // by default the register set is sorted by address. In certain cases it's importand to send
- // the registers in certain order: use the SEQUENTIAL option for that
- Genesys_Register_Set(Options opts) : Genesys_Register_Set()
- {
- if ((opts & SEQUENTIAL) == SEQUENTIAL) {
- sorted_ = false;
- }
- }
-
- void init_reg(uint16_t address, uint8_t default_value)
- {
- if (find_reg_index(address) >= 0) {
- set8(address, default_value);
- return;
- }
- GenesysRegister reg;
- reg.address = address;
- reg.value = default_value;
- registers_.push_back(reg);
- if (sorted_)
- std::sort(registers_.begin(), registers_.end());
- }
-
- void remove_reg(uint16_t address)
- {
- int i = find_reg_index(address);
- if (i < 0) {
- throw std::runtime_error("the register does not exist");
- }
- registers_.erase(registers_.begin() + i);
- }
-
- GenesysRegister& find_reg(uint16_t address)
- {
- int i = find_reg_index(address);
- if (i < 0) {
- throw std::runtime_error("the register does not exist");
- }
- return registers_[i];
- }
-
- const GenesysRegister& find_reg(uint16_t address) const
- {
- int i = find_reg_index(address);
- if (i < 0) {
- throw std::runtime_error("the register does not exist");
- }
- return registers_[i];
- }
-
- GenesysRegister* find_reg_address(uint16_t address)
- {
- return &find_reg(address);
- }
-
- const GenesysRegister* find_reg_address(uint16_t address) const
- {
- return &find_reg(address);
- }
-
- void set8(uint16_t address, uint8_t value)
- {
- find_reg(address).value = value;
- }
-
- void set8_mask(uint16_t address, uint8_t value, uint8_t mask)
- {
- auto& reg = find_reg(address);
- reg.value = (reg.value & ~mask) | value;
- }
-
- void set16(uint16_t address, uint16_t value)
- {
- find_reg(address).value = (value >> 8) & 0xff;
- find_reg(address + 1).value = value & 0xff;
- }
-
- void set24(uint16_t address, uint32_t value)
- {
- find_reg(address).value = (value >> 16) & 0xff;
- find_reg(address + 1).value = (value >> 8) & 0xff;
- find_reg(address + 2).value = value & 0xff;
- }
-
- uint8_t get8(uint16_t address) const
- {
- return find_reg(address).value;
- }
-
- uint16_t get16(uint16_t address) const
- {
- return (find_reg(address).value << 8) | find_reg(address + 1).value;
- }
-
- uint32_t get24(uint16_t address) const
- {
- return (find_reg(address).value << 16) |
- (find_reg(address + 1).value << 8) |
- find_reg(address + 2).value;
- }
-
- void clear() { registers_.clear(); }
- size_t size() const { return registers_.size(); }
-
- iterator begin() { return registers_.begin(); }
- const_iterator begin() const { return registers_.begin(); }
-
- iterator end() { return registers_.end(); }
- const_iterator end() const { return registers_.end(); }
-
-private:
- int find_reg_index(uint16_t address) const
- {
- if (!sorted_) {
- for (size_t i = 0; i < registers_.size(); i++) {
- if (registers_[i].address == address) {
- return i;
- }
- }
- return -1;
- }
-
- GenesysRegister search;
- search.address = address;
- auto it = std::lower_bound(registers_.begin(), registers_.end(), search);
- if (it == registers_.end())
- return -1;
- if (it->address != address)
- return -1;
- return std::distance(registers_.begin(), it);
- }
-
- // registers are stored in a sorted vector
- bool sorted_ = true;
- std::vector<GenesysRegister> registers_;
-};
-
-template<class T, size_t Size>
-struct AssignableArray : public std::array<T, Size> {
- AssignableArray() = default;
- AssignableArray(const AssignableArray&) = default;
- AssignableArray& operator=(const AssignableArray&) = default;
-
- AssignableArray& operator=(std::initializer_list<T> init)
- {
- if (init.size() != std::array<T, Size>::size())
- throw std::runtime_error("An array of incorrect size assigned");
- std::copy(init.begin(), init.end(), std::array<T, Size>::begin());
- return *this;
- }
-};
-
-struct GenesysRegisterSetting {
- GenesysRegisterSetting() = default;
-
- GenesysRegisterSetting(uint16_t p_address, uint8_t p_value) :
- address(p_address), value(p_value)
- {}
-
- GenesysRegisterSetting(uint16_t p_address, uint8_t p_value, uint8_t p_mask) :
- address(p_address), value(p_value), mask(p_mask)
- {}
-
- uint16_t address = 0;
- uint8_t value = 0;
- uint8_t mask = 0xff;
-
- bool operator==(const GenesysRegisterSetting& other) const
- {
- return address == other.address && value == other.value && mask == other.mask;
- }
-};
-
-template<class Stream>
-void serialize(Stream& str, GenesysRegisterSetting& reg)
-{
- serialize(str, reg.address);
- serialize(str, reg.value);
- serialize(str, reg.mask);
-}
-
-class GenesysRegisterSettingSet {
-public:
- using container = std::vector<GenesysRegisterSetting>;
- using iterator = typename container::iterator;
- using const_iterator = typename container::const_iterator;
-
- GenesysRegisterSettingSet() = default;
- GenesysRegisterSettingSet(std::initializer_list<GenesysRegisterSetting> ilist) : regs_(ilist) {}
-
- iterator begin() { return regs_.begin(); }
- const_iterator begin() const { return regs_.begin(); }
- iterator end() { return regs_.end(); }
- const_iterator end() const { return regs_.end(); }
-
- GenesysRegisterSetting& operator[](size_t i) { return regs_[i]; }
- const GenesysRegisterSetting& operator[](size_t i) const { return regs_[i]; }
-
- size_t size() const { return regs_.size(); }
- bool empty() const { return regs_.empty(); }
- void clear() { regs_.clear(); }
-
- void push_back(GenesysRegisterSetting reg) { regs_.push_back(reg); }
-
- void merge(const GenesysRegisterSettingSet& other)
- {
- for (const auto& reg : other) {
- set_value(reg.address, reg.value);
- }
- }
-
- uint8_t get_value(uint16_t address) const
- {
- for (const auto& reg : regs_) {
- if (reg.address == address)
- return reg.value;
- }
- throw std::runtime_error("Unknown register");
- }
-
- void set_value(uint16_t address, uint8_t value)
- {
- for (auto& reg : regs_) {
- if (reg.address == address) {
- reg.value = value;
- return;
- }
- }
- push_back(GenesysRegisterSetting(address, value));
- }
-
- friend void serialize(std::istream& str, GenesysRegisterSettingSet& reg);
- friend void serialize(std::ostream& str, GenesysRegisterSettingSet& reg);
-
- bool operator==(const GenesysRegisterSettingSet& other) const
- {
- return regs_ == other.regs_;
- }
-
-private:
- std::vector<GenesysRegisterSetting> regs_;
-};
-
-inline void serialize(std::istream& str, GenesysRegisterSettingSet& reg)
-{
- reg.clear();
- const size_t max_register_address =
- 1 << (sizeof(GenesysRegisterSetting::address) * CHAR_BIT);
- serialize(str, reg.regs_, max_register_address);
-}
-
-inline void serialize(std::ostream& str, GenesysRegisterSettingSet& reg)
-{
- serialize(str, reg.regs_);
-}
-
-struct GenesysFrontendLayout
-{
- std::array<uint16_t, 3> offset_addr = {};
- std::array<uint16_t, 3> gain_addr = {};
-
- bool operator==(const GenesysFrontendLayout& other) const
- {
- return offset_addr == other.offset_addr && gain_addr == other.gain_addr;
- }
-};
-
-/** @brief Data structure to set up analog frontend.
- The analog frontend converts analog value from image sensor to digital value. It has its own
- control registers which are set up with this structure. The values are written using
- sanei_genesys_fe_write_data.
- */
-struct Genesys_Frontend
-{
- Genesys_Frontend() = default;
-
- // id of the frontend description
- uint8_t fe_id = 0;
-
- // all registers of the frontend
- GenesysRegisterSettingSet regs;
-
- // extra control registers
- std::array<uint8_t, 3> reg2 = {};
-
- GenesysFrontendLayout layout;
-
- void set_offset(unsigned which, uint8_t value)
- {
- regs.set_value(layout.offset_addr[which], value);
- }
-
- void set_gain(unsigned which, uint8_t value)
- {
- regs.set_value(layout.gain_addr[which], value);
- }
-
- uint8_t get_offset(unsigned which) const
- {
- return regs.get_value(layout.offset_addr[which]);
- }
-
- uint8_t get_gain(unsigned which) const
- {
- return regs.get_value(layout.gain_addr[which]);
- }
-
- bool operator==(const Genesys_Frontend& other) const
- {
- return fe_id == other.fe_id &&
- regs == other.regs &&
- reg2 == other.reg2 &&
- layout == other.layout;
- }
-};
-
-template<class Stream>
-void serialize(Stream& str, Genesys_Frontend& x)
-{
- serialize(str, x.fe_id);
- serialize_newline(str);
- serialize(str, x.regs);
- serialize_newline(str);
- serialize(str, x.reg2);
- serialize_newline(str);
- serialize(str, x.layout.offset_addr);
- serialize(str, x.layout.gain_addr);
-}
-
-struct SensorExposure {
- uint16_t red, green, blue;
-};
-
-struct Genesys_Sensor {
-
- Genesys_Sensor() = default;
- ~Genesys_Sensor() = default;
-
- // id of the sensor description
- uint8_t sensor_id = 0;
- int optical_res = 0;
-
- // the minimum and maximum resolution this sensor is usable at. -1 means that the resolution
- // can be any.
- int min_resolution = -1;
- int max_resolution = -1;
-
- // the scan method used with the sensor
- ScanMethod method = ScanMethod::FLATBED;
-
- // CCD may present itself as half or quarter-size CCD on certain resolutions
- int ccd_size_divisor = 1;
-
- int black_pixels = 0;
- // value of the dummy register
- int dummy_pixel = 0;
- // last pixel of CCD margin at optical resolution
- int CCD_start_xoffset = 0;
- // total pixels used by the sensor
- int sensor_pixels = 0;
- // TA CCD target code (reference gain)
- int fau_gain_white_ref = 0;
- // CCD target code (reference gain)
- int gain_white_ref = 0;
-
- // red, green and blue initial exposure values
- SensorExposure exposure;
-
- int exposure_lperiod = -1;
-
- GenesysRegisterSettingSet custom_regs;
- GenesysRegisterSettingSet custom_fe_regs;
-
- // red, green and blue gamma coefficient for default gamma tables
- AssignableArray<float, 3> gamma;
-
- int get_ccd_size_divisor_for_dpi(int xres) const
- {
- if (ccd_size_divisor >= 4 && xres * 4 <= optical_res) {
- return 4;
- }
- if (ccd_size_divisor >= 2 && xres * 2 <= optical_res) {
- return 2;
- }
- return 1;
- }
-
- bool operator==(const Genesys_Sensor& other) const
- {
- return sensor_id == other.sensor_id &&
- optical_res == other.optical_res &&
- min_resolution == other.min_resolution &&
- max_resolution == other.max_resolution &&
- method == other.method &&
- ccd_size_divisor == other.ccd_size_divisor &&
- black_pixels == other.black_pixels &&
- dummy_pixel == other.dummy_pixel &&
- CCD_start_xoffset == other.CCD_start_xoffset &&
- sensor_pixels == other.sensor_pixels &&
- fau_gain_white_ref == other.fau_gain_white_ref &&
- gain_white_ref == other.gain_white_ref &&
- exposure.blue == other.exposure.blue &&
- exposure.green == other.exposure.green &&
- exposure.red == other.exposure.red &&
- exposure_lperiod == other.exposure_lperiod &&
- custom_regs == other.custom_regs &&
- custom_fe_regs == other.custom_fe_regs &&
- gamma == other.gamma;
- }
-};
-
-template<class Stream>
-void serialize(Stream& str, Genesys_Sensor& x)
-{
- serialize(str, x.sensor_id);
- serialize(str, x.optical_res);
- serialize(str, x.min_resolution);
- serialize(str, x.max_resolution);
- serialize(str, x.method);
- serialize(str, x.ccd_size_divisor);
- serialize(str, x.black_pixels);
- serialize(str, x.dummy_pixel);
- serialize(str, x.CCD_start_xoffset);
- serialize(str, x.sensor_pixels);
- serialize(str, x.fau_gain_white_ref);
- serialize(str, x.gain_white_ref);
- serialize_newline(str);
- serialize(str, x.exposure.blue);
- serialize(str, x.exposure.green);
- serialize(str, x.exposure.red);
- serialize(str, x.exposure_lperiod);
- serialize_newline(str);
- serialize(str, x.custom_regs);
- serialize_newline(str);
- serialize(str, x.custom_fe_regs);
- serialize_newline(str);
- serialize(str, x.gamma);
-}
-
-struct Genesys_Gpo
-{
- Genesys_Gpo() = default;
-
- Genesys_Gpo(uint8_t id, const std::array<uint8_t, 2>& v, const std::array<uint8_t, 2>& e)
- {
- gpo_id = id;
- value[0] = v[0];
- value[1] = v[1];
- enable[0] = e[0];
- enable[1] = e[1];
- }
-
- // Genesys_Gpo
- uint8_t gpo_id = 0;
-
- // registers 0x6c and 0x6d on GL841, GL842, GL843, GL846, GL848 and possibly others
- uint8_t value[2] = { 0, 0 };
-
- // registers 0x6e and 0x6f on GL841, GL842, GL843, GL846, GL848 and possibly others
- uint8_t enable[2] = { 0, 0 };
-};
-
-struct Genesys_Motor_Slope
-{
- Genesys_Motor_Slope() = default;
- Genesys_Motor_Slope(int p_maximum_start_speed, int p_maximum_speed, int p_minimum_steps,
- float p_g) :
- maximum_start_speed(p_maximum_start_speed),
- maximum_speed(p_maximum_speed),
- minimum_steps(p_minimum_steps),
- g(p_g)
- {}
-
- // maximum speed allowed when accelerating from standstill. Unit: pixeltime/step
- int maximum_start_speed = 0;
- // maximum speed allowed. Unit: pixeltime/step
- int maximum_speed = 0;
- // number of steps used for default curve
- int minimum_steps = 0;
-
- /* power for non-linear acceleration curves.
- vs*(1-i^g)+ve*(i^g) where
- vs = start speed, ve = end speed,
- i = 0.0 for first entry and i = 1.0 for last entry in default table
- */
- float g = 0;
-};
-
-
-struct Genesys_Motor
-{
- Genesys_Motor() = default;
- Genesys_Motor(uint8_t p_motor_id, int p_base_ydpi, int p_optical_ydpi, int p_max_step_type,
- int p_power_mode_count,
- const std::vector<std::vector<Genesys_Motor_Slope>>& p_slopes) :
- motor_id(p_motor_id),
- base_ydpi(p_base_ydpi),
- optical_ydpi(p_optical_ydpi),
- max_step_type(p_max_step_type),
- power_mode_count(p_power_mode_count),
- slopes(p_slopes)
- {}
-
- // id of the motor description
- uint8_t motor_id = 0;
- // motor base steps. Unit: 1/inch
- int base_ydpi = 0;
- // maximum resolution in y-direction. Unit: 1/inch
- int optical_ydpi = 0;
- // maximum step type. 0-2
- int max_step_type = 0;
- // number of power modes
- int power_mode_count = 0;
- // slopes to derive individual slopes from
- std::vector<std::vector<Genesys_Motor_Slope>> slopes;
-};
-
-typedef enum Genesys_Color_Order
-{
- COLOR_ORDER_RGB,
- COLOR_ORDER_BGR
-}
-Genesys_Color_Order;
-
-
-#define MAX_RESOLUTIONS 13
-#define MAX_DPI 4
-
-#define GENESYS_GL646 646
-#define GENESYS_GL841 841
-#define GENESYS_GL843 843
-#define GENESYS_GL845 845
-#define GENESYS_GL846 846
-#define GENESYS_GL847 847
-#define GENESYS_GL848 848
-#define GENESYS_GL123 123
-#define GENESYS_GL124 124
-
-enum Genesys_Model_Type
-{
- MODEL_UMAX_ASTRA_4500 = 0,
- MODEL_CANON_LIDE_50,
- MODEL_PANASONIC_KV_SS080,
- MODEL_HP_SCANJET_4850C,
- MODEL_HP_SCANJET_G4010,
- MODEL_HP_SCANJET_G4050,
- MODEL_CANON_CANOSCAN_4400F,
- MODEL_CANON_CANOSCAN_8400F,
- MODEL_CANON_CANOSCAN_8600F,
- MODEL_CANON_LIDE_100,
- MODEL_CANON_LIDE_110,
- MODEL_CANON_LIDE_120,
- MODEL_CANON_LIDE_210,
- MODEL_CANON_LIDE_220,
- MODEL_CANON_CANOSCAN_5600F,
- MODEL_CANON_LIDE_700F,
- MODEL_CANON_LIDE_200,
- MODEL_CANON_LIDE_60,
- MODEL_CANON_LIDE_80,
- MODEL_HP_SCANJET_2300C,
- MODEL_HP_SCANJET_2400C,
- MODEL_VISIONEER_STROBE_XP200,
- MODEL_HP_SCANJET_3670C,
- MODEL_PLUSTEK_OPTICPRO_ST12,
- MODEL_PLUSTEK_OPTICPRO_ST24,
- MODEL_MEDION_MD5345,
- MODEL_VISIONEER_STROBE_XP300,
- MODEL_SYSCAN_DOCKETPORT_665,
- MODEL_VISIONEER_ROADWARRIOR,
- MODEL_SYSCAN_DOCKETPORT_465,
- MODEL_VISIONEER_STROBE_XP100_REVISION3,
- MODEL_PENTAX_DSMOBILE_600,
- MODEL_SYSCAN_DOCKETPORT_467,
- MODEL_SYSCAN_DOCKETPORT_685,
- MODEL_SYSCAN_DOCKETPORT_485,
- MODEL_DCT_DOCKETPORT_487,
- MODEL_VISIONEER_7100,
- MODEL_XEROX_2400,
- MODEL_XEROX_TRAVELSCANNER_100,
- MODEL_PLUSTEK_OPTICPRO_3600,
- MODEL_HP_SCANJET_N6310,
- MODEL_PLUSTEK_OPTICBOOK_3800,
- MODEL_CANON_IMAGE_FORMULA_101
-};
-
-enum Genesys_Dac_Type
-{
- DAC_WOLFSON_UMAX = 0,
- DAC_WOLFSON_ST12,
- DAC_WOLFSON_ST24,
- DAC_WOLFSON_5345,
- DAC_WOLFSON_HP2400,
- DAC_WOLFSON_HP2300,
- DAC_CANONLIDE35,
- DAC_AD_XP200,
- DAC_WOLFSON_XP300,
- DAC_WOLFSON_HP3670,
- DAC_WOLFSON_DSM600,
- DAC_CANONLIDE200,
- DAC_KVSS080,
- DAC_G4050,
- DAC_CANONLIDE110,
- DAC_PLUSTEK_3600,
- DAC_CANONLIDE700,
- DAC_CS8400F,
- DAC_CS8600F,
- DAC_IMG101,
- DAC_PLUSTEK3800,
- DAC_CANONLIDE80,
- DAC_CANONLIDE120
-};
-
-enum Genesys_Sensor_Type
-{
- CCD_UMAX = 0,
- CCD_ST12, // SONY ILX548: 5340 Pixel ???
- CCD_ST24, // SONY ILX569: 10680 Pixel ???
- CCD_5345,
- CCD_HP2400,
- CCD_HP2300,
- CCD_CANONLIDE35,
- CIS_XP200, // CIS sensor for Strobe XP200,
- CCD_HP3670,
- CCD_DP665,
- CCD_ROADWARRIOR,
- CCD_DSMOBILE600,
- CCD_XP300,
- CCD_DP685,
- CIS_CANONLIDE200,
- CIS_CANONLIDE100,
- CCD_KVSS080,
- CCD_G4050,
- CIS_CANONLIDE110,
- CCD_PLUSTEK_3600,
- CCD_HP_N6310,
- CIS_CANONLIDE700,
- CCD_CS4400F,
- CCD_CS8400F,
- CCD_CS8600F,
- CCD_IMG101,
- CCD_PLUSTEK3800,
- CIS_CANONLIDE210,
- CIS_CANONLIDE80,
- CIS_CANONLIDE220,
- CIS_CANONLIDE120,
-};
-
-enum Genesys_Gpo_Type
-{
- GPO_UMAX,
- GPO_ST12,
- GPO_ST24,
- GPO_5345,
- GPO_HP2400,
- GPO_HP2300,
- GPO_CANONLIDE35,
- GPO_XP200,
- GPO_XP300,
- GPO_HP3670,
- GPO_DP665,
- GPO_DP685,
- GPO_CANONLIDE200,
- GPO_KVSS080,
- GPO_G4050,
- GPO_CANONLIDE110,
- GPO_PLUSTEK_3600,
- GPO_CANONLIDE210,
- GPO_HP_N6310,
- GPO_CANONLIDE700,
- GPO_CS4400F,
- GPO_CS8400F,
- GPO_CS8600F,
- GPO_IMG101,
- GPO_PLUSTEK3800,
- GPO_CANONLIDE80,
- GPO_CANONLIDE120
-};
-
-enum Genesys_Motor_Type
-{
- MOTOR_UMAX = 0,
- MOTOR_5345,
- MOTOR_ST24,
- MOTOR_HP2400,
- MOTOR_HP2300,
- MOTOR_CANONLIDE35,
- MOTOR_XP200,
- MOTOR_XP300,
- MOTOR_HP3670,
- MOTOR_DP665,
- MOTOR_ROADWARRIOR,
- MOTOR_DSMOBILE_600,
- MOTOR_CANONLIDE200,
- MOTOR_CANONLIDE100,
- MOTOR_KVSS080,
- MOTOR_G4050,
- MOTOR_CANONLIDE110,
- MOTOR_PLUSTEK_3600,
- MOTOR_CANONLIDE700,
- MOTOR_CS8400F,
- MOTOR_CS8600F,
- MOTOR_IMG101,
- MOTOR_PLUSTEK3800,
- MOTOR_CANONLIDE210,
- MOTOR_CANONLIDE80,
- MOTOR_CANONLIDE120
-};
-
-/* Forward typedefs */
-typedef struct Genesys_Device Genesys_Device;
-struct Genesys_Scanner;
-typedef struct Genesys_Calibration_Cache Genesys_Calibration_Cache;
-
-/**
- * Scanner command set description.
- *
- * This description contains parts which are common to all scanners with the
- * same command set, but may have different optical resolution and other
- * parameters.
- */
-typedef struct Genesys_Command_Set
-{
- /** @name Identification */
- /*@{ */
-
- /** Name of this command set */
- SANE_String_Const name;
-
- /*@} */
-
- bool (*needs_home_before_init_regs_for_scan) (Genesys_Device* dev);
-
- /** For ASIC initialization */
- SANE_Status (*init) (Genesys_Device * dev);
-
- SANE_Status (*init_regs_for_warmup) (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs,
- int *channels, int *total_size);
- SANE_Status (*init_regs_for_coarse_calibration) (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs);
- SANE_Status (*init_regs_for_shading) (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs);
- SANE_Status (*init_regs_for_scan) (Genesys_Device * dev, const Genesys_Sensor& sensor);
-
- SANE_Bool (*get_filter_bit) (Genesys_Register_Set * reg);
- SANE_Bool (*get_lineart_bit) (Genesys_Register_Set * reg);
- SANE_Bool (*get_bitset_bit) (Genesys_Register_Set * reg);
- SANE_Bool (*get_gain4_bit) (Genesys_Register_Set * reg);
- SANE_Bool (*get_fast_feed_bit) (Genesys_Register_Set * reg);
-
- SANE_Bool (*test_buffer_empty_bit) (SANE_Byte val);
- SANE_Bool (*test_motor_flag_bit) (SANE_Byte val);
-
- SANE_Status (*set_fe) (Genesys_Device * dev, const Genesys_Sensor& sensor, uint8_t set);
- SANE_Status (*set_powersaving) (Genesys_Device * dev, int delay);
- SANE_Status (*save_power) (Genesys_Device * dev, SANE_Bool enable);
-
- SANE_Status (*begin_scan) (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set * regs,
- SANE_Bool start_motor);
- SANE_Status (*end_scan) (Genesys_Device * dev,
- Genesys_Register_Set * regs,
- SANE_Bool check_stop);
-
- /**
- * Send gamma tables to ASIC
- */
- SANE_Status (*send_gamma_table) (Genesys_Device * dev, const Genesys_Sensor& sensor);
-
- SANE_Status (*search_start_position) (Genesys_Device * dev);
- SANE_Status (*offset_calibration) (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs);
- SANE_Status (*coarse_gain_calibration) (Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, int dpi);
- SANE_Status (*led_calibration) (Genesys_Device * dev, Genesys_Sensor& sensor,
- Genesys_Register_Set& regs);
-
- void (*wait_for_motor_stop) (Genesys_Device* dev);
- SANE_Status (*slow_back_home) (Genesys_Device * dev, SANE_Bool wait_until_home);
- SANE_Status (*rewind) (Genesys_Device * dev);
-
- SANE_Status (*bulk_write_register) (Genesys_Device * dev,
- Genesys_Register_Set& regs);
-
- SANE_Status (*bulk_write_data) (Genesys_Device * dev, uint8_t addr,
- uint8_t * data, size_t len);
-
- SANE_Status (*bulk_read_data) (Genesys_Device * dev, uint8_t addr,
- uint8_t * data, size_t len);
-
- // Updates hardware sensor information in Genesys_Scanner.val[].
- SANE_Status (*update_hardware_sensors) (struct Genesys_Scanner * s);
-
- /* functions for sheetfed scanners */
- /**
- * load document into scanner
- */
- SANE_Status (*load_document) (Genesys_Device * dev);
- /**
- * detects is the scanned document has left scanner. In this
- * case it updates the amount of data to read and set up
- * flags in the dev struct
- */
- SANE_Status (*detect_document_end) (Genesys_Device * dev);
- /**
- * eject document from scanner
- */
- SANE_Status (*eject_document) (Genesys_Device * dev);
- /**
- * search for an black or white area in forward or reverse
- * direction */
- SANE_Status (*search_strip) (Genesys_Device * dev, const Genesys_Sensor& sensor,
- SANE_Bool forward, SANE_Bool black);
-
- bool (*is_compatible_calibration) (Genesys_Device* dev, const Genesys_Sensor& sensor,
- Genesys_Calibration_Cache* cache, SANE_Bool for_overwrite);
-
- /* functions for transparency adapter */
- /**
- * move scanning head to transparency adapter
- */
- SANE_Status (*move_to_ta) (Genesys_Device * dev);
-
- /**
- * write shading data calibration to ASIC
- */
- SANE_Status (*send_shading_data) (Genesys_Device * dev, const Genesys_Sensor& sensor,
- uint8_t * data, int size);
-
- // calculate current scan setup
- void (*calculate_current_setup) (Genesys_Device * dev, const Genesys_Sensor& sensor);
-
- /**
- * cold boot init function
- */
- SANE_Status (*asic_boot) (Genesys_Device * dev, SANE_Bool cold);
-
-} Genesys_Command_Set;
-
-/** @brief structure to describe a scanner model
- * This structure describes a model. It is composed of information on the
- * sensor, the motor, scanner geometry and flags to drive operation.
- */
-typedef struct Genesys_Model
-{
- SANE_String_Const name;
- SANE_String_Const vendor;
- SANE_String_Const model;
- SANE_Int model_id;
-
- SANE_Int asic_type; /* ASIC type gl646 or gl841 */
- Genesys_Command_Set *cmd_set; /* pointers to low level functions */
-
- SANE_Int xdpi_values[MAX_RESOLUTIONS]; /* possible x resolutions */
- SANE_Int ydpi_values[MAX_RESOLUTIONS]; /* possible y resolutions */
- SANE_Int bpp_gray_values[MAX_DPI]; /* possible depths in gray mode */
- SANE_Int bpp_color_values[MAX_DPI]; /* possible depths in color mode */
-
- SANE_Fixed x_offset; /* Start of scan area in mm */
- SANE_Fixed y_offset; /* Start of scan area in mm (Amount of
- feeding needed to get to the medium) */
- SANE_Fixed x_size; /* Size of scan area in mm */
- SANE_Fixed y_size; /* Size of scan area in mm */
-
- SANE_Fixed y_offset_calib; /* Start of white strip in mm */
- SANE_Fixed x_offset_mark; /* Start of black mark in mm */
-
- SANE_Fixed x_offset_ta; /* Start of scan area in TA mode in mm */
- SANE_Fixed y_offset_ta; /* Start of scan area in TA mode in mm */
- SANE_Fixed x_size_ta; /* Size of scan area in TA mode in mm */
- SANE_Fixed y_size_ta; /* Size of scan area in TA mode in mm */
-
- SANE_Fixed y_offset_calib_ta; /* Start of white strip in TA mode in mm */
-
- SANE_Fixed post_scan; /* Size of scan area after paper sensor stops
- sensing document in mm */
- SANE_Fixed eject_feed; /* Amount of feeding needed to eject document
- after finishing scanning in mm */
-
- /* Line-distance correction (in pixel at optical_ydpi) for CCD scanners */
- SANE_Int ld_shift_r; /* red */
- SANE_Int ld_shift_g; /* green */
- SANE_Int ld_shift_b; /* blue */
-
- Genesys_Color_Order line_mode_color_order; /* Order of the CCD/CIS colors */
-
- SANE_Bool is_cis; /* Is this a CIS or CCD scanner? */
- SANE_Bool is_sheetfed; /* Is this sheetfed scanner? */
-
- SANE_Int ccd_type; /* which SENSOR type do we have ? */
- SANE_Int dac_type; /* which DAC do we have ? */
- SANE_Int gpo_type; /* General purpose output type */
- SANE_Int motor_type; /* stepper motor type */
- SANE_Word flags; /* Which hacks are needed for this scanner? */
- SANE_Word buttons; /* Button flags, described existing buttons for the model */
- /*@} */
- SANE_Int shading_lines; /* how many lines are used for shading calibration */
- SANE_Int shading_ta_lines; // how many lines are used for shading calibration in TA mode
- SANE_Int search_lines; /* how many lines are used to search start position */
-} Genesys_Model;
-
-struct Genesys_Settings
-{
- ScanMethod scan_method = ScanMethod::FLATBED;
- ScanColorMode scan_mode = ScanColorMode::LINEART;
-
- // horizontal dpi
- int xres = 0;
- // vertical dpi
- int yres = 0;
-
- //x start on scan table in mm
- double tl_x = 0;
- // y start on scan table in mm
- double tl_y = 0;
-
- // number of lines at scan resolution
- unsigned int lines = 0;
- // number of pixels at scan resolution
- unsigned int pixels = 0;
-
- // bit depth of the scan
- unsigned int depth = 0;
-
- ColorFilter color_filter = ColorFilter::NONE;
-
- // true if scan is true gray, false if monochrome scan
- int true_gray = 0;
-
- // lineart threshold
- int threshold = 0;
-
- // lineart threshold curve for dynamic rasterization
- int threshold_curve = 0;
-
- // Disable interpolation for xres<yres
- int disable_interpolation = 0;
-
- // true is lineart is generated from gray data by the dynamic rasterization algoright
- int dynamic_lineart = 0;
-
- // value for contrast enhancement in the [-100..100] range
- int contrast = 0;
-
- // value for brightness enhancement in the [-100..100] range
- int brightness = 0;
-
- // cache entries expiration time
- int expiration_time = 0;
-};
-
-struct SetupParams {
-
- static constexpr unsigned NOT_SET = std::numeric_limits<unsigned>::max();
-
- // resolution in x direction
- unsigned xres = NOT_SET;
- // resolution in y direction
- unsigned yres = NOT_SET;
- // start pixel in X direction, from dummy_pixel + 1
- float startx = -1;
- // start pixel in Y direction, counted according to base_ydpi
- float starty = -1;
- // the number of pixels in X direction
- unsigned pixels = NOT_SET;
- // the number of pixels in Y direction
- unsigned lines = NOT_SET;
- // the depth of the scan in bits. Allowed are 1, 8, 16
- unsigned depth = NOT_SET;
- // the number of channels
- unsigned channels = NOT_SET;
-
- ScanMethod scan_method = static_cast<ScanMethod>(NOT_SET);
-
- ScanColorMode scan_mode = static_cast<ScanColorMode>(NOT_SET);
-
- ColorFilter color_filter = static_cast<ColorFilter>(NOT_SET);
-
- unsigned flags = NOT_SET;
-
- void assert_valid() const
- {
- if (xres == NOT_SET || yres == NOT_SET || startx < 0 || starty < 0 ||
- pixels == NOT_SET || lines == NOT_SET ||depth == NOT_SET || channels == NOT_SET ||
- scan_method == static_cast<ScanMethod>(NOT_SET) ||
- scan_mode == static_cast<ScanColorMode>(NOT_SET) ||
- color_filter == static_cast<ColorFilter>(NOT_SET) ||
- flags == NOT_SET)
- {
- throw std::runtime_error("SetupParams are not valid");
- }
- }
-
- bool operator==(const SetupParams& other) const
- {
- return xres == other.xres &&
- yres == other.yres &&
- startx == other.startx &&
- starty == other.starty &&
- pixels == other.pixels &&
- lines == other.lines &&
- depth == other.depth &&
- channels == other.channels &&
- scan_method == other.scan_method &&
- scan_mode == other.scan_mode &&
- color_filter == other.color_filter &&
- flags == other.flags;
- }
-};
-
-template<class Stream>
-void serialize(Stream& str, SetupParams& x)
-{
- serialize(str, x.xres);
- serialize(str, x.yres);
- serialize(str, x.startx);
- serialize(str, x.starty);
- serialize(str, x.pixels);
- serialize(str, x.lines);
- serialize(str, x.depth);
- serialize(str, x.channels);
- serialize(str, x.scan_method);
- serialize(str, x.scan_mode);
- serialize(str, x.color_filter);
- serialize(str, x.flags);
-}
-
-struct Genesys_Current_Setup
-{
- // params used for this setup
- SetupParams params;
-
- // pixel count expected from scanner
- int pixels = 0;
- // line count expected from scanner
- int lines = 0;
- // depth expected from scanner
- int depth = 0;
- // channel count expected from scanner
- int channels = 0;
-
- // used exposure time
- int exposure_time = 0;
- // used xres
- float xres = 0;
- // used yres
- float yres = 0;
- // half ccd mode
- unsigned ccd_size_divisor = 1;
- SANE_Int stagger = 0;
- // max shift of any ccd component, including staggered pixels
- SANE_Int max_shift = 0;
-
- bool operator==(const Genesys_Current_Setup& other) const
- {
- return params == other.params &&
- pixels == other.pixels &&
- lines == other.lines &&
- depth == other.depth &&
- channels == other.channels &&
- exposure_time == other.exposure_time &&
- xres == other.xres &&
- yres == other.yres &&
- ccd_size_divisor == other.ccd_size_divisor &&
- stagger == other.stagger &&
- max_shift == other.max_shift;
- }
-};
-
-template<class Stream>
-void serialize(Stream& str, Genesys_Current_Setup& x)
-{
- serialize(str, x.params);
- serialize_newline(str);
- serialize(str, x.pixels);
- serialize(str, x.lines);
- serialize(str, x.depth);
- serialize(str, x.channels);
- serialize(str, x.exposure_time);
- serialize(str, x.xres);
- serialize(str, x.yres);
- serialize(str, x.ccd_size_divisor);
- serialize(str, x.stagger);
- serialize(str, x.max_shift);
-}
-
-struct Genesys_Buffer
-{
- Genesys_Buffer() = default;
-
- size_t size() const { return buffer_.size(); }
- size_t avail() const { return avail_; }
- size_t pos() const { return pos_; }
-
- // TODO: refactor code that uses this function to no longer use it
- void set_pos(size_t pos) { pos_ = pos; }
-
- void alloc(size_t size);
- void clear();
-
- void reset();
-
- uint8_t* get_write_pos(size_t size);
- uint8_t* get_read_pos(); // TODO: mark as const
-
- void produce(size_t size);
- void consume(size_t size);
-
-private:
- std::vector<uint8_t> buffer_;
- // current position in read buffer
- size_t pos_ = 0;
- // data bytes currently in buffer
- size_t avail_ = 0;
-};
-
-struct Genesys_Calibration_Cache
-{
- Genesys_Calibration_Cache() = default;
- ~Genesys_Calibration_Cache() = default;
-
- // used to check if entry is compatible
- Genesys_Current_Setup used_setup;
- time_t last_calibration = 0;
-
- Genesys_Frontend frontend;
- Genesys_Sensor sensor;
-
- size_t calib_pixels = 0;
- size_t calib_channels = 0;
- size_t average_size = 0;
- std::vector<uint8_t> white_average_data;
- std::vector<uint8_t> dark_average_data;
-
- bool operator==(const Genesys_Calibration_Cache& other) const
- {
- return used_setup == other.used_setup &&
- last_calibration == other.last_calibration &&
- frontend == other.frontend &&
- sensor == other.sensor &&
- calib_pixels == other.calib_pixels &&
- calib_channels == other.calib_channels &&
- average_size == other.average_size &&
- white_average_data == other.white_average_data &&
- dark_average_data == other.dark_average_data;
- }
-};
-
-template<class Stream>
-void serialize(Stream& str, Genesys_Calibration_Cache& x)
-{
- serialize(str, x.used_setup);
- serialize_newline(str);
- serialize(str, x.last_calibration);
- serialize_newline(str);
- serialize(str, x.frontend);
- serialize_newline(str);
- serialize(str, x.sensor);
- serialize_newline(str);
- serialize(str, x.calib_pixels);
- serialize(str, x.calib_channels);
- serialize(str, x.average_size);
- serialize_newline(str);
- serialize(str, x.white_average_data);
- serialize_newline(str);
- serialize(str, x.dark_average_data);
-}
-
-/**
- * Describes the current device status for the backend
- * session. This should be more accurately called
- * Genesys_Session .
- */
-struct Genesys_Device
-{
- Genesys_Device() = default;
- ~Genesys_Device();
-
- using Calibration = std::vector<Genesys_Calibration_Cache>;
-
- // frees commonly used data
- void clear();
-
- UsbDevice usb_dev;
- SANE_Word vendorId = 0; /**< USB vendor identifier */
- SANE_Word productId = 0; /**< USB product identifier */
-
- // USB mode:
- // 0: not set
- // 1: USB 1.1
- // 2: USB 2.0
- SANE_Int usb_mode = 0;
-
- SANE_String file_name = nullptr;
- std::string calib_file;
-
- // if enabled, no calibration data will be loaded or saved to files
- SANE_Int force_calibration = 0;
- Genesys_Model *model = nullptr;
-
- Genesys_Register_Set reg;
- Genesys_Register_Set calib_reg;
- Genesys_Settings settings;
- Genesys_Frontend frontend, frontend_initial;
- Genesys_Gpo gpo;
- Genesys_Motor motor;
- uint8_t control[6] = {};
- time_t init_date = 0;
-
- size_t average_size = 0;
- // number of pixels used during shading calibration
- size_t calib_pixels = 0;
- // number of lines used during shading calibration
- size_t calib_lines = 0;
- size_t calib_channels = 0;
- size_t calib_resolution = 0;
- // bytes to read from USB when calibrating. If 0, this is not set
- size_t calib_total_bytes_to_read = 0;
- // certain scanners support much higher resolution when scanning transparency, but we can't
- // read whole width of the scanner as a single line at that resolution. Thus for stuff like
- // calibration we want to read only the possible calibration area.
- size_t calib_pixels_offset = 0;
-
- // gamma overrides. If a respective array is not empty then it means that the gamma for that
- // color is overridden.
- std::vector<uint16_t> gamma_override_tables[3];
-
- std::vector<uint8_t> white_average_data;
- std::vector<uint8_t> dark_average_data;
- uint16_t dark[3] = {};
-
- SANE_Bool already_initialized = 0;
- SANE_Int scanhead_position_in_steps = 0;
- SANE_Int lamp_off_time = 0;
-
- SANE_Bool read_active = 0;
- // signal wether the park command has been issued
- SANE_Bool parking = 0;
-
- // for sheetfed scanner's, is TRUE when there is a document in the scanner
- SANE_Bool document = 0;
-
- SANE_Bool needs_home_ta = 0;
-
- Genesys_Buffer read_buffer;
- Genesys_Buffer lines_buffer;
- Genesys_Buffer shrink_buffer;
- Genesys_Buffer out_buffer;
-
- // buffer for digital lineart from gray data
- Genesys_Buffer binarize_buffer = {};
- // local buffer for gray data during dynamix lineart
- Genesys_Buffer local_buffer = {};
-
- // bytes to read from scanner
- size_t read_bytes_left = 0;
-
- // total bytes read sent to frontend
- size_t total_bytes_read = 0;
- // total bytes read to be sent to frontend
- size_t total_bytes_to_read = 0;
- // asic's word per line
- size_t wpl = 0;
-
- // contains the real used values
- Genesys_Current_Setup current_setup;
-
- // look up table used in dynamic rasterization
- unsigned char lineart_lut[256] = {};
-
- Calibration calibration_cache;
-
- // used red line-distance shift
- SANE_Int ld_shift_r = 0;
- // used green line-distance shift
- SANE_Int ld_shift_g = 0;
- // used blue line-distance shift
- SANE_Int ld_shift_b = 0;
- // number of segments composing the sensor
- int segnb = 0;
- // number of lines used in line interpolation
- int line_interp = 0;
- // number of scan lines used during scan
- int line_count = 0;
- // bytes per full scan widthline
- size_t bpl = 0;
- // bytes distance between an odd and an even pixel
- size_t dist = 0;
- // number of even pixels
- size_t len = 0;
- // current pixel position within sub window
- size_t cur = 0;
- // number of bytes to skip at start of line
- size_t skip = 0;
-
- // array describing the order of the sub-segments of the sensor
- size_t* order = nullptr;
-
- // buffer to handle even/odd data
- Genesys_Buffer oe_buffer = {};
-
- // when true the scanned picture is first buffered to allow software image enhancements
- SANE_Bool buffer_image = 0;
-
- // image buffer where the scanned picture is stored
- std::vector<uint8_t> img_buffer;
-
- // binary logger file
- FILE *binary = nullptr;
-};
-
-typedef struct Genesys_USB_Device_Entry
-{
- SANE_Word vendor; /**< USB vendor identifier */
- SANE_Word product; /**< USB product identifier */
- Genesys_Model *model; /**< Scanner model information */
-} Genesys_USB_Device_Entry;
-
-/**
- * structure for motor database
- */
-typedef struct {
- int motor_type; /**< motor id */
- int exposure; /**< exposure for the slope table */
- int step_type; /**< default step type for given exposure */
- uint32_t *table; // 0-terminated slope table at full step (i.e. step_type == 0)
-} Motor_Profile;
-
-#define FULL_STEP 0
-#define HALF_STEP 1
-#define QUARTER_STEP 2
-#define EIGHTH_STEP 3
-
-#define SLOPE_TABLE_SIZE 1024
-
-#define SCAN_TABLE 0 /* table 1 at 0x4000 for gl124 */
-#define BACKTRACK_TABLE 1 /* table 2 at 0x4800 for gl124 */
-#define STOP_TABLE 2 /* table 3 at 0x5000 for gl124 */
-#define FAST_TABLE 3 /* table 4 at 0x5800 for gl124 */
-#define HOME_TABLE 4 /* table 5 at 0x6000 for gl124 */
-
-#define SCAN_FLAG_SINGLE_LINE 0x001
-#define SCAN_FLAG_DISABLE_SHADING 0x002
-#define SCAN_FLAG_DISABLE_GAMMA 0x004
-#define SCAN_FLAG_DISABLE_BUFFER_FULL_MOVE 0x008
-#define SCAN_FLAG_IGNORE_LINE_DISTANCE 0x010
-#define SCAN_FLAG_USE_OPTICAL_RES 0x020
-#define SCAN_FLAG_DISABLE_LAMP 0x040
-#define SCAN_FLAG_DYNAMIC_LINEART 0x080
-#define SCAN_FLAG_CALIBRATION 0x100
-#define SCAN_FLAG_FEEDING 0x200
-#define SCAN_FLAG_USE_XPA 0x400
-#define SCAN_FLAG_ENABLE_LEDADD 0x800
-#define MOTOR_FLAG_AUTO_GO_HOME 0x01
-#define MOTOR_FLAG_DISABLE_BUFFER_FULL_MOVE 0x02
-#define MOTOR_FLAG_FEED 0x04
-#define MOTOR_FLAG_USE_XPA 0x08
-
-/** @name "Optical flags" */
-/*@{ optical flags available when setting up sensor for scan */
-
-#define OPTICAL_FLAG_DISABLE_GAMMA 0x01 /**< disable gamma correction */
-#define OPTICAL_FLAG_DISABLE_SHADING 0x02 /**< disable shading correction */
-#define OPTICAL_FLAG_DISABLE_LAMP 0x04 /**< turn off lamp */
-#define OPTICAL_FLAG_ENABLE_LEDADD 0x08 /**< enable true CIS gray by enabling LED addition */
-#define OPTICAL_FLAG_DISABLE_DOUBLE 0x10 /**< disable automatic x-direction double data expansion */
-#define OPTICAL_FLAG_STAGGER 0x20 /**< disable stagger correction */
-#define OPTICAL_FLAG_USE_XPA 0x40 /**< use XPA lamp rather than regular one */
-
-/*@} */
-
-/*--------------------------------------------------------------------------*/
-/* common functions needed by low level specific functions */
-/*--------------------------------------------------------------------------*/
-
-inline GenesysRegister* sanei_genesys_get_address(Genesys_Register_Set* regs, uint16_t addr)
-{
- auto* ret = regs->find_reg_address(addr);
- if (ret == nullptr) {
- DBG(DBG_error, "%s: failed to find address for register 0x%02x, crash expected !\n",
- __func__, addr);
- }
- return ret;
-}
-
-inline uint8_t sanei_genesys_read_reg_from_set(Genesys_Register_Set* regs, uint16_t address)
-{
- return regs->get8(address);
-}
-
-inline void sanei_genesys_set_reg_from_set(Genesys_Register_Set* regs, uint16_t address,
- uint8_t value)
-{
- regs->set8(address, value);
-}
-
-extern SANE_Status sanei_genesys_init_cmd_set (Genesys_Device * dev);
-
-extern SANE_Status
-sanei_genesys_read_register (Genesys_Device * dev, uint16_t reg, uint8_t * val);
-
-extern SANE_Status
-sanei_genesys_write_register (Genesys_Device * dev, uint16_t reg, uint8_t val);
-
-extern SANE_Status
-sanei_genesys_read_hregister (Genesys_Device * dev, uint16_t reg, uint8_t * val);
-
-extern SANE_Status
-sanei_genesys_write_hregister (Genesys_Device * dev, uint16_t reg, uint8_t val);
-
-extern SANE_Status
-sanei_genesys_bulk_write_register(Genesys_Device * dev,
- Genesys_Register_Set& regs);
-
-extern SANE_Status sanei_genesys_write_0x8c (Genesys_Device * dev, uint8_t index, uint8_t val);
-
-extern unsigned sanei_genesys_get_bulk_max_size(Genesys_Device * dev);
-
-extern SANE_Status sanei_genesys_bulk_read_data(Genesys_Device * dev, uint8_t addr, uint8_t* data,
- size_t len);
-
-extern SANE_Status sanei_genesys_bulk_write_data(Genesys_Device * dev, uint8_t addr, uint8_t* data,
- size_t len);
-
-extern SANE_Status sanei_genesys_get_status (Genesys_Device * dev, uint8_t * status);
-
-extern void sanei_genesys_print_status (uint8_t val);
-
-extern SANE_Status
-sanei_genesys_write_ahb(Genesys_Device* dev, uint32_t addr, uint32_t size, uint8_t * data);
-
-extern void sanei_genesys_init_structs (Genesys_Device * dev);
-
-const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev);
-Genesys_Sensor& sanei_genesys_find_sensor_any_for_write(Genesys_Device* dev);
-const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, int dpi,
- ScanMethod scan_method = ScanMethod::FLATBED);
-Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, int dpi,
- ScanMethod scan_method = ScanMethod::FLATBED);
-
-extern SANE_Status
-sanei_genesys_init_shading_data (Genesys_Device * dev, const Genesys_Sensor& sensor,
- int pixels_per_line);
-
-extern SANE_Status sanei_genesys_read_valid_words (Genesys_Device * dev,
- unsigned int *steps);
-
-extern SANE_Status sanei_genesys_read_scancnt (Genesys_Device * dev,
- unsigned int *steps);
-
-extern SANE_Status sanei_genesys_read_feed_steps (Genesys_Device * dev,
- unsigned int *steps);
-
-void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sensor,
- Genesys_Register_Set& regs, bool set);
-
-void sanei_genesys_set_motor_power(Genesys_Register_Set& regs, bool set);
-
-extern void
-sanei_genesys_calculate_zmode2 (SANE_Bool two_table,
- uint32_t exposure_time,
- uint16_t * slope_table,
- int reg21,
- int move, int reg22, uint32_t * z1,
- uint32_t * z2);
-
-extern void
-sanei_genesys_calculate_zmode (uint32_t exposure_time,
- uint32_t steps_sum,
- uint16_t last_speed, uint32_t feedl,
- uint8_t fastfed, uint8_t scanfed,
- uint8_t fwdstep, uint8_t tgtime,
- uint32_t * z1, uint32_t * z2);
-
-extern SANE_Status
-sanei_genesys_set_buffer_address (Genesys_Device * dev, uint32_t addr);
-
-/** @brief Reads data from frontend register.
- * Reads data from the given frontend register. May be used to query
- * analog frontend status by reading the right register.
- */
-extern SANE_Status
-sanei_genesys_fe_read_data (Genesys_Device * dev, uint8_t addr,
- uint16_t *data);
-/** @brief Write data to frontend register.
- * Writes data to analog frontend register at the given address.
- * The use and address of registers change from model to model.
- */
-extern SANE_Status
-sanei_genesys_fe_write_data (Genesys_Device * dev, uint8_t addr,
- uint16_t data);
-
-extern SANE_Int
-sanei_genesys_exposure_time2 (Genesys_Device * dev,
- float ydpi, int step_type, int endpixel,
- int led_exposure, int power_mode);
-
-extern SANE_Int
-sanei_genesys_exposure_time (Genesys_Device * dev, Genesys_Register_Set * reg,
- int xdpi);
-extern SANE_Int
-sanei_genesys_generate_slope_table (uint16_t * slope_table, unsigned int max_steps,
- unsigned int use_steps, uint16_t stop_at,
- uint16_t vstart, uint16_t vend,
- unsigned int steps, double g,
- unsigned int *used_steps, unsigned int *vfinal);
-
-extern SANE_Int
-sanei_genesys_create_slope_table (Genesys_Device * dev,
- uint16_t * slope_table, int steps,
- int step_type, int exposure_time,
- SANE_Bool same_speed, double yres,
- int power_mode);
-
-SANE_Int
-sanei_genesys_create_slope_table3 (Genesys_Device * dev,
- uint16_t * slope_table, int max_step,
- unsigned int use_steps,
- int step_type, int exposure_time,
- double yres,
- unsigned int *used_steps,
- unsigned int *final_exposure,
- int power_mode);
-
-void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
- std::vector<uint16_t>& gamma_table, float gamma);
-
-std::vector<uint16_t> get_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor,
- int color);
-
-SANE_Status sanei_genesys_send_gamma_table(Genesys_Device * dev, const Genesys_Sensor& sensor);
-
-extern SANE_Status sanei_genesys_start_motor (Genesys_Device * dev);
-
-extern SANE_Status sanei_genesys_stop_motor (Genesys_Device * dev);
-
-extern SANE_Status
-sanei_genesys_search_reference_point(Genesys_Device * dev, Genesys_Sensor& sensor,
- uint8_t * data,
- int start_pixel, int dpi, int width,
- int height);
-
-extern SANE_Status sanei_genesys_write_file(const char *filename, uint8_t* data, size_t length);
-
-extern SANE_Status
-sanei_genesys_write_pnm_file (const char *filename, uint8_t * data, int depth,
- int channels, int pixels_per_line, int lines);
-
-extern SANE_Status
-sanei_genesys_test_buffer_empty (Genesys_Device * dev, SANE_Bool * empty);
-
-extern SANE_Status
-sanei_genesys_read_data_from_scanner (Genesys_Device * dev, uint8_t * data,
- size_t size);
-
-inline void sanei_genesys_set_double(Genesys_Register_Set* regs, uint16_t addr, uint16_t value)
-{
- regs->set16(addr, value);
-}
-
-inline void sanei_genesys_set_triple(Genesys_Register_Set* regs, uint16_t addr, uint32_t value)
-{
- regs->set24(addr, value);
-}
-
-inline void sanei_genesys_get_double(Genesys_Register_Set* regs, uint16_t addr, uint16_t* value)
-{
- *value = regs->get16(addr);
-}
-
-inline void sanei_genesys_get_triple(Genesys_Register_Set* regs, uint16_t addr, uint32_t* value)
-{
- *value = regs->get24(addr);
-}
-
-inline void sanei_genesys_set_exposure(Genesys_Register_Set& regs, const SensorExposure& exposure)
-{
- regs.set8(0x10, (exposure.red >> 8) & 0xff);
- regs.set8(0x11, exposure.red & 0xff);
- regs.set8(0x12, (exposure.green >> 8) & 0xff);
- regs.set8(0x13, exposure.green & 0xff);
- regs.set8(0x14, (exposure.blue >> 8) & 0xff);
- regs.set8(0x15, exposure.blue & 0xff);
-}
-
-inline uint16_t sanei_genesys_fixup_exposure_value(uint16_t value)
-{
- if ((value & 0xff00) == 0) {
- value |= 0x100;
- }
- if ((value & 0x00ff) == 0) {
- value |= 0x1;
- }
- return value;
-}
-
-inline SensorExposure sanei_genesys_fixup_exposure(SensorExposure exposure)
-{
- exposure.red = sanei_genesys_fixup_exposure_value(exposure.red);
- exposure.green = sanei_genesys_fixup_exposure_value(exposure.green);
- exposure.blue = sanei_genesys_fixup_exposure_value(exposure.blue);
- return exposure;
-}
-
-extern SANE_Status
-sanei_genesys_wait_for_home(Genesys_Device *dev);
-
-extern SANE_Status
-sanei_genesys_asic_init(Genesys_Device *dev, SANE_Bool cold);
-
-int sanei_genesys_compute_dpihw(Genesys_Device *dev, const Genesys_Sensor& sensor, int xres);
-
-int sanei_genesys_compute_dpihw_calibration(Genesys_Device *dev, const Genesys_Sensor& sensor,
- int xres);
-
-extern
-Motor_Profile *sanei_genesys_get_motor_profile(Motor_Profile *motors, int motor_type, int exposure);
-
-extern
-int sanei_genesys_compute_step_type(Motor_Profile *motors, int motor_type, int exposure);
-
-extern
-int sanei_genesys_slope_table(uint16_t *slope, int *steps, int dpi, int exposure, int base_dpi, int step_type, int factor, int motor_type, Motor_Profile *motors);
-
-/** @brief find lowest motor resolution for the device.
- * Parses the resolution list for motor and
- * returns the lowest value.
- * @param dev for which to find the lowest motor resolution
- * @return the lowest available motor resolution for the device
- */
-extern
-int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev);
-
-/** @brief find lowest resolution for the device.
- * Parses the resolution list for motor and sensor and
- * returns the lowest value.
- * @param dev for which to find the lowest resolution
- * @return the lowest available resolution for the device
- */
-extern
-int sanei_genesys_get_lowest_dpi(Genesys_Device *dev);
-
-extern bool
-sanei_genesys_is_compatible_calibration (Genesys_Device * dev, const Genesys_Sensor& sensor,
- Genesys_Calibration_Cache * cache,
- int for_overwrite);
-
-/** @brief compute maximum line distance shift
- * compute maximum line distance shift for the motor and sensor
- * combination. Line distance shift is the distance between different
- * color component of CCD sensors. Since these components aren't at
- * the same physical place, they scan diffrent lines. Software must
- * take this into account to accurately mix color data.
- * @param dev device session to compute max_shift for
- * @param channels number of color channels for the scan
- * @param yres motor resolution used for the scan
- * @param flags scan flags
- * @return 0 or line distance shift
- */
-extern
-int sanei_genesys_compute_max_shift(Genesys_Device *dev,
- int channels,
- int yres,
- int flags);
-
-extern SANE_Status
-sanei_genesys_load_lut (unsigned char * lut,
- int in_bits,
- int out_bits,
- int out_min,
- int out_max,
- int slope,
- int offset);
-
-extern SANE_Status
-sanei_genesys_generate_gamma_buffer(Genesys_Device * dev,
- const Genesys_Sensor& sensor,
- int bits,
- int max,
- int size,
- uint8_t *gamma);
-
-/*---------------------------------------------------------------------------*/
-/* ASIC specific functions declarations */
-/*---------------------------------------------------------------------------*/
-extern SANE_Status sanei_gl646_init_cmd_set (Genesys_Device * dev);
-extern SANE_Status sanei_gl841_init_cmd_set (Genesys_Device * dev);
-extern SANE_Status sanei_gl843_init_cmd_set (Genesys_Device * dev);
-extern SANE_Status sanei_gl846_init_cmd_set (Genesys_Device * dev);
-extern SANE_Status sanei_gl847_init_cmd_set (Genesys_Device * dev);
-extern SANE_Status sanei_gl124_init_cmd_set (Genesys_Device * dev);
-
-// same as usleep, except that it does nothing if testing mode is enabled
-extern void sanei_genesys_usleep(unsigned int useconds);
-
-// same as sanei_genesys_usleep just that the duration is in milliseconds
-extern void sanei_genesys_sleep_ms(unsigned int milliseconds);
-
-void add_function_to_run_at_backend_exit(std::function<void()> function);
-
-// calls functions added via add_function_to_run_at_backend_exit() in reverse order of being
-// added.
-void run_functions_at_backend_exit();
-
-template<class T>
-class StaticInit {
-public:
- StaticInit() = default;
- StaticInit(const StaticInit&) = delete;
- StaticInit& operator=(const StaticInit&) = delete;
-
- template<class... Args>
- void init(Args&& ... args)
- {
- ptr_ = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
- add_function_to_run_at_backend_exit([this](){ deinit(); });
- }
-
- void deinit()
- {
- ptr_.release();
- }
-
- const T* operator->() const { return ptr_.get(); }
- T* operator->() { return ptr_.get(); }
- const T& operator*() const { return *ptr_.get(); }
- T& operator*() { return *ptr_.get(); }
-
-private:
- std::unique_ptr<T> ptr_;
-};
-
-extern StaticInit<std::vector<Genesys_Sensor>> s_sensors;
-void genesys_init_sensor_tables();
-void genesys_init_frontend_tables();
-
-void debug_dump(unsigned level, const Genesys_Settings& settings);
-void debug_dump(unsigned level, const SetupParams& params);
-void debug_dump(unsigned level, const Genesys_Current_Setup& setup);
-
-#endif /* not GENESYS_LOW_H */
diff --git a/backend/gt68xx.c b/backend/gt68xx.c
index fb3bfb4..00190fe 100644
--- a/backend/gt68xx.c
+++ b/backend/gt68xx.c
@@ -752,7 +752,7 @@ init_options (GT68xx_Scanner * s)
/* calibration needed */
s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration";
- s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Need calibration");
+ s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Needs calibration");
s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings");
s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL;
s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE;
@@ -947,25 +947,30 @@ download_firmware_file (GT68xx_Device * dev)
if (strncmp (dev->model->firmware_name, PATH_SEP, 1) != 0)
{
/* probably filename only */
- snprintf (filename, PATH_MAX, "%s%s%s%s%s%s%s",
+ snprintf (filename, sizeof(filename), "%s%s%s%s%s%s%s",
STRINGIFY (PATH_SANE_DATA_DIR),
PATH_SEP, "sane", PATH_SEP, "gt68xx", PATH_SEP,
dev->model->firmware_name);
- snprintf (dirname, PATH_MAX, "%s%s%s%s%s",
+ snprintf (dirname, sizeof(dirname), "%s%s%s%s%s",
STRINGIFY (PATH_SANE_DATA_DIR),
PATH_SEP, "sane", PATH_SEP, "gt68xx");
- strncpy (basename, dev->model->firmware_name, PATH_MAX);
+ strncpy (basename, dev->model->firmware_name, sizeof(basename) - 1);
+ basename[sizeof(basename) - 1] = '\0';
}
else
{
/* absolute path */
char *pos;
- strncpy (filename, dev->model->firmware_name, PATH_MAX);
- strncpy (dirname, dev->model->firmware_name, PATH_MAX);
+ strncpy (filename, dev->model->firmware_name, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ strncpy (dirname, dev->model->firmware_name, sizeof(dirname) - 1);
+ dirname[sizeof(dirname) - 1] = '\0';
+
pos = strrchr (dirname, PATH_SEP[0]);
if (pos)
pos[0] = '\0';
- strncpy (basename, pos + 1, PATH_MAX);
+ strncpy (basename, pos + 1, sizeof(basename) - 1);
+ basename[sizeof(basename) - 1] = '\0';
}
/* first, try to open with exact case */
@@ -994,11 +999,16 @@ download_firmware_file (GT68xx_Device * dev)
{
direntry = readdir (dir);
if (direntry
- && (strncasecmp (direntry->d_name, basename, PATH_MAX) ==
- 0))
+ && (strncasecmp (direntry->d_name, basename, PATH_MAX) == 0))
{
- snprintf (filename, PATH_MAX, "%s%s%s",
- dirname, PATH_SEP, direntry->d_name);
+ int len = snprintf (filename, sizeof(filename), "%s%s%s",
+ dirname, PATH_SEP, direntry->d_name);
+ if ((len < 0) || (len >= (int) sizeof(filename)))
+ {
+ DBG (5, "download_firmware: filepath `%s%s%s' too long\n",
+ dirname, PATH_SEP, direntry->d_name);
+ status = SANE_STATUS_INVAL;
+ }
break;
}
}
diff --git a/backend/hp-option.h b/backend/hp-option.h
index d1795e1..a6da585 100644
--- a/backend/hp-option.h
+++ b/backend/hp-option.h
@@ -118,7 +118,7 @@
# define SANE_NAME_MATRIX_TYPE "matrix-type"
# define SANE_TITLE_MATRIX_TYPE SANE_I18N("Color Matrix")
/* FIXME: better description */
-# define SANE_DESC_MATRIX_TYPE SANE_I18N("Set the scanners color matrix.")
+# define SANE_DESC_MATRIX_TYPE SANE_I18N("Set the scanner's color matrix.")
#endif
#ifndef SANE_NAME_MATRIX_RGB
diff --git a/backend/hp-scl.c b/backend/hp-scl.c
index a7376e6..fae7f97 100644
--- a/backend/hp-scl.c
+++ b/backend/hp-scl.c
@@ -523,8 +523,8 @@ sanei_hp_nonscsi_new (HpScsi * newp, const char * devname, HpConnect connect)
}
/* For SCSI-devices we would have the inquire command here */
- strncpy ((char *)new->inq_data, "\003zzzzzzzHP ------ R000",
- sizeof (new->inq_data));
+ memcpy (new->inq_data, "\003zzzzzzzHP ------ R000",
+ sizeof (new->inq_data));
new->bufp = new->buf + HP_SCSI_CMD_LEN;
new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 );
diff --git a/backend/hp3900_config.c b/backend/hp3900_config.c
index 830243b..dba5302 100644
--- a/backend/hp3900_config.c
+++ b/backend/hp3900_config.c
@@ -1049,7 +1049,7 @@ static SANE_Byte *cfg_motor_resource_get(SANE_Byte *size)
if (rst != NULL)
{
- bzero(rst, sizeof(SANE_Byte) * 32);
+ memset(rst, 0, sizeof(SANE_Byte) * 32);
switch(RTS_Debug->dev_model)
{
diff --git a/backend/hp3900_debug.c b/backend/hp3900_debug.c
index b8cd8f1..7b21c8d 100644
--- a/backend/hp3900_debug.c
+++ b/backend/hp3900_debug.c
@@ -761,7 +761,7 @@ dbg_buffer (SANE_Int level, char *title, SANE_Byte * buffer, SANE_Int size,
snprintf (sline, 80, " BF: ");
else
snprintf (sline, 80, " ");
- bzero (&text, sizeof (text));
+ memset (&text, 0, sizeof (text));
}
data = _B0 (buffer[cont]);
text[col] = (data > 31) ? data : '·';
@@ -776,7 +776,7 @@ dbg_buffer (SANE_Int level, char *title, SANE_Byte * buffer, SANE_Int size,
start + offset - 8);
sline = strcat (sline, sdata);
DBG (level, "%s", sline);
- bzero (sline, 81);
+ memset (sline, 0, 81);
}
}
if (col > 0)
@@ -791,7 +791,7 @@ dbg_buffer (SANE_Int level, char *title, SANE_Byte * buffer, SANE_Int size,
start + offset - 8);
sline = strcat (sline, sdata);
DBG (level, "%s", sline);
- bzero (sline, 81);
+ memset (sline, 0, 81);
}
free (sdata);
}
diff --git a/backend/hp3900_rts8822.c b/backend/hp3900_rts8822.c
index bbd1e38..d76763d 100644
--- a/backend/hp3900_rts8822.c
+++ b/backend/hp3900_rts8822.c
@@ -63,7 +63,7 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string.h> /* bzero() */
+#include <string.h> /* memset() */
#include <time.h> /* clock() */
#include <math.h> /* truncf() */
#include <ctype.h> /* tolower() */
@@ -630,12 +630,12 @@ RTS_Alloc ()
{
SANE_Int rst = OK;
- bzero (dev, sizeof (struct st_device));
+ memset (dev, 0, sizeof (struct st_device));
/* initial registers */
dev->init_regs = malloc (sizeof (SANE_Byte) * RT_BUFFER_LEN);
if (dev->init_regs != NULL)
- bzero (dev->init_regs, sizeof (SANE_Byte) * RT_BUFFER_LEN);
+ memset (dev->init_regs, 0, sizeof (SANE_Byte) * RT_BUFFER_LEN);
else
rst = ERROR;
@@ -643,7 +643,7 @@ RTS_Alloc ()
{
dev->scanning = malloc (sizeof (struct st_scanning));
if (dev->scanning != NULL)
- bzero (dev->scanning, sizeof (struct st_scanning));
+ memset (dev->scanning, 0, sizeof (struct st_scanning));
else
rst = ERROR;
}
@@ -652,7 +652,7 @@ RTS_Alloc ()
{
dev->Reading = malloc (sizeof (struct st_readimage));
if (dev->Reading != NULL)
- bzero (dev->Reading, sizeof (struct st_readimage));
+ memset (dev->Reading, 0, sizeof (struct st_readimage));
else
rst = ERROR;
}
@@ -661,7 +661,7 @@ RTS_Alloc ()
{
dev->Resize = malloc (sizeof (struct st_resize));
if (dev->Resize != NULL)
- bzero (dev->Resize, sizeof (struct st_resize));
+ memset (dev->Resize, 0, sizeof (struct st_resize));
else
rst = ERROR;
}
@@ -670,7 +670,7 @@ RTS_Alloc ()
{
dev->status = malloc (sizeof (struct st_status));
if (dev->status != NULL)
- bzero (dev->status, sizeof (struct st_status));
+ memset (dev->status, 0, sizeof (struct st_status));
else
rst = ERROR;
}
@@ -1255,7 +1255,7 @@ Load_Chipset (struct st_device *dev)
{
SANE_Int model;
- bzero (dev->chipset, sizeof (struct st_chip));
+ memset (dev->chipset, 0, sizeof (struct st_chip));
/* get chipset model of selected scanner */
model = cfg_chipset_model_get (RTS_Debug->dev_model);
@@ -1611,7 +1611,7 @@ RTS_Scanner_SetParams (struct st_device *dev, struct params *param)
compression = FALSE;
/* resetting low level config */
- bzero (&hwdcfg, sizeof (struct st_hwdconfig));
+ memset (&hwdcfg, 0, sizeof (struct st_hwdconfig));
/* setting low level config */
hwdcfg.scantype = scan.scantype;
@@ -1650,7 +1650,7 @@ SetScanParams (struct st_device *dev, SANE_Byte * Regs,
dbg_ScanParams (scancfg);
dbg_hwdcfg (hwdcfg);
- bzero (&mycoords, sizeof (struct st_coords));
+ memset (&mycoords, 0, sizeof (struct st_coords));
/* Copy scancfg to scan2 */
memcpy (&scan2, scancfg, sizeof (struct st_scanparams));
@@ -3675,7 +3675,7 @@ Init_Registers (struct st_device *dev)
DBG (DBG_FNC, "+ Init_Registers:\n");
/* Lee dev->init_regs */
- bzero (dev->init_regs, RT_BUFFER_LEN);
+ memset (dev->init_regs, 0, RT_BUFFER_LEN);
RTS_ReadRegs (dev->usb_handle, dev->init_regs);
Read_FE3E (dev, &v1619);
@@ -4116,7 +4116,7 @@ Lamp_Status_Get (struct st_device *dev, SANE_Byte * flb_lamp,
case RTS8822BL_03A:
*flb_lamp = ((data2 & 0x40) != 0) ? 1 : 0;
*tma_lamp = (((data2 & 0x20) != 0)
- && ((data1 & 0x10) == 1)) ? 1 : 0;
+ && ((data1 & 0x10) != 0)) ? 1 : 0;
break;
default:
if ((_B1 (data1) & 0x10) == 0)
@@ -4825,8 +4825,8 @@ Refs_Analyze_Pattern (struct st_scanparams *scancfg,
if ((scancfg->coord.width - dist) > 1)
{
/* clear buffers */
- bzero (color_sum, sizeof (double) * buffersize);
- bzero (color_dif, sizeof (double) * buffersize);
+ memset (color_sum, 0, sizeof (double) * buffersize);
+ memset (color_dif, 0, sizeof (double) * buffersize);
for (xpos = 0; xpos < scancfg->coord.width; xpos++)
{
@@ -4875,8 +4875,8 @@ Refs_Analyze_Pattern (struct st_scanparams *scancfg,
if ((scancfg->coord.height - dist) > 1)
{
/* clear buffers */
- bzero (color_sum, sizeof (double) * buffersize);
- bzero (color_dif, sizeof (double) * buffersize);
+ memset (color_sum, 0, sizeof (double) * buffersize);
+ memset (color_dif, 0, sizeof (double) * buffersize);
for (ypos = 0; ypos < scancfg->coord.height; ypos++)
{
@@ -4924,8 +4924,8 @@ Refs_Analyze_Pattern (struct st_scanparams *scancfg,
if ((scancfg->coord.width - dist) > 1)
{
/* clear buffers */
- bzero (color_sum, sizeof (double) * buffersize);
- bzero (color_dif, sizeof (double) * buffersize);
+ memset (color_sum, 0, sizeof (double) * buffersize);
+ memset (color_dif, 0, sizeof (double) * buffersize);
for (xpos = 0; xpos < scancfg->coord.width; xpos++)
{
@@ -6188,7 +6188,7 @@ Reading_DestroyBuffers (struct st_device *dev)
dev->scanning->imagebuffer = NULL;
}
- bzero (dev->Reading, sizeof (struct st_readimage));
+ memset (dev->Reading, 0, sizeof (struct st_readimage));
return OK;
}
@@ -6462,7 +6462,7 @@ RTS_ScanCounter_Inc (struct st_device *dev)
break;
default:
/* value is 4 bytes size starting from address 0x21 in lsb format */
- bzero (&somebuffer, sizeof (somebuffer));
+ memset (&somebuffer, 0, sizeof (somebuffer));
somebuffer[4] = 0x0c;
RTS_EEPROM_ReadInteger (dev->usb_handle, 0x21, &idata);
@@ -7786,7 +7786,7 @@ Scan_Read_BufferA (struct st_device *dev, SANE_Int buffer_size, SANE_Int arg2,
opStatus = Reading_Wait (dev, rd->Channels_per_dot,
rd->Channel_size,
iAmount,
- &rd->Bytes_Available, 10, sc);
+ &rd->Bytes_Available, 60, sc);
/* If something fails, perhaps we can read some bytes... */
if (opStatus != OK)
@@ -8072,7 +8072,7 @@ Scan_Start (struct st_device *dev)
dbg_ScanParams (&scancfg);
/* reserva buffer 6 dwords en fa84-fa9f */
- bzero (&hwdcfg, sizeof (struct st_hwdconfig));
+ memset (&hwdcfg, 0, sizeof (struct st_hwdconfig));
/* wait till lamp is at home (should use timeout
windows driver doesn't use it)
@@ -10009,7 +10009,7 @@ Shading_apply (struct st_device *dev, SANE_Byte * Regs,
}
/*3d4c */
- bzero (&calbuffers, sizeof (struct st_cal2));
+ memset (&calbuffers, 0, sizeof (struct st_cal2));
/* If black shading correction is enabled ... */
if ((Regs[0x1cf] & 8) != 0)
@@ -10340,7 +10340,7 @@ RTS_GetImage (struct st_device *dev, SANE_Byte * Regs,
(struct st_hwdconfig *) malloc (sizeof (struct st_hwdconfig));
if (hwdcfg != NULL)
{
- bzero (hwdcfg, sizeof (struct st_hwdconfig));
+ memset (hwdcfg, 0, sizeof (struct st_hwdconfig));
if (((options & 2) != 0) || ((_B1 (options) & 1) != 0))
{
@@ -10404,7 +10404,7 @@ RTS_GetImage (struct st_device *dev, SANE_Byte * Regs,
sizeof (SANE_Byte));
if (myRegs != NULL)
{
- bzero (myRegs,
+ memset (myRegs, 0,
RT_BUFFER_LEN * sizeof (SANE_Byte));
RTS_Setup (dev, myRegs, &scan, hwdcfg,
gain_offset);
@@ -10547,7 +10547,7 @@ Refs_Detect (struct st_device *dev, SANE_Byte * Regs, SANE_Int resolution_x,
*x = *y = 0; /* default */
/* set configuration to scan a little area at the top-left corner */
- bzero (&scancfg, sizeof (struct st_scanparams));
+ memset (&scancfg, 0, sizeof (struct st_scanparams));
scancfg.depth = 8;
scancfg.colormode = CM_GRAY;
scancfg.channel = CL_RED;
@@ -10588,7 +10588,7 @@ Refs_Detect (struct st_device *dev, SANE_Byte * Regs, SANE_Int resolution_x,
pwmlamplevel = 0;
Lamp_PWM_use (dev, 1);
- bzero (&gain_offset, sizeof (struct st_gain_offset));
+ memset (&gain_offset, 0, sizeof (struct st_gain_offset));
for (C = CL_RED; C <= CL_BLUE; C++)
{
gain_offset.pag[C] = 3;
@@ -11290,7 +11290,7 @@ Head_Relocate (struct st_device *dev, SANE_Int speed, SANE_Int direction,
struct st_motormove mymotor;
struct st_motorpos mtrpos;
- bzero (&mymotor, sizeof (struct st_motormove));
+ memset (&mymotor, 0, sizeof (struct st_motormove));
memcpy (Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte));
if (speed < dev->motormove_count)
@@ -11338,7 +11338,7 @@ Calib_CreateFixedBuffers ()
(USHORT *) malloc (0x7f8 * sizeof (USHORT));
if (fixed_black_shading[channel] != NULL)
- bzero (fixed_black_shading[channel], 0x7f8 * sizeof (USHORT));
+ memset (fixed_black_shading[channel], 0, 0x7f8 * sizeof (USHORT));
else
ret = ERROR;
@@ -11348,7 +11348,7 @@ Calib_CreateFixedBuffers ()
(USHORT *) malloc (0x7f8 * sizeof (USHORT));
if (fixed_white_shading[channel] != NULL)
- bzero (fixed_white_shading[channel], 0x7f8 * sizeof (USHORT));
+ memset (fixed_white_shading[channel], 0, 0x7f8 * sizeof (USHORT));
else
ret = ERROR;
@@ -12779,7 +12779,7 @@ Calib_WhiteShading_3 (struct st_device *dev,
/*a743 */
if (lf130 > 0)
- bzero (buffer1, lf130 * sizeof (double));
+ memset (buffer1, 0, lf130 * sizeof (double));
/*a761 */
if (lf12c > 0)
@@ -12959,7 +12959,7 @@ Calib_WhiteShading_3 (struct st_device *dev,
/*a743 */
if (lf130 > 0)
- bzero (buffer1, lf130 * sizeof (double));
+ memset (buffer1, 0, lf130 * sizeof (double));
/*a761 */
if (lf12c > 0)
@@ -13325,14 +13325,14 @@ Calib_BlackShading (struct st_device *dev,
if (scancfg.depth > 8)
{
/*8bb2 */
- bzero (&dbvalue, 6 * sizeof (double));
+ memset (&dbvalue, 0, 6 * sizeof (double));
position = 0;
if (bytes_per_line > 0)
{
do
{
- bzero (&buff3, 0x8000 * sizeof (SANE_Int));
+ memset (&buff3, 0, 0x8000 * sizeof (SANE_Int));
sumatorio = 0;
ptr = buffer + position;
current_line = 0;
@@ -13435,14 +13435,14 @@ Calib_BlackShading (struct st_device *dev,
else
{
/*8eb6 */
- bzero (&dbvalue, 6 * sizeof (double));
+ memset (&dbvalue, 0, 6 * sizeof (double));
position = 0;
if (bytes_per_line > 0)
{
do
{
- bzero (&buff2, 256 * sizeof (SANE_Byte));
+ memset (&buff2, 0, 256 * sizeof (SANE_Byte));
sumatorio = 0;
/* ptr points to the next position of the first line */
ptr = buffer + position;
@@ -13634,7 +13634,7 @@ Calibration (struct st_device *dev, SANE_Byte * Regs,
Calib_LoadConfig (dev, &calibcfg, scan.scantype, scancfg->resolution_x,
scancfg->depth);
- bzero (&calibdata->gain_offset, sizeof (struct st_gain_offset)); /*[42b3654] */
+ memset (&calibdata->gain_offset, 0, sizeof (struct st_gain_offset)); /*[42b3654] */
for (a = CL_RED; a <= CL_BLUE; a++)
{
myCalib->WRef[a] = calibcfg.WRef[a];
@@ -14110,7 +14110,7 @@ Init_Vars (void)
hp_gamma = malloc (sizeof (struct st_gammatables));
if (hp_gamma != NULL)
- bzero (hp_gamma, sizeof (struct st_gammatables));
+ memset (hp_gamma, 0, sizeof (struct st_gammatables));
else
rst = ERROR;
@@ -14118,7 +14118,7 @@ Init_Vars (void)
{
RTS_Debug = malloc (sizeof (struct st_debug_opts));
if (RTS_Debug != NULL)
- bzero (RTS_Debug, sizeof (struct st_debug_opts));
+ memset (RTS_Debug, 0, sizeof (struct st_debug_opts));
else
rst = ERROR;
}
@@ -14127,7 +14127,7 @@ Init_Vars (void)
{
default_gain_offset = malloc (sizeof (struct st_gain_offset));
if (default_gain_offset != NULL)
- bzero (default_gain_offset, sizeof (struct st_gain_offset));
+ memset (default_gain_offset, 0, sizeof (struct st_gain_offset));
else
rst = ERROR;
}
@@ -14136,7 +14136,7 @@ Init_Vars (void)
{
calibdata = malloc (sizeof (struct st_calibration_data));
if (calibdata != NULL)
- bzero (calibdata, sizeof (struct st_calibration_data));
+ memset (calibdata, 0, sizeof (struct st_calibration_data));
else
rst = ERROR;
}
@@ -14145,7 +14145,7 @@ Init_Vars (void)
{
wshading = malloc (sizeof (struct st_shading));
if (wshading != NULL)
- bzero (wshading, sizeof (struct st_shading));
+ memset (wshading, 0, sizeof (struct st_shading));
else
rst = ERROR;
}
@@ -14467,7 +14467,7 @@ WShading_Calibrate (struct st_device *dev, SANE_Byte * Regs,
DBG (DBG_FNC, "> WShading_Calibrate(*myCalib)\n");
- bzero (&myCalibTable, sizeof (struct st_gain_offset));
+ memset (&myCalibTable, 0, sizeof (struct st_gain_offset));
for (C = CL_RED; C <= CL_BLUE; C++)
{
myCalibTable.pag[C] = 3;
@@ -14687,7 +14687,7 @@ motor_pos (struct st_device *dev, SANE_Byte * Regs,
DBG (DBG_FNC, "> Calib_test(*myCalib)\n");
- bzero (&myCalibTable, sizeof (struct st_gain_offset));
+ memset (&myCalibTable, 0, sizeof (struct st_gain_offset));
calibcfg =
(struct st_calibration_config *)
@@ -14821,7 +14821,7 @@ Calib_BlackShading_jkd (struct st_device *dev, SANE_Byte * Regs,
DBG (DBG_FNC, "> Calib_BlackShading_jkd(*myCalib)\n");
- bzero (&myCalibTable, sizeof (struct st_gain_offset));
+ memset (&myCalibTable, 0, sizeof (struct st_gain_offset));
for (C = CL_RED; C <= CL_BLUE; C++)
{
myCalibTable.pag[C] = 3;
@@ -14955,7 +14955,7 @@ Calib_test (struct st_device *dev, SANE_Byte * Regs,
DBG (DBG_FNC, "> Calib_test(*myCalib)\n");
- bzero (&myCalibTable, sizeof (struct st_gain_offset));
+ memset (&myCalibTable, 0, sizeof (struct st_gain_offset));
calibcfg =
(struct st_calibration_config *)
diff --git a/backend/hp3900_sane.c b/backend/hp3900_sane.c
index 410e35e..f8ed139 100644
--- a/backend/hp3900_sane.c
+++ b/backend/hp3900_sane.c
@@ -1405,7 +1405,7 @@ options_init (TScanner * scanner)
pDesc->title = SANE_I18N ("Scanner model");
pDesc->desc =
SANE_I18N
- ("Allows one to test device behaviour with other supported models");
+ ("Allows one to test device behavior with other supported models");
pDesc->type = SANE_TYPE_STRING;
pDesc->size = max_string_size (scanner->list_models);
pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
@@ -1419,7 +1419,7 @@ options_init (TScanner * scanner)
case opt_negative:
pDesc->name = "opt_negative";
pDesc->title = SANE_I18N ("Negative");
- pDesc->desc = SANE_I18N ("Image colours will be inverted");
+ pDesc->desc = SANE_I18N ("Image colors will be inverted");
pDesc->type = SANE_TYPE_BOOL;
pDesc->unit = SANE_UNIT_NONE;
pDesc->size = sizeof (SANE_Word);
@@ -1991,6 +1991,7 @@ option_get (TScanner * scanner, SANE_Int optid, void *result)
/* scanner buttons */
case opt_button_0:
get_button_status (scanner);
+ // fall through
case opt_button_1:
case opt_button_2:
case opt_button_3:
diff --git a/backend/hp3900_usb.c b/backend/hp3900_usb.c
index 440c963..99623b4 100644
--- a/backend/hp3900_usb.c
+++ b/backend/hp3900_usb.c
@@ -459,7 +459,7 @@ show_buffer (SANE_Int level, SANE_Byte * buffer, SANE_Int size)
sdata = (char *) malloc (256);
if (sdata != NULL)
{
- bzero (sline, 256);
+ memset (sline, 0, 256);
for (cont = 0; cont < size; cont++)
{
if (col == 0)
@@ -480,7 +480,7 @@ show_buffer (SANE_Int level, SANE_Byte * buffer, SANE_Int size)
snprintf (sdata, 255, " : %i\n", offset - 8);
sline = strcat (sline, sdata);
DBG (level, "%s", sline);
- bzero (sline, 256);
+ memset (sline, 0, 256);
}
}
if (col > 0)
@@ -494,7 +494,7 @@ show_buffer (SANE_Int level, SANE_Byte * buffer, SANE_Int size)
snprintf (sdata, 255, " : %i\n", offset - 8);
sline = strcat (sline, sdata);
DBG (level, "%s", sline);
- bzero (sline, 256);
+ memset (sline, 0, 256);
}
free (sdata);
}
diff --git a/backend/hpsj5s.c b/backend/hpsj5s.c
index 786a8d6..77fcc46 100644
--- a/backend/hpsj5s.c
+++ b/backend/hpsj5s.c
@@ -961,8 +961,7 @@ GetCalibration ()
{ /*WARNING!!! Deadlock possible! */
bTest = CallFunctionWithRetVal (0xB5);
}
- while ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
- (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5)));
+ while ((bTest & 0x80) ? (bTest & 0x3F) <= 2 : (bTest & 0x3F) >= 5);
CallFunctionWithParameter (0xCD, 0);
/*Skip this line for ECP: */
@@ -1150,8 +1149,7 @@ CalibrateScanElements ()
usleep (1);
}
while ((timeout < 1000) &&
- ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
- (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))));
+ ((bTest & 0x80) ? (bTest & 0x3F) <= 2 : (bTest & 0x3F) >= 5));
/*Let's read it... */
if(timeout < 1000)
@@ -1218,8 +1216,7 @@ CalibrateScanElements ()
usleep (1);
}
while ((timeout < 1000) &&
- ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
- (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))));
+ ((bTest & 0x80) ? (bTest & 0x3F) <= 2 : (bTest & 0x3F) >= 5));
/*Let's read it... */
if(timeout < 1000)
diff --git a/backend/ibm.c b/backend/ibm.c
index e527a04..1f26226 100644
--- a/backend/ibm.c
+++ b/backend/ibm.c
@@ -248,12 +248,14 @@ attach (const char *devnam, Ibm_Device ** devp)
dev->sane.name = strdup (devnam);
dev->sane.vendor = "IBM";
- str = malloc (sizeof(ibuf.product) + sizeof(ibuf.revision) + 1);
+
+ size_t prod_rev_size = sizeof(ibuf.product) + sizeof(ibuf.revision) + 1;
+ str = malloc (prod_rev_size);
if (str)
{
- str[0] = '\0';
- strncat (str, (char *)ibuf.product, sizeof(ibuf.product));
- strncat (str, (char *)ibuf.revision, sizeof(ibuf.revision));
+ snprintf (str, prod_rev_size, "%.*s%.*s",
+ (int) sizeof(ibuf.product), (const char *) ibuf.product,
+ (int) sizeof(ibuf.revision), (const char *) ibuf.revision);
}
dev->sane.model = str;
dev->sane.type = "flatbed scanner";
diff --git a/backend/kodakaio.c b/backend/kodakaio.c
index c8cc9a7..d5c2857 100644
--- a/backend/kodakaio.c
+++ b/backend/kodakaio.c
@@ -3276,12 +3276,11 @@ setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
case OPT_BR_X:
case OPT_BR_Y:
- sval->w = *((SANE_Word *) value);
- if (SANE_UNFIX(sval->w) == 0) {
+ if (SANE_UNFIX(*((SANE_Word *) value)) == 0) {
DBG(17, "invalid br-x or br-y\n");
return SANE_STATUS_INVAL;
}
- /* passthru */
+ // fall through
case OPT_TL_X:
case OPT_TL_Y:
sval->w = *((SANE_Word *) value);
diff --git a/backend/kvs1025_opt.c b/backend/kvs1025_opt.c
index 71fbf89..628f056 100644
--- a/backend/kvs1025_opt.c
+++ b/backend/kvs1025_opt.c
@@ -226,7 +226,7 @@ static const int go_image_emphasis_val[] = {
static SANE_String_Const go_gamma_list[] = {
SANE_I18N ("normal"),
SANE_I18N ("crt"),
- SANE_I18N ("linier"),
+ SANE_I18N ("linear"),
NULL
};
static const int go_gamma_val[] = {
diff --git a/backend/kvs20xx_opt.c b/backend/kvs20xx_opt.c
index fc527f3..e4b841b 100644
--- a/backend/kvs20xx_opt.c
+++ b/backend/kvs20xx_opt.c
@@ -230,7 +230,7 @@ kvs20xx_init_options (struct scanner *s)
o->title = SANE_I18N ("Length control mode");
o->desc =
SANE_I18N
- ("Length Control Mode is a mode that the scanner reads up to the shorter length of actual"
+ ("Length Control Mode causes the scanner to read the shorter of either the length of the actual"
" paper or logical document length.");
o->type = SANE_TYPE_BOOL;
o->unit = SANE_UNIT_NONE;
diff --git a/backend/kvs40xx.c b/backend/kvs40xx.c
index 6c19e55..6416d64 100644
--- a/backend/kvs40xx.c
+++ b/backend/kvs40xx.c
@@ -524,9 +524,10 @@ static SANE_Status read_image_simplex(SANE_Handle handle)
return st;
}
-static SANE_Status read_data(struct scanner *s)
+static void * read_data (void *arg)
{
- SANE_Status st;
+ struct scanner *s = (struct scanner *) arg;
+ SANE_Status st;
int duplex = s->val[DUPLEX].w;
s->read = 0;
s->side = SIDE_FRONT;
@@ -549,7 +550,7 @@ static SANE_Status read_data(struct scanner *s)
return SANE_STATUS_GOOD;
err:
s->scanning = 0;
- return st;
+ return (void *) st;
}
/* Start scanning */
@@ -640,7 +641,7 @@ sane_start (SANE_Handle handle)
goto err;
}
- if (pthread_create (&s->thread, NULL, (void *(*)(void *)) read_data, s))
+ if (pthread_create (&s->thread, NULL, read_data, s))
{
st = SANE_STATUS_IO_ERROR;
goto err;
diff --git a/backend/kvs40xx_opt.c b/backend/kvs40xx_opt.c
index 2bf9a5c..c812f2c 100644
--- a/backend/kvs40xx_opt.c
+++ b/backend/kvs40xx_opt.c
@@ -228,8 +228,8 @@ static SANE_String_Const lamp_list[] = {
};
static SANE_String_Const dfeed_sence_list[] = {
SANE_I18N ("Normal"),
- SANE_I18N ("High sensivity"),
- SANE_I18N ("Low sensivity"),
+ SANE_I18N ("High sensitivity"),
+ SANE_I18N ("Low sensitivity"),
NULL
};
@@ -393,8 +393,8 @@ kvs40xx_init_options (struct scanner *s)
o->title = SANE_I18N ("Length control mode");
o->desc =
SANE_I18N
- ("Length Control Mode is a mode that the scanner reads up to the shorter length of actual"
- " paper or logical document length.");
+ ("Length Control Mode causes the scanner to read the shorter of either the length of the actual"
+ " paper or logical document length");
o->type = SANE_TYPE_BOOL;
o->unit = SANE_UNIT_NONE;
s->val[LENGTHCTL].w = SANE_FALSE;
@@ -715,7 +715,7 @@ kvs40xx_init_options (struct scanner *s)
o->title = SANE_I18N ("JPEG compression");
o->desc =
SANE_I18N
- ("JPEG compression (yours application must be able to uncompress)");
+ ("JPEG compression (your application must be able to uncompress)");
o->type = SANE_TYPE_BOOL;
o->unit = SANE_UNIT_NONE;
@@ -805,8 +805,8 @@ kvs40xx_init_options (struct scanner *s)
o = &s->opt[STOP_SKEW];
o->name = "stop-skew";
- o->title = SANE_I18N ("Stop scanner when a paper have been skewed");
- o->desc = SANE_I18N ("Scanner will be stop when a paper have been skewed");
+ o->title = SANE_I18N ("Stop scanner if a sheet is skewed");
+ o->desc = SANE_I18N ("Scanner will stop if a sheet is skewed");
o->type = SANE_TYPE_BOOL;
o->unit = SANE_UNIT_NONE;
s->val[STOP_SKEW].w = SANE_FALSE;
@@ -814,7 +814,7 @@ kvs40xx_init_options (struct scanner *s)
o = &s->opt[CROP];
o->name = "crop";
o->title = SANE_I18N ("Crop actual image area");
- o->desc = SANE_I18N ("Scanner automatically detect image area and crop it");
+ o->desc = SANE_I18N ("Scanner will automatically detect image area and crop to it");
o->type = SANE_TYPE_BOOL;
o->unit = SANE_UNIT_NONE;
s->val[CROP].w = SANE_FALSE;
@@ -824,7 +824,7 @@ kvs40xx_init_options (struct scanner *s)
o = &s->opt[MIRROR];
o->name = "mirror";
o->title = SANE_I18N ("Mirror image");
- o->desc = SANE_I18N ("It is right and left reversing");
+ o->desc = SANE_I18N ("Left/right mirror image");
o->type = SANE_TYPE_BOOL;
o->unit = SANE_UNIT_NONE;
s->val[MIRROR].w = SANE_FALSE;
diff --git a/backend/magicolor.c b/backend/magicolor.c
index 3b27a85..af9fb1a 100644
--- a/backend/magicolor.c
+++ b/backend/magicolor.c
@@ -2789,12 +2789,11 @@ setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
case OPT_BR_X:
case OPT_BR_Y:
- sval->w = *((SANE_Word *) value);
- if (SANE_UNFIX(sval->w) == 0) {
+ if (SANE_UNFIX(*((SANE_Word *) value)) == 0) {
DBG(17, "invalid br-x or br-y\n");
return SANE_STATUS_INVAL;
}
- /* passthru */
+ // fall through
case OPT_TL_X:
case OPT_TL_Y:
sval->w = *((SANE_Word *) value);
diff --git a/backend/microtek.c b/backend/microtek.c
index 05f8003..c3b87ec 100644
--- a/backend/microtek.c
+++ b/backend/microtek.c
@@ -3444,6 +3444,7 @@ sane_control_option (SANE_Handle handle,
case OPT_RESOLUTION:
if (info)
*info |= SANE_INFO_RELOAD_PARAMS;
+ // fall through
case OPT_SPEED:
case OPT_PREVIEW:
case OPT_BACKTRACK:
diff --git a/backend/mustek_pp.c b/backend/mustek_pp.c
index 912c3bd..bb97f86 100644
--- a/backend/mustek_pp.c
+++ b/backend/mustek_pp.c
@@ -160,8 +160,6 @@ free_cfg_options(int *numoptions, Mustek_pp_config_option** options)
/* do_eof:
* closes the pipeline
*
- * ChangeLog:
- *
* Description:
* closes the pipe (read-only end)
*/
@@ -180,8 +178,6 @@ do_eof (Mustek_pp_Handle *hndl)
/* do_stop:
* ends the reader_process and stops the scanner
*
- * ChangeLog:
- *
* Description:
* kills the reader process with a SIGTERM and cancels the scanner
*/
@@ -218,8 +214,6 @@ do_stop(Mustek_pp_Handle *hndl)
/* sigterm_handler:
* cancel scanner when receiving a SIGTERM
*
- * ChangeLog:
- *
* Description:
* just exit... reader_process takes care that nothing bad will happen
*
@@ -247,8 +241,6 @@ sigterm_handler (int signal __UNUSED__)
/* reader_process:
* receives data from the scanner and stuff it into the pipeline
*
- * ChangeLog:
- *
* Description:
* The signal handle for SIGTERM is initialized.
*
@@ -318,8 +310,6 @@ reader_process (Mustek_pp_Handle * hndl, int pipe)
/* sane_attach:
* adds a new entry to the Mustek_pp_Device *devlist list
*
- * ChangeLog:
- *
* Description:
* After memory for a new device entry is allocated, the
* parameters for the device are determined by a call to
@@ -382,8 +372,6 @@ sane_attach (SANE_String_Const port, SANE_String_Const name, SANE_Int driver, SA
/* init_options:
* Sets up the option descriptors for a device
*
- * ChangeLog:
- *
* Description:
*/
static void
@@ -626,8 +614,6 @@ init_options(Mustek_pp_Handle *hndl)
* Attempts to attach a device to the list after parsing of a section
* of the configuration file.
*
- * ChangeLog:
- *
* Description:
* After parsing a scanner section of the config file, this function
* is called to look for a driver with a matching name. When found,
@@ -691,8 +677,6 @@ attach_device(SANE_String *driver, SANE_String *name,
/* sane_init:
* Reads configuration file and registers hardware driver
*
- * ChangeLog:
- *
* Description:
* in *version_code the SANE version this backend was compiled with and the
* version of the backend is returned. The value of authorize is stored in
@@ -1001,8 +985,6 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
/* sane_exit:
* Unloads all drivers and frees allocated memory
*
- * ChangeLog:
- *
* Description:
* All open devices are closed first. Then all registered devices
* are removed.
@@ -1051,8 +1033,6 @@ sane_exit (void)
/* sane_get_devices:
* Returns a list of registered devices
*
- * ChangeLog:
- *
* Description:
* A possible present old device_list is removed first. A new
* devarray is allocated and filled with pointers to the
@@ -1093,8 +1073,6 @@ sane_get_devices (const SANE_Device *** device_list,
/* sane_open:
* opens a device and prepares it for operation
*
- * ChangeLog:
- *
* Description:
* The device identified by ``devicename'' is looked
* up in the list, or if devicename is zero, the
@@ -1201,8 +1179,6 @@ sane_open (SANE_String_Const devicename, SANE_Handle * handle)
/* sane_close:
* closes a given device and frees all resources
*
- * ChangeLog:
- *
* Description:
* The handle is searched in the list of active handles.
* If it's found, the handle is removed.
@@ -1261,8 +1237,6 @@ sane_close (SANE_Handle handle)
/* sane_get_option_descriptor:
* does what it says
*
- * ChangeLog:
- *
* Description:
*
*/
@@ -1285,8 +1259,6 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
/* sane_control_option:
* Reads or writes an option
*
- * ChangeLog:
- *
* Desription:
* If a pointer to info is given, the value is initialized to zero
* while scanning options cannot be read or written. next a basic
@@ -1523,8 +1495,6 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
/* sane_get_parameters:
* returns the set of parameters, that is used for the next scan
*
- * ChangeLog:
- *
* Description:
*
* First of all it is impossible to change the parameter set
@@ -1716,8 +1686,6 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
/* sane_start:
* starts the scan. data aquisition will start immedially
*
- * ChangeLog:
- *
* Description:
*
*/
@@ -1775,8 +1743,6 @@ sane_start (SANE_Handle handle)
/* sane_read:
* receives data from pipeline and passes it to the caller
*
- * ChangeLog:
- *
* Description:
* ditto
*/
@@ -1877,8 +1843,6 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
/* sane_cancel:
* stops a scan and ends the reader process
*
- * ChangeLog:
- *
* Description:
*
*/
@@ -1900,8 +1864,6 @@ sane_cancel (SANE_Handle handle)
/* sane_set_io_mode:
* toggles between blocking and non-blocking reading
*
- * ChangeLog:
- *
* Description:
*
*/
@@ -1930,8 +1892,6 @@ sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
/* sane_get_select_fd:
* returns the pipeline fd for direct reading
*
- * ChangeLog:
- *
* Description:
* to allow the frontend to receive the data directly it
* can read from the pipeline itself
diff --git a/backend/mustek_usb2_transparent.c b/backend/mustek_usb2_transparent.c
index 33adcc0..74f7b52 100644
--- a/backend/mustek_usb2_transparent.c
+++ b/backend/mustek_usb2_transparent.c
@@ -1382,7 +1382,7 @@ Transparent_LineCalibration16Bits (unsigned short wTAShadingMinus)
memset (lpBuf, 0, 50);
stream = fopen ("/root/darkshading(Tra).pnm", "wb+\n");
- sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth * wCalHeight);
+ sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight);
fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream);
fwrite (lpDarkData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream);
fclose (stream);
diff --git a/backend/nec.c b/backend/nec.c
index f12e997..d123be0 100644
--- a/backend/nec.c
+++ b/backend/nec.c
@@ -349,6 +349,9 @@ sense_handler(int fd, u_char *sense_buffer, void *ss)
DBG(5, "Scanner not ready: undocumented reason\n");
return SANE_STATUS_IO_ERROR;
}
+ default:
+ DBG(5, "Scanner not ready: unknown sense code\n");
+ return SANE_STATUS_IO_ERROR;
}
case 0x03: /* medium error */
DBG(5, "medium error: undocumented reason\n");
@@ -2306,6 +2309,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
case OPT_BR_Y:
if (info && s->val[option].w != *(SANE_Word *) val)
*info |= SANE_INFO_RELOAD_PARAMS;
+ // fall through
case OPT_NUM_OPTS:
case OPT_THRESHOLD:
/* xxx theoretically, we could use OPT_THRESHOLD in
diff --git a/backend/niash.c b/backend/niash.c
index bbc90d3..b62bdba 100644
--- a/backend/niash.c
+++ b/backend/niash.c
@@ -768,6 +768,7 @@ _InitOptions (TScanner * s)
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE |
SANE_CAP_EMULATED;
pVal->w = 50;
+ break;
default:
DBG (DBG_ERR, "Uninitialised option %d\n", i);
diff --git a/backend/pieusb_buffer.h b/backend/pieusb_buffer.h
index 3724a40..282595a 100644
--- a/backend/pieusb_buffer.h
+++ b/backend/pieusb_buffer.h
@@ -48,6 +48,10 @@
#include "pieusb.h"
#include "../include/sane/sanei_ir.h"
+#ifndef L_tmpnam
+#define L_tmpnam 20
+#endif
+
struct Pieusb_Read_Buffer
{
SANE_Uint* data; /* image data - always store as 16 bit values; mmap'ed */
diff --git a/backend/pieusb_specific.c b/backend/pieusb_specific.c
index ce107cf..1b5f0f4 100644
--- a/backend/pieusb_specific.c
+++ b/backend/pieusb_specific.c
@@ -322,7 +322,7 @@ pieusb_initialize_device_definition (Pieusb_Device_Definition* dev, Pieusb_Scann
buf = malloc(9);
if (buf == NULL)
return SANE_STATUS_NO_MEM;
- strncpy(buf, inq->vendor, 8);
+ memcpy(buf, inq->vendor, 8);
pp = buf + 8;
*pp-- = '\0';
while (*pp == ' ') *pp-- = '\0';
@@ -332,7 +332,7 @@ pieusb_initialize_device_definition (Pieusb_Device_Definition* dev, Pieusb_Scann
buf = malloc(17);
if (buf == NULL)
return SANE_STATUS_NO_MEM;
- strncpy(buf, inq->product, 16);
+ memcpy(buf, inq->product, 16);
pp = buf + 16;
*pp-- = '\0';
while (*pp == ' ') *pp-- = '\0';
@@ -346,7 +346,7 @@ pieusb_initialize_device_definition (Pieusb_Device_Definition* dev, Pieusb_Scann
buf = malloc(5);
if (buf == NULL)
return SANE_STATUS_NO_MEM;
- strncpy(buf, inq->productRevision, 4);
+ memcpy(buf, inq->productRevision, 4);
pp = buf + 4;
*pp-- = '\0';
while (*pp == ' ') *pp-- = '\0';
diff --git a/backend/pixma.conf.in b/backend/pixma.conf.in
index 3f5c61a..d6184b4 100644
--- a/backend/pixma.conf.in
+++ b/backend/pixma.conf.in
@@ -1,5 +1,10 @@
# pixma.conf configuration for the sane pixma backend
#
+# disable network scanner detection.
+# This must be the first not commented line
+# Uncomment the following line:
+# networking=no
+#
# bjnp-timeout=5000
# Specify the timeout (in ms) to be used for all the folllowing
# scanners.
@@ -16,10 +21,12 @@
# port number can normally be left out, port 8612 is used as default
# The timeout parameter sets a timeout value for the scanner on
# the same line
-# Example:
+# Examples using bjnp:
# bjnp://myscanner.my.domain:8612 // uses the default 1000ms timeout
# bjnp-timeout=5000
# bjnp://printer-1.pheasant.org // will use the 5000 ms timeout
# bjnp://scanner.bad-network.org/timeout=1500 // timeout set to 1500 ms
# bjnp-timeout=3000 // will be used for auto-detected scanners
#
+# Example using for a scanner using mfnp including the optional timeout:
+# mfnp://scanner.bad-network.org/timeout=1500
diff --git a/backend/pixma.c b/backend/pixma/pixma.c
index d33a74e..f763496 100644
--- a/backend/pixma.c
+++ b/backend/pixma/pixma.c
@@ -242,12 +242,12 @@ cleanup_device_list (void)
}
static void
-find_scanners (void)
+find_scanners (SANE_Bool local_only)
{
unsigned i, nscanners;
cleanup_device_list ();
- nscanners = pixma_find_scanners (conf_devices);
+ nscanners = pixma_find_scanners (conf_devices, local_only);
PDBG (pixma_dbg (3, "pixma_find_scanners() found %u devices\n", nscanners));
dev_list =
(const SANE_Device **) calloc (nscanners + 1, sizeof (*dev_list));
@@ -1602,11 +1602,9 @@ sane_exit (void)
SANE_Status
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
- UNUSED (local_only);
-
if (!device_list)
return SANE_STATUS_INVAL;
- find_scanners ();
+ find_scanners (local_only);
*device_list = dev_list;
return (dev_list) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
}
@@ -1623,7 +1621,7 @@ sane_open (SANE_String_Const name, SANE_Handle * h)
return SANE_STATUS_INVAL;
*h = NULL;
- nscanners = pixma_find_scanners (conf_devices);
+ nscanners = pixma_find_scanners (conf_devices, SANE_FALSE);
if (nscanners == 0)
return SANE_STATUS_INVAL;
if (name[0] == '\0')
diff --git a/backend/pixma.h b/backend/pixma/pixma.h
index 370203a..c2df3cc 100644
--- a/backend/pixma.h
+++ b/backend/pixma/pixma.h
@@ -45,6 +45,9 @@
#ifndef PIXMA_H
#define PIXMA_H
+#include "../include/sane/sane.h"
+
+
/*!
* \mainpage Scanner driver for Canon PIXMA MP series
* \section example Sample code for application
@@ -116,7 +119,7 @@ typedef uint32_t uint32_t;
/** \name Version of the driver */
/**@{*/
#define PIXMA_VERSION_MAJOR 0
-#define PIXMA_VERSION_MINOR 23
+#define PIXMA_VERSION_MINOR 27
#define PIXMA_VERSION_BUILD 0
/**@}*/
@@ -313,7 +316,7 @@ struct pixma_scan_param_t
/** Flag indicating whether the offset correction for TPU scans
* was already performed (to avoid repeated corrections).
- * Currently only used in pixma_mp810.c sub-driver */
+ * Currently only used in pixma_mp800.c sub-driver */
unsigned tpu_offset_added;
/* Flag indicating if data from scanner will be in JPEG format */
@@ -362,6 +365,7 @@ struct pixma_config_t
uint16_t pid; /**< USB Product ID */
unsigned iface; /**< USB Interface number */
const pixma_scan_ops_t *ops; /**< Subdriver ops */
+ unsigned min_xdpi; /**< Minimum horizontal resolution[DPI] */
unsigned xdpi; /**< Maximum horizontal resolution[DPI] */
unsigned ydpi; /**< Maximum vertical resolution[DPI] */
unsigned adftpu_min_dpi; /**< Maximum horizontal resolution[DPI] for adf/tpu
@@ -403,7 +407,7 @@ void pixma_set_debug_level (int level);
*
* \return The number of scanners found currently. The return value is
* guaranteed to be valid until the next call to pixma_find_scanners(). */
-int pixma_find_scanners (const char **conf_devices);
+int pixma_find_scanners (const char **conf_devices, SANE_Bool local_only);
/** Return the model name of the device \a devnr. */
const char *pixma_get_device_model (unsigned devnr);
diff --git a/backend/pixma_bjnp.c b/backend/pixma/pixma_bjnp.c
index 5a9932e..34ba918 100644
--- a/backend/pixma_bjnp.c
+++ b/backend/pixma/pixma_bjnp.c
@@ -39,6 +39,7 @@
whether to permit this exception to apply to your modifications.
If you do not wish that, delete this exception notice.
*/
+
#undef BACKEND_NAME
#define BACKEND_NAME bjnp
@@ -68,6 +69,9 @@
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
/*
* networking stuff
@@ -114,6 +118,40 @@ static int bjnp_no_devices = 0;
* Private functions
*/
+static const struct pixma_config_t *lookup_scanner(const char *makemodel,
+ const struct pixma_config_t *const pixma_devices[])
+{
+ int i;
+ const struct pixma_config_t *cfg;
+ char *match;
+
+ for (i = 0; pixma_devices[i]; i++)
+ {
+ /* loop through the device classes (mp150, mp730 etc) */
+ for (cfg = pixma_devices[i]; cfg->name; cfg++)
+ {
+ /* loop through devices in class */
+ PDBG( bjnp_dbg( LOG_DEBUG3, "lookup_scanner: Checking for %s in %s\n", makemodel, cfg->model));
+ if ((match = strcasestr (makemodel, cfg->model)) != NULL)
+ {
+ /* possible match found, make sure it is not a partial match */
+ /* MP600 and MP600R are different models! */
+ /* some models contain ranges, so check for a '-' too */
+
+ if ((match[strlen(cfg->model)] == ' ') ||
+ (match[strlen(cfg->model)] == '\0') ||
+ (match[strlen(cfg->model)] == '-'))
+ {
+ PDBG( bjnp_dbg (LOG_DEBUG, "lookup_scanner: Scanner model found: Name %s(%s) matches %s\n", cfg->model, cfg->name, makemodel));
+ return cfg;
+ }
+ }
+ }
+ }
+ PDBG( bjnp_dbg (LOG_DEBUG, "lookup_scanner: Scanner model %s not found, giving up!\n", makemodel));
+ return NULL;
+}
+
static void
u8tohex (char *string, const uint8_t *value, int len )
{
@@ -289,6 +327,7 @@ parse_IEEE1284_to_model (char *scanner_id, char *model)
char s[BJNP_IEEE1284_MAX];
char *tok;
+ char * model_str;
strncpy (s, scanner_id, BJNP_IEEE1284_MAX);
s[BJNP_IEEE1284_MAX - 1] = '\0';
@@ -299,10 +338,11 @@ parse_IEEE1284_to_model (char *scanner_id, char *model)
{
/* MDL contains make and model */
- if (strncmp (tok, "MDL:", 4) == 0)
+ if (strncmp (tok, "MDL:", strlen("MDL:")) == 0)
{
- strncpy (model, tok + 4, BJNP_IEEE1284_MAX);
- model[BJNP_IEEE1284_MAX -1] = '\0';
+ model_str = tok + strlen("MDL:");
+ strncpy (model, model_str, BJNP_MODEL_MAX);
+ model[BJNP_MODEL_MAX -1] = '\0';
return 1;
}
tok = strtok (NULL, ";");
@@ -421,6 +461,7 @@ bjnp_open_tcp (int devno)
bjnp_sockaddr_t *addr = device[devno].addr;
char host[BJNP_HOST_MAX];
int port;
+ int connect_timeout = BJNP_TIMEOUT_TCP_CONNECT;
get_address_info( addr, host, &port);
PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_open_tcp: Setting up a TCP socket, dest: %s port %d\n",
@@ -457,16 +498,22 @@ bjnp_open_tcp (int devno)
fcntl (sock, F_SETFD, FD_CLOEXEC);
- if (connect
- (sock, &(addr->addr), sa_size(device[devno].addr) )!= 0)
+ while (connect_timeout > 0)
{
- PDBG (bjnp_dbg
- (LOG_CRIT, "bjnp_open_tcp: ERROR - Can not connect to scanner: %s\n",
- strerror (errno)));
- return -1;
+ if (connect
+ (sock, &(addr->addr), sa_size(device[devno].addr)) == 0)
+ {
+ device[devno].tcp_socket = sock;
+ return 0;
+ }
+ PDBG (bjnp_dbg( LOG_INFO, "bjnp_open_tcp: INFO - Can not yet connect over TCP to scanner: %s, retrying\n",
+ strerror(errno)));
+ usleep(BJNP_TCP_CONNECT_INTERVAL * BJNP_USLEEP_MS);
+ connect_timeout = connect_timeout - BJNP_TCP_CONNECT_INTERVAL;
}
- device[devno].tcp_socket = sock;
- return 0;
+ PDBG (bjnp_dbg
+ (LOG_CRIT, "bjnp_open_tcp: ERROR - Can not connect to scanner, giving up!"));
+ return -1;
}
static int
@@ -598,7 +645,7 @@ set_cmd_from_string (char* protocol_string, struct BJNP_command *cmd, char cmd_c
* Returns: sequence number of command
*/
- strncpy (cmd->BJNP_id, protocol_string, sizeof (cmd->BJNP_id));
+ memcpy (cmd->BJNP_id, protocol_string, sizeof (cmd->BJNP_id));
cmd->dev_type = BJNP_CMD_SCAN;
cmd->cmd_code = cmd_code;
cmd->unknown1 = htons (0);
@@ -617,7 +664,7 @@ set_cmd_for_dev (int devno, struct BJNP_command *cmd, char cmd_code, int payload
* Returns: sequence number of command
*/
- strncpy (cmd->BJNP_id, device[devno].protocol_string, sizeof (cmd->BJNP_id));
+ memcpy(cmd->BJNP_id, device[devno].protocol_string, sizeof (cmd->BJNP_id));
cmd->dev_type = BJNP_CMD_SCAN;
cmd->cmd_code = cmd_code;
cmd->unknown1 = htons (0);
@@ -707,8 +754,8 @@ udp_command (const int dev_no, char *command, int cmd_len, char *response,
FD_ZERO (&fdset);
FD_SET (sockfd, &fdset);
- timeout.tv_sec = device[dev_no].bjnp_timeout /1000;
- timeout.tv_usec = device[dev_no].bjnp_timeout %1000;
+ timeout.tv_sec = device[dev_no].bjnp_ip_timeout /1000;
+ timeout.tv_usec = device[dev_no].bjnp_ip_timeout %1000;
}
while (((result =
select (sockfd + 1, &fdset, NULL, NULL, &timeout)) <= 0)
@@ -738,7 +785,7 @@ udp_command (const int dev_no, char *command, int cmd_len, char *response,
close(sockfd);
PDBG (bjnp_dbg
- (LOG_CRIT, "udp_command: ERROR - no data received (timeout = %d)\n", device[dev_no].bjnp_timeout ) );
+ (LOG_CRIT, "udp_command: ERROR - no data received (timeout = %d)\n", device[dev_no].bjnp_ip_timeout ) );
return -1;
}
@@ -1424,8 +1471,8 @@ bjnp_recv_header (int devno, size_t *payload_size )
FD_ZERO (&input);
FD_SET (fd, &input);
- timeout.tv_sec = device[devno].bjnp_timeout /1000;
- timeout.tv_usec = device[devno].bjnp_timeout %1000;
+ timeout.tv_sec = device[devno].bjnp_ip_timeout /1000;
+ timeout.tv_usec = device[devno].bjnp_ip_timeout %1000;
}
while ( ( (result = select (fd + 1, &input, NULL, NULL, &timeout)) <= 0) &&
(errno == EINTR) && (attempt++ < BJNP_MAX_SELECT_ATTEMPTS));
@@ -1444,7 +1491,7 @@ bjnp_recv_header (int devno, size_t *payload_size )
terrno = errno;
PDBG (bjnp_dbg (LOG_CRIT,
"bjnp_recv_header: ERROR - could not read response header (select timed out after %d ms)!\n",
- device[devno].bjnp_timeout ) );
+ device[devno].bjnp_ip_timeout ) );
errno = terrno;
return SANE_STATUS_IO_ERROR;
}
@@ -1504,7 +1551,7 @@ bjnp_recv_header (int devno, size_t *payload_size )
}
static int
-bjnp_init_device_structure(int dn, bjnp_sockaddr_t *sa, bjnp_protocol_defs_t *protocol_defs, int min_timeout)
+bjnp_init_device_structure(int dn, bjnp_sockaddr_t *sa, bjnp_protocol_defs_t *protocol_defs, int ip_timeout)
{
/* initialize device structure */
@@ -1526,8 +1573,8 @@ bjnp_init_device_structure(int dn, bjnp_sockaddr_t *sa, bjnp_protocol_defs_t *pr
device[dn].address_level = get_scanner_name(sa, name);
device[dn].session_id = 0;
device[dn].serial = -1;
- device[dn].bjnp_timeout = min_timeout;
- device[dn].bjnp_min_timeout = min_timeout;
+ device[dn].bjnp_ip_timeout = ip_timeout;
+ device[dn].bjnp_scanner_timeout = 1000;
device[dn].scanner_data_left = 0;
device[dn].last_cmd = 0;
device[dn].blocksize = BJNP_BLOCKSIZE_START;
@@ -1538,8 +1585,10 @@ bjnp_init_device_structure(int dn, bjnp_sockaddr_t *sa, bjnp_protocol_defs_t *pr
{
PDBG (bjnp_dbg
(LOG_CRIT, "bjnp_init_device_structure: Cannot read mac address, skipping this scanner\n" ) );
+ device[dn].open = 0;
return -1;
}
+ device[dn].open = 1;
return 0;
}
@@ -1600,8 +1649,8 @@ bjnp_recv_data (int devno, SANE_Byte * buffer, size_t start_pos, size_t * len)
/* wait for data to be received, retry on a signal being received */
FD_ZERO (&input);
FD_SET (fd, &input);
- timeout.tv_sec = device[devno].bjnp_timeout /1000;
- timeout.tv_usec = device[devno].bjnp_timeout %1000;
+ timeout.tv_sec = device[devno].bjnp_ip_timeout /1000;
+ timeout.tv_usec = device[devno].bjnp_ip_timeout %1000;
}
while (((result = select (fd + 1, &input, NULL, NULL, &timeout)) <= 0) &&
(errno == EINTR) && (attempt++ < BJNP_MAX_SELECT_ATTEMPTS));
@@ -1621,7 +1670,7 @@ bjnp_recv_data (int devno, SANE_Byte * buffer, size_t start_pos, size_t * len)
terrno = errno;
PDBG (bjnp_dbg (LOG_CRIT,
"bjnp_recv_data: ERROR - could not read response payload (select timed out after %d ms)!\n",
- device[devno].bjnp_timeout) );
+ device[devno].bjnp_ip_timeout) );
errno = terrno;
*len = 0;
return SANE_STATUS_IO_ERROR;
@@ -1658,7 +1707,7 @@ bjnp_allocate_device (SANE_String_Const devname,
struct addrinfo hints;
int result;
int i;
- int min_timeout = BJNP_TIMEOUT_DEFAULT;
+ int ip_timeout = BJNP_TIMEOUT_DEFAULT;
PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_allocate_device(%s) %d\n", devname, bjnp_no_devices));
@@ -1669,13 +1718,11 @@ bjnp_allocate_device (SANE_String_Const devname,
if (strlen (args) > 0)
{
- /* get device specific timeout if any */
+ /* get device specific ip timeout if any */
if (strncmp(args, "timeout=", strlen("timeout=")) == 0)
{
- min_timeout = atoi(args + strlen("timeout="));
- if (min_timeout < BJNP_TIMEOUT_DEFAULT)
- min_timeout = BJNP_TIMEOUT_DEFAULT;
+ ip_timeout = atoi(args + strlen("timeout="));
} else {
PDBG (bjnp_dbg
(LOG_CRIT,
@@ -1735,10 +1782,11 @@ bjnp_allocate_device (SANE_String_Const devname,
return BJNP_STATUS_INVAL;
}
if (bjnp_init_device_structure( bjnp_no_devices, (bjnp_sockaddr_t *)cur -> ai_addr,
- protocol_defs, min_timeout) != 0)
+ protocol_defs, ip_timeout) != 0)
{
/* giving up on this address, try next one if any */
- break;
+ cur = cur->ai_next;
+ continue;
}
for (i = 0; i < bjnp_no_devices; i++)
{
@@ -1759,15 +1807,8 @@ bjnp_allocate_device (SANE_String_Const devname,
device[i].address_level = device[bjnp_no_devices].address_level;
}
- /* check if new timeout value was defined (e.g. from sanei_bjnp_device_open)
- * if so, use new timout value */
+ /* Leave timeout values unchanged, as they were probably specified by the user */
- if (device[i].bjnp_min_timeout < device[bjnp_no_devices].bjnp_min_timeout)
- {
- /* use the longer timeout as requested */
- device[i].bjnp_timeout = device[bjnp_no_devices].bjnp_min_timeout;
- device[i].bjnp_min_timeout = device[bjnp_no_devices].bjnp_min_timeout;
- }
freeaddrinfo(res);
*dn = i;
bjnp_free_device_structure( bjnp_no_devices);
@@ -1778,6 +1819,12 @@ bjnp_allocate_device (SANE_String_Const devname,
}
freeaddrinfo(res);
+ if (device[bjnp_no_devices].open == 0)
+ {
+ PDBG (bjnp_dbg(LOG_NOTICE, "bjnp_allocate_device: Cannot access scanner, skipping!"));
+ return BJNP_STATUS_INVAL;
+ }
+
PDBG (bjnp_dbg (LOG_INFO, "bjnp_allocate_device: Scanner not yet in our list, added it: %s:%s\n", host, port));
/* Commit new device structure */
@@ -1799,16 +1846,15 @@ static void add_scanner(SANE_Int *dev_no,
const char *uri,
SANE_Status (*attach_bjnp)
(SANE_String_Const devname,
- SANE_String_Const makemodel,
SANE_String_Const serial,
- const struct pixma_config_t *
- const pixma_devices[]),
- const struct pixma_config_t *const pixma_devices[])
+ const struct pixma_config_t *cfg),
+ const struct pixma_config_t *const pixma_devices[])
{
char scanner_host[BJNP_HOST_MAX];
char serial[BJNP_SERIAL_MAX];
- char makemodel[BJNP_IEEE1284_MAX];
+ char makemodel[BJNP_MODEL_MAX];
+ const struct pixma_config_t *cfg = NULL;
/* Allocate device structure for scanner */
switch (bjnp_allocate_device (uri, dev_no, scanner_host))
@@ -1821,17 +1867,32 @@ static void add_scanner(SANE_Int *dev_no,
}
else
{
- /*
- * inform caller of found scanner
- */
+ /*
+ * fetch scanner configuration
+ */
+ if ((cfg = lookup_scanner(makemodel, pixma_devices)) == (struct pixma_config_t *)NULL)
+ {
+ PDBG (bjnp_dbg (LOG_CRIT, "add_scanner: Scanner %s is not supported, model is unknown! Please report upstream\n", makemodel));
+ break;
+ }
- determine_scanner_serial (scanner_host, device[*dev_no].mac_address, serial);
+ /*
+ * inform caller of found scanner
+ */
- attach_bjnp (uri, makemodel,
- serial, pixma_devices);
- PDBG (bjnp_dbg (LOG_NOTICE, "add_scanner: New scanner added: %s, serial %s, mac address: %s.\n",
- uri, serial, device[*dev_no].mac_address));
+ determine_scanner_serial (scanner_host, device[*dev_no].mac_address, serial);
+
+ switch (attach_bjnp (uri, serial, cfg))
+ {
+ case SANE_STATUS_GOOD:
+ PDBG (bjnp_dbg (LOG_NOTICE, "add_scanner: New scanner added: %s, serial %s, mac address: %s.\n",
+ uri, serial, device[*dev_no].mac_address));
+ break;
+ default:
+ PDBG (bjnp_dbg (LOG_CRIT, "add_scanner: unexpected error (out of memory?), adding %s\n", makemodel));
+ }
}
+
break;
case BJNP_STATUS_ALREADY_ALLOCATED:
PDBG (bjnp_dbg (LOG_NOTICE, "add_scanner: Scanner at %s was added before, good!\n",
@@ -1845,13 +1906,14 @@ static void add_scanner(SANE_Int *dev_no,
}
}
-int add_default_timeout(char *uri, int timeout, int max_len)
+int add_timeout_to_uri(char *uri, int timeout, int max_len)
{
char method[BJNP_METHOD_MAX];
char host[BJNP_HOST_MAX];
char port_str[BJNP_PORT_MAX];
char args[BJNP_HOST_MAX];
int port;
+ bjnp_protocol_defs_t *proto_struct;
if (split_uri(uri, method, host, port_str, args ) != 0)
{
@@ -1859,24 +1921,33 @@ int add_default_timeout(char *uri, int timeout, int max_len)
}
port = atoi(port_str);
+
if (port == 0)
{
- port = 8612;
+ proto_struct = get_protocol_by_method(method);
+ if (proto_struct == NULL)
+ {
+ PDBG (bjnp_dbg (LOG_NOTICE, "uri: %s: Method %s cannot be recognized\n", uri, method));
+ }
+ else
+ {
+ port = proto_struct-> default_port;
+ }
}
+ /* add timeout value only if missing in URI */
+
if (strstr(args, "timeout=") == NULL)
{
sprintf(args, "timeout=%d", timeout);
}
snprintf(uri, max_len -1, "%s://%s:%d/%s", method,host, port, args);
+ uri[max_len - 1] = '\0';
return 0;
}
-
-/*
- * Public functions
- */
+/** Public functions **/
/** Initialize sanei_bjnp.
*
@@ -1901,11 +1972,9 @@ sanei_bjnp_init (void)
extern SANE_Status
sanei_bjnp_find_devices (const char **conf_devices,
SANE_Status (*attach_bjnp)
- (SANE_String_Const devname,
- SANE_String_Const makemodel,
- SANE_String_Const serial,
- const struct pixma_config_t *
- const pixma_devices[]),
+ (SANE_String_Const devname,
+ SANE_String_Const serial,
+ const struct pixma_config_t *cfg),
const struct pixma_config_t *const pixma_devices[])
{
int numbytes = 0;
@@ -1921,10 +1990,11 @@ sanei_bjnp_find_devices (const char **conf_devices,
fd_set fdset;
fd_set active_fdset;
struct timeval timeout;
- char scanner_host[256];
- char uri[256];
+ char scanner_host[HOST_NAME_MAX];
+ char uri[HOST_NAME_MAX + 32];
int dev_no;
int port;
+ int auto_detect = 1;
int timeout_default = BJNP_TIMEOUT_DEFAULT;
bjnp_sockaddr_t broadcast_addr[BJNP_SOCK_MAX];
bjnp_sockaddr_t scanner_sa;
@@ -1942,38 +2012,57 @@ sanei_bjnp_find_devices (const char **conf_devices,
socket_fd[i] = -1;
}
- /* Add devices from config file */
-
- if (conf_devices[0] == NULL)
- PDBG (bjnp_dbg( LOG_DEBUG, "sanei_bjnp_find_devices: No devices specified in configuration file.\n" ) );
+ /* parse config file */
- for (i = 0; conf_devices[i] != NULL; i++)
+ if (conf_devices[0] != NULL)
{
- if (strncmp(conf_devices[i], "bjnp-timeout=", strlen("bjnp-timeout="))== 0)
+ if (strcmp(conf_devices[0], "networking=no") == 0)
{
- timeout_default = atoi(conf_devices[i] + strlen("bjnp-timeout=") );
- if (timeout_default < BJNP_TIMEOUT_DEFAULT)
- {
- timeout_default = BJNP_TIMEOUT_DEFAULT;
+ /* networking=no may only occur on the first non-commented line */
+
+ PDBG (bjnp_dbg( LOG_DEBUG, "sanei_bjnp_find_devices: Networked scanner detection is disabled in configuration file.\n" ) );
+ return SANE_STATUS_GOOD;
+ }
+ /* parse configuration file */
+
+ for (i = 0; conf_devices[i] != NULL; i++)
+ {
+ if (strncmp(conf_devices[i], "bjnp-timeout=", strlen("bjnp-timeout="))== 0)
+ {
+ timeout_default = atoi(conf_devices[i] + strlen("bjnp-timeout=") );
+ PDBG ( bjnp_dbg (LOG_DEBUG, "Set new default timeout value: %d ms.", timeout_default));
+ continue;
}
- PDBG ( bjnp_dbg
- (LOG_DEBUG, "Set new default timeout value: %d ms.", timeout_default));
- continue;
- }
- PDBG (bjnp_dbg
- (LOG_DEBUG, "sanei_bjnp_find_devices: Adding scanner from pixma.conf: %s\n", conf_devices[i]));
- strncpy(uri, conf_devices[i], sizeof(uri));
- add_default_timeout(uri, timeout_default, sizeof(uri));
- add_scanner(&dev_no, uri, attach_bjnp, pixma_devices);
+ else if (strncmp(conf_devices[i], "auto_detection=no", strlen("auto_detection=no"))== 0)
+ {
+ auto_detect = 0;
+ PDBG ( bjnp_dbg (LOG_DEBUG, "sanei_bjnp_find_devices: auto detection of scanners disabled"));
+ continue;
+ }
+ else
+ {
+ PDBG (bjnp_dbg (LOG_DEBUG, "sanei_bjnp_find_devices: Adding scanner from pixma.conf: %s\n", conf_devices[i]));
+ memcpy(uri, conf_devices[i], sizeof(uri));
+ add_timeout_to_uri(uri, timeout_default, sizeof(uri));
+ add_scanner(&dev_no, uri, attach_bjnp, pixma_devices);
+ }
+ }
+ PDBG (bjnp_dbg (LOG_DEBUG, "sanei_bjnp_find_devices: Added all specified scanners.\n"));
+ }
+ else
+ {
+ PDBG (bjnp_dbg( LOG_DEBUG, "sanei_bjnp_find_devices: Configuration file is empty, No devices specified.\n" ) );
}
- PDBG (bjnp_dbg
- (LOG_DEBUG,
- "sanei_bjnp_find_devices: Added all configured scanners, now do auto detection...\n"));
+ if (auto_detect == 0)
+ {
+ return SANE_STATUS_GOOD;
+ }
/*
* Send UDP DISCOVER to discover scanners and return the list of scanners found
*/
+ PDBG (bjnp_dbg( LOG_DEBUG, "sanei_bjnp_find_devices: Start auto-detection.\n" ) );
FD_ZERO (&fdset);
no_sockets = 0;
@@ -2252,17 +2341,10 @@ sanei_bjnp_deactivate (SANE_Int dn)
extern void
sanei_bjnp_set_timeout (SANE_Int devno, SANE_Int timeout)
{
- if (timeout < device[devno].bjnp_min_timeout)
- {
- PDBG (bjnp_dbg (LOG_INFO, "bjnp_set_timeout to %d, but using minimum value %d\n",
- timeout, device[devno].bjnp_min_timeout));
- timeout = device[devno].bjnp_min_timeout;
- } else {
- PDBG (bjnp_dbg (LOG_INFO, "bjnp_set_timeout to %d\n",
- timeout));
- }
+ PDBG (bjnp_dbg (LOG_INFO, "bjnp_set_timeout to %d\n",
+ timeout));
- device[devno].bjnp_timeout = timeout;
+ device[devno].bjnp_scanner_timeout = timeout;
}
/** Initiate a bulk transfer read.
@@ -2490,7 +2572,7 @@ sanei_bjnp_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size)
char hostname[256];
int resp_len;
int timeout;
- int seconds;
+ int interval;
PDBG (bjnp_dbg
(LOG_INFO, "bjnp_read_int(%d, bufferptr, 0x%lx = %ld):\n", dn,
@@ -2518,17 +2600,21 @@ sanei_bjnp_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size)
}
device[dn].polling_status = BJNP_POLL_STARTED;
- /* fall through to BJNP_POLL_STARTED */
-
+ /* fall through */
case BJNP_POLL_STARTED:
- /* we use only seonds accuracy between poll attempts */
- timeout = device[dn].bjnp_timeout /1000;
+ /* we use only seonds (rounded up) accuracy between poll attempts */
+ timeout = device[dn].bjnp_scanner_timeout /1000 + 1;
+ if (device[dn].bjnp_scanner_timeout %1000 > 0)
+ {
+ timeout++;
+ }
+ interval = 1;
do
{
if ( (resp_len = bjnp_poll_scanner (dn, 2, hostname, getusername (), buffer, *size ) ) < 0 )
{
- PDBG (bjnp_dbg (LOG_NOTICE, "bjnp_read_int: Restarting polling dialog!\n"));
+ PDBG (bjnp_dbg (LOG_NOTICE, "bjnp_read_int: Poll failed, Restarting polling dialog!\n"));
device[dn].polling_status = BJNP_POLL_STOPPED;
*size = 0;
return SANE_STATUS_EOF;
@@ -2539,9 +2625,10 @@ sanei_bjnp_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size)
device[dn].polling_status = BJNP_POLL_STATUS_RECEIVED;
return SANE_STATUS_GOOD;
}
- seconds = timeout > 2 ? 2 : timeout;
- sleep(seconds);
- timeout = timeout - seconds;
+ timeout = timeout - interval;
+ if (timeout <= 0)
+ return SANE_STATUS_EOF;
+ sleep(interval);
} while ( timeout > 0 ) ;
break;
case BJNP_POLL_STATUS_RECEIVED:
diff --git a/backend/pixma_bjnp.h b/backend/pixma/pixma_bjnp.h
index a27082c..79e084e 100644
--- a/backend/pixma_bjnp.h
+++ b/backend/pixma/pixma_bjnp.h
@@ -81,12 +81,10 @@ extern void sanei_bjnp_init (void);
extern SANE_Status
sanei_bjnp_find_devices (const char **conf_devices,
SANE_Status (*attach_bjnp)
- (SANE_String_Const devname,
- SANE_String_Const makemodel,
- SANE_String_Const serial,
- const struct pixma_config_t *
- const pixma_devices[]),
- const struct pixma_config_t *const pixma_devices[]);
+ (SANE_String_Const devname,
+ SANE_String_Const serial,
+ const struct pixma_config_t *cfg),
+ const struct pixma_config_t *const pixma_devices[]);
/** Open a BJNP device.
*
diff --git a/backend/pixma_bjnp_private.h b/backend/pixma/pixma_bjnp_private.h
index 84f5c3f..edfb330 100644
--- a/backend/pixma_bjnp_private.h
+++ b/backend/pixma/pixma_bjnp_private.h
@@ -81,7 +81,9 @@
#define BJNP_BROADCAST_INTERVAL 10 /* ms between broadcasts */
#define BJNP_BC_RESPONSE_TIMEOUT 500 /* waiting time for broadc. responses */
#define BJNP_TIMEOUT_DEFAULT 10000 /* minimum tiemout value for network operations */
+#define BJNP_TIMEOUT_TCP_CONNECT 2000 /* timeout for tcp connect attempts in ms */
#define BJNP_USLEEP_MS 1000 /* sleep for 1 msec */
+#define BJNP_TCP_CONNECT_INTERVAL 100 /* TCP retry interval in ms */
/* retries */
#define BJNP_MAX_SELECT_ATTEMPTS 3 /* max nr of retries on select (EINTR) */
@@ -369,8 +371,8 @@ typedef struct device_s
/* mac-address, used as device serial no */
bjnp_sockaddr_t * addr; /* ip-address of the scanner */
int address_level; /* link local, public or has a FQDN */
- int bjnp_timeout; /* timeout (msec) for next poll command */
- int bjnp_min_timeout; /* device specific min timeout */
+ int bjnp_scanner_timeout; /* timeout (msec) for next poll command */
+ int bjnp_ip_timeout; /* device specific min timeout for the IP-protocol */
#ifdef PIXMA_BJNP_USE_STATUS
/* polling state information */
diff --git a/backend/pixma_common.c b/backend/pixma/pixma_common.c
index 82c4fde..7b7ecec 100644
--- a/backend/pixma_common.c
+++ b/backend/pixma/pixma_common.c
@@ -58,7 +58,7 @@
#include "pixma_io.h"
#include "../include/sane/sanei_usb.h"
-
+#include "../include/sane/sane.h"
#ifdef __GNUC__
# define UNUSED(v) (void) v
@@ -69,14 +69,14 @@
extern const pixma_config_t pixma_mp150_devices[];
extern const pixma_config_t pixma_mp750_devices[];
extern const pixma_config_t pixma_mp730_devices[];
-extern const pixma_config_t pixma_mp810_devices[];
+extern const pixma_config_t pixma_mp800_devices[];
extern const pixma_config_t pixma_iclass_devices[];
static const pixma_config_t *const pixma_devices[] = {
pixma_mp150_devices,
pixma_mp750_devices,
pixma_mp730_devices,
- pixma_mp810_devices,
+ pixma_mp800_devices,
pixma_iclass_devices,
NULL
};
@@ -1164,9 +1164,9 @@ pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n)
}
int
-pixma_find_scanners (const char **conf_devices)
+pixma_find_scanners (const char **conf_devices, SANE_Bool local_only)
{
- return pixma_collect_devices (conf_devices, pixma_devices);
+ return pixma_collect_devices (conf_devices, pixma_devices, local_only);
}
const char *
diff --git a/backend/pixma_common.h b/backend/pixma/pixma_common.h
index c0ed4ba..c0ed4ba 100644
--- a/backend/pixma_common.h
+++ b/backend/pixma/pixma_common.h
diff --git a/backend/pixma_imageclass.c b/backend/pixma/pixma_imageclass.c
index 9301bc6..ce0c37d 100644
--- a/backend/pixma_imageclass.c
+++ b/backend/pixma/pixma_imageclass.c
@@ -119,7 +119,9 @@
#define MF420_PID 0x27f1
#define MF260_PID 0x27f4
#define MF740_PID 0x27fb
+#define MF743_PID 0x27fc
#define MF640_PID 0x27fe
+#define MF645_PID 0x27fd
enum iclass_state_t
@@ -913,6 +915,7 @@ static const pixma_scan_ops_t pixma_iclass_ops = {
0x04a9, pid, /* vid pid */ \
1, /* iface */ \
&pixma_iclass_ops, /* ops */ \
+ 0, /* min_xdpi not used in this subdriver */ \
dpi, dpi, /* xdpi, ydpi */ \
0, /* adftpu_min_dpi not used in this subdriver */ \
adftpu_max_dpi, /* adftpu_max_dpi */ \
@@ -966,14 +969,17 @@ const pixma_config_t pixma_iclass_devices[] = {
DEV ("Canon i-SENSYS MF630 Series", "MF630", MF630_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP),
DEV ("Canon i-SENSYS MF730 Series", "MF730", MF730_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP),
DEV ("Canon i-SENSYS MF731C", "MF731", MF731_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP),
+ DEV ("Canon i-SENSYS MF633C/MF635C", "MF633C/635C", MF630_PID, 600, 0, 637, 877, PIXMA_CAP_ADFDUP), /* max. w = 216mm */
DEV ("Canon imageCLASS MF634C", "MF632C/634C", MF634_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP),
DEV ("Canon imageCLASS MF733C", "MF731C/733C", MF731_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), /* however, we need this for ethernet/wifi */
DEV ("Canon imageCLASS D570", "D570", D570_PID, 600, 0, 640, 877, 0),
DEV ("Canon i-SENSYS MF110 Series", "MF110", MF110_PID, 600, 0, 640, 1050, 0),
DEV ("Canon i-SENSYS MF520 Series", "MF520", MF520_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
DEV ("Canon i-SENSYS MF420 Series", "MF420", MF420_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
- DEV ("Canon i-SENSYS MF260 Series", "MF260", MF260_PID, 600, 0, 640, 1050, 0),
+ DEV ("Canon i-SENSYS MF260 Series", "MF260", MF260_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
DEV ("Canon i-SENSYS MF740 Series", "MF740", MF740_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
- DEV ("Canon i-SENSYS MF640 Series", "MF640", MF640_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
+ DEV ("Canon i-SENSYS MF741C/743C", "MF741C/743C", MF743_PID, 600, 300, 640, 1050, PIXMA_CAP_ADFDUP), /* ADFDUP restricted to 300dpi */
+ DEV ("Canon i-SENSYS MF640 Series", "MF642C/643C/644C", MF640_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
+ DEV ("Canon i-SENSYS MF645C", "MF645C", MF645_PID, 600, 0, 637, 877, PIXMA_CAP_ADFDUP), /* max. w = 216mm */
DEV (NULL, NULL, 0, 0, 0, 0, 0, 0)
};
diff --git a/backend/pixma_io.h b/backend/pixma/pixma_io.h
index 29bb38d..715bab5 100644
--- a/backend/pixma_io.h
+++ b/backend/pixma/pixma_io.h
@@ -93,7 +93,7 @@ void pixma_io_cleanup (void);
* \return Number of devices found */
unsigned pixma_collect_devices (const char ** conf_devices,
const struct pixma_config_t *const
- pixma_devices[]);
+ pixma_devices[], SANE_Bool local_only);
/** Get device configuration. */
const struct pixma_config_t *pixma_get_device_config (unsigned devnr);
diff --git a/backend/pixma_io_sanei.c b/backend/pixma/pixma_io_sanei.c
index 4710cf4..c30b404 100644
--- a/backend/pixma_io_sanei.c
+++ b/backend/pixma/pixma_io_sanei.c
@@ -55,6 +55,7 @@
#include "pixma_bjnp.h"
#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sane.h"
#ifdef __GNUC__
@@ -106,39 +107,6 @@ get_scanner_info (unsigned devnr)
return si;
}
-static const struct pixma_config_t *lookup_scanner(const char *makemodel,
- const struct pixma_config_t *const pixma_devices[])
-{
- int i;
- const struct pixma_config_t *cfg;
- char *match;
-
- for (i = 0; pixma_devices[i]; i++)
- {
- /* loop through the device classes (mp150, mp730 etc) */
- for (cfg = pixma_devices[i]; cfg->name; cfg++)
- {
- /* loop through devices in class */
- if ((match = strcasestr (makemodel, cfg->model)) != NULL)
- {
- /* possible match found, make sure it is not a partial match */
- /* MP600 and MP600R are different models! */
- /* some models contain ranges, so check for a '-' too */
-
- if ((match[strlen(cfg->model)] == ' ') ||
- (match[strlen(cfg->model)] == '\0') ||
- (match[strlen(cfg->model)] == '-'))
- {
- pixma_dbg (3, "Scanner model found: Name %s(%s) matches %s\n", cfg->model, cfg->name, makemodel);
- return cfg;
- }
- }
- pixma_dbg (20, "Scanner model %s(%s) not found, giving up! %s\n", cfg->model, cfg->name, makemodel);
- }
- }
- return NULL;
-}
-
static SANE_Status
attach (SANE_String_Const devname)
{
@@ -159,13 +127,11 @@ attach (SANE_String_Const devname)
static SANE_Status
-attach_bjnp (SANE_String_Const devname, SANE_String_Const makemodel,
+attach_bjnp (SANE_String_Const devname,
SANE_String_Const serial,
- const struct pixma_config_t *const pixma_devices[])
+ const struct pixma_config_t *cfg)
{
scanner_info_t *si;
- const pixma_config_t *cfg;
- SANE_Status error;
si = (scanner_info_t *) calloc (1, sizeof (*si));
if (!si)
@@ -173,19 +139,14 @@ attach_bjnp (SANE_String_Const devname, SANE_String_Const makemodel,
si->devname = strdup (devname);
if (!si->devname)
return SANE_STATUS_NO_MEM;
- if ((cfg = lookup_scanner(makemodel, pixma_devices)) == (struct pixma_config_t *)NULL)
- error = SANE_STATUS_INVAL;
- else
- {
- si->cfg = cfg;
- sprintf(si->serial, "%s_%s", cfg->model, serial);
- si -> interface = INT_BJNP;
- si->next = first_scanner;
- first_scanner = si;
- nscanners++;
- error = SANE_STATUS_GOOD;
- }
- return error;
+
+ si->cfg = cfg;
+ sprintf(si->serial, "%s_%s", cfg->model, serial);
+ si -> interface = INT_BJNP;
+ si->next = first_scanner;
+ first_scanner = si;
+ nscanners++;
+ return SANE_STATUS_GOOD;
}
static void
@@ -349,7 +310,7 @@ pixma_io_cleanup (void)
unsigned
pixma_collect_devices (const char **conf_devices,
- const struct pixma_config_t *const pixma_devices[])
+ const struct pixma_config_t *const pixma_devices[], SANE_Bool local_only)
{
unsigned i, j;
struct scanner_info_t *si;
@@ -374,7 +335,9 @@ pixma_collect_devices (const char **conf_devices,
}
}
}
- sanei_bjnp_find_devices(conf_devices, attach_bjnp, pixma_devices);
+ if (! local_only)
+ sanei_bjnp_find_devices(conf_devices, attach_bjnp, pixma_devices);
+
si = first_scanner;
while (j < nscanners)
{
diff --git a/backend/pixma_mp150.c b/backend/pixma/pixma_mp150.c
index 5f0a4ac..3973702 100644
--- a/backend/pixma_mp150.c
+++ b/backend/pixma/pixma_mp150.c
@@ -49,10 +49,6 @@
4. cancel using ctrl-c (must send abort command)
*/
-#define TPU_48 /* uncomment to activate TPU scan at 48 bits */
-/*#define DEBUG_TPU_48*/ /* uncomment to debug 48 bits TPU on a non TPU device */
-/*#define DEBUG_TPU_24*/ /* uncomment to debug 24 bits TPU on a non TPU device */
-
#include "../include/sane/config.h"
#include <stdio.h>
@@ -101,9 +97,6 @@
#define MP450_PID 0x170b
#define MP500_PID 0x170c
#define MP530_PID 0x1712
-#define MP800_PID 0x170d
-#define MP800R_PID 0x170e
-#define MP830_PID 0x1713
/* Generation 2 */
#define MP160_PID 0x1714
@@ -266,6 +259,7 @@
/* 2019 new devices (untested) */
#define TS8100_PID 0x1821
+#define G2010_PID 0x183a
#define G3010_PID 0x183b
#define G4010_PID 0x183d
#define TS9180_PID 0x183e
@@ -288,7 +282,21 @@
#define TS8230_PID 0x185b
#define TS9580_PID 0x185d
#define TR9530_PID 0x185e
+#define G6000_PID 0x1865
+#define G6080_PID 0x1866
#define XK80_PID 0x1873
+#define TS5300_PID 0x188b
+#define TS5380_PID 0x188c
+#define TS6300_PID 0x188d
+#define TS6380_PID 0x188e
+#define TS7330_PID 0x188f
+#define TS8300_PID 0x1890
+#define TS8380_PID 0x1891
+#define TS8330_PID 0x1892
+#define XK60_PID 0x1893
+#define TS6330_PID 0x1894
+#define TS3300_PID 0x18a2
+#define E3300_PID 0x18a3
/* Generation 4 XML messages that encapsulates the Pixma protocol messages */
#define XML_START_1 \
@@ -336,15 +344,9 @@ enum mp150_cmd_t
cmd_read_image = 0xd420,
cmd_error_info = 0xff20,
- cmd_start_calibrate_ccd_3 = 0xd520,
- cmd_end_calibrate_ccd_3 = 0xd720,
cmd_scan_param_3 = 0xd820,
cmd_scan_start_3 = 0xd920,
cmd_status_3 = 0xda20,
- cmd_get_tpu_info_3 = 0xf520,
- cmd_set_tpu_info_3 = 0xea20,
-
- cmd_e920 = 0xe920 /* seen in MP800 */
};
typedef struct mp150_t
@@ -355,16 +357,17 @@ typedef struct mp150_t
uint8_t current_status[16];
unsigned last_block;
uint8_t generation;
- /* for Generation 3 and CCD shift */
+ /* for Generation 3 shift */
uint8_t *linebuf;
uint8_t *data_left_ofs;
unsigned data_left_len;
- int shift[3];
- unsigned color_shift;
- unsigned stripe_shift;
- uint8_t tpu_datalen;
- uint8_t tpu_data[0x40];
uint8_t adf_state; /* handle adf scanning */
+ unsigned scale; /* Scale factor for lower resolutions, the
+ * scanner doesn't support. We scale down the
+ * image after scanning minimum possible
+ * resolution.
+ */
+
} mp150_t;
/*
@@ -442,12 +445,6 @@ is_scanning_jpeg (pixma_t *s)
}
static int
-is_scanning_from_tpu (pixma_t * s)
-{
- return (s->param->source == PIXMA_SOURCE_TPU);
-}
-
-static int
send_xml_dialog (pixma_t * s, const char * xml_message)
{
mp150_t *mp = (mp150_t *) s->subdriver;
@@ -466,19 +463,13 @@ send_xml_dialog (pixma_t * s, const char * xml_message)
return (strcasestr ((const char *) mp->cb.buf, XML_OK) != NULL);
}
-static void
-new_cmd_tpu_msg (pixma_t *s, pixma_cmdbuf_t * cb, uint16_t cmd)
-{
- pixma_newcmd (cb, cmd, 0, 0);
- cb->buf[3] = (is_scanning_from_tpu (s)) ? 0x01 : 0x00;
-}
-
static int
start_session (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
- new_cmd_tpu_msg (s, &mp->cb, cmd_start_session);
+ pixma_newcmd (&mp->cb, cmd_start_session, 0, 0);
+ mp->cb.buf[3] = 0x00;
return pixma_exec (s, &mp->cb);
}
@@ -487,17 +478,8 @@ start_scan_3 (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
- new_cmd_tpu_msg (s, &mp->cb, cmd_scan_start_3);
- return pixma_exec (s, &mp->cb);
-}
-
-static int
-send_cmd_start_calibrate_ccd_3 (pixma_t * s)
-{
- mp150_t *mp = (mp150_t *) s->subdriver;
-
- pixma_newcmd (&mp->cb, cmd_start_calibrate_ccd_3, 0, 0);
- mp->cb.buf[3] = 0x01;
+ pixma_newcmd (&mp->cb, cmd_scan_start_3, 0, 0);
+ mp->cb.buf[3] = 0x00;
return pixma_exec (s, &mp->cb);
}
@@ -546,13 +528,6 @@ abort_session (pixma_t * s)
}
static int
-send_cmd_e920 (pixma_t * s)
-{
- mp150_t *mp = (mp150_t *) s->subdriver;
- return pixma_exec_short_cmd (s, &mp->cb, cmd_e920);
-}
-
-static int
select_source (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
@@ -579,41 +554,13 @@ select_source (pixma_t * s)
data[6] = 3;
break;
- case PIXMA_SOURCE_TPU:
- data[0] = 4;
- data[1] = 2;
- break;
+ default:
+ return PIXMA_EPROTO;
}
return pixma_exec (s, &mp->cb);
}
static int
-send_get_tpu_info_3 (pixma_t * s)
-{
- mp150_t *mp = (mp150_t *) s->subdriver;
- uint8_t *data;
- int error;
-
- data = pixma_newcmd (&mp->cb, cmd_get_tpu_info_3, 0, 0x34);
- RET_IF_ERR (pixma_exec (s, &mp->cb));
- memcpy (mp->tpu_data, data, 0x34);
- return error;
-}
-
-static int
-send_set_tpu_info (pixma_t * s)
-{
- mp150_t *mp = (mp150_t *) s->subdriver;
- uint8_t *data;
-
- if (mp->tpu_datalen == 0)
- return 0;
- data = pixma_newcmd (&mp->cb, cmd_set_tpu_info_3, 0x34, 0);
- memcpy (data, mp->tpu_data, 0x34);
- return pixma_exec (s, &mp->cb);
-}
-
-static int
send_gamma_table (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
@@ -670,7 +617,7 @@ calc_raw_width (const mp150_t * mp, const pixma_scan_param_t * param)
other models, too? */
if (mp->generation >= 2)
{
- raw_width = ALIGN_SUP (param->w + param->xs, 32);
+ raw_width = ALIGN_SUP ((param->w * mp->scale) + param->xs, 32);
/* PDBG (pixma_dbg (4, "*calc_raw_width***** width %i extended by %i and rounded to %i *****\n", param->w, param->xs, raw_width)); */
}
else if (param->channels == 1)
@@ -684,82 +631,16 @@ calc_raw_width (const mp150_t * mp, const pixma_scan_param_t * param)
return raw_width;
}
-static int
-has_ccd_sensor (pixma_t * s)
-{
- return ((s->cfg->cap & PIXMA_CAP_CCD) != 0);
-}
-
-static int
-is_ccd_grayscale (pixma_t * s)
-{
- return (has_ccd_sensor (s) && (s->param->channels == 1) && !s->param->software_lineart);
-}
-
-static int
-is_ccd_lineart (pixma_t * s)
-{
- return (has_ccd_sensor (s) && s->param->software_lineart);
-}
-
-/* CCD sensors don't have neither a Grayscale mode nor a Lineart mode,
- * but use color mode instead */
static unsigned
-get_cis_ccd_line_size (pixma_t * s)
-{
- return ((s->param->wx ? s->param->line_size / s->param->w * s->param->wx
- : s->param->line_size) * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : 1));
-}
-
-static unsigned
-calc_shifting (pixma_t * s)
+get_cis_line_size (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
- /* If stripes shift needed (CCD devices), how many pixels shift */
- mp->stripe_shift = 0;
- /* If color plane shift (CCD devices), how many pixels shift */
- mp->color_shift = mp->shift[0] = mp->shift[1] = mp->shift[2] = 0;
-
- switch (s->cfg->pid)
- {
- case MP800_PID:
- case MP800R_PID:
- case MP830_PID:
- if (s->param->xdpi == 2400)
- {
- if (is_scanning_from_tpu(s))
- mp->stripe_shift = 6;
- else
- mp->stripe_shift = 3;
- }
- if (s->param->ydpi > 75)
- {
- mp->color_shift = s->param->ydpi / ((s->param->ydpi < 1200) ? 150 : 75);
-
- if (is_scanning_from_tpu (s))
- mp->color_shift = s->param->ydpi / 75;
-
- /* If you're trying to decipher this color-shifting code,
- the following line is where the magic is revealed. */
- mp->shift[1] = mp->color_shift * get_cis_ccd_line_size (s);
- if (is_scanning_from_adf (s))
- { /* ADF */
- mp->shift[0] = 0;
- mp->shift[2] = 2 * mp->shift[1];
- }
- else
- { /* Flatbed or TPU */
- mp->shift[0] = 2 * mp->shift[1];
- mp->shift[2] = 0;
- }
- }
- break;
+ /*PDBG (pixma_dbg (4, "%s: line_size=%ld, w=%d, wx=%d, scale=%d\n",
+ __func__, s->param->line_size, s->param->w, s->param->wx, mp->scale));*/
- default: /* Default, and all CIS devices */
- break;
- }
- return (2 * mp->color_shift + mp->stripe_shift);
+ return (s->param->wx ? s->param->line_size / s->param->w * s->param->wx
+ : s->param->line_size) * mp->scale;
}
static int
@@ -767,33 +648,31 @@ send_scan_param (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
uint8_t *data;
- unsigned raw_width = calc_raw_width (mp, s->param);
- unsigned h = MIN (s->param->h + calc_shifting (s),
- s->cfg->height * s->param->ydpi / 75);
-
- /* TPU scan does not support lineart */
- if (is_scanning_from_tpu (s) && is_ccd_lineart (s))
- {
- return PIXMA_ENOTSUP;
- }
+ unsigned xdpi = s->param->xdpi * mp->scale;
+ unsigned ydpi = s->param->xdpi * mp->scale;
+ unsigned x = s->param->x * mp->scale;
+ unsigned xs = s->param->xs;
+ unsigned y = s->param->y * mp->scale;
+ unsigned wx = calc_raw_width (mp, s->param);
+ unsigned h = MIN (s->param->h, s->cfg->height * s->param->ydpi / 75) * mp->scale;
if (mp->generation <= 2)
{
- /*PDBG (pixma_dbg (4, "*send_scan_param gen. 1-2 ***** Setting: xdpi=%hi ydpi=%hi x=%i y=%i w=%i ***** \n",
- s->param->xdpi,s->param->ydpi,(s->param->x)-(s->param->xs),s->param->y,raw_width));*/
+ PDBG (pixma_dbg (4, "*send_scan_param gen. 1-2 ***** Setting: xdpi=%hi ydpi=%hi x=%i y=%i wx=%i ***** \n",
+ xdpi, ydpi, x-xs, y, wx));
data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x30, 0);
- pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x04);
- pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x06);
- pixma_set_be32 (s->param->x, data + 0x08);
+ pixma_set_be16 (xdpi | 0x8000, data + 0x04);
+ pixma_set_be16 (ydpi | 0x8000, data + 0x06);
+ pixma_set_be32 (x, data + 0x08);
if (mp->generation == 2)
- pixma_set_be32 (s->param->x - s->param->xs, data + 0x08);
- pixma_set_be32 (s->param->y, data + 0x0c);
- pixma_set_be32 (raw_width, data + 0x10);
+ pixma_set_be32 (x - s->param->xs, data + 0x08);
+ pixma_set_be32 (y, data + 0x0c);
+ pixma_set_be32 (wx, data + 0x10);
pixma_set_be32 (h, data + 0x14);
- data[0x18] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04;
+ data[0x18] = (s->param->channels != 1) ? 0x08 : 0x04;
data[0x19] = ((s->param->software_lineart) ? 8 : s->param->depth)
- * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels); /* bits per pixel */
- data[0x1a] = (is_scanning_from_tpu (s) ? 1 : 0);
+ * s->param->channels; /* bits per pixel */
+ data[0x1a] = 0;
data[0x20] = 0xff;
data[0x23] = 0x81;
data[0x26] = 0x02;
@@ -801,15 +680,11 @@ send_scan_param (pixma_t * s)
}
else
{
+ PDBG (pixma_dbg (4, "*send_scan_param gen. 3+ ***** Setting: xdpi=%hi ydpi=%hi x=%i xs=%i y=%i wx=%i h=%i ***** \n",
+ xdpi, ydpi, x, xs, y, wx, h));
data = pixma_newcmd (&mp->cb, cmd_scan_param_3, 0x38, 0);
data[0x00] = (is_scanning_from_adf (s)) ? 0x02 : 0x01;
data[0x01] = 0x01;
- if (is_scanning_from_tpu (s))
- {
- data[0x00] = 0x04;
- data[0x01] = 0x02;
- data[0x1e] = 0x02;
- }
data[0x02] = 0x01;
if (is_scanning_from_adfdup (s))
{
@@ -824,23 +699,16 @@ send_scan_param (pixma_t * s)
{
data[0x05] = 0x01; /* This one also seen at 0. Don't know yet what's used for */
}
- pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x08);
- pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x0a);
- /*PDBG (pixma_dbg (4, "*send_scan_param gen. 3+ ***** Setting: xdpi=%hi ydpi=%hi x=%i y=%i w=%i ***** \n",
- s->param->xdpi,s->param->ydpi,(s->param->x)-(s->param->xs),s->param->y,raw_width));*/
- pixma_set_be32 (s->param->x - s->param->xs, data + 0x0c);
- pixma_set_be32 (s->param->y, data + 0x10);
- pixma_set_be32 (raw_width, data + 0x14);
+ pixma_set_be16 (xdpi | 0x8000, data + 0x08);
+ pixma_set_be16 (ydpi | 0x8000, data + 0x0a);
+ pixma_set_be32 (x - xs, data + 0x0c);
+ pixma_set_be32 (y, data + 0x10);
+ pixma_set_be32 (wx, data + 0x14);
pixma_set_be32 (h, data + 0x18);
- data[0x1c] = ((s->param->channels != 1) || is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 0x08 : 0x04;
+ data[0x1c] = (s->param->channels != 1) ? 0x08 : 0x04;
-#ifdef DEBUG_TPU_48
- data[0x1d] = 24;
-#else
- data[0x1d] = (is_scanning_from_tpu (s)) ? 48
- : (((s->param->software_lineart) ? 8 : s->param->depth)
- * ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels)); /* bits per pixel */
-#endif
+ data[0x1d] = ((s->param->software_lineart) ? 8 : s->param->depth)
+ * s->param->channels; /* bits per pixel */
data[0x1f] = 0x01; /* This one also seen at 0. Don't know yet what's used for */
data[0x20] = 0xff;
@@ -917,10 +785,7 @@ send_time (pixma_t * s)
data = pixma_newcmd (&mp->cb, cmd_time, 20, 0);
pixma_get_time (&now, NULL);
t = localtime (&now);
- snprintf ((char *) data, 16,
- "%02d/%02d/%02d %02d:%02d",
- t->tm_year % 100, t->tm_mon + 1, t->tm_mday,
- t->tm_hour, t->tm_min);
+ strftime ((char *) data, 16, "%y/%m/%d %H:%M", t);
PDBG (pixma_dbg (3, "Sending time: '%s'\n", (char *) data));
return pixma_exec (s, &mp->cb);
}
@@ -1036,7 +901,8 @@ handle_interrupt (pixma_t * s, int timeout)
|| s->cfg->pid == MX720_PID
|| s->cfg->pid == MX920_PID
|| s->cfg->pid == MB2300_PID
- || s->cfg->pid == MB5000_PID)
+ || s->cfg->pid == MB5000_PID
+ || s->cfg->pid == MB5400_PID)
/* button no. in buf[7]
* size in buf[10] 01=A4; 02=Letter; 08=10x15; 09=13x18; 0b=auto
* format in buf[11] 01=JPEG; 02=TIFF; 03=PDF; 04=Kompakt-PDF
@@ -1079,32 +945,6 @@ handle_interrupt (pixma_t * s, int timeout)
}
static int
-init_ccd_lamp_3 (pixma_t * s)
-{
- mp150_t *mp = (mp150_t *) s->subdriver;
- uint8_t *data;
- int error, status_len, tmo;
-
- status_len = 8;
- RET_IF_ERR (query_status (s));
- RET_IF_ERR (query_status (s));
- RET_IF_ERR (send_cmd_start_calibrate_ccd_3 (s));
- RET_IF_ERR (query_status (s));
- tmo = 20; /* like Windows driver, CCD lamp adjustment */
- while (--tmo >= 0)
- {
- data = pixma_newcmd (&mp->cb, cmd_end_calibrate_ccd_3, 0, status_len);
- RET_IF_ERR (pixma_exec (s, &mp->cb));
- memcpy (mp->current_status, data, status_len);
- PDBG (pixma_dbg (3, "Lamp status: %u , timeout in: %u\n", data[0], tmo));
- if (mp->current_status[0] == 3 || !is_scanning_from_tpu (s))
- break;
- WAIT_INTERRUPT (1000);
- }
- return error;
-}
-
-static int
wait_until_ready (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
@@ -1118,8 +958,7 @@ wait_until_ready (pixma_t * s)
if (mp->generation >= 3)
RET_IF_ERR (query_status_3 (s));
else if (s->cfg->pid == MP600_PID ||
- s->cfg->pid == MP600R_PID ||
- s->cfg->pid == MP800R_PID)
+ s->cfg->pid == MP600R_PID)
RET_IF_ERR (query_status (s));
if (--tmo == 0)
{
@@ -1131,30 +970,6 @@ wait_until_ready (pixma_t * s)
return 0;
}
-static uint8_t *
-shift_colors (uint8_t * dptr, uint8_t * sptr,
- unsigned w, unsigned dpi, unsigned pid, unsigned c,
- int * colshft, unsigned strshft)
-{
- unsigned i, sr, sg, sb, st;
- UNUSED(dpi);
- UNUSED(pid);
- sr = colshft[0]; sg = colshft[1]; sb = colshft[2];
- for (i = 0; i < w; i++)
- {
- /* stripes shift for MP800, MP800R at 2400 dpi */
- st = (i % 2 == 0) ? strshft : 0;
-
- *sptr++ = *(dptr++ + sr + st);
- if (c == 6) *sptr++ = *(dptr++ + sr + st);
- *sptr++ = *(dptr++ + sg + st);
- if (c == 6) *sptr++ = *(dptr++ + sg + st);
- *sptr++ = *(dptr++ + sb + st);
- if (c == 6) *sptr++ = *(dptr++ + sb + st);
- }
- return dptr;
-}
-
static void
reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c, unsigned n,
unsigned m, unsigned w, unsigned line_size)
@@ -1168,32 +983,69 @@ reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c, unsigned n,
memcpy (sptr, linebuf, line_size);
}
-#ifndef TPU_48
-static unsigned
-pack_48_24_bpc (uint8_t * sptr, unsigned n)
+/* the scanned image must be shrinked by factor "scale"
+ * the image can be formatted as rgb (c=3) or gray (c=1)
+ * we need to crop the left side (xs)
+ * we ignore more pixels inside scanned line (wx), behind needed line (w)
+ *
+ * example (scale=2):
+ * line | pixel[0] | pixel[1] | ... | pixel[w-1]
+ * ---------
+ * 0 | rgbrgb | rgbrgb | ... | rgbrgb
+ * wx*c | rgbrgb | rgbrgb | ... | rgbrgb
+ */
+uint8_t *
+shrink_image (uint8_t * dptr, uint8_t * sptr, unsigned xs, unsigned w,
+ unsigned wx, unsigned scale, unsigned c)
{
- unsigned i;
- uint8_t *cptr, lsb;
- static uint8_t offset = 0;
+ unsigned i, ic;
+ uint16_t pixel;
+ uint8_t *dst = dptr; /* don't change dptr */
+ uint8_t *src = sptr; /* don't change sptr */
+
+ /*PDBG (pixma_dbg (4, "%s: w=%d, wx=%d, c=%d, scale=%d\n",
+ __func__, w, wx, c, scale));
+ PDBG (pixma_dbg (4, "\tdptr=%ld, sptr=%ld\n",
+ dptr, sptr));*/
+
+ /* crop left side */
+ src += c * xs;
- cptr = sptr;
- if (n % 2 != 0)
- PDBG (pixma_dbg (3, "WARNING: misaligned image.\n"));
- for (i = 0; i < n; i += 2)
+ /* process line */
+ for (i = 0; i < w; i++)
+ {
+ /* process rgb or gray pixel */
+ for (ic = 0; ic < c; ic++)
{
- /* offset = 1 + (offset % 3); */
- lsb = *sptr++;
- *cptr++ = ((*sptr++) << offset) | lsb >> (8 - offset);
+#if 0
+ dst[ic] = src[ic];
+#else
+ pixel = 0;
+
+ /* sum shrink pixels */
+ for (unsigned m = 0; m < scale; m++) /* get pixels from shrinked lines */
+ {
+ for (unsigned n = 0; n < scale; n++) /* get pixels from same line */
+ {
+ pixel += src[ic + c * n + wx * c * m];
+ }
+ }
+ dst[ic] = pixel / (scale * scale);
+#endif
}
- return (n / 2);
+
+ /* jump over shrinked data */
+ src += c * scale;
+ /* next pixel */
+ dst += c;
+ }
+
+ return dst;
}
-#endif
-/* This function deals both with PIXMA CCD sensors producing shifted color
- * planes images, Grayscale CCD scan and Generation >= 3 high dpi images.
- * Each complete line in mp->imgbuf is processed for shifting CCD sensor
- * color planes, reordering pixels above 600 dpi for Generation >= 3, and
- * converting to Grayscale for CCD sensors. */
+/* This function deals with Generation >= 3 high dpi images.
+ * Each complete line in mp->imgbuf is processed for reordering pixels above
+ * 600 dpi for Generation >= 3. */
static unsigned
post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib)
{
@@ -1209,46 +1061,48 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib)
return 0; /* # of non processed bytes */
}
+ /* process image sizes */
+ c = s->param->channels
+ * ((s->param->software_lineart) ? 8 : s->param->depth) / 8; /* color channels count */
+ cw = c * s->param->w; /* image width */
+ cx = c * s->param->xs; /* x-offset */
- c = ((is_ccd_grayscale (s) || is_ccd_lineart (s)) ? 3 : s->param->channels)
- * ((s->param->software_lineart) ? 8 : s->param->depth) / 8;
- cw = c * s->param->w;
- cx = c * s->param->xs;
-
+ /* special image format parameters
+ * n: no. of sub-images
+ * m: sub-image width
+ */
if (mp->generation >= 3)
n = s->param->xdpi / 600;
- else /* FIXME: maybe need different values for CIS and CCD sensors */
+ else
n = s->param->xdpi / 2400;
-
if (s->cfg->pid == MP600_PID || s->cfg->pid == MP600R_PID)
n = s->param->xdpi / 1200;
-
m = (n > 0) ? s->param->wx / n : 1;
+
+ /* Initialize pointers */
sptr = dptr = gptr = cptr = mp->imgbuf;
- line_size = get_cis_ccd_line_size (s);
- /*PDBG (pixma_dbg (4, "*post_process_image_data***** ----- Set n=%u, m=%u, line_size=%u ----- ***** \n", n, m, line_size));*/
+ /* walk through complete received lines */
+ line_size = get_cis_line_size (s);
lines = (mp->data_left_ofs - mp->imgbuf) / line_size;
- /*PDBG (pixma_dbg (4, "*post_process_image_data***** lines = %i > 2 * mp->color_shift + mp->stripe_shift = %i ***** \n",
- lines, 2 * mp->color_shift + mp->stripe_shift));*/
- if (lines > 2 * mp->color_shift + mp->stripe_shift)
+ if (lines > 0)
{
unsigned i;
- lines -= 2 * mp->color_shift + mp->stripe_shift;
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** Processing with c=%u, n=%u, m=%u, wx=%i, line_size=%u, cx=%u, cw=%u ***** \n",
+ c, n, m, s->param->wx, line_size, cx, cw));*/
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** lines = %i ***** \n", lines));*/
+
for (i = 0; i < lines; i++, sptr += line_size)
{
- /* Color plane and stripes shift needed by e.g. CCD */
/*PDBG (pixma_dbg (4, "*post_process_image_data***** Processing with c=%u, n=%u, m=%u, w=%i, line_size=%u ***** \n",
- c, n, m, s->param->wx, line_size));*/
- if (s->cfg->pid != MG5300_PID && s->cfg->pid != MG6300_PID && c >= 3)
- dptr = shift_colors (dptr, sptr,
- s->param->wx, s->param->xdpi, s->cfg->pid, c,
- mp->shift, mp->stripe_shift);
+ c, n, m, s->param->wx, line_size));*/
+ /*PDBG (pixma_dbg (4, "*post_process_image_data***** Pointers: sptr=%lx, dptr=%lx, linebuf=%lx ***** \n",
+ sptr, dptr, mp->linebuf));*/
/* special image format for *most* devices at high dpi.
* MP220, MX360 and generation 5 scanners are exceptions */
- if (n > 0
+ if (n > 1
&& s->cfg->pid != MP220_PID
&& s->cfg->pid != MP490_PID
&& s->cfg->pid != MX360_PID
@@ -1266,15 +1120,22 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib)
|| s->cfg->pid == MX520_PID))
reorder_pixels (mp->linebuf, sptr, c, n, m, s->param->wx, line_size);
- /* Crop line to selected borders */
- memmove(cptr, sptr + cx, cw);
+
+ /* scale image */
+ if (mp->scale > 1)
+ {
+ /* Crop line inside shrink_image() */
+ shrink_image(cptr, sptr, s->param->xs, s->param->w, s->param->wx, mp->scale, c);
+ }
+ else
+ {
+ /* Crop line to selected borders */
+ memmove(cptr, sptr + cx, cw);
+ }
/* Color / Gray to Lineart convert */
if (s->param->software_lineart)
cptr = gptr = pixma_binarize_line (s->param, gptr, cptr, s->param->w, c);
- /* Color to Grayscale convert for CCD sensor */
- else if (is_ccd_grayscale (s))
- cptr = gptr = pixma_rgb_to_gray (gptr, cptr, s->param->w, c);
else
cptr += cw;
}
@@ -1330,9 +1191,6 @@ mp150_open (pixma_t * s)
PDBG (pixma_dbg (3, "*mp150_open***** This is a generation %d scanner. *****\n", mp->generation));
- /* TPU info data setup */
- mp->tpu_datalen = 0;
-
/* adf scanning */
mp->adf_state = state_idle;
@@ -1340,8 +1198,6 @@ mp150_open (pixma_t * s)
{
query_status (s);
handle_interrupt (s, 200);
- if (mp->generation == 3 && has_ccd_sensor (s))
- send_cmd_start_calibrate_ccd_3 (s);
}
return 0;
}
@@ -1370,12 +1226,6 @@ mp150_check_param (pixma_t * s, pixma_scan_param_t * sp)
{
sp->software_lineart = 0;
sp->depth = 8;
-#ifdef TPU_48
-#ifndef DEBUG_TPU_48
- if (sp->source == PIXMA_SOURCE_TPU)
-#endif
- sp->depth = 16; /* TPU in 16 bits mode */
-#endif
}
else
{
@@ -1403,14 +1253,14 @@ mp150_check_param (pixma_t * s, pixma_scan_param_t * sp)
{
/* mod 32 and expansion of the X scan limits */
/*PDBG (pixma_dbg (4, "*mp150_check_param***** ----- Initially: x=%i, y=%i, w=%i, h=%i *****\n", sp->x, sp->y, sp->w, sp->h));*/
- sp->xs = (sp->x) % 32;
+ sp->xs = (sp->x * mp->scale) % 32;
}
else
sp->xs = 0;
/*PDBG (pixma_dbg (4, "*mp150_check_param***** Selected origin, origin shift: %i, %i *****\n", sp->x, sp->xs));*/
sp->wx = calc_raw_width (mp, sp);
sp->line_size = sp->w * sp->channels * (((sp->software_lineart) ? 8 : sp->depth) / 8); /* bytes per line per color after cropping */
- /*PDBG (pixma_dbg (4, "*mp150_check_param***** Final scan width and line-size: %i, %i *****\n", sp->wx, sp->line_size));*/
+ /*PDBG (pixma_dbg (4, "*mp150_check_param***** Final scan width and line-size: %i, %li *****\n", sp->wx, sp->line_size));*/
/* Some exceptions here for particular devices */
/* Those devices can scan up to legal 14" with ADF, but A4 11.7" in flatbed */
@@ -1418,27 +1268,6 @@ mp150_check_param (pixma_t * s, pixma_scan_param_t * sp)
if ((s->cfg->cap & PIXMA_CAP_ADF) && sp->source == PIXMA_SOURCE_FLATBED)
sp->h = MIN (sp->h, 877 * sp->xdpi / 75);
- if (sp->source == PIXMA_SOURCE_TPU
- || s->cfg->pid == LIDE300_PID
- || s->cfg->pid == LIDE400_PID)
- {
- uint8_t k;
-
- /* TPU mode: lowest res is 150 or 300 dpi */
- if (mp->generation >= 3)
- k = MAX (sp->xdpi, 300) / sp->xdpi;
- else
- k = MAX (sp->xdpi, 150) / sp->xdpi;
- sp->x *= k;
- sp->xs *= k;
- sp->y *= k;
- sp->w *= k;
- sp->wx *= k;
- sp->h *= k;
- sp->xdpi *= k;
- sp->ydpi = sp->xdpi;
- }
-
if (sp->source == PIXMA_SOURCE_ADF || sp->source == PIXMA_SOURCE_ADFDUP)
{
uint8_t k = 1;
@@ -1460,6 +1289,14 @@ mp150_check_param (pixma_t * s, pixma_scan_param_t * sp)
(sp->source == PIXMA_SOURCE_ADF ||
sp->source == PIXMA_SOURCE_ADFDUP);
+ mp->scale = 1;
+ if (s->cfg->min_xdpi && sp->xdpi < s->cfg->min_xdpi)
+ {
+ mp->scale = s->cfg->min_xdpi / sp->xdpi;
+ }
+ /*PDBG (pixma_dbg (4, "*mp150_check_param***** xdpi=%u, min_xdpi=%u, scale=%u *****\n",
+ sp->xdpi, s->cfg->min_xdpi, mp->scale));*/
+
/*PDBG (pixma_dbg (4, "*mp150_check_param***** Finally: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u *****\n",
sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx));*/
return 0;
@@ -1534,33 +1371,6 @@ mp150_scan (pixma_t * s)
}
}
- if (has_ccd_sensor (s) && (mp->generation <= 2))
- {
- error = send_cmd_e920 (s);
- switch (error)
- {
- case PIXMA_ECANCELED:
- case PIXMA_EBUSY:
- PDBG (pixma_dbg (2, "cmd e920 or d520 returned %s\n",
- pixma_strerror (error)));
- /* fall through */
- case 0:
- query_status (s);
- break;
- default:
- PDBG (pixma_dbg (1, "WARNING:cmd e920 or d520 failed %s\n",
- pixma_strerror (error)));
- return error;
- }
- tmo = 3; /* like Windows driver, CCD calibration ? */
- while (--tmo >= 0)
- {
- WAIT_INTERRUPT (1000);
- PDBG (pixma_dbg (2, "CCD Calibration ends in %d sec.\n", tmo));
- }
- /* pixma_sleep(2000000); */
- }
-
tmo = 10;
/* adf: first page or idle */
if (mp->generation <= 2 || mp->adf_state == state_idle)
@@ -1593,17 +1403,13 @@ mp150_scan (pixma_t * s)
mp->state = state_warmup;
if ((error >= 0) && (mp->generation <= 2))
error = select_source (s);
- if ((error >= 0) && (mp->generation >= 3) && has_ccd_sensor (s))
- error = init_ccd_lamp_3 (s);
- if ((error >= 0) && !is_scanning_from_tpu (s) && !is_scanning_jpeg (s))
+ if ((error >= 0) && !is_scanning_jpeg (s))
{
int i;
for (i = (mp->generation >= 3) ? 3 : 1 ; i > 0 && error >= 0; i--)
error = send_gamma_table (s);
}
- else if (error >= 0) /* in TPU mode, for gen 1, 2, and 3 */
- error = send_set_tpu_info (s);
}
else /* ADF pageid != 0 and gen3 or above */
{ /* next sheet from ADF */
@@ -1645,8 +1451,8 @@ mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
mp->state = state_scanning;
mp->last_block = 0;
- line_size = get_cis_ccd_line_size (s);
- proc_buf_size = (2 * calc_shifting (s) + 2) * line_size;
+ line_size = get_cis_line_size (s);
+ proc_buf_size = 2 * line_size;
mp->cb.buf = realloc (mp->cb.buf,
CMDBUF_SIZE + IMAGE_BLOCK_SIZE + proc_buf_size);
if (!mp->cb.buf)
@@ -1699,15 +1505,6 @@ mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
{ /* no image data at this moment. */
pixma_sleep (10000);
}
- /* For TPU at 48 bits/pixel to output at 24 bits/pixel */
-#ifndef DEBUG_TPU_48
-#ifndef TPU_48
-#ifndef DEBUG_TPU_24
- if (is_scanning_from_tpu (s))
-#endif
- bytes_received = pack_48_24_bpc (mp->imgbuf + mp->data_left_len, bytes_received);
-#endif
-#endif
/* Post-process the image data */
mp->data_left_ofs = mp->imgbuf + mp->data_left_len + bytes_received;
mp->data_left_len = post_process_image_data (s, ib);
@@ -1732,9 +1529,6 @@ mp150_finish_scan (pixma_t * s)
case state_scanning:
case state_warmup:
case state_finished:
- /* Send the get TPU info message */
- if (is_scanning_from_tpu (s) && mp->tpu_datalen == 0)
- send_get_tpu_info_3 (s);
/* FIXME: to process several pages ADF scan, must not send
* abort_session and start_session between pages (last_block=0x28) */
if (mp->generation <= 2 || !is_scanning_from_adf (s) || mp->last_block == 0x38)
@@ -1795,219 +1589,229 @@ static const pixma_scan_ops_t pixma_mp150_ops = {
mp150_get_status
};
-#define DEVICE(name, model, pid, dpi, adftpu_min_dpi, adftpu_max_dpi, w, h, cap) { \
+#define DEVICE(name, model, pid, min_dpi, dpi, adftpu_min_dpi, adftpu_max_dpi, w, h, cap) { \
name, /* name */ \
model, /* model */ \
CANON_VID, pid, /* vid pid */ \
0, /* iface */ \
&pixma_mp150_ops, /* ops */ \
+ min_dpi, /* min_xdpi */ \
dpi, 2*(dpi), /* xdpi, ydpi */ \
adftpu_min_dpi, adftpu_max_dpi, /* adftpu_min_dpi, adftpu_max_dpi */ \
0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \
w, h, /* width, height */ \
PIXMA_CAP_EASY_RGB| \
- PIXMA_CAP_GRAY| /* CIS with native grayscale and CCD with software grayscale */ \
+ PIXMA_CAP_GRAY| /* CIS with native grayscale */ \
PIXMA_CAP_LINEART| /* all scanners with software lineart */ \
PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \
}
-#define END_OF_DEVICE_LIST DEVICE(NULL, NULL, 0, 0, 0, 0, 0, 0, 0)
+#define END_OF_DEVICE_LIST DEVICE(NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0)
const pixma_config_t pixma_mp150_devices[] = {
/* Generation 1: CIS */
- DEVICE ("Canon PIXMA MP150", "MP150", MP150_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP170", "MP170", MP170_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP450", "MP450", MP450_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP500", "MP500", MP500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP530", "MP530", MP530_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
-
- /* Generation 1: CCD */
- DEVICE ("Canon PIXMA MP800", "MP800", MP800_PID, 2400, 150, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
- DEVICE ("Canon PIXMA MP800R", "MP800R", MP800R_PID, 2400, 150, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
- DEVICE ("Canon PIXMA MP830", "MP830", MP830_PID, 2400, 150, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MP150", "MP150", MP150_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP170", "MP170", MP170_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP450", "MP450", MP450_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP500", "MP500", MP500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP530", "MP530", MP530_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
/* Generation 2: CIS */
- DEVICE ("Canon PIXMA MP140", "MP140", MP140_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP160", "MP160", MP160_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP180", "MP180", MP180_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP460", "MP460", MP460_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP510", "MP510", MP510_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP600", "MP600", MP600_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP600R", "MP600R", MP600R_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP140", "MP140", MP140_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP160", "MP160", MP160_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP180", "MP180", MP180_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP460", "MP460", MP460_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP510", "MP510", MP510_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP600", "MP600", MP600_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP600R", "MP600R", MP600R_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Generation 3: CIS */
- DEVICE ("Canon PIXMA MP210", "MP210", MP210_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP220", "MP220", MP220_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP470", "MP470", MP470_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP520", "MP520", MP520_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP610", "MP610", MP610_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
-
- DEVICE ("Canon PIXMA MX300", "MX300", MX300_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MX310", "MX310", MX310_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX700", "MX700", MX700_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX850", "MX850", MX850_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon PIXMA MX7600", "MX7600", MX7600_PID, 4800, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
-
- DEVICE ("Canon PIXMA MP630", "MP630", MP630_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP620", "MP620", MP620_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP540", "MP540", MP540_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP480", "MP480", MP480_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP240", "MP240", MP240_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP260", "MP260", MP260_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP190", "MP190", MP190_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP210", "MP210", MP210_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP220", "MP220", MP220_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP470", "MP470", MP470_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP520", "MP520", MP520_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP610", "MP610", MP610_PID, 0, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ DEVICE ("Canon PIXMA MX300", "MX300", MX300_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MX310", "MX310", MX310_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX700", "MX700", MX700_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX850", "MX850", MX850_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX7600", "MX7600", MX7600_PID, 0, 4800, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+
+ DEVICE ("Canon PIXMA MP630", "MP630", MP630_PID, 0, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP620", "MP620", MP620_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP540", "MP540", MP540_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP480", "MP480", MP480_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP240", "MP240", MP240_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP260", "MP260", MP260_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP190", "MP190", MP190_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* PIXMA 2009 vintage */
- DEVICE ("Canon PIXMA MX320", "MX320", MX320_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX330", "MX330", MX330_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX860", "MX860", MX860_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX320", "MX320", MX320_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX330", "MX330", MX330_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX860", "MX860", MX860_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
/* width and height adjusted to flatbed size 21.8 x 30.2 cm^2 respective
* Not sure if anything's going wrong here, leaving as is
- DEVICE ("Canon PIXMA MX860", "MX860", MX860_PID, 2400, 0, 0, 638, 880, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),*/
+ DEVICE ("Canon PIXMA MX860", "MX860", MX860_PID, 0, 2400, 0, 0, 638, 880, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),*/
/* PIXMA 2010 vintage */
- DEVICE ("Canon PIXMA MX340", "MX340", MX340_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX350", "MX350", MX350_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX870", "MX870", MX870_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX340", "MX340", MX340_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX350", "MX350", MX350_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX870", "MX870", MX870_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
/* PIXMA 2011 vintage */
- DEVICE ("Canon PIXMA MX360", "MX360", MX360_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX410", "MX410", MX410_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX420", "MX420", MX420_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX880 Series", "MX880", MX880_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX360", "MX360", MX360_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX410", "MX410", MX410_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX420", "MX420", MX420_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX880 Series", "MX880", MX880_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
/* Generation 4: CIS */
- DEVICE ("Canon PIXMA MP640", "MP640", MP640_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP560", "MP560", MP560_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP550", "MP550", MP550_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP490", "MP490", MP490_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP250", "MP250", MP250_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP270", "MP270", MP270_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
-
- /* Latest devices (2010) Generation 4 CIS/CCD */
- DEVICE ("Canon PIXMA MP280", "MP280", MP280_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS), /* TODO: 1200dpi doesn't work yet */
- DEVICE ("Canon PIXMA MP495", "MP495", MP495_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG5100", "MG5100", MG5100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG5200", "MG5200", MG5200_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6100", "MG6100", MG6100_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
-
- /* Latest devices (2011) Generation 5 CIS/CCD */
- DEVICE ("Canon PIXMA MG2100", "MG2100", MG2100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG3100", "MG3100", MG3100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG4100", "MG4100", MG4100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG5300", "MG5300", MG5300_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6200", "MG6200", MG6200_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MP493", "MP493", MP493_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA E500", "E500", E500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP640", "MP640", MP640_PID, 0, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP560", "MP560", MP560_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP550", "MP550", MP550_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP490", "MP490", MP490_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP250", "MP250", MP250_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP270", "MP270", MP270_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* Latest devices (2010) Generation 4 CIS */
+ DEVICE ("Canon PIXMA MP280", "MP280", MP280_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS), /* TODO: 1200dpi doesn't work yet */
+ DEVICE ("Canon PIXMA MP495", "MP495", MP495_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5100", "MG5100", MG5100_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5200", "MG5200", MG5200_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6100", "MG6100", MG6100_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+ /* Latest devices (2011) Generation 5 CIS */
+ DEVICE ("Canon PIXMA MG2100", "MG2100", MG2100_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG3100", "MG3100", MG3100_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG4100", "MG4100", MG4100_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5300", "MG5300", MG5300_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6200", "MG6200", MG6200_PID, 0, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MP493", "MP493", MP493_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E500", "E500", E500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2012) Generation 5 CIS */
- DEVICE ("Canon PIXMA MX370 Series", "MX370", MX370_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX430 Series", "MX430", MX430_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX510 Series", "MX510", MX510_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX710 Series", "MX710", MX710_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon PIXMA MX890 Series", "MX890", MX890_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon PIXMA E600 Series", "E600", E600_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MG4200", "MG4200", MG4200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MX370 Series", "MX370", MX370_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX430 Series", "MX430", MX430_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX510 Series", "MX510", MX510_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX710 Series", "MX710", MX710_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX890 Series", "MX890", MX890_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA E600 Series", "E600", E600_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MG4200", "MG4200", MG4200_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2013) Generation 5 CIS */
- DEVICE ("Canon PIXMA E510", "E510", E510_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA E610", "E610", E610_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MP230", "MP230", MP230_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG2200 Series", "MG2200", MG2200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG3200 Series", "MG3200", MG3200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG5400 Series", "MG5400", MG5400_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6300 Series", "MG6300", MG6300_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MX390 Series", "MX390", MX390_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX450 Series", "MX450", MX450_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX520 Series", "MX520", MX520_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX720 Series", "MX720", MX720_PID, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon PIXMA MX920 Series", "MX920", MX920_PID, 2400, 0, 600, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon PIXMA MG2400 Series", "MG2400", MG2400_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG2500 Series", "MG2500", MG2500_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG3500 Series", "MG3500", MG3500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG5500 Series", "MG5500", MG5500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6400 Series", "MG6400", MG6400_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6500 Series", "MG6500", MG6500_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG7100 Series", "MG7100", MG7100_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E510", "E510", E510_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E610", "E610", E610_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MP230", "MP230", MP230_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG2200 Series", "MG2200", MG2200_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG3200 Series", "MG3200", MG3200_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5400 Series", "MG5400", MG5400_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6300 Series", "MG6300", MG6300_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MX390 Series", "MX390", MX390_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX450 Series", "MX450", MX450_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX520 Series", "MX520", MX520_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX720 Series", "MX720", MX720_PID, 0, 2400, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MX920 Series", "MX920", MX920_PID, 0, 2400, 0, 600, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA MG2400 Series", "MG2400", MG2400_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG2500 Series", "MG2500", MG2500_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG3500 Series", "MG3500", MG3500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5500 Series", "MG5500", MG5500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6400 Series", "MG6400", MG6400_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6500 Series", "MG6500", MG6500_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG7100 Series", "MG7100", MG7100_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2014) Generation 5 CIS */
- DEVICE ("Canon PIXMA MX470 Series", "MX470", MX470_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MX530 Series", "MX530", MX530_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon MAXIFY MB5000 Series", "MB5000", MB5000_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon MAXIFY MB5300 Series", "MB5300", MB5300_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon MAXIFY MB2000 Series", "MB2000", MB2000_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon MAXIFY MB2100 Series", "MB2100", MB2100_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG),
- DEVICE ("Canon MAXIFY MB2300 Series", "MB2300", MB2300_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon MAXIFY MB2700 Series", "MB2700", MB2700_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG),
- DEVICE ("Canon PIXMA E400", "E400", E400_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA E560", "E560", E560_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG7500 Series", "MG7500", MG7500_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6600 Series", "MG6600", MG6600_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG5600 Series", "MG5600", MG5600_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG2900 Series", "MG2900", MG2900_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA E460 Series", "E460", E460_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MX470 Series", "MX470", MX470_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MX530 Series", "MX530", MX530_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon MAXIFY MB5000 Series", "MB5000", MB5000_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG),
+ DEVICE ("Canon MAXIFY MB5300 Series", "MB5300", MB5300_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon MAXIFY MB2000 Series", "MB2000", MB2000_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP | PIXMA_CAP_ADF_JPEG),
+ DEVICE ("Canon MAXIFY MB2100 Series", "MB2100", MB2100_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG),
+ DEVICE ("Canon MAXIFY MB2300 Series", "MB2300", MB2300_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG),
+ DEVICE ("Canon MAXIFY MB2700 Series", "MB2700", MB2700_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG),
+ DEVICE ("Canon PIXMA E400", "E400", E400_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E560", "E560", E560_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG7500 Series", "MG7500", MG7500_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6600 Series", "MG6600", MG6600_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5600 Series", "MG5600", MG5600_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG2900 Series", "MG2900", MG2900_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E460 Series", "E460", E460_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2015) Generation 5 CIS */
- DEVICE ("Canon PIXMA MX490 Series", "MX490", MX490_PID, 600, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA E480 Series", "E480", E480_PID, 600, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MG3600 Series", "MG3600", MG3600_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG7700 Series", "MG7700", MG7700_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6900 Series", "MG6900", MG6900_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG6800 Series", "MG6800", MG6800_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG5700 Series", "MG5700", MG5700_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MX490 Series", "MX490", MX490_PID, 0, 600, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA E480 Series", "E480", E480_PID, 0, 600, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MG3600 Series", "MG3600", MG3600_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG7700 Series", "MG7700", MG7700_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6900 Series", "MG6900", MG6900_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG6800 Series", "MG6800", MG6800_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG5700 Series", "MG5700", MG5700_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2016) Generation 5 CIS */
- DEVICE ("Canon PIXMA G3000", "G3000", G3000_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA G2000", "G2000", G2000_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS9000 Series", "TS9000", TS9000_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS8000 Series", "TS8000", TS8000_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS6000 Series", "TS6000", TS6000_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS5000 Series", "TS5000", TS5000_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA MG3000 Series", "MG3000", MG3000_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA E470 Series", "E470", E470_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA E410 Series", "E410", E410_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA G3000", "G3000", G3000_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA G2000", "G2000", G2000_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS9000 Series", "TS9000", TS9000_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8000 Series", "TS8000", TS8000_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6000 Series", "TS6000", TS6000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS5000 Series", "TS5000", TS5000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA MG3000 Series", "MG3000", MG3000_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E470 Series", "E470", E470_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E410 Series", "E410", E410_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2017) Generation 5 CIS */
- DEVICE ("Canon PIXMA G4000", "G4000", G4000_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS6100 Series", "TS6100", TS6100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS5100 Series", "TS5100", TS5100_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS3100 Series", "TS3100", TS3100_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA E3100 Series", "E3100", E3100_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA G4000", "G4000", G4000_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6100 Series", "TS6100", TS6100_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS5100 Series", "TS5100", TS5100_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS3100 Series", "TS3100", TS3100_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E3100 Series", "E3100", E3100_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2018) Generation 5 CIS */
- DEVICE ("Canon MAXIFY MB5400 Series", "MB5400", MB5400_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon MAXIFY MB5100 Series", "MB5100", MB5100_PID, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
- DEVICE ("Canon PIXMA TS9100 Series", "TS9100", TS9100_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TR8500 Series", "TR8500", TR8500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA TR7500 Series", "TR7500", TR7500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA TS9500 Series", "TS9500", TS9500_PID, 1200, 0, 600, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("CanoScan LiDE 400", "LIDE400", LIDE400_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("CanoScan LiDE 300", "LIDE300", LIDE300_PID, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon MAXIFY MB5400 Series", "MB5400", MB5400_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP | PIXMA_CAP_ADF_JPEG),
+ DEVICE ("Canon MAXIFY MB5100 Series", "MB5100", MB5100_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP),
+ DEVICE ("Canon PIXMA TS9100 Series", "TS9100", TS9100_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TR8500 Series", "TR8500", TR8500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA TR7500 Series", "TR7500", TR7500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA TS9500 Series", "TS9500", TS9500_PID, 0, 1200, 0, 600, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("CanoScan LiDE 400", "LIDE400", LIDE400_PID, 300, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("CanoScan LiDE 300", "LIDE300", LIDE300_PID, 300, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
/* Latest devices (2019) Generation 5 CIS */
- DEVICE ("Canon PIXMA TS8100 Series", "TS8100", TS8100_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA G3010 Series", "G3010", G3010_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA G4010 Series", "G4010", G4010_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA TS9180 Series", "TS9180", TS9180_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS8180 Series", "TS8180", TS8180_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS6180 Series", "TS6180", TS6180_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TR8580 Series", "TR8580", TR8580_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA TS8130 Series", "TS8130", TS8130_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS6130 Series", "TS6130", TS6130_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TR8530 Series", "TR8530", TR8530_PID, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA TR7530 Series", "TR7530", TR7530_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXUS XK50 Series", "XK50", XK50_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXUS XK70 Series", "XK70", XK70_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TR4500 Series", "TR4500", TR4500_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA E4200 Series", "E4200", E4200_PID, 600, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA TS6200 Series", "TS6200", TS6200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS6280 Series", "TS6280", TS6280_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS6230 Series", "TS6230", TS6230_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS8200 Series", "TS8200", TS8200_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS8280 Series", "TS8280", TS8280_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS8230 Series", "TS8230", TS8230_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
- DEVICE ("Canon PIXMA TS9580 Series", "TS9580", TS9580_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA TR9530 Series", "TR9530", TR9530_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXUS XK80 Series", "XK80", XK80_PID, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8100 Series", "TS8100", TS8100_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA G2010 Series", "G2010", G2010_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA G3010 Series", "G3010", G3010_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA G4010 Series", "G4010", G4010_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA TS9180 Series", "TS9180", TS9180_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8180 Series", "TS8180", TS8180_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6180 Series", "TS6180", TS6180_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TR8580 Series", "TR8580", TR8580_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA TS8130 Series", "TS8130", TS8130_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6130 Series", "TS6130", TS6130_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TR8530 Series", "TR8530", TR8530_PID, 0, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA TR7530 Series", "TR7530", TR7530_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXUS XK50 Series", "XK50", XK50_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXUS XK70 Series", "XK70", XK70_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TR4500 Series", "TR4500", TR4500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA E4200 Series", "E4200", E4200_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA TS6200 Series", "TS6200", TS6200_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6280 Series", "TS6280", TS6280_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6230 Series", "TS6230", TS6230_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8200 Series", "TS8200", TS8200_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8280 Series", "TS8280", TS8280_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8230 Series", "TS8230", TS8230_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS9580 Series", "TS9580", TS9580_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA TR9530 Series", "TR9530", TR9530_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA G6000 Series", "G6000", G6000_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA G6080 Series", "G6080", G6080_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXUS XK80 Series", "XK80", XK80_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS5300 Series", "TS5300", TS5300_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS5380 Series", "TS5380", TS5380_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6300 Series", "TS6300", TS6300_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6380 Series", "TS6380", TS6380_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS7330 Series", "TS7330", TS7330_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8380 Series", "TS8380", TS8380_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS8330 Series", "TS8330", TS8330_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA XK60 Series", "XK60", XK60_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS6330 Series", "TS6330", TS6330_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA TS3300 Series", "TS3300", TS3300_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+ DEVICE ("Canon PIXMA E3300 Series", "E3300", E3300_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
END_OF_DEVICE_LIST
};
diff --git a/backend/pixma_mp730.c b/backend/pixma/pixma_mp730.c
index c801daa..93d518b 100644
--- a/backend/pixma_mp730.c
+++ b/backend/pixma/pixma_mp730.c
@@ -298,10 +298,7 @@ send_time (pixma_t * s)
data = pixma_newcmd (&mp->cb, cmd_time, 20, 0);
pixma_get_time (&now, NULL);
t = localtime (&now);
- snprintf ((char *) data, 16,
- "%02d/%02d/%02d %02d:%02d",
- t->tm_year % 100, t->tm_mon + 1, t->tm_mday,
- t->tm_hour, t->tm_min);
+ strftime ((char *) data, 16, "%y/%m/%d %H:%M", t);
PDBG (pixma_dbg (3, "Sending time: '%s'\n", (char *) data));
return pixma_exec (s, &mp->cb);
}
@@ -818,6 +815,7 @@ static const pixma_scan_ops_t pixma_mp730_ops = {
0x04a9, pid, /* vid pid */ \
1, /* iface */ \
&pixma_mp730_ops, /* ops */ \
+ 0, /* min_xdpi not used in this subdriver */ \
dpi, dpi, /* xdpi, ydpi */ \
0, 0, /* adftpu_min_dpi & adftpu_max_dpi not used in this subdriver */ \
0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \
diff --git a/backend/pixma_mp750.c b/backend/pixma/pixma_mp750.c
index 5bd6ef0..7f00023 100644
--- a/backend/pixma_mp750.c
+++ b/backend/pixma/pixma_mp750.c
@@ -955,16 +955,18 @@ static const pixma_scan_ops_t pixma_mp750_ops = {
0x04a9, pid, /* vid pid */ \
0, /* iface */ \
&pixma_mp750_ops, /* ops */ \
+ 0, /* min_xdpi not used in this subdriver */ \
dpi, 2*(dpi), /* xdpi, ydpi */ \
0, 0, /* adftpu_min_dpi & adftpu_max_dpi not used in this subdriver */ \
0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \
637, 877, /* width, height */ \
- PIXMA_CAP_GRAY|PIXMA_CAP_EVENTS|cap \
+ PIXMA_CAP_CCD| /* all scanners with CCD */ \
+ PIXMA_CAP_GRAY|PIXMA_CAP_EVENTS|cap \
}
const pixma_config_t pixma_mp750_devices[] = {
- DEVICE ("Canon PIXMA MP750", "MP750", MP750_PID, 2400, PIXMA_CAP_CCD | PIXMA_CAP_ADF),
- DEVICE ("Canon PIXMA MP760/770", "MP760/770", MP760_PID, 2400, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
- DEVICE ("Canon PIXMA MP780/790", "MP780/790", MP780_PID, 2400, PIXMA_CAP_CCD | PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MP750", "MP750", MP750_PID, 2400, PIXMA_CAP_ADF),
+ DEVICE ("Canon PIXMA MP760/770", "MP760/770", MP760_PID, 2400, PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP780/790", "MP780/790", MP780_PID, 2400, PIXMA_CAP_ADF),
DEVICE (NULL, NULL, 0, 0, 0)
};
diff --git a/backend/pixma_mp810.c b/backend/pixma/pixma_mp800.c
index 5d81e3f..feef611 100644
--- a/backend/pixma_mp810.c
+++ b/backend/pixma/pixma_mp800.c
@@ -96,6 +96,11 @@
#define CANON_VID 0x04a9
+/* Generation 1 */
+#define MP800_PID 0x170d
+#define MP800R_PID 0x170e
+#define MP830_PID 0x1713
+
/* Generation 2 */
#define MP810_PID 0x171a
#define MP960_PID 0x171b
@@ -575,6 +580,39 @@ static unsigned calc_shifting (pixma_t * s)
switch (s->cfg->pid)
{
+ case MP800_PID:
+ case MP800R_PID:
+ case MP830_PID:
+ if (s->param->xdpi == 2400)
+ {
+ if (is_scanning_from_tpu(s))
+ mp->stripe_shift = 6;
+ else
+ mp->stripe_shift = 3;
+ }
+ if (s->param->ydpi > 75)
+ {
+ mp->color_shift = s->param->ydpi / ((s->param->ydpi < 1200) ? 150 : 75);
+
+ if (is_scanning_from_tpu (s))
+ mp->color_shift = s->param->ydpi / 75;
+
+ /* If you're trying to decipher this color-shifting code,
+ the following line is where the magic is revealed. */
+ mp->shift[1] = mp->color_shift * get_cis_ccd_line_size (s);
+ if (is_scanning_from_adf (s))
+ { /* ADF */
+ mp->shift[0] = 0;
+ mp->shift[2] = 2 * mp->shift[1];
+ }
+ else
+ { /* Flatbed or TPU */
+ mp->shift[0] = 2 * mp->shift[1];
+ mp->shift[2] = 0;
+ }
+ }
+ break;
+
case MP970_PID: /* MP970 at 4800 dpi */
case CS8800F_PID: /* CanoScan 8800F at 4800 dpi */
if (s->param->xdpi == 4800)
@@ -1021,8 +1059,7 @@ static int send_time (pixma_t * s)
data = pixma_newcmd (&mp->cb, cmd_time, 20, 0);
pixma_get_time (&now, NULL);
t = localtime (&now);
- snprintf ((char *) data, 16, "%02d/%02d/%02d %02d:%02d", t->tm_year % 100,
- t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min);
+ strftime ((char *) data, 16, "%y/%m/%d %H:%M", t);
PDBG(pixma_dbg (3, "Sending time: '%s'\n", (char *) data));
return pixma_exec (s, &mp->cb);
}
@@ -1210,6 +1247,8 @@ static int wait_until_ready (pixma_t * s)
WAIT_INTERRUPT(1000);
if (mp->generation >= 3)
RET_IF_ERR(query_status_3 (s));
+ else if (s->cfg->pid == MP800R_PID)
+ RET_IF_ERR (query_status (s));
if (--tmo == 0)
{
PDBG(pixma_dbg (1, "WARNING: Timed out in wait_until_ready()\n"));
@@ -1783,7 +1822,7 @@ static int mp810_open (pixma_t * s)
mp->imgbuf = buf + CMDBUF_SIZE;
/* General rules for setting Pixma protocol generation # */
- mp->generation = (s->cfg->pid >= MP810_PID) ? 2 : 1; /* no generation 1 devices anyway, but keep similar to pixma_mp150.c file */
+ mp->generation = (s->cfg->pid >= MP810_PID) ? 2 : 1;
if (s->cfg->pid >= MP970_PID)
mp->generation = 3;
@@ -2324,7 +2363,7 @@ static int mp810_get_status (pixma_t * s, pixma_device_status_t * status)
return 0;
}
-static const pixma_scan_ops_t pixma_mp810_ops =
+static const pixma_scan_ops_t pixma_mp800_ops =
{
mp810_open,
mp810_close,
@@ -2341,11 +2380,13 @@ static const pixma_scan_ops_t pixma_mp810_ops =
model, /* model */ \
CANON_VID, pid, /* vid pid */ \
0, /* iface */ \
- &pixma_mp810_ops, /* ops */ \
+ &pixma_mp800_ops, /* ops */ \
+ 0, /* min_xdpi not used in this subdriver */ \
dpi, 2*(dpi), /* xdpi, ydpi */ \
adftpu_min_dpi, adftpu_max_dpi, /* adftpu_min_dpi, adftpu_max_dpi */ \
tpuir_min_dpi, tpuir_max_dpi, /* tpuir_min_dpi, tpuir_max_dpi */ \
w, h, /* width, height */ \
+ PIXMA_CAP_CCD| /* all scanners with CCD*/ \
PIXMA_CAP_EASY_RGB| \
PIXMA_CAP_GRAY| /* all scanners with software grayscale */ \
PIXMA_CAP_LINEART| /* all scanners with software lineart */ \
@@ -2354,35 +2395,40 @@ static const pixma_scan_ops_t pixma_mp810_ops =
#define END_OF_DEVICE_LIST DEVICE(NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-const pixma_config_t pixma_mp810_devices[] =
+const pixma_config_t pixma_mp800_devices[] =
{
+ /* Generation 1: CCD */
+ DEVICE ("Canon PIXMA MP800", "MP800", MP800_PID, 2400, 150, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP800R", "MP800R", MP800R_PID, 2400, 150, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP830", "MP830", MP830_PID, 2400, 150, 0, 0, 0, 638, 877, PIXMA_CAP_ADFDUP),
+
/* Generation 2: CCD */
- DEVICE ("Canon PIXMA MP810", "MP810", MP810_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
- DEVICE ("Canon PIXMA MP960", "MP960", MP960_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP810", "MP810", MP810_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MP960", "MP960", MP960_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
/* Generation 3 CCD not managed as Generation 2 */
- DEVICE ("Canon Pixma MP970", "MP970", MP970_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon Pixma MP970", "MP970", MP970_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
/* Flatbed scanner CCD (2007) */
- DEVICE ("Canoscan 8800F", "8800F", CS8800F_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT),
+ DEVICE ("Canoscan 8800F", "8800F", CS8800F_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT),
/* PIXMA 2008 vintage CCD */
- DEVICE ("Canon MP980 series", "MP980", MP980_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon MP980 series", "MP980", MP980_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
/* Generation 4 CCD */
- DEVICE ("Canon MP990 series", "MP990", MP990_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon MP990 series", "MP990", MP990_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
/* Flatbed scanner (2010) */
- DEVICE ("Canoscan 9000F", "9000F", CS9000F_PID, 4800, 300, 9600, 600, 2400, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPUIR /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT),
+ DEVICE ("Canoscan 9000F", "9000F", CS9000F_PID, 4800, 300, 9600, 600, 2400, 638, 877, PIXMA_CAP_TPUIR /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT),
/* Latest devices (2010) Generation 4 CCD untested */
- DEVICE ("Canon PIXMA MG8100", "MG8100", MG8100_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MG8100", "MG8100", MG8100_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
/* Latest devices (2011) Generation 4 CCD untested */
- DEVICE ("Canon PIXMA MG8200", "MG8200", MG8200_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPU),
+ DEVICE ("Canon PIXMA MG8200", "MG8200", MG8200_PID, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU),
/* Flatbed scanner (2013) */
- DEVICE ("Canoscan 9000F Mark II", "9000FMarkII", CS9000F_MII_PID, 4800, 300, 9600, 600, 2400, 638, 877, PIXMA_CAP_CCD | PIXMA_CAP_TPUIR | PIXMA_CAP_48BIT),
+ DEVICE ("Canoscan 9000F Mark II", "9000FMarkII", CS9000F_MII_PID, 4800, 300, 9600, 600, 2400, 638, 877, PIXMA_CAP_TPUIR | PIXMA_CAP_48BIT),
END_OF_DEVICE_LIST
};
diff --git a/backend/pixma_rename.h b/backend/pixma/pixma_rename.h
index ce68ed3..ad3d960 100644
--- a/backend/pixma_rename.h
+++ b/backend/pixma/pixma_rename.h
@@ -81,7 +81,7 @@
#define pixma_mp150_devices sanei_pixma_mp150_devices
#define pixma_mp730_devices sanei_pixma_mp730_devices
#define pixma_mp750_devices sanei_pixma_mp750_devices
-#define pixma_mp810_devices sanei_pixma_mp810_devices
+#define pixma_mp800_devices sanei_pixma_mp800_devices
#define pixma_iclass_devices sanei_pixma_iclass_devices
#define pixma_newcmd sanei_pixma_newcmd
#define pixma_open sanei_pixma_open
diff --git a/backend/pixma_sane_options.c b/backend/pixma/pixma_sane_options.c
index 6e6abfc..2b8f609 100644
--- a/backend/pixma_sane_options.c
+++ b/backend/pixma/pixma_sane_options.c
@@ -346,7 +346,7 @@ int build_option_descriptors(struct pixma_sane_t *ss)
sod = &opt->sod;
sod->type = SANE_TYPE_INT;
sod->title = SANE_I18N("ADF Waiting Time");
- sod->desc = SANE_I18N("When set, the scanner searches the waiting time in seconds for a new document inserted into the automatic document feeder.");
+ sod->desc = SANE_I18N("When set, the scanner waits upto the specified time in seconds for a new document inserted into the automatic document feeder.");
sod->name = "adf-wait";
sod->unit = SANE_UNIT_NONE;
sod->size = 1 * sizeof(SANE_Word);
diff --git a/backend/pixma_sane_options.h b/backend/pixma/pixma_sane_options.h
index 1472f1f..1472f1f 100644
--- a/backend/pixma_sane_options.h
+++ b/backend/pixma/pixma_sane_options.h
diff --git a/backend/scripts/pixma_gen_options.py b/backend/pixma/scripts/pixma_gen_options.py
index c4c75e0..c4c75e0 100755
--- a/backend/scripts/pixma_gen_options.py
+++ b/backend/pixma/scripts/pixma_gen_options.py
diff --git a/backend/plustek-pp_motor.c b/backend/plustek-pp_motor.c
index 7f4b1ac..c48710e 100644
--- a/backend/plustek-pp_motor.c
+++ b/backend/plustek-pp_motor.c
@@ -1912,6 +1912,7 @@ static void motorP96SetupRunTable( pScanData ps )
case 3:
if (*(p.pb + 2))
bColor = 1;
+ // fall through
case 2:
if (*(p.pb + 1))
bColor++;
diff --git a/backend/plustek-usb.c b/backend/plustek-usb.c
index 5c6fbeb..107bf9e 100644
--- a/backend/plustek-usb.c
+++ b/backend/plustek-usb.c
@@ -193,6 +193,7 @@ usb_initDev( Plustek_Device *dev, int idx, int handle, int vendor )
int i;
ScanParam sParam;
u_short tmp = 0;
+ int ret = 0;
DBG( _DBG_INFO, "usb_initDev(%d,0x%04x,%i)\n",
idx, vendor, dev->initialized );
@@ -305,11 +306,16 @@ usb_initDev( Plustek_Device *dev, int idx, int handle, int vendor )
}
ptr = getenv ("HOME");
- if( NULL == ptr ) {
- sprintf( tmp_str2, "/tmp/%s", tmp_str1 );
- } else {
- sprintf( tmp_str2, "%s/.sane/%s", ptr, tmp_str1 );
+ ret = ( NULL == ptr )?
+ snprintf( tmp_str2, sizeof(tmp_str2), "/tmp/%s", tmp_str1 ):
+ snprintf( tmp_str2, sizeof(tmp_str2), "%s/.sane/%s", ptr, tmp_str1 );
+
+ if ((ret < 0) || (ret > (int)sizeof(tmp_str2))) {
+ DBG( _DBG_WARNING,
+ "Failed to generate calibration file path. Default substituted.\n" );
+ snprintf(tmp_str2, sizeof(tmp_str2), "/tmp/plustek-default");
}
+
dev->calFile = strdup( tmp_str2 );
DBG( _DBG_INFO, "Calibration file-names set to:\n" );
DBG( _DBG_INFO, ">%s-coarse.cal<\n", dev->calFile );
diff --git a/backend/plustek-usbcal.c b/backend/plustek-usbcal.c
index 3b9d93a..84a4105 100644
--- a/backend/plustek-usbcal.c
+++ b/backend/plustek-usbcal.c
@@ -306,7 +306,7 @@ cano_AdjustLightsource( Plustek_Device *dev )
min_rgb.Blue = hw->blue_lamp_on;
if((dev->adj.rlampoff != -1) &&
- (dev->adj.glampoff != -1) && (dev->adj.rlampoff != -1)) {
+ (dev->adj.glampoff != -1) && (dev->adj.blampoff != -1)) {
DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
return SANE_TRUE;
}
diff --git a/backend/plustek.c b/backend/plustek.c
index e1d9e09..eaddbd3 100644
--- a/backend/plustek.c
+++ b/backend/plustek.c
@@ -1085,14 +1085,14 @@ init_options( Plustek_Scanner *s )
/* scanner buttons */
for( i = OPT_BUTTON_0; i <= OPT_BUTTON_LAST; i++ ) {
- char name [12];
- char title [128];
+ char buf [128];
- sprintf (name, "button %d", i - OPT_BUTTON_0);
- sprintf (title, "Scanner button %d", i - OPT_BUTTON_0);
+ snprintf (buf, sizeof(buf), "button %d", i - OPT_BUTTON_0);
+ s->opt[i].name = strdup(buf);
+
+ snprintf (buf, sizeof(buf), "Scanner button %d", i - OPT_BUTTON_0);
+ s->opt[i].title = strdup(buf);
- s->opt[i].name = strdup(name);
- s->opt[i].title = strdup(title);
s->opt[i].desc = SANE_I18N("This option reflects the status "
"of the scanner buttons.");
s->opt[i].type = SANE_TYPE_BOOL;
@@ -1916,6 +1916,7 @@ sane_control_option( SANE_Handle handle, SANE_Int option,
case OPT_BUTTON_0:
if(!s->calibrating)
usb_UpdateButtonStatus(s);
+ // fall through
case OPT_BUTTON_1:
case OPT_BUTTON_2:
case OPT_BUTTON_3:
diff --git a/backend/plustek_pp.c b/backend/plustek_pp.c
index 551cf27..fdcc6b6 100644
--- a/backend/plustek_pp.c
+++ b/backend/plustek_pp.c
@@ -1625,7 +1625,7 @@ SANE_Status sane_control_option( SANE_Handle handle, SANE_Int option,
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
}
- /* fall through to OPT_HALFTONE */
+ // fall through
case OPT_HALFTONE:
s->val[option].w = optval - s->opt[option].constraint.string_list;
break;
diff --git a/backend/ricoh.c b/backend/ricoh.c
index 58b3a53..fbe5c58 100644
--- a/backend/ricoh.c
+++ b/backend/ricoh.c
@@ -222,12 +222,14 @@ attach (const char *devnam, Ricoh_Device ** devp)
dev->sane.name = strdup (devnam);
dev->sane.vendor = "RICOH";
- str = malloc (sizeof(ibuf.product) + sizeof(ibuf.revision) + 1);
+
+ size_t prod_rev_size = sizeof(ibuf.product) + sizeof(ibuf.revision) + 1;
+ str = malloc (prod_rev_size);
if (str)
{
- str[0] = '\0';
- strncat (str, (char *)ibuf.product, sizeof(ibuf.product));
- strncat (str, (char *)ibuf.revision, sizeof(ibuf.revision));
+ snprintf (str, prod_rev_size, "%.*s%.*s",
+ (int) sizeof(ibuf.product), (const char *) ibuf.product,
+ (int) sizeof(ibuf.revision), (const char *) ibuf.revision);
}
dev->sane.model = str;
dev->sane.type = "flatbed scanner";
diff --git a/backend/ricoh2.c b/backend/ricoh2.c
index 8aa938e..f719268 100644
--- a/backend/ricoh2.c
+++ b/backend/ricoh2.c
@@ -1,6 +1,6 @@
/* sane - Scanner Access Now Easy.
- Copyright (C) 2018 Stanislav Yuzvinsky
+ Copyright (C) 2018, 2019 Stanislav Yuzvinsky
Based on the work done by viruxx
This file is part of the SANE package.
@@ -113,9 +113,10 @@ typedef struct Ricoh2_device_info {
Ricoh2_device_info;
static Ricoh2_device_info supported_devices[] = {
- { 0x042c, "Aficio SP100SU" },
- { 0x0438, "Aficio SG3100SNw" },
- { 0x0448, "Aficio SP111SU" }
+ { 0x042c, "Aficio SP-100SU" },
+ { 0x0438, "Aficio SG-3100SNw" },
+ { 0x0439, "Aficio SG-3110SFNw" },
+ { 0x0448, "Aficio SP-111SU/SP-112SU" }
};
static SANE_String_Const mode_list[] = {
diff --git a/backend/ricoh2_buffer.c b/backend/ricoh2_buffer.c
index b8d7d90..e79a7f3 100644
--- a/backend/ricoh2_buffer.c
+++ b/backend/ricoh2_buffer.c
@@ -46,7 +46,12 @@
#include <memory.h>
#include <assert.h>
+
+#if defined(__APPLE__) && defined(__MACH__)
+#include <malloc/malloc.h>
+#else
#include <malloc.h>
+#endif
#include "../include/sane/sanei_debug.h"
diff --git a/backend/s9036.c b/backend/s9036.c
index aa18df7..4124b7b 100644
--- a/backend/s9036.c
+++ b/backend/s9036.c
@@ -1022,6 +1022,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
case OPT_BR_Y:
if (info)
*info |= SANE_INFO_RELOAD_PARAMS;
+ // fall through
case OPT_BRIGHT_ADJUST:
case OPT_CONTR_ADJUST:
s->val[option] = *(SANE_Word *) val;
diff --git a/backend/sane_strstatus.c b/backend/sane_strstatus.c
index 1fc2220..c76d305 100644
--- a/backend/sane_strstatus.c
+++ b/backend/sane_strstatus.c
@@ -62,7 +62,7 @@ sane_strstatus (SANE_Status status)
return SANE_I18N("Operation not supported");
case SANE_STATUS_CANCELLED:
- return SANE_I18N("Operation was cancelled");
+ return SANE_I18N("Operation was canceled");
case SANE_STATUS_DEVICE_BUSY:
return SANE_I18N("Device busy");
diff --git a/backend/sharp.c b/backend/sharp.c
index b2807d7..701b179 100644
--- a/backend/sharp.c
+++ b/backend/sharp.c
@@ -2825,6 +2825,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
case OPT_BR_Y:
if (info && s->val[option].w != *(SANE_Word *) val)
*info |= SANE_INFO_RELOAD_PARAMS;
+ // fall through
case OPT_NUM_OPTS:
case OPT_THRESHOLD:
/* xxx theoretically, we could use OPT_THRESHOLD in
diff --git a/backend/sm3600.c b/backend/sm3600.c
index 6e411c3..8f8adfc 100644
--- a/backend/sm3600.c
+++ b/backend/sm3600.c
@@ -609,14 +609,14 @@ sane_control_option (SANE_Handle handle, SANE_Int iOpt,
{
case optResolution:
case optTLX: case optTLY: case optBRX: case optBRY:
- if (pnInfo) (*pnInfo) |= SANE_INFO_RELOAD_PARAMS;
- /* fall through side effect free */
+ if (pnInfo) (*pnInfo) |= SANE_INFO_RELOAD_PARAMS;
+ // fall through
+ case optPreview:
+ case optGrayPreview:
#ifdef SM3600_SUPPORT_EXPOSURE
case optBrightness:
case optContrast:
#endif
- case optPreview:
- case optGrayPreview:
this->aoptVal[iOpt].w = *(SANE_Word*)pVal;
break;
case optMode:
diff --git a/backend/snapscan-options.c b/backend/snapscan-options.c
index 3ef85ae..999b312 100644
--- a/backend/snapscan-options.c
+++ b/backend/snapscan-options.c
@@ -927,7 +927,7 @@ static void init_options (SnapScan_Scanner * ps)
po[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
po[OPT_RGB_LPR].name = "rgb-lpr";
- po[OPT_RGB_LPR].title = SANE_I18N("Colour lines per read");
+ po[OPT_RGB_LPR].title = SANE_I18N("Color lines per read");
po[OPT_RGB_LPR].desc = lpr_desc;
po[OPT_RGB_LPR].type = SANE_TYPE_INT;
po[OPT_RGB_LPR].unit = SANE_UNIT_NONE;
@@ -939,7 +939,7 @@ static void init_options (SnapScan_Scanner * ps)
ps->rgb_lpr = def_rgb_lpr;
po[OPT_GS_LPR].name = "gs-lpr";
- po[OPT_GS_LPR].title = SANE_I18N("Greyscale lines per read");
+ po[OPT_GS_LPR].title = SANE_I18N("Grayscale lines per read");
po[OPT_GS_LPR].desc = lpr_desc;
po[OPT_GS_LPR].type = SANE_TYPE_INT;
po[OPT_GS_LPR].unit = SANE_UNIT_NONE;
diff --git a/backend/stv680.c b/backend/stv680.c
index 8d2fda3..473def0 100644
--- a/backend/stv680.c
+++ b/backend/stv680.c
@@ -1189,8 +1189,8 @@ stv680_fill_image (Stv680_Vidcam * dev)
}
#define MSG_MAXLEN 45
-#define CHAR_HEIGHT 11
-#define CHAR_WIDTH 6
+#define TEXT_CHAR_HEIGHT 11
+#define TEXT_CHAR_WIDTH 6
#define CHAR_START 4
static SANE_Status
@@ -1216,14 +1216,14 @@ stv680_add_text (SANE_Byte * image, int width, int height, char *txt)
len = strftime (line, MSG_MAXLEN, fmttxt, tm);
- for (y = 0; y < CHAR_HEIGHT; y++)
+ for (y = 0; y < TEXT_CHAR_HEIGHT; y++)
{
- ptr = image + 3 * width * (height - CHAR_HEIGHT - 2 + y) + 12;
+ ptr = image + 3 * width * (height - TEXT_CHAR_HEIGHT - 2 + y) + 12;
for (x = 0; x < len; x++)
{
- f = fontdata[line[x] * CHAR_HEIGHT + y];
- for (i = CHAR_WIDTH - 1; i >= 0; i--)
+ f = fontdata[line[x] * TEXT_CHAR_HEIGHT + y];
+ for (i = TEXT_CHAR_WIDTH - 1; i >= 0; i--)
{
if (f & (CHAR_START << i))
{
diff --git a/backend/umax_pp.c b/backend/umax_pp.c
index b1121ef..16adbe3 100644
--- a/backend/umax_pp.c
+++ b/backend/umax_pp.c
@@ -103,10 +103,6 @@
* 129 if you want to know which parameters are unused
*/
-/* history:
- * see Changelog
- */
-
#define UMAX_PP_BUILD 2301
#define UMAX_PP_STATE "release"
@@ -206,34 +202,28 @@ umax_pp_attach (SANEI_Config * config, const char *devname)
SANE_Status status = SANE_STATUS_GOOD;
int ret, prt = 0, mdl;
char model[32];
- char name[64];
- char *val;
-
- memset (name, 0, 64);
+ const char *name = NULL;
+ const char *val;
- if ((strlen (devname) < 3))
+ if (!devname || (strlen (devname) < 3))
return SANE_STATUS_INVAL;
sanei_umax_pp_setastra (atoi((SANE_Char *) config->values[CFG_ASTRA]));
/* if the name begins with a slash, it's a device, else it's an addr */
- if (devname != NULL)
+ if ((devname[0] == '/'))
{
- if ((devname[0] == '/'))
- {
- strncpy (name, devname, 64);
- }
+ name = devname;
+ }
+ else
+ {
+ if ((devname[0] == '0')
+ && ((devname[1] == 'x') || (devname[1] == 'X')))
+ prt = strtol (devname + 2, NULL, 16);
else
- {
- if ((devname[0] == '0')
- && ((devname[1] == 'x') || (devname[1] == 'X')))
- prt = strtol (devname + 2, NULL, 16);
- else
- prt = atoi (devname);
- }
+ prt = atoi (devname);
}
-
for (i = 0; i < num_devices; i++)
{
if (devname[0] == '/')
@@ -295,7 +285,7 @@ umax_pp_attach (SANEI_Config * config, const char *devname)
devname);
return SANE_STATUS_IO_ERROR;
}
- sprintf (model, "Astra %dP", mdl);
+ snprintf (model, sizeof(model), "Astra %dP", mdl);
dev = malloc (sizeof (Umax_PP_Descriptor) * (num_devices + 1));
@@ -319,12 +309,12 @@ umax_pp_attach (SANEI_Config * config, const char *devname)
num_devices++;
/* if there are user provided values, use them */
- val=(SANE_Char *) config->values[CFG_NAME];
+ val=(const SANE_Char *) config->values[CFG_NAME];
if(strlen(val)==0)
dev->sane.name = strdup (devname);
else
dev->sane.name = strdup (val);
- val=(SANE_Char *) config->values[CFG_VENDOR];
+ val=(const SANE_Char *) config->values[CFG_VENDOR];
if(strlen(val)==0)
dev->sane.vendor = strdup ("UMAX");
else
@@ -351,11 +341,11 @@ umax_pp_attach (SANEI_Config * config, const char *devname)
dev->max_h_size = 2550;
dev->max_v_size = 3500;
}
- val=(SANE_Char *) config->values[CFG_MODEL];
+ val=(const SANE_Char *) config->values[CFG_MODEL];
if(strlen(val)==0)
- dev->sane.model = strdup (model);
+ dev->sane.model = strdup (model);
else
- dev->sane.model = strdup (val);
+ dev->sane.model = strdup (val);
DBG (3, "umax_pp_attach: device %s attached\n", devname);
@@ -1462,6 +1452,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
if (info)
*info |= SANE_INFO_RELOAD_PARAMS;
+ // fall through
case OPT_GRAY_GAIN:
case OPT_GREEN_GAIN:
case OPT_RED_GAIN:
diff --git a/backend/umax_pp_low.c b/backend/umax_pp_low.c
index c5d9a93..ddcf3da 100644
--- a/backend/umax_pp_low.c
+++ b/backend/umax_pp_low.c
@@ -924,7 +924,7 @@ sanei_parport_find_device (void)
int
-sanei_umax_pp_initPort (int port, char *name)
+sanei_umax_pp_initPort (int port, const char *name)
{
#ifndef IO_SUPPORT_MISSING
# ifdef HAVE_LINUX_PPDEV_H
@@ -1027,26 +1027,20 @@ sanei_umax_pp_initPort (int port, char *name)
}
else
{
- sprintf (strmodes, "\n");
- if (modes & PARPORT_MODE_PCSPP)
- sprintf (strmodes, "%s\t\tPARPORT_MODE_PCSPP\n",
- strmodes);
- if (modes & PARPORT_MODE_TRISTATE)
- sprintf (strmodes, "%s\t\tPARPORT_MODE_TRISTATE\n",
- strmodes);
- if (modes & PARPORT_MODE_EPP)
- sprintf (strmodes, "%s\t\tPARPORT_MODE_EPP\n", strmodes);
+ snprintf(strmodes, sizeof(strmodes),
+ "\n%s%s%s%s%s%s",
+ (modes & PARPORT_MODE_PCSPP)? "\t\tPARPORT_MODE_PCSPP\n": "",
+ (modes & PARPORT_MODE_TRISTATE)? "\t\tPARPORT_MODE_TRISTATE\n": "",
+ (modes & PARPORT_MODE_EPP)? "\t\tPARPORT_MODE_EPP\n": "",
+ (modes & PARPORT_MODE_ECP)? "\t\tPARPORT_MODE_ECP\n": "",
+ (modes & PARPORT_MODE_COMPAT)? "\t\tPARPORT_MODE_COMPAT\n": "",
+ (modes & PARPORT_MODE_DMA)? "\t\tPARPORT_MODE_DMA\n": "");
+
if (modes & PARPORT_MODE_ECP)
{
- sprintf (strmodes, "%s\t\tPARPORT_MODE_ECP\n",
- strmodes);
gECP = 1;
}
- if (modes & PARPORT_MODE_COMPAT)
- sprintf (strmodes, "%s\t\tPARPORT_MODE_COMPAT\n",
- strmodes);
- if (modes & PARPORT_MODE_DMA)
- sprintf (strmodes, "%s\t\tPARPORT_MODE_DMA\n", strmodes);
+
DBG (32, "parport modes: %X\n", modes);
DBG (32, "parport modes: %s\n", strmodes);
if (!(modes & PARPORT_MODE_EPP)
@@ -10239,7 +10233,7 @@ moveToOrigin (void)
end[0] = 0x19;
end[1] = 0xD5;
end[4] = 0x1B;
-
+ // fall through
case 1220:
case 2000:
w = 300;
@@ -11226,6 +11220,7 @@ sanei_umax_pp_startScan (int x, int y, int width, int height, int dpi,
}
else
y += 80;
+ // fall through
default:
y += 8;
break;
diff --git a/backend/umax_pp_low.h b/backend/umax_pp_low.h
index 5e986c0..253ef6a 100644
--- a/backend/umax_pp_low.h
+++ b/backend/umax_pp_low.h
@@ -46,7 +46,7 @@
/*****************************************************************************/
/* set port to 'idle state' and get iopl */
/*****************************************************************************/
-extern int sanei_umax_pp_initPort (int port, char *name);
+extern int sanei_umax_pp_initPort (int port, const char *name);
extern int sanei_umax_pp_initScanner (int recover);
extern int sanei_umax_pp_initTransport (int recover);
extern int sanei_umax_pp_endSession (void);
diff --git a/backend/umax_pp_mid.c b/backend/umax_pp_mid.c
index 5f9fd5e..4b16745 100644
--- a/backend/umax_pp_mid.c
+++ b/backend/umax_pp_mid.c
@@ -199,7 +199,7 @@ sanei_umax_pp_model (int port, int *model)
}
int
-sanei_umax_pp_attach (int port, char *name)
+sanei_umax_pp_attach (int port, const char *name)
{
int recover = 0;
diff --git a/backend/umax_pp_mid.h b/backend/umax_pp_mid.h
index 5903a45..97d1366 100644
--- a/backend/umax_pp_mid.h
+++ b/backend/umax_pp_mid.h
@@ -74,7 +74,7 @@
*/
-extern int sanei_umax_pp_attach (int port, char *name);
+extern int sanei_umax_pp_attach (int port, const char *name);
/*
recognizes 1220P from 2000P
diff --git a/backend/xerox_mfp.c b/backend/xerox_mfp.c
index b7fcbee..f5fd70e 100644
--- a/backend/xerox_mfp.c
+++ b/backend/xerox_mfp.c
@@ -95,6 +95,9 @@ static char *str_cmd(int cmd)
#define MAX_DUMP 70
const char *encTmpFileName = "/tmp/stmp_enc.tmp";
+/*
+ * Decode jpeg from `infilename` into dev->decData of dev->decDataSize size.
+ */
static int decompress(struct device __sane_unused__ *dev,
const char __sane_unused__ *infilename)
{
@@ -131,6 +134,7 @@ static int decompress(struct device __sane_unused__ *dev,
height = cinfo.output_height;
pixel_size = cinfo.output_components;
bmp_size = width * height * pixel_size;
+ assert(bmp_size <= POST_DATASIZE);
dev->decDataSize = bmp_size;
row_stride = width * pixel_size;
@@ -152,32 +156,30 @@ static int decompress(struct device __sane_unused__ *dev,
#endif
}
+/* copy from decoded jpeg image (dev->decData) into user's buffer (pDest) */
+/* returns 0 if there is no data to copy */
static int copy_decompress_data(struct device *dev, unsigned char *pDest, int maxlen, int *destLen)
{
int data_size = 0;
- size_t result = 0, retVal = 0;
-
- if (0 == dev->decDataSize) {
- *destLen = 0;
- return retVal;
- }
+ if (destLen)
+ *destLen = 0;
+ if (!dev->decDataSize)
+ return 0;
data_size = dev->decDataSize - dev->currentDecDataIndex;
- if (data_size > maxlen) {
+ if (data_size > maxlen)
data_size = maxlen;
+ if (data_size && pDest) {
+ memcpy(pDest, dev->decData + dev->currentDecDataIndex, data_size);
+ if (destLen)
+ *destLen = data_size;
+ dev->currentDecDataIndex += data_size;
}
- memcpy(pDest, dev->decData+dev->currentDecDataIndex, data_size);
- result = data_size;
- *destLen = result;
- dev->currentDecDataIndex += result;
- retVal = result;
-
if (dev->decDataSize == dev->currentDecDataIndex) {
dev->currentDecDataIndex = 0;
dev->decDataSize = 0;
}
-
- return retVal;
+ return 1;
}
static int decompress_tempfile(struct device *dev)
@@ -209,6 +211,8 @@ static int isSupportedDevice(struct device __sane_unused__ *dev)
if (dev->compressionTypes & (1 << 6)) {
/* blacklist malfunctioning device(s) */
if (!strncmp(dev->sane.model, "SCX-4500W", 9) ||
+ !strncmp(dev->sane.model, "C460", 4) ||
+ !!strstr(dev->sane.model, "CLX-3170") ||
!strncmp(dev->sane.model, "M288x", 5))
return 0;
return 1;
@@ -1293,9 +1297,10 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
dev->decDataSize > 0) {
int diff = dev->total_img_size - dev->total_out_size;
int bufLen = (diff < maxlen) ? diff : maxlen;
- if (0 < diff &&
- 0 < copy_decompress_data(dev, buf, bufLen, lenp)) {
- dev->total_out_size += *lenp;
+ if (diff &&
+ copy_decompress_data(dev, buf, bufLen, lenp)) {
+ if (lenp)
+ dev->total_out_size += *lenp;
return SANE_STATUS_GOOD;
}
}
@@ -1459,6 +1464,7 @@ sane_start(SANE_Handle h)
if (!dev->data && !(dev->data = malloc(DATASIZE)))
return ret_cancel(dev, SANE_STATUS_NO_MEM);
+ /* this is for jpeg mode only */
if (!dev->decData && !(dev->decData = malloc(POST_DATASIZE)))
return ret_cancel(dev, SANE_STATUS_NO_MEM);
diff --git a/backend/xerox_mfp.h b/backend/xerox_mfp.h
index 3d93f06..d85fe14 100644
--- a/backend/xerox_mfp.h
+++ b/backend/xerox_mfp.h
@@ -74,8 +74,8 @@ struct device {
#define DATATAIL(dev) ((dev->dataoff + dev->datalen) & DATAMASK)
#define DATAROOM(dev) dataroom(dev)
-#define POST_DATASIZE 0xFFFFFF
- SANE_Byte *decData;
+#define POST_DATASIZE 0xFFFFFF /* 16777215 bytes */
+ SANE_Byte *decData; /* static buffer of POST_DATASIZE bytes */
int decDataSize;
int currentDecDataIndex;
/* data from CMD_INQUIRY: */