diff options
Diffstat (limited to 'backend')
144 files changed, 21758 insertions, 18985 deletions
diff --git a/backend/Makefile.am b/backend/Makefile.am index 4a947bf..2ac341c 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -65,6 +65,7 @@ EXTRA_DIST += saned.conf.in BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \ artec_eplus48u.conf avision.conf bh.conf \ canon630u.conf canon.conf canon_dr.conf \ + canon_lide70.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 \ @@ -157,6 +158,7 @@ be_convenience_libs = libabaton.la libagfafocus.la \ libapple.la libartec.la libartec_eplus48u.la \ libas6e.la libavision.la libbh.la \ libcanon.la libcanon630u.la libcanon_dr.la \ + libcanon_lide70.la \ libcanon_pp.la libcardscan.la libcoolscan.la \ libcoolscan2.la libcoolscan3.la libdc25.la \ libdc210.la libdc240.la libdell1600n_net.la \ @@ -190,6 +192,7 @@ be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \ libsane-apple.la libsane-artec.la libsane-artec_eplus48u.la \ libsane-as6e.la libsane-avision.la libsane-bh.la \ libsane-canon.la libsane-canon630u.la libsane-canon_dr.la \ + libsane-canon_lide70.la \ libsane-canon_pp.la libsane-cardscan.la libsane-coolscan.la \ libsane-coolscan2.la libsane-coolscan3.la libsane-dc25.la \ libsane-dc210.la libsane-dc240.la libsane-dell1600n_net.la \ @@ -344,6 +347,17 @@ libsane_canon_dr_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) libcanon_dr.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 += canon_dr.conf.in +libcanon_lide70_la_SOURCES = canon_lide70.c +libcanon_lide70_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_lide70 + +nodist_libsane_canon_lide70_la_SOURCES = canon_lide70-s.c +libsane_canon_lide70_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_lide70 +libsane_canon_lide70_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) +libsane_canon_lide70_la_LIBADD = $(COMMON_LIBS) libcanon_lide70.la ../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 += canon_lide70.conf.in +# TODO: Why are this distributed but not compiled? +EXTRA_DIST += canon_lide70-common.c + libcanon_pp_la_SOURCES = canon_pp.c canon_pp.h canon_pp-io.c canon_pp-io.h canon_pp-dev.c canon_pp-dev.h libcanon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp @@ -437,13 +451,26 @@ 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 +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 \ + escl/escl_pdf.c \ + escl/escl_crop.c +libescl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(POPPLER_GLIB_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_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(POPPLER_GLIB_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl +libsane_escl_la_CFLAGS = $(AM_CFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(POPPLER_GLIB_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) +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 $(MATH_LIB) $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS) $(POPPLER_GLIB_LIBS) $(XML_LIBS) $(libcurl_LIBS) $(AVAHI_LIBS) endif endif endif @@ -501,10 +528,9 @@ libsane_fujitsu_la_LIBADD = $(COMMON_LIBS) libfujitsu.la ../sanei/sanei_init_deb EXTRA_DIST += fujitsu.conf.in 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/command_set_common.h genesys/command_set_common.cpp \ genesys/device.h genesys/device.cpp \ genesys/enums.h genesys/enums.cpp \ genesys/error.h genesys/error.cpp \ @@ -512,6 +538,7 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.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/gl842.cpp genesys/gl842.h genesys/gl842_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 \ @@ -532,15 +559,16 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \ genesys/status.h genesys/status.cpp \ genesys/tables_frontend.cpp \ genesys/tables_gpo.cpp \ + genesys/tables_memory_layout.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/value_filter.h \ genesys/utilities.h libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys @@ -548,7 +576,10 @@ libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys 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) +libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la \ + ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \ + ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo \ + $(MATH_LIB) $(TIFF_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += genesys.conf.in libgphoto2_i_la_SOURCES = gphoto2.c gphoto2.h @@ -682,10 +713,10 @@ libsane_kodak_la_LIBADD = $(COMMON_LIBS) libkodak.la ../sanei/sanei_init_debug.l EXTRA_DIST += kodak.conf.in libkodakaio_la_SOURCES = kodakaio.c kodakaio.h -libkodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodakaio +libkodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=kodakaio nodist_libsane_kodakaio_la_SOURCES = kodakaio-s.c -libsane_kodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodakaio +libsane_kodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=kodakaio libsane_kodakaio_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_kodakaio_la_LIBADD = $(COMMON_LIBS) libkodakaio.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_tcp.lo ../sanei/sanei_udp.lo $(USB_LIBS) $(SOCKET_LIBS) $(AVAHI_LIBS) $(MATH_LIB) $(RESMGR_LIBS) EXTRA_DIST += kodakaio.conf.in @@ -904,15 +935,34 @@ libpixma_la_SOURCES = pixma/pixma.c \ pixma/pixma_bjnp.h \ pixma/pixma_bjnp_private.h \ pixma/pixma_rename.h -libpixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma +libpixma_la_CPPFLAGS = $(AM_CPPFLAGS) $(XML_CFLAGS) -DBACKEND_NAME=pixma + +# Generate options files included by pixma/pixma.c from said file. +# The circular dependency means we cannot add it as a prerequisite +# for the targets that build the options files. + +$(srcdir)/pixma/pixma.c: \ + $(srcdir)/pixma/pixma_sane_options.h \ + $(srcdir)/pixma/pixma_sane_options.c + +$(srcdir)/pixma/pixma_sane_options.h: + @echo Generating $@ from $(@D)/pixma.c + @(cd $(@D); python scripts/pixma_gen_options.py h < pixma.c > $(@F)) +$(srcdir)/pixma/pixma_sane_options.c: + @echo Generating $@ from $(@D)/pixma.c + @(cd $(@D); python scripts/pixma_gen_options.py < pixma.c > $(@F)) + +EXTRA_DIST += pixma/pixma_sane_options.c +EXTRA_DIST += pixma/pixma_sane_options.h +EXTRA_DIST += pixma/scripts/pixma_gen_options.py +CLEANFILES += pixma/pixma_sane_options.c +CLEANFILES += pixma/pixma_sane_options.h 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) $(SANEI_THREAD_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) $(XML_LIBS) $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += pixma.conf.in -# 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 diff --git a/backend/avision.c b/backend/avision.c index 55b5f4f..862a275 100644 --- a/backend/avision.c +++ b/backend/avision.c @@ -164,451 +164,583 @@ static Avision_HWEntry Avision_Device_List [] = { "AVISION", "AV100CS", 0, 0, "Avision", "AV100CS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "AVISION", "AV100IIICS", 0, 0, "Avision", "AV100IIICS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "AVISION", "AV100S", 0, 0, "Avision", "AV100S", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x0638, 0x0A27, "Avision", "AV120", - AV_INT_STATUS}, + AV_INT_STATUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A3C, "Avision", "AV121", - AV_INT_BUTTON | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA}, + AV_INT_BUTTON | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0A33, "Avision", "AV122", - AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET}, + AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, + { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } + }, /* comment="sheetfed duplex scanner" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0A93, "Avision", "AV122 C2", - AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_NOT_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET}, + AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_NOT_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, + { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } + }, /* comment="sheetfed duplex scanner" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0A24, "Avision", "AV210", - AV_INT_BUTTON | AV_ACCEL_TABLE}, + AV_INT_BUTTON | AV_ACCEL_TABLE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A25, "Avision", "AV210", - AV_INT_BUTTON | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN}, + AV_INT_BUTTON | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A3A, "Avision", "AV210C2", - AV_INT_BUTTON | AV_GRAY_MODES}, + AV_INT_BUTTON | AV_GRAY_MODES, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2F, "Avision", "AV210C2-G", - AV_INT_BUTTON | AV_GRAY_MODES}, + AV_INT_BUTTON | AV_GRAY_MODES, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x1A35, "Avision", "AV210D2+", - AV_INT_BUTTON | AV_USE_GRAY_FILTER}, + AV_INT_BUTTON | AV_USE_GRAY_FILTER, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A23, "Avision", "AV220", - AV_INT_BUTTON | AV_GRAY_MODES}, + AV_INT_BUTTON | AV_GRAY_MODES, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2A, "Avision", "AV220C2", - AV_INT_BUTTON | AV_CANCEL_BUTTON}, + AV_INT_BUTTON | AV_CANCEL_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2B, "Avision", "AV220D2", - AV_INT_BUTTON | AV_CANCEL_BUTTON}, + AV_INT_BUTTON | AV_CANCEL_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x1A31, "Avision", "AV220D2+", - AV_INT_BUTTON | AV_CANCEL_BUTTON | AV_USE_GRAY_FILTER}, + AV_INT_BUTTON | AV_CANCEL_BUTTON | AV_USE_GRAY_FILTER, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2C, "Avision", "AV220+", - AV_INT_BUTTON | AV_CANCEL_BUTTON}, + AV_INT_BUTTON | AV_CANCEL_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2D, "Avision", "AV220C2-G", - AV_INT_BUTTON | AV_CANCEL_BUTTON}, + AV_INT_BUTTON | AV_CANCEL_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2E, "Avision", "AV220C2-B", - AV_INT_BUTTON | AV_CANCEL_BUTTON}, + AV_INT_BUTTON | AV_CANCEL_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A94, "Avision", "AV220-G", - AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_FIRMWARE}, + AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_FIRMWARE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { "AVISION", "AV240SC", 0, 0, "Avision", "AV240SC", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "AVISION", "AV260CS", 0, 0, "Avision", "AV260CS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "AVISION", "AV360CS", 0, 0, "Avision", "AV360CS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "AVISION", "AV363CS", 0, 0, "Avision", "AV363CS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "AVISION", "AV420CS", 0, 0, "Avision", "AV420CS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "AVISION", "AV6120", 0, 0, "Avision", "AV6120", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, "AV610", 0x0638, 0x0a18, "Avision", "AV610", - AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | AV_INT_BUTTON}, + AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x0638, 0x0a18, "Avision", "AV600U Plus", /* If this unit requires the AV_INT_STATUS flag, then we'll need to alter the code to deal with two different devices with the same USB id (AV610 above) */ - AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | /* AV_INT_STATUS | */ AV_INT_BUTTON}, + AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | /* AV_INT_STATUS | */ AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x0638, 0x0a5e, "Avision", "AV610C2", - AV_NO_BACKGROUND | AV_INT_BUTTON}, /* cancel button -> sense abort! */ + AV_NO_BACKGROUND | AV_INT_BUTTON, /* cancel button -> sense abort! */ + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x0638, 0x0a41, "Avision", "AM3000 Series", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="MFD" */ /* status="basic" */ { NULL, NULL, 0x0638, 0x0a16, "Avision", "DS610CU Scancopier", - AV_INT_STATUS}, + AV_INT_STATUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 600 dpi, A4" */ /* status="good" */ { "AVISION", "AV620CS", 0, 0, "Avision", "AV620CS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 600 dpi" */ /* status="complete" */ { "AVISION", "AV620CS Plus", 0, 0, "Avision", "AV620CS Plus", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi" */ /* status="complete" */ { "AVISION", "AV630CS", 0, 0, "Avision", "AV630CS", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi" */ /* status="complete" */ { "AVISION", "AV630CSL", 0, 0, "Avision", "AV630CSL", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi" */ /* status="untested" */ { "AVISION", "AV6240", 0, 0, "Avision", "AV6240", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A13, "Avision", "AV600U", - AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_SOFT_SCALE | AV_INT_STATUS | AV_NO_BUTTON}, + AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_SOFT_SCALE | AV_INT_STATUS | AV_NO_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 600 dpi" */ /* status="good" */ { "AVISION", "AV660S", 0, 0, "Avision", "AV660S", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV680S", 0, 0, "Avision", "AV680S", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV690U", 0, 0, "Avision", "AV690U", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 2400 dpi" */ /* status="untested" */ { "AVISION", "AV800S", 0, 0, "Avision", "AV800S", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV810C", 0, 0, "Avision", "AV810C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV820", 0, 0, "Avision", "AV820", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV820C", 0, 0, "Avision", "AV820C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV820C Plus", 0, 0, "Avision", "AV820C Plus", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV830C", 0, 0, "Avision", "AV830C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV830C Plus", 0, 0, "Avision", "AV830C Plus", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV880", 0, 0, "Avision", "AV880", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV880C", 0, 0, "Avision", "AV880C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV3200C", 0, 0, "Avision", "AV3200C", - AV_NON_INTERLACED_DUPLEX_300 | AV_FASTER_WITH_FILTER}, + AV_NON_INTERLACED_DUPLEX_300 | AV_FASTER_WITH_FILTER, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3200SU", 0x0638, 0x0A4E, "Avision", "AV3200SU", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3730SU", 0x0638, 0x0A4F, "Avision", "AV3730SU", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3750SU", 0x0638, 0x0A65, "Avision", "AV3750SU", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3800C", 0, 0, "Avision", "AV3800C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3850SU", 0x0638, 0x0a66, "Avision", "AV3850SU", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "FB6000E", 0, 0, "Avision", "FB6000E", - AV_NON_INTERLACED_DUPLEX_300}, + AV_NON_INTERLACED_DUPLEX_300, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3 - duplex! - zero edge!" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0a82, "Avision", "FB6080E", - AV_NON_INTERLACED_DUPLEX_300}, + AV_NON_INTERLACED_DUPLEX_300, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3 - duplex! - zero edge!" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0a84, "Avision", "FB2080E", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 600 dpi, zero-edge" ASIC 7 */ /* status="basic" */ { "AVISION", "AV8000S", 0, 0, "Avision", "AV8000S", - AV_DOES_NOT_KEEP_WINDOW}, + AV_DOES_NOT_KEEP_WINDOW, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0a4d, "Avision", "AV8050U", - AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA}, + AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3 - duplex!" */ /* status="complete" */ { "AVISION", "AV8300", 0x0638, 0x0A40, "Avision", "AV8300", - AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA}, + AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3 - duplex!" */ /* status="complete" */ { "AVISION", "AV8350", 0x0638, 0x0A68, "Avision", "AV8350", - AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA}, + AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3 - duplex!" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A61, "Avision", "IT8300", - AV_NON_INTERLACED_DUPLEX_300 | AV_ACCEL_TABLE}, + AV_NON_INTERLACED_DUPLEX_300 | AV_ACCEL_TABLE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3 - duplex!, LCD screen, paper sensors" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0AA1, "Avision", "@V2500", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="" */ /* status="untested" */ { NULL, NULL, 0x0638, 0x0A45, "Avision", "@V5100", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi, A3 - duplex!, LCD screen, paper sensors" */ /* status="good" */ { "AVISION", "AVA3", 0, 0, "Avision", "AVA3", - AV_FORCE_A3}, + AV_FORCE_A3, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 600 dpi, A3" */ /* status="basic" */ @@ -617,21 +749,27 @@ static Avision_HWEntry Avision_Device_List [] = { "HP", "ScanJet 5300C", 0x03f0, 0x0701, "Hewlett-Packard", "ScanJet 5300C", - AV_INT_STATUS}, + AV_INT_STATUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 2400 dpi - some FW revisions have x-axis image scaling problems over 1200 dpi" */ /* status="complete" */ { "HP", "ScanJet 5370C", 0x03f0, 0x0701, "Hewlett-Packard", "ScanJet 5370C", - AV_MULTI_CALIB_CMD | AV_INT_STATUS}, + AV_MULTI_CALIB_CMD | AV_INT_STATUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 2400 dpi - some FW revisions have x-axis image scaling problems over 1200 dpi" */ /* status="good" */ { "hp", "scanjet 7400c", 0x03f0, 0x0801, "Hewlett-Packard", "ScanJet 7400c", - AV_LIGHT_CHECK_BOGUS | AV_NO_64BYTE_ALIGN | AV_INT_STATUS}, + AV_LIGHT_CHECK_BOGUS | AV_NO_64BYTE_ALIGN | AV_INT_STATUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 2400 dpi - dual USB/SCSI interface" */ /* status="good" */ @@ -639,14 +777,18 @@ static Avision_HWEntry Avision_Device_List [] = { "hp", "scanjet 7450c", 0x03f0, 0x0801, "Hewlett-Packard", "ScanJet 7450c", - AV_NO_64BYTE_ALIGN | AV_INT_STATUS}, + AV_NO_64BYTE_ALIGN | AV_INT_STATUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 2400 dpi - dual USB/SCSI interface" */ /* status="good" */ { "hp", "scanjet 7490c", 0x03f0, 0x0801, "Hewlett-Packard", "ScanJet 7490c", - AV_NO_64BYTE_ALIGN | AV_INT_STATUS}, + AV_NO_64BYTE_ALIGN | AV_INT_STATUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 1200 dpi - dual USB/SCSI interface" */ /* status="good" */ @@ -654,7 +796,9 @@ static Avision_HWEntry Avision_Device_List [] = { "HP", "C9930A", 0x03f0, 0x0b01, "Hewlett-Packard", "ScanJet 8200", - AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE}, + AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ @@ -662,7 +806,9 @@ static Avision_HWEntry Avision_Device_List [] = { "HP", "C9930A", 0x03f0, 0x0b01, "Hewlett-Packard", "ScanJet 8250", - AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE}, + AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ #endif @@ -670,7 +816,9 @@ static Avision_HWEntry Avision_Device_List [] = { "HP", "C9930A", 0x03f0, 0x3905, "Hewlett-Packard", "ScanJet 8270", - AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE}, + AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ @@ -678,7 +826,9 @@ static Avision_HWEntry Avision_Device_List [] = { "HP", "C9930A", 0x03f0, 0x0b01, "Hewlett-Packard", "ScanJet 8290", - AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE}, + AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 4800 (?) dpi - USB 2.0 and SCSI - only SCSI tested so far" */ /* status="good" */ @@ -686,7 +836,9 @@ static Avision_HWEntry Avision_Device_List [] = { "HP", "C9930A", 0x03f0, 0x3805, "Hewlett-Packard", "ScanJet 8300", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ @@ -694,14 +846,18 @@ static Avision_HWEntry Avision_Device_List [] = { "HP", "C9930A", 0x03f0, 0x3805, "Hewlett-Packard", "ScanJet 8350", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ { "HP", "C9930A", 0x03f0, 0x3805, "Hewlett-Packard", "ScanJet 8390", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ @@ -709,79 +865,103 @@ static Avision_HWEntry Avision_Device_List [] = { "Minolta", "#2882", 0, 0, "Minolta", "Dimage Scan Dual I", - AV_FORCE_FILM | AV_NO_START_SCAN}, /* not AV_FILMSCANNER (no frame control) */ + AV_FORCE_FILM | AV_NO_START_SCAN, /* not AV_FILMSCANNER (no frame control) */ + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="basic" */ { "Minolta", "#2887", 0, 0, "Minolta", "Scan Multi Pro", - AV_FORCE_FILM | AV_NO_START_SCAN}, /* AV_FILMSCANNER (frame control)? */ + AV_FORCE_FILM | AV_NO_START_SCAN, /* not AV_FILMSCANNER (no frame control) */ + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "MINOLTA", "FS-V1", 0x0638, 0x026a, "Minolta", "Dimage Scan Dual II", - AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_12_BIT_MODE}, + AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_12_BIT_MODE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, film-scanner" */ /* status="good" */ { "MINOLTA", "Elite II", 0x0686, 0x4004, "Minolta", "Elite II", - AV_FILMSCANNER | AV_ONE_CALIB_CMD}, + AV_FILMSCANNER | AV_ONE_CALIB_CMD, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, film-scanner" */ /* status="untested" */ { "MINOLTA", "FS-V3", 0x0686, 0x400d, "Minolta", "Dimage Scan Dual III", - AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_ACCEL_TABLE}, + AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_ACCEL_TABLE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, film-scanner" */ /* status="good" */ { "MINOLTA", "FS-V4", 0x0686, 0x400e, "Minolta", "Dimage Scan Elite 5400", - AV_FILMSCANNER | AV_ONE_CALIB_CMD | /*AV_ACCEL_TABLE |*/ AV_NO_START_SCAN}, + AV_FILMSCANNER | AV_ONE_CALIB_CMD | /*AV_ACCEL_TABLE |*/ AV_NO_START_SCAN, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, film-scanner" */ /* status="good" */ { "QMS", "SC-110", 0x0638, 0x0a15, "Minolta-QMS", "SC-110", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="" */ /* status="untested" */ { "QMS", "SC-215", 0x0638, 0x0a16, "Minolta-QMS", "SC-215", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="" */ /* status="good" */ { "MITSBISH", "MCA-ADFC", 0, 0, "Mitsubishi", "MCA-ADFC", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "MITSBISH", "MCA-S1200C", 0, 0, "Mitsubishi", "S1200C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "MITSBISH", "MCA-S600C", 0, 0, "Mitsubishi", "S600C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "MITSBISH", "SS600", 0, 0, "Mitsubishi", "SS600", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ /* The next are all untested ... */ @@ -789,209 +969,291 @@ static Avision_HWEntry Avision_Device_List [] = { "FCPA", "ScanPartner", 0, 0, "Fujitsu", "ScanPartner", - AV_FUJITSU}, + AV_FUJITSU, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "FCPA", "ScanPartner 10", 0, 0, "Fujitsu", "ScanPartner 10", - AV_FUJITSU}, + AV_FUJITSU, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "FCPA", "ScanPartner 10C", 0, 0, "Fujitsu", "ScanPartner 10C", - AV_FUJITSU}, + AV_FUJITSU, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "FCPA", "ScanPartner 15C", 0, 0, "Fujitsu", "ScanPartner 15C", - AV_FUJITSU}, + AV_FUJITSU, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "FCPA", "ScanPartner 300C", 0, 0, "Fujitsu", "ScanPartner 300C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "FCPA", "ScanPartner 600C", 0, 0, "Fujitsu", "ScanPartner 600C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "FCPA", "ScanPartner 620C", 0, 0, "Fujitsu", "ScanPartner 620C", - AV_LIGHT_CHECK_BOGUS}, + AV_LIGHT_CHECK_BOGUS, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { "FCPA", "ScanPartner Jr", 0, 0, "Fujitsu", "ScanPartner Jr", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { "FCPA", "ScanStation", 0, 0, "Fujitsu", "ScanStation", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04c5, 0x1029, "Fujitsu", "fi-4010CU", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04c5, 0x10ef, "Fujitsu", "fi-5015C", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x040a, 0x6001, "Kodak", "i30", - AV_INT_BUTTON | AV_GRAY_MODES}, + AV_INT_BUTTON | AV_GRAY_MODES, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x040a, 0x6002, "Kodak", "i40", - AV_INT_BUTTON | AV_GRAY_MODES}, + AV_INT_BUTTON | AV_GRAY_MODES, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="basic" */ { NULL, NULL, 0x040a, 0x6003, "Kodak", "i50", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x040a, 0x6003, "Kodak", "i55", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ #endif { NULL, NULL, 0x040a, 0x6004, "Kodak", "i60", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x040a, 0x6004, "Kodak", "i65", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ #endif { NULL, NULL, 0x040a, 0x6005, "Kodak", "i80", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ + { NULL, NULL, + 0x040a, 0x6013, + "Kodak", "i1120", + AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_USE_GRAY_FILTER | AV_SOFT_SCALE | + AV_FORCE_CALIB | AV_NO_QSCAN_MODE | AV_NO_QCALIB_MODE | AV_OVERSCAN_OPTDPI | + AV_NO_REAR | AV_FASTFEED_ON_CANCEL | AV_GAMMA_10 | AV_MULTI_SHEET_SCAN, + { /* offsets */ + -1.5, /* first sheet (should be identical for single / duplex) */ + {2.5, -6.0}, /* front-only scan */ + {{2.0, -14.0}, {-10.0, -2.0}} /* duplex scan */ + } + }, + /* comment="duplex sheetfed scanner" */ + /* status="good" */ + /* This is a Kodak OEM device manufactured by avision. + It uses an Avision firmware modified by Kodak, so + some modifications needed to be done here. */ + { "iVina", "1200U", 0x0638, 0x0268, "iVina", "1200U", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0424, "Visioneer", "Strobe XP 450", - AV_INT_BUTTON | AV_ACCEL_TABLE}, + AV_INT_BUTTON | AV_ACCEL_TABLE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0491, "Visioneer", "Strobe XP 450-G", - AV_INT_BUTTON | AV_ACCEL_TABLE}, + AV_INT_BUTTON | AV_ACCEL_TABLE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0479, "Visioneer", "Strobe XP 470", - AV_INT_BUTTON | AV_ACCEL_TABLE}, + AV_INT_BUTTON | AV_ACCEL_TABLE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x048F, "Visioneer", "Strobe XP 470-G", - AV_INT_BUTTON | AV_ACCEL_TABLE}, + AV_INT_BUTTON | AV_ACCEL_TABLE, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0420, "Visioneer", "9320", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0421, "Visioneer", "9450", - AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_NO_BUTTON | AV_NO_TUNE_SCAN_LENGTH}, + AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_NO_BUTTON | AV_NO_TUNE_SCAN_LENGTH, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x047A, "Visioneer", "9450-G", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0422, "Visioneer", "9550", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0390, "Visioneer", "9650", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x047B, "Visioneer", "9650-G", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0423, "Visioneer", "9750", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0493, "Visioneer", "9750-G", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0497, "Visioneer", "Patriot 430", - AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET}, + AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, + { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ @@ -999,7 +1261,9 @@ static Avision_HWEntry Avision_Device_List [] = { NULL, NULL, 0x04a7, 0x048F, "Visioneer", "Patriot 470", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ #endif @@ -1007,150 +1271,198 @@ static Avision_HWEntry Avision_Device_List [] = { NULL, NULL, 0x04a7, 0x0498, "Visioneer", "Patriot 680", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0499, "Visioneer", "Patriot 780", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x049C, "Xerox", "DocuMate150", - AV_INT_BUTTON | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK}, + AV_INT_BUTTON | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0477, "Xerox", "DocuMate152", - AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET | AV_BACKGROUND_QUIRK}, + AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK, + { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x049D, "Xerox", "DocuMate162", - AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_REAR_OFFSET | AV_BACKGROUND_QUIRK}, + AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK, + { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0448, "Xerox", "DocuMate250", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0490, "Xerox", "DocuMate250-G", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0449, "Xerox", "DocuMate252", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x048C, "Xerox", "DocuMate252-G", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0476, "Xerox", "DocuMate232", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x044c, "Xerox", "DocuMate262", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x048D, "Xerox", "DocuMate262-G", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x04a7, "Xerox", "DocuMate262i", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0475, "Xerox", "DocuMate272", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x048E, "Xerox", "DocuMate272-G", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0446, "Xerox", "DocuMate510", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0495, "Xerox", "DocuMate512", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x047c, "Xerox", "DocuMate510-G", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0447, "Xerox", "DocuMate520", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0492, "Xerox", "DocuMate520-G", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x04a7, 0x0498, "Xerox", "DocuMate632", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ #endif { NULL, NULL, 0x04a7, 0x0478, "Xerox", "DocuMate752", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x049A, "Xerox", "DocuMate752", - AV_INT_BUTTON}, + AV_INT_BUTTON, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x0638, 0x0a16, "OKI", "S700 Scancopier", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, 600 dpi, A4" */ /* status="good" */ #endif @@ -1158,14 +1470,18 @@ static Avision_HWEntry Avision_Device_List [] = { "B+H", "2000F", 0, 0, "Bell+Howell", "2000F", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi, A4" */ /* status="basic" */ { NULL, NULL, 0x0482, 0x0335, "Kyocera", "FS-1016MFP", - 0}, + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + }, /* comment="1 pass, ??? dpi, A4" */ /* status="untested" */ @@ -1207,7 +1523,9 @@ Lexmark X4500 MFP { NULL, NULL, 0, 0, NULL, NULL, - 0} + 0, + { 0, {0, 0}, {{0, 0}, {0, 0}} } + } }; #if 0 @@ -2584,16 +2902,156 @@ compute_parameters (Avision_Scanner* s) s->avdimen.bry += overscan; } - /* rear offset compensation */ - if (s->avdimen.interlaced_duplex && dev->hw->feature_type & AV_REAR_OFFSET) { - const double offset = 0.5; /* in current affected models 1/2 inch */ - s->avdimen.rear_offset = (int) (offset * s->avdimen.hw_yres); - DBG (1, "sane_compute_parameters: rear_offset: %d!\n", s->avdimen.rear_offset); - /* we do not limit against the bottom-y here, as rear offset always - applies to ADF scans, only */ - } - else { - s->avdimen.rear_offset = 0; + /* ADF offset compensation + Calculate offsets for skipping lines later with considering overscan + which applies to both, front and rear. The difference needs to be cut + off on the other side. */ + if (dev->adf_offset_compensation && s->avdimen.interlaced_duplex) { + /* ADF Duplex scan */ + + struct { + mm_offset front; + mm_offset rear; + } offsets = {{0, 0}, {0, 0}}; + + double overscan; + double bry_offset = 0; + + /* top */ + overscan = fmax(0, fmax(dev->hw->offset.duplex.front.top, + dev->hw->offset.duplex.rear.top)); + + offsets.front.top += overscan - dev->hw->offset.duplex.front.top; + offsets.rear.top += overscan - dev->hw->offset.duplex.rear.top; + bry_offset += overscan; + + /* bottom */ + overscan = fmax(0, fmax(dev->hw->offset.duplex.front.bottom, + dev->hw->offset.duplex.rear.bottom)); + + offsets.front.bottom += overscan - dev->hw->offset.duplex.front.bottom; + offsets.rear.bottom += overscan - dev->hw->offset.duplex.rear.bottom; + bry_offset += overscan; + + /* first page offset */ + if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) { + /* only applies to multi-sheet-scan */ + + if (dev->hw->offset.first > 0) { + /* move down: + - add top overscan in send_tune_scan_length (effective for all pages!) + - skip bottom lines at page n=0, front and rear + - skip top lines at page n>0, front and rear + */ + if (s->page == 0) { + offsets.front.bottom += dev->hw->offset.first; + offsets.rear.bottom += dev->hw->offset.first; + } else { + offsets.front.top += dev->hw->offset.first; + offsets.rear.top += dev->hw->offset.first; + } + + } else if (dev->hw->offset.first < 0) { + /* move up: + - add bottom overscan in send_tune_scan_length (effective for all pages!) + - skip top lines at page n=0, front and rear + - skip bottom lines at page n>0, front and rear + */ + if (s->page == 0) { + offsets.front.top += fabs(dev->hw->offset.first); + offsets.rear.top += fabs(dev->hw->offset.first); + } else { + offsets.front.bottom += fabs(dev->hw->offset.first); + offsets.rear.bottom += fabs(dev->hw->offset.first); + } + + } + bry_offset += fabs(dev->hw->offset.first); + } + + /* convert to lines */ + s->avdimen.offset.front.top = (int) ( offsets.front.top * s->avdimen.yres / MM_PER_INCH ); + s->avdimen.offset.front.bottom = (int) ( offsets.front.bottom * s->avdimen.yres / MM_PER_INCH ); + s->avdimen.offset.rear.top = (int) ( offsets.rear.top * s->avdimen.yres / MM_PER_INCH ); + s->avdimen.offset.rear.bottom = (int) ( offsets.rear.bottom * s->avdimen.yres / MM_PER_INCH ); + + /* add overscan to bry (hw_lines) */ + s->avdimen.bry += (long) ( bry_offset * s->avdimen.hw_yres / MM_PER_INCH ); + + DBG (1, "sane_compute_parameters: front offset: top: %d!\n", + s->avdimen.offset.front.top); + DBG (1, "sane_compute_parameters: front offset: bottom: %d!\n", + s->avdimen.offset.front.bottom); + DBG (1, "sane_compute_parameters: rear offset: top: %d!\n", + s->avdimen.offset.rear.top); + DBG (1, "sane_compute_parameters: rear offset: bottom: %d!\n", + s->avdimen.offset.rear.bottom); + + } else if (dev->adf_offset_compensation && s->source_mode == AV_ADF) { + /* ADF front scan */ + + mm_offset offsets = {0, 0}; + double bry_offset = 0; + + /* top */ + if (dev->hw->offset.front.top < 0) + offsets.top += fabs(dev->hw->offset.front.top); + else + bry_offset += dev->hw->offset.front.top; + + /* bottom */ + if (dev->hw->offset.front.bottom < 0) + offsets.bottom += fabs(dev->hw->offset.front.bottom); + else + bry_offset += dev->hw->offset.front.bottom; + + /* first page offset */ + if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) { + /* only applies to multi-sheet-scan */ + + if (dev->hw->offset.first > 0) { + /* move down: + - add top overscan in send_tune_scan_length (effective for all pages!) + - skip bottom lines at page n=0 + - skip top lines at page n>0 + */ + if (s->page == 0) + offsets.bottom += dev->hw->offset.first; + else + offsets.top += dev->hw->offset.first; + + } else if (dev->hw->offset.first < 0) { + /* move up: + - add bottom overscan in send_tune_scan_length (effective for all pages!) + - skip top lines at page n=0 + - skip bottom lines at page n>0 + */ + if (s->page == 0) + offsets.top += fabs(dev->hw->offset.first); + else + offsets.bottom += fabs(dev->hw->offset.first); + + } + bry_offset += fabs(dev->hw->offset.first); + } + + /* convert to lines */ + s->avdimen.offset.front.top = (int) ( offsets.top * s->avdimen.yres / MM_PER_INCH ); + s->avdimen.offset.front.bottom = (int) ( offsets.bottom * s->avdimen.yres / MM_PER_INCH ); + + /* add overscan to bry (hw_lines) */ + s->avdimen.bry += (long) ( bry_offset * s->avdimen.hw_yres / MM_PER_INCH ); + + DBG (1, "sane_compute_parameters: front offset: top: %d!\n", + s->avdimen.offset.front.top); + DBG (1, "sane_compute_parameters: front offset: bottom: %d!\n", + s->avdimen.offset.front.bottom); + + } else { + s->avdimen.offset.front.top = 0; + s->avdimen.offset.front.bottom = 0; + s->avdimen.offset.rear.top = 0; + s->avdimen.offset.rear.bottom = 0; } memset (&s->params, 0, sizeof (s->params)); @@ -4088,6 +4546,8 @@ get_double ( &(result[48] ) )); dev->inquiry_button_control = BIT (result[50], 6) | BIT (result[51],2); dev->inquiry_exposure_control = BIT(result[51],7); + if (dev->scanner_type != AV_FILM && !(dev->hw->feature_type & AV_FORCE_FILM)) + dev->inquiry_exposure_control = 0; dev->inquiry_max_shading_target = get_double ( &(result[75]) ); dev->inquiry_color_boundary = result[54]; @@ -4365,7 +4825,10 @@ get_tune_scan_length (Avision_Scanner* s) static SANE_Status send_tune_scan_length (Avision_Scanner* s) { - int top, bottom; + Avision_Device* dev = s->hw; + + int top, bottom, dpi; + double offset = 0; SANE_Status status; size_t size; @@ -4385,10 +4848,34 @@ send_tune_scan_length (Avision_Scanner* s) set_triple (scmd.transferlen, size); /* the SPEC says optical DPI, but real world measuring suggests it is 1200 - as in the window descriptor */ - top = 1200 * SANE_UNFIX (s->val[OPT_OVERSCAN_TOP].w) / MM_PER_INCH; + as in the window descriptor. + MN: This is not true for at least Kodak i1120 where it is optical DPI + */ + dpi = 1200; + if (dev->hw->feature_type & AV_OVERSCAN_OPTDPI) + dpi = dev->inquiry_optical_res; + + top = dpi * SANE_UNFIX (s->val[OPT_OVERSCAN_TOP].w) / MM_PER_INCH; DBG (3, "send_tune_scan_length: top: %d\n", top); + /* top offset compensation */ + if (dev->adf_offset_compensation) { + if (s->avdimen.interlaced_duplex) + offset += fmax(0, fmax(dev->hw->offset.duplex.front.top, + dev->hw->offset.duplex.rear.top) ); + else if (s->source_mode == AV_ADF) + offset += fmax(0, dev->hw->offset.front.top); + + /* first page offset */ + if (dev->hw->offset.first > 0) + offset += dev->hw->offset.first; + + /* convert to lines */ + int top_offset = dpi * offset / MM_PER_INCH; + top += top_offset; + DBG (3, "send_tune_scan_length: top offset: %d\n", top_offset); + } + set_double (scmd.datatypequal, 0x0001); /* attach, 0x000 is shorten */ set_double (payload.vertical, top); /* set_double (payload.horizontal, 0); */ @@ -4405,9 +4892,28 @@ send_tune_scan_length (Avision_Scanner* s) } scmd.datatypecode = 0x95; /* Attach/Truncate tail(right) of scan length */ - bottom = 1200 * SANE_UNFIX (s->val[OPT_OVERSCAN_BOTTOM].w) / MM_PER_INCH; + bottom = dpi * SANE_UNFIX (s->val[OPT_OVERSCAN_BOTTOM].w) / MM_PER_INCH; DBG (3, "send_tune_scan_length: bottom: %d\n", bottom); + /* bottom offset compensation */ + offset = 0; + if (dev->adf_offset_compensation) { + if (s->avdimen.interlaced_duplex) + offset += fmax(0, fmax(dev->hw->offset.duplex.front.bottom, + dev->hw->offset.duplex.rear.bottom) ); + else if (s->source_mode == AV_ADF) + offset += fmax(0, dev->hw->offset.front.bottom); + + /* first page offset */ + if (dev->hw->offset.first < 0) + offset += fabs(dev->hw->offset.first); + + /* convert to lines */ + int bottom_offset = dpi * offset / MM_PER_INCH; + bottom += bottom_offset; + DBG (3, "send_tune_scan_length: bottom offset: %d\n", bottom_offset); + } + set_double (payload.vertical, bottom); /*set_double (payload.horizontal, 0); */ @@ -4973,7 +5479,7 @@ normal_calibration (Avision_Scanner* s) return status; /* check if need do calibration */ - if (calib_format.flags != 1) { + if (calib_format.flags != 1 && !(s->hw->hw->feature_type & AV_FORCE_CALIB)) { DBG (1, "normal_calibration: Scanner claims no calibration needed -> skipped!\n"); return SANE_STATUS_GOOD; } @@ -4990,7 +5496,7 @@ normal_calibration (Avision_Scanner* s) return SANE_STATUS_NO_MEM; /* check if we need to do dark calibration (shading) */ - if (BIT(calib_format.ability1, 3)) + if (BIT(calib_format.ability1, 2)) { DBG (1, "normal_calibration: reading dark data\n"); /* read dark calib data */ @@ -5260,11 +5766,24 @@ send_gamma (Avision_Scanner* s) v2 = 0; } - for (k = 0; k < gamma_values; ++ k, ++ i) { - gamma_data [i] = (uint8_t) - (((v1 * (gamma_values - k)) + (v2 * k) ) / (double) gamma_values); - } + if (s->hw->hw->feature_type & AV_GAMMA_UINT16) { + /* Use some pointer-cast magic to use gamma_data as uint16 array + and write as big-endian since values get swapped */ + ((uint16_t *)gamma_data) [i++] = (uint16_t)v1<<8; + } else { + /* interpolate gamma_values to gamma_data */ + for (k = 0; k < gamma_values; ++ k, ++ i) { + gamma_data [i] = (uint8_t) + (((v1 * (gamma_values - k)) + (v2 * k) ) / (double) gamma_values); + } + } + } + + /* with AV_GAMMA_UINT16 only every second value is filled, so double i */ + if (s->hw->hw->feature_type & AV_GAMMA_UINT16) + i *= 2; + /* fill the gamma table - (e.g.) if 11bit (old protocol) table */ { size_t t_i = i-1; @@ -5597,8 +6116,10 @@ set_window (Avision_Scanner* s) paralen += sizeof (cmd.window.avision.type.fujitsu); else if (!dev->inquiry_new_protocol) paralen += sizeof (cmd.window.avision.type.old); - else + else if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) paralen += sizeof (cmd.window.avision.type.normal); + else + paralen += sizeof (cmd.window.avision.type.normal) - 1; DBG (2, "set_window: final paralen: %d\n", paralen); @@ -5624,7 +6145,7 @@ set_window (Avision_Scanner* s) set_quad (cmd.window.descriptor.width, s->avdimen.hw_pixels_per_line * base_dpi_rel / s->avdimen.hw_xres + 1); - line_count = s->avdimen.hw_lines + 2 * s->avdimen.line_difference + s->avdimen.rear_offset; + line_count = s->avdimen.hw_lines + 2 * s->avdimen.line_difference; set_quad (cmd.window.descriptor.length, line_count * base_dpi_rel / s->avdimen.hw_yres + 1); @@ -5667,6 +6188,13 @@ set_window (Avision_Scanner* s) DBG (3, "set_window: filling ADF bits\n"); SET_BIT (cmd.window.avision.bitset1, 7); + if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) { + /* Always set bit 7 to enable single_sheet_scan option (defaults to off). + This removes the 1s pause between two sheets and fixes some offsets. */ + SET_BIT(cmd.window.avision.type.normal.bitset3, 7); + cmd.window.avision.type.normal.single_sheet_scan = 0; + } + /* normal, interlaced duplex scanners */ if (dev->inquiry_duplex_interlaced) { DBG (3, "set_window: interlaced duplex type\n"); @@ -5694,12 +6222,14 @@ set_window (Avision_Scanner* s) if ( !(dev->hw->feature_type & AV_FUJITSU) ) { /* quality scan option switch */ - if (s->val[OPT_QSCAN].w == SANE_TRUE) { + if (s->val[OPT_QSCAN].w == SANE_TRUE && + !(dev->hw->feature_type & AV_NO_QSCAN_MODE)) { SET_BIT (cmd.window.avision.type.normal.bitset2, 4); } /* quality calibration option switch (inverted! if set == speed) */ - if (s->val[OPT_QCALIB].w == SANE_FALSE) { + if (s->val[OPT_QCALIB].w == SANE_FALSE && + !(dev->hw->feature_type & AV_NO_QCALIB_MODE)) { SET_BIT (cmd.window.avision.type.normal.bitset2, 3); } @@ -6172,7 +6702,8 @@ start_scan (Avision_Scanner* s) SET_BIT(cmd.bitset1,6); } - if (s->val[OPT_QSCAN].w == SANE_TRUE) { + if (s->val[OPT_QSCAN].w == SANE_TRUE && + !(s->hw->hw->feature_type & AV_NO_QSCAN_MODE)) { SET_BIT(cmd.bitset1,7); } @@ -6216,12 +6747,19 @@ do_eof (Avision_Scanner *s) static SANE_Status do_cancel (Avision_Scanner* s) { + int status; + DBG (3, "do_cancel:\n"); s->prepared = s->scanning = SANE_FALSE; s->duplex_rear_valid = SANE_FALSE; s->page = 0; - s->cancelled = 1; + s->cancelled = SANE_TRUE; + + if (s->read_fds >= 0) { + close(s->read_fds); + s->read_fds = -1; + } if (sanei_thread_is_valid (s->reader_pid)) { int exit_status; @@ -6232,6 +6770,12 @@ do_cancel (Avision_Scanner* s) sanei_thread_invalidate (s->reader_pid); } + if (s->hw->hw->feature_type & AV_FASTFEED_ON_CANCEL) { + status = release_unit (s, 1); + if (status != SANE_STATUS_GOOD) + DBG (1, "do_cancel: release_unit failed\n"); + } + return SANE_STATUS_CANCELLED; } @@ -6496,6 +7040,8 @@ init_options (Avision_Scanner* s) s->opt[OPT_QSCAN].type = SANE_TYPE_BOOL; s->opt[OPT_QSCAN].unit = SANE_UNIT_NONE; s->val[OPT_QSCAN].w = SANE_TRUE; + if (dev->hw->feature_type & AV_NO_QSCAN_MODE) + s->opt[OPT_QSCAN].cap |= SANE_CAP_INACTIVE; /* Quality Calibration */ s->opt[OPT_QCALIB].name = SANE_NAME_QUALITY_CAL; @@ -6504,6 +7050,8 @@ init_options (Avision_Scanner* s) s->opt[OPT_QCALIB].type = SANE_TYPE_BOOL; s->opt[OPT_QCALIB].unit = SANE_UNIT_NONE; s->val[OPT_QCALIB].w = SANE_TRUE; + if (dev->hw->feature_type & AV_NO_QCALIB_MODE) + s->opt[OPT_QCALIB].cap |= SANE_CAP_INACTIVE; /* gray scale gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; @@ -6716,8 +7264,10 @@ reader_process (void *data) sigset_t sigterm_set; sigset_t ignore_set; struct SIGACTION act; + int old; FILE* fp; + FILE* fp_fd = 0; /* for ADF bottom offset truncating */ FILE* rear_fp = 0; /* used to store the deinterlaced rear data */ FILE* raw_fp = 0; /* used to write the RAW image data for debugging */ @@ -6757,21 +7307,30 @@ reader_process (void *data) DBG (3, "reader_process:\n"); - if (sanei_thread_is_forked()) + if (sanei_thread_is_forked()) { close (s->read_fds); + s->read_fds = -1; - sigfillset (&ignore_set); - sigdelset (&ignore_set, SIGTERM); + sigfillset (&ignore_set); + sigdelset (&ignore_set, SIGTERM); #if defined (__APPLE__) && defined (__MACH__) - sigdelset (&ignore_set, SIGUSR2); + sigdelset (&ignore_set, SIGUSR2); #endif - sigprocmask (SIG_SETMASK, &ignore_set, 0); + sigprocmask (SIG_SETMASK, &ignore_set, 0); - memset (&act, 0, sizeof (act)); - sigaction (SIGTERM, &act, 0); + memset (&act, 0, sizeof (act)); + sigaction (SIGTERM, &act, 0); - sigemptyset (&sigterm_set); - sigaddset (&sigterm_set, SIGTERM); + sigemptyset (&sigterm_set); + sigaddset (&sigterm_set, SIGTERM); + } +#ifdef USE_PTHREAD + else { + int old; + pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &old); + pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, &old); + } +#endif gray_mode = color_mode_is_shaded (s->c_mode); @@ -6792,6 +7351,16 @@ reader_process (void *data) if (!fp) return SANE_STATUS_NO_MEM; + if (dev->adf_offset_compensation) { + DBG (3, "reader_process: redirecting output data to temp file for ADF offset compensation.\n"); + fp_fd = fp; + fp = fopen (s->duplex_offtmp_fname, "w+"); + if (!fp) { + fclose(fp_fd); + return SANE_STATUS_NO_MEM; + } + } + /* start scan ? */ if ((deinterlace == NONE && !((dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && s->duplex_rear_valid)) || (deinterlace != NONE && !s->duplex_rear_valid)) @@ -6897,9 +7466,9 @@ reader_process (void *data) } /* calculate params for the reading loop */ - total_size = s->avdimen.hw_bytes_per_line * (s->avdimen.hw_lines + - 2 * s->avdimen.line_difference + - s->avdimen.rear_offset); + total_size = s->avdimen.hw_bytes_per_line * + (s->avdimen.hw_lines + 2 * s->avdimen.line_difference); + if (deinterlace != NONE && !s->duplex_rear_valid) total_size *= 2; DBG (3, "reader_process: total_size: %lu\n", (u_long) total_size); @@ -6958,9 +7527,22 @@ reader_process (void *data) (u_long) processed_bytes, (u_long) total_size); DBG (5, "reader_process: this_read: %lu\n", (u_long) this_read); - sigprocmask (SIG_BLOCK, &sigterm_set, 0); + if (sanei_thread_is_forked()) + sigprocmask (SIG_BLOCK, &sigterm_set, 0); +#ifdef USE_PTHREAD + else + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old); +#endif + status = read_data (s, stripe_data + stripe_fill, &this_read); - sigprocmask (SIG_UNBLOCK, &sigterm_set, 0); + + if (sanei_thread_is_forked()) + sigprocmask (SIG_UNBLOCK, &sigterm_set, 0); +#ifdef USE_PTHREAD + else + pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &old); +#endif + /* only EOF on the second stripe, as otherwise the rear page is shorter */ @@ -7255,24 +7837,7 @@ reader_process (void *data) if (s->avdimen.hw_xres == s->avdimen.xres && s->avdimen.hw_yres == s->avdimen.yres) /* No scaling */ { - int lines, _hw_line = hw_line; - uint8_t* src = out_data; - /* we support cropping at the beginning and end due to rear offset */ - for (lines = useful_bytes / s->avdimen.hw_bytes_per_line; - lines > 0; --lines, ++_hw_line, src += s->avdimen.hw_bytes_per_line) - { - if (deinterlace != NONE) { - /* crop rear offset :-( */ - if ( (!s->duplex_rear_valid && _hw_line >= s->avdimen.hw_lines) || - (s->duplex_rear_valid && _hw_line < s->avdimen.rear_offset) ) - { - DBG (7, "reader_process: skip due read offset line: %d\n", line); - continue; - } - } - fwrite (src, s->avdimen.hw_bytes_per_line, 1, fp); - ++line; - } + fwrite (out_data, useful_bytes, 1, fp); } else /* Software scaling - watch out - this code bites back! */ { @@ -7296,22 +7861,11 @@ reader_process (void *data) unsigned int v; /* accumulator */ /* Break out if we do not have the hw source line - yet, - or when we are past the end of wanted data (e.g. on the - front page due to rear_offset). Also take the read_offset - into account on the rear side */ - if (deinterlace != NONE) { - if (!s->duplex_rear_valid && syy >= s->avdimen.hw_lines) { - DBG (7, "reader_process: skip due past intended front page lines: %d\n", sy); - break; - } - else if (s->duplex_rear_valid) { - /* the beginning is to be skipped, accessed thru offset */ - DBG (7, "reader_process: rear_offset adjusting source: %d\n", sy); - sy += s->avdimen.rear_offset; - syy += s->avdimen.rear_offset; - } + or when we are past the end of wanted data */ + if (deinterlace != NONE && !s->duplex_rear_valid && syy >= s->avdimen.hw_lines) { + DBG (7, "reader_process: skip due past intended front page lines: %d\n", sy); + break; } - if (sy >= hw_line_end || syy >= hw_line_end) { DBG (3, "reader_process: source line %d-%d not yet avail\n", sy, syy); @@ -7466,6 +8020,35 @@ reader_process (void *data) } } + /* ADF offset compensation */ + if (dev->adf_offset_compensation) { + + long lines; + uint8_t* buffer; + + buffer = malloc (s->params.bytes_per_line); + lines = ftell(fp) / s->params.bytes_per_line; + rewind(fp); + + for (long line = 0; line < lines; line++) { + fread(buffer, s->params.bytes_per_line, 1, fp); + + if ( (!s->duplex_rear_valid && (line < s->avdimen.offset.front.top)) || + (s->duplex_rear_valid && (line < s->avdimen.offset.rear.top)) ) { + DBG (7, "reader_process: skip due read offset line: %ld\n", line); + continue; + } + + if ( (!s->duplex_rear_valid && (line > (lines - s->avdimen.offset.front.bottom))) || + (s->duplex_rear_valid && (line > (lines - s->avdimen.offset.rear.bottom))) ) { + DBG (7, "reader_process: skip due read offset line: %ld to %ld\n", line, lines); + break; /* nothing more to write, so break out here */ + } + + fwrite(buffer, s->params.bytes_per_line, 1, fp_fd); + } + } + /* Eject film holder and/or release_unit - but only for non-duplex-rear / non-virtual scans. */ if ((deinterlace != NONE && s->duplex_rear_valid) || @@ -7527,6 +8110,9 @@ reader_process (void *data) if (rear_fp) fclose (rear_fp); + if (fp_fd) + fclose(fp_fd); + if (ip_data) free (ip_data); if (ip_history) free (ip_history); @@ -7859,9 +8445,17 @@ sane_open (SANE_String_Const devicename, SANE_Handle *handle) http://www.poynton.com/GammaFAQ.html - Avision's driver defaults to 2.2 though. */ + Avision's driver defaults to 2.2 though. + + MN: This is not true for at least Kodak i1120's windows driver. + Some real-world testing showed that a gamma of 1.0 is needed + for this scanner to give decent scan results. Add an option for this... + */ + { - const double gamma = 2.22; + double gamma = 2.22; + if (s->hw->hw->feature_type & AV_GAMMA_10) + gamma = 1.0; const double one_over_gamma = 1. / gamma; for (i = 0; i < 4; ++ i) @@ -7915,6 +8509,29 @@ sane_open (SANE_String_Const devicename, SANE_Handle *handle) /* initialize the options */ init_options (s); + if (dev->inquiry_duplex_interlaced && + (dev->hw->offset.first != 0 || + dev->hw->offset.front.top != 0 || + dev->hw->offset.front.bottom != 0 || + dev->hw->offset.duplex.front.top != 0 || + dev->hw->offset.duplex.front.bottom != 0 || + dev->hw->offset.duplex.rear.top != 0 || + dev->hw->offset.duplex.rear.bottom != 0) ) + dev->adf_offset_compensation = SANE_TRUE; + + if (dev->adf_offset_compensation) { + strncpy(s->duplex_offtmp_fname, "/tmp/avision-offtmp-XXXXXX", PATH_MAX); + + if (! mktemp(s->duplex_offtmp_fname) ) { + DBG (1, "sane_open: failed to generate temporary fname for ADF offset compensation temp file\n"); + return SANE_STATUS_NO_MEM; + } + else { + DBG (1, "sane_open: temporary fname for ADF offset compensation temp file: %s\n", + s->duplex_offtmp_fname); + } + } + if (dev->inquiry_duplex_interlaced || dev->scanner_type == AV_FILM || dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) { /* Might need at least *DOS (Windows flavour and OS/2) portability fix @@ -8026,6 +8643,11 @@ sane_close (SANE_Handle handle) *(s->duplex_rear_fname) = 0; } + if (*(s->duplex_offtmp_fname)) { + unlink (s->duplex_offtmp_fname); + *(s->duplex_offtmp_fname) = 0; + } + free (handle); } @@ -8332,7 +8954,7 @@ sane_start (SANE_Handle handle) return SANE_STATUS_DEVICE_BUSY; /* Clear cancellation status */ - s->cancelled = 0; + s->cancelled = SANE_FALSE; /* Make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ @@ -8560,8 +9182,10 @@ sane_start (SANE_Handle handle) DBG (3, "sane_start: starting thread\n"); s->reader_pid = sanei_thread_begin (reader_process, (void *) s); - if (sanei_thread_is_forked()) - close (s->write_fds); + if (sanei_thread_is_forked()) { + close (s->write_fds); + s->write_fds = -1; + } return SANE_STATUS_GOOD; diff --git a/backend/avision.h b/backend/avision.h index 58552c0..9017bf2 100644 --- a/backend/avision.h +++ b/backend/avision.h @@ -81,6 +81,12 @@ typedef struct Avision_Connection { } Avision_Connection; +/* structure for ADF offsets in mm */ +typedef struct mm_offset { + double top; + double bottom; +} mm_offset; + typedef struct Avision_HWEntry { const char* scsi_mfg; const char* scsi_model; @@ -184,9 +190,6 @@ typedef struct Avision_HWEntry { /* does the scanner contain a Cancel button? */ #define AV_CANCEL_BUTTON ((uint64_t)1<<28) - /* is the rear image offset? */ - #define AV_REAR_OFFSET ((uint64_t)1<<29) - /* some devices do not need a START_SCAN, even hang with it */ #define AV_NO_START_SCAN ((uint64_t)1<<30) @@ -204,9 +207,46 @@ typedef struct Avision_HWEntry { /* For scanners which need to have their firmware read to properly function. */ #define AV_FIRMWARE ((uint64_t)1<<35) + /* at least Kodak i1120 claims no calibration needed but windows driver does it anyways */ + #define AV_FORCE_CALIB ((uint64_t)1<<36) + + /* at least Kodak i1120 does not have an explicit "quality-scan" mode */ + #define AV_NO_QSCAN_MODE ((uint64_t)1<<37) + + /* at least Kodak i1120 optical DPI is used for overscan calculation */ + #define AV_OVERSCAN_OPTDPI ((uint64_t)1<<38) + + /* some scanners support fast feed-out of the sheet when cancelling a running scan */ + #define AV_FASTFEED_ON_CANCEL ((uint64_t)1<<39) + + /* at least Kodak i1120 does not have an explicit "quality-calibration" mode */ + #define AV_NO_QCALIB_MODE ((uint64_t)1<<40) + + /* Kodak i1120 needs gamma = 1.0 to give decent results */ + #define AV_GAMMA_10 ((uint64_t)1<<41) + + /* Kodak i1120 has a different gamma table format (like a uint16/double array) */ + #define AV_GAMMA_UINT16 ((uint64_t)1<<42) + + /* Kodak i1120 has single-sheet and multi-sheet scan modes. This option sets + bitset3[7] which enables multi-sheet scan by default so there is no pause + of 1s between two sheets in ADF scan mode. This also fixes some offsets + when scanning multiple sheets. */ + #define AV_MULTI_SHEET_SCAN ((uint64_t)1<<43) + /* maybe more ...*/ uint64_t feature_type; + /* ADF offsets in mm */ + struct { + float first; /* offset difference first sheet */ + mm_offset front; /* front-only */ + struct { + mm_offset front; + mm_offset rear; + } duplex; + } offset; + } Avision_HWEntry; typedef enum { @@ -301,6 +341,13 @@ enum Avision_Option NUM_OPTIONS /* must come last */ }; +/* structure for ADF offsets in pixels of HW res */ +typedef struct hwpx_offset { + int top; + int bottom; +} hwpx_offset; + + typedef struct Avision_Dimensions { /* in dpi */ @@ -315,7 +362,11 @@ typedef struct Avision_Dimensions /* in pixels */ int line_difference; - int rear_offset; /* in pixels of HW res */ + + struct { + hwpx_offset front; + hwpx_offset rear; + } offset; /* interlaced duplex scan */ SANE_Bool interlaced_duplex; @@ -407,6 +458,8 @@ typedef struct Avision_Device int inquiry_bits_per_channel; int inquiry_no_gray_modes; + SANE_Bool adf_offset_compensation; + int scsi_buffer_size; /* nice to have SCSI buffer size */ int read_stripe_size; /* stripes to be read at-a-time */ @@ -451,6 +504,7 @@ typedef struct Avision_Scanner /* Internal data for duplex scans */ char duplex_rear_fname [PATH_MAX]; + char duplex_offtmp_fname [PATH_MAX]; SANE_Bool duplex_rear_valid; color_mode c_mode; @@ -670,6 +724,8 @@ typedef struct command_set_window_window uint8_t line_width_msb; uint8_t line_count_msb; uint8_t background_lines; + + uint8_t single_sheet_scan; /* from Kodak SVT tool */ } normal; struct { diff --git a/backend/canon_dr.c b/backend/canon_dr.c index f6cd5d4..17ee7b6 100644 --- a/backend/canon_dr.c +++ b/backend/canon_dr.c @@ -3951,10 +3951,12 @@ get_pixelsize(struct scanner *s) in, &inLen ); - if(ret == SANE_STATUS_GOOD && - get_R_PSIZE_width(in) > 0 && - get_R_PSIZE_length(in) > 0){ + if(ret != SANE_STATUS_GOOD){ + DBG (10, "get_pixelsize: error reading, status = %d\n", ret); + break; + } + if(get_R_PSIZE_width(in) > 0 && get_R_PSIZE_length(in) > 0){ DBG (15, "get_pixelsize: w:%d h:%d\n", get_R_PSIZE_width(in) * s->u.dpi_x / 1200, get_R_PSIZE_length(in) * s->u.dpi_y / 1200); diff --git a/backend/canon_lide70-common.c b/backend/canon_lide70-common.c new file mode 100644 index 0000000..a0eb5c0 --- /dev/null +++ b/backend/canon_lide70-common.c @@ -0,0 +1,3023 @@ +/* sane - Scanner Access Now Easy. + + BACKEND canon_lide70 + + Copyright (C) 2019 Juergen Ernst and pimvantend. + + 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. + + This file implements a SANE backend for the Canon CanoScan LiDE 70 */ + +#include <errno.h> +#include <fcntl.h> /* open */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> /* usleep */ +#include <time.h> +#include <math.h> /* exp() */ +#ifdef HAVE_OS2_H +#include <sys/types.h> /* mode_t */ +#endif +#include <sys/stat.h> + +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_RECIP_DEVICE 0x00 +#define USB_DIR_OUT 0x00 +#define USB_DIR_IN 0x80 + +#define MSEC 1000 /* 1ms = 1000us */ + +/* Assign status and verify a good return code */ +#define CHK(A) {if ((status = A) != SANE_STATUS_GOOD) {\ + DBG (1, "Failure on line of %s: %d\n", \ + __FILE__, __LINE__ ); return A; }} + +typedef SANE_Byte byte; + +/***************************************************** + Canon LiDE70 calibration and scan +******************************************************/ + +/* at 600 dpi */ +#define CANON_MAX_WIDTH 5104 /* 8.5in */ +/* this may not be right */ +#define CANON_MAX_HEIGHT 7300 /* 11.66in */ +/* Just for my scanner, or is this universal? Calibrate? */ + +/* data structures and constants */ +typedef struct CANON_Handle +{ + /* options */ + SANE_Option_Descriptor opt[num_options]; + Option_Value val[num_options]; + SANE_Parameters params; + + SANE_Word graymode; + char *product; /* product name */ + int fd; /* scanner fd */ + int x1, x2, y1, y2; /* in pixels, at 600 dpi */ + long width, height; /* at scan resolution */ + unsigned char value_08, value_09; /* left */ + unsigned char value_0a, value_0b; /* right */ + unsigned char value_67, value_68; /* bottom */ + unsigned char value_51; /* lamp colors */ + int resolution; /* dpi */ + char *fname; /* output file name */ + FILE *fp; /* output file pointer (for reading) */ + unsigned char absolute_threshold; +} +CANON_Handle; + + +static byte cmd_buffer[] = { 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, 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, 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, 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, + 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, 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, 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, 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, + 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, 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, 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, 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, + 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, 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, 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, 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, + 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/***************************************************** + CP2155 communication primitives + Provides I/O routines to Philips CP2155BE chip +******************************************************/ + +typedef int CP2155_Register; + +/* Write single byte to CP2155 register */ +static SANE_Status +cp2155_set (int fd, CP2155_Register reg, byte data) +{ + SANE_Status status; + size_t count; + + cmd_buffer[0] = (reg >> 8) & 0xff; + cmd_buffer[1] = (reg) & 0xff; + cmd_buffer[2] = 0x01; + cmd_buffer[3] = 0x00; + cmd_buffer[4] = data; + + count = 5; + status = sanei_usb_write_bulk (fd, cmd_buffer, &count); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "cp2155_set: sanei_usb_write_bulk error\n"); + } + + return status; +} + +/* Read single byte from CP2155 register */ +static SANE_Status +cp2155_get (int fd, CP2155_Register reg, byte * data) +{ + SANE_Status status; + size_t count; + + cmd_buffer[0] = 0x01; + cmd_buffer[1] = (reg) & 0xff; + cmd_buffer[2] = 0x01; + cmd_buffer[3] = 0x00; + + count = 4; + status = sanei_usb_write_bulk (fd, cmd_buffer, &count); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "cp2155_get: sanei_usb_write_bulk error\n"); + return status; + } + + usleep (1 * MSEC); + + count = 1; + status = sanei_usb_read_bulk (fd, data, &count); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "cp2155_get: sanei_usb_read_bulk error\n"); + } + + return status; +} + +/* Write a block of data to CP2155 chip */ +static SANE_Status +cp2155_write (int fd, byte * data, size_t size) +{ + SANE_Status status; + size_t count = size + 4; + + cmd_buffer[0] = 0x04; + cmd_buffer[1] = 0x70; + cmd_buffer[2] = (size) & 0xff; + cmd_buffer[3] = (size >> 8) & 0xff; + memcpy (cmd_buffer + 4, data, size); + + status = sanei_usb_write_bulk (fd, cmd_buffer, &count); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "cp2155_write: sanei_usb_write_bulk error\n"); + } + + return status; +} + +/* Read a block of data from CP2155 chip */ +static SANE_Status +cp2155_read (int fd, byte * data, size_t size) +{ + SANE_Status status; + size_t count; + + cmd_buffer[0] = 0x05; + cmd_buffer[1] = 0x70; + cmd_buffer[2] = (size) & 0xff; + cmd_buffer[3] = (size >> 8) & 0xff; + + count = 4; + status = sanei_usb_write_bulk (fd, cmd_buffer, &count); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "cp2155_read: sanei_usb_write_bulk error\n"); + return status; + } + + usleep (1 * MSEC); + + count = size; + status = sanei_usb_read_bulk (fd, data, &count); +/* + if (status != SANE_STATUS_GOOD) + { + DBG (1, "cp2155_read: sanei_usb_read_bulk error %lu\n", (u_long) count); + } +*/ + return status; +} + +/*****************************************************/ + +static void +cp2155_block1 (int fd, byte v001, unsigned int addr, byte * data, size_t size) +{ + size_t count = size; + + while ((count & 0x0f) != 0) + { + count++; + } + + byte pgLO = (count) & 0xff; + byte pgHI = (count >> 8) & 0xff; +/* + DBG (1, "cp2155_block1 %06x %02x %04lx %04lx\n", addr, v001, (u_long) size, + (u_long) count); +*/ + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, v001); + cp2155_set (fd, 0x72, pgHI); + cp2155_set (fd, 0x73, pgLO); + cp2155_set (fd, 0x74, (addr >> 16) & 0xff); + cp2155_set (fd, 0x75, (addr >> 8) & 0xff); + cp2155_set (fd, 0x76, (addr) & 0xff); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + cp2155_write (fd, data, count); +} + +/* size=0x0100 */ +/* gamma table red*/ +static byte cp2155_gamma_red_data[] = { + 0x00, 0x14, 0x1c, 0x26, 0x2a, 0x2e, 0x34, 0x37, 0x3a, 0x3f, 0x42, 0x44, + 0x48, 0x4a, 0x4c, 0x50, + 0x52, 0x53, 0x57, 0x58, 0x5c, 0x5d, 0x5f, 0x62, 0x63, 0x64, 0x67, 0x68, + 0x6a, 0x6c, 0x6e, 0x6f, + 0x71, 0x72, 0x74, 0x76, 0x77, 0x78, 0x7a, 0x7c, 0x7e, 0x7f, 0x80, 0x82, + 0x83, 0x84, 0x86, 0x87, + 0x88, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x91, 0x92, 0x93, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9b, + 0x9b, 0x9c, 0x9e, 0x9f, 0x9f, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, + 0xb8, 0xb8, 0xb9, 0xba, + 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xc9, 0xca, 0xcb, 0xcc, 0xcc, 0xce, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, + 0xd2, 0xd3, 0xd4, 0xd5, + 0xd5, 0xd6, 0xd7, 0xd7, 0xd9, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xdd, + 0xdf, 0xdf, 0xe0, 0xe1, + 0xe1, 0xe2, 0xe3, 0xe3, 0xe4, 0xe5, 0xe5, 0xe6, 0xe7, 0xe7, 0xe8, 0xe9, + 0xe9, 0xea, 0xeb, 0xeb, + 0xec, 0xed, 0xed, 0xee, 0xef, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf3, + 0xf4, 0xf5, 0xf5, 0xf6, + 0xf7, 0xf7, 0xf8, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, + 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +/* size=0x0100 */ +/* gamma table */ +static byte cp2155_gamma_greenblue_data[] = { + 0x00, 0x14, 0x1c, 0x21, 0x26, 0x2a, 0x2e, 0x31, 0x34, 0x37, 0x3a, 0x3d, + 0x3f, 0x42, 0x44, 0x46, + 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x53, 0x55, 0x57, 0x58, 0x5a, 0x5c, + 0x5d, 0x5f, 0x60, 0x62, + 0x63, 0x64, 0x66, 0x67, 0x68, 0x6a, 0x6b, 0x6c, 0x6e, 0x6f, 0x70, 0x71, + 0x72, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, + 0x93, 0x94, 0x95, 0x96, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, + 0xac, 0xad, 0xae, 0xaf, + 0xaf, 0xb0, 0xb1, 0xb1, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, + 0xb8, 0xb8, 0xb9, 0xba, + 0xba, 0xbb, 0xbc, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc1, + 0xc2, 0xc3, 0xc3, 0xc4, + 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xcb, 0xcb, + 0xcc, 0xcc, 0xcd, 0xce, + 0xce, 0xcf, 0xcf, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd4, 0xd5, + 0xd5, 0xd6, 0xd6, 0xd7, + 0xd7, 0xd8, 0xd9, 0xd9, 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, + 0xde, 0xdf, 0xdf, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe6, + 0xe6, 0xe7, 0xe7, 0xe8, + 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xec, 0xed, 0xed, 0xee, + 0xee, 0xef, 0xef, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, + 0xf6, 0xf7, 0xf7, 0xf8, + 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, + 0xfe, 0xfe, 0xff, 0xff +}; + +/* size=0x01f4 */ +static byte cp2155_slope09_back[] = { + 0x80, 0x25, 0x00, 0x25, 0x84, 0x24, 0x0b, 0x24, 0x96, 0x23, 0x23, 0x23, + 0xb3, 0x22, 0x46, 0x22, + 0xdb, 0x21, 0x73, 0x21, 0x0e, 0x21, 0xab, 0x20, 0x4a, 0x20, 0xeb, 0x1f, + 0x8f, 0x1f, 0x34, 0x1f, + 0xdc, 0x1e, 0x85, 0x1e, 0x31, 0x1e, 0xde, 0x1d, 0x8d, 0x1d, 0x3e, 0x1d, + 0xf0, 0x1c, 0xa4, 0x1c, + 0x59, 0x1c, 0x10, 0x1c, 0xc9, 0x1b, 0x83, 0x1b, 0x3e, 0x1b, 0xfa, 0x1a, + 0xb8, 0x1a, 0x77, 0x1a, + 0x38, 0x1a, 0xf9, 0x19, 0xbc, 0x19, 0x80, 0x19, 0x44, 0x19, 0x0a, 0x19, + 0xd1, 0x18, 0x99, 0x18, + 0x62, 0x18, 0x2c, 0x18, 0xf7, 0x17, 0xc3, 0x17, 0x8f, 0x17, 0x5d, 0x17, + 0x2b, 0x17, 0xfa, 0x16, + 0xca, 0x16, 0x9b, 0x16, 0x6c, 0x16, 0x3e, 0x16, 0x11, 0x16, 0xe5, 0x15, + 0xb9, 0x15, 0x8e, 0x15, + 0x64, 0x15, 0x3a, 0x15, 0x11, 0x15, 0xe9, 0x14, 0xc1, 0x14, 0x9a, 0x14, + 0x73, 0x14, 0x4d, 0x14, + 0x27, 0x14, 0x02, 0x14, 0xde, 0x13, 0xba, 0x13, 0x96, 0x13, 0x74, 0x13, + 0x51, 0x13, 0x2f, 0x13, + 0x0d, 0x13, 0xec, 0x12, 0xcc, 0x12, 0xab, 0x12, 0x8c, 0x12, 0x6c, 0x12, + 0x4d, 0x12, 0x2f, 0x12, + 0x11, 0x12, 0xf3, 0x11, 0xd5, 0x11, 0xb8, 0x11, 0x9c, 0x11, 0x80, 0x11, + 0x64, 0x11, 0x48, 0x11, + 0x2d, 0x11, 0x12, 0x11, 0xf7, 0x10, 0xdd, 0x10, 0xc3, 0x10, 0xa9, 0x10, + 0x90, 0x10, 0x77, 0x10, + 0x5e, 0x10, 0x46, 0x10, 0x2e, 0x10, 0x16, 0x10, 0xfe, 0x0f, 0xe7, 0x0f, + 0xd0, 0x0f, 0xb9, 0x0f, + 0xa2, 0x0f, 0x8c, 0x0f, 0x76, 0x0f, 0x60, 0x0f, 0x4b, 0x0f, 0x35, 0x0f, + 0x20, 0x0f, 0x0b, 0x0f, + 0xf7, 0x0e, 0xe2, 0x0e, 0xce, 0x0e, 0xba, 0x0e, 0xa6, 0x0e, 0x92, 0x0e, + 0x7f, 0x0e, 0x6c, 0x0e, + 0x59, 0x0e, 0x46, 0x0e, 0x33, 0x0e, 0x21, 0x0e, 0x0f, 0x0e, 0xfd, 0x0d, + 0xeb, 0x0d, 0xd9, 0x0d, + 0xc8, 0x0d, 0xb6, 0x0d, 0xa5, 0x0d, 0x94, 0x0d, 0x83, 0x0d, 0x73, 0x0d, + 0x62, 0x0d, 0x52, 0x0d, + 0x41, 0x0d, 0x31, 0x0d, 0x22, 0x0d, 0x12, 0x0d, 0x02, 0x0d, 0xf3, 0x0c, + 0xe3, 0x0c, 0xd4, 0x0c, + 0xc5, 0x0c, 0xb6, 0x0c, 0xa7, 0x0c, 0x99, 0x0c, 0x8a, 0x0c, 0x7c, 0x0c, + 0x6e, 0x0c, 0x60, 0x0c, + 0x52, 0x0c, 0x44, 0x0c, 0x36, 0x0c, 0x28, 0x0c, 0x1b, 0x0c, 0x0d, 0x0c, + 0x00, 0x0c, 0xf3, 0x0b, + 0xe6, 0x0b, 0xd9, 0x0b, 0xcc, 0x0b, 0xbf, 0x0b, 0xb3, 0x0b, 0xa6, 0x0b, + 0x9a, 0x0b, 0x8e, 0x0b, + 0x81, 0x0b, 0x75, 0x0b, 0x69, 0x0b, 0x5d, 0x0b, 0x52, 0x0b, 0x46, 0x0b, + 0x3a, 0x0b, 0x2f, 0x0b, + 0x23, 0x0b, 0x18, 0x0b, 0x0d, 0x0b, 0x02, 0x0b, 0xf6, 0x0a, 0xeb, 0x0a, + 0xe1, 0x0a, 0xd6, 0x0a, + 0xcb, 0x0a, 0xc0, 0x0a, 0xb6, 0x0a, 0xab, 0x0a, 0xa1, 0x0a, 0x97, 0x0a, + 0x8c, 0x0a, 0x82, 0x0a, + 0x78, 0x0a, 0x6e, 0x0a, 0x64, 0x0a, 0x5a, 0x0a, 0x50, 0x0a, 0x47, 0x0a, + 0x3d, 0x0a, 0x33, 0x0a, + 0x2a, 0x0a, 0x20, 0x0a, 0x17, 0x0a, 0x0e, 0x0a, 0x04, 0x0a, 0xfb, 0x09, + 0xf2, 0x09, 0xe9, 0x09, + 0xe0, 0x09, 0xd7, 0x09, 0xce, 0x09, 0xc6, 0x09, 0xbd, 0x09, 0xb4, 0x09, + 0xab, 0x09, 0xa3, 0x09, + 0x9a, 0x09, 0x92, 0x09, 0x8a, 0x09, 0x81, 0x09, 0x79, 0x09, 0x71, 0x09, + 0x69, 0x09, 0x61, 0x09, + 0x59, 0x09, 0x51, 0x09, 0x49, 0x09, 0x41, 0x09, 0x39, 0x09, 0x31, 0x09, + 0x29, 0x09, 0x22, 0x09, + 0x1a, 0x09, 0x12, 0x09, 0x0b, 0x09, 0x03, 0x09, 0xfc, 0x08, 0xf5, 0x08, + 0xed, 0x08, 0xe6, 0x08, + 0xdf, 0x08, 0xd8, 0x08, 0xd0, 0x08, 0xc9, 0x08, 0xc2, 0x08, 0xbb, 0x08, + 0xb4, 0x08, 0xad, 0x08, + 0xa6, 0x08, 0xa0, 0x08 +}; + +/* size=0x0018 */ +static byte cp2155_slope10_back[] = { + 0x80, 0x25, 0xc0, 0x1c, 0x4f, 0x17, 0x9a, 0x13, 0xe9, 0x10, 0xde, 0x0e, + 0x44, 0x0d, 0xfa, 0x0b, + 0xea, 0x0a, 0x07, 0x0a, 0x46, 0x09, 0xa0, 0x08 +}; + +static void +cp2155_block2 (int fd, unsigned int addr) +{ + DBG (1, "cp2155_block2 %06x\n", addr); + cp2155_block1 (fd, 0x16, addr, cp2155_gamma_red_data, 0x0100); +} + +static void +cp2155_block3 (int fd, unsigned int addr) +{ + DBG (1, "cp2155_block3 %06x\n", addr); + cp2155_block1 (fd, 0x16, addr, cp2155_gamma_greenblue_data, 0x0100); +} + +static void +cp2155_set_slope (int fd, unsigned int addr, byte * data, size_t size) +{ +/* + DBG (1, "cp2155_set_slope %06x %04lx\n", addr, (u_long) size); +*/ + cp2155_block1 (fd, 0x14, addr, data, size); +} + +/* size=0x0075 */ +static byte cp2155_set_regs_data6[] = { + 0x00, 0x00, 0x00, 0x69, 0x00, 0xe8, 0x1d, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x2e, 0x00, 0x04, + 0x04, 0xf8, 0x07, 0x32, 0x32, 0x32, 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x02, + 0x00, 0x03, 0x15, 0x15, 0x15, 0x15, 0x04, 0x07, 0x29, 0x29, 0x09, 0x09, + 0x02, 0x06, 0x12, 0x12, + 0x03, 0x05, 0x05, 0x03, 0x05, 0x41, 0x61, 0x21, 0x21, 0x25, 0x25, 0x25, + 0x40, 0x40, 0x40, 0x06, + 0x40, 0x06, 0x00, 0x36, 0xd0, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x83, + 0x7c, 0x02, 0x1c, 0x9c, + 0x38, 0x28, 0x28, 0x27, 0x27, 0x25, 0x25, 0x21, 0x21, 0x1c, 0x1c, 0x16, + 0x16, 0x0f, 0x0f, 0x08, + 0x08, 0x00, 0x00, 0x08, 0x08, 0x0f, 0x0f, 0x16, 0x16, 0x1c, 0x1c, 0x21, + 0x21, 0x25, 0x25, 0x27, + 0x27, 0x02, 0x02, 0x22, 0x00 +}; + +/* size=0x0075 */ +static byte cp2155_set_regs_nr[] = { + 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xa0, 0xa1, 0xa2, 0xa3, 0x64, 0x65, + 0x61, 0x62, 0x63, 0x50, + 0x50, 0x90, 0x51, 0x5a, 0x5b, 0x5c, 0x5d, 0x52, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5e, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, 0x50, 0x51, 0x81, 0x81, 0x82, 0x82, + 0x83, 0x84, 0x80, 0x80, + 0xb0, 0x10, 0x10, 0x9b, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x12, 0x13, 0x16, 0x21, + 0x22, 0x20, 0x1d, 0x1e, 0x1f, 0x66, 0x67, 0x68, 0x1a, 0x1b, 0x1c, 0x15, + 0x14, 0x17, 0x43, 0x44, + 0x45, 0x23, 0x33, 0x24, 0x34, 0x25, 0x35, 0x26, 0x36, 0x27, 0x37, 0x28, + 0x38, 0x29, 0x39, 0x2a, + 0x3a, 0x2b, 0x3b, 0x2c, 0x3c, 0x2d, 0x3d, 0x2e, 0x3e, 0x2f, 0x3f, 0x30, + 0x40, 0x31, 0x41, 0x32, + 0x42, 0xca, 0xca, 0xca, 0x18 +}; + +static void +cp2155_set_regs (int fd, byte * data) +{ + DBG (1, "cp2155_set_regs\n"); + int i; + + for (i = 0; i < 0x0075; i++) + { + if (cp2155_set_regs_nr[i] != 0x90) + { + cp2155_set (fd, cp2155_set_regs_nr[i], data[i]); + } + } +} + +static void +cp2155_block5 (int fd, byte v001) +{ + DBG (1, "cp2155_block5 %02x\n", v001); + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0xb0, v001); +} + +static void +cp2155_block6 (int fd, byte v001, byte v002) +{ + DBG (1, "cp2155_block6 %02x %02x\n", v001, v002); + cp2155_set (fd, 0x80, v001); + cp2155_set (fd, 0x11, v002); +} + +static void +cp2155_block8 (int fd) +{ + DBG (1, "cp2155_block8\n"); + cp2155_set (fd, 0x04, 0x0c); + cp2155_set (fd, 0x05, 0x00); + cp2155_set (fd, 0x06, 0x00); +} + +static void +cp2155_set_gamma (int fd) +{ + DBG (1, "cp2155_set_gamma\n"); +/* gamma tables */ + cp2155_block3 (fd, 0x000000); + cp2155_block3 (fd, 0x000100); + cp2155_block3 (fd, 0x000200); +} + +static void +cp2155_set_gamma600 (int fd) +{ + DBG (1, "cp2155_set_gamma\n"); +/* gamma tables */ + cp2155_block2 (fd, 0x000000); + cp2155_block3 (fd, 0x000100); + cp2155_block3 (fd, 0x000200); +} + +static void +cp2155_motor (int fd, byte v001, byte v002) +{ + DBG (1, "cp2155_motor %02x %02x\n", v001, v002); + cp2155_set (fd, 0x10, v001); + cp2155_set (fd, 0x11, v002); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x03, 0x01); /* starts motor */ +} + +void +make_buf (size_t count, unsigned char *buf) +{ + size_t i = 4; + int hiword = 62756; + int loword = 20918; + unsigned char hihi = (hiword >> 8) & 0xff; + unsigned char hilo = (hiword) & 0xff; + unsigned char lohi = (loword >> 8) & 0xff; + unsigned char lolo = (loword) & 0xff; + buf[0] = 0x04; + buf[1] = 0x70; + buf[2] = (count - 4) & 0xff; + buf[3] = ((count - 4) >> 8) & 0xff; + while (i < count) + { + buf[i] = hilo; + i++; + buf[i] = hihi; + i++; + buf[i] = lolo; + i++; + buf[i] = lohi; + i++; + } +} + +void +big_write (int fd, size_t count, unsigned char *buf) +{ + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x51); + cp2155_set (fd, 0x73, 0x70); + cp2155_set (fd, 0x74, 0x00); + cp2155_set (fd, 0x75, 0x00); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + make_buf (count, buf); + sanei_usb_write_bulk (fd, buf, &count); + + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x51); + cp2155_set (fd, 0x73, 0x70); + cp2155_set (fd, 0x74, 0x00); + cp2155_set (fd, 0x75, 0xb0); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + sanei_usb_write_bulk (fd, buf, &count); + + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x51); + cp2155_set (fd, 0x73, 0x70); + cp2155_set (fd, 0x74, 0x01); + cp2155_set (fd, 0x75, 0x60); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + sanei_usb_write_bulk (fd, buf, &count); + +} + +void +startblob0075 (CANON_Handle * chndl, unsigned char *buf) +{ + + int fd; + fd = chndl->fd; + size_t count; + + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0xb0, 0x03); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x08, chndl->value_08); + cp2155_set (fd, 0x09, chndl->value_09); + cp2155_set (fd, 0x0a, chndl->value_0a); + cp2155_set (fd, 0x0b, chndl->value_0b); + cp2155_set (fd, 0xa0, 0x1d); + cp2155_set (fd, 0xa1, 0x00); + cp2155_set (fd, 0xa2, 0x06); + cp2155_set (fd, 0xa3, 0x70); + cp2155_set (fd, 0x64, 0x00); + cp2155_set (fd, 0x65, 0x00); + cp2155_set (fd, 0x61, 0x00); + cp2155_set (fd, 0x62, 0x2e); + cp2155_set (fd, 0x63, 0x00); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x5a, 0x32); + cp2155_set (fd, 0x5b, 0x32); + cp2155_set (fd, 0x5c, 0x32); + cp2155_set (fd, 0x5d, 0x32); + cp2155_set (fd, 0x52, 0x09); + cp2155_set (fd, 0x53, 0x5a); + cp2155_set (fd, 0x54, 0x06); + cp2155_set (fd, 0x55, 0x08); + cp2155_set (fd, 0x56, 0x05); + cp2155_set (fd, 0x57, 0x5f); + cp2155_set (fd, 0x58, 0xa9); + cp2155_set (fd, 0x59, 0xce); + cp2155_set (fd, 0x5e, 0x02); + cp2155_set (fd, 0x5f, 0x00); + cp2155_set (fd, 0x5f, 0x03); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x83, 0x02); + cp2155_set (fd, 0x84, 0x06); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0xb0, 0x0b); + + big_write (fd, 20852, buf); + + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x9b, 0x03); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0xc1); + cp2155_set (fd, 0x11, 0xc1); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x12, 0x40); + cp2155_set (fd, 0x13, 0x40); + cp2155_set (fd, 0x16, 0x40); + cp2155_set (fd, 0x21, 0x06); + cp2155_set (fd, 0x22, 0x40); + cp2155_set (fd, 0x20, 0x06); + cp2155_set (fd, 0x1d, 0x00); + cp2155_set (fd, 0x1e, 0x00); + cp2155_set (fd, 0x1f, 0xf0); + cp2155_set (fd, 0x66, 0x00); + cp2155_set (fd, 0x67, chndl->value_67); + cp2155_set (fd, 0x68, chndl->value_68); + cp2155_set (fd, 0x1a, 0x00); + cp2155_set (fd, 0x1b, 0x00); + cp2155_set (fd, 0x1c, 0x02); + cp2155_set (fd, 0x15, 0x83); + cp2155_set (fd, 0x14, 0x7c); + cp2155_set (fd, 0x17, 0x02); + cp2155_set (fd, 0x43, 0x1c); + cp2155_set (fd, 0x44, 0x9c); + cp2155_set (fd, 0x45, 0x38); + cp2155_set (fd, 0x23, 0x28); + cp2155_set (fd, 0x33, 0x28); + cp2155_set (fd, 0x24, 0x27); + cp2155_set (fd, 0x34, 0x27); + cp2155_set (fd, 0x25, 0x25); + cp2155_set (fd, 0x35, 0x25); + cp2155_set (fd, 0x26, 0x21); + cp2155_set (fd, 0x36, 0x21); + cp2155_set (fd, 0x27, 0x1c); + cp2155_set (fd, 0x37, 0x1c); + cp2155_set (fd, 0x28, 0x16); + cp2155_set (fd, 0x38, 0x16); + cp2155_set (fd, 0x29, 0x0f); + cp2155_set (fd, 0x39, 0x0f); + cp2155_set (fd, 0x2a, 0x08); + cp2155_set (fd, 0x3a, 0x08); + cp2155_set (fd, 0x2b, 0x00); + cp2155_set (fd, 0x3b, 0x00); + cp2155_set (fd, 0x2c, 0x08); + cp2155_set (fd, 0x3c, 0x08); + cp2155_set (fd, 0x2d, 0x0f); + cp2155_set (fd, 0x3d, 0x0f); + cp2155_set (fd, 0x2e, 0x16); + cp2155_set (fd, 0x3e, 0x16); + cp2155_set (fd, 0x2f, 0x1c); + cp2155_set (fd, 0x3f, 0x1c); + cp2155_set (fd, 0x30, 0x21); + cp2155_set (fd, 0x40, 0x21); + cp2155_set (fd, 0x31, 0x25); + cp2155_set (fd, 0x41, 0x25); + cp2155_set (fd, 0x32, 0x27); + cp2155_set (fd, 0x42, 0x27); + cp2155_set (fd, 0xca, 0x01); + cp2155_set (fd, 0xca, 0x01); + cp2155_set (fd, 0xca, 0x11); + cp2155_set (fd, 0x18, 0x00); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x01); + cp2155_set (fd, 0x73, 0x00); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x00); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x00\x01\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000010, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000020, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000030, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000040, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000050, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000060, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\xf0\x23\x80\x22\x2c\x21", + 16); + memcpy (buf + 0x00000070, + "\xf1\x1f\xcd\x1e\xbd\x1d\xc0\x1c\xd2\x1b\xf4\x1a\x22\x1a\x5e\x19", + 16); + memcpy (buf + 0x00000080, + "\xa4\x18\xf5\x17\x4f\x17\xb2\x16\x1d\x16\x90\x15\x09\x15\x89\x14", + 16); + memcpy (buf + 0x00000090, + "\x0e\x14\x9a\x13\x2a\x13\xc0\x12\x59\x12\xf8\x11\x9a\x11\x3f\x11", + 16); + memcpy (buf + 0x000000a0, + "\xe9\x10\x96\x10\x46\x10\xf8\x0f\xae\x0f\x66\x0f\x21\x0f\xde\x0e", + 16); + memcpy (buf + 0x000000b0, + "\x9e\x0e\x60\x0e\x23\x0e\xe9\x0d\xb0\x0d\x7a\x0d\x44\x0d\x11\x0d", + 16); + memcpy (buf + 0x000000c0, + "\xdf\x0c\xaf\x0c\x80\x0c\x52\x0c\x25\x0c\xfa\x0b\xd0\x0b\xa7\x0b", + 16); + memcpy (buf + 0x000000d0, + "\x80\x0b\x59\x0b\x33\x0b\x0e\x0b\xea\x0a\xc8\x0a\xa5\x0a\x84\x0a", + 16); + memcpy (buf + 0x000000e0, + "\x64\x0a\x44\x0a\x25\x0a\x07\x0a\xe9\x09\xcd\x09\xb0\x09\x95\x09", + 16); + memcpy (buf + 0x000000f0, + "\x7a\x09\x60\x09\x46\x09\x2c\x09\x14\x09\xfc\x08\xe4\x08\xcd\x08", + 16); + memcpy (buf + 0x00000100, "\xb6\x08\xa0\x08", 4); + count = 260; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x01); + cp2155_set (fd, 0x73, 0x00); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x02); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x00\x01\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000010, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000020, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000030, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000040, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000050, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000060, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\xf0\x23\x80\x22\x2c\x21", + 16); + memcpy (buf + 0x00000070, + "\xf1\x1f\xcd\x1e\xbd\x1d\xc0\x1c\xd2\x1b\xf4\x1a\x22\x1a\x5e\x19", + 16); + memcpy (buf + 0x00000080, + "\xa4\x18\xf5\x17\x4f\x17\xb2\x16\x1d\x16\x90\x15\x09\x15\x89\x14", + 16); + memcpy (buf + 0x00000090, + "\x0e\x14\x9a\x13\x2a\x13\xc0\x12\x59\x12\xf8\x11\x9a\x11\x3f\x11", + 16); + memcpy (buf + 0x000000a0, + "\xe9\x10\x96\x10\x46\x10\xf8\x0f\xae\x0f\x66\x0f\x21\x0f\xde\x0e", + 16); + memcpy (buf + 0x000000b0, + "\x9e\x0e\x60\x0e\x23\x0e\xe9\x0d\xb0\x0d\x7a\x0d\x44\x0d\x11\x0d", + 16); + memcpy (buf + 0x000000c0, + "\xdf\x0c\xaf\x0c\x80\x0c\x52\x0c\x25\x0c\xfa\x0b\xd0\x0b\xa7\x0b", + 16); + memcpy (buf + 0x000000d0, + "\x80\x0b\x59\x0b\x33\x0b\x0e\x0b\xea\x0a\xc8\x0a\xa5\x0a\x84\x0a", + 16); + memcpy (buf + 0x000000e0, + "\x64\x0a\x44\x0a\x25\x0a\x07\x0a\xe9\x09\xcd\x09\xb0\x09\x95\x09", + 16); + memcpy (buf + 0x000000f0, + "\x7a\x09\x60\x09\x46\x09\x2c\x09\x14\x09\xfc\x08\xe4\x08\xcd\x08", + 16); + memcpy (buf + 0x00000100, "\xb6\x08\xa0\x08", 4); + count = 260; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x04); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x80\x25\xc0\x1c\x4f\x17\x9a\x13\xe9\x10\xde\x0e", + 16); + memcpy (buf + 0x00000010, + "\x44\x0d\xfa\x0b\xea\x0a\x07\x0a\x46\x09\xa0\x08\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000020, "\x80\x25\x80\x25", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x01); + cp2155_set (fd, 0x73, 0x00); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x06); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x00\x01\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000010, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000020, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000030, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000040, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000050, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000060, + "\x80\x25\x80\x25\x80\x25\x80\x25\x80\x25\xf0\x23\x80\x22\x2c\x21", + 16); + memcpy (buf + 0x00000070, + "\xf1\x1f\xcd\x1e\xbd\x1d\xc0\x1c\xd2\x1b\xf4\x1a\x22\x1a\x5e\x19", + 16); + memcpy (buf + 0x00000080, + "\xa4\x18\xf5\x17\x4f\x17\xb2\x16\x1d\x16\x90\x15\x09\x15\x89\x14", + 16); + memcpy (buf + 0x00000090, + "\x0e\x14\x9a\x13\x2a\x13\xc0\x12\x59\x12\xf8\x11\x9a\x11\x3f\x11", + 16); + memcpy (buf + 0x000000a0, + "\xe9\x10\x96\x10\x46\x10\xf8\x0f\xae\x0f\x66\x0f\x21\x0f\xde\x0e", + 16); + memcpy (buf + 0x000000b0, + "\x9e\x0e\x60\x0e\x23\x0e\xe9\x0d\xb0\x0d\x7a\x0d\x44\x0d\x11\x0d", + 16); + memcpy (buf + 0x000000c0, + "\xdf\x0c\xaf\x0c\x80\x0c\x52\x0c\x25\x0c\xfa\x0b\xd0\x0b\xa7\x0b", + 16); + memcpy (buf + 0x000000d0, + "\x80\x0b\x59\x0b\x33\x0b\x0e\x0b\xea\x0a\xc8\x0a\xa5\x0a\x84\x0a", + 16); + memcpy (buf + 0x000000e0, + "\x64\x0a\x44\x0a\x25\x0a\x07\x0a\xe9\x09\xcd\x09\xb0\x09\x95\x09", + 16); + memcpy (buf + 0x000000f0, + "\x7a\x09\x60\x09\x46\x09\x2c\x09\x14\x09\xfc\x08\xe4\x08\xcd\x08", + 16); + memcpy (buf + 0x00000100, "\xb6\x08\xa0\x08", 4); + count = 260; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x08); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x80\x25\xc0\x1c\x4f\x17\x9a\x13\xe9\x10\xde\x0e", + 16); + memcpy (buf + 0x00000010, + "\x44\x0d\xfa\x0b\xea\x0a\x07\x0a\x46\x09\xa0\x08\x80\x25\x80\x25", + 16); + memcpy (buf + 0x00000020, "\x80\x25\x80\x25", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x9b, 0x02); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x91); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x03, 0x01); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x18); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x10); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + +} + +void +startblob0150 (CANON_Handle * chndl, unsigned char *buf) +{ + + int fd; + fd = chndl->fd; + size_t count; + + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0xb0, 0x02); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x08, chndl->value_08); + cp2155_set (fd, 0x09, chndl->value_09); + cp2155_set (fd, 0x0a, chndl->value_0a); + cp2155_set (fd, 0x0b, chndl->value_0b); + cp2155_set (fd, 0xa0, 0x1d); + cp2155_set (fd, 0xa1, 0x00); + cp2155_set (fd, 0xa2, 0x0c); + cp2155_set (fd, 0xa3, 0xd0); + cp2155_set (fd, 0x64, 0x00); + cp2155_set (fd, 0x65, 0x00); + cp2155_set (fd, 0x61, 0x00); + cp2155_set (fd, 0x62, 0x1e); + cp2155_set (fd, 0x63, 0xa0); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x5a, 0x32); + cp2155_set (fd, 0x5b, 0x32); + cp2155_set (fd, 0x5c, 0x32); + cp2155_set (fd, 0x5d, 0x32); + cp2155_set (fd, 0x52, 0x09); + cp2155_set (fd, 0x53, 0x5a); + cp2155_set (fd, 0x54, 0x06); + cp2155_set (fd, 0x55, 0x08); + cp2155_set (fd, 0x56, 0x05); + cp2155_set (fd, 0x57, 0x5f); + cp2155_set (fd, 0x58, 0xa9); + cp2155_set (fd, 0x59, 0xce); + cp2155_set (fd, 0x5e, 0x02); + cp2155_set (fd, 0x5f, 0x00); + cp2155_set (fd, 0x5f, 0x03); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x83, 0x02); + cp2155_set (fd, 0x84, 0x06); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0xb0, 0x0a); + + big_write (fd, 20852, buf); + + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x9b, 0x03); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x12, 0x40); + cp2155_set (fd, 0x13, 0x40); + cp2155_set (fd, 0x16, 0x40); + cp2155_set (fd, 0x21, 0x06); + cp2155_set (fd, 0x22, 0x40); + cp2155_set (fd, 0x20, 0x06); + cp2155_set (fd, 0x1d, 0x00); + cp2155_set (fd, 0x1e, 0x00); + cp2155_set (fd, 0x1f, 0x04); + cp2155_set (fd, 0x66, 0x00); + cp2155_set (fd, 0x67, chndl->value_67); + cp2155_set (fd, 0x68, chndl->value_68); + cp2155_set (fd, 0x1a, 0x00); + cp2155_set (fd, 0x1b, 0x00); + cp2155_set (fd, 0x1c, 0x02); + cp2155_set (fd, 0x15, 0x84); + cp2155_set (fd, 0x14, 0x7c); + cp2155_set (fd, 0x17, 0x02); + cp2155_set (fd, 0x43, 0x1c); + cp2155_set (fd, 0x44, 0x9c); + cp2155_set (fd, 0x45, 0x38); + cp2155_set (fd, 0x23, 0x28); + cp2155_set (fd, 0x33, 0x28); + cp2155_set (fd, 0x24, 0x27); + cp2155_set (fd, 0x34, 0x27); + cp2155_set (fd, 0x25, 0x25); + cp2155_set (fd, 0x35, 0x25); + cp2155_set (fd, 0x26, 0x21); + cp2155_set (fd, 0x36, 0x21); + cp2155_set (fd, 0x27, 0x1c); + cp2155_set (fd, 0x37, 0x1c); + cp2155_set (fd, 0x28, 0x16); + cp2155_set (fd, 0x38, 0x16); + cp2155_set (fd, 0x29, 0x0f); + cp2155_set (fd, 0x39, 0x0f); + cp2155_set (fd, 0x2a, 0x08); + cp2155_set (fd, 0x3a, 0x08); + cp2155_set (fd, 0x2b, 0x00); + cp2155_set (fd, 0x3b, 0x00); + cp2155_set (fd, 0x2c, 0x08); + cp2155_set (fd, 0x3c, 0x08); + cp2155_set (fd, 0x2d, 0x0f); + cp2155_set (fd, 0x3d, 0x0f); + cp2155_set (fd, 0x2e, 0x16); + cp2155_set (fd, 0x3e, 0x16); + cp2155_set (fd, 0x2f, 0x1c); + cp2155_set (fd, 0x3f, 0x1c); + cp2155_set (fd, 0x30, 0x21); + cp2155_set (fd, 0x40, 0x21); + cp2155_set (fd, 0x31, 0x25); + cp2155_set (fd, 0x41, 0x25); + cp2155_set (fd, 0x32, 0x27); + cp2155_set (fd, 0x42, 0x27); + cp2155_set (fd, 0xca, 0x01); + cp2155_set (fd, 0xca, 0x01); + cp2155_set (fd, 0xca, 0x11); + cp2155_set (fd, 0x18, 0x00); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x01); + cp2155_set (fd, 0x73, 0x00); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x00); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x00\x01\x80\x25\xd7\x24\x35\x24\x98\x23\x00\x23\x6d\x22", + 16); + memcpy (buf + 0x00000010, + "\xdf\x21\x56\x21\xd1\x20\x50\x20\xd2\x1f\x59\x1f\xe3\x1e\x70\x1e", + 16); + memcpy (buf + 0x00000020, + "\x01\x1e\x95\x1d\x2c\x1d\xc6\x1c\x62\x1c\x02\x1c\xa3\x1b\x47\x1b", + 16); + memcpy (buf + 0x00000030, + "\xee\x1a\x97\x1a\x42\x1a\xef\x19\x9e\x19\x4f\x19\x02\x19\xb7\x18", + 16); + memcpy (buf + 0x00000040, + "\x6d\x18\x25\x18\xdf\x17\x9a\x17\x57\x17\x16\x17\xd6\x16\x97\x16", + 16); + memcpy (buf + 0x00000050, + "\x59\x16\x1d\x16\xe2\x15\xa8\x15\x70\x15\x38\x15\x02\x15\xcd\x14", + 16); + memcpy (buf + 0x00000060, + "\x99\x14\x66\x14\x33\x14\x02\x14\xd2\x13\xa2\x13\x74\x13\x46\x13", + 16); + memcpy (buf + 0x00000070, + "\x19\x13\xed\x12\xc2\x12\x98\x12\x6e\x12\x45\x12\x1d\x12\xf5\x11", + 16); + memcpy (buf + 0x00000080, + "\xce\x11\xa8\x11\x82\x11\x5d\x11\x39\x11\x15\x11\xf2\x10\xcf\x10", + 16); + memcpy (buf + 0x00000090, + "\xad\x10\x8b\x10\x6a\x10\x4a\x10\x2a\x10\x0a\x10\xeb\x0f\xcc\x0f", + 16); + memcpy (buf + 0x000000a0, + "\xae\x0f\x90\x0f\x73\x0f\x56\x0f\x3a\x0f\x1e\x0f\x02\x0f\xe7\x0e", + 16); + memcpy (buf + 0x000000b0, + "\xcc\x0e\xb2\x0e\x97\x0e\x7e\x0e\x64\x0e\x4b\x0e\x32\x0e\x1a\x0e", + 16); + memcpy (buf + 0x000000c0, + "\x02\x0e\xea\x0d\xd3\x0d\xbc\x0d\xa5\x0d\x8e\x0d\x78\x0d\x62\x0d", + 16); + memcpy (buf + 0x000000d0, + "\x4d\x0d\x37\x0d\x22\x0d\x0d\x0d\xf8\x0c\xe4\x0c\xd0\x0c\xbc\x0c", + 16); + memcpy (buf + 0x000000e0, + "\xa8\x0c\x95\x0c\x82\x0c\x6f\x0c\x5c\x0c\x4a\x0c\x37\x0c\x25\x0c", + 16); + memcpy (buf + 0x000000f0, + "\x14\x0c\x02\x0c\xf0\x0b\xdf\x0b\xce\x0b\xbd\x0b\xac\x0b\x9c\x0b", + 16); + memcpy (buf + 0x00000100, "\x8c\x0b\x7c\x0b", 4); + count = 260; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x01); + cp2155_set (fd, 0x73, 0x00); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x02); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x00\x01\x80\x25\xd7\x24\x35\x24\x98\x23\x00\x23\x6d\x22", + 16); + memcpy (buf + 0x00000010, + "\xdf\x21\x56\x21\xd1\x20\x50\x20\xd2\x1f\x59\x1f\xe3\x1e\x70\x1e", + 16); + memcpy (buf + 0x00000020, + "\x01\x1e\x95\x1d\x2c\x1d\xc6\x1c\x62\x1c\x02\x1c\xa3\x1b\x47\x1b", + 16); + memcpy (buf + 0x00000030, + "\xee\x1a\x97\x1a\x42\x1a\xef\x19\x9e\x19\x4f\x19\x02\x19\xb7\x18", + 16); + memcpy (buf + 0x00000040, + "\x6d\x18\x25\x18\xdf\x17\x9a\x17\x57\x17\x16\x17\xd6\x16\x97\x16", + 16); + memcpy (buf + 0x00000050, + "\x59\x16\x1d\x16\xe2\x15\xa8\x15\x70\x15\x38\x15\x02\x15\xcd\x14", + 16); + memcpy (buf + 0x00000060, + "\x99\x14\x66\x14\x33\x14\x02\x14\xd2\x13\xa2\x13\x74\x13\x46\x13", + 16); + memcpy (buf + 0x00000070, + "\x19\x13\xed\x12\xc2\x12\x98\x12\x6e\x12\x45\x12\x1d\x12\xf5\x11", + 16); + memcpy (buf + 0x00000080, + "\xce\x11\xa8\x11\x82\x11\x5d\x11\x39\x11\x15\x11\xf2\x10\xcf\x10", + 16); + memcpy (buf + 0x00000090, + "\xad\x10\x8b\x10\x6a\x10\x4a\x10\x2a\x10\x0a\x10\xeb\x0f\xcc\x0f", + 16); + memcpy (buf + 0x000000a0, + "\xae\x0f\x90\x0f\x73\x0f\x56\x0f\x3a\x0f\x1e\x0f\x02\x0f\xe7\x0e", + 16); + memcpy (buf + 0x000000b0, + "\xcc\x0e\xb2\x0e\x97\x0e\x7e\x0e\x64\x0e\x4b\x0e\x32\x0e\x1a\x0e", + 16); + memcpy (buf + 0x000000c0, + "\x02\x0e\xea\x0d\xd3\x0d\xbc\x0d\xa5\x0d\x8e\x0d\x78\x0d\x62\x0d", + 16); + memcpy (buf + 0x000000d0, + "\x4d\x0d\x37\x0d\x22\x0d\x0d\x0d\xf8\x0c\xe4\x0c\xd0\x0c\xbc\x0c", + 16); + memcpy (buf + 0x000000e0, + "\xa8\x0c\x95\x0c\x82\x0c\x6f\x0c\x5c\x0c\x4a\x0c\x37\x0c\x25\x0c", + 16); + memcpy (buf + 0x000000f0, + "\x14\x0c\x02\x0c\xf0\x0b\xdf\x0b\xce\x0b\xbd\x0b\xac\x0b\x9c\x0b", + 16); + memcpy (buf + 0x00000100, "\x8c\x0b\x7c\x0b", 4); + count = 260; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x04); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x80\x25\x18\x1f\x8f\x1a\x2d\x17\x8f\x14\x79\x12", + 16); + memcpy (buf + 0x00000010, + "\xc6\x10\x5b\x0f\x2a\x0e\x24\x0d\x41\x0c\x7c\x0b\xe3\x1e\x70\x1e", + 16); + memcpy (buf + 0x00000020, "\x01\x1e\x95\x1d", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x01); + cp2155_set (fd, 0x73, 0x00); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x06); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x00\x01\x80\x25\xd7\x24\x35\x24\x98\x23\x00\x23\x6d\x22", + 16); + memcpy (buf + 0x00000010, + "\xdf\x21\x56\x21\xd1\x20\x50\x20\xd2\x1f\x59\x1f\xe3\x1e\x70\x1e", + 16); + memcpy (buf + 0x00000020, + "\x01\x1e\x95\x1d\x2c\x1d\xc6\x1c\x62\x1c\x02\x1c\xa3\x1b\x47\x1b", + 16); + memcpy (buf + 0x00000030, + "\xee\x1a\x97\x1a\x42\x1a\xef\x19\x9e\x19\x4f\x19\x02\x19\xb7\x18", + 16); + memcpy (buf + 0x00000040, + "\x6d\x18\x25\x18\xdf\x17\x9a\x17\x57\x17\x16\x17\xd6\x16\x97\x16", + 16); + memcpy (buf + 0x00000050, + "\x59\x16\x1d\x16\xe2\x15\xa8\x15\x70\x15\x38\x15\x02\x15\xcd\x14", + 16); + memcpy (buf + 0x00000060, + "\x99\x14\x66\x14\x33\x14\x02\x14\xd2\x13\xa2\x13\x74\x13\x46\x13", + 16); + memcpy (buf + 0x00000070, + "\x19\x13\xed\x12\xc2\x12\x98\x12\x6e\x12\x45\x12\x1d\x12\xf5\x11", + 16); + memcpy (buf + 0x00000080, + "\xce\x11\xa8\x11\x82\x11\x5d\x11\x39\x11\x15\x11\xf2\x10\xcf\x10", + 16); + memcpy (buf + 0x00000090, + "\xad\x10\x8b\x10\x6a\x10\x4a\x10\x2a\x10\x0a\x10\xeb\x0f\xcc\x0f", + 16); + memcpy (buf + 0x000000a0, + "\xae\x0f\x90\x0f\x73\x0f\x56\x0f\x3a\x0f\x1e\x0f\x02\x0f\xe7\x0e", + 16); + memcpy (buf + 0x000000b0, + "\xcc\x0e\xb2\x0e\x97\x0e\x7e\x0e\x64\x0e\x4b\x0e\x32\x0e\x1a\x0e", + 16); + memcpy (buf + 0x000000c0, + "\x02\x0e\xea\x0d\xd3\x0d\xbc\x0d\xa5\x0d\x8e\x0d\x78\x0d\x62\x0d", + 16); + memcpy (buf + 0x000000d0, + "\x4d\x0d\x37\x0d\x22\x0d\x0d\x0d\xf8\x0c\xe4\x0c\xd0\x0c\xbc\x0c", + 16); + memcpy (buf + 0x000000e0, + "\xa8\x0c\x95\x0c\x82\x0c\x6f\x0c\x5c\x0c\x4a\x0c\x37\x0c\x25\x0c", + 16); + memcpy (buf + 0x000000f0, + "\x14\x0c\x02\x0c\xf0\x0b\xdf\x0b\xce\x0b\xbd\x0b\xac\x0b\x9c\x0b", + 16); + memcpy (buf + 0x00000100, "\x8c\x0b\x7c\x0b", 4); + count = 260; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x08); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x80\x25\x18\x1f\x8f\x1a\x2d\x17\x8f\x14\x79\x12", + 16); + memcpy (buf + 0x00000010, + "\xc6\x10\x5b\x0f\x2a\x0e\x24\x0d\x41\x0c\x7c\x0b\xe3\x1e\x70\x1e", + 16); + memcpy (buf + 0x00000020, "\x01\x1e\x95\x1d", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x9b, 0x02); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x91); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x03, 0x01); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x18); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x10); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + +} + +void +startblob0300 (CANON_Handle * chndl, unsigned char *buf) +{ + + int fd; + fd = chndl->fd; + size_t count; + + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0xb0, 0x01); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x08, chndl->value_08); + cp2155_set (fd, 0x09, chndl->value_09); + cp2155_set (fd, 0x0a, chndl->value_0a); + cp2155_set (fd, 0x0b, chndl->value_0b); + cp2155_set (fd, 0xa0, 0x1d); + cp2155_set (fd, 0xa1, 0x00); + cp2155_set (fd, 0xa2, 0x19); + cp2155_set (fd, 0xa3, 0x30); + cp2155_set (fd, 0x64, 0x00); + cp2155_set (fd, 0x65, 0x00); + cp2155_set (fd, 0x61, 0x00); + cp2155_set (fd, 0x62, 0x2a); + cp2155_set (fd, 0x63, 0x80); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x5a, 0x32); + cp2155_set (fd, 0x5b, 0x32); + cp2155_set (fd, 0x5c, 0x32); + cp2155_set (fd, 0x5d, 0x32); + cp2155_set (fd, 0x52, 0x09); + cp2155_set (fd, 0x53, 0x5a); + cp2155_set (fd, 0x54, 0x06); + cp2155_set (fd, 0x55, 0x08); + cp2155_set (fd, 0x56, 0x05); + cp2155_set (fd, 0x57, 0x5f); + cp2155_set (fd, 0x58, 0xa9); + cp2155_set (fd, 0x59, 0xce); + cp2155_set (fd, 0x5e, 0x02); + cp2155_set (fd, 0x5f, 0x00); + cp2155_set (fd, 0x5f, 0x03); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x83, 0x02); + cp2155_set (fd, 0x84, 0x06); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0xb0, 0x09); + + big_write (fd, 20852, buf); + + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x9b, 0x01); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x12, 0x0c); + cp2155_set (fd, 0x13, 0x0c); + cp2155_set (fd, 0x16, 0x0c); + cp2155_set (fd, 0x21, 0x06); + cp2155_set (fd, 0x22, 0x0c); + cp2155_set (fd, 0x20, 0x06); + cp2155_set (fd, 0x1d, 0x00); + cp2155_set (fd, 0x1e, 0x00); + cp2155_set (fd, 0x1f, 0x04); + cp2155_set (fd, 0x66, 0x00); + cp2155_set (fd, 0x67, chndl->value_67); + cp2155_set (fd, 0x68, chndl->value_68); + cp2155_set (fd, 0x1a, 0x00); + cp2155_set (fd, 0x1b, 0x00); + cp2155_set (fd, 0x1c, 0x02); + cp2155_set (fd, 0x15, 0x83); + cp2155_set (fd, 0x14, 0x7c); + cp2155_set (fd, 0x17, 0x02); + cp2155_set (fd, 0x43, 0x1c); + cp2155_set (fd, 0x44, 0x9c); + cp2155_set (fd, 0x45, 0x38); + cp2155_set (fd, 0x23, 0x14); + cp2155_set (fd, 0x33, 0x14); + cp2155_set (fd, 0x24, 0x14); + cp2155_set (fd, 0x34, 0x14); + cp2155_set (fd, 0x25, 0x14); + cp2155_set (fd, 0x35, 0x14); + cp2155_set (fd, 0x26, 0x14); + cp2155_set (fd, 0x36, 0x14); + cp2155_set (fd, 0x27, 0x14); + cp2155_set (fd, 0x37, 0x14); + cp2155_set (fd, 0x28, 0x14); + cp2155_set (fd, 0x38, 0x14); + cp2155_set (fd, 0x29, 0x14); + cp2155_set (fd, 0x39, 0x14); + cp2155_set (fd, 0x2a, 0x14); + cp2155_set (fd, 0x3a, 0x14); + cp2155_set (fd, 0x2b, 0x14); + cp2155_set (fd, 0x3b, 0x14); + cp2155_set (fd, 0x2c, 0x14); + cp2155_set (fd, 0x3c, 0x14); + cp2155_set (fd, 0x2d, 0x14); + cp2155_set (fd, 0x3d, 0x14); + cp2155_set (fd, 0x2e, 0x14); + cp2155_set (fd, 0x3e, 0x14); + cp2155_set (fd, 0x2f, 0x14); + cp2155_set (fd, 0x3f, 0x14); + cp2155_set (fd, 0x30, 0x14); + cp2155_set (fd, 0x40, 0x14); + cp2155_set (fd, 0x31, 0x14); + cp2155_set (fd, 0x41, 0x14); + cp2155_set (fd, 0x32, 0x14); + cp2155_set (fd, 0x42, 0x14); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0x18, 0x00); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x30); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x00); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x30\x00\x80\x25\x36\x25\xee\x24\xa8\x24\x62\x24\x1d\x24", + 16); + memcpy (buf + 0x00000010, + "\xd9\x23\x96\x23\x54\x23\x13\x23\xd3\x22\x94\x22\x56\x22\x19\x22", + 16); + memcpy (buf + 0x00000020, + "\xdc\x21\xa1\x21\x66\x21\x2c\x21\xf3\x20\xba\x20\x82\x20\x4b\x20", + 16); + memcpy (buf + 0x00000030, "\x15\x20\xe0\x1f", 4); + count = 52; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x30); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x02); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x30\x00\x80\x25\x36\x25\xee\x24\xa8\x24\x62\x24\x1d\x24", + 16); + memcpy (buf + 0x00000010, + "\xd9\x23\x96\x23\x54\x23\x13\x23\xd3\x22\x94\x22\x56\x22\x19\x22", + 16); + memcpy (buf + 0x00000020, + "\xdc\x21\xa1\x21\x66\x21\x2c\x21\xf3\x20\xba\x20\x82\x20\x4b\x20", + 16); + memcpy (buf + 0x00000030, "\x15\x20\xe0\x1f", 4); + count = 52; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x04); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x80\x25\xe8\x24\x55\x24\xc7\x23\x3d\x23\xb7\x22", + 16); + memcpy (buf + 0x00000010, + "\x35\x22\xb6\x21\x3c\x21\xc4\x20\x50\x20\xe0\x1f\x56\x22\x19\x22", + 16); + memcpy (buf + 0x00000020, "\xdc\x21\xa1\x21", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x30); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x06); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x30\x00\x80\x25\x36\x25\xee\x24\xa8\x24\x62\x24\x1d\x24", + 16); + memcpy (buf + 0x00000010, + "\xd9\x23\x96\x23\x54\x23\x13\x23\xd3\x22\x94\x22\x56\x22\x19\x22", + 16); + memcpy (buf + 0x00000020, + "\xdc\x21\xa1\x21\x66\x21\x2c\x21\xf3\x20\xba\x20\x82\x20\x4b\x20", + 16); + memcpy (buf + 0x00000030, "\x15\x20\xe0\x1f", 4); + count = 52; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x08); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x80\x25\xe8\x24\x55\x24\xc7\x23\x3d\x23\xb7\x22", + 16); + memcpy (buf + 0x00000010, + "\x35\x22\xb6\x21\x3c\x21\xc4\x20\x50\x20\xe0\x1f\x56\x22\x19\x22", + 16); + memcpy (buf + 0x00000020, "\xdc\x21\xa1\x21", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x9b, 0x00); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x91); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x03, 0x01); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x18); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x10); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + +} + +void +startblob0600 (CANON_Handle * chndl, unsigned char *buf) +{ + + int fd; + fd = chndl->fd; + size_t count; + + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0x90, 0xd8); + cp2155_set (fd, 0xb0, 0x00); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x08, chndl->value_08); + cp2155_set (fd, 0x09, chndl->value_09); + cp2155_set (fd, 0x0a, chndl->value_0a); + cp2155_set (fd, 0x0b, chndl->value_0b); + cp2155_set (fd, 0xa0, 0x1d); + cp2155_set (fd, 0xa1, 0x00); + cp2155_set (fd, 0xa2, 0x77); + cp2155_set (fd, 0xa3, 0xb0); + cp2155_set (fd, 0x64, 0x00); + cp2155_set (fd, 0x65, 0x00); + cp2155_set (fd, 0x61, 0x00); + cp2155_set (fd, 0x62, 0x15); + cp2155_set (fd, 0x63, 0xe0); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x5a, 0x32); + cp2155_set (fd, 0x5b, 0x32); + cp2155_set (fd, 0x5c, 0x32); + cp2155_set (fd, 0x5d, 0x32); + cp2155_set (fd, 0x52, 0x07); + cp2155_set (fd, 0x53, 0xd0); + cp2155_set (fd, 0x54, 0x07); + cp2155_set (fd, 0x55, 0xd0); + cp2155_set (fd, 0x56, 0x07); + cp2155_set (fd, 0x57, 0xd0); + cp2155_set (fd, 0x58, 0x00); + cp2155_set (fd, 0x59, 0x01); + cp2155_set (fd, 0x5e, 0x02); + cp2155_set (fd, 0x5f, 0x00); + cp2155_set (fd, 0x5f, 0x03); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x83, 0x02); + cp2155_set (fd, 0x84, 0x06); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0xb0, 0x00); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x9b, 0x01); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x83); + cp2155_set (fd, 0x11, 0x83); + cp2155_set (fd, 0x11, 0xc3); + cp2155_set (fd, 0x11, 0xc3); + cp2155_set (fd, 0x11, 0xc3); + cp2155_set (fd, 0x11, 0xc1); + cp2155_set (fd, 0x11, 0xc1); + cp2155_set (fd, 0x12, 0x12); + cp2155_set (fd, 0x13, 0x00); + cp2155_set (fd, 0x16, 0x12); + cp2155_set (fd, 0x21, 0x06); + cp2155_set (fd, 0x22, 0x12); + cp2155_set (fd, 0x20, 0x06); + cp2155_set (fd, 0x1d, 0x00); + cp2155_set (fd, 0x1e, 0x00); + cp2155_set (fd, 0x1f, 0x04); + cp2155_set (fd, 0x66, 0x00); + cp2155_set (fd, 0x67, chndl->value_67); + cp2155_set (fd, 0x68, chndl->value_68); + cp2155_set (fd, 0x1a, 0x00); + cp2155_set (fd, 0x1b, 0x00); + cp2155_set (fd, 0x1c, 0x02); + cp2155_set (fd, 0x15, 0x01); + cp2155_set (fd, 0x14, 0x01); + cp2155_set (fd, 0x17, 0x01); + cp2155_set (fd, 0x43, 0x1c); + cp2155_set (fd, 0x44, 0x9c); + cp2155_set (fd, 0x45, 0x38); + cp2155_set (fd, 0x23, 0x14); + cp2155_set (fd, 0x33, 0x14); + cp2155_set (fd, 0x24, 0x14); + cp2155_set (fd, 0x34, 0x14); + cp2155_set (fd, 0x25, 0x14); + cp2155_set (fd, 0x35, 0x14); + cp2155_set (fd, 0x26, 0x14); + cp2155_set (fd, 0x36, 0x14); + cp2155_set (fd, 0x27, 0x14); + cp2155_set (fd, 0x37, 0x14); + cp2155_set (fd, 0x28, 0x14); + cp2155_set (fd, 0x38, 0x14); + cp2155_set (fd, 0x29, 0x14); + cp2155_set (fd, 0x39, 0x14); + cp2155_set (fd, 0x2a, 0x14); + cp2155_set (fd, 0x3a, 0x14); + cp2155_set (fd, 0x2b, 0x14); + cp2155_set (fd, 0x3b, 0x14); + cp2155_set (fd, 0x2c, 0x14); + cp2155_set (fd, 0x3c, 0x14); + cp2155_set (fd, 0x2d, 0x14); + cp2155_set (fd, 0x3d, 0x14); + cp2155_set (fd, 0x2e, 0x14); + cp2155_set (fd, 0x3e, 0x14); + cp2155_set (fd, 0x2f, 0x14); + cp2155_set (fd, 0x3f, 0x14); + cp2155_set (fd, 0x30, 0x14); + cp2155_set (fd, 0x40, 0x14); + cp2155_set (fd, 0x31, 0x14); + cp2155_set (fd, 0x41, 0x14); + cp2155_set (fd, 0x32, 0x14); + cp2155_set (fd, 0x42, 0x14); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0x18, 0x00); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x50); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x00); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x0000, + "\x04\x70\x50\x00\x80\x25\x58\x25\x32\x25\x0b\x25\xe5\x24\xc0\x24", + 16); + memcpy (buf + 0x0010, + "\x9a\x24\x75\x24\x50\x24\x2b\x24\x07\x24\xe3\x23\xbf\x23\x9c\x23", + 16); + memcpy (buf + 0x0020, + "\x79\x23\x56\x23\x33\x23\x11\x23\xee\x22\xcd\x22\xab\x22\x8a\x22", + 16); + memcpy (buf + 0x0030, + "\x68\x22\x48\x22\x27\x22\x07\x22\xe6\x21\xc7\x21\xa7\x21\x87\x21", + 16); + memcpy (buf + 0x0040, + "\x68\x21\x49\x21\x2a\x21\x0c\x21\xee\x20\xd0\x20\x00\x00\x00\x00", + 16); + memcpy (buf + 0x0050, "\x00\x00\x00\x00", 4); + count = 84; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x50); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x02); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x0000, + "\x04\x70\x50\x00\x80\x25\x58\x25\x32\x25\x0b\x25\xe5\x24\xc0\x24", + 16); + memcpy (buf + 0x0010, + "\x9a\x24\x75\x24\x50\x24\x2b\x24\x07\x24\xe3\x23\xbf\x23\x9c\x23", + 16); + memcpy (buf + 0x0020, + "\x79\x23\x56\x23\x33\x23\x11\x23\xee\x22\xcd\x22\xab\x22\x8a\x22", + 16); + memcpy (buf + 0x0030, + "\x68\x22\x48\x22\x27\x22\x07\x22\xe6\x21\xc7\x21\xa7\x21\x87\x21", + 16); + memcpy (buf + 0x0040, + "\x68\x21\x49\x21\x2a\x21\x0c\x21\xee\x20\xd0\x20\x00\x00\x00\x00", + 16); + memcpy (buf + 0x0050, "\x00\x00\x00\x00", 4); + count = 84; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x04); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x0000, + "\x04\x70\x20\x00\x80\x25\x04\x25\x8c\x24\x18\x24\xa5\x23\x36\x23", + 16); + memcpy (buf + 0x0010, + "\xca\x22\x60\x22\xf8\x21\x93\x21\x30\x21\xd0\x20\x00\x00\x00\x00", + 16); + memcpy (buf + 0x0020, "\x00\x00\x00\x00", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x50); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x06); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x0000, + "\x04\x70\x50\x00\x80\x25\x58\x25\x32\x25\x0b\x25\xe5\x24\xc0\x24", + 16); + memcpy (buf + 0x0010, + "\x9a\x24\x75\x24\x50\x24\x2b\x24\x07\x24\xe3\x23\xbf\x23\x9c\x23", + 16); + memcpy (buf + 0x0020, + "\x79\x23\x56\x23\x33\x23\x11\x23\xee\x22\xcd\x22\xab\x22\x8a\x22", + 16); + memcpy (buf + 0x0030, + "\x68\x22\x48\x22\x27\x22\x07\x22\xe6\x21\xc7\x21\xa7\x21\x87\x21", + 16); + memcpy (buf + 0x0040, + "\x68\x21\x49\x21\x2a\x21\x0c\x21\xee\x20\xd0\x20\x00\x00\x00\x00", + 16); + memcpy (buf + 0x0050, "\x00\x00\x00\x00", 4); + count = 84; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x08); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x0000, + "\x04\x70\x20\x00\x80\x25\x04\x25\x8c\x24\x18\x24\xa5\x23\x36\x23", + 16); + memcpy (buf + 0x0010, + "\xca\x22\x60\x22\xf8\x21\x93\x21\x30\x21\xd0\x20\x00\x00\x00\x00", + 16); + memcpy (buf + 0x0020, "\x00\x00\x00\x00", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x9b, 0x00); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0xd1); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x03, 0x01); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x18); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x10); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + +} + +void +startblob1200 (CANON_Handle * chndl, unsigned char *buf) +{ + + int fd; + fd = chndl->fd; + size_t count; + + cp2155_set (fd, 0x90, 0xc8); + cp2155_set (fd, 0x90, 0xe8); + cp2155_set (fd, 0xb0, 0x00); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x07, 0x00); + cp2155_set (fd, 0x08, chndl->value_08); + cp2155_set (fd, 0x09, chndl->value_09); + cp2155_set (fd, 0x0a, chndl->value_0a); + cp2155_set (fd, 0x0b, chndl->value_0b); + cp2155_set (fd, 0xa0, 0x1d); + cp2155_set (fd, 0xa1, 0x00); + cp2155_set (fd, 0xa2, 0x63); + cp2155_set (fd, 0xa3, 0xd0); + cp2155_set (fd, 0x64, 0x00); + cp2155_set (fd, 0x65, 0x00); + cp2155_set (fd, 0x61, 0x00); + cp2155_set (fd, 0x62, 0xaa); + cp2155_set (fd, 0x63, 0x00); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x5a, 0x32); + cp2155_set (fd, 0x5b, 0x32); + cp2155_set (fd, 0x5c, 0x32); + cp2155_set (fd, 0x5d, 0x32); + cp2155_set (fd, 0x52, 0x11); + cp2155_set (fd, 0x53, 0x50); + cp2155_set (fd, 0x54, 0x0c); + cp2155_set (fd, 0x55, 0x01); + cp2155_set (fd, 0x56, 0x0a); + cp2155_set (fd, 0x57, 0xae); + cp2155_set (fd, 0x58, 0xa9); + cp2155_set (fd, 0x59, 0xce); + cp2155_set (fd, 0x5e, 0x02); + cp2155_set (fd, 0x5f, 0x00); + cp2155_set (fd, 0x5f, 0x03); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x50, 0x04); + cp2155_set (fd, 0x51, chndl->value_51); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x81, 0x29); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x82, 0x09); + cp2155_set (fd, 0x83, 0x02); + cp2155_set (fd, 0x84, 0x06); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0xb0, 0x08); + + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0xa1); + cp2155_set (fd, 0x73, 0xa0); + cp2155_set (fd, 0x74, 0x00); + cp2155_set (fd, 0x75, 0x00); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + count = 41380; + make_buf (count, buf); + sanei_usb_write_bulk (fd, buf, &count); + + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0xa1); + cp2155_set (fd, 0x73, 0xa0); + cp2155_set (fd, 0x74, 0x00); + cp2155_set (fd, 0x75, 0xb0); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + sanei_usb_write_bulk (fd, buf, &count); + + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0xa1); + cp2155_set (fd, 0x73, 0xa0); + cp2155_set (fd, 0x74, 0x01); + cp2155_set (fd, 0x75, 0x60); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + sanei_usb_write_bulk (fd, buf, &count); + + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x9b, 0x01); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x11, 0x81); + cp2155_set (fd, 0x12, 0x06); + cp2155_set (fd, 0x13, 0x06); + cp2155_set (fd, 0x16, 0x06); + cp2155_set (fd, 0x21, 0x06); + cp2155_set (fd, 0x22, 0x06); + cp2155_set (fd, 0x20, 0x06); + cp2155_set (fd, 0x1d, 0x00); + cp2155_set (fd, 0x1e, 0x00); + cp2155_set (fd, 0x1f, 0x04); + cp2155_set (fd, 0x66, 0x00); + cp2155_set (fd, 0x67, chndl->value_67); + cp2155_set (fd, 0x68, chndl->value_68); + cp2155_set (fd, 0x1a, 0x00); + cp2155_set (fd, 0x1b, 0x00); + cp2155_set (fd, 0x1c, 0x02); + cp2155_set (fd, 0x15, 0x80); + cp2155_set (fd, 0x14, 0x7c); + cp2155_set (fd, 0x17, 0x01); + cp2155_set (fd, 0x43, 0x1c); + cp2155_set (fd, 0x44, 0x9c); + cp2155_set (fd, 0x45, 0x38); + cp2155_set (fd, 0x23, 0x14); + cp2155_set (fd, 0x33, 0x14); + cp2155_set (fd, 0x24, 0x14); + cp2155_set (fd, 0x34, 0x14); + cp2155_set (fd, 0x25, 0x12); + cp2155_set (fd, 0x35, 0x12); + cp2155_set (fd, 0x26, 0x11); + cp2155_set (fd, 0x36, 0x11); + cp2155_set (fd, 0x27, 0x0e); + cp2155_set (fd, 0x37, 0x0e); + cp2155_set (fd, 0x28, 0x0b); + cp2155_set (fd, 0x38, 0x0b); + cp2155_set (fd, 0x29, 0x08); + cp2155_set (fd, 0x39, 0x08); + cp2155_set (fd, 0x2a, 0x04); + cp2155_set (fd, 0x3a, 0x04); + cp2155_set (fd, 0x2b, 0x00); + cp2155_set (fd, 0x3b, 0x00); + cp2155_set (fd, 0x2c, 0x04); + cp2155_set (fd, 0x3c, 0x04); + cp2155_set (fd, 0x2d, 0x08); + cp2155_set (fd, 0x3d, 0x08); + cp2155_set (fd, 0x2e, 0x0b); + cp2155_set (fd, 0x3e, 0x0b); + cp2155_set (fd, 0x2f, 0x0e); + cp2155_set (fd, 0x3f, 0x0e); + cp2155_set (fd, 0x30, 0x11); + cp2155_set (fd, 0x40, 0x11); + cp2155_set (fd, 0x31, 0x12); + cp2155_set (fd, 0x41, 0x12); + cp2155_set (fd, 0x32, 0x14); + cp2155_set (fd, 0x42, 0x14); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0xca, 0x00); + cp2155_set (fd, 0x18, 0x01); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x00); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff", + 16); + memcpy (buf + 0x00000010, + "\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\x00\x00", + 16); + memcpy (buf + 0x00000020, "\x00\x00\x00\x00", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x02); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff", + 16); + memcpy (buf + 0x00000010, + "\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\x00\x00", + 16); + memcpy (buf + 0x00000020, "\x00\x00\x00\x00", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x04); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff", + 16); + memcpy (buf + 0x00000010, + "\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\x00\x00", + 16); + memcpy (buf + 0x00000020, "\x00\x00\x00\x00", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x06); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff", + 16); + memcpy (buf + 0x00000010, + "\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\x00\x00", + 16); + memcpy (buf + 0x00000020, "\x00\x00\x00\x00", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x14); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x20); + cp2155_set (fd, 0x74, 0x03); + cp2155_set (fd, 0x75, 0x08); + cp2155_set (fd, 0x76, 0x00); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + memcpy (buf + 0x00000000, + "\x04\x70\x18\x00\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff", + 16); + memcpy (buf + 0x00000010, + "\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\x00\x00", + 16); + memcpy (buf + 0x00000020, "\x00\x00\x00\x00", 4); + count = 36; + sanei_usb_write_bulk (fd, buf, &count); + cp2155_set (fd, 0x9b, 0x00); + cp2155_set (fd, 0x10, 0x05); + cp2155_set (fd, 0x11, 0x91); + cp2155_set (fd, 0x60, 0x15); + cp2155_set (fd, 0x80, 0x12); + cp2155_set (fd, 0x03, 0x01); + cp2155_set (fd, 0x71, 0x01); + cp2155_set (fd, 0x0230, 0x11); + cp2155_set (fd, 0x71, 0x18); + cp2155_set (fd, 0x72, 0x00); + cp2155_set (fd, 0x73, 0x10); + cp2155_set (fd, 0x0239, 0x40); + cp2155_set (fd, 0x0238, 0x89); + cp2155_set (fd, 0x023c, 0x2f); + cp2155_set (fd, 0x0264, 0x20); + +} + +void +send_start_blob (CANON_Handle * chndl) +{ + unsigned char buf[0xf000]; + + int fd; + fd = chndl->fd; + +/* value_51: lamp colors + bit 0 set: red on, bit 1 set: green on, bit 2 set: blue on + all bits off: no scan is made +*/ + chndl->value_51 = 0x07; + + switch (chndl->val[opt_resolution].w) + { + case 75: + chndl->value_67 = 0x0a; /* 3*7300/8 */ + chndl->value_68 = 0xb1; + break; + case 150: + chndl->value_67 = 0x15; /* 3*7300/4 */ + chndl->value_68 = 0x63; + break; + case 300: + chndl->value_67 = 0x2a; /* 3*7300/2 */ + chndl->value_68 = 0xc6; + break; + case 600: + chndl->value_67 = 0x55; /* 3*7300 */ + chndl->value_68 = 0x8c; + break; + case 1200: + chndl->value_67 = 0xab; /* 6*7300 */ + chndl->value_68 = 0x18; + } + + cp2155_block6 (fd, 0x12, 0x83); + cp2155_set (fd, 0x90, 0xf8); + cp2155_block6 (fd, 0x12, 0x83); +/* start preparing real scan */ + cp2155_set (fd, 0x01, 0x29); + cp2155_block8 (fd); + cp2155_set (fd, 0x01, 0x29); + cp2155_set_gamma (fd); + + switch (chndl->val[opt_resolution].w) + { + case 75: + startblob0075 (chndl, buf); + break; + case 150: + startblob0150 (chndl, buf); + break; + case 300: + startblob0300 (chndl, buf); + break; + case 600: + cp2155_set_gamma600 (fd); + startblob0600 (chndl, buf); + break; + case 1200: + startblob1200 (chndl, buf); + } +} + +/* Wait until data ready */ +static long +wait_for_data (CANON_Handle * chndl) +{ + int fd; + fd = chndl->fd; + time_t start_time = time (NULL); + long size; + byte value; + + DBG (12, "waiting...\n"); + + while (1) + { + size = 0; + cp2155_get (fd, 0x46, &value); + DBG (1, "home sensor: %02x\n", value); + if (value == 0) + { + send_start_blob (chndl); + cp2155_get (fd, 0x46, &value); + DBG (1, "home sensor: %02x\n", value); + } + + if (cp2155_get (fd, 0xa5, &value) != SANE_STATUS_GOOD) + { + return -1; + } + + size += value; + + if (cp2155_get (fd, 0xa6, &value) != SANE_STATUS_GOOD) + { + return -1; + } + + size <<= 8; + size += value; + + if (cp2155_get (fd, 0xa7, &value) != SANE_STATUS_GOOD) + { + return -1; + } + + size <<= 8; + size += value; + + if (size != 0) + { + return 2 * size; + } + + /* Give it 5 seconds */ + if ((time (NULL) - start_time) > 5) + { + DBG (1, "wait_for_data: timed out (%ld)\n", size); + return -1; + } + + usleep (1 * MSEC); + } +} + +static void +go_home_without_wait (int fd) +{ + byte value; + cp2155_get (fd, 0x46, &value); + if (value == 0x08) + { + return; + } + cp2155_block6 (fd, 0x12, 0xc1); + cp2155_set (fd, 0x01, 0x29); + cp2155_block8 (fd); + cp2155_set (fd, 0x01, 0x29); + cp2155_set_gamma (fd); + cp2155_block5 (fd, 0x03); + cp2155_set_regs (fd, cp2155_set_regs_data6); + cp2155_set_slope (fd, 0x030000, cp2155_slope09_back, 0x01f4); + cp2155_set_slope (fd, 0x030200, cp2155_slope09_back, 0x01f4); + cp2155_set_slope (fd, 0x030400, cp2155_slope10_back, 0x0018); + cp2155_set_slope (fd, 0x030600, cp2155_slope09_back, 0x01f4); + cp2155_set_slope (fd, 0x030800, cp2155_slope10_back, 0x0018); + + cp2155_motor (fd, 0x05, 0x35); +} + + +static int +go_home (int fd) +{ + byte value; + cp2155_get (fd, 0x46, &value); + if (value == 0x08) + { + return 0; + } + + go_home_without_wait (fd); + + while (1) + { + usleep (200 * MSEC); + cp2155_get (fd, 0x46, &value); + DBG (1, "home sensor: %02x\n", value); + + if (value == 0x08) + { + break; + } + } + return 0; +} + + +/* Scanner init, called at calibration and scan time. + Returns: + 1 if this was the first time the scanner was plugged in, + 0 afterward, and + -1 on error. */ + +static int +init (CANON_Handle * chndl) +{ + int fd = chndl->fd; + byte value; + int result = 0; + + cp2155_get (fd, 0xd0, &value); + /* Detect if scanner is plugged in */ + if (value != 0x81 && value != 0x40) + { + DBG (0, "INIT: unexpected value: %x\n", value); + } + + if (value == 0x00) + { + return -1; + } + + cp2155_set (fd, 0x02, 0x01); + cp2155_set (fd, 0x02, 0x00); + cp2155_set (fd, 0x01, 0x00); + cp2155_set (fd, 0x01, 0x28); + cp2155_set (fd, 0x90, 0x4f); + cp2155_set (fd, 0x92, 0xff); + cp2155_set (fd, 0x93, 0x00); + cp2155_set (fd, 0x91, 0x1f); + cp2155_set (fd, 0x95, 0x1f); + cp2155_set (fd, 0x97, 0x1f); + cp2155_set (fd, 0x9b, 0x00); + cp2155_set (fd, 0x9c, 0x07); + cp2155_set (fd, 0x90, 0x4d); + cp2155_set (fd, 0x90, 0xcd); + cp2155_set (fd, 0x90, 0xcc); + cp2155_set (fd, 0x9b, 0x01); + cp2155_set (fd, 0xa0, 0x04); + cp2155_set (fd, 0xa0, 0x05); + cp2155_set (fd, 0x01, 0x28); + cp2155_set (fd, 0x04, 0x0c); + cp2155_set (fd, 0x05, 0x00); + cp2155_set (fd, 0x06, 0x00); + cp2155_set (fd, 0x98, 0x00); + cp2155_set (fd, 0x98, 0x00); + cp2155_set (fd, 0x98, 0x02); + cp2155_set (fd, 0x99, 0x28); + cp2155_set (fd, 0x9a, 0x03); + cp2155_set (fd, 0x80, 0x10); + cp2155_set (fd, 0x8d, 0x00); + cp2155_set (fd, 0x8d, 0x04); + + cp2155_set (fd, 0x85, 0x00); + cp2155_set (fd, 0x87, 0x00); + cp2155_set (fd, 0x88, 0x70); + + cp2155_set (fd, 0x85, 0x03); + cp2155_set (fd, 0x87, 0x00); + cp2155_set (fd, 0x88, 0x28); + + cp2155_set (fd, 0x85, 0x06); + cp2155_set (fd, 0x87, 0x00); + cp2155_set (fd, 0x88, 0x28); + + + DBG (1, "INIT state: %0d\n", result); + return result; +} + +/* Scan and save the resulting image as r,g,b non-interleaved PPM file */ +static SANE_Status +preread (CANON_Handle * chndl, SANE_Byte * data, FILE * fp) +{ + SANE_Status status = SANE_STATUS_GOOD; + + static byte linebuf[0x40000]; + byte readbuf[0xf000]; + int fd = chndl->fd; + long width = chndl->params.pixels_per_line; + /* set width to next multiple of 0x10 */ + while ((width % 0x10) != 0xf) + { + width++; + } + + width++; + + byte *srcptr = readbuf; + static byte *dstptr = linebuf; + byte *endptr = linebuf + 3 * width; /* Red line + Green line + Blue line */ + long datasize = 0; + static long line = 0; + size_t offset = 0; + size_t bytes_written; + static byte slot = 0; + + /* Data coming back is "width" bytes Red data, width bytes Green, + width bytes Blue, repeat for "height" lines. */ +/* while (line < height) process one buffer from the scanner */ + long startline = line; + + if (line >= (chndl->y1) * chndl->val[opt_resolution].w / 600 + + chndl->params.lines) + { + status = SANE_STATUS_EOF; + init (chndl); + line = 0; + slot = 0; + dstptr = linebuf; + return status; + } + datasize = wait_for_data (chndl); + + if (datasize < 0) + { + DBG (1, "no data\n"); + status = SANE_STATUS_EOF; + return status; + } + + if (datasize > 0xf000) + { + datasize = 0xf000; + } + + DBG (12, "scan line %ld %ld\n", line, datasize); + + cp2155_set (fd, 0x72, (datasize >> 8) & 0xff); + cp2155_set (fd, 0x73, (datasize) & 0xff); + + status = cp2155_read (fd, readbuf, datasize); + + if (status != SANE_STATUS_GOOD) + { + status = SANE_STATUS_INVAL; + return status; + } + + /* Contorsions to convert data from line-by-line RGB to byte-by-byte RGB, + without reading in the whole buffer first. One image line is + constructed in buffer linebuf and written to temp file if complete. */ + int idx = 0; + srcptr = readbuf; + + while (idx < datasize) + { + *dstptr = (byte) * srcptr; + idx++; + srcptr += 1; + dstptr += 3; + + if (dstptr >= endptr) /* line of one color complete */ + { + slot++; /* next color for this line */ + dstptr = linebuf + slot; /* restart shortly after beginning */ + if (slot == 3) /* all colors done */ + { + slot = 0; /* back to first color */ + dstptr = linebuf; /* back to beginning of line */ + line++; /* number of line just completed */ + /* use scanner->width instead of width to remove pad bytes */ + if (line > (chndl->y1) * chndl->val[opt_resolution].w / 600) + { + if (chndl->params.format == SANE_FRAME_RGB) + { + memcpy (data + offset, linebuf, 3 * chndl->width); + offset += 3 * chndl->width; + } + else + { + int grayvalue; + int lineelement = 0; + while (lineelement < chndl->width) + { + grayvalue = linebuf[3 * lineelement] + + linebuf[3 * lineelement + 1] + + linebuf[3 * lineelement + 2]; + grayvalue /= 3; + if (chndl->params.depth == 8) /* gray */ + { + data[offset + lineelement] = (byte) grayvalue; + } + else /* lineart */ + { + if (lineelement % 8 == 0) + { + data[offset + (lineelement >> 3)] = 0; + } + if ((byte) grayvalue < + chndl->absolute_threshold) + { + data[offset + (lineelement >> 3)] |= + (1 << (7 - lineelement % 8)); + } + } + lineelement++; + } + offset += chndl->params.bytes_per_line; + } + DBG (6, "line %ld written...\n", line); + } + + if (line == (chndl->y1) * chndl->val[opt_resolution].w / 600 + + chndl->params.lines) + { + break; + } + + } + } + } /* one readbuf processed */ + bytes_written = fwrite (data, 1, offset, fp); + DBG (6, "%ld bytes written\n", bytes_written); + if (bytes_written != offset) + { + status = SANE_STATUS_IO_ERROR; + } + DBG (6, "%ld lines from readbuf\n", line - startline); + return status; /* to escape from this loop + after processing only one data buffer */ +} + +/* Scan and save the resulting image as r,g,b non-interleaved PPM file */ +static SANE_Status +do_scan (CANON_Handle * chndl) +{ + SANE_Status status = SANE_STATUS_GOOD; + SANE_Byte outbuf[0x40000]; + FILE *fp; + fp = fopen (chndl->fname, "w"); + if (!fp) + { + DBG (1, "err:%s when opening %s\n", strerror (errno), chndl->fname); + return SANE_STATUS_IO_ERROR; + } + int fd = chndl->fd; + long width = chndl->params.pixels_per_line; + if (chndl->val[opt_resolution].w < 600) + { + width = width * 600 / chndl->val[opt_resolution].w; + } + /* set width to next multiple of 0x10 */ + while ((width % 0x10) != 0xf) + { + width++; + } + + long x_start; + long x_end; + long left_edge = 0x69; + switch (chndl->val[opt_resolution].w) + { + case 75: + case 150: + case 300: + case 600: + left_edge = 0x69; + break; + case 1200: + left_edge = 0x87; + } + x_start = left_edge + chndl->x1 * chndl->val[opt_resolution].w / 600; + if (chndl->val[opt_resolution].w < 600) + { + x_start = left_edge + chndl->x1; + } + x_end = x_start + (width); + width++; + + chndl->value_08 = (x_start >> 8) & 0xff; + chndl->value_09 = (x_start) & 0xff; + chndl->value_0a = (x_end >> 8) & 0xff; + chndl->value_0b = (x_end) & 0xff; + + DBG (3, "val_08: %02x\n", chndl->value_08); + DBG (3, "val_09: %02x\n", chndl->value_09); + DBG (3, "val_0a: %02x\n", chndl->value_0a); + DBG (3, "val_0b: %02x\n", chndl->value_0b); + DBG (3, "chndl->width: %04lx\n", chndl->width); + + send_start_blob (chndl); + + while (status == SANE_STATUS_GOOD) + { + status = preread (chndl, outbuf, fp); + } + go_home_without_wait (fd); + + if (status == SANE_STATUS_EOF) + { + status = SANE_STATUS_GOOD; + } + + fclose (fp); + DBG (6, "created scan file %s\n", chndl->fname); + + return status; +} + +/* Scan sequence */ +/* resolution is 75,150,300,600,1200 + scan coordinates in 600-dpi pixels */ + +static SANE_Status +scan (CANON_Handle * chndl) +{ + SANE_Status status = SANE_STATUS_GOOD; + /* Resolution: dpi 75, 150, 300, 600, 1200 */ + switch (chndl->val[opt_resolution].w) + { + case 75: + case 150: + case 300: + case 600: + case 1200: + break; + default: + chndl->val[opt_resolution].w = 600; + } + + chndl->width = chndl->params.pixels_per_line; + chndl->height = + (chndl->y2 - chndl->y1) * chndl->val[opt_resolution].w / 600; + DBG (1, "dpi=%d\n", chndl->val[opt_resolution].w); + DBG (1, "x1=%d y1=%d\n", chndl->x1, chndl->y1); + DBG (1, "x2=%d y2=%d\n", chndl->x2, chndl->y2); + DBG (1, "width=%ld height=%ld\n", chndl->width, chndl->height); + + CHK (do_scan (chndl)); + return status; +} + + +static SANE_Status +CANON_set_scan_parameters (CANON_Handle * chndl) +{ + int left; + int top; + int right; + int bottom; + + double leftf; + double rightf; + double topf; + double bottomf; + + double widthf; + double heightf; + int widthi; + int heighti; + + int top_edge = 7; + if (chndl->val[opt_resolution].w < 300) + { + top_edge = 0; + } + + left = SANE_UNFIX (chndl->val[opt_tl_x].w) / MM_IN_INCH * 600; + top = (top_edge + SANE_UNFIX (chndl->val[opt_tl_y].w)) / MM_IN_INCH * 600; + right = SANE_UNFIX (chndl->val[opt_br_x].w) / MM_IN_INCH * 600; + bottom = + (top_edge + SANE_UNFIX (chndl->val[opt_br_y].w)) / MM_IN_INCH * 600; + + leftf = SANE_UNFIX (chndl->val[opt_tl_x].w); + rightf = SANE_UNFIX (chndl->val[opt_br_x].w); + topf = SANE_UNFIX (chndl->val[opt_tl_y].w); + bottomf = SANE_UNFIX (chndl->val[opt_br_y].w); + + widthf = (rightf - leftf) / MM_PER_INCH * 600; + widthi = (int) widthf; + heightf = (bottomf - topf) / MM_PER_INCH * 600; + heighti = (int) heightf; + + DBG (2, "CANON_set_scan_parameters:\n"); + DBG (2, "widthf = %f\n", widthf); + DBG (2, "widthi = %d\n", widthi); + DBG (2, "in 600dpi pixels:\n"); + DBG (2, "left = %d, top = %d\n", left, top); + DBG (2, "right = %d, bottom = %d\n", right, bottom); + + /* Validate the input parameters */ + if ((left < 0) || (right > CANON_MAX_WIDTH)) + { + return SANE_STATUS_INVAL; + } + + if ((top < 0) || (bottom > CANON_MAX_HEIGHT)) + { + return SANE_STATUS_INVAL; + } + + if (((right - left) < 10) || ((bottom - top) < 10)) + { + return SANE_STATUS_INVAL; + } + + if ((chndl->val[opt_resolution].w != 75) && + (chndl->val[opt_resolution].w != 150) && + (chndl->val[opt_resolution].w != 300) && + (chndl->val[opt_resolution].w != 600) && + (chndl->val[opt_resolution].w != 1200)) + { + return SANE_STATUS_INVAL; + } + + /* Store params */ + chndl->x1 = left; + chndl->x2 = left + widthi; + chndl->y1 = top; + chndl->y2 = top + heighti; + chndl->absolute_threshold = (chndl->val[opt_threshold].w * 255) / 100; + return SANE_STATUS_GOOD; +} + + +static SANE_Status +CANON_close_device (CANON_Handle * scan) +{ + DBG (3, "CANON_close_device:\n"); + sanei_usb_close (scan->fd); + return SANE_STATUS_GOOD; +} + + +static SANE_Status +CANON_open_device (CANON_Handle * scan, const char *dev) +{ + SANE_Word vendor; + SANE_Word product; + SANE_Status res; + + DBG (3, "CANON_open_device: `%s'\n", dev); + + scan->fname = NULL; + scan->fp = NULL; + + res = sanei_usb_open (dev, &scan->fd); + + if (res != SANE_STATUS_GOOD) + { + DBG (1, "CANON_open_device: couldn't open device `%s': %s\n", dev, + sane_strstatus (res)); + return res; + } + + scan->product = "unknown"; + +#ifndef NO_AUTODETECT + /* We have opened the device. Check that it is a USB scanner. */ + if (sanei_usb_get_vendor_product (scan->fd, &vendor, &product) != + SANE_STATUS_GOOD) + { + DBG (1, "CANON_open_device: sanei_usb_get_vendor_product failed\n"); + /* This is not a USB scanner, or SANE or the OS doesn't support it. */ + sanei_usb_close (scan->fd); + scan->fd = -1; + return SANE_STATUS_UNSUPPORTED; + } + + /* Make sure we have a CANON scanner */ + if (vendor == 0x04a9) + { + scan->product = "Canon"; + + if (product == 0x2224) + { + scan->product = "CanoScan LiDE 600F"; + } + else if (product == 0x2225) + { + scan->product = "CanoScan LiDE 70"; + } + else + { + DBG (1, "CANON_open_device: incorrect vendor/product (0x%x/0x%x)\n", + vendor, product); + sanei_usb_close (scan->fd); + scan->fd = -1; + return SANE_STATUS_UNSUPPORTED; + } + } +#endif + + return SANE_STATUS_GOOD; +} + + +static const char * +CANON_get_device_name (CANON_Handle * chndl) +{ + return chndl->product; +} + + +static SANE_Status +CANON_finish_scan (CANON_Handle * chndl) +{ + DBG (3, "CANON_finish_scan:\n"); + + if (chndl->fp) + { + fclose (chndl->fp); + } + + chndl->fp = NULL; + + /* remove temp file */ + if (chndl->fname) + { + DBG (4, "removing temp file %s\n", chndl->fname); + unlink (chndl->fname); + free (chndl->fname); + } + + chndl->fname = NULL; + return SANE_STATUS_GOOD; +} + + +static SANE_Status +CANON_start_scan (CANON_Handle * chndl) +{ + SANE_Status status; + int result; + int fd; + DBG (3, "CANON_start_scan called\n"); + + /* choose a temp file name for scan data */ + chndl->fname = strdup ("/tmp/scan.XXXXXX"); + fd = mkstemp (chndl->fname); + + if (!fd) + { + return SANE_STATUS_IO_ERROR; + } + + close (fd); + + /* check if calibration needed */ + result = init (chndl); + + if (result < 0) + { + DBG (1, "Can't talk on USB.\n"); + return SANE_STATUS_IO_ERROR; + } + + go_home (chndl->fd); + + /* scan */ + if ((status = scan (chndl)) != SANE_STATUS_GOOD) + { + CANON_finish_scan (chndl); + return status; + } + + /* read the temp file back out */ + chndl->fp = fopen (chndl->fname, "r"); + DBG (4, "reading %s\n", chndl->fname); + + if (!chndl->fp) + { + DBG (1, "open %s", chndl->fname); + return SANE_STATUS_IO_ERROR; + } + + return SANE_STATUS_GOOD; +} + + +static SANE_Status +CANON_read (CANON_Handle * chndl, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length) +{ + SANE_Status status; + int read_len; + + DBG (5, "CANON_read called\n"); + + if (!chndl->fp) + { + return SANE_STATUS_INVAL; + } + + read_len = fread (data, 1, max_length, chndl->fp); + /* return some data */ + if (read_len > 0) + { + *length = read_len; + DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length); + return SANE_STATUS_GOOD; + } + + /* EOF or file err */ + *length = 0; + + if (feof (chndl->fp)) + { + DBG (4, "EOF\n"); + status = SANE_STATUS_EOF; + } + else + { + DBG (4, "IO ERR\n"); + status = SANE_STATUS_IO_ERROR; + } + + CANON_finish_scan (chndl); + DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length); + return status; +} diff --git a/backend/canon_lide70.c b/backend/canon_lide70.c new file mode 100644 index 0000000..100a45f --- /dev/null +++ b/backend/canon_lide70.c @@ -0,0 +1,960 @@ +/* sane - Scanner Access Now Easy. + + BACKEND canon_lide70 + + Copyright (C) 2019 Juergen Ernst and pimvantend. + + 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. + + This file implements a SANE backend for the Canon CanoScan LiDE 70 */ + +#define BUILD 0 +#define MM_IN_INCH 25.4 + +#include "../include/sane/config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_config.h" +#include "../include/sane/sanei_usb.h" +#define BACKEND_NAME canon_lide70 +#define CANONUSB_CONFIG_FILE "canon_lide70.conf" +#include "../include/sane/sanei_backend.h" + +typedef enum +{ + opt_num_opts = 0, + opt_mode_group, + opt_threshold, + opt_mode, + opt_resolution, + opt_non_blocking, + opt_geometry_group, + opt_tl_x, + opt_tl_y, + opt_br_x, + opt_br_y, + /* must come last: */ + num_options +} +canon_opts; + +#include "canon_lide70-common.c" + +static size_t +max_string_size (const SANE_String_Const strings[]) +{ + size_t size, max_size = 0; + SANE_Int i; + + for (i = 0; strings[i]; ++i) + { + size = strlen (strings[i]) + 1; + if (size > max_size) + max_size = size; + } + return max_size; +} + +static SANE_String_Const mode_list[] = { + SANE_VALUE_SCAN_MODE_COLOR, + SANE_VALUE_SCAN_MODE_GRAY, + SANE_VALUE_SCAN_MODE_LINEART, + 0 +}; + +static SANE_Fixed init_tl_x = SANE_FIX (0.0); +static SANE_Fixed init_tl_y = SANE_FIX (0.0); +static SANE_Fixed init_br_x = SANE_FIX (80.0); +static SANE_Fixed init_br_y = SANE_FIX (100.0); +static SANE_Int init_threshold = 75; +static SANE_Int init_resolution = 600; +static SANE_String init_mode = SANE_VALUE_SCAN_MODE_COLOR; +static SANE_Int init_graymode = 0; +static SANE_Bool init_non_blocking = SANE_FALSE; + +/*-----------------------------------------------------------------*/ +/* +Scan range +*/ + +static const SANE_Range widthRange = { + 0, /* minimum */ + SANE_FIX (CANON_MAX_WIDTH * MM_IN_INCH / 600), /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range heightRange = { + 0, /* minimum */ +/* SANE_FIX (CANON_MAX_HEIGHT * MM_IN_INCH / 600 - TOP_EDGE ), maximum */ + SANE_FIX (297.0), + 0 /* quantization */ +}; + +static const SANE_Range threshold_range = { + 0, + 100, + 1 +}; + +static SANE_Int resolution_list[] = { 5, + 75, + 150, + 300, + 600, + 1200 +}; + +typedef struct Canon_Device +{ + struct Canon_Device *next; + SANE_String name; + SANE_Device sane; +} +Canon_Device; + +/* Canon_Scanner is the type used for the sane handle */ +typedef struct Canon_Scanner +{ + struct Canon_Scanner *next; + Canon_Device *device; + CANON_Handle scan; +} +Canon_Scanner; + +static int num_devices = 0; +static const SANE_Device **devlist = NULL; +static Canon_Device *first_dev = NULL; +static Canon_Scanner *first_handle = NULL; + +/*-----------------------------------------------------------------*/ +static SANE_Status +attach_scanner (const char *devicename, Canon_Device ** devp) +{ + CANON_Handle scan; + Canon_Device *dev; + SANE_Status status; + + DBG (3, "attach_scanner: %s\n", devicename); + + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devicename) == 0) + { + if (devp) + *devp = dev; + return SANE_STATUS_GOOD; + } + } + + dev = malloc (sizeof (*dev)); + if (!dev) + return SANE_STATUS_NO_MEM; + memset (dev, '\0', sizeof (Canon_Device)); /* clear structure */ + + DBG (4, "attach_scanner: opening %s\n", devicename); + + status = CANON_open_device (&scan, devicename); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "ERROR: attach_scanner: opening %s failed\n", devicename); + free (dev); + return status; + } + dev->name = strdup (devicename); + dev->sane.name = dev->name; + dev->sane.vendor = "CANON"; + dev->sane.model = CANON_get_device_name (&scan); + dev->sane.type = "flatbed scanner"; + CANON_close_device (&scan); + + ++num_devices; + dev->next = first_dev; + first_dev = dev; + + if (devp) + *devp = dev; + return SANE_STATUS_GOOD; +} + + +/* callback function for sanei_usb_attach_matching_devices */ +static SANE_Status +attach_one (const char *name) +{ + attach_scanner (name, 0); + return SANE_STATUS_GOOD; +} + + +/* Find our devices */ +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + char config_line[PATH_MAX]; + size_t len; + FILE *fp; + + DBG_INIT (); + +#if 0 + DBG_LEVEL = 10; +#endif + + DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", + version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); + DBG (1, "sane_init: SANE Canon LiDE70 backend version %d.%d.%d from %s\n", + V_MAJOR, V_MINOR, BUILD, PACKAGE_STRING); + + if (version_code) + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD); + + sanei_usb_init (); + + fp = sanei_config_open (CANONUSB_CONFIG_FILE); + + if (!fp) + { + /* no config-file: try these */ + attach_scanner ("/dev/scanner", 0); + attach_scanner ("/dev/usbscanner", 0); + attach_scanner ("/dev/usb/scanner", 0); + return SANE_STATUS_GOOD; + } + + DBG (3, "reading configure file %s\n", CANONUSB_CONFIG_FILE); + + while (sanei_config_read (config_line, sizeof (config_line), fp)) + { + if (config_line[0] == '#') + continue; /* ignore line comments */ + + len = strlen (config_line); + + if (!len) + continue; /* ignore empty lines */ + + DBG (4, "attach_matching_devices(%s)\n", config_line); + sanei_usb_attach_matching_devices (config_line, attach_one); + } + + DBG (4, "finished reading configure file\n"); + + fclose (fp); + + return SANE_STATUS_GOOD; +} + + +void +sane_exit (void) +{ + Canon_Device *dev, *next; + + DBG (3, "sane_exit\n"); + + for (dev = first_dev; dev; dev = next) + { + next = dev->next; + free (dev->name); + free (dev); + } + + if (devlist) + free (devlist); + return; +} + + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + Canon_Device *dev; + int i; + + DBG (3, "sane_get_devices(local_only = %d)\n", local_only); + + if (devlist) + free (devlist); + + devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); + if (!devlist) + return SANE_STATUS_NO_MEM; + + i = 0; + + for (dev = first_dev; i < num_devices; dev = dev->next) + devlist[i++] = &dev->sane; + + devlist[i++] = 0; + + *device_list = devlist; + + return SANE_STATUS_GOOD; +} + +static SANE_Status +init_options (CANON_Handle * chndl) +{ + SANE_Option_Descriptor *od; + + DBG (2, "begin init_options: chndl=%p\n", (void *) chndl); + + /* opt_num_opts */ + od = &chndl->opt[opt_num_opts]; + od->name = ""; + od->title = SANE_TITLE_NUM_OPTIONS; + od->desc = SANE_DESC_NUM_OPTIONS; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + chndl->val[opt_num_opts].w = num_options; + + DBG (2, "val[opt_num_opts]: %d\n", chndl->val[opt_num_opts].w); + + /* opt_mode_group */ + od = &chndl->opt[opt_mode_group]; + od->name = ""; + od->title = SANE_I18N ("Scan Mode"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + chndl->val[opt_mode_group].w = 0; + + /* opt_mode */ + od = &chndl->opt[opt_mode]; + od->name = SANE_NAME_SCAN_MODE; + od->title = SANE_TITLE_SCAN_MODE; + od->desc = SANE_DESC_SCAN_MODE; + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (mode_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = mode_list; + chndl->val[opt_mode].s = malloc (od->size); + if (!chndl->val[opt_mode].s) + return SANE_STATUS_NO_MEM; + strcpy (chndl->val[opt_mode].s, init_mode); + chndl->graymode = init_graymode; + + /* opt_threshold */ + od = &chndl->opt[opt_threshold]; + od->name = SANE_NAME_THRESHOLD; + od->title = SANE_TITLE_THRESHOLD; + od->desc = SANE_DESC_THRESHOLD; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_PERCENT; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &threshold_range; + chndl->val[opt_threshold].w = init_threshold; + + /* opt_resolution */ + od = &chndl->opt[opt_resolution]; + od->name = SANE_NAME_SCAN_RESOLUTION; + od->title = SANE_TITLE_SCAN_RESOLUTION; + od->desc = SANE_DESC_SCAN_RESOLUTION; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_DPI; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_WORD_LIST; + od->constraint.word_list = resolution_list; + chndl->val[opt_resolution].w = init_resolution; + + /* opt_non_blocking */ + od = &chndl->opt[opt_non_blocking]; + od->name = "non-blocking"; + od->title = SANE_I18N ("Use non-blocking IO"); + od->desc = SANE_I18N ("Use non-blocking IO for sane_read() if supported " + "by the frontend."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + chndl->val[opt_non_blocking].w = init_non_blocking; + + /* opt_geometry_group */ + od = &chndl->opt[opt_geometry_group]; + od->name = ""; + od->title = SANE_I18N ("Geometry"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + chndl->val[opt_geometry_group].w = 0; + + /* opt_tl_x */ + od = &chndl->opt[opt_tl_x]; + od->name = SANE_NAME_SCAN_TL_X; + od->title = SANE_TITLE_SCAN_TL_X; + od->desc = SANE_DESC_SCAN_TL_X; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &widthRange; + chndl->val[opt_tl_x].w = init_tl_x; + + /* opt_tl_y */ + od = &chndl->opt[opt_tl_y]; + od->name = SANE_NAME_SCAN_TL_Y; + od->title = SANE_TITLE_SCAN_TL_Y; + od->desc = SANE_DESC_SCAN_TL_Y; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &heightRange; + chndl->val[opt_tl_y].w = init_tl_y; + + /* opt_br_x */ + od = &chndl->opt[opt_br_x]; + od->name = SANE_NAME_SCAN_BR_X; + od->title = SANE_TITLE_SCAN_BR_X; + od->desc = SANE_DESC_SCAN_BR_X; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &widthRange; + chndl->val[opt_br_x].w = init_br_x; + + /* opt_br_y */ + od = &chndl->opt[opt_br_y]; + od->name = SANE_NAME_SCAN_BR_Y; + od->title = SANE_TITLE_SCAN_BR_Y; + od->desc = SANE_DESC_SCAN_BR_Y; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &heightRange; + chndl->val[opt_br_y].w = init_br_y; + + DBG (2, "end init_options: chndl=%p\n", (void *) chndl); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + Canon_Device *dev; + SANE_Status status; + Canon_Scanner *scanner; + + DBG (3, "sane_open\n"); + + if (devicename[0]) /* search for devicename */ + { + DBG (4, "sane_open: devicename=%s\n", devicename); + + for (dev = first_dev; dev; dev = dev->next) + if (strcmp (dev->sane.name, devicename) == 0) + break; + + if (!dev) + { + status = attach_scanner (devicename, &dev); + + if (status != SANE_STATUS_GOOD) + return status; + } + } + else + { + DBG (2, "sane_open: no devicename, opening first device\n"); + dev = first_dev; + } + + if (!dev) + return SANE_STATUS_INVAL; + + scanner = malloc (sizeof (*scanner)); + + if (!scanner) + return SANE_STATUS_NO_MEM; + + memset (scanner, 0, sizeof (*scanner)); + scanner->device = dev; + + status = CANON_open_device (&scanner->scan, dev->sane.name); + + if (status != SANE_STATUS_GOOD) + { + free (scanner); + return status; + } + + status = init_options (&scanner->scan); + + *handle = scanner; + + /* insert newly opened handle into list of open handles: */ + scanner->next = first_handle; + + first_handle = scanner; + + return status; +} + +static void +print_options (CANON_Handle * chndl) +{ + SANE_Option_Descriptor *od; + SANE_Word option_number; + SANE_Char caps[1024]; + + for (option_number = 0; option_number < num_options; option_number++) + { + od = &chndl->opt[option_number]; + DBG (50, "-----> number: %d\n", option_number); + DBG (50, " name: `%s'\n", od->name); + DBG (50, " title: `%s'\n", od->title); + DBG (50, " description: `%s'\n", od->desc); + DBG (50, " type: %s\n", + od->type == SANE_TYPE_BOOL ? "SANE_TYPE_BOOL" : + od->type == SANE_TYPE_INT ? "SANE_TYPE_INT" : + od->type == SANE_TYPE_FIXED ? "SANE_TYPE_FIXED" : + od->type == SANE_TYPE_STRING ? "SANE_TYPE_STRING" : + od->type == SANE_TYPE_BUTTON ? "SANE_TYPE_BUTTON" : + od->type == SANE_TYPE_GROUP ? "SANE_TYPE_GROUP" : "unknown"); + DBG (50, " unit: %s\n", + od->unit == SANE_UNIT_NONE ? "SANE_UNIT_NONE" : + od->unit == SANE_UNIT_PIXEL ? "SANE_UNIT_PIXEL" : + od->unit == SANE_UNIT_BIT ? "SANE_UNIT_BIT" : + od->unit == SANE_UNIT_MM ? "SANE_UNIT_MM" : + od->unit == SANE_UNIT_DPI ? "SANE_UNIT_DPI" : + od->unit == SANE_UNIT_PERCENT ? "SANE_UNIT_PERCENT" : + od->unit == SANE_UNIT_MICROSECOND ? "SANE_UNIT_MICROSECOND" : + "unknown"); + DBG (50, " size: %d\n", od->size); + caps[0] = '\0'; + if (od->cap & SANE_CAP_SOFT_SELECT) + strcat (caps, "SANE_CAP_SOFT_SELECT "); + if (od->cap & SANE_CAP_HARD_SELECT) + strcat (caps, "SANE_CAP_HARD_SELECT "); + if (od->cap & SANE_CAP_SOFT_DETECT) + strcat (caps, "SANE_CAP_SOFT_DETECT "); + if (od->cap & SANE_CAP_EMULATED) + strcat (caps, "SANE_CAP_EMULATED "); + if (od->cap & SANE_CAP_AUTOMATIC) + strcat (caps, "SANE_CAP_AUTOMATIC "); + if (od->cap & SANE_CAP_INACTIVE) + strcat (caps, "SANE_CAP_INACTIVE "); + if (od->cap & SANE_CAP_ADVANCED) + strcat (caps, "SANE_CAP_ADVANCED "); + DBG (50, " capabilities: %s\n", caps); + DBG (50, "constraint type: %s\n", + od->constraint_type == SANE_CONSTRAINT_NONE ? + "SANE_CONSTRAINT_NONE" : + od->constraint_type == SANE_CONSTRAINT_RANGE ? + "SANE_CONSTRAINT_RANGE" : + od->constraint_type == SANE_CONSTRAINT_WORD_LIST ? + "SANE_CONSTRAINT_WORD_LIST" : + od->constraint_type == SANE_CONSTRAINT_STRING_LIST ? + "SANE_CONSTRAINT_STRING_LIST" : "unknown"); + if (od->type == SANE_TYPE_INT) + DBG (50, " value: %d\n", chndl->val[option_number].w); + else if (od->type == SANE_TYPE_FIXED) + DBG (50, " value: %f\n", + SANE_UNFIX (chndl->val[option_number].w)); + else if (od->type == SANE_TYPE_STRING) + DBG (50, " value: %s\n", chndl->val[option_number].s); + } +} + +void +sane_close (SANE_Handle handle) +{ + Canon_Scanner *prev, *scanner; + SANE_Status res; + + DBG (3, "sane_close\n"); + + scanner = handle; + print_options (&scanner->scan); + + if (!first_handle) + { + DBG (1, "ERROR: sane_close: no handles opened\n"); + return; + } + + /* remove handle from list of open handles: */ + + prev = NULL; + + for (scanner = first_handle; scanner; scanner = scanner->next) + { + if (scanner == handle) + break; + + prev = scanner; + } + + if (!scanner) + { + DBG (1, "ERROR: sane_close: invalid handle %p\n", handle); + return; /* oops, not a handle we know about */ + } + + if (prev) + prev->next = scanner->next; + else + first_handle = scanner->next; + + res = CANON_close_device (&scanner->scan); + DBG (3, "CANON_close_device returned: %d\n", res); + free (scanner); +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Canon_Scanner *scanner = handle; + CANON_Handle *chndl = &scanner->scan; + + + DBG (4, "sane_get_option_descriptor: handle=%p, option = %d\n", + (void *) handle, option); + if (option < 0 || option >= num_options) + { + DBG (3, "sane_get_option_descriptor: option < 0 || " + "option > num_options\n"); + return 0; + } + + return &chndl->opt[option]; +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, + void *value, SANE_Int * info) +{ + Canon_Scanner *scanner = handle; + CANON_Handle *chndl = &scanner->scan; + + SANE_Int myinfo = 0; + SANE_Status status; + + DBG (4, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", + (void *) handle, option, action, (void *) value, (void *) info); + + if (option < 0 || option >= num_options) + { + DBG (1, "sane_control_option: option < 0 || option > num_options\n"); + return SANE_STATUS_INVAL; + } + + if (!SANE_OPTION_IS_ACTIVE (chndl->opt[option].cap)) + { + DBG (1, "sane_control_option: option is inactive\n"); + return SANE_STATUS_INVAL; + } + + if (chndl->opt[option].type == SANE_TYPE_GROUP) + { + DBG (1, "sane_control_option: option is a group\n"); + return SANE_STATUS_INVAL; + } + + switch (action) + { + case SANE_ACTION_SET_VALUE: + if (!SANE_OPTION_IS_SETTABLE (chndl->opt[option].cap)) + { + DBG (1, "sane_control_option: option is not setable\n"); + return SANE_STATUS_INVAL; + } + status = sanei_constrain_value (&chndl->opt[option], value, &myinfo); + if (status != SANE_STATUS_GOOD) + { + DBG (3, "sane_control_option: sanei_constrain_value returned %s\n", + sane_strstatus (status)); + return status; + } + switch (option) + { + case opt_tl_x: /* Fixed with parameter reloading */ + case opt_tl_y: + case opt_br_x: + case opt_br_y: + if (chndl->val[option].w == *(SANE_Fixed *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, chndl->opt[option].name); + break; + } + chndl->val[option].w = *(SANE_Fixed *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + DBG (4, "sane_control_option: set option %d (%s) to %.0f %s\n", + option, chndl->opt[option].name, + SANE_UNFIX (*(SANE_Fixed *) value), + chndl->opt[option].unit == SANE_UNIT_MM ? "mm" : "dpi"); + break; + case opt_non_blocking: + if (chndl->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, chndl->opt[option].name); + break; + } + chndl->val[option].w = *(SANE_Bool *) value; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, chndl->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_resolution: + case opt_threshold: + if (chndl->val[option].w == *(SANE_Int *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, chndl->opt[option].name); + break; + } + chndl->val[option].w = *(SANE_Int *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + DBG (4, "sane_control_option: set option %d (%s) to %d\n", + option, chndl->opt[option].name, *(SANE_Int *) value); + break; + case opt_mode: + if (strcmp (chndl->val[option].s, value) == 0) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, chndl->opt[option].name); + break; + } + strcpy (chndl->val[option].s, (SANE_String) value); + + if (strcmp (chndl->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == + 0) + { + chndl->opt[opt_threshold].cap &= ~SANE_CAP_INACTIVE; + chndl->graymode = 2; + } + if (strcmp (chndl->val[option].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + chndl->opt[opt_threshold].cap |= SANE_CAP_INACTIVE; + chndl->graymode = 0; + } + if (strcmp (chndl->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) + { + chndl->opt[opt_threshold].cap |= SANE_CAP_INACTIVE; + chndl->graymode = 1; + } + + + myinfo |= SANE_INFO_RELOAD_PARAMS; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, chndl->opt[option].name, (SANE_String) value); + break; + default: + DBG (1, "sane_control_option: trying to set unexpected option\n"); + return SANE_STATUS_INVAL; + } + break; + + case SANE_ACTION_GET_VALUE: + switch (option) + { + case opt_num_opts: + *(SANE_Word *) value = num_options; + DBG (4, "sane_control_option: get option 0, value = %d\n", + num_options); + break; + case opt_tl_x: /* Fixed options */ + case opt_tl_y: + case opt_br_x: + case opt_br_y: + { + *(SANE_Fixed *) value = chndl->val[option].w; + DBG (4, + "sane_control_option: get option %d (%s), value=%.1f %s\n", + option, chndl->opt[option].name, + SANE_UNFIX (*(SANE_Fixed *) value), + chndl->opt[option].unit == + SANE_UNIT_MM ? "mm" : SANE_UNIT_DPI ? "dpi" : ""); + break; + } + case opt_non_blocking: + *(SANE_Bool *) value = chndl->val[option].w; + DBG (4, + "sane_control_option: get option %d (%s), value=%s\n", + option, chndl->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_mode: /* String (list) options */ + strcpy (value, chndl->val[option].s); + DBG (4, "sane_control_option: get option %d (%s), value=`%s'\n", + option, chndl->opt[option].name, (SANE_String) value); + break; + case opt_resolution: + case opt_threshold: + *(SANE_Int *) value = chndl->val[option].w; + DBG (4, "sane_control_option: get option %d (%s), value=%d\n", + option, chndl->opt[option].name, *(SANE_Int *) value); + break; + default: + DBG (1, "sane_control_option: trying to get unexpected option\n"); + return SANE_STATUS_INVAL; + } + break; + default: + DBG (1, "sane_control_option: trying unexpected action %d\n", action); + return SANE_STATUS_INVAL; + } + + if (info) + *info = myinfo; + return SANE_STATUS_GOOD; +} + + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + Canon_Scanner *hndl = handle; /* Eliminate compiler warning */ + CANON_Handle *chndl = &hndl->scan; + + int rc = SANE_STATUS_GOOD; + int w = SANE_UNFIX (chndl->val[opt_br_x].w - + chndl->val[opt_tl_x].w) / MM_IN_INCH * + chndl->val[opt_resolution].w; + int h = + SANE_UNFIX (chndl->val[opt_br_y].w - + chndl->val[opt_tl_y].w) / MM_IN_INCH * + chndl->val[opt_resolution].w; + + DBG (3, "sane_get_parameters\n"); + chndl->params.depth = 8; + chndl->params.last_frame = SANE_TRUE; + chndl->params.pixels_per_line = w; + chndl->params.lines = h; + + if (chndl->graymode == 1) + { + chndl->params.format = SANE_FRAME_GRAY; + chndl->params.bytes_per_line = w; + } + else if (chndl->graymode == 2) + { + chndl->params.format = SANE_FRAME_GRAY; + w /= 8; + + if ((chndl->params.pixels_per_line % 8) != 0) + w++; + + chndl->params.bytes_per_line = w; + chndl->params.depth = 1; + } + else + { + chndl->params.format = SANE_FRAME_RGB; + chndl->params.bytes_per_line = w * 3; + } + + *params = chndl->params; + DBG (1, "%d\n", chndl->params.format); + return rc; +} + + +SANE_Status +sane_start (SANE_Handle handle) +{ + Canon_Scanner *scanner = handle; + CANON_Handle *chndl = &scanner->scan; + SANE_Status res; + + DBG (3, "sane_start\n"); + + res = sane_get_parameters (handle, &chndl->params); + res = CANON_set_scan_parameters (&scanner->scan); + + if (res != SANE_STATUS_GOOD) + return res; + + return CANON_start_scan (&scanner->scan); +} + + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length) +{ + Canon_Scanner *scanner = handle; + return CANON_read (&scanner->scan, data, max_length, length); +} + + +void +sane_cancel (SANE_Handle handle) +{ + DBG (3, "sane_cancel: handle = %p\n", handle); + DBG (3, "sane_cancel: cancelling is unsupported in this backend\n"); +} + + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + DBG (3, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, + non_blocking); + if (non_blocking != SANE_FALSE) + return SANE_STATUS_UNSUPPORTED; + return SANE_STATUS_GOOD; +} + + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + handle = handle; /* silence gcc */ + fd = fd; /* silence gcc */ + return SANE_STATUS_UNSUPPORTED; +} diff --git a/backend/canon_lide70.conf.in b/backend/canon_lide70.conf.in new file mode 100644 index 0000000..97b551c --- /dev/null +++ b/backend/canon_lide70.conf.in @@ -0,0 +1,8 @@ +# Options for the canon_lide70 backend + +# Autodetect the Canon CanoScan LiDE 70 +usb 0x04a9 0x2225 + +# device list for non-linux-systems (enable if autodetect fails): +#/dev/scanner +#/dev/usb/scanner0 diff --git a/backend/dll.c b/backend/dll.c index 73ffde4..d78d409 100644 --- a/backend/dll.c +++ b/backend/dll.c @@ -142,6 +142,10 @@ posix_dlsym (void *handle, const char *func) # define PATH_MAX 1024 #endif +#ifndef NAME_MAX +# define NAME_MAX FILENAME_MAX +#endif + #if defined(_WIN32) || defined(HAVE_OS2_H) # define DIR_SEP ";" #else diff --git a/backend/dll.conf.in b/backend/dll.conf.in index 92091cb..cc7dfec 100644 --- a/backend/dll.conf.in +++ b/backend/dll.conf.in @@ -19,6 +19,7 @@ bh canon canon630u canon_dr +canon_lide70 #canon_pp cardscan coolscan diff --git a/backend/dmc.c b/backend/dmc.c index ddc76c3..363a33f 100644 --- a/backend/dmc.c +++ b/backend/dmc.c @@ -512,59 +512,65 @@ DMCInitOptions(DMC_Camera *c) static SANE_Status DMCSetMode(DMC_Camera *c, int mode) { - switch(mode) { + switch (mode) + { case IMAGE_MFI: - c->tl_x_range.min = 0; - c->tl_x_range.max = c->tl_x_range.max; - c->tl_y_range.min = 0; - c->tl_y_range.max = c->tl_y_range.max; - c->br_x_range.min = 800; - c->br_x_range.max = c->br_x_range.max; - c->br_y_range.min = 599; - c->br_y_range.max = c->br_y_range.max; - break; + c->tl_x_range.min = 0; + c->tl_x_range.max = 800; + c->tl_y_range.min = 0; + c->tl_y_range.max = 599; + c->br_x_range.min = c->tl_x_range.min; + c->br_x_range.max = c->tl_x_range.max; + c->br_y_range.min = c->tl_y_range.min; + c->br_y_range.max = c->tl_y_range.max; + break; + case IMAGE_VIEWFINDER: - c->tl_x_range.min = 0; - c->tl_x_range.max = c->tl_x_range.max; - c->tl_y_range.min = 0; - c->tl_y_range.max = c->tl_y_range.max; - c->br_x_range.min = 269; - c->br_x_range.max = c->br_x_range.max; - c->br_y_range.min = 200; - c->br_y_range.max = c->br_y_range.max; - break; + c->tl_x_range.min = 0; + c->tl_x_range.max = 269; + c->tl_y_range.min = 0; + c->tl_y_range.max = 200; + c->br_x_range.min = c->tl_x_range.min; + c->br_x_range.max = c->tl_x_range.max; + c->br_y_range.min = c->tl_y_range.min; + c->br_y_range.max = c->tl_y_range.max; + break; + case IMAGE_RAW: - c->tl_x_range.min = 0; - c->tl_x_range.max = c->tl_x_range.max; - c->tl_y_range.min = 0; - c->tl_y_range.max = c->tl_y_range.max; - c->br_x_range.min = 1598; - c->br_x_range.max = c->br_x_range.max; - c->br_y_range.min = 599; - c->br_y_range.max = c->br_y_range.max; - break; + c->tl_x_range.min = 0; + c->tl_x_range.max = 1598; + c->tl_y_range.min = 0; + c->tl_y_range.max = 599; + c->br_x_range.min = c->tl_x_range.min; + c->br_x_range.max = c->tl_x_range.max; + c->br_y_range.min = c->tl_y_range.min; + c->br_y_range.max = c->tl_y_range.max; + break; + case IMAGE_THUMB: - c->tl_x_range.min = 0; - c->tl_x_range.max = c->tl_x_range.max; - c->tl_y_range.min = 0; - c->tl_y_range.max = c->tl_y_range.max; - c->br_x_range.min = 79; - c->br_x_range.max = c->br_x_range.max; - c->br_y_range.min = 59; - c->br_y_range.max = c->br_y_range.max; - break; + c->tl_x_range.min = 0; + c->tl_x_range.max = 79; + c->tl_y_range.min = 0; + c->tl_y_range.max = 59; + c->br_x_range.min = c->tl_x_range.min; + c->br_x_range.max = c->tl_x_range.max; + c->br_y_range.min = c->tl_y_range.min; + c->br_y_range.max = c->tl_y_range.max; + break; + case IMAGE_SUPER_RES: - c->tl_x_range.min = 0; - c->tl_x_range.max = c->tl_x_range.max; - c->tl_y_range.min = 0; - c->tl_y_range.max = c->tl_y_range.max; - c->br_x_range.min = 1598; - c->br_x_range.max = c->br_x_range.max; - c->br_y_range.min = 1199; - c->br_y_range.max = c->br_y_range.max; - break; + c->tl_x_range.min = 0; + c->tl_x_range.max = 1598; + c->tl_y_range.min = 0; + c->tl_y_range.max = 1199; + c->br_x_range.min = c->tl_x_range.min; + c->br_x_range.max = c->tl_x_range.max; + c->br_y_range.min = c->tl_y_range.min; + c->br_y_range.max = c->tl_y_range.max; + break; + default: - return SANE_STATUS_INVAL; + return SANE_STATUS_INVAL; } c->imageMode = mode; c->val[OPT_TL_X].w = c->tl_x_range.min; diff --git a/backend/epsonds-net.c b/backend/epsonds-net.c index 8ea236b..3c8be29 100644 --- a/backend/epsonds-net.c +++ b/backend/epsonds-net.c @@ -32,11 +32,12 @@ #include "sane/sanei_debug.h" -static int +static ssize_t epsonds_net_read_raw(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status *status) { - int ready, read = -1; + int ready; + ssize_t read = -1; fd_set readable; struct timeval tv; @@ -62,106 +63,98 @@ epsonds_net_read_raw(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, return read; } -int -epsonds_net_read(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, +static ssize_t +epsonds_net_read_buf(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status * status) { - ssize_t size; ssize_t read = 0; - unsigned char header[12]; - /* read from buffer, if available */ - if (wanted && s->netptr != s->netbuf) { - DBG(23, "reading %lu from buffer at %p, %lu available\n", - (u_long) wanted, s->netptr, (u_long) s->netlen); + DBG(23, "%s: reading up to %lu from buffer at %p, %lu available\n", + __func__, (u_long) wanted, s->netptr, (u_long) s->netlen); - memcpy(buf, s->netptr, wanted); - read = wanted; + if ((size_t) wanted > s->netlen) { + *status = SANE_STATUS_IO_ERROR; + wanted = s->netlen; + } - s->netlen -= wanted; + memcpy(buf, s->netptr, wanted); + read = wanted; - if (s->netlen == 0) { - DBG(23, "%s: freeing %p\n", __func__, s->netbuf); - free(s->netbuf); - s->netbuf = s->netptr = NULL; - s->netlen = 0; - } + s->netptr += read; + s->netlen -= read; + + if (s->netlen == 0) { + DBG(23, "%s: freeing %p\n", __func__, s->netbuf); + free(s->netbuf); + s->netbuf = s->netptr = NULL; + s->netlen = 0; + } + + return read; +} + +ssize_t +epsonds_net_read(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, + SANE_Status * status) +{ + if (wanted < 0) { + *status = SANE_STATUS_INVAL; + return 0; + } - return read; + size_t size; + ssize_t read = 0; + unsigned char header[12]; + + /* read from remainder of buffer */ + if (s->netptr) { + return epsonds_net_read_buf(s, buf, wanted, status); } /* receive net header */ - size = epsonds_net_read_raw(s, header, 12, status); - if (size != 12) { + read = epsonds_net_read_raw(s, header, 12, status); + if (read != 12) { return 0; } + /* validate header */ if (header[0] != 'I' || header[1] != 'S') { DBG(1, "header mismatch: %02X %02x\n", header[0], header[1]); *status = SANE_STATUS_IO_ERROR; return 0; } - // incoming payload size + /* parse payload size */ size = be32atoh(&header[6]); - DBG(23, "%s: wanted = %lu, available = %lu\n", __func__, - (u_long) wanted, (u_long) size); - *status = SANE_STATUS_GOOD; - if (size == wanted) { + if (!s->netbuf) { + DBG(15, "%s: direct read\n", __func__); + DBG(23, "%s: wanted = %lu, available = %lu\n", __func__, + (u_long) wanted, (u_long) size); - DBG(15, "%s: full read\n", __func__); - - if (size) { - read = epsonds_net_read_raw(s, buf, size, status); + if ((size_t) wanted > size) { + wanted = size; } - if (s->netbuf) { - free(s->netbuf); - s->netbuf = NULL; - s->netlen = 0; - } - - if (read < 0) { - return 0; - } - - } else if (wanted < size) { - - DBG(23, "%s: long tail\n", __func__); - - read = epsonds_net_read_raw(s, s->netbuf, size, status); - if (read != size) { - return 0; - } - - memcpy(buf, s->netbuf, wanted); - read = wanted; - - free(s->netbuf); - s->netbuf = NULL; - s->netlen = 0; - + read = epsonds_net_read_raw(s, buf, wanted, status); } else { + DBG(15, "%s: buffered read\n", __func__); + DBG(23, "%s: bufferable = %lu, available = %lu\n", __func__, + (u_long) s->netlen, (u_long) size); - DBG(23, "%s: partial read\n", __func__); - - read = epsonds_net_read_raw(s, s->netbuf, size, status); - if (read != size) { - return 0; + if (s->netlen > size) { + s->netlen = size; } - s->netlen = size - wanted; - s->netptr += wanted; - read = wanted; - - DBG(23, "0,4 %02x %02x\n", s->netbuf[0], s->netbuf[4]); - DBG(23, "storing %lu to buffer at %p, next read at %p, %lu bytes left\n", - (u_long) size, s->netbuf, s->netptr, (u_long) s->netlen); + /* fill buffer */ + read = epsonds_net_read_raw(s, s->netbuf, s->netlen, status); + s->netptr = s->netbuf; + s->netlen = (read > 0 ? read : 0); - memcpy(buf, s->netbuf, wanted); + /* copy wanted part */ + read = epsonds_net_read_buf(s, buf, wanted, status); } return read; @@ -175,23 +168,38 @@ epsonds_net_request_read(epsonds_scanner *s, size_t len) return status; } -int +size_t epsonds_net_write(epsonds_scanner *s, unsigned int cmd, const unsigned char *buf, size_t buf_size, size_t reply_len, SANE_Status *status) { unsigned char *h1, *h2; unsigned char *packet = malloc(12 + 8); - /* XXX check allocation failure */ + if (!packet) { + *status = SANE_STATUS_NO_MEM; + return 0; + } h1 = packet; // packet header h2 = packet + 12; // data header if (reply_len) { - s->netbuf = s->netptr = malloc(reply_len); + if (s->netbuf) { + DBG(23, "%s, freeing %p, %ld bytes unprocessed\n", + __func__, s->netbuf, (u_long) s->netlen); + free(s->netbuf); + s->netbuf = s->netptr = NULL; + s->netlen = 0; + } + s->netbuf = malloc(reply_len); + if (!s->netbuf) { + free(packet); + *status = SANE_STATUS_NO_MEM; + return 0; + } s->netlen = reply_len; - DBG(24, "allocated %lu bytes at %p\n", - (u_long) reply_len, s->netbuf); + DBG(24, "%s: allocated %lu bytes at %p\n", __func__, + (u_long) s->netlen, s->netbuf); } DBG(24, "%s: cmd = %04x, buf = %p, buf_size = %lu, reply_len = %lu\n", diff --git a/backend/epsonds-net.h b/backend/epsonds-net.h index f7b173e..107301b 100644 --- a/backend/epsonds-net.h +++ b/backend/epsonds-net.h @@ -4,9 +4,9 @@ #include <sys/types.h> #include "../include/sane/sane.h" -extern int epsonds_net_read(struct epsonds_scanner *s, unsigned char *buf, ssize_t buf_size, +extern ssize_t epsonds_net_read(struct epsonds_scanner *s, unsigned char *buf, ssize_t buf_size, SANE_Status *status); -extern int epsonds_net_write(struct epsonds_scanner *s, unsigned int cmd, const unsigned char *buf, +extern size_t epsonds_net_write(struct epsonds_scanner *s, unsigned int cmd, const unsigned char *buf, size_t buf_size, size_t reply_len, SANE_Status *status); extern SANE_Status epsonds_net_lock(struct epsonds_scanner *s); diff --git a/backend/epsonds.c b/backend/epsonds.c index fb9694a..f2af220 100644 --- a/backend/epsonds.c +++ b/backend/epsonds.c @@ -47,6 +47,8 @@ #ifdef HAVE_SYS_TIME_H # include <sys/time.h> #endif +#include <sys/types.h> +#include <sys/socket.h> #include <unistd.h> #include "sane/saneopts.h" @@ -245,8 +247,8 @@ open_scanner(epsonds_scanner *s) /* the scanner sends a kind of welcome msg */ // XXX check command type, answer to connect is 0x80 - read = eds_recv(s, buf, 3, &status); - if (read != 3) { + read = eds_recv(s, buf, 5, &status); + if (read != 5) { sanei_tcp_close(s->fd); s->fd = -1; return SANE_STATUS_IO_ERROR; diff --git a/backend/escl.conf.in b/backend/escl.conf.in index 2aa6257..9c482b5 100644 --- a/backend/escl.conf.in +++ b/backend/escl.conf.in @@ -8,6 +8,12 @@ # -> 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. +# You can also configure a device on a single line starting with 'device' +# by writing a complete URL and an optional model name. + +#device http://123.456.789.10:8080 OptionalModel1 +#device https://123.456.789.10:443 "Optional Model 2" +#device unix:/run/proxy.sock:http://123.456.789.10:80 #[device] diff --git a/backend/escl/escl.c b/backend/escl/escl.c index 8df6c5c..c40fd98 100644 --- a/backend/escl/escl.c +++ b/backend/escl/escl.c @@ -46,14 +46,16 @@ static int num_devices = 0; typedef struct Handled { struct Handled *next; - SANE_String_Const name; + ESCL_Device *device; 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_Range x_range1; + SANE_Range x_range2; + SANE_Range y_range1; + SANE_Range y_range2; SANE_Bool cancel; SANE_Bool write_scan_data; SANE_Bool decompress_scan_data; @@ -61,6 +63,59 @@ typedef struct Handled { SANE_Parameters ps; } escl_sane_t; +static ESCL_Device * +escl_free_device(ESCL_Device *current) +{ + if (!current) return NULL; + free((void*)current->ip_address); + free((void*)current->model_name); + free((void*)current->type); + free(current->unix_socket); + free(current); + return NULL; +} + +void +escl_free_handler(escl_sane_t *handler) +{ + if (handler == NULL) + return; + + escl_free_device(handler->device); + free(handler); +} + +SANE_Status escl_parse_name(SANE_String_Const name, ESCL_Device *device); + +static SANE_Status +escl_check_and_add_device(ESCL_Device *current) +{ + if(!current) { + DBG (10, "ESCL_Device *current us null.\n"); + return (SANE_STATUS_NO_MEM); + } + if (!current->ip_address) { + DBG (10, "Ip Address allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } + if (current->port_nb == 0) { + DBG (10, "No port defined.\n"); + return (SANE_STATUS_NO_MEM); + } + if (!current->model_name) { + DBG (10, "Modele Name allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } + if (!current->type) { + DBG (10, "Scanner Type allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } + ++num_devices; + current->next = list_devices_primary; + list_devices_primary = current; + return (SANE_STATUS_GOOD); +} + /** * \fn static SANE_Status escl_add_in_list(ESCL_Device *current) * \brief Function that adds all the element needed to my list : @@ -72,10 +127,18 @@ typedef struct Handled { 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); + if(!current) { + DBG (10, "ESCL_Device *current us null.\n"); + return (SANE_STATUS_NO_MEM); + } + + if (SANE_STATUS_GOOD == + escl_check_and_add_device(current)) { + list_devices_primary = current; + return (SANE_STATUS_GOOD); + } + current = escl_free_device(current); + return (SANE_STATUS_NO_MEM); } /** @@ -89,19 +152,44 @@ escl_add_in_list(ESCL_Device *current) SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type) { + char tmp[PATH_MAX] = { 0 }; + char *model = NULL; 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); + if (strcmp(current->ip_address, ip_address) == 0) + { + if (strcmp(current->type, type)) + { + if(!strcmp(type, "_uscans._tcp") || + !strcmp(type, "https")) + { + free (current->type); + current->type = strdup(type); + current->port_nb = port_nb; + current->https = SANE_TRUE; + } + return (SANE_STATUS_GOOD); + } + else if (current->port_nb == port_nb) + return (SANE_STATUS_GOOD); + } + } + current = (ESCL_Device*)calloc(1, sizeof(*current)); + if (current == NULL) { + DBG (10, "New device allocation failure.\n"); + return (SANE_STATUS_NO_MEM); } - 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); + + if (strcmp(type, "_uscan._tcp") != 0 && strcmp(type, "http") != 0) { + snprintf(tmp, sizeof(tmp), "%s SSL", model_name); + current->https = SANE_TRUE; + } else { + current->https = SANE_FALSE; + } + model = (char*)(tmp[0] != 0 ? tmp : model_name); + current->model_name = strdup(model); current->ip_address = strdup(ip_address); current->type = strdup(type); return escl_add_in_list(current); @@ -121,13 +209,51 @@ max_string_size(const SANE_String_Const strings[]) int i = 0; for (i = 0; strings[i]; ++i) { - size_t size = strlen (strings[i]); - if (size > max_size) - max_size = size; + size_t size = strlen (strings[i]); + if (size > max_size) + max_size = size; } return (max_size + 1); } +static char * +get_vendor(char *search) +{ + if(strcasestr(search, "Epson")) + return strdup("Epson"); + else if(strcasestr(search, "Fujitsu")) + return strdup("Fujitsu"); + else if(strcasestr(search, "HP")) + return strdup("HP"); + else if(strcasestr(search, "Canon")) + return strdup("Canon"); + else if(strcasestr(search, "Lexmark")) + return strdup("Lexmark"); + else if(strcasestr(search, "Samsung")) + return strdup("Samsung"); + else if(strcasestr(search, "Xerox")) + return strdup("Xerox"); + else if(strcasestr(search, "OKI")) + return strdup("OKI"); + else if(strcasestr(search, "Hewlett Packard")) + return strdup("Hewlett Packard"); + else if(strcasestr(search, "IBM")) + return strdup("IBM"); + else if(strcasestr(search, "Mustek")) + return strdup("Mustek"); + else if(strcasestr(search, "Ricoh")) + return strdup("Ricoh"); + else if(strcasestr(search, "Sharp")) + return strdup("Sharp"); + else if(strcasestr(search, "UMAX")) + return strdup("UMAX"); + else if(strcasestr(search, "PINT")) + return strdup("PINT"); + else if(strcasestr(search, "Brother")) + return strdup("Brother"); + return NULL; +} + /** * \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). @@ -142,19 +268,61 @@ max_string_size(const SANE_String_Const strings[]) static SANE_Device * convertFromESCLDev(ESCL_Device *cdev) { + char *tmp; + int len, lv = 0; + char unix_path[PATH_MAX+7] = { 0 }; SANE_Device *sdev = (SANE_Device*) calloc(1, sizeof(SANE_Device)); - char tmp[PATH_MAX] = { 0 }; + if (!sdev) { + DBG (10, "Sane_Device allocation failure.\n"); + return NULL; + } + + if (cdev->unix_socket && strlen(cdev->unix_socket)) { + snprintf(unix_path, sizeof(unix_path), "unix:%s:", cdev->unix_socket); + } + len = snprintf(NULL, 0, "%shttp%s://%s:%d", + unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb); + len++; + tmp = (char *)malloc(len); + if (!tmp) { + DBG (10, "Name allocation failure.\n"); + goto freedev; + } + snprintf(tmp, len, "%shttp%s://%s:%d", + unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb); + sdev->name = tmp; - 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->vendor = get_vendor(cdev->model_name); + + if (!sdev->vendor) + sdev->vendor = strdup("ESCL"); + else + lv = strlen(sdev->vendor) + 1; + if (!sdev->vendor) { + DBG (10, "Vendor allocation failure.\n"); + goto freemodel; + } + sdev->model = strdup(lv + cdev->model_name); + if (!sdev->model) { + DBG (10, "Model allocation failure.\n"); + goto freename; + } sdev->type = strdup("flatbed scanner"); + if (!sdev->type) { + DBG (10, "Scanner Type allocation failure.\n"); + goto freevendor; + } return (sdev); +freevendor: + free((void*)sdev->vendor); +freemodel: + free((void*)sdev->model); +freename: + free((void*)sdev->name); +freedev: + free((void*)sdev); + return NULL; } /** @@ -174,9 +342,9 @@ sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) SANE_Status status = SANE_STATUS_GOOD; curl_global_init(CURL_GLOBAL_ALL); if (version_code != NULL) - *version_code = SANE_VERSION_CODE(1, 0, 0); + *version_code = SANE_VERSION_CODE(1, 0, 0); if (status != SANE_STATUS_GOOD) - return (status); + return (status); return (SANE_STATUS_GOOD); } @@ -194,12 +362,12 @@ sane_exit(void) ESCL_Device *next = NULL; while (list_devices_primary != NULL) { - next = list_devices_primary->next; - free(list_devices_primary); - list_devices_primary = next; + next = list_devices_primary->next; + free(list_devices_primary); + list_devices_primary = next; } if (devlist) - free (devlist); + free (devlist); list_devices_primary = NULL; devlist = NULL; curl_global_cleanup(); @@ -218,44 +386,85 @@ static SANE_Status attach_one_config(SANEI_Config __sane_unused__ *config, const char *line) { int port = 0; - static int count = 0; + SANE_Status status; static ESCL_Device *escl_device = NULL; - if (strncmp(line, "[device]", 8) == 0) { - count = 0; + if (strncmp(line, "device", 6) == 0) { + char *name_str = NULL; + char *opt_model = NULL; + + line = sanei_config_get_string(line + 6, &name_str); + DBG (10, "New Escl_Device URL [%s].\n", (name_str ? name_str : "VIDE")); + if (!name_str || !*name_str) { + DBG (1, "Escl_Device URL missing.\n"); + return SANE_STATUS_INVAL; + } + if (*line) { + line = sanei_config_get_string(line, &opt_model); + DBG (10, "New Escl_Device model [%s].\n", opt_model); + } + + escl_free_device(escl_device); escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device)); + if (!escl_device) { + DBG (10, "New Escl_Device allocation failure.\n"); + free(name_str); + return (SANE_STATUS_NO_MEM); + } + status = escl_parse_name(name_str, escl_device); + free(name_str); + if (status != SANE_STATUS_GOOD) { + escl_free_device(escl_device); + escl_device = NULL; + return status; + } + escl_device->model_name = opt_model ? opt_model : strdup("Unknown model"); + escl_device->type = strdup("flatbed scanner"); + } + + if (strncmp(line, "[device]", 8) == 0) { + escl_device = escl_free_device(escl_device); + escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device)); + if (!escl_device) { + DBG (10, "New Escl_Device allocation failure."); + return (SANE_STATUS_NO_MEM); + } } 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); - } + const char *ip_space = sanei_config_skip_whitespace(line + 2); + DBG (10, "New Escl_Device IP [%s].", (ip_space ? ip_space : "VIDE")); + if (escl_device != NULL && ip_space != NULL) { + DBG (10, "New Escl_Device IP Affected."); + 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; - } + DBG (10, "New Escl_Device PORT [%d].", port); + if (escl_device != NULL) { + DBG (10, "New Escl_Device PORT Affected."); + 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); - } + const char *model_space = sanei_config_skip_whitespace(line + 5); + DBG (10, "New Escl_Device MODEL [%s].", (model_space ? model_space : "VIDE")); + if (escl_device != NULL && model_space != NULL) { + DBG (10, "New Escl_Device MODEL Affected."); + 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); - } + const char *type_space = sanei_config_skip_whitespace(line + 4); + DBG (10, "New Escl_Device TYPE [%s].", (type_space ? type_space : "VIDE")); + if (escl_device != NULL && type_space != NULL) { + DBG (10, "New Escl_Device TYPE Affected."); + escl_device->type = strdup(type_space); + } } - if (count == 4) - return (escl_add_in_list(escl_device)); - return (SANE_STATUS_GOOD); + status = escl_check_and_add_device(escl_device); + if (status == SANE_STATUS_GOOD) + escl_device = NULL; + return status; } /** @@ -269,7 +478,7 @@ 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); + return (device_list ? SANE_STATUS_GOOD : SANE_STATUS_INVAL); DBG (10, "escl sane_get_devices\n"); ESCL_Device *dev = NULL; @@ -277,29 +486,46 @@ sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) SANE_Status status; if (device_list == NULL) - return (SANE_STATUS_INVAL); + return (SANE_STATUS_INVAL); status = sanei_configure_attach(ESCL_CONFIG_FILE, NULL, attach_one_config); if (status != SANE_STATUS_GOOD) - return (status); + return (status); escl_devices(&status); if (status != SANE_STATUS_GOOD) - return (status); + return (status); if (devlist) - free(devlist); + free(devlist); devlist = (const SANE_Device **) calloc (num_devices + 1, sizeof (devlist[0])); if (devlist == NULL) - return (SANE_STATUS_NO_MEM); + 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++; + 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; } +/* Returns the length of the longest string, including the terminating + * character. */ +static size_t +_source_size_max (SANE_String_Const * sources) +{ + size_t size = 0; + + while(*sources) + { + size_t t = strlen (*sources) + 1; + if (t > size) + size = t; + sources++; + } + return size; +} + /** * \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 @@ -309,26 +535,51 @@ sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) * \return status (if everything is OK, status = SANE_STATUS_GOOD) */ static SANE_Status -init_options(SANE_String_Const name, escl_sane_t *s) +init_options(SANE_String_Const name_source, 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); + if (!s->scanner) return SANE_STATUS_INVAL; + if (name_source) { + int source = s->scanner->source; + DBG (10, "escl init_options name [%s]\n", name_source); + if (!strcmp(name_source, SANE_I18N ("ADF Duplex"))) + s->scanner->source = ADFDUPLEX; + else if (!strncmp(name_source, "A", 1) || + !strcmp(name_source, SANE_I18N ("ADF"))) + s->scanner->source = ADFSIMPLEX; + else + s->scanner->source = PLATEN; + if (source == s->scanner->source) return status; + } + else + s->scanner->source = PLATEN; 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[i].size = sizeof (SANE_Word); + s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + s->x_range1.min = 0; + s->x_range1.max = + PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxWidth - + s->scanner->caps[s->scanner->source].MinWidth), + 300.0); + s->x_range1.quant = 0; + s->x_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinWidth, 300.0); + s->x_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxWidth, 300.0); + s->x_range2.quant = 0; + s->y_range1.min = 0; + s->y_range1.max = + PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxHeight - + s->scanner->caps[s->scanner->source].MinHeight), + 300.0); + s->y_range1.quant = 0; + s->y_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinHeight, 300.0); + s->y_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxHeight, 300.0); + s->y_range2.quant = 0; 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; @@ -347,10 +598,18 @@ init_options(SANE_String_Const name, escl_sane_t *s) 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_MODE].constraint.string_list = s->scanner->caps[s->scanner->source].ColorModes; + s->val[OPT_MODE].s = (char *)strdup(s->scanner->caps[s->scanner->source].ColorModes[0]); + if (!s->val[OPT_MODE].s) { + DBG (10, "Color Mode Default allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } + s->opt[OPT_MODE].size = max_string_size(s->scanner->caps[s->scanner->source].ColorModes); + s->scanner->caps[s->scanner->source].default_color = (char *)strdup(s->scanner->caps[s->scanner->source].ColorModes[0]); + if (!s->scanner->caps[s->scanner->source].default_color) { + DBG (10, "Color Mode Default allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; @@ -358,9 +617,9 @@ init_options(SANE_String_Const name, escl_sane_t *s) 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_RESOLUTION].constraint.word_list = s->scanner->caps[s->scanner->source].SupportedResolutions; + s->val[OPT_RESOLUTION].w = s->scanner->caps[s->scanner->source].SupportedResolutions[1]; + s->scanner->caps[s->scanner->source].default_resolution = s->scanner->caps[s->scanner->source].SupportedResolutions[1]; s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; @@ -376,7 +635,7 @@ init_options(SANE_String_Const name, escl_sane_t *s) 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].desc = SANE_DESC_GEOMETRY; 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; @@ -385,40 +644,107 @@ init_options(SANE_String_Const name, escl_sane_t *s) 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].size = sizeof(SANE_Fixed); + s->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + 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 = &s->x_range; - s->val[OPT_TL_X].w = s->scanner->RiskyLeftMargin; + s->opt[OPT_TL_X].constraint.range = &s->x_range1; + s->val[OPT_TL_X].w = s->x_range1.min; 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].size = sizeof(SANE_Fixed); + s->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + 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 = &s->y_range; - s->val[OPT_TL_Y].w = s->scanner->RiskyTopMargin; + s->opt[OPT_TL_Y].constraint.range = &s->y_range1; + s->val[OPT_TL_Y].w = s->y_range1.min; 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].size = sizeof(SANE_Fixed); + s->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + 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 = &s->x_range; - s->val[OPT_BR_X].w = s->scanner->MaxWidth; + s->opt[OPT_BR_X].constraint.range = &s->x_range2; + s->val[OPT_BR_X].w = s->x_range2.max; 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].size = sizeof(SANE_Fixed); + s->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + 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 = &s->y_range; - s->val[OPT_BR_Y].w = s->scanner->MaxHeight; + s->opt[OPT_BR_Y].constraint.range = &s->y_range2; + s->val[OPT_BR_Y].w = s->y_range2.max; + + /* OPT_SCAN_SOURCE */ + s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE; + s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE; + s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE; + s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING; + s->opt[OPT_SCAN_SOURCE].size = _source_size_max(s->scanner->Sources); + s->opt[OPT_SCAN_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_SCAN_SOURCE].constraint.string_list = s->scanner->Sources; + if (s->val[OPT_SCAN_SOURCE].s) + free (s->val[OPT_SCAN_SOURCE].s); + s->val[OPT_SCAN_SOURCE].s = strdup (s->scanner->Sources[s->scanner->source]); return (status); } +SANE_Status +escl_parse_name(SANE_String_Const name, ESCL_Device *device) +{ + SANE_String_Const host = NULL; + SANE_String_Const port_str = NULL; + DBG(10, "escl_parse_name\n"); + if (name == NULL || device == NULL) { + return SANE_STATUS_INVAL; + } + + if (strncmp(name, "unix:", 5) == 0) { + SANE_String_Const socket = name + 5; + name = strchr(socket, ':'); + if (name == NULL) + return SANE_STATUS_INVAL; + device->unix_socket = strndup(socket, name - socket); + name++; + } + + if (strncmp(name, "https://", 8) == 0) { + device->https = SANE_TRUE; + host = name + 8; + } else if (strncmp(name, "http://", 7) == 0) { + device->https = SANE_FALSE; + host = name + 7; + } else { + DBG(1, "Unknown URL scheme in %s", name); + return SANE_STATUS_INVAL; + } + + port_str = strchr(host, ':'); + if (port_str == NULL) { + DBG(1, "Port missing from URL: %s", name); + return SANE_STATUS_INVAL; + } + port_str++; + device->port_nb = atoi(port_str); + if (device->port_nb < 1 || device->port_nb > 65535) { + DBG(1, "Invalid port number in URL: %s", name); + return SANE_STATUS_INVAL; + } + + device->ip_address = strndup(host, port_str - host - 1); + return SANE_STATUS_GOOD; +} + /** * \fn SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h) * \brief Function that establishes a connection with the device named by 'name', @@ -437,28 +763,45 @@ sane_open(SANE_String_Const name, SANE_Handle *h) if (name == NULL) return (SANE_STATUS_INVAL); - status = escl_status(name); - if (status != SANE_STATUS_GOOD) - return (status); + + ESCL_Device *device = calloc(1, sizeof(ESCL_Device)); + if (device == NULL) { + DBG (10, "Handle device allocation failure.\n"); + return SANE_STATUS_NO_MEM; + } + status = escl_parse_name(name, device); + if (status != SANE_STATUS_GOOD) { + escl_free_device(device); + return status; + } + handler = (escl_sane_t *)calloc(1, sizeof(escl_sane_t)); - if (handler == NULL) + if (handler == NULL) { + escl_free_device(device); return (SANE_STATUS_NO_MEM); - handler->name = strdup(name); - handler->scanner = escl_capabilities(name, &status); - if (status != SANE_STATUS_GOOD) + } + handler->device = device; // Handler owns device now. + handler->scanner = escl_capabilities(device, &status); + if (status != SANE_STATUS_GOOD) { + escl_free_handler(handler); return (status); - status = init_options(name, handler); - if (status != SANE_STATUS_GOOD) + } + status = init_options(NULL, handler); + if (status != SANE_STATUS_GOOD) { + escl_free_handler(handler); 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.pixels_per_line = MM_TO_PIXEL(handler->val[OPT_BR_X].w, 300.0); + handler->ps.lines = MM_TO_PIXEL(handler->val[OPT_BR_Y].w, 300.0); handler->ps.bytes_per_line = handler->ps.pixels_per_line * 3; status = sane_get_parameters(handler, 0); - if (status != SANE_STATUS_GOOD) + if (status != SANE_STATUS_GOOD) { + escl_free_handler(handler); return (status); + } handler->cancel = SANE_FALSE; handler->write_scan_data = SANE_FALSE; handler->decompress_scan_data = SANE_FALSE; @@ -483,8 +826,11 @@ sane_cancel(SANE_Handle h) fclose(handler->scanner->tmp); handler->scanner->tmp = NULL; } + handler->scanner->work = SANE_FALSE; handler->cancel = SANE_TRUE; - escl_scanner(handler->name, handler->result); + escl_scanner(handler->device, handler->result); + free(handler->result); + handler->result = NULL; } /** @@ -497,7 +843,7 @@ sane_close(SANE_Handle h) { DBG (10, "escl sane_close\n"); if (h != NULL) { - free(h); + escl_free_handler(h); h = NULL; } } @@ -517,8 +863,8 @@ sane_get_option_descriptor(SANE_Handle h, SANE_Int n) escl_sane_t *s = h; if ((unsigned) n >= NUM_OPTIONS || n < 0) - return (0); - return (s->opt + n); + return (0); + return (&s->opt[n]); } /** @@ -541,62 +887,92 @@ sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int escl_sane_t *handler = h; if (i) - *i = 0; + *i = 0; if (n >= NUM_OPTIONS || n < 0) - return (SANE_STATUS_INVAL); + 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); + switch (n) { + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_NUM_OPTS: + case OPT_RESOLUTION: + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + *(SANE_Word *) v = handler->val[n].w; + break; + case OPT_SCAN_SOURCE: + 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; - } + switch (n) { + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_NUM_OPTS: + case OPT_RESOLUTION: + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + handler->val[n].w = *(SANE_Word *) v; + if (i) + *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; + break; + case OPT_SCAN_SOURCE: + DBG(10, "SET OPT_SCAN_SOURCE(%s)\n", (SANE_String_Const)v); + init_options((SANE_String_Const)v, handler); + 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 (!handler->val[n].s) { + DBG (10, "OPT_MODE allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } + if (i) + *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; + break; + default: + break; + } } return (SANE_STATUS_GOOD); } +static SANE_Bool +_go_next_page(SANE_Status status, + SANE_Status job) +{ + // Thank's Alexander Pevzner (pzz@apevzner.com) + SANE_Status st = SANE_STATUS_NO_DOCS; + switch (status) { + case SANE_STATUS_GOOD: + case SANE_STATUS_UNSUPPORTED: + case SANE_STATUS_DEVICE_BUSY: { + DBG(10, "eSCL : Test next page\n"); + if (job != SANE_STATUS_GOOD) { + DBG(10, "eSCL : Go next page\n"); + st = SANE_STATUS_GOOD; + } + break; + } + default: + DBG(10, "eSCL : No next page\n"); + } + return st; +} + /** * \fn SANE_Status sane_start(SANE_Handle h) * \brief Function that initiates aquisition of an image from the device represented by handle 'h'. @@ -614,70 +990,137 @@ sane_start(SANE_Handle h) int he = 0; int bps = 0; - if (handler->name == NULL) + if (handler->device == NULL) { + DBG(1, "Missing handler device.\n"); 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"); + if (handler->scanner->work == SANE_FALSE) { + SANE_Status st = escl_status(handler->device, + handler->scanner->source, + NULL, + NULL); + if (st != SANE_STATUS_GOOD) + return st; + if(handler->scanner->caps[handler->scanner->source].default_color) + free(handler->scanner->caps[handler->scanner->source].default_color); + if (handler->val[OPT_PREVIEW].w == SANE_TRUE) + { + int i = 0, val = 9999;; + if (handler->val[OPT_GRAY_PREVIEW].w == SANE_TRUE || + !strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) + handler->scanner->caps[handler->scanner->source].default_color = + strdup("Grayscale8"); + else + handler->scanner->caps[handler->scanner->source].default_color = + strdup("RGB24"); + if (!handler->scanner->caps[handler->scanner->source].default_color) { + DBG (10, "Default Color allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } + for (i = 1; i < handler->scanner->caps[handler->scanner->source].SupportedResolutionsSize; i++) + { + if (val > handler->scanner->caps[handler->scanner->source].SupportedResolutions[i]) + val = handler->scanner->caps[handler->scanner->source].SupportedResolutions[i]; + } + handler->scanner->caps[handler->scanner->source].default_resolution = val; + } 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->caps[handler->scanner->source].default_resolution = + handler->val[OPT_RESOLUTION].w; + if (!strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) + handler->scanner->caps[handler->scanner->source].default_color = strdup("Grayscale8"); + else if (!strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) + handler->scanner->caps[handler->scanner->source].default_color = + strdup("BlackAndWhite1"); + else + handler->scanner->caps[handler->scanner->source].default_color = + strdup("RGB24"); } - handler->scanner->default_resolution = val; + handler->scanner->caps[handler->scanner->source].height = + MM_TO_PIXEL(handler->val[OPT_BR_Y].w, 300.0); + handler->scanner->caps[handler->scanner->source].width = + MM_TO_PIXEL(handler->val[OPT_BR_X].w, 300.0);; + if (handler->x_range1.min == handler->val[OPT_TL_X].w) + handler->scanner->caps[handler->scanner->source].pos_x = 0; + else + handler->scanner->caps[handler->scanner->source].pos_x = + MM_TO_PIXEL((handler->val[OPT_TL_X].w - handler->x_range1.min), + 300.0); + if (handler->y_range1.min == handler->val[OPT_TL_X].w) + handler->scanner->caps[handler->scanner->source].pos_y = 0; + else + handler->scanner->caps[handler->scanner->source].pos_y = + MM_TO_PIXEL((handler->val[OPT_TL_Y].w - handler->y_range1.min), + 300.0); + DBG(10, "Calculate Size Image [%dx%d|%dx%d]\n", + handler->scanner->caps[handler->scanner->source].pos_x, + handler->scanner->caps[handler->scanner->source].pos_y, + handler->scanner->caps[handler->scanner->source].width, + handler->scanner->caps[handler->scanner->source].height); + if (!handler->scanner->caps[handler->scanner->source].default_color) { + DBG (10, "Default Color allocation failure.\n"); + return (SANE_STATUS_NO_MEM); + } + handler->result = escl_newjob(handler->scanner, handler->device, &status); + if (status != SANE_STATUS_GOOD) + return (status); } 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"); + SANE_Status job = SANE_STATUS_UNSUPPORTED; + SANE_Status st = escl_status(handler->device, + handler->scanner->source, + handler->result, + &job); + DBG(10, "eSCL : command returned status %s\n", sane_strstatus(st)); + if (_go_next_page(st, job) != SANE_STATUS_GOOD) + { + handler->scanner->work = SANE_FALSE; + return SANE_STATUS_NO_DOCS; + } } - handler->result = escl_newjob(handler->scanner, handler->name, &status); + status = escl_scan(handler->scanner, handler->device, handler->result); 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")) + return (status); + if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/jpeg")) { status = get_JPEG_data(handler->scanner, &w, &he, &bps); } - else if (!strcmp(handler->scanner->default_format, "image/png")) + else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/png")) { status = get_PNG_data(handler->scanner, &w, &he, &bps); } - else if (!strcmp(handler->scanner->default_format, "image/tiff")) + else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/tiff")) { status = get_TIFF_data(handler->scanner, &w, &he, &bps); } - else - return SANE_STATUS_INVAL; + else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "application/pdf")) + { + status = get_PDF_data(handler->scanner, &w, &he, &bps); + } + else { + DBG(10, "Unknow image format\n"); + return SANE_STATUS_INVAL; + } + + DBG(10, "2-Size Image (%ld)[%dx%d|%dx%d]\n", handler->scanner->img_size, 0, 0, w, he); if (status != SANE_STATUS_GOOD) - return (status); + 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; + handler->scanner->work = SANE_FALSE; +// DBG(10, "NEXT Frame [%s]\n", (handler->ps.last_frame ? "Non" : "Oui")); + DBG(10, "Real Size Image [%dx%d|%dx%d]\n", 0, 0, w, he); return (status); } @@ -700,7 +1143,7 @@ sane_get_parameters(SANE_Handle h, SANE_Parameters *p) return (status); if (p != NULL) { p->depth = 8; - p->last_frame = SANE_TRUE; + p->last_frame = handler->ps.last_frame; p->format = SANE_FRAME_RGB; p->pixels_per_line = handler->ps.pixels_per_line; p->lines = handler->ps.lines; @@ -729,6 +1172,7 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len) if (!handler | !buf | !len) return (SANE_STATUS_INVAL); + if (handler->cancel) return (SANE_STATUS_CANCELLED); if (!handler->write_scan_data) @@ -756,10 +1200,23 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len) } } else { + SANE_Status job = SANE_STATUS_UNSUPPORTED; *len = 0; free(handler->scanner->img_data); handler->scanner->img_data = NULL; - return (SANE_STATUS_EOF); + if (handler->scanner->source != PLATEN) { + SANE_Bool next_page = SANE_FALSE; + SANE_Status st = escl_status(handler->device, + handler->scanner->source, + handler->result, + &job); + DBG(10, "eSCL : command returned status %s\n", sane_strstatus(st)); + if (_go_next_page(st, job) == SANE_STATUS_GOOD) + next_page = SANE_TRUE; + handler->scanner->work = SANE_TRUE; + handler->ps.last_frame = !next_page; + } + return SANE_STATUS_EOF; } return (SANE_STATUS_GOOD); } @@ -775,3 +1232,39 @@ sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ n { return (SANE_STATUS_UNSUPPORTED); } + +/** + * \fn void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path) + * \brief Uses the device info in 'device' and the path from 'path' to construct + * a full URL. Sets this URL and any necessary connection options into + * 'handle'. + */ +void +escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path) +{ + int url_len; + char *url; + + url_len = snprintf(NULL, 0, "%s://%s:%d%s", + (device->https ? "https" : "http"), device->ip_address, + device->port_nb, path); + url_len++; + url = (char *)malloc(url_len); + snprintf(url, url_len, "%s://%s:%d%s", + (device->https ? "https" : "http"), device->ip_address, + device->port_nb, path); + + DBG( 1, "escl_curl_url: URL: %s\n", url ); + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + if (device->https) { + DBG( 1, "Ignoring safety certificates, use https\n"); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); + } + if (device->unix_socket != NULL) { + DBG( 1, "Using local socket %s\n", device->unix_socket ); + curl_easy_setopt(handle, CURLOPT_UNIX_SOCKET_PATH, + device->unix_socket); + } +} diff --git a/backend/escl/escl.h b/backend/escl/escl.h index 82910bd..53ce7c7 100644 --- a/backend/escl/escl.h +++ b/backend/escl/escl.h @@ -43,6 +43,7 @@ #include "../include/sane/sane.h" #include <stdio.h> +#include <math.h> #ifndef BACKEND_NAME #define BACKEND_NAME escl @@ -63,6 +64,14 @@ #define ESCL_CONFIG_FILE "escl.conf" + +enum { + PLATEN = 0, + ADFSIMPLEX, + ADFDUPLEX +}; + + typedef struct { int p1_0; int p2_0; @@ -82,9 +91,11 @@ typedef struct ESCL_Device { int port_nb; char *ip_address; char *type; + SANE_Bool https; + char *unix_socket; } ESCL_Device; -typedef struct capabilities +typedef struct capst { int height; int width; @@ -104,6 +115,7 @@ typedef struct capabilities int ContentTypesSize; SANE_String_Const *DocumentFormats; int DocumentFormatsSize; + int format_ext; SANE_Int *SupportedResolutions; int SupportedResolutionsSize; SANE_String_Const *SupportedIntents; @@ -114,11 +126,21 @@ typedef struct capabilities int RiskyRightMargin; int RiskyTopMargin; int RiskyBottomMargin; + int duplex; +} caps_t; + +typedef struct capabilities +{ + caps_t caps[3]; + int source; + SANE_String_Const *Sources; + int SourcesSize; FILE *tmp; unsigned char *img_data; long img_size; long img_read; - int format_ext; + size_t real_read; + SANE_Bool work; } capabilities_t; typedef struct { @@ -148,24 +170,45 @@ enum OPT_TL_Y, OPT_BR_X, OPT_BR_Y, + + OPT_SCAN_SOURCE, + NUM_OPTIONS }; +#define PIXEL_TO_MM(pixels, dpi) SANE_FIX((double)pixels * 25.4 / (dpi)) +#define MM_TO_PIXEL(millimeters, dpi) (SANE_Word)round(SANE_UNFIX(millimeters) * (dpi) / 25.4) + 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); +SANE_Status escl_device_add(int port_nb, const char *model_name, + char *ip_address, char *type); +SANE_Status escl_status(const ESCL_Device *device, + int source, + const char* jobId, + SANE_Status *job); +capabilities_t *escl_capabilities(const ESCL_Device *device, SANE_Status *status); +char *escl_newjob(capabilities_t *scanner, const ESCL_Device *device, + SANE_Status *status); +SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, + char *result); +void escl_scanner(const ESCL_Device *device, char *result); + +typedef void CURL; +void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path); + +unsigned char *escl_crop_surface(capabilities_t *scanner, unsigned char *surface, + int w, int h, int bps, int *width, int *height); // JPEG -SANE_Status get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps); +SANE_Status get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps); // PNG -SANE_Status get_PNG_data(capabilities_t *scanner, int *w, int *h, int *bps); +SANE_Status get_PNG_data(capabilities_t *scanner, int *width, int *height, int *bps); // TIFF -SANE_Status get_TIFF_data(capabilities_t *scanner, int *w, int *h, int *bps); +SANE_Status get_TIFF_data(capabilities_t *scanner, int *width, int *height, int *bps); + +// PDF +SANE_Status get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps); #endif diff --git a/backend/escl/escl_capabilities.c b/backend/escl/escl_capabilities.c index 690ff1e..fdd5cfe 100644 --- a/backend/escl/escl_capabilities.c +++ b/backend/escl/escl_capabilities.c @@ -45,7 +45,7 @@ struct cap * \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 + * \return SANE_VALUE_SCAN_MODE_GRAY / SANE_VALUE_SCAN_MODE_COLOR / SANE_VALUE_SCAN_MODE_LINEART; NULL otherwise */ static SANE_String_Const convert_elements(SANE_String_Const str) @@ -54,6 +54,10 @@ convert_elements(SANE_String_Const str) return (SANE_VALUE_SCAN_MODE_GRAY); else if (strcmp(str, "RGB24") == 0) return (SANE_VALUE_SCAN_MODE_COLOR); +#if(defined HAVE_POPPLER_GLIB) + else if (strcmp(str, "BlackAndWhite1") == 0) + return (SANE_VALUE_SCAN_MODE_LINEART); +#endif return (NULL); } @@ -137,7 +141,7 @@ memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp) char *str = realloc(mem->memory, mem->size + realsize + 1); if (str == NULL) { - fprintf(stderr, "not enough memory (realloc returned NULL)\n"); + DBG(10, "not enough memory (realloc returned NULL)\n"); return (0); } mem->memory = str; @@ -175,48 +179,61 @@ find_nodes_c(xmlNode *node) * \return 0 */ static int -find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner) +find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner, int type) { 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); + if (strcmp(name, "ColorMode") == 0) { + const char *color = (SANE_String_Const)xmlNodeGetContent(node); + if (type == PLATEN || strcmp(color, "BlackAndWhite1")) + scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].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); + scanner->caps[type].ContentTypes = char_to_array(scanner->caps[type].ContentTypes, &scanner->caps[type].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++) + SANE_Bool have_jpeg = SANE_FALSE, have_png = SANE_FALSE, have_tiff = SANE_FALSE, have_pdf = SANE_FALSE; + scanner->caps[type].DocumentFormats = char_to_array(scanner->caps[type].DocumentFormats, &scanner->caps[type].DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0); + for(; i < scanner->caps[type].DocumentFormatsSize; i++) { - if (scanner->default_format == NULL && !strcmp(scanner->DocumentFormats[i], "image/jpeg")) + if (!strcmp(scanner->caps[type].DocumentFormats[i], "image/jpeg")) { - scanner->default_format = strdup("image/jpeg"); + have_jpeg = SANE_TRUE; } #if(defined HAVE_LIBPNG) - else if(!strcmp(scanner->DocumentFormats[i], "image/png") && (scanner->default_format == NULL || strcmp(scanner->default_format, "image/tiff"))) + else if(!strcmp(scanner->caps[type].DocumentFormats[i], "image/png")) { - if (scanner->default_format) - free(scanner->default_format); - scanner->default_format = strdup("image/png"); + have_png = SANE_TRUE; } #endif #if(defined HAVE_TIFFIO_H) - else if(!strcmp(scanner->DocumentFormats[i], "image/tiff")) + else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "image/tiff")) + { + have_tiff = SANE_TRUE; + } +#endif +#if(defined HAVE_POPPLER_GLIB) + else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "application/pdf")) { - if (scanner->default_format) - free(scanner->default_format); - scanner->default_format = strdup("image/tiff"); + have_pdf = SANE_TRUE; } #endif } - fprintf(stderr, "Capability : [%s]\n", scanner->default_format); + if (have_pdf) + scanner->caps[type].default_format = strdup("application/pdf"); + else if (have_tiff) + scanner->caps[type].default_format = strdup("image/tiff"); + else if (have_png) + scanner->caps[type].default_format = strdup("image/png"); + else if (have_jpeg) + scanner->caps[type].default_format = strdup("image/jpeg"); } else if (strcmp(name, "DocumentFormatExt") == 0) - scanner->format_ext = 1; + scanner->caps[type].format_ext = 1; else if (strcmp(name, "Intent") == 0) - scanner->SupportedIntents = char_to_array(scanner->SupportedIntents, &scanner->SupportedIntentsSize, (SANE_String_Const)xmlNodeGetContent(node), 0); + scanner->caps[type].SupportedIntents = char_to_array(scanner->caps[type].SupportedIntents, &scanner->caps[type].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))); + scanner->caps[type].SupportedResolutions = int_to_array(scanner->caps[type].SupportedResolutions, &scanner->caps[type].SupportedResolutionsSize, atoi((const char *)xmlNodeGetContent(node))); return (0); } @@ -230,39 +247,39 @@ find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner) * \return 0 */ static int -find_value_of_int_variables(xmlNode *node, capabilities_t *scanner) +find_value_of_int_variables(xmlNode *node, capabilities_t *scanner, int type) { 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)); + scanner->caps[type].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)); + if (scanner->caps[type].MaxWidth == 0 || MaxWidth < scanner->caps[type].MaxWidth) + scanner->caps[type].MaxWidth = atoi((const char *)xmlNodeGetContent(node)); } else if (strcmp(name, "MinHeight") == 0) - scanner->MinHeight = atoi((const char*)xmlNodeGetContent(node)); + scanner->caps[type].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)); + if (scanner->caps[type].MaxHeight == 0 || MaxHeight < scanner->caps[type].MaxHeight) + scanner->caps[type].MaxHeight = atoi((const char *)xmlNodeGetContent(node)); } else if (strcmp(name, "MaxScanRegions") == 0) - scanner->MaxScanRegions = atoi((const char *)xmlNodeGetContent(node)); + scanner->caps[type].MaxScanRegions = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "MaxOpticalXResolution") == 0) - scanner->MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node)); + scanner->caps[type].MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "RiskyLeftMargin") == 0) - scanner->RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node)); + scanner->caps[type].RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "RiskyRightMargin") == 0) - scanner->RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node)); + scanner->caps[type].RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "RiskyTopMargin") == 0) - scanner->RiskyTopMargin = atoi((const char *)xmlNodeGetContent(node)); + scanner->caps[type].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); + scanner->caps[type].RiskyBottomMargin = atoi((const char *)xmlNodeGetContent(node)); + find_valor_of_array_variables(node, scanner, type); return (0); } @@ -275,7 +292,7 @@ find_value_of_int_variables(xmlNode *node, capabilities_t *scanner) * \return 0 */ static int -find_true_variables(xmlNode *node, capabilities_t *scanner) +find_true_variables(xmlNode *node, capabilities_t *scanner, int type) { const char *name = (const char *)node->name; if (strcmp(name, "MinWidth") == 0 || @@ -294,7 +311,7 @@ find_true_variables(xmlNode *node, capabilities_t *scanner) strcmp(name, "RiskyTopMargin") == 0 || strcmp(name, "RiskyBottomMargin") == 0 || strcmp(name, "DocumentFormatExt") == 0) - find_value_of_int_variables(node, scanner); + find_value_of_int_variables(node, scanner, type); return (0); } @@ -305,21 +322,67 @@ find_true_variables(xmlNode *node, capabilities_t *scanner) * \return 0 */ static int -print_xml_c(xmlNode *node, capabilities_t *scanner) +print_xml_c(xmlNode *node, capabilities_t *scanner, int type) { while (node) { if (node->type == XML_ELEMENT_NODE) { - if (find_nodes_c(node)) - find_true_variables(node, scanner); + if (find_nodes_c(node) && type != -1) + find_true_variables(node, scanner, type); } - print_xml_c(node->children, scanner); + if (!strcmp((const char *)node->name, "PlatenInputCaps")) { + scanner->Sources[PLATEN] = (SANE_String_Const)strdup(SANE_I18N ("Flatbed")); + scanner->SourcesSize++; + scanner->source = PLATEN; + print_xml_c(node->children, scanner, PLATEN); + scanner->caps[PLATEN].duplex = 0; + } + else if (!strcmp((const char *)node->name, "AdfSimplexInputCaps")) { + scanner->Sources[ADFSIMPLEX] = (SANE_String_Const)strdup(SANE_I18N("ADF")); + scanner->SourcesSize++; + if (scanner->source == -1) scanner->source = ADFSIMPLEX; + print_xml_c(node->children, scanner, ADFSIMPLEX); + scanner->caps[ADFSIMPLEX].duplex = 0; + } + else if (!strcmp((const char *)node->name, "AdfDuplexInputCaps")) { + scanner->Sources[ADFDUPLEX] = (SANE_String_Const)strdup(SANE_I18N ("ADF Duplex")); + scanner->SourcesSize++; + if (scanner->source == -1) scanner->source = ADFDUPLEX; + print_xml_c(node->children, scanner, ADFDUPLEX); + scanner->caps[ADFDUPLEX].duplex = 1; + } + else + print_xml_c(node->children, scanner, type); node = node->next; } return (0); } +static void +_reduce_color_modes(capabilities_t *scanner) +{ + int type = 0; + for (type = 0; type < 3; type++) { + if (scanner->caps[type].ColorModesSize) { + if (scanner->caps[type].default_format && + strcmp(scanner->caps[type].default_format, "application/pdf")) { + if (scanner->caps[type].ColorModesSize == 3) { + free(scanner->caps[type].ColorModes); + scanner->caps[type].ColorModes = NULL; + scanner->caps[type].ColorModesSize = 0; + scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, + &scanner->caps[type].ColorModesSize, + (SANE_String_Const)SANE_VALUE_SCAN_MODE_GRAY, 0); + scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, + &scanner->caps[type].ColorModesSize, + (SANE_String_Const)SANE_VALUE_SCAN_MODE_COLOR, 0); + } + } + } + } +} + /** - * \fn capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status) + * \fn capabilities_t *escl_capabilities(const ESCL_Device *device, 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". @@ -327,18 +390,18 @@ print_xml_c(xmlNode *node, capabilities_t *scanner) * \return scanner (the structure that stocks all the capabilities elements) */ capabilities_t * -escl_capabilities(SANE_String_Const name, SANE_Status *status) +escl_capabilities(const ESCL_Device *device, 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; + int i = 0; const char *scanner_capabilities = "/eSCL/ScannerCapabilities"; - char tmp[PATH_MAX] = { 0 }; *status = SANE_STATUS_GOOD; - if (name == NULL) + if (device == NULL) *status = SANE_STATUS_NO_MEM; var = (struct cap *)calloc(1, sizeof(struct cap)); if (var == NULL) @@ -346,32 +409,41 @@ escl_capabilities(SANE_String_Const name, SANE_Status *status) 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); - } + escl_curl_url(curl_handle, device, scanner_capabilities); 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"); + CURLcode res = curl_easy_perform(curl_handle); + if (res != CURLE_OK) { + DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res)); *status = SANE_STATUS_INVAL; + goto clean_data; } + DBG( 10, "XML Capabilities[\n%s\n]\n", var->memory); data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0); - if (data == NULL) + if (data == NULL) { *status = SANE_STATUS_NO_MEM; + goto clean_data; + } node = xmlDocGetRootElement(data); - if (node == NULL) + if (node == NULL) { *status = SANE_STATUS_NO_MEM; - print_xml_c(node, scanner); + goto clean; + } + + scanner->source = 0; + scanner->Sources = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * 4); + for (i = 0; i < 4; i++) + scanner->Sources[i] = NULL; + print_xml_c(node, scanner, -1); + _reduce_color_modes(scanner); +clean: xmlFreeDoc(data); +clean_data: xmlCleanupParser(); xmlMemoryDump(); curl_easy_cleanup(curl_handle); - free(var->memory); + if (var) + free(var->memory); + free(var); return (scanner); } diff --git a/backend/escl/escl_crop.c b/backend/escl/escl_crop.c new file mode 100644 index 0000000..8740d22 --- /dev/null +++ b/backend/escl/escl_crop.c @@ -0,0 +1,102 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2020 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> + +unsigned char * +escl_crop_surface(capabilities_t *scanner, + unsigned char *surface, + int w, + int h, + int bps, + int *width, + int *height) +{ + double ratio = 1.0; + int x_off = 0, x = 0; + int real_w = 0; + int y_off = 0, y = 0; + int real_h = 0; + unsigned char *surface_crop = NULL; + + DBG( 1, "Escl Image Crop\n"); + ratio = (double)w / (double)scanner->caps[scanner->source].width; + scanner->caps[scanner->source].width = w; + if (scanner->caps[scanner->source].pos_x < 0) + scanner->caps[scanner->source].pos_x = 0; + if (scanner->caps[scanner->source].pos_x && + (scanner->caps[scanner->source].width > + scanner->caps[scanner->source].pos_x)) + x_off = (int)((double)scanner->caps[scanner->source].pos_x * ratio); + real_w = scanner->caps[scanner->source].width - x_off; + + scanner->caps[scanner->source].height = h; + if (scanner->caps[scanner->source].pos_y && + (scanner->caps[scanner->source].height > + scanner->caps[scanner->source].pos_y)) + y_off = (int)((double)scanner->caps[scanner->source].pos_y * ratio); + real_h = scanner->caps[scanner->source].height - y_off; + + DBG( 1, "Escl Image Crop [%dx%d|%dx%d]\n", scanner->caps[scanner->source].pos_x, scanner->caps[scanner->source].pos_y, + scanner->caps[scanner->source].width, scanner->caps[scanner->source].height); + + *width = real_w; + *height = real_h; + DBG( 1, "Escl Image Crop [%dx%d]\n", *width, *height); + if (x_off > 0 || real_w < scanner->caps[scanner->source].width || + y_off > 0 || real_h < scanner->caps[scanner->source].height) { + surface_crop = (unsigned char *)malloc (sizeof (unsigned char) * real_w + * real_h * bps); + if(!surface_crop) { + DBG( 1, "Escl Crop : Surface_crop Memory allocation problem\n"); + free(surface); + surface = NULL; + goto finish; + } + for (y = 0; y < real_h; y++) + { + for (x = 0; x < real_w; x++) + { + surface_crop[(y * real_w * bps) + (x * bps)] = + surface[((y + y_off) * w * bps) + ((x + x_off) * bps)]; + surface_crop[(y * real_w * bps) + (x * bps) + 1] = + surface[((y + y_off) * w * bps) + ((x + x_off) * bps) + 1]; + surface_crop[(y * real_w * bps) + (x * bps) + 2] = + surface[((y + y_off) * w * bps) + ((x + x_off) * bps) + 2]; + } + } + free(surface); + surface = surface_crop; + } + // we don't need row pointers anymore + scanner->img_data = surface; + scanner->img_size = (int)(real_w * real_h * bps); + scanner->img_read = 0; +finish: + return surface; +} diff --git a/backend/escl/escl_jpeg.c b/backend/escl/escl_jpeg.c index d6287ef..8d6b6b6 100644 --- a/backend/escl/escl_jpeg.c +++ b/backend/escl/escl_jpeg.c @@ -120,7 +120,6 @@ jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx) 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; @@ -154,7 +153,7 @@ output_no_message(j_common_ptr __sane_unused__ cinfo) * \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) +get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps) { int start = 0; struct jpeg_decompress_struct cinfo; @@ -162,6 +161,11 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps) unsigned char *surface = NULL; struct my_error_mgr jerr; int lineSize = 0; + JDIMENSION x_off = 0; + JDIMENSION y_off = 0; + JDIMENSION w = 0; + JDIMENSION h = 0; + int pos = 0; if (scanner->tmp == NULL) return (SANE_STATUS_INVAL); @@ -174,6 +178,7 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps) jpeg_destroy_decompress(&cinfo); if (surface != NULL) free(surface); + fseek(scanner->tmp, start, SEEK_SET); DBG( 1, "Escl Jpeg : Error reading jpeg\n"); if (scanner->tmp) { fclose(scanner->tmp); @@ -187,10 +192,42 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps) 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 (cinfo.output_width < (unsigned int)scanner->caps[scanner->source].width) + scanner->caps[scanner->source].width = cinfo.output_width; + if (scanner->caps[scanner->source].pos_x < 0) + scanner->caps[scanner->source].pos_x = 0; + + if (cinfo.output_height < (unsigned int)scanner->caps[scanner->source].height) + scanner->caps[scanner->source].height = cinfo.output_height; + if (scanner->caps[scanner->source].pos_y < 0) + scanner->caps[scanner->source].pos_y = 0; + DBG(10, "1-JPEF Geometry [%dx%d|%dx%d]\n", + scanner->caps[scanner->source].pos_x, + scanner->caps[scanner->source].pos_y, + scanner->caps[scanner->source].width, + scanner->caps[scanner->source].height); + x_off = scanner->caps[scanner->source].pos_x; + if (x_off > (unsigned int)scanner->caps[scanner->source].width) { + w = scanner->caps[scanner->source].width; + x_off = 0; + } + else + w = scanner->caps[scanner->source].width - x_off; + y_off = scanner->caps[scanner->source].pos_y; + if(y_off > (unsigned int)scanner->caps[scanner->source].height) { + h = scanner->caps[scanner->source].height; + y_off = 0; + } + else + h = scanner->caps[scanner->source].height - y_off; + DBG(10, "2-JPEF Geometry [%dx%d|%dx%d]\n", + x_off, + y_off, + w, + h); + surface = malloc(w * h * 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); @@ -198,17 +235,23 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps) } 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); + if (x_off > 0 || w < cinfo.output_width) + jpeg_crop_scanline(&cinfo, &x_off, &w); + lineSize = w * cinfo.output_components; + if (y_off > 0) + jpeg_skip_scanlines(&cinfo, y_off); + pos = 0; + while (cinfo.output_scanline < (unsigned int)scanner->caps[scanner->source].height) { + rowptr[0] = (JSAMPROW)surface + (lineSize * pos); // ..cinfo.output_scanline); jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1); - } + pos++; + } scanner->img_data = surface; - scanner->img_size = lineSize * cinfo.output_height; + scanner->img_size = lineSize * h; scanner->img_read = 0; - *w = cinfo.output_width; - *h = cinfo.output_height; + *width = w; + *height = h; *bps = cinfo.output_components; jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); @@ -220,8 +263,8 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps) SANE_Status get_JPEG_data(capabilities_t __sane_unused__ *scanner, - int __sane_unused__ *w, - int __sane_unused__ *h, + int __sane_unused__ *width, + int __sane_unused__ *height, int __sane_unused__ *bps) { return (SANE_STATUS_INVAL); diff --git a/backend/escl/escl_mupdf.c b/backend/escl/escl_mupdf.c new file mode 100644 index 0000000..9399218 --- /dev/null +++ b/backend/escl/escl_mupdf.c @@ -0,0 +1,256 @@ +/* sane - Scanner Access Now Easy. + + 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 <errno.h> + +#if(defined HAVE_MUPDF) +#include <mupdf/fitz.h> +#endif + +#include <setjmp.h> + + +#if(defined HAVE_MUPDF) + +// TODO: WIN32: HANDLE CreateFileW(), etc. +// TODO: POSIX: int creat(), read(), write(), lseeko, etc. + +typedef struct fz_file_stream_escl_s +{ + FILE *file; + unsigned char buffer[4096]; +} fz_file_stream_escl; + +static int +next_file_escl(fz_context *ctx, fz_stream *stm, size_t n) +{ + fz_file_stream_escl *state = stm->state; + + /* n is only a hint, that we can safely ignore */ + n = fread(state->buffer, 1, sizeof(state->buffer), state->file); + if (n < sizeof(state->buffer) && ferror(state->file)) + fz_throw(ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno)); + stm->rp = state->buffer; + stm->wp = state->buffer + n; + stm->pos += (int64_t)n; + + if (n == 0) + return EOF; + return *stm->rp++; +} + +static void +drop_file_escl(fz_context *ctx, void *state_) +{ + fz_file_stream_escl *state = state_; + int n = fclose(state->file); + if (n < 0) + fz_warn(ctx, "close error: %s", strerror(errno)); + fz_free(ctx, state); +} + +static void +seek_file_escl(fz_context *ctx, fz_stream *stm, int64_t offset, int whence) +{ + fz_file_stream_escl *state = stm->state; +#ifdef _WIN32 + int64_t n = _fseeki64(state->file, offset, whence); +#else + int64_t n = fseeko(state->file, offset, whence); +#endif + if (n < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek: %s", strerror(errno)); +#ifdef _WIN32 + stm->pos = _ftelli64(state->file); +#else + stm->pos = ftello(state->file); +#endif + stm->rp = state->buffer; + stm->wp = state->buffer; +} + +static fz_stream * +fz_open_file_ptr_escl(fz_context *ctx, FILE *file) +{ + fz_stream *stm; + fz_file_stream_escl *state = fz_malloc_struct(ctx, fz_file_stream_escl); + state->file = file; + + stm = fz_new_stream(ctx, state, next_file_escl, drop_file_escl); + stm->seek = seek_file_escl; + + return stm; +} + +/** + * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler) + * \brief Function that aims to decompress the pdf 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_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps) +{ + int page_number = -1, page_count = -2; + fz_context *ctx; + fz_document *doc; + fz_pixmap *pix; + fz_matrix ctm; + fz_stream *stream; + unsigned char *surface = NULL; /* Image data */ + SANE_Status status = SANE_STATUS_GOOD; + + /* Create a context to hold the exception stack and various caches. */ + ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); + if (!ctx) + { + DBG(1, "cannot create mupdf context\n"); + status = SANE_STATUS_INVAL; + goto close_file; + } + + /* Register the default file types to handle. */ + fz_try(ctx) + fz_register_document_handlers(ctx); + fz_catch(ctx) + { + DBG(1, "cannot register document handlers: %s\n", fz_caught_message(ctx)); + status = SANE_STATUS_INVAL; + goto drop_context; + } + + /* Open the stream. */ + fz_try(ctx) + stream = fz_open_file_ptr_escl(ctx, scanner->tmp); + fz_catch(ctx) + { + DBG(1, "cannot open stream: %s\n", fz_caught_message(ctx)); + status = SANE_STATUS_INVAL; + goto drop_context; + } + + /* Seek stream. */ + fz_try(ctx) + fz_seek(ctx, stream, 0, SEEK_SET); + fz_catch(ctx) + { + DBG(1, "cannot seek stream: %s\n", fz_caught_message(ctx)); + status = SANE_STATUS_INVAL; + goto drop_stream; + } + + /* Open the document. */ + fz_try(ctx) + doc = fz_open_document_with_stream(ctx, "filename.pdf", stream); + fz_catch(ctx) + { + DBG(1, "cannot open document: %s\n", fz_caught_message(ctx)); + status = SANE_STATUS_INVAL; + goto drop_stream; + } + + /* Count the number of pages. */ + fz_try(ctx) + page_count = fz_count_pages(ctx, doc); + fz_catch(ctx) + { + DBG(1, "cannot count number of pages: %s\n", fz_caught_message(ctx)); + status = SANE_STATUS_INVAL; + goto drop_document; + } + + if (page_number < 0 || page_number >= page_count) + { + DBG(1, "page number out of range: %d (page count %d)\n", page_number + 1, page_count); + status = SANE_STATUS_INVAL; + goto drop_document; + } + + /* Compute a transformation matrix for the zoom and rotation desired. */ + /* The default resolution without scaling is 72 dpi. */ + fz_scale(&ctm, (float)1.0, (float)1.0); + fz_pre_rotate(&ctm, (float)0.0); + + /* Render page to an RGB pixmap. */ + fz_try(ctx) + pix = fz_new_pixmap_from_page_number(ctx, doc, 0, &ctm, fz_device_rgb(ctx), 0); + fz_catch(ctx) + { + DBG(1, "cannot render page: %s\n", fz_caught_message(ctx)); + status = SANE_STATUS_INVAL; + goto drop_document; + } + + surface = malloc(pix->h * pix->stride); + memcpy(surface, pix->samples, (pix->h * pix->stride)); + + // If necessary, trim the image. + surface = escl_crop_surface(scanner, surface, pix->w, pix->h, pix->n, width, height); + if (!surface) { + DBG( 1, "Escl Pdf : Surface Memory allocation problem\n"); + status = SANE_STATUS_NO_MEM; + goto drop_pix; + } + *bps = pix->n; + + /* Clean up. */ +drop_pix: + fz_drop_pixmap(ctx, pix); +drop_document: + fz_drop_document(ctx, doc); +drop_stream: + fz_drop_stream(ctx, stream); +drop_context: + fz_drop_context(ctx); + +close_file: + if (scanner->tmp) + fclose(scanner->tmp); + scanner->tmp = NULL; + return status; +} +#else + +SANE_Status +get_PDF_data(capabilities_t __sane_unused__ *scanner, + int __sane_unused__ *width, + int __sane_unused__ *height, + int __sane_unused__ *bps) +{ + return (SANE_STATUS_INVAL); +} + +#endif diff --git a/backend/escl/escl_newjob.c b/backend/escl/escl_newjob.c index 279b9df..ee8c03c 100644 --- a/backend/escl/escl_newjob.c +++ b/backend/escl/escl_newjob.c @@ -68,18 +68,11 @@ static const char settings[] = " <scan:ColorMode>%s</scan:ColorMode>" \ " <scan:XResolution>%d</scan:XResolution>" \ " <scan:YResolution>%d</scan:YResolution>" \ - " <pwg:InputSource>Platen</pwg:InputSource>" \ + " <pwg:InputSource>%s</pwg:InputSource>" \ + " <scan:InputSource>%s</scan:InputSource>" \ + "%s" \ "</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 : @@ -122,7 +115,7 @@ download_callback(void *str, size_t size, size_t nmemb, void *userp) } /** - * \fn char *escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status) + * \fn char *escl_newjob (capabilities_t *scanner, const ESCL_Device *device, 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 @@ -131,22 +124,23 @@ download_callback(void *str, size_t size, size_t nmemb, void *userp) * \return result (the 'new job', situated in LOCATION) */ char * -escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status) +escl_newjob (capabilities_t *scanner, const ESCL_Device *device, SANE_Status *status) { CURL *curl_handle = NULL; + int off_x = 0, off_y = 0; 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; + char duplex_mode[1024] = { 0 }; *status = SANE_STATUS_GOOD; - if (name == NULL || scanner == NULL) { + if (device == NULL || scanner == NULL) { *status = SANE_STATUS_NO_MEM; DBG( 1, "Create NewJob : the name or the scan are invalid.\n"); return (NULL); @@ -165,64 +159,89 @@ escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *statu return (NULL); } curl_handle = curl_easy_init(); - if (scanner->format_ext == 1) + if (scanner->caps[scanner->source].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; + char f_ext_tmp[1024]; + snprintf(f_ext_tmp, sizeof(f_ext_tmp), + " <scan:DocumentFormatExt>%s</scan:DocumentFormatExt>", + scanner->caps[scanner->source].default_format); + format_ext = f_ext_tmp; } else format_ext = f_ext; - DBG( 1, "Create NewJob : %s\n", scanner->default_format); + if(scanner->source > PLATEN && scanner->Sources[ADFDUPLEX]) { + snprintf(duplex_mode, sizeof(duplex_mode), + " <scan:Duplex>%s</scan:Duplex>", + scanner->source == ADFDUPLEX ? "true" : "false"); + } + DBG( 1, "Create NewJob : %s\n", scanner->caps[scanner->source].default_format); + if (scanner->caps[scanner->source].pos_x > scanner->caps[scanner->source].width) + off_x = (scanner->caps[scanner->source].pos_x > scanner->caps[scanner->source].width) / 2; + if (scanner->caps[scanner->source].pos_y > scanner->caps[scanner->source].height) + off_y = (scanner->caps[scanner->source].pos_y > scanner->caps[scanner->source].height) / 2; 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); + char *source = (scanner->source == PLATEN ? "Platen" : "Feeder"); + snprintf(cap_data, sizeof(cap_data), settings, + scanner->caps[scanner->source].height, + scanner->caps[scanner->source].width, + off_x, + off_y, + scanner->caps[scanner->source].default_format, + format_ext, + scanner->caps[scanner->source].default_color, + scanner->caps[scanner->source].default_resolution, + scanner->caps[scanner->source].default_resolution, + source, + source, + duplex_mode[0] == 0 ? "" : duplex_mode); 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); - } + escl_curl_url(curl_handle, device, scan_jobs); 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"); + CURLcode res = curl_easy_perform(curl_handle); + if (res != CURLE_OK) { + DBG( 1, "Create NewJob : the scanner responded incorrectly: %s\n", curl_easy_strerror(res)); *status = SANE_STATUS_INVAL; } else { if (download->memory != NULL) { - if (strstr(download->memory, "Location:")) { - temporary = strrchr(download->memory, '/'); + char *tmp_location = strstr(download->memory, "Location:"); + if (tmp_location) { + temporary = strchr(tmp_location, '\r'); + if (temporary == NULL) + temporary = strchr(tmp_location, '\n'); 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); + *temporary = '\0'; + location = strrchr(tmp_location,'/'); + if (location) { + result = strdup(location); + DBG( 1, "Create NewJob : %s\n", result); + *temporary = '\n'; + } + } + if (result == NULL) { + DBG( 1, "Error : Create NewJob, no location: %s\n", download->memory); + *status = SANE_STATUS_INVAL; } free(download->memory); } else { - DBG( 1, "Create NewJob : The creation of the failed job\n"); - *status = SANE_STATUS_INVAL; + DBG( 1, "Create NewJob : The creation of the failed job: %s\n", download->memory); + // If "409 Conflict" appear it means that there is no paper in feeder + if (strstr(download->memory, "409 Conflict") != NULL) + *status = SANE_STATUS_NO_DOCS; + // If "503 Service Unavailable" appear, it means that device is busy (scanning in progress) + else if (strstr(download->memory, "503 Service Unavailable") != NULL) + *status = SANE_STATUS_DEVICE_BUSY; + else + *status = SANE_STATUS_INVAL; } } else { diff --git a/backend/escl/escl_pdf.c b/backend/escl/escl_pdf.c new file mode 100644 index 0000000..ae85a3a --- /dev/null +++ b/backend/escl/escl_pdf.c @@ -0,0 +1,223 @@ +/* sane - Scanner Access Now Easy. + + 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stddef.h> +#include <math.h> + +#include <errno.h> + +#if(defined HAVE_POPPLER_GLIB) +#include <poppler/glib/poppler.h> +#endif + +#include <setjmp.h> + + +#if(defined HAVE_POPPLER_GLIB) + +#define INPUT_BUFFER_SIZE 4096 + +static unsigned char* +set_file_in_buffer(FILE *fp, int *size) +{ + char buffer[1024] = { 0 }; + unsigned char *data = (unsigned char *)calloc(1, sizeof(char)); + int nx = 0; + + while(!feof(fp)) + { + int n = fread(buffer,sizeof(char),1024,fp); + unsigned char *t = realloc(data, nx + n + 1); + if (t == NULL) { + DBG(10, "not enough memory (realloc returned NULL)"); + free(data); + return NULL; + } + data = t; + memcpy(&(data[nx]), buffer, n); + nx = nx + n; + data[nx] = 0; + } + *size = nx; + return data; +} + +static unsigned char * +cairo_surface_to_pixels (cairo_surface_t *surface, int bps) +{ + int cairo_width, cairo_height, cairo_rowstride; + unsigned char *data, *dst, *cairo_data; + unsigned int *src; + int x, y; + + cairo_width = cairo_image_surface_get_width (surface); + cairo_height = cairo_image_surface_get_height (surface); + cairo_rowstride = cairo_image_surface_get_stride (surface); + cairo_data = cairo_image_surface_get_data (surface); + data = (unsigned char*)calloc(1, sizeof(unsigned char) * (cairo_height * cairo_width * bps)); + + for (y = 0; y < cairo_height; y++) + { + src = (unsigned int *) (cairo_data + y * cairo_rowstride); + dst = data + y * (cairo_width * bps); + for (x = 0; x < cairo_width; x++) + { + dst[0] = (*src >> 16) & 0xff; + dst[1] = (*src >> 8) & 0xff; + dst[2] = (*src >> 0) & 0xff; + dst += bps; + src++; + } + } + return data; +} + +SANE_Status +get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps) +{ + cairo_surface_t *cairo_surface = NULL; + cairo_t *cr; + PopplerPage *page; + PopplerDocument *doc; + double dw, dh; + int w, h, size = 0; + char *data = NULL; + unsigned char* surface = NULL; + SANE_Status status = SANE_STATUS_GOOD; + + + data = (char*)set_file_in_buffer(scanner->tmp, &size); + if (!data) { + DBG(1, "Error : poppler_document_new_from_data"); + status = SANE_STATUS_INVAL; + goto close_file; + } + doc = poppler_document_new_from_data(data, + size, + NULL, + NULL); + + if (!doc) { + DBG(1, "Error : poppler_document_new_from_data"); + status = SANE_STATUS_INVAL; + goto free_file; + } + + page = poppler_document_get_page (doc, 0); + if (!page) { + DBG(1, "Error : poppler_document_get_page"); + status = SANE_STATUS_INVAL; + goto free_doc; + } + + poppler_page_get_size (page, &dw, &dh); + dw = (double)scanner->caps[scanner->source].default_resolution * dw / 72.0; + dh = (double)scanner->caps[scanner->source].default_resolution * dh / 72.0; + w = (int)ceil(dw); + h = (int)ceil(dh); + cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + if (!cairo_surface) { + DBG(1, "Error : cairo_image_surface_create"); + status = SANE_STATUS_INVAL; + goto free_page; + } + + cr = cairo_create (cairo_surface); + if (!cairo_surface) { + DBG(1, "Error : cairo_create"); + status = SANE_STATUS_INVAL; + goto free_surface; + } + cairo_scale (cr, (double)scanner->caps[scanner->source].default_resolution / 72.0, + (double)scanner->caps[scanner->source].default_resolution / 72.0); + cairo_save (cr); + poppler_page_render (page, cr); + cairo_restore (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + int st = cairo_status(cr); + if (st) + { + DBG(1, "%s", cairo_status_to_string (st)); + status = SANE_STATUS_INVAL; + goto destroy_cr; + } + + *bps = 3; + + DBG(1, "Escl Pdf : Image Size [%dx%d]\n", w, h); + + surface = cairo_surface_to_pixels (cairo_surface, *bps); + if (!surface) { + status = SANE_STATUS_NO_MEM; + DBG(1, "Escl Pdf : Surface Memory allocation problem"); + goto destroy_cr; + } + + // If necessary, trim the image. + surface = escl_crop_surface(scanner, surface, w, h, *bps, width, height); + if (!surface) { + DBG(1, "Escl Pdf Crop: Surface Memory allocation problem"); + status = SANE_STATUS_NO_MEM; + } + +destroy_cr: + cairo_destroy (cr); +free_surface: + cairo_surface_destroy (cairo_surface); +free_page: + g_object_unref (page); +free_doc: + g_object_unref (doc); +free_file: + free(data); +close_file: + if (scanner->tmp) + fclose(scanner->tmp); + scanner->tmp = NULL; + return status; +} +#else + +SANE_Status +get_PDF_data(capabilities_t __sane_unused__ *scanner, + int __sane_unused__ *width, + int __sane_unused__ *height, + int __sane_unused__ *bps) +{ + return (SANE_STATUS_INVAL); +} + +#endif diff --git a/backend/escl/escl_png.c b/backend/escl/escl_png.c index 18f6f35..cf92449 100644 --- a/backend/escl/escl_png.c +++ b/backend/escl/escl_png.c @@ -49,14 +49,15 @@ * \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) +get_PNG_data(capabilities_t *scanner, int *width, int *height, int *bps) { - 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 w = 0; + unsigned int h = 0; + int components = 3; + unsigned char *surface = NULL; /* Image data */ unsigned int i = 0; png_byte magic[8]; + SANE_Status status = SANE_STATUS_GOOD; // read magic number fread (magic, 1, sizeof (magic), scanner->tmp); @@ -64,11 +65,8 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components) 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); + status = SANE_STATUS_INVAL; + goto close_file; } // create a png read struct png_structp png_ptr = png_create_read_struct @@ -76,12 +74,8 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components) 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); + status = SANE_STATUS_INVAL; + goto close_file; } // create a png info struct png_infop info_ptr = png_create_info_struct (png_ptr); @@ -89,26 +83,19 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components) { 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); + status = SANE_STATUS_INVAL; + goto close_file; } // 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; - } + if (surface) + free (surface); DBG( 1, "Escl Png : PNG read error.\n"); - return (SANE_STATUS_INVAL); + status = SANE_STATUS_INVAL; + goto close_file; } // setup libpng for using standard C fread() function // with our FILE pointer @@ -128,63 +115,79 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components) 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); + DBG(1, "PNG format not supported.\n"); + status = SANE_STATUS_NO_MEM; + goto close_file; } + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bps = 4; + components = 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); + components = 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*)(&w), + (png_uint_32*)(&h), + &bit_depth, &color_type, + NULL, NULL, NULL); + + *bps = components; + // we can now allocate memory for storing pixel data + surface = (unsigned char *)malloc (sizeof (unsigned char) * w + * h * components); + if (!surface) { + DBG( 1, "Escl Png : texels Memory allocation problem\n"); + status = SANE_STATUS_NO_MEM; + goto close_file; + } + 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) * h); + if (!row_pointers) { + DBG( 1, "Escl Png : row_pointers Memory allocation problem\n"); + free(surface); + status = SANE_STATUS_NO_MEM; + goto close_file; + } + for (i = 0; i < h; ++i) + { + row_pointers[i] = (png_bytep)(surface + + ((h - (i + 1)) * w * components)); + } + // read pixel data using row pointers + png_read_image (png_ptr, row_pointers); + + // If necessary, trim the image. + surface = escl_crop_surface(scanner, surface, w, h, components, width, height); + if (!surface) { + DBG( 1, "Escl Png : Surface Memory allocation problem\n"); + status = SANE_STATUS_NO_MEM; + goto close_file; + } + + free (row_pointers); + +close_file: + if (scanner->tmp) + fclose(scanner->tmp); scanner->tmp = NULL; - return (SANE_STATUS_GOOD); + return (status); } #else SANE_Status get_PNG_data(capabilities_t __sane_unused__ *scanner, - int __sane_unused__ *w, - int __sane_unused__ *h, + int __sane_unused__ *width, + int __sane_unused__ *height, int __sane_unused__ *bps) { return (SANE_STATUS_INVAL); diff --git a/backend/escl/escl_reset.c b/backend/escl/escl_reset.c index 7722d89..64d779a 100644 --- a/backend/escl/escl_reset.c +++ b/backend/escl/escl_reset.c @@ -31,13 +31,22 @@ #include <curl/curl.h> +static size_t +write_callback(void __sane_unused__*str, + size_t __sane_unused__ size, + size_t nmemb, + void __sane_unused__ *userp) +{ + return nmemb; +} + /** - * \fn void escl_scanner(SANE_String_Const name, char *result) + * \fn void escl_scanner(const ESCL_Device *device, 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) +escl_scanner(const ESCL_Device *device, char *result) { CURL *curl_handle = NULL; const char *scan_jobs = "/eSCL/ScanJobs"; @@ -46,30 +55,25 @@ escl_scanner(SANE_String_Const name, char *result) int i = 0; long answer = 0; - if (name == NULL || result == NULL) + if (device == 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); - } + snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s", + scan_jobs, result, scanner_start); + escl_curl_url(curl_handle, device, scan_cmd); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); 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; - } + i++; + if (i >= 15) return; } curl_easy_cleanup(curl_handle); + if (SANE_STATUS_GOOD != escl_status(device, + PLATEN, + NULL, + NULL)) + goto CURL_CALL; } } diff --git a/backend/escl/escl_scan.c b/backend/escl/escl_scan.c index 8f077a1..9fce801 100644 --- a/backend/escl/escl_scan.c +++ b/backend/escl/escl_scan.c @@ -43,13 +43,14 @@ 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); - + capabilities_t *scanner = (capabilities_t *)userp; + size_t to_write = fwrite(str, size, nmemb, scanner->tmp); + scanner->real_read += to_write; return (to_write); } /** - * \fn SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result) + * \fn SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, 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 @@ -58,7 +59,7 @@ write_callback(void *str, size_t size, size_t nmemb, void *userp) * \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) +escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *result) { CURL *curl_handle = NULL; const char *scan_jobs = "/eSCL/ScanJobs"; @@ -66,34 +67,41 @@ escl_scan(capabilities_t __sane_unused__ *scanner, SANE_String_Const name, char char scan_cmd[PATH_MAX] = { 0 }; SANE_Status status = SANE_STATUS_GOOD; - if (name == NULL) + if (device == NULL) return (SANE_STATUS_NO_MEM); + scanner->real_read = 0; 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); - } + snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s", + scan_jobs, result, scanner_start); + escl_curl_url(curl_handle, device, scan_cmd); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); + if (scanner->tmp) + fclose(scanner->tmp); scanner->tmp = tmpfile(); if (scanner->tmp != NULL) { - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner->tmp); - if (curl_easy_perform(curl_handle) != CURLE_OK) { + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner); + CURLcode res = curl_easy_perform(curl_handle); + if (res != CURLE_OK) { + DBG( 1, "Unable to scan: %s\n", curl_easy_strerror(res)); + fclose(scanner->tmp); + scanner->tmp = NULL; status = SANE_STATUS_INVAL; + goto cleanup; } - else - curl_easy_cleanup(curl_handle); fseek(scanner->tmp, 0, SEEK_SET); } else status = SANE_STATUS_NO_MEM; +cleanup: + curl_easy_cleanup(curl_handle); + } + DBG(10, "eSCL scan : [%s]\treal read (%ld)\n", sane_strstatus(status), scanner->real_read); + if (scanner->real_read == 0) + { + fclose(scanner->tmp); + scanner->tmp = NULL; + return SANE_STATUS_NO_DOCS; } return (status); } diff --git a/backend/escl/escl_status.c b/backend/escl/escl_status.c index 68b51dc..7b98566 100644 --- a/backend/escl/escl_status.c +++ b/backend/escl/escl_status.c @@ -83,34 +83,105 @@ find_nodes_s(xmlNode *node) 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) +print_xml_job_status(xmlNode *node, + SANE_Status *job, + int *image) { - int x = 0; + while (node) { + if (node->type == XML_ELEMENT_NODE) { + if (find_nodes_s(node)) { + if (strcmp((const char *)node->name, "JobState") == 0) { + const char *state = (const char *)xmlNodeGetContent(node); + if (!strcmp(state, "Processing")) { + *job = SANE_STATUS_DEVICE_BUSY; + DBG(10, "jobId Processing SANE_STATUS_DEVICE_BUSY\n"); + } + else if (!strcmp(state, "Completed")) { + *job = SANE_STATUS_GOOD; + DBG(10, "jobId Completed SANE_STATUS_GOOD\n"); + } + else if (strcmp((const char *)node->name, "ImagesToTransfer") == 0) { + const char *state = (const char *)xmlNodeGetContent(node); + *image = atoi(state); + } + } + } + } + print_xml_job_status(node->children, job, image); + node = node->next; + } +} +static void +print_xml_platen_and_adf_status(xmlNode *node, + SANE_Status *platen, + SANE_Status *adf, + const char* jobId, + SANE_Status *job, + int *image) +{ while (node) { if (node->type == XML_ELEMENT_NODE) { if (find_nodes_s(node)) { - if (strcmp((const char *)node->name, "State") == 0) - x = 1; + if (strcmp((const char *)node->name, "State") == 0) { + DBG(10, "State\t"); + const char *state = (const char *)xmlNodeGetContent(node); + if (!strcmp(state, "Idle")) { + DBG(10, "Idle SANE_STATUS_GOOD\n"); + *platen = SANE_STATUS_GOOD; + } else if (!strcmp(state, "Processing")) { + DBG(10, "Processing SANE_STATUS_DEVICE_BUSY\n"); + *platen = SANE_STATUS_DEVICE_BUSY; + } else { + DBG(10, "%s SANE_STATUS_UNSUPPORTED\n", state); + *platen = SANE_STATUS_UNSUPPORTED; + } + } + // Thank's Alexander Pevzner (pzz@apevzner.com) + else if (adf && strcmp((const char *)node->name, "AdfState") == 0) { + const char *state = (const char *)xmlNodeGetContent(node); + if (!strcmp(state, "ScannerAdfLoaded")){ + DBG(10, "ScannerAdfLoaded SANE_STATUS_GOOD\n"); + *adf = SANE_STATUS_GOOD; + } else if (!strcmp(state, "ScannerAdfJam")) { + DBG(10, "ScannerAdfJam SANE_STATUS_JAMMED\n"); + *adf = SANE_STATUS_JAMMED; + } else if (!strcmp(state, "ScannerAdfDoorOpen")) { + DBG(10, "ScannerAdfDoorOpen SANE_STATUS_COVER_OPEN\n"); + *adf = SANE_STATUS_COVER_OPEN; + } else if (!strcmp(state, "ScannerAdfProcessing")) { + /* Kyocera version */ + DBG(10, "ScannerAdfProcessing SANE_STATUS_NO_DOC\n"); + *adf = SANE_STATUS_NO_DOCS; + } else if (!strcmp(state, "ScannerAdfEmpty")) { + DBG(10, "ScannerAdfEmpty SANE_STATUS_NO_DOCS\n"); + /* Cannon TR4500, EPSON XP-7100 */ + *adf = SANE_STATUS_NO_DOCS; + } else { + DBG(10, "%s SANE_STATUS_NO_DOCS\n", state); + *adf = SANE_STATUS_UNSUPPORTED; + } + } + else if (jobId && job && strcmp((const char *)node->name, "JobUri") == 0) { + if (strstr((const char *)xmlNodeGetContent(node), jobId)) { + print_xml_job_status(node, job, image); + } + } } - if (x == 1 && strcmp((const char *)xmlNodeGetContent(node), "Idle") == 0) - *status = SANE_STATUS_GOOD; } - print_xml_s(node->children, status); + print_xml_platen_and_adf_status(node->children, + platen, + adf, + jobId, + job, + image); node = node->next; } } /** - * \fn SANE_Status escl_status(SANE_String_Const name) + * \fn SANE_Status escl_status(const ESCL_Device *device) * \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". @@ -118,40 +189,45 @@ print_xml_s(xmlNode *node, SANE_Status *status) * \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) +escl_status(const ESCL_Device *device, + int source, + const char* jobId, + SANE_Status *job) { - SANE_Status status; + SANE_Status status = SANE_STATUS_DEVICE_BUSY; + SANE_Status platen= SANE_STATUS_DEVICE_BUSY; + SANE_Status adf= SANE_STATUS_DEVICE_BUSY; 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 }; + int image = -1; + int pass = 0; +reload: - if (name == NULL) + if (device == NULL) return (SANE_STATUS_NO_MEM); + status = SANE_STATUS_DEVICE_BUSY; + platen= SANE_STATUS_DEVICE_BUSY; + adf= SANE_STATUS_DEVICE_BUSY; 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); - } + + escl_curl_url(curl_handle, device, scanner_status); 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"); + CURLcode res = curl_easy_perform(curl_handle); + if (res != CURLE_OK) { + DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res)); status = SANE_STATUS_INVAL; goto clean_data; } + DBG( 10, "eSCL : Status : %s.\n", var->memory); data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0); if (data == NULL) { status = SANE_STATUS_NO_MEM; @@ -162,8 +238,18 @@ escl_status(SANE_String_Const name) status = SANE_STATUS_NO_MEM; goto clean; } - status = SANE_STATUS_DEVICE_BUSY; - print_xml_s(node, &status); + /* Decode Job status */ + // Thank's Alexander Pevzner (pzz@apevzner.com) + print_xml_platen_and_adf_status(node, &platen, &adf, jobId, job, &image); + if (platen != SANE_STATUS_GOOD && + platen != SANE_STATUS_UNSUPPORTED) { + status = platen; + } else if (source == PLATEN) { + status = platen; + } else { + status = adf; + } + DBG (10, "STATUS : %s\n", sane_strstatus(status)); clean: xmlFreeDoc(data); clean_data: @@ -172,5 +258,14 @@ clean_data: curl_easy_cleanup(curl_handle); free(var->memory); free(var); + if (pass == 0 && + source != PLATEN && + image == 0 && + (status == SANE_STATUS_GOOD || + status == SANE_STATUS_UNSUPPORTED || + status == SANE_STATUS_DEVICE_BUSY)) { + pass = 1; + goto reload; + } return (status); } diff --git a/backend/escl/escl_tiff.c b/backend/escl/escl_tiff.c index 52aec20..98bc5f3 100644 --- a/backend/escl/escl_tiff.c +++ b/backend/escl/escl_tiff.c @@ -50,60 +50,59 @@ * \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) +get_TIFF_data(capabilities_t *scanner, int *width, int *height, int *bps) { TIFF* tif = NULL; - uint32 width = 0; /* largeur */ - uint32 height = 0; /* hauteur */ - unsigned char *raster = NULL; /* données de l'image */ - int bps = 4; + uint32 w = 0; + uint32 h = 0; + unsigned char *surface = NULL; /* image data*/ + int components = 4; uint32 npixels = 0; + SANE_Status status = SANE_STATUS_GOOD; 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); + status = SANE_STATUS_INVAL; + goto close_file; } - TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); - TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); - npixels = width * height; - raster = (unsigned char*) malloc(npixels * sizeof (uint32)); - if (raster != NULL) + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); + npixels = w * h; + surface = (unsigned char*) malloc(npixels * sizeof (uint32)); + if (surface != NULL) { - DBG( 1, "Escl Tiff : Memory allocation problem.\n"); - if (scanner->tmp) { - fclose(scanner->tmp); - scanner->tmp = NULL; - } - return (SANE_STATUS_INVAL); + DBG( 1, "Escl Tiff : raster Memory allocation problem.\n"); + status = SANE_STATUS_INVAL; + goto close_tiff; } - if (!TIFFReadRGBAImage(tif, width, height, (uint32 *)raster, 0)) + if (!TIFFReadRGBAImage(tif, w, h, (uint32 *)surface, 0)) { DBG( 1, "Escl Tiff : Problem reading image data.\n"); - if (scanner->tmp) { - fclose(scanner->tmp); - scanner->tmp = NULL; - } - return (SANE_STATUS_INVAL); + status = SANE_STATUS_INVAL; + free(surface); + goto close_tiff; } - *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; + + *bps = components; + + // If necessary, trim the image. + surface = escl_crop_surface(scanner, surface, w, h, components, width, height); + if (!surface) { + DBG( 1, "Escl Tiff : Surface Memory allocation problem\n"); + status = SANE_STATUS_INVAL; + } + +close_tiff: TIFFClose(tif); - fclose(scanner->tmp); +close_file: + if (scanner->tmp) + fclose(scanner->tmp); scanner->tmp = NULL; - return (SANE_STATUS_GOOD); + return (status); } #else diff --git a/backend/fujitsu-scsi.h b/backend/fujitsu-scsi.h index 38425a6..c2b28dd 100644 --- a/backend/fujitsu-scsi.h +++ b/backend/fujitsu-scsi.h @@ -383,6 +383,9 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) #define get_IN_op_halt(in) getbitfield(in+0x7a, 1, 7) +#define get_IN_return_path(in) getbitfield(in+0x7c, 1, 7) +#define get_IN_energy_star3(in) getbitfield(in+0x7c, 1, 6) + /* ==================================================================== */ /* page codes used by mode_sense and mode_select */ #define MS_pc_unk 0x2c /* Used by iX500 */ @@ -763,6 +766,7 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) #define get_GHS_fb_open(in) getbitfield(in+0x03, 1, 3) #define get_GHS_paper_end(in) getbitfield(in+0x03, 1, 2) #define get_GHS_fb_on(in) getbitfield(in+0x03, 1, 1) +#define get_GHS_exit(in) getbitfield(in+0x03, 1, 0) #define get_GHS_sleep(in) getbitfield(in+0x04, 1, 7) #define get_GHS_clean(in) getbitfield(in+0x04, 1, 6) @@ -823,7 +827,8 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) #define SCANNER_CONTROL_len 10 #define set_SC_ric(icb, val) setbitfield(icb + 1, 1, 4, val) -#define set_SC_function(icb, val) setbitfield(icb + 1, 0xf, 0, val) +#define set_SC_function_1(icb, val) setbitfield(icb + 1, 0xf, 0, val) +#define set_SC_function_2(icb, val) icb[2] = (val >> 4) #define SC_function_adf 0x00 #define SC_function_fb 0x01 #define SC_function_fb_hs 0x02 @@ -836,6 +841,9 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) #define SC_function_scan_complete 0x09 #define SC_function_eject_complete 0x0a #define SC_function_manual_feed 0x0c +#define SC_function_mfeed 0x0f +#define SC_function_continuous 0x1f +#define SC_function_rpath 0x2f /* used with SC_function_panel */ #define set_SC_led_eb(icb, val) setbitfield(icb + 5, 1, 7, val) diff --git a/backend/fujitsu.c b/backend/fujitsu.c index 5dc466c..d24975e 100644 --- a/backend/fujitsu.c +++ b/backend/fujitsu.c @@ -603,8 +603,12 @@ v134 2019-02-23, MAN - rewrite init_vpd for scanners which fail to report overscan correctly - v135 2019-11-10, MAN + v135 2019-11-10, MAN (SANE 1.0.29) - set has_MS_lamp=0 for fi-72x0, bug #134 + v136 2020-02-07, MAN + - add support for fi-800R + - add support for card scanning slot (Return Path) + - fix bug with reading hardware sensors on first invocation SANE FLOW DIAGRAM @@ -654,7 +658,7 @@ #include "fujitsu.h" #define DEBUG 1 -#define BUILD 134 +#define BUILD 136 /* values for SANE_DEBUG_FUJITSU env var: - errors 5 @@ -678,6 +682,9 @@ #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFBACK SANE_I18N("ADF Back") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") +#define STRING_CARDFRONT SANE_I18N("Card Front") +#define STRING_CARDBACK SANE_I18N("Card Back") +#define STRING_CARDDUPLEX SANE_I18N("Card Duplex") #define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART #define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE @@ -1821,6 +1828,12 @@ init_vpd (struct fujitsu *s) DBG (15, " object position halt: %d\n", s->has_op_halt); } + if (payload_off >= 0x7c) { + s->has_return_path = get_IN_return_path(in); + DBG (15, " return path (card) scanning: %d\n", s->has_return_path); + DBG (15, " energy star 3: %d\n", get_IN_energy_star3(in)); + } + DBG (10, "init_vpd: finish\n"); return SANE_STATUS_GOOD; @@ -2498,6 +2511,8 @@ init_user (struct fujitsu *s) s->source = SOURCE_FLATBED; else if(s->has_adf) s->source = SOURCE_ADF_FRONT; + else if(s->has_return_path) + s->source = SOURCE_CARD_FRONT; /* scan mode */ if(s->can_mode[MODE_LINEART]) @@ -2875,6 +2890,16 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) s->source_list[i++]=STRING_ADFDUPLEX; } } + if(s->has_return_path){ + s->source_list[i++]=STRING_CARDFRONT; + + if(s->has_back){ + s->source_list[i++]=STRING_CARDBACK; + } + if(s->has_duplex){ + s->source_list[i++]=STRING_CARDDUPLEX; + } + } s->source_list[i]=NULL; opt->name = SANE_NAME_SCAN_SOURCE; @@ -3049,7 +3074,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->paper_x_range; - if(s->has_adf){ + if(s->has_adf || s->has_return_path){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; @@ -3076,7 +3101,7 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->paper_y_range; - if(s->has_adf){ + if(s->has_adf || s->has_return_path){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; @@ -4474,6 +4499,18 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) opt->cap = SANE_CAP_INACTIVE; } + if(option==OPT_CARD_LOADED){ + opt->name = "card-loaded"; + opt->title = SANE_I18N ("Card loaded"); + opt->desc = SANE_I18N ("Card slot contains paper"); + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status && s->has_return_path) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + if(option==OPT_SLEEP){ opt->name = "power-save"; opt->title = SANE_I18N ("Power saving"); @@ -4697,6 +4734,15 @@ sane_control_option (SANE_Handle handle, SANE_Int option, else if(s->source == SOURCE_ADF_DUPLEX){ strcpy (val, STRING_ADFDUPLEX); } + else if(s->source == SOURCE_CARD_FRONT){ + strcpy (val, STRING_CARDFRONT); + } + else if(s->source == SOURCE_CARD_BACK){ + strcpy (val, STRING_CARDBACK); + } + else if(s->source == SOURCE_CARD_DUPLEX){ + strcpy (val, STRING_CARDDUPLEX); + } return SANE_STATUS_GOOD; case OPT_MODE: @@ -5215,6 +5261,11 @@ sane_control_option (SANE_Handle handle, SANE_Int option, *val_p = s->hw_adf_open; return ret; + case OPT_CARD_LOADED: + ret = get_hardware_status(s,option); + *val_p = s->hw_card_loaded; + return ret; + case OPT_SLEEP: ret = get_hardware_status(s,option); *val_p = s->hw_sleep; @@ -5323,6 +5374,15 @@ sane_control_option (SANE_Handle handle, SANE_Int option, else if (!strcmp (val, STRING_ADFDUPLEX)) { tmp = SOURCE_ADF_DUPLEX; } + else if (!strcmp (val, STRING_CARDFRONT)) { + tmp = SOURCE_CARD_FRONT; + } + else if (!strcmp (val, STRING_CARDBACK)) { + tmp = SOURCE_CARD_BACK; + } + else if (!strcmp (val, STRING_CARDDUPLEX)) { + tmp = SOURCE_CARD_DUPLEX; + } else{ tmp = SOURCE_FLATBED; } @@ -5912,12 +5972,12 @@ get_hardware_status (struct fujitsu *s, SANE_Int option) /* only run this if frontend has already read the last time we got it */ /* or if we don't care for such bookkeeping (private use) */ - if (!option || s->hw_read[option-OPT_TOP]) { + if (!option || !s->hw_data_avail[option-OPT_TOP]) { DBG (15, "get_hardware_status: running\n"); - /* mark all values as unread */ - memset(s->hw_read,0,sizeof(s->hw_read)); + /* mark all values as available */ + memset(s->hw_data_avail,1,sizeof(s->hw_data_avail)); if (s->has_cmd_hw_status){ unsigned char cmd[GET_HW_STATUS_len]; @@ -5950,6 +6010,7 @@ get_hardware_status (struct fujitsu *s, SANE_Int option) s->hw_hopper = get_GHS_hopper(in); s->hw_omr = get_GHS_omr(in); s->hw_adf_open = get_GHS_adf_open(in); + s->hw_card_loaded = get_GHS_exit(in); s->hw_sleep = get_GHS_sleep(in); s->hw_send_sw = get_GHS_send_sw(in); @@ -6015,7 +6076,7 @@ get_hardware_status (struct fujitsu *s, SANE_Int option) } if(option) - s->hw_read[option-OPT_TOP] = 1; + s->hw_data_avail[option-OPT_TOP] = 0; DBG (10, "get_hardware_status: finish\n"); @@ -6905,8 +6966,8 @@ sane_start (SANE_Handle handle) } /* low mem mode messes up the side marker, reset it */ - if(s->source == SOURCE_ADF_DUPLEX && s->low_mem - && s->eof_tx[SIDE_FRONT] && s->eof_tx[SIDE_BACK] + if((s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) + && s->low_mem && s->eof_tx[SIDE_FRONT] && s->eof_tx[SIDE_BACK] ){ s->side = SIDE_BACK; } @@ -6915,7 +6976,7 @@ sane_start (SANE_Handle handle) if(!s->started){ /* load side marker */ - if(s->source == SOURCE_ADF_BACK){ + if(s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK){ s->side = SIDE_BACK; } else{ @@ -6936,6 +6997,12 @@ sane_start (SANE_Handle handle) DBG (5, "sane_start: ERROR: cannot control fb, ignoring\n"); } } + else if(s->source == SOURCE_CARD_FRONT || s->source == SOURCE_CARD_BACK || s->source == SOURCE_CARD_DUPLEX){ + ret = scanner_control(s, SC_function_rpath); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot control rp, ignoring\n"); + } + } else{ ret = scanner_control(s, SC_function_adf); if (ret != SANE_STATUS_GOOD) { @@ -7038,7 +7105,7 @@ sane_start (SANE_Handle handle) } } /* if already running, duplex needs to switch sides */ - else if(s->source == SOURCE_ADF_DUPLEX){ + else if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX){ s->side = !s->side; } @@ -7047,7 +7114,7 @@ sane_start (SANE_Handle handle) /* otherwise buffered back page will be lost */ /* ingest paper with adf (no-op for fb) */ /* dont call object pos or scan on back side of duplex scan */ - if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK){ + if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK){ s->bytes_rx[0]=0; s->bytes_rx[1]=0; @@ -7095,7 +7162,7 @@ sane_start (SANE_Handle handle) } /* store the number of front bytes */ - if ( s->source != SOURCE_ADF_BACK ){ + if ( s->source != SOURCE_ADF_BACK && s->source != SOURCE_CARD_BACK ){ s->bytes_tot[SIDE_FRONT] = s->s_params.bytes_per_line * s->s_params.lines; s->buff_tot[SIDE_FRONT] = s->buffer_size; @@ -7114,13 +7181,14 @@ sane_start (SANE_Handle handle) } /* store the number of back bytes */ - if ( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK ){ + if ( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK + || s->source == SOURCE_CARD_DUPLEX || s->source == SOURCE_CARD_BACK ){ s->bytes_tot[SIDE_BACK] = s->s_params.bytes_per_line * s->s_params.lines; s->buff_tot[SIDE_BACK] = s->bytes_tot[SIDE_BACK]; /* the back buffer is normally very large, but some scanners or * option combinations dont need it, so we make a small one */ - if(s->low_mem || s->source == SOURCE_ADF_BACK + if(s->low_mem || s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK || s->duplex_interlace == DUPLEX_INTERLACE_NONE) s->buff_tot[SIDE_BACK] = s->buffer_size; } @@ -7308,13 +7376,14 @@ scanner_control (struct fujitsu *s, int function) memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SCANNER_CONTROL_code); - set_SC_function (cmd, function); + set_SC_function_1 (cmd, function); + set_SC_function_2 (cmd, function); DBG (15, "scanner_control: function %d\n",function); /* don't really need to ask for adf if that's the only option */ /* doing so causes the 3091 to complain */ - if(function == SC_function_adf && !s->has_flatbed){ + if(function == SC_function_adf && !s->has_flatbed && !s->has_return_path){ DBG (10, "scanner_control: adf function not required\n"); return ret; } @@ -7486,7 +7555,7 @@ set_window (struct fujitsu *s) set_WPDB_wdblen(header, SW_desc_len); /* init the window block */ - if (s->source == SOURCE_ADF_BACK) { + if (s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK) { set_WD_wid (desc1, WD_wid_back); } else{ @@ -7675,7 +7744,7 @@ set_window (struct fujitsu *s) } /* when in duplex mode, copy first desc block into second */ - if (s->source == SOURCE_ADF_DUPLEX) { + if (s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) { memcpy (desc2, desc1, SW_desc_len); set_WD_wid (desc2, WD_wid_back); @@ -7823,7 +7892,7 @@ get_pixelsize(struct fujitsu *s, int actual) } /* - * Issues the SCSI OBJECT POSITION command if an ADF is in use. + * Issues the SCSI OBJECT POSITION command if an ADF or card scanner is in use. */ static SANE_Status object_position (struct fujitsu *s, int action) @@ -7880,9 +7949,9 @@ start_scan (struct fujitsu *s) DBG (10, "start_scan: start\n"); - if (s->source != SOURCE_ADF_DUPLEX) { + if (s->source != SOURCE_ADF_DUPLEX && s->source != SOURCE_CARD_DUPLEX) { outLen--; - if(s->source == SOURCE_ADF_BACK) { + if(s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK) { out[0] = WD_wid_back; } } @@ -7983,7 +8052,8 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len /* swap sides if user asked for low-mem mode, we are duplexing, * and there is data waiting on the other side */ - if(s->low_mem && s->source == SOURCE_ADF_DUPLEX + if(s->low_mem + && (s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && (s->bytes_rx[!s->side] > s->bytes_tx[!s->side] || (s->eof_rx[!s->side] && !s->eof_tx[!s->side]) ) @@ -8013,7 +8083,7 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len } /* end 3091 */ /* alternating jpeg duplex interlacing */ - else if(s->source == SOURCE_ADF_DUPLEX + else if((s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && s->s_params.format == SANE_FRAME_JPEG && s->jpeg_interlace == JPEG_INTERLACE_ALT ){ @@ -8025,7 +8095,7 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len } /* end alt jpeg */ /* alternating pnm duplex interlacing */ - else if(s->source == SOURCE_ADF_DUPLEX + else if((s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && s->s_params.format != SANE_FRAME_JPEG && s->duplex_interlace == DUPLEX_INTERLACE_ALT ){ @@ -8080,7 +8150,8 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len /* swap sides if user asked for low-mem mode, we are duplexing, * and there is data waiting on the other side */ - if(s->low_mem && s->source == SOURCE_ADF_DUPLEX + if(s->low_mem + && (s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && (s->bytes_rx[!s->side] > s->bytes_tx[!s->side] || (s->eof_rx[!s->side] && !s->eof_tx[!s->side]) ) @@ -9291,6 +9362,10 @@ sense_handler (int fd, unsigned char * sensed_data, void *arg) DBG (5, "Medium error: Carrier sheet\n"); return SANE_STATUS_JAMMED; } + if (0x0c == ascq) { + DBG (5, "Medium error: ADF blocked by card\n"); + return SANE_STATUS_JAMMED; + } if (0x10 == ascq) { DBG (5, "Medium error: no ink cartridge\n"); return SANE_STATUS_IO_ERROR; @@ -10092,7 +10167,9 @@ buffer_deskew(struct fujitsu *s, int side) DBG (10, "buffer_deskew: start\n"); /*only find skew on first image from a page, or if first image had error */ - if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK || s->deskew_stat){ + if(s->side == SIDE_FRONT + || s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK + || s->deskew_stat){ s->deskew_stat = sanei_magic_findSkew( &s->s_params,s->buffers[side],s->resolution_x,s->resolution_y, diff --git a/backend/fujitsu.conf.in b/backend/fujitsu.conf.in index 4f2b1a9..8e2115f 100644 --- a/backend/fujitsu.conf.in +++ b/backend/fujitsu.conf.in @@ -255,3 +255,12 @@ usb 0x04c5 0x1522 #ScanSnap iX1500 usb 0x04c5 0x159f + +#fi-800R +usb 0x04c5 0x15fc + +#fi-7900 +usb 0x04c5 0x160a + +#fi-7800 +usb 0x04c5 0x160b diff --git a/backend/fujitsu.h b/backend/fujitsu.h index 4c20474..3b3ce54 100644 --- a/backend/fujitsu.h +++ b/backend/fujitsu.h @@ -112,6 +112,7 @@ enum fujitsu_Option OPT_HOPPER, OPT_OMR, OPT_ADF_OPEN, + OPT_CARD_LOADED, OPT_SLEEP, OPT_SEND_SW, OPT_MANUAL_FEED, @@ -277,6 +278,7 @@ struct fujitsu int has_comp_JPG2; int has_comp_JPG3; int has_op_halt; + int has_return_path; /*FIXME: more endorser data? */ int endorser_type_f; @@ -361,7 +363,7 @@ struct fujitsu /*mode group*/ SANE_String_Const mode_list[7]; - SANE_String_Const source_list[5]; + SANE_String_Const source_list[8]; SANE_Int res_list[17]; SANE_Range res_range; @@ -599,6 +601,7 @@ struct fujitsu int hw_hopper; int hw_omr; int hw_adf_open; + int hw_card_loaded; int hw_sleep; int hw_send_sw; @@ -618,7 +621,7 @@ struct fujitsu int hw_density_sw; /* values which are used to track the frontend's access to sensors */ - char hw_read[NUM_OPTIONS-OPT_TOP]; + char hw_data_avail[NUM_OPTIONS-OPT_TOP]; }; #define CONNECTION_SCSI 0 /* SCSI interface */ @@ -631,6 +634,9 @@ struct fujitsu #define SOURCE_ADF_FRONT 1 #define SOURCE_ADF_BACK 2 #define SOURCE_ADF_DUPLEX 3 +#define SOURCE_CARD_FRONT 4 +#define SOURCE_CARD_BACK 5 +#define SOURCE_CARD_DUPLEX 6 #define COMP_NONE WD_cmp_NONE #define COMP_JPEG WD_cmp_JPG1 diff --git a/backend/genesys.conf.in b/backend/genesys.conf.in index 786ccd5..8268da3 100644 --- a/backend/genesys.conf.in +++ b/backend/genesys.conf.in @@ -39,6 +39,9 @@ usb 0x04a9 0x221c # Canon LiDE 80 usb 0x04a9 0x2214 +# Canon LiDE 90 +usb 0x04a9 0x1900 + # Canon 4400F usb 0x04a9 0x2228 @@ -124,15 +127,24 @@ usb 0x03f0 0x4605 # Plustek OpticBook 3600 usb 0x07b3 0x0900 +# Plustek OpticFilm 7200 +usb 0x07b3 0x0807 + # Plustek OpticFilm 7200i usb 0x07b3 0x0c04 # Plustek OpticFilm 7300 usb 0x07b3 0x0c12 +# Plustek OpticFilm 7400 +usb 0x07b3 0x0c3a + # Plustek OpticFilm 7500i usb 0x07b3 0x0c13 +# Plustek OpticFilm 8200i +usb 0x07b3 0x130d + # Primax Electronics, Ltd Xerox 2400 Onetouch usb 0x0461 0x038b diff --git a/backend/genesys/buffer.cpp b/backend/genesys/buffer.cpp deleted file mode 100644 index f17e361..0000000 --- a/backend/genesys/buffer.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* 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 deleted file mode 100644 index e9c889b..0000000 --- a/backend/genesys/buffer.h +++ /dev/null @@ -1,89 +0,0 @@ -/* 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 index f14aaa3..81d94ea 100644 --- a/backend/genesys/calibration.h +++ b/backend/genesys/calibration.h @@ -63,8 +63,7 @@ struct Genesys_Calibration_Cache Genesys_Frontend frontend; Genesys_Sensor sensor; - size_t calib_pixels = 0; - size_t calib_channels = 0; + ScanSession session; size_t average_size = 0; std::vector<std::uint16_t> white_average_data; std::vector<std::uint16_t> dark_average_data; @@ -75,8 +74,7 @@ struct Genesys_Calibration_Cache last_calibration == other.last_calibration && frontend == other.frontend && sensor == other.sensor && - calib_pixels == other.calib_pixels && - calib_channels == other.calib_channels && + session == other.session && average_size == other.average_size && white_average_data == other.white_average_data && dark_average_data == other.dark_average_data; @@ -94,8 +92,7 @@ void serialize(Stream& str, Genesys_Calibration_Cache& x) serialize_newline(str); serialize(str, x.sensor); serialize_newline(str); - serialize(str, x.calib_pixels); - serialize(str, x.calib_channels); + serialize(str, x.session); serialize(str, x.average_size); serialize_newline(str); serialize(str, x.white_average_data); diff --git a/backend/genesys/command_set.h b/backend/genesys/command_set.h index ab3a4b6..056cba8 100644 --- a/backend/genesys/command_set.h +++ b/backend/genesys/command_set.h @@ -67,14 +67,10 @@ public: 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; + Genesys_Register_Set* regs) 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 @@ -98,7 +94,6 @@ public: */ 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, @@ -112,15 +107,10 @@ public: // 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. + /** Needed on some chipsets before reading the status of the home sensor as the sensor may be + controlled by additional GPIO registers. */ - 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; } + virtual void update_home_sensor_gpio(Genesys_Device& dev) const = 0; // functions for sheetfed scanners @@ -134,14 +124,6 @@ public: /// 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, @@ -159,6 +141,16 @@ public: /// cold boot init function virtual void asic_boot(Genesys_Device* dev, bool cold) const = 0; + + /// checks if specific scan head is at home position + virtual bool is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const = 0; + + /// enables or disables XPA slider motor + virtual void set_xpa_lamp_power(Genesys_Device& dev, bool set) const = 0; + + /// enables or disables XPA slider motor + virtual void set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, + MotorMode mode) const = 0; }; } // namespace genesys diff --git a/backend/genesys/command_set_common.cpp b/backend/genesys/command_set_common.cpp new file mode 100644 index 0000000..381404e --- /dev/null +++ b/backend/genesys/command_set_common.cpp @@ -0,0 +1,248 @@ +/* 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. +*/ + +#define DEBUG_DECLARE_ONLY + +#include "command_set_common.h" +#include "low.h" +#include "value_filter.h" + +namespace genesys { + +CommandSetCommon::~CommandSetCommon() = default; + +bool CommandSetCommon::is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const +{ + struct HeadSettings { + ModelId model_id; + ScanHeadId scan_head; + GenesysRegisterSettingSet regs; + }; + + HeadSettings settings[] = { + { ModelId::CANON_8600F, + ScanHeadId::PRIMARY, { + { 0x6c, 0x20, 0x60 }, + { 0xa6, 0x00, 0x01 }, + } + }, + { ModelId::CANON_8600F, + ScanHeadId::SECONDARY, { + { 0x6c, 0x00, 0x60 }, + { 0xa6, 0x01, 0x01 }, + } + }, + }; + + for (const auto& setting : settings) { + if (setting.model_id == dev.model->model_id && + setting.scan_head == scan_head) + { + auto reg_backup = apply_reg_settings_to_device_with_backup(dev, setting.regs); + auto status = scanner_read_status(dev); + apply_reg_settings_to_device(dev, reg_backup); + return status.is_at_home; + } + } + + auto status = scanner_read_status(dev); + return status.is_at_home; +} + +void CommandSetCommon::set_xpa_lamp_power(Genesys_Device& dev, bool set) const + +{ + 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_4400F, ScanMethod::TRANSPARENCY, {}, {} }, + { ModelId::CANON_5600F, ScanMethod::TRANSPARENCY, {}, {} }, + { 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_7200, ScanMethod::TRANSPARENCY, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY_INFRARED, { + { 0xa8, 0x07, 0x07 }, + }, { + { 0xa8, 0x00, 0x07 }, + } + }, + { ModelId::PLUSTEK_OPTICFILM_7300, ScanMethod::TRANSPARENCY, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7400, ScanMethod::TRANSPARENCY, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY_INFRARED, { + { 0xa8, 0x07, 0x07 }, + }, { + { 0xa8, 0x00, 0x07 }, + } + }, + { ModelId::PLUSTEK_OPTICFILM_8200I, ScanMethod::TRANSPARENCY, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_8200I, ScanMethod::TRANSPARENCY_INFRARED, { + { 0xa8, 0x04, 0x04 }, + }, { + { 0xa8, 0x00, 0x04 }, + } + }, + }; + + 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; + } + } + + throw SaneException("Could not find XPA lamp settings"); +} + + +void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, + MotorMode mode) const +{ + DBG_HELPER(dbg); + + struct MotorSettings { + ModelId model_id; + ValueFilterAny<unsigned> resolutions; + GenesysRegisterSettingSet regs_primary_and_secondary; + GenesysRegisterSettingSet regs_primary; + GenesysRegisterSettingSet regs_secondary; + }; + + MotorSettings settings[] = { + { ModelId::CANON_8400F, { 400, 800, 1600, 3200 }, { + { 0x6c, 0x00, 0x90 }, + { 0xa9, 0x04, 0x06 }, + }, { + { 0x6c, 0x90, 0x90 }, + { 0xa9, 0x02, 0x06 }, + }, {} + }, + { ModelId::CANON_8600F, { 300, 600, 1200 }, { + { 0x6c, 0x00, 0x60 }, + { 0xa6, 0x01, 0x41 }, + }, { + { 0x6c, 0x20, 0x62 }, + { 0xa6, 0x00, 0x41 }, + }, { + { 0x6c, 0x40, 0x62 }, + { 0xa6, 0x01, 0x41 }, + } + }, + { ModelId::CANON_8600F, { 2400, 4800 }, { + { 0x6c, 0x02, 0x62 }, + { 0xa6, 0x01, 0x41 }, + }, { + { 0x6c, 0x20, 0x62 }, + { 0xa6, 0x00, 0x41 }, + }, { + { 0x6c, 0x40, 0x62 }, + { 0xa6, 0x01, 0x41 }, + } + }, + { ModelId::HP_SCANJET_G4050, VALUE_FILTER_ANY, { + { 0x6b, 0x81, 0x81 }, // set MULTFILM and GPOADF + { 0x6c, 0x00, 0x40 }, // note that reverse change is not applied on off + // 0xa6 register 0x08 bit likely sets motor power. No move at all without that one + { 0xa6, 0x08, 0x08 }, // note that reverse change is not applied on off + { 0xa8, 0x00, 0x04 }, + { 0xa9, 0x30, 0x30 }, + }, { + { 0x6b, 0x00, 0x01 }, // BUG: note that only ADF is unset + { 0xa8, 0x04, 0x04 }, + { 0xa9, 0x00, 0x10 }, // note that 0x20 bit is not reset + }, {} + }, + { ModelId::PLUSTEK_OPTICFILM_7200, VALUE_FILTER_ANY, {}, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7200I, VALUE_FILTER_ANY, {}, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7300, VALUE_FILTER_ANY, {}, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7400, VALUE_FILTER_ANY, {}, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_7500I, VALUE_FILTER_ANY, {}, {}, {} }, + { ModelId::PLUSTEK_OPTICFILM_8200I, VALUE_FILTER_ANY, {}, {}, {} }, + }; + + for (const auto& setting : settings) { + if (setting.model_id == dev.model->model_id && + setting.resolutions.matches(dev.session.output_resolution)) + { + switch (mode) { + case MotorMode::PRIMARY: { + apply_reg_settings_to_device(dev, setting.regs_primary); + break; + } + case MotorMode::PRIMARY_AND_SECONDARY: { + apply_reg_settings_to_device(dev, setting.regs_primary_and_secondary); + break; + } + case MotorMode::SECONDARY: { + apply_reg_settings_to_device(dev, setting.regs_secondary); + break; + } + } + regs.state.motor_mode = mode; + return; + } + } + + throw SaneException("Motor settings have not been found"); +} + +} // namespace genesys diff --git a/backend/genesys/command_set_common.h b/backend/genesys/command_set_common.h new file mode 100644 index 0000000..784fcd7 --- /dev/null +++ b/backend/genesys/command_set_common.h @@ -0,0 +1,48 @@ +/* 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. +*/ + +#ifndef BACKEND_GENESYS_COMMAND_SET_COMMON_H +#define BACKEND_GENESYS_COMMAND_SET_COMMON_H + +#include "command_set.h" + +namespace genesys { + + +/** Common command set functionality + */ +class CommandSetCommon : public CommandSet +{ +public: + ~CommandSetCommon() override; + + bool is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const override; + + void set_xpa_lamp_power(Genesys_Device& dev, bool set) const override; + + void set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, + MotorMode mode) const override; +}; + +} // namespace genesys + +#endif // BACKEND_GENESYS_COMMAND_SET_COMMON_H diff --git a/backend/genesys/conv.cpp b/backend/genesys/conv.cpp deleted file mode 100644 index a87c463..0000000 --- a/backend/genesys/conv.cpp +++ /dev/null @@ -1,238 +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. -*/ - -#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 deleted file mode 100644 index 446a80d..0000000 --- a/backend/genesys/conv.h +++ /dev/null @@ -1,69 +0,0 @@ -/* 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 index ba035fd..95bede8 100644 --- a/backend/genesys/device.cpp +++ b/backend/genesys/device.cpp @@ -62,15 +62,24 @@ std::vector<unsigned> MethodResolutions::get_resolutions() const return ret; } -const MethodResolutions& Genesys_Model::get_resolution_settings(ScanMethod method) const +const MethodResolutions* Genesys_Model::get_resolution_settings_ptr(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; + return &res_for_method; } } } + return nullptr; + +} +const MethodResolutions& Genesys_Model::get_resolution_settings(ScanMethod method) const +{ + const auto* ptr = get_resolution_settings_ptr(method); + if (ptr) + return *ptr; + throw SaneException("Could not find resolution settings for method %d", static_cast<unsigned>(method)); } @@ -80,6 +89,12 @@ std::vector<unsigned> Genesys_Model::get_resolutions(ScanMethod method) const return get_resolution_settings(method).get_resolutions(); } +bool Genesys_Model::has_method(ScanMethod method) const +{ + return get_resolution_settings_ptr(method) != nullptr; +} + + Genesys_Device::~Genesys_Device() { clear(); @@ -87,10 +102,6 @@ Genesys_Device::~Genesys_Device() void Genesys_Device::clear() { - read_buffer.clear(); - binarize_buffer.clear(); - local_buffer.clear(); - calib_file.clear(); calibration_cache.clear(); @@ -99,9 +110,9 @@ void Genesys_Device::clear() dark_average_data.clear(); } -ImagePipelineNodeBytesSource& Genesys_Device::get_pipeline_source() +ImagePipelineNodeBufferedCallableSource& Genesys_Device::get_pipeline_source() { - return static_cast<ImagePipelineNodeBytesSource&>(pipeline.front()); + return static_cast<ImagePipelineNodeBufferedCallableSource&>(pipeline.front()); } bool Genesys_Device::is_head_pos_known(ScanHeadId scan_head) const @@ -124,10 +135,14 @@ unsigned Genesys_Device::head_pos(ScanHeadId scan_head) const } } -void Genesys_Device::set_head_pos_unknown() +void Genesys_Device::set_head_pos_unknown(ScanHeadId scan_head) { - is_head_pos_primary_known_ = false; - is_head_pos_secondary_known_ = false; + if ((scan_head & ScanHeadId::PRIMARY) != ScanHeadId::NONE) { + is_head_pos_primary_known_ = false; + } + if ((scan_head & ScanHeadId::SECONDARY) != ScanHeadId::NONE) { + is_head_pos_secondary_known_ = false; + } } void Genesys_Device::set_head_pos_zero(ScanHeadId scan_head) @@ -205,12 +220,15 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev) << " 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' + << " initial_regs: " << format_indent_braced_list(4, dev.initial_regs) << '\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' + << " frontend_initial: " << format_indent_braced_list(4, dev.frontend_initial) << '\n'; + if (!dev.memory_layout.regs.empty()) { + out << " memory_layout.regs: " + << format_indent_braced_list(4, dev.memory_layout.regs) << '\n'; + } + out << " 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]) << ' ' @@ -220,13 +238,7 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev) << 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' @@ -242,31 +254,47 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev) << " 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_write_only(Genesys_Device& dev, + const GenesysRegisterSettingSet& regs) +{ + GenesysRegisterSettingSet backup; + for (const auto& reg : regs) { + dev.interface->write_register(reg.address, reg.value); + } +} + void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs) { + apply_reg_settings_to_device_with_backup(dev, regs); +} + +GenesysRegisterSettingSet + apply_reg_settings_to_device_with_backup(Genesys_Device& dev, + const GenesysRegisterSettingSet& regs) +{ + GenesysRegisterSettingSet backup; 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); + std::uint8_t old_val = dev.interface->read_register(reg.address); + std::uint8_t new_val = (old_val & ~reg.mask) | (reg.value & reg.mask); + dev.interface->write_register(reg.address, new_val); + + using SettingType = GenesysRegisterSettingSet::SettingType; + backup.push_back(SettingType{reg.address, + static_cast<std::uint8_t>(old_val & reg.mask), + reg.mask}); } + return backup; } } // namespace genesys diff --git a/backend/genesys/device.h b/backend/genesys/device.h index 6c744c9..ded6a48 100644 --- a/backend/genesys/device.h +++ b/backend/genesys/device.h @@ -46,7 +46,6 @@ #include "calibration.h" #include "command_set.h" -#include "buffer.h" #include "enums.h" #include "image_pipeline.h" #include "motor.h" @@ -55,6 +54,7 @@ #include "register.h" #include "usb_device.h" #include "scanner_interface.h" +#include "utilities.h" #include <vector> namespace genesys { @@ -77,22 +77,15 @@ struct Genesys_Gpo GenesysRegisterSettingSet regs; }; -/// Stores a SANE_Fixed value which is automatically converted from and to floating-point values -class FixedFloat +struct MemoryLayout { -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; } + // This is used on GL845, GL846, GL847 and GL124 which have special registers to define the + // memory layout + MemoryLayout() = default; - operator double() const { return value(); } + ValueFilter<ModelId> models; - double value() const { return SANE_UNFIX(value_); } - -private: - SANE_Fixed value_ = 0; + GenesysRegisterSettingSet regs; }; struct MethodResolutions @@ -106,6 +99,16 @@ struct MethodResolutions return *std::min_element(resolutions_x.begin(), resolutions_x.end()); } + unsigned get_nearest_resolution_x(unsigned resolution) const + { + return *std::min_element(resolutions_x.begin(), resolutions_x.end(), + [&](unsigned lhs, unsigned rhs) + { + return std::abs(static_cast<int>(lhs) - static_cast<int>(resolution)) < + std::abs(static_cast<int>(rhs) - static_cast<int>(resolution)); + }); + } + unsigned get_min_resolution_y() const { return *std::min_element(resolutions_y.begin(), resolutions_y.end()); @@ -143,51 +146,67 @@ struct Genesys_Model // All offsets below are with respect to the sensor home position // Start of scan area in mm - FixedFloat x_offset = 0; + float x_offset = 0; // Start of scan area in mm (Amount of feeding needed to get to the medium) - FixedFloat y_offset = 0; + float y_offset = 0; // Size of scan area in mm - FixedFloat x_size = 0; + float x_size = 0; // Size of scan area in mm - FixedFloat y_size = 0; + float y_size = 0; - // Start of white strip in mm - FixedFloat y_offset_calib_white = 0; + // Start of white strip in mm for scanners that use separate dark and white shading calibration. + float y_offset_calib_white = 0; + + // The size of the scan area that is used to acquire shading data in mm + float y_size_calib_mm = 0; + + // Start of the black/white strip in mm for scanners that use unified dark and white shading + // calibration. + float y_offset_calib_dark_white_mm = 0; + + // The size of the scan area that is used to acquire dark/white shading data in mm + float y_size_calib_dark_white_mm = 0; + + // The width of the scan area that is used to acquire shading data + float x_size_calib_mm = 0; // Start of black mark in mm - FixedFloat x_offset_calib_black = 0; + float x_offset_calib_black = 0; // Start of scan area in transparency mode in mm - FixedFloat x_offset_ta = 0; + float x_offset_ta = 0; // Start of scan area in transparency mode in mm - FixedFloat y_offset_ta = 0; + float y_offset_ta = 0; // Size of scan area in transparency mode in mm - FixedFloat x_size_ta = 0; + float x_size_ta = 0; // Size of scan area in transparency mode in mm - FixedFloat y_size_ta = 0; + float 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; + float y_offset_sensor_to_ta = 0; // Start of white strip in transparency mode in mm - FixedFloat y_offset_calib_white_ta = 0; + float y_offset_calib_white_ta = 0; // Start of black strip in transparency mode in mm - FixedFloat y_offset_calib_black_ta = 0; + float y_offset_calib_black_ta = 0; + + // The size of the scan area that is used to acquire shading data in transparency mode in mm + float y_size_calib_ta_mm = 0; // Size of scan area after paper sensor stop sensing document in mm - FixedFloat post_scan = 0; + float post_scan = 0; // Amount of feeding needed to eject document after finishing scanning in mm - FixedFloat eject_feed = 0; + float eject_feed = 0; - // Line-distance correction (in pixel at optical_ydpi) for CCD scanners + // Line-distance correction (in pixel at motor base_ydpi) for CCD scanners SANE_Int ld_shift_r = 0; SANE_Int ld_shift_g = 0; SANE_Int ld_shift_b = 0; @@ -210,22 +229,24 @@ struct Genesys_Model // stepper motor type MotorId motor_id = MotorId::UNKNOWN; - // Which hacks are needed for this scanner? - SANE_Word flags = 0; + // Which customizations are needed for this scanner? + ModelFlag flags = ModelFlag::NONE; // 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; + // returns nullptr if method is not supported + const MethodResolutions* get_resolution_settings_ptr(ScanMethod method) const; + + // throws if method is not supported const MethodResolutions& get_resolution_settings(ScanMethod method) const; std::vector<unsigned> get_resolutions(ScanMethod method) const; + + bool has_method(ScanMethod method) const; }; /** @@ -243,8 +264,8 @@ struct Genesys_Device // frees commonly used data void clear(); - SANE_Word vendorId = 0; /**< USB vendor identifier */ - SANE_Word productId = 0; /**< USB product identifier */ + std::uint16_t vendorId = 0; // USB vendor identifier + std::uint16_t productId = 0; // USB product identifier // USB mode: // 0: not set @@ -261,42 +282,25 @@ struct Genesys_Device // acquiring the positions of the black and white strips and the actual scan area bool ignore_offsets = false; - Genesys_Model *model = nullptr; + const 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_Register_Set initial_regs; 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; + MemoryLayout memory_layout; 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]; @@ -313,13 +317,6 @@ struct Genesys_Device // 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 @@ -328,9 +325,6 @@ struct Genesys_Device // 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 @@ -339,28 +333,19 @@ struct Genesys_Device // 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(); + ImagePipelineNodeBufferedCallableSource& 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_unknown(ScanHeadId scan_head); 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); @@ -382,6 +367,12 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev); void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs); +void apply_reg_settings_to_device_write_only(Genesys_Device& dev, + const GenesysRegisterSettingSet& regs); +GenesysRegisterSettingSet + apply_reg_settings_to_device_with_backup(Genesys_Device& dev, + const GenesysRegisterSettingSet& regs); + } // namespace genesys #endif diff --git a/backend/genesys/enums.cpp b/backend/genesys/enums.cpp index f515cfd..cd4be7d 100644 --- a/backend/genesys/enums.cpp +++ b/backend/genesys/enums.cpp @@ -109,6 +109,248 @@ std::ostream& operator<<(std::ostream& out, ColorFilter mode) return out; } +std::ostream& operator<<(std::ostream& out, ModelId id) +{ + switch (id) { + case ModelId::UNKNOWN: out << "UNKNOWN"; break; + case ModelId::CANON_4400F: out << "CANON_4400F"; break; + case ModelId::CANON_5600F: out << "CANON_5600F"; break; + case ModelId::CANON_8400F: out << "CANON_8400F"; break; + case ModelId::CANON_8600F: out << "CANON_8600F"; break; + case ModelId::CANON_IMAGE_FORMULA_101: out << "CANON_IMAGE_FORMULA_101"; break; + case ModelId::CANON_LIDE_50: out << "CANON_LIDE_50"; break; + case ModelId::CANON_LIDE_60: out << "CANON_LIDE_60"; break; + case ModelId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; + case ModelId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; + case ModelId::CANON_LIDE_100: out << "CANON_LIDE_100"; break; + case ModelId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; + case ModelId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; + case ModelId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; + case ModelId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; + case ModelId::CANON_LIDE_220: out << "CANON_LIDE_220"; break; + case ModelId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; + case ModelId::DCT_DOCKETPORT_487: out << "DCT_DOCKETPORT_487"; break; + case ModelId::HP_SCANJET_2300C: out << "HP_SCANJET_2300C"; break; + case ModelId::HP_SCANJET_2400C: out << "HP_SCANJET_2400C"; break; + case ModelId::HP_SCANJET_3670: out << "HP_SCANJET_3670"; break; + case ModelId::HP_SCANJET_4850C: out << "HP_SCANJET_4850C"; break; + case ModelId::HP_SCANJET_G4010: out << "HP_SCANJET_G4010"; break; + case ModelId::HP_SCANJET_G4050: out << "HP_SCANJET_G4050"; break; + case ModelId::HP_SCANJET_N6310: out << "HP_SCANJET_N6310"; break; + case ModelId::MEDION_MD5345: out << "MEDION_MD5345"; break; + case ModelId::PANASONIC_KV_SS080: out << "PANASONIC_KV_SS080"; break; + case ModelId::PENTAX_DSMOBILE_600: out << "PENTAX_DSMOBILE_600"; break; + case ModelId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; + case ModelId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; + case ModelId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; + case ModelId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; + case ModelId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; + case ModelId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; + case ModelId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; + case ModelId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; + case ModelId::PLUSTEK_OPTICPRO_ST12: out << "PLUSTEK_OPTICPRO_ST12"; break; + case ModelId::PLUSTEK_OPTICPRO_ST24: out << "PLUSTEK_OPTICPRO_ST24"; break; + case ModelId::SYSCAN_DOCKETPORT_465: out << "SYSCAN_DOCKETPORT_465"; break; + case ModelId::SYSCAN_DOCKETPORT_467: out << "SYSCAN_DOCKETPORT_467"; break; + case ModelId::SYSCAN_DOCKETPORT_485: out << "SYSCAN_DOCKETPORT_485"; break; + case ModelId::SYSCAN_DOCKETPORT_665: out << "SYSCAN_DOCKETPORT_665"; break; + case ModelId::SYSCAN_DOCKETPORT_685: out << "SYSCAN_DOCKETPORT_685"; break; + case ModelId::UMAX_ASTRA_4500: out << "UMAX_ASTRA_4500"; break; + case ModelId::VISIONEER_7100: out << "VISIONEER_7100"; break; + case ModelId::VISIONEER_ROADWARRIOR: out << "VISIONEER_ROADWARRIOR"; break; + case ModelId::VISIONEER_STROBE_XP100_REVISION3: + out << "VISIONEER_STROBE_XP100_REVISION3"; break; + case ModelId::VISIONEER_STROBE_XP200: out << "VISIONEER_STROBE_XP200"; break; + case ModelId::VISIONEER_STROBE_XP300: out << "VISIONEER_STROBE_XP300"; break; + case ModelId::XEROX_2400: out << "XEROX_2400"; break; + case ModelId::XEROX_TRAVELSCANNER_100: out << "XEROX_TRAVELSCANNER_100"; break; + default: + out << static_cast<unsigned>(id); break; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, SensorId id) +{ + switch (id) { + case SensorId::CCD_5345: out << "CCD_5345"; break; + case SensorId::CCD_CANON_4400F: out << "CCD_CANON_4400F"; break; + case SensorId::CCD_CANON_5600F: out << "CCD_CANON_5600F"; break; + case SensorId::CCD_CANON_8400F: out << "CCD_CANON_8400F"; break; + case SensorId::CCD_CANON_8600F: out << "CCD_CANON_8600F"; break; + case SensorId::CCD_DP665: out << "CCD_DP665"; break; + case SensorId::CCD_DP685: out << "CCD_DP685"; break; + case SensorId::CCD_DSMOBILE600: out << "CCD_DSMOBILE600"; break; + case SensorId::CCD_DOCKETPORT_487: out << "CCD_DOCKETPORT_487"; break; + case SensorId::CCD_G4050: out << "CCD_G4050"; break; + case SensorId::CCD_HP2300: out << "CCD_HP2300"; break; + case SensorId::CCD_HP2400: out << "CCD_HP2400"; break; + case SensorId::CCD_HP3670: out << "CCD_HP3670"; break; + case SensorId::CCD_HP_N6310: out << "CCD_HP_N6310"; break; + case SensorId::CCD_HP_4850C: out << "CCD_HP_4850C"; break; + case SensorId::CCD_IMG101: out << "CCD_IMG101"; break; + case SensorId::CCD_KVSS080: out << "CCD_KVSS080"; break; + case SensorId::CCD_PLUSTEK_OPTICBOOK_3800: out << "CCD_PLUSTEK_OPTICBOOK_3800"; break; + case SensorId::CCD_PLUSTEK_OPTICFILM_7200: out << "CCD_PLUSTEK_OPTICFILM_7200"; break; + case SensorId::CCD_PLUSTEK_OPTICFILM_7200I: out << "CCD_PLUSTEK_OPTICFILM_7200I"; break; + case SensorId::CCD_PLUSTEK_OPTICFILM_7300: out << "CCD_PLUSTEK_OPTICFILM_7300"; break; + case SensorId::CCD_PLUSTEK_OPTICFILM_7400: out << "CCD_PLUSTEK_OPTICFILM_7400"; break; + case SensorId::CCD_PLUSTEK_OPTICFILM_7500I: out << "CCD_PLUSTEK_OPTICFILM_7500I"; break; + case SensorId::CCD_PLUSTEK_OPTICFILM_8200I: out << "CCD_PLUSTEK_OPTICFILM_8200I"; break; + case SensorId::CCD_PLUSTEK_OPTICPRO_3600: out << "CCD_PLUSTEK_OPTICPRO_3600"; break; + case SensorId::CCD_ROADWARRIOR: out << "CCD_ROADWARRIOR"; break; + case SensorId::CCD_ST12: out << "CCD_ST12"; break; + case SensorId::CCD_ST24: out << "CCD_ST24"; break; + case SensorId::CCD_UMAX: out << "CCD_UMAX"; break; + case SensorId::CCD_XP300: out << "CCD_XP300"; break; + case SensorId::CIS_CANON_LIDE_35: out << "CIS_CANON_LIDE_35"; break; + case SensorId::CIS_CANON_LIDE_60: out << "CIS_CANON_LIDE_60"; break; + case SensorId::CIS_CANON_LIDE_80: out << "CIS_CANON_LIDE_80"; break; + case SensorId::CIS_CANON_LIDE_90: out << "CIS_CANON_LIDE_90"; break; + case SensorId::CIS_CANON_LIDE_100: out << "CIS_CANON_LIDE_100"; break; + case SensorId::CIS_CANON_LIDE_110: out << "CIS_CANON_LIDE_110"; break; + case SensorId::CIS_CANON_LIDE_120: out << "CIS_CANON_LIDE_120"; break; + case SensorId::CIS_CANON_LIDE_200: out << "CIS_CANON_LIDE_200"; break; + case SensorId::CIS_CANON_LIDE_210: out << "CIS_CANON_LIDE_210"; break; + case SensorId::CIS_CANON_LIDE_220: out << "CIS_CANON_LIDE_220"; break; + case SensorId::CIS_CANON_LIDE_700F: out << "CIS_CANON_LIDE_700F"; break; + case SensorId::CIS_XP200: out << "CIS_XP200"; break; + default: + out << static_cast<unsigned>(id); break; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, AdcId id) +{ + switch (id) { + case AdcId::UNKNOWN: out << "UNKNOWN"; break; + case AdcId::AD_XP200: out << "AD_XP200"; break; + case AdcId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; + case AdcId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; + case AdcId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; + case AdcId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; + case AdcId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; + case AdcId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; + case AdcId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; + case AdcId::CANON_4400F: out << "CANON_4400F"; break; + case AdcId::CANON_5600F: out << "CANON_5600F"; break; + case AdcId::CANON_8400F: out << "CANON_8400F"; break; + case AdcId::CANON_8600F: out << "CANON_8600F"; break; + case AdcId::G4050: out << "G4050"; break; + case AdcId::IMG101: out << "IMG101"; break; + case AdcId::KVSS080: out << "KVSS080"; break; + case AdcId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; + case AdcId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; + case AdcId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; + case AdcId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; + case AdcId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; + case AdcId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; + case AdcId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; + case AdcId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; + case AdcId::WOLFSON_5345: out << "WOLFSON_5345"; break; + case AdcId::WOLFSON_DSM600: out << "WOLFSON_DSM600"; break; + case AdcId::WOLFSON_HP2300: out << "WOLFSON_HP2300"; break; + case AdcId::WOLFSON_HP2400: out << "WOLFSON_HP2400"; break; + case AdcId::WOLFSON_HP3670: out << "WOLFSON_HP3670"; break; + case AdcId::WOLFSON_ST12: out << "WOLFSON_ST12"; break; + case AdcId::WOLFSON_ST24: out << "WOLFSON_ST24"; break; + case AdcId::WOLFSON_UMAX: out << "WOLFSON_UMAX"; break; + case AdcId::WOLFSON_XP300: out << "WOLFSON_XP300"; break; + default: + out << static_cast<unsigned>(id); break; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, GpioId id) +{ + switch (id) { + case GpioId::UNKNOWN: out << "UNKNOWN"; break; + case GpioId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; + case GpioId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; + case GpioId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; + case GpioId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; + case GpioId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; + case GpioId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; + case GpioId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; + case GpioId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; + case GpioId::CANON_4400F: out << "CANON_4400F"; break; + case GpioId::CANON_5600F: out << "CANON_5600F"; break; + case GpioId::CANON_8400F: out << "CANON_8400F"; break; + case GpioId::CANON_8600F: out << "CANON_8600F"; break; + case GpioId::DP665: out << "DP665"; break; + case GpioId::DP685: out << "DP685"; break; + case GpioId::G4050: out << "G4050"; break; + case GpioId::HP2300: out << "HP2300"; break; + case GpioId::HP2400: out << "HP2400"; break; + case GpioId::HP3670: out << "HP3670"; break; + case GpioId::HP_N6310: out << "HP_N6310"; break; + case GpioId::IMG101: out << "IMG101"; break; + case GpioId::KVSS080: out << "KVSS080"; break; + case GpioId::MD_5345: out << "MD_5345"; break; + case GpioId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; + case GpioId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; + case GpioId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; + case GpioId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; + case GpioId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; + case GpioId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; + case GpioId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; + case GpioId::ST12: out << "ST12"; break; + case GpioId::ST24: out << "ST24"; break; + case GpioId::UMAX: out << "UMAX"; break; + case GpioId::XP200: out << "XP200"; break; + case GpioId::XP300: out << "XP300"; break; + default: out << static_cast<unsigned>(id); break; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, MotorId id) +{ + switch (id) { + case MotorId::UNKNOWN: out << "UNKNOWN"; break; + case MotorId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; + case MotorId::CANON_LIDE_100: out << "CANON_LIDE_100"; break; + case MotorId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; + case MotorId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; + case MotorId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; + case MotorId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; + case MotorId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; + case MotorId::CANON_LIDE_60: out << "CANON_LIDE_60"; break; + case MotorId::CANON_LIDE_700: out << "CANON_LIDE_700"; break; + case MotorId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; + case MotorId::CANON_4400F: out << "CANON_4400F"; break; + case MotorId::CANON_5600F: out << "CANON_5600F"; break; + case MotorId::CANON_8400F: out << "CANON_8400F"; break; + case MotorId::CANON_8600F: out << "CANON_8600F"; break; + case MotorId::DP665: out << "DP665"; break; + case MotorId::DSMOBILE_600: out << "DSMOBILE_600"; break; + case MotorId::G4050: out << "G4050"; break; + case MotorId::HP2300: out << "HP2300"; break; + case MotorId::HP2400: out << "HP2400"; break; + case MotorId::HP3670: out << "HP3670"; break; + case MotorId::IMG101: out << "IMG101"; break; + case MotorId::KVSS080: out << "KVSS080"; break; + case MotorId::MD_5345: out << "MD_5345"; break; + case MotorId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; + case MotorId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; + case MotorId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; + case MotorId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; + case MotorId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; + case MotorId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; + case MotorId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; + case MotorId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; + case MotorId::ROADWARRIOR: out << "ROADWARRIOR"; break; + case MotorId::ST24: out << "ST24"; break; + case MotorId::UMAX: out << "UMAX"; break; + case MotorId::XP200: out << "XP200"; break; + case MotorId::XP300: out << "XP300"; break; + default: out << static_cast<unsigned>(id); break; + } + return out; +} + std::ostream& operator<<(std::ostream& out, StepType type) { switch (type) { diff --git a/backend/genesys/enums.h b/backend/genesys/enums.h index 810c4ca..0e16ba4 100644 --- a/backend/genesys/enums.h +++ b/backend/genesys/enums.h @@ -182,6 +182,7 @@ enum class ModelId : unsigned CANON_LIDE_50, CANON_LIDE_60, CANON_LIDE_80, + CANON_LIDE_90, CANON_LIDE_100, CANON_LIDE_110, CANON_LIDE_120, @@ -201,9 +202,12 @@ enum class ModelId : unsigned PANASONIC_KV_SS080, PENTAX_DSMOBILE_600, PLUSTEK_OPTICBOOK_3800, + PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, + PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, + PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, PLUSTEK_OPTICPRO_ST12, PLUSTEK_OPTICPRO_ST24, @@ -222,16 +226,33 @@ enum class ModelId : unsigned XEROX_TRAVELSCANNER_100, }; +inline void serialize(std::istream& str, ModelId& x) +{ + unsigned value; + serialize(str, value); + x = static_cast<ModelId>(value); +} + +inline void serialize(std::ostream& str, ModelId& x) +{ + unsigned value = static_cast<unsigned>(x); + serialize(str, value); +} + +std::ostream& operator<<(std::ostream& out, ModelId id); + enum class SensorId : unsigned { UNKNOWN = 0, CCD_5345, CCD_CANON_4400F, + CCD_CANON_5600F, CCD_CANON_8400F, CCD_CANON_8600F, CCD_DP665, CCD_DP685, CCD_DSMOBILE600, + CCD_DOCKETPORT_487, CCD_G4050, CCD_HP2300, CCD_HP2400, @@ -241,9 +262,12 @@ enum class SensorId : unsigned CCD_IMG101, CCD_KVSS080, CCD_PLUSTEK_OPTICBOOK_3800, + CCD_PLUSTEK_OPTICFILM_7200, CCD_PLUSTEK_OPTICFILM_7200I, CCD_PLUSTEK_OPTICFILM_7300, + CCD_PLUSTEK_OPTICFILM_7400, CCD_PLUSTEK_OPTICFILM_7500I, + CCD_PLUSTEK_OPTICFILM_8200I, CCD_PLUSTEK_OPTICPRO_3600, CCD_ROADWARRIOR, CCD_ST12, // SONY ILX548: 5340 Pixel ??? @@ -251,7 +275,9 @@ enum class SensorId : unsigned CCD_UMAX, CCD_XP300, CIS_CANON_LIDE_35, + CIS_CANON_LIDE_60, CIS_CANON_LIDE_80, + CIS_CANON_LIDE_90, CIS_CANON_LIDE_100, CIS_CANON_LIDE_110, CIS_CANON_LIDE_120, @@ -275,6 +301,8 @@ inline void serialize(std::ostream& str, SensorId& x) serialize(str, value); } +std::ostream& operator<<(std::ostream& out, SensorId id); + enum class AdcId : unsigned { @@ -282,20 +310,25 @@ enum class AdcId : unsigned AD_XP200, CANON_LIDE_35, CANON_LIDE_80, + CANON_LIDE_90, CANON_LIDE_110, CANON_LIDE_120, CANON_LIDE_200, CANON_LIDE_700F, CANON_4400F, + CANON_5600F, CANON_8400F, CANON_8600F, G4050, IMG101, KVSS080, PLUSTEK_OPTICBOOK_3800, + PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, + PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, + PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, WOLFSON_5345, WOLFSON_DSM600, @@ -321,17 +354,21 @@ inline void serialize(std::ostream& str, AdcId& x) serialize(str, value); } +std::ostream& operator<<(std::ostream& out, AdcId id); + enum class GpioId : unsigned { UNKNOWN = 0, CANON_LIDE_35, CANON_LIDE_80, + CANON_LIDE_90, CANON_LIDE_110, CANON_LIDE_120, CANON_LIDE_200, CANON_LIDE_210, CANON_LIDE_700F, CANON_4400F, + CANON_5600F, CANON_8400F, CANON_8600F, DP665, @@ -345,9 +382,12 @@ enum class GpioId : unsigned KVSS080, MD_5345, PLUSTEK_OPTICBOOK_3800, + PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, + PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, + PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, ST12, ST24, @@ -356,6 +396,8 @@ enum class GpioId : unsigned XP300, }; +std::ostream& operator<<(std::ostream& out, GpioId id); + enum class MotorId : unsigned { UNKNOWN = 0, @@ -365,9 +407,12 @@ enum class MotorId : unsigned CANON_LIDE_200, CANON_LIDE_210, CANON_LIDE_35, + CANON_LIDE_60, CANON_LIDE_700, CANON_LIDE_80, + CANON_LIDE_90, CANON_4400F, + CANON_5600F, CANON_8400F, CANON_8600F, DP665, @@ -380,9 +425,12 @@ enum class MotorId : unsigned KVSS080, MD_5345, PLUSTEK_OPTICBOOK_3800, + PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, + PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, + PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, ROADWARRIOR, ST24, @@ -391,6 +439,8 @@ enum class MotorId : unsigned XP300, }; +std::ostream& operator<<(std::ostream& out, MotorId id); + enum class StepType : unsigned { FULL = 0, @@ -423,6 +473,7 @@ enum class AsicType : unsigned UNKNOWN = 0, GL646, GL841, + GL842, GL843, GL845, GL846, @@ -431,6 +482,92 @@ enum class AsicType : unsigned }; +enum class ModelFlag : unsigned +{ + // no flags + NONE = 0, + + // scanner is not tested, print a warning as it's likely it won't work + UNTESTED = 1 << 0, + + // use 14-bit gamma table instead of 12-bit + GAMMA_14BIT = 1 << 1, + + // perform lamp warmup + WARMUP = 1 << 4, + + // whether to disable offset and gain calibration + DISABLE_ADC_CALIBRATION = 1 << 5, + + // whether to disable exposure calibration (this currently is only done on CIS + // scanners) + DISABLE_EXPOSURE_CALIBRATION = 1 << 6, + + // whether to disable shading calibration completely + DISABLE_SHADING_CALIBRATION = 1 << 7, + + // do dark calibration + DARK_CALIBRATION = 1 << 8, + + // host-side calibration uses a complete scan + HOST_SIDE_CALIBRATION_COMPLETE_SCAN = 1 << 9, + + // whether scanner must wait for the head while parking + MUST_WAIT = 1 << 10, + + // use zeroes for dark calibration + USE_CONSTANT_FOR_DARK_CALIBRATION = 1 << 11, + + // do dark and white calibration in one run + DARK_WHITE_CALIBRATION = 1 << 12, + + // allow custom gamma tables + CUSTOM_GAMMA = 1 << 13, + + // disable fast feeding mode on this scanner + DISABLE_FAST_FEEDING = 1 << 14, + + // the scanner uses multi-segment sensors that must be handled during calibration + SIS_SENSOR = 1 << 16, + + // the head must be reparked between shading scans + SHADING_REPARK = 1 << 18, + + // the scanner outputs inverted pixel data + INVERT_PIXEL_DATA = 1 << 19, + + // the scanner outputs 16-bit data that is byte-inverted + SWAP_16BIT_DATA = 1 << 20, + + // the scanner has transparency, but it's implemented using only one motor + UTA_NO_SECONDARY_MOTOR = 1 << 21, + + // the scanner has transparency, but it's implemented using only one lamp + TA_NO_SECONDARY_LAMP = 1 << 22, +}; + +inline ModelFlag operator|(ModelFlag left, ModelFlag right) +{ + return static_cast<ModelFlag>(static_cast<unsigned>(left) | static_cast<unsigned>(right)); +} + +inline ModelFlag& operator|=(ModelFlag& left, ModelFlag right) +{ + left = left | right; + return left; +} + +inline ModelFlag operator&(ModelFlag left, ModelFlag right) +{ + return static_cast<ModelFlag>(static_cast<unsigned>(left) & static_cast<unsigned>(right)); +} + +inline bool has_flag(ModelFlag flags, ModelFlag which) +{ + return (flags & which) == which; +} + + enum class ScanFlag : unsigned { NONE = 0, @@ -438,14 +575,24 @@ enum class ScanFlag : unsigned 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, + + // if this flag is set the sensor will always be handled ignoring staggering of multiple + // sensors to achieve high resolution. + IGNORE_STAGGER_OFFSET = 1 << 4, + + // if this flag is set the sensor will always be handled as if the components that scan + // different colors are at the same position. + IGNORE_COLOR_OFFSET = 1 << 5, + + DISABLE_LAMP = 1 << 6, + CALIBRATION = 1 << 7, + FEEDING = 1 << 8, + USE_XPA = 1 << 9, + ENABLE_LEDADD = 1 << 10, + REVERSE = 1 << 12, + + // the scanner should return head to home position automatically after scan. + AUTO_GO_HOME = 1 << 13, }; inline ScanFlag operator|(ScanFlag left, ScanFlag right) @@ -485,45 +632,18 @@ inline void serialize(std::ostream& str, ScanFlag& x) 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 }; +enum class MotorMode : unsigned +{ + PRIMARY = 0, + PRIMARY_AND_SECONDARY, + SECONDARY, +}; } // namespace genesys diff --git a/backend/genesys/error.cpp b/backend/genesys/error.cpp index 6c921c1..46d79c9 100644 --- a/backend/genesys/error.cpp +++ b/backend/genesys/error.cpp @@ -45,6 +45,7 @@ #include "error.h" #include <cstdarg> +#include <cstdlib> namespace genesys { @@ -212,4 +213,32 @@ void DebugMessageHelper::vlog(unsigned level, const char* format, ...) DBG(level, "%s: %s\n", func_, msg.c_str()); } +enum class LogImageDataStatus +{ + NOT_SET, + ENABLED, + DISABLED +}; + +static LogImageDataStatus s_log_image_data_setting = LogImageDataStatus::NOT_SET; + +LogImageDataStatus dbg_read_log_image_data_setting() +{ + auto* setting = std::getenv("SANE_DEBUG_GENESYS_IMAGE"); + if (!setting) + return LogImageDataStatus::DISABLED; + auto setting_int = std::strtol(setting, nullptr, 10); + if (setting_int == 0) + return LogImageDataStatus::DISABLED; + return LogImageDataStatus::ENABLED; +} + +bool dbg_log_image_data() +{ + if (s_log_image_data_setting == LogImageDataStatus::NOT_SET) { + s_log_image_data_setting = dbg_read_log_image_data_setting(); + } + return s_log_image_data_setting == LogImageDataStatus::ENABLED; +} + } // namespace genesys diff --git a/backend/genesys/error.h b/backend/genesys/error.h index 5aba8cf..26235dd 100644 --- a/backend/genesys/error.h +++ b/backend/genesys/error.h @@ -137,7 +137,6 @@ private: unsigned num_exceptions_on_enter_ = 0; }; - #if defined(__GNUC__) || defined(__clang__) #define GENESYS_CURRENT_FUNCTION __PRETTY_FUNCTION__ #elif defined(__FUNCSIG__) @@ -149,6 +148,8 @@ private: #define DBG_HELPER(var) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION) #define DBG_HELPER_ARGS(var, ...) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION, __VA_ARGS__) +bool dbg_log_image_data(); + template<class F> SANE_Status wrap_exceptions_to_status_code(const char* func, F&& function) { @@ -172,6 +173,27 @@ SANE_Status wrap_exceptions_to_status_code(const char* func, F&& function) } template<class F> +SANE_Status wrap_exceptions_to_status_code_return(const char* func, F&& function) +{ + try { + return function(); + } 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()); + return SANE_STATUS_INVAL; + } catch (...) { + DBG(DBG_error, "%s: got unknown uncaught exception\n", func); + return SANE_STATUS_INVAL; + } +} + +template<class F> void catch_all_exceptions(const char* func, F&& function) { try { diff --git a/backend/genesys/fwd.h b/backend/genesys/fwd.h index 2d55f98..ea335f7 100644 --- a/backend/genesys/fwd.h +++ b/backend/genesys/fwd.h @@ -46,9 +46,6 @@ namespace genesys { -// buffer.h -struct Genesys_Buffer; - // calibration.h struct Genesys_Calibration_Cache; @@ -56,7 +53,6 @@ struct Genesys_Calibration_Cache; class CommandSet; // device.h -class FixedFloat; struct Genesys_Gpo; struct MethodResolutions; struct Genesys_Model; @@ -75,8 +71,6 @@ class Image; // image_buffer.h class ImageBuffer; -class FakeBufferModel; -class ImageBufferGenesysUsb; // image_pipeline.h class ImagePipelineNode; @@ -88,12 +82,12 @@ struct Pixel; struct RawPixel; // low.h -struct Genesys_USB_Device_Entry; -struct Motor_Profile; +struct UsbDeviceEntry; // motor.h struct Genesys_Motor; struct MotorSlope; +struct MotorProfile; struct MotorSlopeTable; // register.h @@ -113,7 +107,6 @@ class ScannerInterfaceUsb; class TestScannerInterface; // sensor.h -class ResolutionFilter; struct GenesysFrontendLayout; struct Genesys_Frontend; struct SensorExposure; @@ -124,6 +117,10 @@ struct Genesys_Settings; struct SetupParams; struct ScanSession; +// value_filter.h +template<class T> class ValueFilter; +template<class T> class ValueFilterAny; + // test_usb_device.h class TestUsbDevice; diff --git a/backend/genesys/genesys.cpp b/backend/genesys/genesys.cpp index 7c25168..9d80cfa 100644 --- a/backend/genesys/genesys.cpp +++ b/backend/genesys/genesys.cpp @@ -61,9 +61,9 @@ #define DEBUG_NOT_STATIC #include "genesys.h" -#include "conv.h" #include "gl124_registers.h" #include "gl841_registers.h" +#include "gl842_registers.h" #include "gl843_registers.h" #include "gl846_registers.h" #include "gl847_registers.h" @@ -73,7 +73,6 @@ #include "test_scanner_interface.h" #include "test_settings.h" #include "../include/sane/sanei_config.h" -#include "../include/sane/sanei_magic.h" #include <array> #include <cmath> @@ -111,8 +110,8 @@ 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, + // SANE_TITLE_HALFTONE, not used + // SANE_VALUE_SCAN_MODE_LINEART, not used nullptr }; @@ -131,12 +130,6 @@ static SANE_String_Const cis_color_filter_list[] = { nullptr }; -static SANE_Range swdespeck_range = { - 1, - 9, - 1 -}; - static SANE_Range time_range = { 0, /* minimum */ 60, /* maximum */ @@ -162,15 +155,9 @@ static const SANE_Range u16_range = { }; static const SANE_Range percentage_range = { - SANE_FIX (0), /* minimum */ - SANE_FIX (100), /* maximum */ - SANE_FIX (1) /* quantization */ -}; - -static const SANE_Range threshold_curve_range = { - 0, /* minimum */ - 127, /* maximum */ - 1 /* quantization */ + float_to_fixed(0), // minimum + float_to_fixed(100), // maximum + float_to_fixed(1) // quantization }; /** @@ -191,7 +178,7 @@ static const SANE_Range expiration_range = { 1 /* quantization */ }; -const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev) +const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev) { DBG_HELPER(dbg); for (const auto& sensor : *s_sensors) { @@ -202,7 +189,7 @@ const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev) throw std::runtime_error("Given device does not have sensor defined"); } -Genesys_Sensor* find_sensor_impl(Genesys_Device* dev, unsigned dpi, unsigned channels, +Genesys_Sensor* find_sensor_impl(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, @@ -217,7 +204,7 @@ Genesys_Sensor* find_sensor_impl(Genesys_Device* dev, unsigned dpi, unsigned cha return nullptr; } -bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels, +bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, @@ -225,8 +212,8 @@ bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channe return find_sensor_impl(dev, dpi, channels, scan_method) != nullptr; } -const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels, - ScanMethod scan_method) +const Genesys_Sensor& sanei_genesys_find_sensor(const 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)); @@ -250,12 +237,14 @@ Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigne std::vector<std::reference_wrapper<const Genesys_Sensor>> - sanei_genesys_find_sensors_all(Genesys_Device* dev, ScanMethod scan_method) + sanei_genesys_find_sensors_all(const 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); + for (auto& sensor : *s_sensors) { + if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) { + ret.push_back(sensor); + } } return ret; } @@ -308,6 +297,24 @@ void sanei_genesys_init_structs (Genesys_Device * dev) } } + 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) + { + bool memory_layout_found = false; + for (const auto& memory_layout : *s_memory_layout) { + if (memory_layout.models.matches(dev->model->model_id)) { + dev->memory_layout = memory_layout; + memory_layout_found = true; + break; + } + } + if (!memory_layout_found) { + throw SaneException("Could not find memory layout"); + } + } + 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), @@ -316,33 +323,6 @@ void sanei_genesys_init_structs (Genesys_Device * dev) } } -/* 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 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 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 - * @return Motor slope table - * @note all times in pixel time - */ -MotorSlopeTable sanei_genesys_create_slope_table3(AsicType asic_type, const Genesys_Motor& motor, - StepType step_type, int exposure_time, - unsigned yres) -{ - unsigned target_speed_w = (exposure_time * yres) / motor.base_ydpi; - - 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 * Generates a gamma table of the given length within 0 and the given * maximum value @@ -382,7 +362,7 @@ void sanei_genesys_create_default_gamma_table(Genesys_Device* dev, int size = 0; int max = 0; if (dev->model->asic_type == AsicType::GL646) { - if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) { + if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { size = 16384; } else { size = 4096; @@ -411,34 +391,30 @@ 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, - StepType step_type, int endpixel, int exposure_by_led) +SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, const MotorProfile& profile, float ydpi, + int endpixel, int exposure_by_led) { int exposure_by_ccd = endpixel + 32; - unsigned max_speed_motor_w = dev->motor.get_slope(step_type).max_speed_w; + unsigned max_speed_motor_w = profile.slope.max_speed_w; int exposure_by_motor = static_cast<int>((max_speed_motor_w * dev->motor.base_ydpi) / ydpi); int exposure = exposure_by_ccd; - if (exposure < exposure_by_motor) - exposure = exposure_by_motor; + if (exposure < exposure_by_motor) { + exposure = exposure_by_motor; + } - if (exposure < exposure_by_led && dev->model->is_cis) - exposure = exposure_by_led; + 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 => exposure=%d\n", __func__, - static_cast<int>(ydpi), static_cast<unsigned>(step_type), endpixel, - exposure_by_led, exposure); - return exposure; + return exposure; } /* Sends a block of shading information to the scanner. The data is placed at address 0x0000 for color mode, gray mode and unconditionally for the following CCD chips: HP2300, HP2400 and HP5345 - In the other cases (lineart, halftone on ccd chips not mentioned) the - addresses are 0x2a00 for dpihw==0, 0x5500 for dpihw==1 and 0xa800 for - dpihw==2. //Note: why this? The data needs to be of size "size", and in little endian byte order. */ @@ -446,7 +422,6 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S uint8_t* data, int size) { DBG_HELPER_ARGS(dbg, "(size = %d)", size); - int dpihw; int start_address; /* ASIC higher than gl843 doesn't have register 2A/2B, so we route to @@ -457,84 +432,30 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S return; } - /* gl646, gl84[123] case */ - dpihw = dev->reg.get8(0x05) >> 6; - - /* TODO invert the test so only the 2 models behaving like that are - * tested instead of adding all the others */ - /* 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->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"); - } - } - else { // color - start_address = 0x00; - } + start_address = 0x00; dev->interface->write_buffer(0x3c, start_address, data, size); } -// ? void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, int pixels_per_line) { 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; - if (dev->cmd_set->has_send_shading_data()) { return; } DBG(DBG_proc, "%s (pixels_per_line = %d)\n", __func__, pixels_per_line); - // BUG: GRAY shouldn't probably be in the if condition below. Discovered when refactoring - if (dev->settings.scan_mode == ScanColorMode::GRAY || - dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) - { - channels = 3; - } else { - channels = 1; - } + unsigned channels = dev->settings.get_channels(); // 16 bit black, 16 bit white std::vector<uint8_t> shading_data(pixels_per_line * 4 * channels, 0); uint8_t* shading_data_ptr = shading_data.data(); - for (i = 0; i < pixels_per_line * channels; i++) - { + for (unsigned i = 0; i < pixels_per_line * channels; i++) { *shading_data_ptr++ = 0x00; /* dark lo */ *shading_data_ptr++ = 0x00; /* dark hi */ *shading_data_ptr++ = 0x00; /* white lo */ @@ -545,184 +466,6 @@ void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& 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 -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)) { - 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 */ - 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; - } - } - - image2 = image; - if (DBG_LEVEL >= DBG_data) - sanei_genesys_write_pnm_file("gl_laplace.pnm", image.data(), 8, 1, width, height); - - /* apply X direction sobel filter - -1 0 1 - -2 0 2 - -1 0 1 - and finds threshold level - */ - level = 0; - 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) - current = 255; - 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); - - /* set up detection level */ - level = level / 3; - - /* find left black margin first - todo: search top before left - we average the result of N searches */ - left = 0; - count = 0; - for (y = 2; y < 11; y++) - { - x = 8; - while ((x < width / 2) && (image[y * width + x] < level)) - { - image[y * width + x] = 255; - x++; - } - count++; - left += x; - } - if (DBG_LEVEL >= DBG_data) - 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; - - /* find top edge by detecting black strip */ - /* apply Y direction sobel filter - -1 -2 -1 - 0 0 0 - 1 2 1 - */ - level = 0; - 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) - current = 255; - image[y * width + x] = current; - if (current > level) - level = current; - } - } - if (DBG_LEVEL >= DBG_data) - sanei_genesys_write_pnm_file("gl_ysobel.pnm", image.data(), 8, 1, width, height); - - /* set up detection level */ - level = level / 3; - - /* search top of horizontal black stripe : TODO yet another flag */ - if (dev->model->sensor_id == SensorId::CCD_5345 - && dev->model->motor_id == MotorId::MD_5345) - { - top = 0; - count = 0; - for (x = width / 2; x < width - 1; x++) - { - y = 2; - while ((y < height) && (image[x + y * width] < level)) - { - image[y * width + x] = 255; - y++; - } - count++; - top += y; - } - if (DBG_LEVEL >= DBG_data) - sanei_genesys_write_pnm_file("gl_detected-ysobel.pnm", image.data(), 8, 1, width, height); - top = top / count; - - /* 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_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->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; - for (x = 10; x < 60; x++) - { - y = 2; - while ((y < height) && (image[x + y * width] < level)) - y++; - top += y; - count++; - } - top = top / count; - 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); -} - -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 @@ -730,6 +473,16 @@ namespace gl124 { void scanner_clear_scan_and_feed_counts(Genesys_Device& dev) { switch (dev.model->asic_type) { + case AsicType::GL841: { + dev.interface->write_register(gl841::REG_0x0D, + gl841::REG_0x0D_CLRLNCNT); + break; + } + case AsicType::GL842: { + dev.interface->write_register(gl842::REG_0x0D, + gl842::REG_0x0D_CLRLNCNT); + break; + } case AsicType::GL843: { dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRLNCNT | gl843::REG_0x0D_CLRMCNT); @@ -756,34 +509,107 @@ void scanner_clear_scan_and_feed_counts(Genesys_Device& dev) } } -void scanner_clear_scan_and_feed_counts2(Genesys_Device& dev) +void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr, + const std::vector<uint16_t>& slope_table) { - // 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); + DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %zu", table_nr, slope_table.size()); + + unsigned max_table_nr = 0; + switch (dev->model->asic_type) { + case AsicType::GL646: { + max_table_nr = 2; break; } + case AsicType::GL841: + case AsicType::GL842: + case AsicType::GL843: 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); + case AsicType::GL846: + case AsicType::GL847: + case AsicType::GL124: { + max_table_nr = 4; 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); + default: + throw SaneException("Unsupported ASIC type"); + } + + if (table_nr > max_table_nr) { + throw SaneException("invalid table number %d", table_nr); + } + + std::vector<uint8_t> table; + table.reserve(slope_table.size() * 2); + for (std::size_t i = 0; i < slope_table.size(); i++) { + table.push_back(slope_table[i] & 0xff); + table.push_back(slope_table[i] >> 8); + } + if (dev->model->asic_type == AsicType::GL841 || + dev->model->model_id == ModelId::CANON_LIDE_90) + { + // BUG: do this on all gl842 scanners + auto max_table_size = get_slope_table_max_size(dev->model->asic_type); + table.reserve(max_table_size * 2); + while (table.size() < max_table_size * 2) { + table.push_back(slope_table.back() & 0xff); + table.push_back(slope_table.back() >> 8); + } + } + + if (dev->interface->is_mock()) { + dev->interface->record_slope_table(table_nr, slope_table); + } + + switch (dev->model->asic_type) { + case AsicType::GL646: { + unsigned dpihw = dev->reg.find_reg(0x05).value >> 6; + unsigned start_address = 0; + 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"); + } + dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(), + table.size()); break; } + case AsicType::GL841: + case AsicType::GL842: { + unsigned start_address = 0; + switch (sensor.register_dpihw) { + case 600: start_address = 0x08000; break; + case 1200: start_address = 0x10000; break; + case 2400: start_address = 0x20000; break; + default: throw SaneException("Unexpected dpihw"); + } + dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(), + table.size()); + break; + } + case AsicType::GL843: { + // 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(), + table.size()); + break; + } + case AsicType::GL845: + case AsicType::GL846: + case AsicType::GL847: 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); + // slope table addresses are fixed + dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, table.size(), + table.data()); break; } default: - throw SaneException("Unsupported asic type"); + throw SaneException("Unsupported ASIC type"); } + } bool scanner_is_motor_stopped(Genesys_Device& dev) @@ -794,9 +620,18 @@ bool scanner_is_motor_stopped(Genesys_Device& dev) return !status.is_motor_enabled && status.is_feeding_finished; } case AsicType::GL841: { + auto status = scanner_read_status(dev); auto reg = dev.interface->read_register(gl841::REG_0x40); - return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG)); + return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG) && + !status.is_motor_enabled); + } + case AsicType::GL842: { + auto status = scanner_read_status(dev); + auto reg = dev.interface->read_register(gl842::REG_0x40); + + return (!(reg & gl842::REG_0x40_DATAENB) && !(reg & gl842::REG_0x40_MOTMFLG) && + !status.is_motor_enabled); } case AsicType::GL843: { auto status = scanner_read_status(dev); @@ -832,11 +667,31 @@ bool scanner_is_motor_stopped(Genesys_Device& dev) } } +void scanner_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->asic_type != AsicType::GL841 && + dev.model->asic_type != AsicType::GL843) + { + regs_set_exposure(dev.model->asic_type, regs, sensor.exposure); + } + + dev.segment_order = sensor.segment_order; +} + void scanner_stop_action(Genesys_Device& dev) { DBG_HELPER(dbg); switch (dev.model->asic_type) { + case AsicType::GL841: + case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: @@ -847,9 +702,7 @@ void scanner_stop_action(Genesys_Device& dev) throw SaneException("Unsupported asic type"); } - if (dev.cmd_set->needs_update_home_sensor_gpio()) { - dev.cmd_set->update_home_sensor_gpio(dev); - } + dev.cmd_set->update_home_sensor_gpio(dev); if (scanner_is_motor_stopped(dev)) { DBG(DBG_info, "%s: already stopped\n", __func__); @@ -878,6 +731,7 @@ void scanner_stop_action_no_move(Genesys_Device& dev, genesys::Genesys_Register_ switch (dev.model->asic_type) { case AsicType::GL646: case AsicType::GL841: + case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: @@ -908,7 +762,9 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D 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); + scan_method == ScanMethod::TRANSPARENCY_INFRARED) && + (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)); + bool uses_secondary_pos = uses_secondary_head && dev.model->default_method == ScanMethod::FLATBED; @@ -934,21 +790,19 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D session.params.yres = resolution; session.params.startx = 0; session.params.starty = steps; - session.params.pixels = 100; + session.params.pixels = 50; session.params.lines = 3; session.params.depth = 8; - session.params.channels = 3; + session.params.channels = 1; 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.scan_mode = ScanColorMode::GRAY; + session.params.color_filter = ColorFilter::GREEN; + session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::FEEDING | - ScanFlag::IGNORE_LINE_DISTANCE; + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; if (dev.model->asic_type == AsicType::GL124) { session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE; @@ -963,20 +817,21 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D 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}); + regs_set_exposure(dev.model->asic_type, local_reg, + sanei_genesys_fixup_exposure({0, 0, 0})); } - scanner_clear_scan_and_feed_counts2(dev); + scanner_clear_scan_and_feed_counts(dev); dev.interface->write_registers(local_reg); if (uses_secondary_head) { - gl843::gl843_set_xpa_motor_power(&dev, local_reg, true); + dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY_AND_SECONDARY); } try { scanner_start_action(dev, true); } catch (...) { catch_all_exceptions(__func__, [&]() { - gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); + dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); }); catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); }); // restore original registers @@ -992,17 +847,18 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D 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); - } + scanner_stop_action(dev); if (uses_secondary_head) { - gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); + dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); } return; } // wait until feed count reaches the required value + if (dev.model->model_id == ModelId::CANON_LIDE_700F) { + dev.cmd_set->update_home_sensor_gpio(dev); + } + // FIXME: should porbably wait for some timeout Status status; for (unsigned i = 0;; ++i) { @@ -1015,12 +871,9 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D 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); - } + scanner_stop_action(dev); if (uses_secondary_head) { - gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); + dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); } dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps); @@ -1032,11 +885,22 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D dev.interface->sleep_ms(100); } +void scanner_move_to_ta(Genesys_Device& dev) +{ + DBG_HELPER(dbg); + + unsigned feed = static_cast<unsigned>((dev.model->y_offset_sensor_to_ta * dev.motor.base_ydpi) / + MM_PER_INCH); + scanner_move(dev, dev.model->default_method, feed, Direction::FORWARD); +} + 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::GL841: + case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: @@ -1047,11 +911,17 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home) throw SaneException("Unsupported asic type"); } + if (dev.model->is_sheetfed) { + dbg.vlog(DBG_proc, "sheetfed scanner, skipping going back home"); + return; + } + // FIXME: also check whether the scanner actually has a secondary head - if (!dev.is_head_pos_known(ScanHeadId::SECONDARY) || + 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) + dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) && + (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR))) { scanner_move_back_home_ta(dev); } @@ -1064,9 +934,7 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home) Direction::BACKWARD); } - if (dev.cmd_set->needs_update_home_sensor_gpio()) { - dev.cmd_set->update_home_sensor_gpio(dev); - } + dev.cmd_set->update_home_sensor_gpio(dev); auto status = scanner_read_reliable_status(dev); @@ -1076,15 +944,6 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home) 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); @@ -1093,28 +952,22 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home) 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.startx = 0; + session.params.starty = 40000; + session.params.pixels = 50; + session.params.lines = 3; 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.scan_mode = ScanColorMode::GRAY; + session.params.color_filter = ColorFilter::GREEN; + session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | - ScanFlag::IGNORE_LINE_DISTANCE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET | ScanFlag::REVERSE; + if (dev.model->asic_type == AsicType::GL843) { session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE; } @@ -1143,9 +996,7 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home) throw; } - if (dev.cmd_set->needs_update_home_sensor_gpio()) { - dev.cmd_set->update_home_sensor_gpio(dev); - } + dev.cmd_set->update_home_sensor_gpio(dev); if (is_testing_mode()) { dev.interface->test_checkpoint("move_back_home"); @@ -1174,18 +1025,49 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home) // 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(); + dev.set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY); throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home"); } dbg.log(DBG_info, "scanhead is still moving"); } +namespace { + bool should_use_secondary_motor_mode(Genesys_Device& dev) + { + bool should_use = !dev.is_head_pos_known(ScanHeadId::SECONDARY) || + !dev.is_head_pos_known(ScanHeadId::PRIMARY) || + dev.head_pos(ScanHeadId::SECONDARY) > dev.head_pos(ScanHeadId::PRIMARY); + bool supports = dev.model->model_id == ModelId::CANON_8600F; + return should_use && supports; + } + + void handle_motor_position_after_move_back_home_ta(Genesys_Device& dev, MotorMode motor_mode) + { + if (motor_mode == MotorMode::SECONDARY) { + dev.set_head_pos_zero(ScanHeadId::SECONDARY); + return; + } + + 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); + } + } +} // namespace + void scanner_move_back_home_ta(Genesys_Device& dev) { DBG_HELPER(dbg); switch (dev.model->asic_type) { + case AsicType::GL842: case AsicType::GL843: + case AsicType::GL845: break; default: throw SaneException("Unsupported asic type"); @@ -1199,7 +1081,9 @@ void scanner_move_back_home_ta(Genesys_Device& dev) 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) + dev.is_head_pos_known(ScanHeadId::PRIMARY) && + dev.head_pos(ScanHeadId::SECONDARY) > 1000 && + dev.head_pos(ScanHeadId::SECONDARY) <= dev.head_pos(ScanHeadId::PRIMARY)) { // leave 500 steps for regular slow back home scanner_move(dev, scan_method, dev.head_pos(ScanHeadId::SECONDARY) - 500, @@ -1209,18 +1093,20 @@ void scanner_move_back_home_ta(Genesys_Device& dev) 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.startx = 0; + session.params.starty = 40000; + session.params.pixels = 50; + session.params.lines = 3; 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.color_filter = ColorFilter::GREEN; + session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | - ScanFlag::IGNORE_LINE_DISTANCE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET | ScanFlag::REVERSE; compute_session(&dev, session, sensor); @@ -1230,7 +1116,11 @@ void scanner_move_back_home_ta(Genesys_Device& dev) scanner_clear_scan_and_feed_counts(dev); dev.interface->write_registers(local_reg); - gl843::gl843_set_xpa_motor_power(&dev, local_reg, true); + + auto motor_mode = should_use_secondary_motor_mode(dev) ? MotorMode::SECONDARY + : MotorMode::PRIMARY_AND_SECONDARY; + + dev.cmd_set->set_motor_mode(dev, local_reg, motor_mode); try { scanner_start_action(dev, true); @@ -1244,18 +1134,10 @@ void scanner_move_back_home_ta(Genesys_Device& dev) 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); - } + handle_motor_position_after_move_back_home_ta(dev, motor_mode); scanner_stop_action(dev); - gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); + dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); return; } @@ -1266,18 +1148,10 @@ void scanner_move_back_home_ta(Genesys_Device& 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); - } + handle_motor_position_after_move_back_home_ta(dev, motor_mode); scanner_stop_action(dev); - gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); + dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); return; } @@ -1287,325 +1161,1148 @@ void scanner_move_back_home_ta(Genesys_Device& dev) 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) +void scanner_search_strip(Genesys_Device& dev, bool forward, bool black) { - DBG(DBG_info, "%s: two_table=%d\n", __func__, two_table); + DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); - // acceleration total time - unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps, - 0, std::plus<unsigned>()); + if (dev.model->asic_type == AsicType::GL841 && !black && forward) { + dev.frontend.set_gain(0, 0xff); + dev.frontend.set_gain(1, 0xff); + dev.frontend.set_gain(2, 0xff); + } - /* 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; + // 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(); + unsigned channels = 1; - /* 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]); + auto& sensor = sanei_genesys_find_sensor(&dev, dpi, channels, dev.settings.scan_method); + dev.cmd_set->set_fe(&dev, sensor, AFE_SET); + scanner_stop_action(dev); + + + // shading calibration is done with dev.motor.base_ydpi + unsigned lines = static_cast<unsigned>(dev.model->y_size_calib_mm * dpi / MM_PER_INCH); + if (dev.model->asic_type == AsicType::GL841) { + lines = 10; // TODO: use dev.model->search_lines + lines = static_cast<unsigned>((lines * dpi) / MM_PER_INCH); + } + + unsigned pixels = dev.model->x_size_calib_mm * dpi / MM_PER_INCH; + + dev.set_head_pos_zero(ScanHeadId::PRIMARY); + + unsigned length = 20; + if (dev.model->asic_type == AsicType::GL841) { + // 20 cm max length for calibration sheet + length = static_cast<unsigned>(((200 * dpi) / MM_PER_INCH) / lines); + } + + auto 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 (dev.model->asic_type != AsicType::GL841 && !forward) { + session.params.flags |= ScanFlag::REVERSE; + } + compute_session(&dev, session, sensor); + + dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session); + + 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"); + scanner_stop_action(dev); + return; + } + + wait_until_buffer_non_empty(&dev); + + // now we're on target, we can read data + auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + + scanner_stop_action(dev); + + unsigned pass = 0; + if (dbg_log_image_data()) { + char title[80]; + std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff", + black ? "black" : "white", forward ? "fwd" : "bwd", pass); + write_tiff_file(title, image); + } + + // loop until strip is found or maximum pass number done + bool found = false; + while (pass < length && !found) { + dev.interface->write_registers(local_reg); + + // now start scan + dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true); + + wait_until_buffer_non_empty(&dev); + + // now we're on target, we can read data + image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + + scanner_stop_action(dev); + + if (dbg_log_image_data()) { + char title[80]; + std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff", + black ? "black" : "white", + forward ? "fwd" : "bwd", static_cast<int>(pass)); + write_tiff_file(title, image); + } + + unsigned white_level = 90; + unsigned black_level = 60; + + std::size_t count = 0; + // 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 ame color + if (forward) { + + for (std::size_t y = 0; y < image.get_height() && !found; y++) { + count = 0; + + // count of white/black pixels depending on the color searched + for (std::size_t x = 0; x < image.get_width(); x++) { + + // when searching for black, detect white pixels + if (black && image.get_raw_channel(x, y, 0) > white_level) { + count++; + } + + // when searching for white, detect black pixels + if (!black && image.get_raw_channel(x, y, 0) < 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 + + auto found_percentage = (count * 100 / image.get_width()); + if (found_percentage < 3) { + found = 1; + DBG(DBG_data, "%s: strip found forward during pass %d at line %zu\n", __func__, + pass, y); + } else { + DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, + image.get_width(), count, found_percentage); + } + } + } else { + /* since calibration scans are done forward, we need the whole area + to be of the required color when searching backward + */ + count = 0; + for (std::size_t y = 0; y < image.get_height(); y++) { + // count of white/black pixels depending on the color searched + for (std::size_t x = 0; x < image.get_width(); x++) { + // when searching for black, detect white pixels + if (black && image.get_raw_channel(x, y, 0) > white_level) { + count++; + } + // when searching for white, detect black pixels + if (!black && image.get_raw_channel(x, y, 0) < 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 + auto found_percentage = count * 100 / (image.get_width() * image.get_height()); + if (found_percentage < 3) { + found = 1; + DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); + } else { + DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, image.get_width(), + count, found_percentage); + } + } + pass++; + } + + if (found) { + DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white"); } else { - sum = sum + slope_table[acceleration_steps - 1]; + throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", + black ? "black" : "white"); } - *out_z2 = sum % exposure_time; } -static uint8_t genesys_adjust_gain(double* applied_multi, double multi, uint8_t gain) +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]; +} + +bool should_calibrate_only_active_area(const Genesys_Device& dev, + const Genesys_Settings& settings) { - double voltage, original_voltage; - uint8_t new_gain = 0; + if (settings.scan_method == ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) { + return true; + } + if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) { + return true; + } + } + return false; +} + +void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) +{ + DBG_HELPER(dbg); + + if (dev.model->asic_type == AsicType::GL842 && + dev.frontend.layout.type != FrontendType::WOLFSON) + { + return; + } + + if (dev.model->asic_type == AsicType::GL843 && + dev.frontend.layout.type != FrontendType::WOLFSON) + { + return; + } + + if (dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846) + { + // no gain nor offset for AKM AFE + std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04); + if ((reg04 & gl846::REG_0x04_FESET) == 0x02) { + return; + } + } + if (dev.model->asic_type == AsicType::GL847) { + // no gain nor offset for AKM AFE + std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04); + if ((reg04 & gl847::REG_0x04_FESET) == 0x02) { + return; + } + } - DBG(DBG_proc, "%s: multi=%f, gain=%d\n", __func__, multi, gain); + if (dev.model->asic_type == AsicType::GL124) { + std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A); + if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) { + return; + } + } - voltage = 0.5 + gain * 0.25; - original_voltage = voltage; + unsigned target_pixels = dev.model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; + unsigned start_pixel = 0; + unsigned black_pixels = (sensor.black_pixels * sensor.full_resolution) / sensor.full_resolution; - voltage *= multi; + unsigned channels = 3; + unsigned lines = 1; + unsigned resolution = sensor.full_resolution; - new_gain = static_cast<std::uint8_t>((voltage - 0.5) * 4); - if (new_gain > 0x0e) - new_gain = 0x0e; + const Genesys_Sensor* calib_sensor = &sensor; + if (dev.model->asic_type == AsicType::GL843) { + lines = 8; - voltage = 0.5 + (new_gain) * 0.25; + // compute divider factor to compute final pixels number + const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels, + dev.settings.scan_method); + resolution = dpihw_sensor.shading_resolution; + unsigned factor = sensor.full_resolution / resolution; - *applied_multi = voltage / original_voltage; + calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, + dev.settings.scan_method); - DBG(DBG_proc, "%s: orig voltage=%.2f, new voltage=%.2f, *applied_multi=%f, new_gain=%d\n", - __func__, original_voltage, voltage, *applied_multi, new_gain); + target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; + black_pixels = calib_sensor->black_pixels / factor; - return new_gain; -} + if (should_calibrate_only_active_area(dev, dev.settings)) { + float offset = dev.model->x_offset_ta; + start_pixel = static_cast<int>((offset * calib_sensor->get_optical_resolution()) / MM_PER_INCH); + float size = dev.model->x_size_ta; + target_pixels = static_cast<int>((size * calib_sensor->get_optical_resolution()) / MM_PER_INCH); + } -// 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) -{ + if (dev.model->model_id == ModelId::CANON_4400F && + dev.settings.scan_method == ScanMethod::FLATBED) + { + return; + } + } - DBG_HELPER_ARGS(dbg, "channels=%d, channel=%d, size=%d", channels, channel, size); - int gain_white_ref, sum, range; - int average; - int i; + if (dev.model->model_id == ModelId::CANON_5600F) { + // FIXME: use same approach as for GL843 scanners + lines = 8; + } - range = size / 50; + if (dev.model->asic_type == AsicType::GL847) { + calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, + dev.settings.scan_method); + } - if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || - dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + ScanFlag flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::SINGLE_LINE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; + + if (dev.settings.scan_method == ScanMethod::TRANSPARENCY || + dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - gain_white_ref = sensor.fau_gain_white_ref * 256; + 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 = dev.model->asic_type == AsicType::GL843 ? ColorFilter::RED + : dev.settings.color_filter; + session.params.flags = flags; + compute_session(&dev, session, *calib_sensor); + + dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session); + + unsigned output_pixels = session.output_pixels; + + sanei_genesys_set_motor_power(regs, false); + + int top[3], bottom[3]; + int topavg[3], bottomavg[3], avg[3]; + + // 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, ®s, true); + + if (is_testing_mode()) { + dev.interface->test_checkpoint("offset_calibration"); + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + scanner_stop_action_no_move(dev, regs); + } + return; + } + + Image first_line; + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + first_line = read_unshuffled_image_from_scanner(&dev, session, + session.output_total_bytes_raw); + scanner_stop_action_no_move(dev, regs); } else { - gain_white_ref = sensor.gain_white_ref * 256; + first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + + if (dev.model->model_id == ModelId::CANON_5600F) { + scanner_stop_action_no_move(dev, regs); + } + } + + if (dbg_log_image_data()) { + char fn[40]; + std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.tiff", + bottom[0], bottom[1], bottom[2]); + write_tiff_file(fn, first_line); } - if (range < 1) - range = 1; + for (unsigned ch = 0; ch < 3; ch++) { + bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch); + DBG(DBG_info, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]); + } - size = size / (2 * range * channels); + // 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); - data += (channel * 2); + // scan with top AFE values + dev.interface->write_registers(regs); + DBG(DBG_info, "%s: starting second line reading\n", __func__); - *max_average = 0; + dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true); - while (size--) + Image second_line; + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) { - sum = 0; - for (i = 0; i < range; i++) - { - sum += (*data); - sum += *(data + 1) * 256; - data += (2 * channels); /* byte based */ - } + second_line = read_unshuffled_image_from_scanner(&dev, session, + session.output_total_bytes_raw); + scanner_stop_action_no_move(dev, regs); + } else { + second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); - average = (sum / range); - if (average > *max_average) - *max_average = average; + if (dev.model->model_id == ModelId::CANON_5600F) { + scanner_stop_action_no_move(dev, regs); + } } - DBG(DBG_proc, "%s: max_average=%d, gain_white_ref = %d, finished\n", __func__, *max_average, - gain_white_ref); + for (unsigned ch = 0; ch < 3; ch++){ + topavg[ch] = dark_average_channel(second_line, black_pixels, ch); + DBG(DBG_info, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]); + } + + unsigned pass = 0; + + std::vector<std::uint8_t> debug_image; + std::size_t debug_image_lines = 0; + std::string debug_image_info; - if (*max_average >= gain_white_ref) - throw SaneException(SANE_STATUS_INVAL); + // loop until acceptable level + while ((pass < 32) && ((top[0] - bottom[0] > 1) || + (top[1] - bottom[1] > 1) || + (top[2] - bottom[2] > 1))) + { + pass++; + + 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, ®s, true); + + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + second_line = read_unshuffled_image_from_scanner(&dev, session, + session.output_total_bytes_raw); + scanner_stop_action_no_move(dev, regs); + } else { + second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + + if (dev.model->model_id == ModelId::CANON_5600F) { + scanner_stop_action_no_move(dev, regs); + } + } + + if (dbg_log_image_data()) { + char title[100]; + std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n", + lines, output_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_log_image_data()) { + sanei_genesys_write_file("gl_offset_all_desc.txt", + reinterpret_cast<const std::uint8_t*>(debug_image_info.data()), + debug_image_info.size()); + write_tiff_file("gl_offset_all.tiff", debug_image.data(), session.params.depth, channels, + output_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)); } -/* todo: understand, values are too high */ -static int -genesys_average_black (Genesys_Device * dev, int channel, - uint8_t * data, int pixels) +/* 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 scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs, unsigned dpi) { - int i; - int sum; - int pixel_step; + DBG_HELPER_ARGS(dbg, "dpi = %d", dpi); - DBG(DBG_proc, "%s: channel=%d, pixels=%d\n", __func__, channel, pixels); + if (dev.model->asic_type == AsicType::GL842 && + dev.frontend.layout.type != FrontendType::WOLFSON) + { + return; + } - sum = 0; + if (dev.model->asic_type == AsicType::GL843 && + dev.frontend.layout.type != FrontendType::WOLFSON) + { + return; + } - if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) + if (dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846) { - data += (channel * 2); - pixel_step = 3 * 2; + // no gain nor offset for AKM AFE + std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04); + if ((reg04 & gl846::REG_0x04_FESET) == 0x02) { + return; + } } - else + + if (dev.model->asic_type == AsicType::GL847) { + // no gain nor offset for AKM AFE + std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04); + if ((reg04 & gl847::REG_0x04_FESET) == 0x02) { + return; + } + } + + if (dev.model->asic_type == AsicType::GL124) { + // no gain nor offset for TI AFE + std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A); + if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) { + return; + } + } + + if (dev.model->asic_type == AsicType::GL841) { + // feed to white strip if needed + if (dev.model->y_offset_calib_white > 0) { + unsigned move = static_cast<unsigned>( + (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH); + scanner_move(dev, dev.model->default_method, move, Direction::FORWARD); + } + } + + // coarse gain calibration is always done in color mode + unsigned channels = 3; + + unsigned resolution = sensor.full_resolution; + if (dev.model->asic_type == AsicType::GL841) { + const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels, + dev.settings.scan_method); + resolution = dpihw_sensor.shading_resolution; + } + + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) { - pixel_step = 2; + const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dpi, channels, + dev.settings.scan_method); + resolution = dpihw_sensor.shading_resolution; } - for (i = 0; i < pixels; i++) + float coeff = 1; + + // Follow CKSEL + if (dev.model->sensor_id == SensorId::CCD_KVSS080 || + dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846 || + dev.model->asic_type == AsicType::GL847 || + dev.model->asic_type == AsicType::GL124) { - sum += *data; - sum += *(data + 1) * 256; + if (dev.settings.xres < sensor.full_resolution) { + coeff = 0.9f; + } + } - data += pixel_step; + unsigned lines = 10; + if (dev.model->asic_type == AsicType::GL841) { + lines = 1; } - DBG(DBG_proc, "%s = %d\n", __func__, sum / pixels); + const Genesys_Sensor* calib_sensor = &sensor; + if (dev.model->asic_type == AsicType::GL841 || + dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843 || + dev.model->asic_type == AsicType::GL847) + { + calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, + dev.settings.scan_method); + } - return sum / pixels; + ScanFlag flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::SINGLE_LINE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; + + 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 = dev.model->asic_type == AsicType::GL841 ? dev.settings.yres : resolution; + session.params.startx = 0; + session.params.starty = 0; + session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; + session.params.lines = lines; + session.params.depth = dev.model->asic_type == AsicType::GL841 ? 16 : 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 { + dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session); + } catch (...) { + if (dev.model->asic_type != AsicType::GL841) { + catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); }); + } + throw; + } + + if (dev.model->asic_type != AsicType::GL841) { + sanei_genesys_set_motor_power(regs, false); + } + + dev.interface->write_registers(regs); + + if (dev.model->asic_type != AsicType::GL841) { + dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET); + } + dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true); + + if (is_testing_mode()) { + dev.interface->test_checkpoint("coarse_gain_calibration"); + scanner_stop_action(dev); + dev.cmd_set->move_back_home(&dev, true); + return; + } + + Image image; + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); + } else if (dev.model->asic_type == AsicType::GL124) { + // BUG: we probably want to read whole image, not just first line + image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes); + } else { + image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + } + + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + scanner_stop_action_no_move(dev, regs); + } + + if (dbg_log_image_data()) { + write_tiff_file("gl_coarse_gain.tiff", image); + } + + for (unsigned ch = 0; ch < channels; ch++) { + float curr_output = 0; + float target_value = 0; + + if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + 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(image.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()); + curr_output = static_cast<float>(values[unsigned((values.size() - 1) * 0.95)]); + target_value = calib_sensor->gain_white_ref * coeff; + + } else if (dev.model->asic_type == AsicType::GL841) { + // FIXME: use the GL843 approach + unsigned max = 0; + for (std::size_t x = 0; x < image.get_width(); x++) { + auto value = image.get_raw_channel(x, 0, ch); + if (value > max) { + max = value; + } + } + + curr_output = max; + target_value = 65535.0f; + } else { + // FIXME: use the GL843 approach + auto width = image.get_width(); + + std::uint64_t total = 0; + for (std::size_t x = width / 4; x < (width * 3 / 4); x++) { + total += image.get_raw_channel(x, 0, ch); + } + + curr_output = total / (width / 2); + target_value = calib_sensor->gain_white_ref * coeff; + } + + std::uint8_t out_gain = compute_frontend_gain(curr_output, target_value, + dev.frontend.layout.type); + dev.frontend.set_gain(ch, out_gain); + + DBG(DBG_proc, "%s: channel %d, curr=%f, target=%f, out_gain:%d\n", __func__, ch, + curr_output, target_value, out_gain); + + if (dev.model->asic_type == AsicType::GL841 && + target_value / curr_output > 30) + { + 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"); + throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked"); + } + + dbg.vlog(DBG_info, "gain=(%d, %d, %d)", dev.frontend.get_gain(0), dev.frontend.get_gain(1), + dev.frontend.get_gain(2)); + } + + if (dev.model->is_cis) { + std::uint8_t min_gain = std::min({dev.frontend.get_gain(0), + dev.frontend.get_gain(1), + dev.frontend.get_gain(2)}); + + dev.frontend.set_gain(0, min_gain); + dev.frontend.set_gain(1, min_gain); + dev.frontend.set_gain(2, min_gain); + } + + dbg.vlog(DBG_info, "final gain=(%d, %d, %d)", dev.frontend.get_gain(0), + dev.frontend.get_gain(1), dev.frontend.get_gain(2)); + + scanner_stop_action(dev); + + dev.cmd_set->move_back_home(&dev, true); } +namespace gl124 { + void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs); +} // namespace gl124 -// 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) +SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) { - DBG_HELPER_ARGS(dbg, "scan_mode = %d", static_cast<unsigned>(dev->settings.scan_mode)); - int black_pixels; - int white_average; - uint8_t offset[4] = { 0xa0, 0x00, 0xa0, 0x40 }; /* first value isn't used */ - uint16_t white[12], dark[12]; - int i, j; + DBG_HELPER(dbg); - black_pixels = sensor.black_pixels - * dev->settings.xres / sensor.optical_res; + float move = 0; - unsigned channels = dev->settings.get_channels(); + if (dev.model->asic_type == AsicType::GL841) { + if (dev.model->y_offset_calib_white > 0) { + move = (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH; + scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move), + Direction::FORWARD); + } + } else if (dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + // do nothing + } else if (dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846 || + dev.model->asic_type == AsicType::GL847) + { + move = 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); + } + } else if (dev.model->asic_type == AsicType::GL124) { + gl124::move_to_calibration_area(&dev, sensor, regs); + } - DBG(DBG_info, "channels %d y_size %f xres %d\n", channels, dev->model->y_size.value(), - dev->settings.xres); - 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); + unsigned channels = 3; + unsigned resolution = sensor.shading_resolution; + const auto& calib_sensor = sanei_genesys_find_sensor(&dev, resolution, channels, + dev.settings.scan_method); + + 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) + { + regs = dev.reg; // FIXME: apply this to all ASICs + } + + unsigned yres = resolution; + if (dev.model->asic_type == AsicType::GL841) { + yres = dev.settings.yres; // FIXME: remove this + } + + ScanSession session; + session.params.xres = resolution; + session.params.yres = yres; + session.params.startx = 0; + session.params.starty = 0; + session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; + 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_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; + compute_session(&dev, session, calib_sensor); + + dev.cmd_set->init_regs_for_scan_session(&dev, calib_sensor, ®s, session); + + if (dev.model->asic_type == AsicType::GL841) { + dev.interface->write_registers(regs); // FIXME: remove this + } + + std::uint16_t exp[3]; - dev->cmd_set->set_fe(dev, sensor, AFE_INIT); + if (dev.model->asic_type == AsicType::GL841) { + exp[0] = sensor.exposure.red; + exp[1] = sensor.exposure.green; + exp[2] = sensor.exposure.blue; + } else { + exp[0] = calib_sensor.exposure.red; + exp[1] = calib_sensor.exposure.green; + exp[2] = calib_sensor.exposure.blue; + } + + std::uint16_t target = sensor.gain_white_ref * 256; - dev->frontend.set_gain(0, 2); - dev->frontend.set_gain(1, 2); - dev->frontend.set_gain(2, 2); // TODO: ? was 2 - dev->frontend.set_offset(0, offset[0]); - dev->frontend.set_offset(1, offset[0]); - dev->frontend.set_offset(2, offset[0]); + std::uint16_t min_exposure = 500; // only gl841 + std::uint16_t max_exposure = ((exp[0] + exp[1] + exp[2]) / 3) * 2; // only gl841 - for (i = 0; i < 4; i++) /* read 4 lines */ + std::uint16_t top[3] = {}; + std::uint16_t bottom[3] = {}; + + if (dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846) { - if (i < 3) /* first 3 lines */ - { - dev->frontend.set_offset(0, offset[i]); - dev->frontend.set_offset(1, offset[i]); - dev->frontend.set_offset(2, offset[i]); + bottom[0] = 29000; + bottom[1] = 29000; + bottom[2] = 29000; + + top[0] = 41000; + top[1] = 51000; + top[2] = 51000; + } else if (dev.model->asic_type == AsicType::GL847) { + bottom[0] = 28000; + bottom[1] = 28000; + bottom[2] = 28000; + + top[0] = 32000; + top[1] = 32000; + top[2] = 32000; + } + + 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) + { + sanei_genesys_set_motor_power(regs, false); + } + + bool acceptable = false; + for (unsigned i_test = 0; i_test < 100 && !acceptable; ++i_test) { + regs_set_exposure(dev.model->asic_type, regs, { exp[0], exp[1], exp[2] }); + + if (dev.model->asic_type == AsicType::GL841) { + // FIXME: remove + dev.interface->write_register(0x10, (exp[0] >> 8) & 0xff); + dev.interface->write_register(0x11, exp[0] & 0xff); + dev.interface->write_register(0x12, (exp[1] >> 8) & 0xff); + dev.interface->write_register(0x13, exp[1] & 0xff); + dev.interface->write_register(0x14, (exp[2] >> 8) & 0xff); + dev.interface->write_register(0x15, exp[2] & 0xff); } - if (i == 1) /* second line */ - { - double applied_multi; - double gain_white_ref; + dev.interface->write_registers(regs); - if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || - dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) - { - gain_white_ref = sensor.fau_gain_white_ref * 256; + dbg.log(DBG_info, "starting line reading"); + dev.cmd_set->begin_scan(&dev, calib_sensor, ®s, true); + + if (is_testing_mode()) { + dev.interface->test_checkpoint("led_calibration"); + if (dev.model->asic_type == AsicType::GL841) { + scanner_stop_action(dev); + dev.cmd_set->move_back_home(&dev, true); + return { exp[0], exp[1], exp[2] }; + } else if (dev.model->asic_type == AsicType::GL124) { + scanner_stop_action(dev); + return calib_sensor.exposure; } else { - gain_white_ref = sensor.gain_white_ref * 256; + scanner_stop_action(dev); + dev.cmd_set->move_back_home(&dev, true); + return calib_sensor.exposure; } + } - // white and black are defined downwards - - uint8_t gain0 = genesys_adjust_gain(&applied_multi, - gain_white_ref / (white[0] - dark[0]), - dev->frontend.get_gain(0)); - uint8_t gain1 = genesys_adjust_gain(&applied_multi, - gain_white_ref / (white[1] - dark[1]), - dev->frontend.get_gain(1)); - uint8_t gain2 = genesys_adjust_gain(&applied_multi, - gain_white_ref / (white[2] - dark[2]), - dev->frontend.get_gain(2)); - // FIXME: looks like overwritten data. Are the above calculations doing - // anything at all? - dev->frontend.set_gain(0, gain0); - dev->frontend.set_gain(1, gain1); - dev->frontend.set_gain(2, gain2); - dev->frontend.set_gain(0, 2); - dev->frontend.set_gain(1, 2); - dev->frontend.set_gain(2, 2); - - 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)); - } + auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes); - if (i == 3) /* last line */ - { - double x, y, rate; + scanner_stop_action(dev); - for (j = 0; j < 3; j++) - { + if (dbg_log_image_data()) { + char fn[30]; + std::snprintf(fn, 30, "gl_led_%02d.tiff", i_test); + write_tiff_file(fn, image); + } - 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]; - rate = (x - DARK_VALUE - y) * 254 / x + 0.5; + int avg[3]; + 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(); + } - uint8_t curr_offset = static_cast<uint8_t>(rate); + dbg.vlog(DBG_info, "average: %d, %d, %d", avg[0], avg[1], avg[2]); - if (curr_offset > 0x7f) { - curr_offset = 0x7f; - } - curr_offset <<= 1; - dev->frontend.set_offset(j, curr_offset); - } - } - 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)); + acceptable = true; - DBG(DBG_info, - "%s: doing scan: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__, - dev->frontend.get_gain(0), - dev->frontend.get_gain(1), - dev->frontend.get_gain(2), - dev->frontend.get_offset(0), - dev->frontend.get_offset(1), - dev->frontend.get_offset(2)); + if (dev.model->asic_type == AsicType::GL841) { + 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; + } - dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false); + // for scanners using target value + if (target > 0) { + acceptable = true; + for (unsigned i = 0; i < 3; i++) { + // we accept +- 2% delta from target + if (std::abs(avg[i] - target) > target / 50) { + exp[i] = (exp[i] * target) / avg[i]; + acceptable = false; + } + } + } else { + if (!acceptable) { + unsigned 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 + */ + unsigned 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; + } - if (is_testing_mode()) { - dev->interface->test_checkpoint("coarse_calibration"); - dev->cmd_set->end_scan(dev, &dev->calib_reg, true); - return; + } + } + } else if (dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846) + { + for (unsigned i = 0; i < 3; i++) { + if (avg[i] < bottom[i]) { + if (avg[i] != 0) { + exp[i] = (exp[i] * bottom[i]) / avg[i]; + } else { + exp[i] *= 10; + } + acceptable = false; + } + if (avg[i] > top[i]) { + if (avg[i] != 0) { + exp[i] = (exp[i] * top[i]) / avg[i]; + } else { + exp[i] *= 10; + } + acceptable = false; + } + } + } else if (dev.model->asic_type == AsicType::GL847) { + for (unsigned i = 0; i < 3; i++) { + if (avg[i] < bottom[i] || avg[i] > top[i]) { + auto target = (bottom[i] + top[i]) / 2; + if (avg[i] != 0) { + exp[i] = (exp[i] * target) / avg[i]; + } else { + exp[i] *= 10; + } + + acceptable = false; + } + } + } else if (dev.model->asic_type == AsicType::GL124) { + for (unsigned i = 0; i < 3; i++) { + // we accept +- 2% delta from target + if (std::abs(avg[i] - target) > target / 50) { + float prev_weight = 0.5; + if (avg[i] != 0) { + exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight); + } else { + exp[i] = exp[i] * prev_weight + (exp[i] * 10) * (1 - prev_weight); + } + acceptable = false; + } + } } + } - 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; + 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) + { + // set these values as final ones for scan + regs_set_exposure(dev.model->asic_type, dev.reg, { exp[0], exp[1], exp[2] }); + } - for (count = 0; count < static_cast<unsigned>(size * 4 / 2); count++) { - all_data_8[count] = all_data[count * 2 + 1]; + if (dev.model->asic_type == AsicType::GL841 || + dev.model->asic_type == AsicType::GL842 || + dev.model->asic_type == AsicType::GL843) + { + dev.cmd_set->move_back_home(&dev, true); + } + + if (dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846 || + dev.model->asic_type == AsicType::GL847) + { + if (move > 20) { + dev.cmd_set->move_back_home(&dev, true); } - sanei_genesys_write_pnm_file("gl_coarse.pnm", all_data_8.data(), 8, channels, size / 6, 4); - } + } - dev->cmd_set->end_scan(dev, &dev->calib_reg, true); + dbg.vlog(DBG_info,"acceptable exposure: %d, %d, %d\n", exp[0], exp[1], exp[2]); - 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); - white[i * 3 + j] = white_average; - dark[i * 3 + j] = - genesys_average_black (dev, j, calibration_data.data(), - black_pixels); - DBG(DBG_info, "%s: white[%d]=%d, black[%d]=%d\n", __func__, - i * 3 + j, white[i * 3 + j], i * 3 + j, dark[i * 3 + j]); - } - } - else /* one color-component modes */ - { - 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); - } - } /* for (i = 0; i < 4; i++) */ - - DBG(DBG_info, "%s: final: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__, - dev->frontend.get_gain(0), - dev->frontend.get_gain(1), - dev->frontend.get_gain(2), - dev->frontend.get_offset(0), - dev->frontend.get_offset(1), - dev->frontend.get_offset(2)); + return { exp[0], exp[1], exp[2] }; +} + +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) +{ + // 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; } /** @@ -1614,22 +2311,41 @@ static void genesys_coarse_calibration(Genesys_Device* dev, Genesys_Sensor& sens * @param dev scanner's device */ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& local_reg, std::vector<std::uint16_t>& out_average_data, bool is_dark, const std::string& log_filename_prefix) { DBG_HELPER(dbg); + if (dev->model->asic_type == AsicType::GL646) { + dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); + local_reg = dev->reg; + } else { + local_reg = dev->reg; + dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); + dev->interface->write_registers(local_reg); + } + debug_dump(DBG_info, dev->calib_session); size_t size; uint32_t pixels_per_line; - uint8_t channels; - /* end pixel - start pixel */ - pixels_per_line = dev->calib_pixels; - channels = dev->calib_channels; + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843 || + dev->model->model_id == ModelId::CANON_5600F) + { + pixels_per_line = dev->calib_session.output_pixels; + } else { + // BUG: this selects incorrect pixel number + pixels_per_line = dev->calib_session.params.pixels; + } + unsigned channels = dev->calib_session.params.channels; - uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset; + // BUG: we are using wrong pixel number here + unsigned start_offset = + dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; + unsigned out_pixels_per_line = pixels_per_line + start_offset; // FIXME: we set this during both dark and white calibration. A cleaner approach should // probably be used @@ -1644,61 +2360,55 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_ } // 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 == AsicType::GL843) { - size = channels * 2 * pixels_per_line * dev->calib_lines; + // but this needs checking. Note the extra line when computing size. + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843 || + dev->model->model_id == ModelId::CANON_5600F) + { + size = dev->calib_session.output_total_bytes_raw; } else { - size = channels * 2 * pixels_per_line * (dev->calib_lines + 1); + size = channels * 2 * pixels_per_line * (dev->calib_session.params.lines + 1); } std::vector<uint16_t> calibration_data(size / 2); - bool motor = true; - if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) - { - 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 (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); + sanei_genesys_set_lamp_power(dev, sensor, local_reg, false); } else { - sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true); - sanei_genesys_set_motor_power(dev->calib_reg, motor); + sanei_genesys_set_lamp_power(dev, sensor, local_reg, true); } + sanei_genesys_set_motor_power(local_reg, true); - dev->interface->write_registers(dev->calib_reg); + dev->interface->write_registers(local_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) { + } else if (has_flag(dev->model->flags, ModelFlag::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); } bool start_motor = !is_dark; - dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, start_motor); + dev->cmd_set->begin_scan(dev, sensor, &local_reg, start_motor); 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); + dev->cmd_set->end_scan(dev, &local_reg, true); return; } 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); + dev->cmd_set->end_scan(dev, &local_reg, true); - if (dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) { + if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { for (std::size_t i = 0; i < size / 2; ++i) { auto value = calibration_data[i]; value = ((value >> 8) & 0xff) | ((value << 8) & 0xff00); @@ -1706,30 +2416,29 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_ } } + if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { + for (std::size_t i = 0; i < size / 2; ++i) { + calibration_data[i] = 0xffff - calibration_data[i]; + } + } + std::fill(out_average_data.begin(), - out_average_data.begin() + dev->calib_pixels_offset * channels, 0); + out_average_data.begin() + start_offset * channels, 0); - compute_array_percentile_approx(out_average_data.data() + dev->calib_pixels_offset * channels, + compute_array_percentile_approx(out_average_data.data() + + start_offset * channels, calibration_data.data(), - dev->calib_lines, pixels_per_line * channels, + dev->calib_session.params.lines, pixels_per_line * channels, 0.5f); - 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); + if (dbg_log_image_data()) { + write_tiff_file(log_filename_prefix + "_shading.tiff", calibration_data.data(), 16, + channels, pixels_per_line, dev->calib_session.params.lines); + write_tiff_file(log_filename_prefix + "_average.tiff", out_average_data.data(), 16, + channels, out_pixels_per_line, 1); } } - -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 @@ -1737,18 +2446,28 @@ static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_ * can be computed from previous calibration data (when doing offset * calibration ?) */ -static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor& sensor) +static void genesys_dark_shading_by_dummy_pixel(Genesys_Device* dev, const Genesys_Sensor& sensor) { DBG_HELPER(dbg); uint32_t pixels_per_line; - uint8_t channels; uint32_t skip, xend; int dummy1, dummy2, dummy3; /* dummy black average per channel */ - pixels_per_line = dev->calib_pixels; - channels = dev->calib_channels; + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { + pixels_per_line = dev->calib_session.output_pixels; + } else { + pixels_per_line = dev->calib_session.params.pixels; + } + + unsigned channels = dev->calib_session.params.channels; + + // BUG: we are using wrong pixel number here + unsigned start_offset = + dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; - uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset; + unsigned out_pixels_per_line = pixels_per_line + start_offset; dev->average_size = channels * out_pixels_per_line; dev->dark_average_data.clear(); @@ -1756,8 +2475,7 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor /* we average values on 'the left' where CCD pixels are under casing and give darkest values. We then use these as dummy dark calibration */ - if (dev->settings.xres <= sensor.optical_res / 2) - { + if (dev->settings.xres <= sensor.full_resolution / 2) { skip = 4; xend = 36; } @@ -1807,17 +2525,22 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor } } +static void genesys_dark_shading_by_constant(Genesys_Device& dev) +{ + dev.dark_average_data.clear(); + dev.dark_average_data.resize(dev.average_size, 0x0101); +} static void genesys_repark_sensor_before_shading(Genesys_Device* dev) { DBG_HELPER(dbg); - if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) { + if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) { dev->cmd_set->move_back_home(dev, true); if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - dev->cmd_set->move_to_ta(dev); + scanner_move_to_ta(*dev); } } } @@ -1825,34 +2548,153 @@ static void genesys_repark_sensor_before_shading(Genesys_Device* dev) static void genesys_repark_sensor_after_white_shading(Genesys_Device* dev) { DBG_HELPER(dbg); - if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) { + if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) { dev->cmd_set->move_back_home(dev, true); } } -static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor) +static void genesys_host_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) { DBG_HELPER(dbg); - genesys_shading_calibration_impl(dev, sensor, dev->white_average_data, false, "gl_white_"); + + if (is_dark && dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { + // FIXME: dark shading currently not supported on infrared transparency scans + return; + } + + auto local_reg = dev.reg; + dev.cmd_set->init_regs_for_shading(&dev, sensor, local_reg); + + auto& session = dev.calib_session; + debug_dump(DBG_info, session); + + // 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, local_reg, false); + } else { + sanei_genesys_set_lamp_power(&dev, sensor, local_reg, true); + } + sanei_genesys_set_motor_power(local_reg, true); + + dev.interface->write_registers(local_reg); + + if (is_dark) { + // wait some time to let lamp to get dark + dev.interface->sleep_ms(200); + } else if (has_flag(dev.model->flags, ModelFlag::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); + } + + bool start_motor = !is_dark; + dev.cmd_set->begin_scan(&dev, sensor, &local_reg, start_motor); + + if (is_testing_mode()) { + dev.interface->test_checkpoint(is_dark ? "host_dark_shading_calibration" + : "host_white_shading_calibration"); + dev.cmd_set->end_scan(&dev, &local_reg, true); + return; + } + + Image image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); + scanner_stop_action(dev); + + auto start_offset = session.params.startx; + auto out_pixels_per_line = start_offset + session.output_pixels; + + // FIXME: we set this during both dark and white calibration. A cleaner approach should + // probably be used + dev.average_size = session.params.channels * out_pixels_per_line; + + out_average_data.clear(); + out_average_data.resize(dev.average_size); + + std::fill(out_average_data.begin(), + out_average_data.begin() + start_offset * session.params.channels, 0); + + compute_array_percentile_approx(out_average_data.data() + + start_offset * session.params.channels, + reinterpret_cast<std::uint16_t*>(image.get_row_ptr(0)), + session.params.lines, + session.output_pixels * session.params.channels, + 0.5f); + + if (dbg_log_image_data()) { + write_tiff_file(log_filename_prefix + "_host_shading.tiff", image); + write_tiff_file(log_filename_prefix + "_host_average.tiff", out_average_data.data(), 16, + session.params.channels, out_pixels_per_line, 1); + } +} + +static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& local_reg) +{ + DBG_HELPER(dbg); + if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) { + genesys_host_shading_calibration_impl(*dev, sensor, dev->dark_average_data, true, + "gl_black"); + } else { + genesys_shading_calibration_impl(dev, sensor, local_reg, dev->dark_average_data, true, + "gl_black"); + } +} + +static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& local_reg) +{ + DBG_HELPER(dbg); + if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) { + genesys_host_shading_calibration_impl(*dev, sensor, dev->white_average_data, false, + "gl_white"); + } else { + genesys_shading_calibration_impl(dev, sensor, local_reg, 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 void genesys_dark_white_shading_calibration(Genesys_Device* dev, - const Genesys_Sensor& sensor) + const Genesys_Sensor& sensor, + Genesys_Register_Set& local_reg) { - DBG_HELPER_ARGS(dbg, "lines = %zu", dev->calib_lines); + DBG_HELPER(dbg); + + if (dev->model->asic_type == AsicType::GL646) { + dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); + local_reg = dev->reg; + } else { + local_reg = dev->reg; + dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); + dev->interface->write_registers(local_reg); + } + size_t size; uint32_t pixels_per_line; - uint8_t channels; unsigned int x; uint32_t dark, white, dark_sum, white_sum, dark_count, white_count, col, dif; - pixels_per_line = dev->calib_pixels; - channels = dev->calib_channels; + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { + pixels_per_line = dev->calib_session.output_pixels; + } else { + pixels_per_line = dev->calib_session.params.pixels; + } - uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset; + unsigned channels = dev->calib_session.params.channels; + + // BUG: we are using wrong pixel number here + unsigned start_offset = + dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; + + unsigned out_pixels_per_line = pixels_per_line + start_offset; dev->average_size = channels * out_pixels_per_line; @@ -1862,68 +2704,65 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev, dev->dark_average_data.clear(); dev->dark_average_data.resize(dev->average_size); - if (dev->calib_total_bytes_to_read > 0) - size = dev->calib_total_bytes_to_read; - else - size = channels * 2 * pixels_per_line * dev->calib_lines; - - std::vector<uint8_t> calibration_data(size); - - bool motor = true; - if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) { - motor = false; + size = dev->calib_session.output_total_bytes_raw; + } else { + // FIXME: on GL841 this is different than dev->calib_session.output_total_bytes_raw, + // needs checking + size = channels * 2 * pixels_per_line * dev->calib_session.params.lines; } + std::vector<uint8_t> calibration_data(size); + // 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); + sanei_genesys_set_lamp_power(dev, sensor, local_reg, true); + sanei_genesys_set_motor_power(local_reg, true); - dev->interface->write_registers(dev->calib_reg); + dev->interface->write_registers(local_reg); - dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false); + dev->cmd_set->begin_scan(dev, sensor, &local_reg, false); if (is_testing_mode()) { dev->interface->test_checkpoint("dark_white_shading_calibration"); - dev->cmd_set->end_scan(dev, &dev->calib_reg, true); + dev->cmd_set->end_scan(dev, &local_reg, true); return; } sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size); - dev->cmd_set->end_scan(dev, &dev->calib_reg, true); + dev->cmd_set->end_scan(dev, &local_reg, true); - if (DBG_LEVEL >= DBG_data) - { - if (dev->model->is_cis) - { - sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", calibration_data.data(), - 16, 1, pixels_per_line*channels, - dev->calib_lines); - } - else - { - sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", calibration_data.data(), - 16, channels, pixels_per_line, - dev->calib_lines); + if (dbg_log_image_data()) { + if (dev->model->is_cis) { + write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(), + 16, 1, pixels_per_line*channels, + dev->calib_session.params.lines); + } else { + write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(), + 16, channels, pixels_per_line, + dev->calib_session.params.lines); } } std::fill(dev->dark_average_data.begin(), - dev->dark_average_data.begin() + dev->calib_pixels_offset * channels, 0); + dev->dark_average_data.begin() + start_offset * channels, 0); std::fill(dev->white_average_data.begin(), - dev->white_average_data.begin() + dev->calib_pixels_offset * channels, 0); + dev->white_average_data.begin() + start_offset * channels, 0); - 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; + uint16_t* average_white = dev->white_average_data.data() + + start_offset * channels; + uint16_t* average_dark = dev->dark_average_data.data() + + start_offset * channels; for (x = 0; x < pixels_per_line * channels; x++) { dark = 0xffff; white = 0; - for (std::size_t y = 0; y < dev->calib_lines; y++) + for (std::size_t y = 0; y < dev->calib_session.params.lines; y++) { col = calibration_data[(x + y * pixels_per_line * channels) * 2]; col |= @@ -1947,7 +2786,7 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev, white_count = 0; white_sum = 0; - for (std::size_t y = 0; y < dev->calib_lines; y++) + for (std::size_t y = 0; y < dev->calib_session.params.lines; y++) { col = calibration_data[(x + y * pixels_per_line * channels) * 2]; col |= @@ -1974,11 +2813,11 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev, *average_white++ = white_sum; } - 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); + if (dbg_log_image_data()) { + write_tiff_file("gl_white_average.tiff", dev->white_average_data.data(), 16, channels, + out_pixels_per_line, 1); + write_tiff_file("gl_dark_average.tiff", dev->dark_average_data.data(), 16, channels, + out_pixels_per_line, 1); } } @@ -2085,13 +2924,12 @@ compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor, */ res = dev->settings.xres; - if (sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres) > 1) - { + if (sensor.full_resolution > sensor.get_optical_resolution()) { res *= 2; } - /* this should be evenly dividable */ - basepixels = sensor.optical_res / res; + // this should be evenly dividable + basepixels = sensor.full_resolution / res; /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */ if (basepixels < 1) @@ -2376,9 +3214,10 @@ compute_shifted_coefficients (Genesys_Device * dev, 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 */ - basepixels = sensor.optical_res / x; /*this should be evenly dividable */ + if (sensor.full_resolution > sensor.get_optical_resolution()) { + x *= 2; // scanner is using half-ccd mode + } + basepixels = sensor.full_resolution / x; // this should be evenly dividable /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */ if (basepixels < 1) @@ -2451,19 +3290,30 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ { DBG_HELPER(dbg); - if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) { + if (sensor.use_host_side_calib) { 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 coeff, target_code, words_per_color = 0; - pixels_per_line = dev->calib_pixels + dev->calib_pixels_offset; - channels = dev->calib_channels; + + // BUG: we are using wrong pixel number here + unsigned start_offset = + dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; + + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { + pixels_per_line = dev->calib_session.output_pixels + start_offset; + } else { + pixels_per_line = dev->calib_session.params.pixels + start_offset; + } + + unsigned channels = dev->calib_session.params.channels; /* we always build data for three channels, even for gray * we make the shading data such that each color channel data line is contiguous @@ -2504,25 +3354,27 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ // contains 16bit words in little endian std::vector<uint8_t> shading_data(length, 0); + if (!dev->calib_session.computed) { + genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length); + return; + } + /* TARGET/(Wn-Dn) = white gain -> ~1.xxx then it is multiplied by 0x2000 or 0x4000 to give an integer Wn = white average for column n Dn = dark average for column n */ - if (get_registers_gain4_bit(dev->model->asic_type, dev->calib_reg)) { + if (get_registers_gain4_bit(dev->model->asic_type, dev->reg)) { coeff = 0x4000; } else { coeff = 0x2000; } /* compute avg factor */ - if(dev->settings.xres>sensor.optical_res) - { - factor=1; - } - else - { - factor=sensor.optical_res/dev->settings.xres; + if (dev->settings.xres > sensor.full_resolution) { + factor = 1; + } else { + factor = sensor.full_resolution / dev->settings.xres; } /* for GL646, shading data is planar if REG_0x01_FASTMOD is set and @@ -2536,6 +3388,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ switch (dev->model->sensor_id) { case SensorId::CCD_XP300: + case SensorId::CCD_DOCKETPORT_487: case SensorId::CCD_ROADWARRIOR: case SensorId::CCD_DP665: case SensorId::CCD_DP685: @@ -2570,10 +3423,9 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ case SensorId::CCD_HP2300: target_code = 0xdc00; o = 2; - if(dev->settings.xres<=sensor.optical_res/2) - { - o = o - sensor.dummy_pixel / 2; - } + if (dev->settings.xres <= sensor.full_resolution / 2) { + o = o - sensor.dummy_pixel / 2; + } compute_coefficients (dev, shading_data.data(), pixels_per_line, @@ -2586,7 +3438,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ case SensorId::CCD_5345: target_code = 0xe000; o = 4; - if(dev->settings.xres<=sensor.optical_res/2) + if(dev->settings.xres<=sensor.full_resolution/2) { o = o - sensor.dummy_pixel; } @@ -2633,9 +3485,12 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ case SensorId::CCD_CANON_4400F: case SensorId::CCD_CANON_8400F: case SensorId::CCD_CANON_8600F: + case SensorId::CCD_PLUSTEK_OPTICFILM_7200: case SensorId::CCD_PLUSTEK_OPTICFILM_7200I: case SensorId::CCD_PLUSTEK_OPTICFILM_7300: + case SensorId::CCD_PLUSTEK_OPTICFILM_7400: case SensorId::CCD_PLUSTEK_OPTICFILM_7500I: + case SensorId::CCD_PLUSTEK_OPTICFILM_8200I: target_code = 0xe000; o = 0; compute_coefficients (dev, @@ -2654,6 +3509,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ case SensorId::CIS_CANON_LIDE_120: case SensorId::CIS_CANON_LIDE_210: case SensorId::CIS_CANON_LIDE_220: + case SensorId::CCD_CANON_5600F: /* TODO store this in a data struct so we avoid * growing this switch */ switch(dev->model->sensor_id) @@ -2684,6 +3540,8 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ target_code); break; case SensorId::CIS_CANON_LIDE_35: + case SensorId::CIS_CANON_LIDE_60: + case SensorId::CIS_CANON_LIDE_90: compute_averaged_planar (dev, sensor, shading_data.data(), pixels_per_line, @@ -2756,9 +3614,8 @@ genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor) /* we don't restore the gamma fields */ sensor.exposure = cache.sensor.exposure; + dev->calib_session = cache.session; dev->average_size = cache.average_size; - dev->calib_pixels = cache.calib_pixels; - dev->calib_channels = cache.calib_channels; dev->dark_average_data = cache.dark_average_data; dev->white_average_data = cache.white_average_data; @@ -2812,8 +3669,7 @@ static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor& found_cache_it->frontend = dev->frontend; found_cache_it->sensor = sensor; - found_cache_it->calib_pixels = dev->calib_pixels; - found_cache_it->calib_channels = dev->calib_channels; + found_cache_it->session = dev->calib_session; #ifdef HAVE_SYS_TIME_H gettimeofday(&time, nullptr); @@ -2821,20 +3677,13 @@ static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor& #endif } -/** - * does the calibration process for a flatbed scanner - * - offset calibration - * - gain calibration - * - shading calibration - * @param dev device to calibrate - */ static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor) { DBG_HELPER(dbg); - uint32_t pixels_per_line; + uint32_t pixels_per_line; - unsigned coarse_res = sensor.optical_res; - if (dev->settings.yres <= sensor.optical_res / 2) { + unsigned coarse_res = sensor.full_resolution; + if (dev->settings.yres <= sensor.full_resolution / 2) { coarse_res /= 2; } @@ -2848,35 +3697,29 @@ static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sen coarse_res = 1200; } - /* do offset calibration if needed */ - if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) - { + auto local_reg = dev->initial_regs; + + if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { + // do ADC calibration first. dev->interface->record_progress_message("offset_calibration"); - dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg); + dev->cmd_set->offset_calibration(dev, sensor, local_reg); - /* since all the registers are set up correctly, just use them */ 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); - - dev->interface->record_progress_message("genesys_coarse_calibration"); - genesys_coarse_calibration(dev, sensor); + dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); } - if (dev->model->is_cis) + if (dev->model->is_cis && + !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION)) { - /* the afe now sends valid data for doing led calibration */ + // ADC now sends correct data, we can configure the exposure for the LEDs dev->interface->record_progress_message("led_calibration"); switch (dev->model->asic_type) { case AsicType::GL124: + case AsicType::GL841: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: { - auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg); + auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg); for (auto& sensor_update : sanei_genesys_find_sensors_all_for_write(dev, sensor.method)) { sensor_update.get().exposure = calib_exposure; @@ -2885,80 +3728,66 @@ static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sen break; } default: { - sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg); + sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg); } } - - /* calibrate afe again to match new exposure */ - if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) { + if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { + // recalibrate ADC again for the new LED exposure 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 + dev->cmd_set->offset_calibration(dev, sensor, local_reg); 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); - - dev->interface->record_progress_message("genesys_coarse_calibration"); - genesys_coarse_calibration(dev, sensor); + dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); } } /* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */ - if (!(dev->model->flags & GENESYS_FLAG_SIS_SENSOR)) - { + if (!has_flag(dev->model->flags, ModelFlag::SIS_SENSOR)) { 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; + } else { + pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size_calib_mm * dev->settings.xres) + / MM_PER_INCH); } // 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 || - dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) - { - dev->cmd_set->move_to_ta(dev); - } + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + scanner_move_to_ta(*dev); + } // 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); - - 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); - - 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 (!has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { + if (has_flag(dev->model->flags, ModelFlag::DARK_WHITE_CALIBRATION)) { + dev->interface->record_progress_message("genesys_dark_white_shading_calibration"); + genesys_dark_white_shading_calibration(dev, sensor, local_reg); + } else { + DBG(DBG_proc, "%s : genesys_dark_shading_calibration local_reg ", __func__); + debug_dump(DBG_proc, local_reg); - dev->interface->record_progress_message("genesys_dark_shading_calibration"); - genesys_dark_shading_calibration(dev, sensor); - genesys_repark_sensor_before_shading(dev); - } + if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { + dev->interface->record_progress_message("genesys_dark_shading_calibration"); + genesys_dark_shading_calibration(dev, sensor, local_reg); + genesys_repark_sensor_before_shading(dev); + } - dev->interface->record_progress_message("init_regs_for_shading2"); - dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); + dev->interface->record_progress_message("genesys_white_shading_calibration"); + genesys_white_shading_calibration(dev, sensor, local_reg); - dev->interface->record_progress_message("genesys_white_shading_calibration"); - genesys_white_shading_calibration(dev, sensor); - genesys_repark_sensor_after_white_shading(dev); + genesys_repark_sensor_after_white_shading(dev); - if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) { - genesys_dummy_dark_shading(dev, sensor); + if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { + if (has_flag(dev->model->flags, ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION)) { + genesys_dark_shading_by_constant(*dev); + } else { + genesys_dark_shading_by_dummy_pixel(dev, sensor); + } + } } } @@ -2982,68 +3811,62 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se DBG_HELPER(dbg); bool forward = true; + auto local_reg = dev->initial_regs; + // 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 */ - dev->settings.xres = sensor.optical_res; - /* XP200 needs to calibrate a full and half sensor's resolution */ - if (dev->model->sensor_id == SensorId::CIS_XP200 && - dev->settings.xres <= sensor.optical_res / 2) - { - dev->settings.xres /= 2; - } + unsigned coarse_res = sensor.full_resolution; /* the afe needs to sends valid data even before calibration */ /* go to a white area */ try { - dev->cmd_set->search_strip(dev, sensor, forward, false); + scanner_search_strip(*dev, forward, false); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; } - if (dev->model->is_cis) - { - dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg); + if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { + // do ADC calibration first. + dev->interface->record_progress_message("offset_calibration"); + dev->cmd_set->offset_calibration(dev, sensor, local_reg); + + dev->interface->record_progress_message("coarse_gain_calibration"); + dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); } - /* calibrate afe */ - if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) + if (dev->model->is_cis && + !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION)) { - dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg); - - /* since all the registers are set up correctly, just use them */ + // ADC now sends correct data, we can configure the exposure for the LEDs + dev->interface->record_progress_message("led_calibration"); + dev->cmd_set->led_calibration(dev, sensor, local_reg); - 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. */ - { - dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg); + if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { + // recalibrate ADC again for the new LED exposure + dev->interface->record_progress_message("offset_calibration"); + dev->cmd_set->offset_calibration(dev, sensor, local_reg); - genesys_coarse_calibration(dev, sensor); + dev->interface->record_progress_message("coarse_gain_calibration"); + dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); + } } /* search for a full width black strip and then do a 16 bit scan to * gather black shading data */ - if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) - { - /* seek black/white reverse/forward */ + if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { + // seek black/white reverse/forward try { - dev->cmd_set->search_strip(dev, sensor, forward, true); + scanner_search_strip(*dev, forward, true); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; } - dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); - try { - genesys_dark_shading_calibration(dev, sensor); + genesys_dark_shading_calibration(dev, sensor, local_reg); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; @@ -3054,7 +3877,7 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se /* go to a white area */ try { - dev->cmd_set->search_strip(dev, sensor, forward, false); + scanner_search_strip(*dev, forward, false); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; @@ -3062,10 +3885,8 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se genesys_repark_sensor_before_shading(dev); - dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); - try { - genesys_white_shading_calibration(dev, sensor); + genesys_white_shading_calibration(dev, sensor, local_reg); genesys_repark_sensor_after_white_shading(dev); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); @@ -3073,17 +3894,9 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se } // 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, 0x0f0f); - /* XXX STEF XXX - * with black point in white shading, build an average black - * pixel and use it to fill the dark_average - * dev->calib_pixels - (sensor.sensor_pixels * dev->settings.xres) / sensor.optical_res, - dev->calib_lines, - */ + // FIXME: shouldn't we use genesys_dark_shading_by_dummy_pixel() ? + if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { + genesys_dark_shading_by_constant(*dev); } /* send the shading coefficient when doing whole line shading @@ -3099,7 +3912,7 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se dev->cmd_set->eject_document(dev); // restore settings - dev->settings.xres = sensor.optical_res; + dev->settings.xres = sensor.full_resolution; } /** @@ -3129,22 +3942,23 @@ static void genesys_warmup_lamp(Genesys_Device* dev) { DBG_HELPER(dbg); unsigned seconds = 0; - int pixel; - int channels, total_size; - double first_average = 0; - double second_average = 0; - int difference = 255; - int lines = 3; const auto& sensor = sanei_genesys_find_sensor_any(dev); - dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg, &channels, &total_size); + dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg); + dev->interface->write_registers(dev->reg); + + auto total_pixels = dev->session.output_pixels; + auto total_size = dev->session.output_line_bytes; + auto channels = dev->session.params.channels; + auto lines = dev->session.output_line_count; + 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__); + do { + first_line = second_line; + dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false); if (is_testing_mode()) { @@ -3155,72 +3969,44 @@ static void genesys_warmup_lamp(Genesys_Device* dev) wait_until_buffer_non_empty(dev); - try { - sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); - } catch (...) { - // FIXME: document why this retry is here - sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); - } - + sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); dev->cmd_set->end_scan(dev, &dev->reg, true); - dev->interface->sleep_ms(1000); - seconds++; + // compute difference between the two scans + double first_average = 0; + double second_average = 0; + for (unsigned pixel = 0; pixel < total_size; pixel++) { + // 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++; + } else { + first_average += first_line[pixel]; + second_average += second_line[pixel]; + } + } - dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false); + first_average /= total_pixels; + second_average /= total_pixels; - wait_until_buffer_non_empty(dev); + if (dbg_log_image_data()) { + write_tiff_file("gl_warmup1.tiff", first_line.data(), dev->session.params.depth, + channels, total_size / (lines * channels), lines); + write_tiff_file("gl_warmup2.tiff", second_line.data(), dev->session.params.depth, + channels, total_size / (lines * channels), lines); + } - sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); - dev->cmd_set->end_scan(dev, &dev->reg, true); + DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average, + second_average); - /* compute difference between the two scans */ - for (pixel = 0; pixel < total_size; pixel++) - { - // 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++; - } - else - { - first_average += first_line[pixel]; - second_average += second_line[pixel]; - } - } - if (dev->session.params.depth == 16) { - first_average /= pixel; - second_average /= pixel; - 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)); - - if (second_average > (100 * 256) - && (difference / second_average) < 0.002) - break; - } - else - { - first_average /= pixel; - second_average /= pixel; - if (DBG_LEVEL >= DBG_data) - { - sanei_genesys_write_pnm_file("gl_warmup1.pnm", first_line.data(), 8, channels, - total_size / (lines * channels), lines); - sanei_genesys_write_pnm_file("gl_warmup2.pnm", second_line.data(), 8, channels, - total_size / (lines * channels), lines); - } - DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average, - second_average); - /* if delta below 15/255 ~= 5.8%, lamp is considred warm enough */ - if (fabs (first_average - second_average) < 15 - && second_average > 55) - break; - } + float average_difference = std::fabs(first_average - second_average) / second_average; + if (second_average > 0 && average_difference < 0.005) + { + dbg.vlog(DBG_info, "difference: %f, exiting", average_difference); + break; + } - /* sleep another second before next loop */ dev->interface->sleep_ms(1000); seconds++; } while (seconds < WARMUP_TIME); @@ -3236,6 +4022,37 @@ static void genesys_warmup_lamp(Genesys_Device* dev) } } +static void init_regs_for_scan(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) +{ + DBG_HELPER(dbg); + debug_dump(DBG_info, dev.settings); + + auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, dev.settings); + + if (dev.model->asic_type == AsicType::GL124 || + dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846 || + dev.model->asic_type == AsicType::GL847) + { + /* 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 && session.params.starty > 700) { + scanner_move(dev, dev.model->default_method, + static_cast<unsigned>(session.params.starty - 500), + Direction::FORWARD); + session.params.starty = 500; + } + compute_session(&dev, session, sensor); + } + + dev.cmd_set->init_regs_for_scan_session(&dev, sensor, ®s, session); +} // High-level start of scanning static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) @@ -3243,6 +4060,7 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) DBG_HELPER(dbg); unsigned int steps, expected; + /* 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) { @@ -3254,38 +4072,30 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) /* 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)) + if (has_flag(dev->model->flags, ModelFlag::WARMUP) && + (dev->settings.scan_method != ScanMethod::TRANSPARENCY_INFRARED)) { + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + scanner_move_to_ta(*dev); + } + genesys_warmup_lamp(dev); } /* set top left x and y values by scanning the internals if flatbed scanners */ if (!dev->model->is_sheetfed) { - /* do the geometry detection only once */ - if ((dev->model->flags & GENESYS_FLAG_SEARCH_START) - && (dev->model->y_offset_calib_white == 0)) - { - dev->cmd_set->search_start_position (dev); - - 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 = false; - dev->cmd_set->move_back_home(dev, true); - } + // TODO: check we can drop this since we cannot have the scanner's head wandering here + 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->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - dev->cmd_set->move_to_ta(dev); + scanner_move_to_ta(*dev); } /* load document if needed (for sheetfed scanner for instance) */ @@ -3304,22 +4114,18 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) /* 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) { + // calibration : sheetfed scanners can't calibrate before each scan. + // also don't run calibration for those scanners where all passes are disabled + bool shading_disabled = + has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION) && + has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION) && + has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION); + if (!shading_disabled && !dev->model->is_sheetfed) { genesys_scanner_calibration(dev, sensor); - genesys_save_calibration (dev, sensor); - } - else - { + genesys_save_calibration(dev, sensor); + } else { DBG(DBG_warn, "%s: no calibration done\n", __func__); - } - } - - /* build look up table for dynamic lineart */ - 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); + } } dev->cmd_set->wait_for_motor_stop(dev); @@ -3331,10 +4137,10 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - dev->cmd_set->move_to_ta(dev); + scanner_move_to_ta(*dev); } - dev->cmd_set->init_regs_for_scan(dev, sensor); + init_regs_for_scan(*dev, sensor, dev->reg); /* no lamp during scan */ if (lamp_off) { @@ -3344,7 +4150,7 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) /* GL124 is using SHDAREA, so we have to wait for scan to be set up before * sending shading data */ if (dev->cmd_set->has_send_shading_data() && - !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) + !has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { genesys_send_shading_coefficient(dev, sensor); } @@ -3386,33 +4192,6 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) } } -static void genesys_fill_read_buffer(Genesys_Device* dev) -{ - DBG_HELPER(dbg); - - /* for sheetfed scanner, we must check is document is shorter than - * the requested scan */ - if (dev->model->is_sheetfed) { - dev->cmd_set->detect_document_end(dev); - } - - 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. - * There are currently 3 intermediate stages: - * - handling of odd/even sensors - * - handling of line interpolation for motors that can't have low - * enough dpi - * - handling of multi-segments sensors - * - * This is also the place where full duplex data will be handled. - */ - dev->pipeline_buffer.get_data(size, dev->read_buffer.get_write_pos(size)); - - 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. @@ -3422,8 +4201,6 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio { DBG_HELPER(dbg); size_t bytes = 0; - uint8_t *work_buffer_src; - Genesys_Buffer *src_buffer; if (!dev->read_active) { *len = 0; @@ -3439,7 +4216,7 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio { /* issue park command immediatly in case scanner can handle it * so we save time */ - if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) && + if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) && !dev->parking) { dev->cmd_set->move_back_home(dev, false); @@ -3448,61 +4225,22 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio throw SaneException(SANE_STATUS_EOF, "nothing more to scan: EOF"); } -/* convert data */ -/* - 0. fill_read_buffer --------------- read_buffer ---------------------- - 1a). (opt)uncis (assumes color components to be laid out - planar) - 1b). (opt)reverse_RGB (assumes pixels to be BGR or BBGGRR)) --------------- lines_buffer ---------------------- - 2a). (opt)line_distance_correction (assumes RGB or RRGGBB) - 2b). (opt)unstagger (assumes pixels to be depth*channels/8 - bytes long, unshrinked) -------------- shrink_buffer --------------------- - 3. (opt)shrink_lines (assumes component separation in pixels) --------------- out_buffer ----------------------- - 4. memcpy to destination (for lineart with bit reversal) -*/ -/*FIXME: for lineart we need sub byte addressing in buffers, or conversion to - bytes at 0. and back to bits at 4. -Problems with the first approach: - - its not clear how to check if we need to output an incomplete byte - because it is the last one. - */ -/*FIXME: add lineart support for gl646. in the meantime add logic to convert - from gray to lineart at the end? would suffer the above problem, - total_bytes_to_read and total_bytes_read help in that case. - */ - 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); - - src_buffer = &(dev->read_buffer); - - /* move data to destination */ - bytes = std::min(src_buffer->avail(), *len); - - work_buffer_src = src_buffer->get_read_pos(); - - std::memcpy(destination, work_buffer_src, bytes); - *len = bytes; + if (dev->model->is_sheetfed) { + dev->cmd_set->detect_document_end(dev); + } - /* 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->pipeline_buffer.get_data(*len, destination); dev->total_bytes_read += *len; - - src_buffer->consume(bytes); } /* end scan if all needed data have been read */ @@ -3576,181 +4314,113 @@ static unsigned pick_resolution(const std::vector<unsigned>& resolutions, unsign return best_res; } -static void calc_parameters(Genesys_Scanner* s) +static Genesys_Settings calculate_scan_settings(Genesys_Scanner* s) { 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); - tl_y = SANE_UNFIX(s->pos_top_left_y); - br_x = SANE_UNFIX(s->pos_bottom_right_x); - br_y = SANE_UNFIX(s->pos_bottom_right_y); + const auto* dev = s->dev; + Genesys_Settings settings; + settings.scan_method = s->scan_method; + settings.scan_mode = option_string_to_scan_color_mode(s->mode); - s->params.last_frame = true; /* only single pass scanning supported */ + settings.depth = s->bit_depth; - if (s->mode == SANE_VALUE_SCAN_MODE_GRAY || s->mode == SANE_VALUE_SCAN_MODE_LINEART) { - s->params.format = SANE_FRAME_GRAY; - } else { - s->params.format = SANE_FRAME_RGB; + if (settings.depth > 8) { + settings.depth = 16; + } else if (settings.depth < 8) { + settings.depth = 1; } - if (s->mode == SANE_VALUE_SCAN_MODE_LINEART) { - s->params.depth = 1; - } else { - 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; + const auto& resolutions = dev->model->get_resolution_settings(settings.scan_method); - /* interpolation */ - s->dev->settings.disable_interpolation = s->disable_interpolation; + settings.xres = pick_resolution(resolutions.resolutions_x, s->resolution, "X"); + settings.yres = pick_resolution(resolutions.resolutions_y, s->resolution, "Y"); - // FIXME: use correct sensor - const auto& sensor = sanei_genesys_find_sensor_any(s->dev); - - // hardware settings - 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; + settings.tl_x = fixed_to_float(s->pos_top_left_x); + settings.tl_y = fixed_to_float(s->pos_top_left_y); + float br_x = fixed_to_float(s->pos_bottom_right_x); + float br_y = fixed_to_float(s->pos_bottom_right_y); - 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) / + settings.lines = static_cast<unsigned>(((br_y - settings.tl_y) * 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 == 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 == 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; - } - } - - unsigned xres_factor = s->resolution / s->dev->settings.xres; - unsigned bytes_per_line = 0; - - if (s->params.depth > 8) - { - s->params.depth = 16; - bytes_per_line = 2 * pixels_per_line; - } - else if (s->params.depth == 1) - { - // 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) { - bytes_per_line *= 3; - } + unsigned pixels_per_line = static_cast<unsigned>(((br_x - settings.tl_x) * settings.xres) / + MM_PER_INCH); - s->dev->settings.scan_mode = option_string_to_scan_color_mode(s->mode); + const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, settings.get_channels(), + settings.scan_method); - s->dev->settings.lines = s->params.lines; - 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; + pixels_per_line = session_adjust_output_pixels(pixels_per_line, *dev, sensor, + settings.xres, settings.yres, true); - // threshold setting - s->dev->settings.threshold = static_cast<int>(2.55 * (SANE_UNFIX(s->threshold))); + unsigned xres_factor = s->resolution / settings.xres; + settings.pixels = pixels_per_line; + settings.requested_pixels = pixels_per_line * xres_factor; - // color filter if (s->color_filter == "Red") { - s->dev->settings.color_filter = ColorFilter::RED; + settings.color_filter = ColorFilter::RED; } else if (s->color_filter == "Green") { - s->dev->settings.color_filter = ColorFilter::GREEN; + settings.color_filter = ColorFilter::GREEN; } else if (s->color_filter == "Blue") { - s->dev->settings.color_filter = ColorFilter::BLUE; + settings.color_filter = ColorFilter::BLUE; } else { - s->dev->settings.color_filter = ColorFilter::NONE; + settings.color_filter = ColorFilter::NONE; } - // true gray if (s->color_filter == "None") { - s->dev->settings.true_gray = 1; + settings.true_gray = 1; } else { - s->dev->settings.true_gray = 0; + settings.true_gray = 0; } - // threshold curve for dynamic rasterization - s->dev->settings.threshold_curve = s->threshold_curve; - - /* some digital processing requires the whole picture to be buffered */ - /* no digital processing takes place when doing preview, or when bit depth is - * higher than 8 bits */ - if ((s->swdespeck || s->swcrop || s->swdeskew || s->swderotate ||(SANE_UNFIX(s->swskip)>0)) - && (!s->preview) - && (s->bit_depth <= 8)) - { - s->dev->buffer_image = true; - } - else - { - s->dev->buffer_image = false; + // brigthness and contrast only for for 8 bit scans + if (s->bit_depth == 8) { + settings.contrast = (s->contrast * 127) / 100; + settings.brightness = (s->brightness * 127) / 100; + } else { + settings.contrast = 0; + settings.brightness = 0; } - /* brigthness and contrast only for for 8 bit scans */ - if(s->bit_depth <= 8) - { - s->dev->settings.contrast = (s->contrast * 127) / 100; - s->dev->settings.brightness = (s->brightness * 127) / 100; - } - else - { - s->dev->settings.contrast=0; - s->dev->settings.brightness=0; + settings.expiration_time = s->expiration_time; + + return settings; +} + +static SANE_Parameters calculate_scan_parameters(const Genesys_Device& dev, + const Genesys_Settings& settings) +{ + DBG_HELPER(dbg); + + auto sensor = sanei_genesys_find_sensor(&dev, settings.xres, settings.get_channels(), + settings.scan_method); + auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, settings); + auto pipeline = build_image_pipeline(dev, session, 0, false); + + SANE_Parameters params; + if (settings.scan_mode == ScanColorMode::GRAY) { + params.format = SANE_FRAME_GRAY; + } else { + params.format = SANE_FRAME_RGB; } + // only single-pass scanning supported + params.last_frame = true; + params.depth = settings.depth; + params.lines = pipeline.get_output_height(); + params.pixels_per_line = pipeline.get_output_width(); + params.bytes_per_line = pipeline.get_output_row_bytes(); - /* cache expiration time */ - s->dev->settings.expiration_time = s->expiration_time; + return params; } +static void calc_parameters(Genesys_Scanner* s) +{ + DBG_HELPER(dbg); + + s->dev->settings = calculate_scan_settings(s); + s->params = calculate_scan_parameters(*s->dev, s->dev->settings); +} static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& bpp) { @@ -3760,7 +4430,7 @@ static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& b /** @brief this function initialize a gamma vector based on the ASIC: * Set up a default gamma table vector based on device description - * gl646: 12 or 14 bits gamma table depending on GENESYS_FLAG_14BIT_GAMMA + * gl646: 12 or 14 bits gamma table depending on ModelFlag::GAMMA_14BIT * gl84x: 16 bits * gl12x: 16 bits * @param scanner pointer to scanner session to get options @@ -3776,8 +4446,7 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option) scanner->opt[option].unit = SANE_UNIT_NONE; scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE; if (scanner->dev->model->asic_type == AsicType::GL646) { - if ((scanner->dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) != 0) - { + if (has_flag(scanner->dev->model->flags, ModelFlag::GAMMA_14BIT)) { scanner->opt[option].size = 16384 * sizeof (SANE_Word); scanner->opt[option].constraint.range = &u14_range; } @@ -3802,9 +4471,9 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option) static SANE_Range create_range(float size) { SANE_Range range; - range.min = SANE_FIX(0.0); - range.max = SANE_FIX(size); - range.quant = SANE_FIX(0.0); + range.min = float_to_fixed(0.0); + range.max = float_to_fixed(size); + range.quant = float_to_fixed(0.0); return range; } @@ -3855,7 +4524,7 @@ static std::string calibration_filename(Genesys_Device *currdev) /* count models of the same names if several scanners attached */ if(s_devices->size() > 1) { for (const auto& dev : *s_devices) { - if (dev.model->model_id == currdev->model->model_id) { + if (dev.vendorId == currdev->vendorId && dev.productId == currdev->productId) { count++; } } @@ -3921,13 +4590,13 @@ static void set_xy_range_option_values(Genesys_Scanner& s) { 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)); + s.opt_x_range = create_range(s.dev->model->x_size); + s.opt_y_range = create_range(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_x_range = create_range(s.dev->model->x_size_ta); + s.opt_y_range = create_range(s.dev->model->y_size_ta); } s.opt[OPT_TL_X].constraint.range = &s.opt_x_range; @@ -3945,7 +4614,7 @@ static void init_options(Genesys_Scanner* s) { DBG_HELPER(dbg); SANE_Int option; - Genesys_Model *model = s->dev->model; + const Genesys_Model* model = s->dev->model; memset (s->opt, 0, sizeof (s->opt)); @@ -4038,8 +4707,8 @@ static void init_options(Genesys_Scanner* s) s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - s->opt_x_range = create_range(static_cast<float>(model->x_size)); - s->opt_y_range = create_range(static_cast<float>(model->y_size)); + s->opt_x_range = create_range(model->x_size); + s->opt_y_range = create_range(model->y_size); // scan area s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; @@ -4116,8 +4785,7 @@ static void init_options(Genesys_Scanner* s) /* currently, there are only gamma table options in this group, * so if the scanner doesn't support gamma table, disable the * whole group */ - if (!(model->flags & GENESYS_FLAG_CUSTOM_GAMMA)) - { + if (!has_flag(model->flags, ModelFlag::CUSTOM_GAMMA)) { s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; DBG(DBG_info, "%s: custom gamma disabled\n", __func__); @@ -4127,61 +4795,6 @@ static void init_options(Genesys_Scanner* s) * memory than used by the full scanned image and may fail at high * resolution */ - /* software deskew */ - s->opt[OPT_SWDESKEW].name = "swdeskew"; - s->opt[OPT_SWDESKEW].title = "Software deskew"; - s->opt[OPT_SWDESKEW].desc = "Request backend to rotate skewed pages digitally"; - s->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL; - s->opt[OPT_SWDESKEW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - s->swdeskew = false; - - /* software deskew */ - s->opt[OPT_SWDESPECK].name = "swdespeck"; - s->opt[OPT_SWDESPECK].title = "Software despeck"; - s->opt[OPT_SWDESPECK].desc = "Request backend to remove lone dots digitally"; - s->opt[OPT_SWDESPECK].type = SANE_TYPE_BOOL; - s->opt[OPT_SWDESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - s->swdespeck = false; - - /* software despeckle radius */ - s->opt[OPT_DESPECK].name = "despeck"; - s->opt[OPT_DESPECK].title = "Software despeckle diameter"; - s->opt[OPT_DESPECK].desc = "Maximum diameter of lone dots to remove from scan"; - s->opt[OPT_DESPECK].type = SANE_TYPE_INT; - s->opt[OPT_DESPECK].unit = SANE_UNIT_NONE; - s->opt[OPT_DESPECK].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_DESPECK].constraint.range = &swdespeck_range; - s->opt[OPT_DESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED | SANE_CAP_INACTIVE; - s->despeck = 1; - - /* crop by software */ - s->opt[OPT_SWCROP].name = "swcrop"; - s->opt[OPT_SWCROP].title = SANE_I18N ("Software crop"); - s->opt[OPT_SWCROP].desc = SANE_I18N ("Request backend to remove border from pages digitally"); - s->opt[OPT_SWCROP].type = SANE_TYPE_BOOL; - s->opt[OPT_SWCROP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - s->opt[OPT_SWCROP].unit = SANE_UNIT_NONE; - s->swcrop = false; - - /* Software blank page skip */ - s->opt[OPT_SWSKIP].name = "swskip"; - s->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage"); - s->opt[OPT_SWSKIP].desc = SANE_I18N("Request driver to discard pages with low numbers of dark pixels"); - s->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED; - s->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT; - s->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_SWSKIP].constraint.range = &(percentage_range); - s->opt[OPT_SWSKIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - s->swskip = 0; // disable by default - - /* Software Derotate */ - s->opt[OPT_SWDEROTATE].name = "swderotate"; - s->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate"); - s->opt[OPT_SWDEROTATE].desc = SANE_I18N("Request driver to detect and correct 90 degree image rotation"); - s->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL; - s->opt[OPT_SWDEROTATE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - s->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE; - s->swderotate = false; /* Software brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; @@ -4214,39 +4827,6 @@ static void init_options(Genesys_Scanner* s) s->opt[OPT_EXTRAS_GROUP].size = 0; s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - /* BW threshold */ - s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; - s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; - s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; - s->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED; - s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT; - s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_THRESHOLD].constraint.range = &percentage_range; - s->threshold = SANE_FIX(50); - - /* BW threshold curve */ - s->opt[OPT_THRESHOLD_CURVE].name = "threshold-curve"; - s->opt[OPT_THRESHOLD_CURVE].title = SANE_I18N ("Threshold curve"); - s->opt[OPT_THRESHOLD_CURVE].desc = SANE_I18N ("Dynamic threshold curve, from light to dark, normally 50-65"); - s->opt[OPT_THRESHOLD_CURVE].type = SANE_TYPE_INT; - s->opt[OPT_THRESHOLD_CURVE].unit = SANE_UNIT_NONE; - s->opt[OPT_THRESHOLD_CURVE].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range; - s->threshold_curve = 50; - - /* disable_interpolation */ - s->opt[OPT_DISABLE_INTERPOLATION].name = "disable-interpolation"; - s->opt[OPT_DISABLE_INTERPOLATION].title = - SANE_I18N ("Disable interpolation"); - s->opt[OPT_DISABLE_INTERPOLATION].desc = - SANE_I18N - ("When using high resolutions where the horizontal resolution is smaller " - "than the vertical resolution this disables horizontal interpolation."); - s->opt[OPT_DISABLE_INTERPOLATION].type = SANE_TYPE_BOOL; - s->opt[OPT_DISABLE_INTERPOLATION].unit = SANE_UNIT_NONE; - s->opt[OPT_DISABLE_INTERPOLATION].constraint_type = SANE_CONSTRAINT_NONE; - s->disable_interpolation = false; - /* color filter */ s->opt[OPT_COLOR_FILTER].name = "color-filter"; s->opt[OPT_COLOR_FILTER].title = SANE_I18N ("Color filter"); @@ -4508,36 +5088,39 @@ check_present (SANE_String_Const devname) noexcept return SANE_STATUS_GOOD; } -static Genesys_Device* attach_usb_device(const char* devname, - std::uint16_t vendor_id, std::uint16_t product_id) +const UsbDeviceEntry& get_matching_usb_dev(std::uint16_t vendor_id, std::uint16_t product_id, + std::uint16_t bcd_device) { - 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 (usb_dev.matches(vendor_id, product_id, bcd_device)) { + return usb_dev; } } - if (found_usb_dev == nullptr) { - throw SaneException("vendor 0x%xd product 0x%xd is not supported by this backend", - vendor_id, product_id); - } + throw SaneException("vendor 0x%x product 0x%x (bcdDevice 0x%x) " + "is not supported by this backend", + vendor_id, product_id, bcd_device); +} + +static Genesys_Device* attach_usb_device(const char* devname, + std::uint16_t vendor_id, std::uint16_t product_id, + std::uint16_t bcd_device) +{ + const auto& usb_dev = get_matching_usb_dev(vendor_id, product_id, bcd_device); 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->vendorId = vendor_id; + dev->productId = product_id; + dev->model = &usb_dev.model(); dev->usb_mode = 0; // i.e. unset dev->already_initialized = false; return dev; } +static bool s_attach_device_by_name_evaluate_bcd_device = false; + 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); @@ -4560,26 +5143,31 @@ static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may usb_dev.open(devname); DBG(DBG_info, "%s: device `%s' successfully opened\n", __func__, devname); - int vendor, product; - usb_dev.get_vendor_product(vendor, product); + auto vendor_id = usb_dev.get_vendor_id(); + auto product_id = usb_dev.get_product_id(); + auto bcd_device = UsbDeviceEntry::BCD_DEVICE_NOT_SET; + if (s_attach_device_by_name_evaluate_bcd_device) { + // when the device is already known before scanning, we don't want to call get_bcd_device() + // when iterating devices, as that will interfere with record/replay during testing. + bcd_device = usb_dev.get_bcd_device(); + } usb_dev.close(); /* KV-SS080 is an auxiliary device which requires a master device to be here */ - if(vendor == 0x04da && product == 0x100f) - { + if (vendor_id == 0x04da && product_id == 0x100f) { 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); + sanei_usb_find_devices(vendor_id, 0x1006, check_present); + sanei_usb_find_devices(vendor_id, 0x1007, check_present); + sanei_usb_find_devices(vendor_id, 0x1010, check_present); if (present == false) { throw SaneException("master device not present"); } } - Genesys_Device* dev = attach_usb_device(devname, vendor, product); + Genesys_Device* dev = attach_usb_device(devname, vendor_id, product_id, bcd_device); - DBG(DBG_info, "%s: found %s flatbed scanner %s at %s\n", __func__, dev->model->vendor, - dev->model->model, dev->file_name.c_str()); + DBG(DBG_info, "%s: found %u flatbed scanner %u at %s\n", __func__, vendor_id, product_id, + dev->file_name.c_str()); return dev; } @@ -4614,7 +5202,8 @@ static void probe_genesys_devices() DBG_HELPER(dbg); if (is_testing_mode()) { attach_usb_device(get_testing_device_name().c_str(), - get_testing_vendor_id(), get_testing_product_id()); + get_testing_vendor_id(), get_testing_product_id(), + get_testing_bcd_device()); return; } @@ -4625,7 +5214,12 @@ static void probe_genesys_devices() config.values = nullptr; config.count = 0; - TIE(sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys)); + auto status = sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys); + if (status == SANE_STATUS_ACCESS_DENIED) { + dbg.vlog(DBG_error0, "Critical error: Couldn't access configuration file '%s'", + GENESYS_CONFIG_FILE); + } + TIE(status); DBG(DBG_info, "%s: %zu devices currently attached\n", __func__, s_devices->size()); } @@ -4637,7 +5231,7 @@ static void probe_genesys_devices() of Genesys_Calibration_Cache as is. */ static const char* CALIBRATION_IDENT = "sane_genesys"; -static const int CALIBRATION_VERSION = 21; +static const int CALIBRATION_VERSION = 31; bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration, const std::string& path) @@ -4706,114 +5300,6 @@ static void write_calibration(Genesys_Device::Calibration& calibration, const st write_calibration(str, calibration); } -/** @brief buffer scanned picture - * In order to allow digital processing, we must be able to put all the - * scanned picture in a buffer. - */ -static void genesys_buffer_image(Genesys_Scanner *s) -{ - 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 */ - size_t size; /**> size of image buffer */ - size_t read_size; /**> size of reads */ - int lines; /** number of lines of the scan */ - Genesys_Device *dev = s->dev; - - /* compute maximum number of lines for the scan */ - if (s->params.lines > 0) - { - lines = s->params.lines; - } - else - { - 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.scan_mode == ScanColorMode::LINEART) { - maximum *= 8; - } - - /* initial size of the read buffer */ - size = - ((2048 * 2048) / s->params.bytes_per_line) * s->params.bytes_per_line; - - /* read size */ - read_size = size / 2; - - dev->img_buffer.resize(size); - - /* loop reading data until we reach maximum or EOF */ - total = 0; - while (total < maximum) { - len = size - maximum; - if (len > read_size) - { - len = read_size; - } - - 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) { - 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 && !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.scan_mode == ScanColorMode::LINEART) { - total/=8; - std::vector<uint8_t> lineart(total); - - genesys_gray_lineart (dev, - dev->img_buffer.data(), - lineart.data(), - dev->settings.pixels, - (total*8)/dev->settings.pixels, - dev->settings.threshold); - dev->img_buffer = lineart; - } - - /* update counters */ - dev->total_bytes_to_read = total; - dev->total_bytes_read = 0; - - /* update params */ - s->params.lines = total / s->params.bytes_per_line; - if (DBG_LEVEL >= DBG_io2) - { - sanei_genesys_write_pnm_file("gl_unprocessed.pnm", dev->img_buffer.data(), s->params.depth, - s->params.format==SANE_FRAME_RGB ? 3 : 1, - s->params.pixels_per_line, s->params.lines); - } -} - /* -------------------------- SANE API functions ------------------------- */ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize) @@ -4839,9 +5325,6 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize) sanei_usb_init(); } - /* init sanei_magic */ - sanei_magic_init(); - s_scanners.init(); s_devices.init(); s_sane_devices.init(); @@ -4850,8 +5333,8 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize) genesys_init_sensor_tables(); genesys_init_frontend_tables(); genesys_init_gpo_tables(); + genesys_init_memory_layout_tables(); genesys_init_motor_tables(); - genesys_init_motor_profile_tables(); genesys_init_usb_device_tables(); @@ -4864,6 +5347,7 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize) ); // cold-plug case :detection of allready connected scanners + s_attach_device_by_name_evaluate_bcd_device = false; probe_genesys_devices(); } @@ -4903,6 +5387,7 @@ void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_on // hot-plug case : detection of newly connected scanners */ sanei_usb_scan_devices(); } + s_attach_device_by_name_evaluate_bcd_device = true; probe_genesys_devices(); s_sane_devices->clear(); @@ -4969,7 +5454,7 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle) } if (dev) { - DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->model->name); + DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->file_name.c_str()); } else if (is_testing_mode()) { DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename); } else { @@ -4991,37 +5476,53 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle) throw SaneException("could not find the device to open: %s", devicename); } - if (dev->model->flags & GENESYS_FLAG_UNTESTED) - { - DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n"); - DBG(DBG_error0, " had only limited testing. Please be careful and \n"); - DBG(DBG_error0, " report any failure/success to \n"); - DBG(DBG_error0, " sane-devel@alioth-lists.debian.net. Please provide as many\n"); - DBG(DBG_error0, " details as possible, e.g. the exact name of your\n"); - DBG(DBG_error0, " scanner and what does (not) work.\n"); - } + if (is_testing_mode()) { + // during testing we need to initialize dev->model before test scanner interface is created + // as that it needs to know what type of chip it needs to mimic. + auto vendor_id = get_testing_vendor_id(); + auto product_id = get_testing_product_id(); + auto bcd_device = get_testing_bcd_device(); - dbg.vstatus("open device '%s'", dev->file_name.c_str()); + dev->model = &get_matching_usb_dev(vendor_id, product_id, bcd_device).model(); - if (is_testing_mode()) { - auto interface = std::unique_ptr<TestScannerInterface>{new TestScannerInterface{dev}}; + auto interface = std::unique_ptr<TestScannerInterface>{ + new TestScannerInterface{dev, vendor_id, product_id, bcd_device}}; interface->set_checkpoint_callback(get_testing_checkpoint_callback()); dev->interface = std::move(interface); + + dev->interface->get_usb_device().open(dev->file_name.c_str()); } else { dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}}; + + dbg.vstatus("open device '%s'", dev->file_name.c_str()); + dev->interface->get_usb_device().open(dev->file_name.c_str()); + dbg.clear(); + + auto bcd_device = dev->interface->get_usb_device().get_bcd_device(); + + dev->model = &get_matching_usb_dev(dev->vendorId, dev->productId, bcd_device).model(); + } + + dbg.vlog(DBG_info, "Opened device %s", dev->model->name); + + if (has_flag(dev->model->flags, ModelFlag::UNTESTED)) { + DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n"); + DBG(DBG_error0, " had only limited testing. Please be careful and \n"); + DBG(DBG_error0, " report any failure/success to \n"); + DBG(DBG_error0, " sane-devel@alioth-lists.debian.net. Please provide as many\n"); + DBG(DBG_error0, " details as possible, e.g. the exact name of your\n"); + DBG(DBG_error0, " scanner and what does (not) work.\n"); } - 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->dev = dev; s->scanning = false; - s->dev->parking = false; - s->dev->read_active = false; - s->dev->force_calibration = 0; - s->dev->line_count = 0; + dev->parking = false; + dev->read_active = false; + dev->force_calibration = 0; + dev->line_count = 0; *handle = s; @@ -5029,31 +5530,18 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle) sanei_genesys_init_structs (dev); } + dev->cmd_set = create_cmd_set(dev->model->asic_type); + init_options(s); - sanei_genesys_init_cmd_set(s->dev); + DBG_INIT(); // 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 - s->dev->cmd_set->update_hardware_sensors (s); - - /* here is the place to fetch a stored calibration cache */ - if (s->dev->force_calibration == 0) - { - 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()); - - catch_all_exceptions(__func__, [&]() - { - sanei_genesys_read_calibration(s->dev->calibration_cache, s->dev->calib_file); - }); - } + dev->cmd_set->update_hardware_sensors (s); } SANE_GENESYS_API_LINKAGE @@ -5085,46 +5573,42 @@ sane_close_impl(SANE_Handle handle) return; /* oops, not a handle we know about */ } - Genesys_Scanner* s = &*it; + auto* dev = it->dev; - /* eject document for sheetfed scanners */ - 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) { - sanei_genesys_wait_for_home(s->dev); + // eject document for sheetfed scanners + if (dev->model->is_sheetfed) { + catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); + } else { + // in case scanner is parking, wait for the head to reach home position + if (dev->parking) { + sanei_genesys_wait_for_home(dev); } } // enable power saving before leaving - s->dev->cmd_set->save_power(s->dev, true); + dev->cmd_set->save_power(dev, true); // here is the place to store calibration cache - if (s->dev->force_calibration == 0 && !is_testing_mode()) { - catch_all_exceptions(__func__, [&](){ write_calibration(s->dev->calibration_cache, - s->dev->calib_file); }); + if (dev->force_calibration == 0 && !is_testing_mode()) { + catch_all_exceptions(__func__, [&](){ write_calibration(dev->calibration_cache, + dev->calib_file); }); } - s->dev->already_initialized = false; - - s->dev->clear(); + dev->already_initialized = false; + dev->clear(); // LAMP OFF : same register across all the ASICs */ - s->dev->interface->write_register(0x03, 0x00); + dev->interface->write_register(0x03, 0x00); - catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().clear_halt(); }); + catch_all_exceptions(__func__, [&](){ 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->interface->get_usb_device().reset(); }); + catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().reset(); }); - // not freeing s->dev because it's in the dev list - catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().close(); }); + // not freeing dev because it's in the dev list + catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().close(); }); - s_scanners->erase(it); + s_scanners->erase(it); } SANE_GENESYS_API_LINKAGE @@ -5174,7 +5658,7 @@ static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int return; } case SANE_TYPE_FIXED: { - dbg.vlog(DBG_proc, "value: %f", SANE_UNFIX(*reinterpret_cast<SANE_Word*>(val))); + dbg.vlog(DBG_proc, "value: %f", fixed_to_float(*reinterpret_cast<SANE_Word*>(val))); return; } case SANE_TYPE_STRING: { @@ -5189,18 +5673,19 @@ static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int static void get_option_value(Genesys_Scanner* s, int option, void* val) { DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option); + auto* dev = s->dev; unsigned int i; SANE_Word* table = nullptr; std::vector<uint16_t> gamma_table; unsigned option_size = 0; 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)) + if (sanei_genesys_has_sensor(dev, dev->settings.xres, dev->settings.get_channels(), + 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); + sensor = &sanei_genesys_find_sensor(dev, dev->settings.xres, + dev->settings.get_channels(), + dev->settings.scan_method); } switch (option) @@ -5231,39 +5716,12 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val) case OPT_PREVIEW: *reinterpret_cast<SANE_Word*>(val) = s->preview; break; - case OPT_THRESHOLD: - *reinterpret_cast<SANE_Word*>(val) = s->threshold; - break; - case OPT_THRESHOLD_CURVE: - *reinterpret_cast<SANE_Word*>(val) = s->threshold_curve; - break; - case OPT_DISABLE_INTERPOLATION: - *reinterpret_cast<SANE_Word*>(val) = s->disable_interpolation; - break; case OPT_LAMP_OFF: *reinterpret_cast<SANE_Word*>(val) = s->lamp_off; break; case OPT_LAMP_OFF_TIME: *reinterpret_cast<SANE_Word*>(val) = s->lamp_off_time; break; - case OPT_SWDESKEW: - *reinterpret_cast<SANE_Word*>(val) = s->swdeskew; - break; - case OPT_SWCROP: - *reinterpret_cast<SANE_Word*>(val) = s->swcrop; - break; - case OPT_SWDESPECK: - *reinterpret_cast<SANE_Word*>(val) = s->swdespeck; - break; - case OPT_SWDEROTATE: - *reinterpret_cast<SANE_Word*>(val) = s->swderotate; - break; - case OPT_SWSKIP: - *reinterpret_cast<SANE_Word*>(val) = s->swskip; - break; - case OPT_DESPECK: - *reinterpret_cast<SANE_Word*>(val) = s->despeck; - break; case OPT_CONTRAST: *reinterpret_cast<SANE_Word*>(val) = s->contrast; break; @@ -5297,13 +5755,13 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val) 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); - } else if (s->color_filter == "Blue") { - gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE); - } else { - gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN); - } + if (s->color_filter == "Red") { + gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED); + } else if (s->color_filter == "Blue") { + gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE); + } else { + gamma_table = get_gamma_table(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"); @@ -5317,7 +5775,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val) throw SaneException("Unsupported scanner mode selected"); table = reinterpret_cast<SANE_Word*>(val); - gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_RED); + gamma_table = get_gamma_table(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"); @@ -5331,7 +5789,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val) throw SaneException("Unsupported scanner mode selected"); table = reinterpret_cast<SANE_Word*>(val); - gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN); + gamma_table = get_gamma_table(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"); @@ -5345,7 +5803,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val) throw SaneException("Unsupported scanner mode selected"); table = reinterpret_cast<SANE_Word*>(val); - gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE); + gamma_table = get_gamma_table(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"); @@ -5377,11 +5835,10 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val) bool result = true; - auto session = s->dev->cmd_set->calculate_scan_session(s->dev, *sensor, - s->dev->settings); + auto session = dev->cmd_set->calculate_scan_session(dev, *sensor, dev->settings); - for (auto& cache : s->dev->calibration_cache) { - if (sanei_genesys_is_compatible_calibration(s->dev, session, &cache, false)) { + for (auto& cache : dev->calibration_cache) { + if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) { *reinterpret_cast<SANE_Bool*>(val) = SANE_FALSE; } } @@ -5400,6 +5857,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val) static void set_calibration_value(Genesys_Scanner* s, const char* val) { DBG_HELPER(dbg); + auto dev = s->dev; std::string new_calib_path = val; Genesys_Device::Calibration new_calibration; @@ -5414,8 +5872,8 @@ static void set_calibration_value(Genesys_Scanner* s, const char* val) return; } - s->dev->calibration_cache = std::move(new_calibration); - s->dev->calib_file = new_calib_path; + dev->calibration_cache = std::move(new_calibration); + dev->calib_file = new_calib_path; s->calibration_file = new_calib_path; DBG(DBG_info, "%s: Calibration filename set to '%s':\n", __func__, new_calib_path.c_str()); } @@ -5426,12 +5884,13 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option); print_option(dbg, *s, option, val); + auto* dev = s->dev; + SANE_Word *table; unsigned int i; unsigned option_size = 0; - switch (option) - { + switch (option) { case OPT_TL_X: s->pos_top_left_x = *reinterpret_cast<SANE_Word*>(val); calc_parameters(s); @@ -5457,46 +5916,6 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; - case OPT_THRESHOLD: - s->threshold = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_THRESHOLD_CURVE: - s->threshold_curve = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_SWCROP: - s->swcrop = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_SWDESKEW: - s->swdeskew = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_DESPECK: - s->despeck = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_SWDEROTATE: - s->swderotate = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_SWSKIP: - s->swskip = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_DISABLE_INTERPOLATION: - s->disable_interpolation = *reinterpret_cast<SANE_Word*>(val); - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; case OPT_LAMP_OFF: s->lamp_off = *reinterpret_cast<SANE_Word*>(val); calc_parameters(s); @@ -5517,38 +5936,14 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; - case OPT_SWDESPECK: - s->swdespeck = *reinterpret_cast<SANE_Word*>(val); - if (s->swdespeck) { - ENABLE(OPT_DESPECK); - } else { - DISABLE(OPT_DESPECK); - } - calc_parameters(s); - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - break; /* software enhancement functions only apply to 8 or 1 bits data */ case OPT_BIT_DEPTH: s->bit_depth = *reinterpret_cast<SANE_Word*>(val); if(s->bit_depth>8) { - DISABLE(OPT_SWDESKEW); - DISABLE(OPT_SWDESPECK); - DISABLE(OPT_SWCROP); - DISABLE(OPT_DESPECK); - DISABLE(OPT_SWDEROTATE); - DISABLE(OPT_SWSKIP); DISABLE(OPT_CONTRAST); DISABLE(OPT_BRIGHTNESS); - } - else - { - ENABLE(OPT_SWDESKEW); - ENABLE(OPT_SWDESPECK); - ENABLE(OPT_SWCROP); - ENABLE(OPT_DESPECK); - ENABLE(OPT_SWDEROTATE); - ENABLE(OPT_SWSKIP); + } else { ENABLE(OPT_CONTRAST); ENABLE(OPT_BRIGHTNESS); } @@ -5567,38 +5962,22 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int } break; } - case OPT_MODE: - s->mode = reinterpret_cast<const char*>(val); + case OPT_MODE: { + s->mode = reinterpret_cast<const char*>(val); - if (s->mode == SANE_VALUE_SCAN_MODE_LINEART) - { - ENABLE (OPT_THRESHOLD); - ENABLE (OPT_THRESHOLD_CURVE); - DISABLE (OPT_BIT_DEPTH); - if (s->dev->model->asic_type != AsicType::GL646 || !s->dev->model->is_cis) { + if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) { + if (dev->model->asic_type != AsicType::GL646 || !dev->model->is_cis) { ENABLE(OPT_COLOR_FILTER); } - } - else - { - DISABLE (OPT_THRESHOLD); - DISABLE (OPT_THRESHOLD_CURVE); - if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) - { - 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]; - } - } - calc_parameters(s); + create_bpp_list(s, dev->model->bpp_gray_values); + s->bit_depth = dev->model->bpp_gray_values[0]; + } else { + DISABLE(OPT_COLOR_FILTER); + create_bpp_list(s, dev->model->bpp_color_values); + s->bit_depth = dev->model->bpp_color_values[0]; + } + + calc_parameters(s); /* if custom gamma, toggle gamma table options according to the mode */ if (s->custom_gamma) @@ -5621,30 +6000,31 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; + } case OPT_COLOR_FILTER: s->color_filter = reinterpret_cast<const char*>(val); calc_parameters(s); break; - case OPT_CALIBRATION_FILE: - if (s->dev->force_calibration == 0) { + case OPT_CALIBRATION_FILE: { + if (dev->force_calibration == 0) { set_calibration_value(s, reinterpret_cast<const char*>(val)); } break; - case OPT_LAMP_OFF_TIME: - if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) { - s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val); - 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 - s->dev->cmd_set->set_powersaving(s->dev, s->expiration_time); - } - break; - - case OPT_CUSTOM_GAMMA: + case OPT_LAMP_OFF_TIME: { + if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) { + s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val); + dev->cmd_set->set_powersaving(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); + } + break; + } + case OPT_CUSTOM_GAMMA: { *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; s->custom_gamma = *reinterpret_cast<SANE_Bool*>(val); @@ -5670,88 +6050,96 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int DISABLE (OPT_GAMMA_VECTOR_R); DISABLE (OPT_GAMMA_VECTOR_G); DISABLE (OPT_GAMMA_VECTOR_B); - for (auto& table : s->dev->gamma_override_tables) { - table.clear(); + for (auto& table : dev->gamma_override_tables) { + table.clear(); + } } - } - break; - - case OPT_GAMMA_VECTOR: - table = reinterpret_cast<SANE_Word*>(val); - option_size = s->opt[option].size / sizeof (SANE_Word); + break; + } - s->dev->gamma_override_tables[GENESYS_RED].resize(option_size); - s->dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); - s->dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); - for (i = 0; i < option_size; i++) { - s->dev->gamma_override_tables[GENESYS_RED][i] = table[i]; - s->dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; - s->dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; + case OPT_GAMMA_VECTOR: { + table = reinterpret_cast<SANE_Word*>(val); + option_size = s->opt[option].size / sizeof (SANE_Word); + + dev->gamma_override_tables[GENESYS_RED].resize(option_size); + dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); + dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); + for (i = 0; i < option_size; i++) { + dev->gamma_override_tables[GENESYS_RED][i] = table[i]; + dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; + dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; + } + break; } - break; - case OPT_GAMMA_VECTOR_R: - 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++) { - s->dev->gamma_override_tables[GENESYS_RED][i] = table[i]; + case OPT_GAMMA_VECTOR_R: { + table = reinterpret_cast<SANE_Word*>(val); + option_size = s->opt[option].size / sizeof (SANE_Word); + dev->gamma_override_tables[GENESYS_RED].resize(option_size); + for (i = 0; i < option_size; i++) { + dev->gamma_override_tables[GENESYS_RED][i] = table[i]; + } + break; } - break; - case OPT_GAMMA_VECTOR_G: - 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++) { - s->dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; + case OPT_GAMMA_VECTOR_G: { + table = reinterpret_cast<SANE_Word*>(val); + option_size = s->opt[option].size / sizeof (SANE_Word); + dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); + for (i = 0; i < option_size; i++) { + dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; + } + break; } - break; - case OPT_GAMMA_VECTOR_B: - 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]; + case OPT_GAMMA_VECTOR_B: { + table = reinterpret_cast<SANE_Word*>(val); + option_size = s->opt[option].size / sizeof (SANE_Word); + dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); + for (i = 0; i < option_size; i++) { + dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; + } + break; } - 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); + auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres, + dev->settings.get_channels(), + dev->settings.scan_method); catch_all_exceptions(__func__, [&]() { - s->dev->cmd_set->save_power(s->dev, false); - genesys_scanner_calibration(s->dev, sensor); + dev->cmd_set->save_power(dev, false); + genesys_scanner_calibration(dev, sensor); }); catch_all_exceptions(__func__, [&]() { - s->dev->cmd_set->save_power(s->dev, true); + dev->cmd_set->save_power(dev, true); }); *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; } - case OPT_CLEAR_CALIBRATION: - s->dev->calibration_cache.clear(); + case OPT_CLEAR_CALIBRATION: { + dev->calibration_cache.clear(); - /* remove file */ - unlink(s->dev->calib_file.c_str()); - /* signals that sensors will have to be read again */ - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - break; - case OPT_FORCE_CALIBRATION: - s->dev->force_calibration = 1; - s->dev->calibration_cache.clear(); - s->dev->calib_file.clear(); + // remove file + unlink(dev->calib_file.c_str()); + // signals that sensors will have to be read again + *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + break; + } + case OPT_FORCE_CALIBRATION: { + dev->force_calibration = 1; + dev->calibration_cache.clear(); + dev->calib_file.clear(); - /* signals that sensors will have to be read again */ - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - break; + // signals that sensors will have to be read again + *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + break; + } case OPT_IGNORE_OFFSETS: { - s->dev->ignore_offsets = true; + dev->ignore_offsets = true; break; } - default: - DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option); + default: { + DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option); + } } } @@ -5829,13 +6217,13 @@ void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); + auto* dev = s->dev; /* don't recompute parameters once data reading is active, ie during scan */ - if (!s->dev->read_active) { + if (!dev->read_active) { calc_parameters(s); } - if (params) - { + if (params) { *params = s->params; /* in the case of a sheetfed scanner, when full height is specified @@ -5843,11 +6231,11 @@ void 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 && !s->dev->buffer_image && + if (dev->model->is_sheetfed && s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max) { - params->lines = -1; - } + params->lines = -1; + } } debug_dump(DBG_proc, *params); } @@ -5865,6 +6253,7 @@ void sane_start_impl(SANE_Handle handle) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); + auto* dev = s->dev; if (s->pos_top_left_x >= s->pos_bottom_right_x) { throw SaneException("top left x >= bottom right x"); @@ -5873,67 +6262,27 @@ void sane_start_impl(SANE_Handle handle) 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. */ - - calc_parameters(s); - genesys_start_scan(s->dev, s->lamp_off); - - s->scanning = true; + // fetch stored calibration + if (dev->force_calibration == 0) { + auto path = calibration_filename(dev); + s->calibration_file = path; + dev->calib_file = path; + DBG(DBG_info, "%s: Calibration filename set to:\n", __func__); + DBG(DBG_info, "%s: >%s<\n", __func__, dev->calib_file.c_str()); - /* allocate intermediate buffer when doing dynamic lineart */ - 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(); - s->dev->local_buffer.alloc(s->dev->binarize_buffer.size() * 8); + catch_all_exceptions(__func__, [&]() + { + sanei_genesys_read_calibration(dev->calibration_cache, dev->calib_file); + }); } - /* if one of the software enhancement option is selected, - * we do the scan internally, process picture then put it an internal - * buffer. Since cropping may change scan parameters, we recompute them - * at the end */ - if (s->dev->buffer_image) - { - 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)) { - 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; - } + // First make sure we have a current parameter set. Some of the + // parameters will be overwritten below, but that's OK. - 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.get_channels(), - s->dev->settings.scan_method); - catch_all_exceptions(__func__, [&](){ genesys_deskew(s, sensor); }); - } - - if (s->swdespeck) { - catch_all_exceptions(__func__, [&](){ genesys_despeck(s); }); - } - - if(s->swcrop) { - catch_all_exceptions(__func__, [&](){ genesys_crop(s); }); - } + calc_parameters(s); + genesys_start_scan(dev, s->lamp_off); - if(s->swderotate) { - catch_all_exceptions(__func__, [&](){ genesys_derotate(s); }); - } - } + s->scanning = true; } SANE_GENESYS_API_LINKAGE @@ -5945,18 +6294,18 @@ SANE_Status sane_start(SANE_Handle handle) }); } -void sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len) +// returns SANE_STATUS_GOOD if there are more data, SANE_STATUS_EOF otherwise +SANE_Status sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); - Genesys_Device *dev; size_t local_len; if (!s) { throw SaneException("handle is nullptr"); } - dev=s->dev; + auto* dev = s->dev; if (!dev) { throw SaneException("dev is nullptr"); } @@ -5986,86 +6335,33 @@ void sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_ /* issue park command immediatly in case scanner can handle it * so we save time */ - if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) && + if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) && !dev->parking) { dev->cmd_set->move_back_home(dev, false); dev->parking = true; } - throw SaneException(SANE_STATUS_EOF); + return SANE_STATUS_EOF; } local_len = max_len; - /* in case of image processing, all data has been stored in - * buffer_image. So read data from it if it exists, else from scanner */ - if(!dev->buffer_image) - { - /* dynamic lineart is another kind of digital processing that needs - * another layer of buffering on top of genesys_read_ordered_data */ - 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(); - genesys_read_ordered_data(dev, dev->local_buffer.get_write_pos(local_len), - &local_len); - dev->local_buffer.produce(local_len); - - 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 (static_cast<std::size_t>(max_len) > dev->binarize_buffer.avail()) - { - local_len=dev->binarize_buffer.avail(); - } - if(local_len) - { - memcpy(buf, dev->binarize_buffer.get_read_pos(), local_len); - dev->binarize_buffer.consume(local_len); - } - } - else - { - // most usual case, direct read of data from scanner */ - genesys_read_ordered_data(dev, buf, &local_len); - } - } - else /* read data from buffer */ - { - if(dev->total_bytes_read+local_len>dev->total_bytes_to_read) - { - local_len=dev->total_bytes_to_read-dev->total_bytes_read; - } - memcpy(buf, dev->img_buffer.data() + dev->total_bytes_read, local_len); - dev->total_bytes_read+=local_len; - } + genesys_read_ordered_data(dev, buf, &local_len); *len = local_len; if (local_len > static_cast<std::size_t>(max_len)) { - fprintf (stderr, "[genesys] sane_read: returning incorrect length!!\n"); + dbg.log(DBG_error, "error: returning incorrect length"); } DBG(DBG_proc, "%s: %d bytes returned\n", __func__, *len); + return SANE_STATUS_GOOD; } 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 wrap_exceptions_to_status_code_return(__func__, [=]() { - sane_read_impl(handle, buf, max_len, len); + return sane_read_impl(handle, buf, max_len, len); }); } @@ -6073,36 +6369,31 @@ void sane_cancel_impl(SANE_Handle handle) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); + auto* dev = s->dev; s->scanning = false; - s->dev->read_active = false; - s->dev->img_buffer.clear(); + dev->read_active = false; - /* no need to end scan if we are parking the head */ - if (!s->dev->parking) { - s->dev->cmd_set->end_scan(s->dev, &s->dev->reg, true); + // no need to end scan if we are parking the head + if (!dev->parking) { + dev->cmd_set->end_scan(dev, &dev->reg, true); } - /* park head if flatbed scanner */ - 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); + // park head if flatbed scanner + if (!dev->model->is_sheetfed) { + if (!dev->parking) { + dev->cmd_set->move_back_home(dev, has_flag(dev->model->flags, ModelFlag::MUST_WAIT)); + dev->parking = !has_flag(dev->model->flags, ModelFlag::MUST_WAIT); } - } - else - { /* in case of sheetfed scanners, we have to eject the document if still present */ - s->dev->cmd_set->eject_document(s->dev); + } else { + // in case of sheetfed scanners, we have to eject the document if still present + dev->cmd_set->eject_document(dev); } - /* enable power saving mode unless we are parking .... */ - if (!s->dev->parking) { - s->dev->cmd_set->save_power(s->dev, true); + // enable power saving mode unless we are parking .... + if (!dev->parking) { + dev->cmd_set->save_power(dev, true); } - - return; } SANE_GENESYS_API_LINKAGE diff --git a/backend/genesys/genesys.h b/backend/genesys/genesys.h index 255bf76..9b1a087 100644 --- a/backend/genesys/genesys.h +++ b/backend/genesys/genesys.h @@ -107,21 +107,12 @@ enum Genesys_Option OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, - OPT_SWDESKEW, - OPT_SWCROP, - OPT_SWDESPECK, - OPT_DESPECK, - OPT_SWSKIP, - OPT_SWDEROTATE, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_EXTRAS_GROUP, OPT_LAMP_OFF_TIME, OPT_LAMP_OFF, - OPT_THRESHOLD, - OPT_THRESHOLD_CURVE, - OPT_DISABLE_INTERPOLATION, OPT_COLOR_FILTER, OPT_CALIBRATION_FILE, OPT_EXPIRATION_TIME, @@ -213,18 +204,9 @@ struct Genesys_Scanner // 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_interpolation = false; + bool preview = false; // TODO: currently not used bool lamp_off = false; SANE_Word lamp_off_time = 0; - bool swdeskew = false; - bool swcrop = false; - bool swdespeck = false; - bool swderotate = false; - SANE_Word swskip = 0; - SANE_Word despeck = 0; SANE_Word contrast = 0; SANE_Word brightness = 0; SANE_Word expiration_time = 0; diff --git a/backend/genesys/gl124.cpp b/backend/genesys/gl124.cpp index 054f1ef..d3fc1bc 100644 --- a/backend/genesys/gl124.cpp +++ b/backend/genesys/gl124.cpp @@ -53,6 +53,37 @@ namespace genesys { namespace gl124 { +struct Gpio_layout +{ + std::uint8_t r31; + std::uint8_t r32; + std::uint8_t r33; + std::uint8_t r34; + std::uint8_t r35; + std::uint8_t r36; + std::uint8_t r38; +}; + +/** @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 + }, +}; + + /** @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. @@ -336,54 +367,9 @@ gl124_init_registers (Genesys_Device * dev) // 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()); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, + 3, ScanMethod::FLATBED); + sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); } /** @brief * Set register values of 'special' ti type frontend @@ -397,12 +383,8 @@ 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; + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; } // start writing to DAC @@ -441,11 +423,8 @@ void CommandSetGl124::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, (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; + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; } val = dev->interface->read_register(REG_0x0A); @@ -466,14 +445,14 @@ void CommandSetGl124::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, static void gl124_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, - const Motor_Profile& motor_profile, + const MotorProfile& 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) + ScanFlag flags) { DBG_HELPER(dbg); int use_fast_fed; @@ -533,11 +512,8 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev, 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; @@ -548,15 +524,15 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev, r02 &= ~REG_0x02_FASTFED; } - if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { r02 |= REG_0x02_AGOHOME; } - if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) || (yres >= sensor.optical_res)) + if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (yres >= sensor.full_resolution)) { r02 |= REG_0x02_ACDCDIS; } - if (has_flag(flags, MotorFlag::REVERSE)) { + if (has_flag(flags, ScanFlag::REVERSE)) { r02 |= REG_0x02_MTRREV; } @@ -566,13 +542,12 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev, 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); + auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, yres, + scan_exposure_time, 1, motor_profile); + scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); - reg->set16(REG_STEPNO, scan_table.steps_count); + reg->set16(REG_STEPNO, scan_table.table.size()); /* fast table */ fast_dpi=yres; @@ -583,28 +558,26 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev, 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); + auto fast_table = create_slope_table(dev->model->asic_type, dev->motor, fast_dpi, + scan_exposure_time, 1, motor_profile); + scanner_send_slope_table(dev, sensor, STOP_TABLE, fast_table.table); + scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); - reg->set16(REG_FASTNO, fast_table.steps_count); - reg->set16(REG_FSHDEC, fast_table.steps_count); - reg->set16(REG_FMOVNO, fast_table.steps_count); + reg->set16(REG_FASTNO, fast_table.table.size()); + reg->set16(REG_FSHDEC, fast_table.table.size()); + reg->set16(REG_FMOVNO, fast_table.table.size()); /* 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 = scan_table.table.size(); + if (has_flag(flags, ScanFlag::FEEDING)) { dist *= 2; } if (use_fast_fed) { - dist += fast_table.steps_count * 2; + dist += fast_table.table.size() * 2; } - DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist); /* get sure we don't use insane value */ if (dist < feedl) { @@ -614,160 +587,97 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev, } 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, + scan_table.table.size(), feedl, - scan_table.steps_count, + scan_table.table.size(), &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); + reg->set16(REG_FMOVDEC, fast_table.table.size()); } - -/** @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); + scanner_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)) + has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { - r->value &= ~REG_0x01_DVDSET; + reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; } else { - r->value |= REG_0x01_DVDSET; + reg->find_reg(REG_0x01).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__); + reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; + } else { + // BUG: the following is likely incorrect + reg->find_reg(REG_0x03).value |= ~REG_0x03_AVEENB; } 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); + dev->interface->write_register(REG_0x114, 0x7f); + dev->interface->write_register(REG_0x115, 0x7f); /* 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); + reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: - r->value &= ~REG_0x04_LINEART; - r->value |= REG_0x04_BITSET; + reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; + reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } - r->value &= ~REG_0x04_FILTER; + reg->find_reg(REG_0x04).value &= ~REG_0x04_FILTER; if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: - r->value |= 0x10; + reg->find_reg(REG_0x04).value |= 0x10; break; case ColorFilter::BLUE: - r->value |= 0x30; + reg->find_reg(REG_0x04).value |= 0x30; break; case ColorFilter::GREEN: - r->value |= 0x20; + reg->find_reg(REG_0x04).value |= 0x20; break; default: break; // should not happen } } - sanei_genesys_set_dpihw(*reg, sensor, dpihw); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, + session.params.channels, + session.params.scan_method); + sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -775,25 +685,16 @@ static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens 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); + reg->set16(REG_DPISET, sensor.register_dpiset); - r = sanei_genesys_get_address(reg, REG_0x06); - r->value |= REG_0x06_GAIN4; + reg->find_reg(REG_0x06).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; + reg->find_reg(REG_0x60).value &= ~REG_0x60_LEDADD; if (session.enable_ledadd) { - r->value |= REG_0x60_LEDADD; + reg->find_reg(REG_0x60).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)); @@ -803,31 +704,32 @@ static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens 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; + reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY; if (session.enable_ledadd) { - r->value |= REG_0x01_TRUEGRAY; + reg->find_reg(0x01).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); } } + std::uint32_t pixel_endx = session.pixel_endx; + if (pixel_endx == reg->get24(REG_SEGCNT)) { + pixel_endx = 0; + } reg->set24(REG_STRPIXEL, session.pixel_startx); - reg->set24(REG_ENDPIXEL, session.pixel_endx); + reg->set24(REG_ENDPIXEL, pixel_endx); dev->line_count = 0; - build_image_pipeline(dev, session); + setup_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_MAXWD, session.output_line_bytes_raw * session.params.channels * + session.optical_resolution / session.full_resolution); 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); } @@ -838,7 +740,6 @@ void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Gene DBG_HELPER(dbg); session.assert_computed(); - int move; int exposure_time; int dummy = 0; @@ -856,9 +757,7 @@ void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Gene } else { exposure_time = sensor.exposure_lperiod; } - const auto& motor_profile = sanei_genesys_get_motor_profile(*gl124_motor_profiles, - dev->model->motor_id, - exposure_time); + const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session); 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)); @@ -870,30 +769,13 @@ void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Gene // 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); + session.optical_line_count, + dummy, session.params.starty, session.params.scan_mode, + session.params.flags); /*** prepares data reordering ***/ - dev->read_buffer.clear(); - dev->read_buffer.alloc(session.buffer_size_read); - dev->read_active = true; dev->session = session; @@ -909,21 +791,24 @@ 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); + unsigned move_dpi = dev->motor.base_ydpi / 4; + float move = dev->model->y_offset; + move += dev->settings.tl_y; + move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + + float start = dev->model->x_offset; + start += settings.tl_x; + start /= sensor.full_resolution / sensor.get_optical_resolution(); + start = static_cast<float>((start * settings.xres) / 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.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; @@ -953,17 +838,15 @@ void CommandSetGl124::save_power(Genesys_Device* dev, bool enable) const 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; + dev->reg.find_reg(REG_0x03).value &= ~0xf0; if(delay<15) { - r->value |= delay; + dev->reg.find_reg(REG_0x03).value |= delay; } else { - r->value |= 0x0f; + dev->reg.find_reg(REG_0x03).value |= 0x0f; } } @@ -1031,8 +914,7 @@ void CommandSetGl124::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens // 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); + scanner_clear_scan_and_feed_counts(*dev); // enable scan and motor uint8_t val = dev->interface->read_register(REG_0x01); @@ -1069,177 +951,43 @@ void CommandSetGl124::move_back_home(Genesys_Device* dev, bool wait_until_home) 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, ®s, 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); + unsigned channels = 3; + unsigned resolution = sensor.shading_resolution; - resolution /= ccd_size_divisor; - dev->calib_lines /= ccd_size_divisor; // reducing just because we reduced the resolution + unsigned calib_lines = + static_cast<unsigned>(dev->model->y_size_calib_mm * resolution / MM_PER_INCH); - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, - dev->calib_channels, + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 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; + unsigned 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.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; + session.params.lines = calib_lines; session.params.depth = 16; - session.params.channels = dev->calib_channels; + 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 = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | - ScanFlag::DISABLE_BUFFER_FULL_MOVE | - ScanFlag::IGNORE_LINE_DISTANCE; + ScanFlag::DISABLE_BUFFER_FULL_MOVE; compute_session(dev, session, calib_sensor); try { @@ -1250,7 +998,7 @@ void CommandSetGl124::init_regs_for_shading(Genesys_Device* dev, const Genesys_S } sanei_genesys_set_motor_power(regs, false); - dev->interface->write_registers(regs); + dev->calib_session = session; } void CommandSetGl124::wait_for_motor_stop(Genesys_Device* dev) const @@ -1272,56 +1020,6 @@ void CommandSetGl124::wait_for_motor_stop(Genesys_Device* dev) const 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. @@ -1330,8 +1028,7 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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; + std::uint32_t addr, length, segcnt, pixels, i; uint8_t *ptr, *src; /* logical size of a color as seen by generic code of the frontend */ @@ -1339,16 +1036,6 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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 */ @@ -1359,7 +1046,7 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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_factor", std::to_string(sensor.shading_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)); @@ -1375,47 +1062,18 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso ptr = buffer.data(); /* iterate on both sensor segment */ - for(x=0;x<pixels;x+=4*factor) - { + for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_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; + for (unsigned s = 0; s < dev->session.segment_count; s++) + { + unsigned segnum = dev->session.segment_count > 1 ? sensor.segment_order[s] : 0; + ptr[0+pixels*s]=src[0+segcnt*segnum]; + ptr[1+pixels*s]=src[1+segcnt*segnum]; + ptr[2+pixels*s]=src[2+segcnt*segnum]; + ptr[3+pixels*s]=src[3+segcnt*segnum]; } /* next shading coefficient */ @@ -1433,20 +1091,17 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso * 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 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; @@ -1456,7 +1111,7 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& session.params.yres = resolution; session.params.startx = 0; session.params.starty = 0; - session.params.pixels = pixels; + session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = 1; session.params.depth = 8; session.params.channels = channels; @@ -1466,14 +1121,12 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | - ScanFlag::IGNORE_LINE_DISTANCE; + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; compute_session(dev, session, move_sensor); dev->cmd_set->init_regs_for_scan_session(dev, move_sensor, ®s, session); - size = pixels * 3; - std::vector<uint8_t> line(size); - // write registers and scan data dev->interface->write_registers(regs); @@ -1486,14 +1139,13 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& return; } - sanei_genesys_read_data_from_scanner(dev, line.data(), size); + auto image = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes); // 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); + if (dbg_log_image_data()) { + write_tiff_file("gl124_movetocalarea.tiff", image); } } @@ -1505,513 +1157,60 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& 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, ®s, 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, ®s, 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] }; + return scanner_led_calibration(*dev, sensor, regs); } -/** - * 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, ®s, 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, ®s, 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, ®s, 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, ®s, 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)); + scanner_offset_calibration(*dev, sensor, regs); } - -/* 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, ®s, 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, ®s, 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); + scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } // 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 + Genesys_Register_Set* reg) const { DBG_HELPER(dbg); - int num_pixels; - - *channels=3; *reg = dev->reg; + auto flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::SINGLE_LINE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + flags |= ScanFlag::USE_XPA; + } + ScanSession session; - session.params.xres = sensor.optical_res; + session.params.xres = sensor.full_resolution; session.params.yres = dev->motor.base_ydpi; - session.params.startx = sensor.sensor_pixels / 4; + session.params.startx = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH / 4; session.params.starty = 0; - session.params.pixels = sensor.sensor_pixels / 2; + session.params.pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH / 2; session.params.lines = 1; - session.params.depth = 8; - session.params.channels = *channels; + session.params.depth = dev->model->bpp_color_values.front(); + 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; + session.params.flags = flags; + 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 @@ -2049,64 +1248,8 @@ static void gl124_init_gpio(Genesys_Device* dev) 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); + apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /** @@ -2118,7 +1261,7 @@ void CommandSetGl124::init(Genesys_Device* dev) const DBG_INIT (); DBG_HELPER(dbg); - sanei_genesys_asic_init(dev, 0); + sanei_genesys_asic_init(dev); } @@ -2244,26 +1387,5 @@ void CommandSetGl124::eject_document(Genesys_Device* dev) const 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 index cdf8faf..ea7041e 100644 --- a/backend/genesys/gl124.h +++ b/backend/genesys/gl124.h @@ -45,74 +45,12 @@ #define BACKEND_GENESYS_GL124_H #include "genesys.h" -#include "command_set.h" +#include "command_set_common.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 +class CommandSetGl124 : public CommandSetCommon { public: ~CommandSetGl124() override = default; @@ -122,17 +60,11 @@ public: 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; + 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; @@ -148,8 +80,6 @@ public: 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; @@ -165,8 +95,6 @@ public: 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; @@ -175,11 +103,6 @@ public: 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; diff --git a/backend/genesys/gl646.cpp b/backend/genesys/gl646.cpp index 04ee85e..61fa1e0 100644 --- a/backend/genesys/gl646.cpp +++ b/backend/genesys/gl646.cpp @@ -63,9 +63,336 @@ 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); +static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution); + + +static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set, int dpi); + +static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + const ScanSession& session, bool move, + 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); + +/** + * master motor settings table entry + */ +struct Motor_Master +{ + 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 +}; + +/** + * 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 */ +}; /** * reads value from gpio endpoint @@ -105,44 +432,6 @@ static void gl646_stop_motor(Genesys_Device* dev) } /** - * 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 @@ -157,7 +446,6 @@ static int get_cksel(SensorId sensor_id, int required, unsigned channels) 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; } } @@ -177,7 +465,6 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene uint32_t move = session.params.starty; - int i, nb; Motor_Master *motor = nullptr; uint32_t z1, z2; int feedl; @@ -185,57 +472,47 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene /* 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++; + for (unsigned i = 0; i < sizeof (motor_master) / sizeof (Motor_Master); ++i) { + 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]; + } } - if (motor == nullptr) - { + 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); - } + scanner_setup_sensor(*dev, sensor, *regs); /* 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)); + auto slope_table1 = create_slope_table_for_speed(motor->slope1, motor->slope1.max_speed_w, + StepType::FULL, 1, 4, + get_slope_table_max_size(AsicType::GL646)); + auto slope_table2 = create_slope_table_for_speed(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; + regs->find_reg(0x01).value |= REG_0x01_DOGENB | 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) + // if device has no calibration, don't enable shading correction + if (has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || + has_flag(session.params.flags, ScanFlag::DISABLE_SHADING)) { regs->find_reg(0x01).value &= ~REG_0x01_DVDSET; + } else { + regs->find_reg(0x01).value |= REG_0x01_DVDSET; } regs->find_reg(0x01).value &= ~REG_0x01_FASTMOD; @@ -284,7 +561,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene break; } - if (dev->model->is_sheetfed) { + if (dev->model->is_sheetfed || !has_flag(session.params.flags, ScanFlag::AUTO_GO_HOME)) { regs->find_reg(0x02).value &= ~REG_0x02_AGOHOME; } else { regs->find_reg(0x02).value |= REG_0x02_AGOHOME; @@ -314,14 +591,20 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene break; } - sanei_genesys_set_dpihw(*regs, sensor, sensor.optical_res); + sanei_genesys_set_dpihw(*regs, sensor.full_resolution); /* gamma enable for scans */ - if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) { + if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { regs->find_reg(0x05).value |= REG_0x05_GMM14BIT; } - regs->find_reg(0x05).value &= ~REG_0x05_GMMENB; + if (!has_flag(session.params.flags, ScanFlag::DISABLE_GAMMA) && + session.params.depth < 16) + { + regs->find_reg(REG_0x05).value |= REG_0x05_GMMENB; + } else { + regs->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; + } /* true CIS gray if needed */ if (dev->model->is_cis && session.params.channels == 1 && dev->settings.true_gray) { @@ -356,17 +639,17 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene // 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_table1.table.size() > slope_table2.table.size() + 100) { + slope_table2.expand_table(slope_table1.table.size() - 100, 1); } - if (slope_table2.steps_count > slope_table1.steps_count + 100) { - slope_table1.steps_count += slope_table2.steps_count - 100; + if (slope_table2.table.size() > slope_table1.table.size() + 100) { + slope_table1.expand_table(slope_table2.table.size() - 100, 1); } - if (slope_table1.steps_count >= slope_table2.steps_count) { - backward_steps += (slope_table1.steps_count - slope_table2.steps_count) * 2; + if (slope_table1.table.size() >= slope_table2.table.size()) { + backward_steps += (slope_table1.table.size() - slope_table2.table.size()) * 2; } else { - forward_steps += (slope_table2.steps_count - slope_table1.steps_count) * 2; + forward_steps += (slope_table2.table.size() - slope_table1.table.size()) * 2; } if (forward_steps > 255) { @@ -382,8 +665,8 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene 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(0x21).value = slope_table1.table.size(); + regs->find_reg(0x24).value = slope_table2.table.size(); regs->find_reg(0x22).value = forward_steps; regs->find_reg(0x23).value = backward_steps; @@ -401,8 +684,11 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene 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()); + // FIXME: the incoming sensor is selected for incorrect resolution + const auto& dpiset_sensor = sanei_genesys_find_sensor(dev, session.params.xres, + session.params.channels, + session.params.scan_method); + regs->set16(REG_DPISET, dpiset_sensor.register_dpiset); regs->set16(REG_LPERIOD, sensor.exposure_lperiod); /* move distance must be adjusted to take into account the extra lines @@ -410,8 +696,8 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene 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; + unsigned total_lines = session.max_color_shift_lines + session.num_staggered_lines; + int feed_offset = (total_lines * dev->motor.base_ydpi) / motor->dpi; if (feedl > feed_offset) { feedl = feedl - feed_offset; } @@ -424,8 +710,6 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene /* 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) { @@ -505,12 +789,12 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene if (motor->fastfed) { - feedl = feedl - 2 * slope_table2.steps_count - - (slope_table1.steps_count >> step_shift); + feedl = feedl - 2 * slope_table2.table.size() - + (slope_table1.table.size() >> step_shift); } else { - feedl = feedl - (slope_table1.steps_count >> step_shift); + feedl = feedl - (slope_table1.table.size() >> step_shift); } break; } @@ -520,7 +804,6 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene feedl = 0; } - DBG(DBG_info, "%s: final move=%d\n", __func__, feedl); regs->set24(REG_FEEDL, feedl); regs->find_reg(0x65).value = motor->mtrpwm; @@ -528,7 +811,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene sanei_genesys_calculate_zmod(regs->find_reg(0x02).value & REG_0x02_FASTFED, sensor.exposure_lperiod, slope_table1.table, - slope_table1.steps_count, + slope_table1.table.size(), move, motor->fwdbwd, &z1, &z2); /* no z1/z2 for sheetfed scanners */ @@ -538,7 +821,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene } regs->set16(REG_Z1MOD, z1); regs->set16(REG_Z2MOD, z2); - regs->find_reg(0x6b).value = slope_table2.steps_count; + regs->find_reg(0x6b).value = slope_table2.table.size(); regs->find_reg(0x6c).value = (regs->find_reg(0x6c).value & REG_0x6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16) & 0x07); @@ -548,10 +831,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene // 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); + setup_image_pipeline(*dev, session); dev->read_active = true; @@ -578,32 +858,8 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene } } - 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__); + scanner_send_slope_table(dev, sensor, 0, slope_table1.table); + scanner_send_slope_table(dev, sensor, 1, slope_table2.table); } /** @@ -632,8 +888,8 @@ gl646_init_regs (Genesys_Device * dev) 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 */ + 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 } @@ -648,8 +904,8 @@ gl646_init_regs (Genesys_Device * dev) 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? */ + 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: @@ -664,9 +920,9 @@ gl646_init_regs (Genesys_Device * dev) 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); + sanei_genesys_set_dpihw(dev->reg, sensor.full_resolution); - if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) { + if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { dev->reg.find_reg(0x05).value |= REG_0x05_GMM14BIT; } if (dev->model->adc_id == AdcId::AD_XP200) { @@ -679,8 +935,7 @@ gl646_init_regs (Genesys_Device * dev) dev->reg.find_reg(0x06).value = 0x18; // PWRBIT on, shading gain=8, normal AFE image capture } - - gl646_setup_sensor(dev, sensor, &dev->reg); + scanner_setup_sensor(*dev, sensor, dev->reg); dev->reg.find_reg(0x1e).value = 0xf0; /* watch-dog time */ @@ -788,54 +1043,15 @@ gl646_init_regs (Genesys_Device * dev) 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)); + if (set == AFE_INIT) { - dev->frontend = dev->frontend_initial; + dev->frontend = dev->frontend_initial; // write them to analog frontend dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); @@ -888,8 +1104,7 @@ static void gl646_wm_hp3670(Genesys_Device* dev, const Genesys_Sensor& sensor, u default: /* AFE_SET */ /* mode setup */ i = dev->frontend.regs.get_value(0x03); - if (dpi > sensor.optical_res / 2) - { + if (dpi > sensor.full_resolution / 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 */ @@ -947,11 +1162,8 @@ static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint } /* 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; + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; // reset only done on init dev->interface->write_fe_register(0x04, 0x80); @@ -1174,14 +1386,15 @@ void CommandSetGl646::load_document(Genesys_Device* dev) const 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)); + auto slope_table = create_slope_table_for_speed(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); + const auto& sensor = sanei_genesys_find_sensor_any(dev); + scanner_send_slope_table(dev, sensor, 1, slope_table.table); dev->interface->write_registers(regs); @@ -1292,9 +1505,8 @@ void CommandSetGl646::eject_document(Genesys_Device* dev) const // 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; + DBG(DBG_info, "%s: no more document to eject\n", __func__); + return; } // there is a document inserted, eject it @@ -1331,14 +1543,16 @@ void CommandSetGl646::eject_document(Genesys_Device* dev) const 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)); + auto slope_table = create_slope_table_for_speed(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); + // FIXME: sensor is not used. + const auto& sensor = sanei_genesys_find_sensor_any(dev); + scanner_send_slope_table(dev, sensor, 1, slope_table.table); dev->interface->write_registers(regs); @@ -1473,7 +1687,7 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) if (!i) /* the loop counted down to 0, scanner still is busy */ { - dev->set_head_pos_unknown(); + dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY); throw SaneException(SANE_STATUS_DEVICE_BUSY, "motor is still on: device busy"); } @@ -1489,15 +1703,15 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) 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; + session.params.flags = ScanFlag::REVERSE | + ScanFlag::AUTO_GO_HOME | + ScanFlag::DISABLE_GAMMA; if (dev->model->default_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } @@ -1520,8 +1734,7 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) /* 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; + return; } // starts scan @@ -1554,7 +1767,6 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) 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; @@ -1567,7 +1779,7 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) // 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(); + dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY); throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home"); } @@ -1576,165 +1788,60 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) } /** - * 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; + unsigned cksel = get_cksel(dev->model->sensor_id, dev->settings.xres, channels); + + unsigned resolution = sensor.get_optical_resolution() / cksel; + // FIXME: we select wrong calibration sensor 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); + auto pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; - 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; + unsigned calib_lines = + static_cast<unsigned>(dev->model->y_size_calib_mm * resolution / MM_PER_INCH); + + 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 = calib_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::IGNORE_COLOR_OFFSET | + ScanFlag::IGNORE_STAGGER_OFFSET; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { + session.params.flags |= ScanFlag::USE_XPA; } + compute_session(dev, session, calib_sensor); + + dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); + + dev->calib_session = session; /* 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; + dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED; 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 @@ -1745,109 +1852,6 @@ bool CommandSetGl646::needs_home_before_init_regs_for_scan(Genesys_Device* dev) } /** - * 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 @@ -1857,9 +1861,7 @@ void CommandSetGl646::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor int address; int bits; - /* gamma table size */ - if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) - { + if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { size = 16384; bits = 14; } @@ -1903,45 +1905,42 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes { 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; + ScanColorMode scan_mode = ScanColorMode::COLOR_SINGLE_PASS; + if (dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS) { + 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; + // offset calibration is always done in color mode + unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; - settings.disable_interpolation = 0; - settings.threshold = 0; + ScanSession session; + session.params.xres = sensor.full_resolution; + session.params.yres = sensor.full_resolution; + session.params.startx = 0; + session.params.starty = 0; + session.params.pixels = 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 = scan_mode; + session.params.color_filter = ColorFilter::RED; + session.params.flags = ScanFlag::DISABLE_SHADING; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { + session.params.flags |= ScanFlag::USE_XPA; + } + compute_session(dev, session, sensor); - /* colors * bytes_per_color * scan lines */ - total_size = settings.pixels * channels * 2 * 1; + // colors * bytes_per_color * scan lines + unsigned total_size = pixels * channels * 2 * 1; std::vector<uint8_t> line(total_size); @@ -1968,38 +1967,34 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes DBG(DBG_info, "%s: starting first line reading\n", __func__); - simple_scan(dev, calib_sensor, settings, false, true, false, line, "led_calibration"); + dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); + simple_scan(dev, calib_sensor, session, 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); - } + if (dbg_log_image_data()) { + char fn[30]; + std::snprintf(fn, 30, "gl646_led_%02d.tiff", turn); + write_tiff_file(fn, line.data(), 16, channels, 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; + for (i = 0; i < pixels; i++) { + 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]; + } + avg[j] += val; } - avg[j] /= settings.pixels; + avg[j] /= pixels; } DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); @@ -2088,31 +2083,40 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& unsigned int channels; int pass = 0; - SANE_Int resolution; - Genesys_Settings settings; - unsigned int x, y, adr, min; + unsigned 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; + + // FIXME: maybe reuse `sensor` + const auto& calib_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, + ScanMethod::FLATBED); + black_pixels = (calib_sensor.black_pixels * sensor.full_resolution) / calib_sensor.full_resolution; + + unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; + unsigned lines = CALIBRATION_LINES; + + if (dev->model->is_cis) { + lines = ((lines + 2) / 3) * 3; + } + + ScanSession session; + session.params.xres = sensor.full_resolution; + session.params.yres = sensor.full_resolution; + session.params.startx = 0; + session.params.starty = 0; + session.params.pixels = pixels; + session.params.lines = lines; + 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 = ScanFlag::DISABLE_SHADING; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { + session.params.flags |= ScanFlag::USE_XPA; + } + compute_session(dev, session, calib_sensor); /* scan first line of data with no gain */ dev->frontend.set_gain(0, 0); @@ -2129,27 +2133,24 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& 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"); + + dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); + simple_scan(dev, calib_sensor, session, 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); - } + if (dbg_log_image_data()) { + char title[30]; + std::snprintf(title, 30, "gl646_offset%03d.tiff", static_cast<int>(bottom)); + write_tiff_file(title, line.data(), 8, channels, pixels, lines); + } min = 0; - for (y = 0; y < settings.lines; y++) - { - for (x = 0; x < black_pixels; x++) - { - adr = (x + y * settings.pixels) * channels; + for (unsigned y = 0; y < lines; y++) { + for (unsigned x = 0; x < black_pixels; x++) { + adr = (x + y * pixels) * channels; if (line[adr] > min) min = line[adr]; if (line[adr + 1] > min) @@ -2159,7 +2160,7 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& } } - DBG(DBG_io2, "%s: pass=%d, min=%d\n", __func__, pass, min); + DBG(DBG_info, "%s: pass=%d, min=%d\n", __func__, pass, min); bottom++; } while (pass < 128 && min == 0); @@ -2187,9 +2188,7 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens DBG_HELPER(dbg); (void) regs; - unsigned int channels; int pass = 0, avg; - Genesys_Settings settings; int topavg, bottomavg; int top, bottom, black_pixels; @@ -2198,32 +2197,38 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens 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; + unsigned resolution = dev->settings.xres; + unsigned channels = 3; - DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels); + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, + ScanMethod::FLATBED); + black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.full_resolution; - 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; + unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; + unsigned lines = CALIBRATION_LINES; + if (dev->model->is_cis) { + lines = ((lines + 2) / 3) * 3; + } - settings.disable_interpolation = 0; - settings.threshold = 0; + 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 = 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 = ScanFlag::DISABLE_SHADING; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { + session.params.flags |= ScanFlag::USE_XPA; + } + compute_session(dev, session, sensor); /* scan first line of data with no gain, but with offset from * last calibration */ @@ -2239,38 +2244,32 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens std::vector<uint8_t> first_line, second_line; - simple_scan(dev, calib_sensor, settings, false, true, false, first_line, - "offset_first_line"); + dev->cmd_set->init_regs_for_scan_session(dev, sensor, &dev->reg, session); + simple_scan(dev, calib_sensor, session, 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); + if (dbg_log_image_data()) { + char title[30]; + std::snprintf(title, 30, "gl646_offset%03d.tiff", bottom); + write_tiff_file(title, first_line.data(), 8, channels, pixels, lines); } - bottomavg = dark_average(first_line.data(), settings.pixels, settings.lines, channels, - black_pixels); - DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg); + bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels); + DBG(DBG_info, "%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"); + dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); + simple_scan(dev, calib_sensor, session, 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); + if (dbg_log_image_data()) { + char title[30]; + std::snprintf(title, 30, "gl646_offset%03d.tiff", top); + write_tiff_file(title, second_line.data(), 8, channels, pixels, lines); } - topavg = dark_average(second_line.data(), settings.pixels, settings.lines, channels, - black_pixels); - DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg); + topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels); + DBG(DBG_info, "%s: top avg=%d\n", __func__, topavg); if (is_testing_mode()) { return; @@ -2287,20 +2286,17 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens dev->frontend.set_offset(2, (top + bottom) / 2); // scan with no move - simple_scan(dev, calib_sensor, settings, false, true, false, second_line, + dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); + simple_scan(dev, calib_sensor, session, 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); - } + if (dbg_log_image_data()) { + char title[30]; + std::snprintf(title, 30, "gl646_offset%03d.tiff", dev->frontend.get_offset(1)); + write_tiff_file(title, second_line.data(), 8, channels, pixels, lines); + } - avg = - dark_average (second_line.data(), settings.pixels, settings.lines, channels, - black_pixels); + 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 */ @@ -2322,102 +2318,6 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens 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 @@ -2430,76 +2330,67 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys { DBG_HELPER(dbg); (void) dpi; + (void) sensor; + (void) regs; - 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; + unsigned 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, + // BUG: the following comment is incorrect + // we are searching a sensor resolution */ + const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, 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; + unsigned pixels = 0; + float start = 0; + if (dev->settings.scan_method == ScanMethod::FLATBED) { + pixels = dev->model->x_size_calib_mm * dev->settings.xres / MM_PER_INCH; + } else { + start = dev->model->x_offset_ta; + pixels = static_cast<unsigned>( + (dev->model->x_size_ta * dev->settings.xres) / MM_PER_INCH); } - else - { - settings.tl_x = dev->model->x_offset_ta; - settings.pixels = static_cast<unsigned>((dev->model->x_size_ta * resolution) / MM_PER_INCH); + + unsigned lines = CALIBRATION_LINES; + // round up to multiple of 3 in case of CIS scanner + if (dev->model->is_cis) { + lines = ((lines + 2) / 3) * 3; } - 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 = static_cast<float>((start * dev->settings.xres) / MM_PER_INCH); + + ScanSession session; + session.params.xres = dev->settings.xres; + session.params.yres = dev->settings.xres; + session.params.startx = static_cast<unsigned>(start); + 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 = ColorFilter::RED; + session.params.flags = ScanFlag::DISABLE_SHADING; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { + session.params.flags |= ScanFlag::USE_XPA; + } + compute_session(dev, session, calib_sensor); /* 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; + average[0] = 0; + average[1] = 0; + average[2] = 0; + + unsigned pass = 0; std::vector<uint8_t> line; @@ -2509,75 +2400,60 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys (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"); + dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); + simple_scan(dev, calib_sensor, session, 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; - } - } + if (dbg_log_image_data()) { + std::sprintf(title, "gl646_gain%02d.tiff", pass); + write_tiff_file(title, line.data(), 8, channels, pixels, 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 (unsigned k = 0; k < channels; k++) { + + // we find the maximum white value, so we can deduce a threshold + // to average white values + unsigned maximum = 0; + for (unsigned i = 0; i < lines; i++) { + for (unsigned j = 0; j < pixels; j++) { + unsigned val = line[i * channels * pixels + j + k]; + maximum = std::max(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); + // computes white average + average[k] = 0; + unsigned count = 0; + for (unsigned i = 0; i < lines; i++) { + for (unsigned j = 0; j < pixels; j++) { + // averaging only white points allow us not to care about dark margins + unsigned val = line[i * channels * pixels + j + k]; + if (val > maximum) { + average[k] += val; + count++; + } + } + } + average[k] = average[k] / count; - DBG(DBG_proc, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k], - dev->frontend.get_gain(k)); - } - } + // adjusts gain for the channel + if (average[k] < calib_sensor.gain_white_ref) { + dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1); + } - 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: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k], + dev->frontend.get_gain(k)); + } } - 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)); + 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)); } /** @@ -2585,46 +2461,43 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys * */ void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, - Genesys_Register_Set* local_reg, int* channels, - int* total_size) const + Genesys_Register_Set* local_reg) 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); - + unsigned resolution = 300; 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); + // set up for a full width 2 lines gray scan without moving + unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; - /* we are not going to move, so clear these bits */ - dev->reg.find_reg(0x02).value &= ~(REG_0x02_FASTFED | REG_0x02_AGOHOME); + 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 = 2; + session.params.depth = dev->model->bpp_gray_values.front(); + session.params.channels = 1; + 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 (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { + session.params.flags |= ScanFlag::USE_XPA; + } + compute_session(dev, session, local_sensor); + + dev->cmd_set->init_regs_for_scan_session(dev, local_sensor, &dev->reg, session); - /* don't enable any correction for this scan */ - dev->reg.find_reg(0x01).value &= ~REG_0x01_DVDSET; + /* we are not going to move, so clear these bits */ + dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED; /* copy to local_reg */ *local_reg = dev->reg; @@ -2632,66 +2505,8 @@ void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Se /* 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); + gl646_set_fe(dev, local_sensor, AFE_SET, session.params.xres); } /* * @@ -2731,10 +2546,11 @@ void CommandSetGl646::init(Genesys_Device* dev) const gl646_init_regs (dev); // Init shading data - sanei_genesys_init_shading_data(dev, sensor, sensor.sensor_pixels); + sanei_genesys_init_shading_data(dev, sensor, + dev->model->x_size_calib_mm * sensor.full_resolution / + MM_PER_INCH); - /* initial calibration reg values */ - dev->calib_reg = dev->reg; + dev->initial_regs = dev->reg; } // execute physical unit init only if cold @@ -2787,7 +2603,7 @@ void CommandSetGl646::init(Genesys_Device* dev) const if (dev->model->gpio_id != GpioId::HP3670 && dev->model->gpio_id != GpioId::HP2400) { - switch (sensor.optical_res) + switch (sensor.full_resolution) { case 600: addr = 0x08200; @@ -2810,9 +2626,6 @@ void CommandSetGl646::init(Genesys_Device* dev) const } 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 @@ -2828,104 +2641,44 @@ void CommandSetGl646::init(Genesys_Device* dev) const /* 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); - } + 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) + const ScanSession& session, bool move, + 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; + unsigned lines = session.output_line_count; + if (!dev->model->is_cis) { + lines++; } - /* setup for move then scan */ - split = !(move && settings.tl_y > 0); - setup_for_scan(dev, sensor, &dev->reg, settings, split, false, false, !forward); + std::size_t size = lines * session.params.pixels; + unsigned bpp = session.params.depth == 16 ? 2 : 1; - /* 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(); + size *= bpp * session.params.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; - } + gl646_set_fe(dev, sensor, AFE_SET, session.params.xres); - /* enable gamma table for the scan */ - dev->reg.find_reg(0x05).value |= REG_0x05_GMMENB; + // no watch dog for simple scan + dev->reg.find_reg(0x01).value &= ~REG_0x01_DOGENB; /* 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; + sanei_genesys_set_motor_power(dev->reg, false); } /* no automatic go home when using XPA */ - if (settings.scan_method == ScanMethod::TRANSPARENCY) { + if (session.params.scan_method == ScanMethod::TRANSPARENCY) { dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME; } @@ -2946,46 +2699,38 @@ static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, 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); - } - } + if (dev->model->is_cis && session.params.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) { + auto pixels_count = session.params.pixels; + + std::vector<uint8_t> buffer(pixels_count * 3 * bpp); + + if (bpp == 1) { + for (unsigned y = 0; y < lines; y++) { + // reorder line + for (unsigned x = 0; x < pixels_count; x++) { + buffer[x * 3] = data[y * pixels_count * 3 + x]; + buffer[x * 3 + 1] = data[y * pixels_count * 3 + pixels_count + x]; + buffer[x * 3 + 2] = data[y * pixels_count * 3 + 2 * pixels_count + x]; + } + // copy line back + std::memcpy(data.data() + pixels_count * 3 * y, buffer.data(), pixels_count * 3); + } + } else { + for (unsigned y = 0; y < lines; y++) { + // reorder line + auto pixels_count = session.params.pixels; + for (unsigned x = 0; x < pixels_count; x++) { + buffer[x * 6] = data[y * pixels_count * 6 + x * 2]; + buffer[x * 6 + 1] = data[y * pixels_count * 6 + x * 2 + 1]; + buffer[x * 6 + 2] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2]; + buffer[x * 6 + 3] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2 + 1]; + buffer[x * 6 + 4] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2]; + buffer[x * 6 + 5] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2 + 1]; + } + // copy line back + std::memcpy(data.data() + pixels_count * 6 * y, buffer.data(),pixels_count * 6); + } + } } // end scan , waiting the motor to stop if needed (if moving), but without ejecting doc @@ -2993,42 +2738,6 @@ static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, } /** - * 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' */ @@ -3130,22 +2839,16 @@ void CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const } /* XPA detection */ - if (dev->model->flags & GENESYS_FLAG_XPA) - { + if (dev->model->has_method(ScanMethod::TRANSPARENCY)) { 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; - } + if ((value & 0x40) == 0) { + session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE; + } else { + session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; + } break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); @@ -3153,6 +2856,11 @@ void CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const } } +void CommandSetGl646::update_home_sensor_gpio(Genesys_Device& dev) const +{ + DBG_HELPER(dbg); + (void) dev; +} static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution) { @@ -3167,7 +2875,7 @@ static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which * is after the second slope table */ - switch (sensor.optical_res) + switch (sensor.full_resolution) { case 600: addr = 0x08200; @@ -3203,159 +2911,9 @@ static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int 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; @@ -3377,26 +2935,25 @@ ScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev, { // 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); + move = dev->model->y_offset; // add tl_y to base movement } - move += static_cast<float>(settings.tl_y); + move += 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); + move = static_cast<float>((move * dev->motor.base_ydpi) / MM_PER_INCH); + float start = settings.tl_x; if (settings.scan_method == ScanMethod::FLATBED) { - start += static_cast<float>(dev->model->x_offset); + start += dev->model->x_offset; } else { - start += static_cast<float>(dev->model->x_offset_ta); + start += dev->model->x_offset_ta; } - start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); + start = static_cast<float>((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; @@ -3411,7 +2968,7 @@ ScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev, 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; + session.params.flags = ScanFlag::AUTO_GO_HOME; if (settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } @@ -3427,10 +2984,5 @@ void CommandSetGl646::asic_boot(Genesys_Device *dev, bool cold) const 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 index afcfa05..8ab2c96 100644 --- a/backend/genesys/gl646.h +++ b/backend/genesys/gl646.h @@ -48,395 +48,13 @@ #define BACKEND_GENESYS_GL646_H #include "genesys.h" -#include "command_set.h" +#include "command_set_common.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 +class CommandSetGl646 : public CommandSetCommon { public: ~CommandSetGl646() override = default; @@ -446,17 +64,11 @@ public: 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; + 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; @@ -472,8 +84,6 @@ public: 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; @@ -489,17 +99,14 @@ public: void update_hardware_sensors(struct Genesys_Scanner* s) const override; + 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; diff --git a/backend/genesys/gl646_registers.h b/backend/genesys/gl646_registers.h index 2fe8f19..6ee9549 100644 --- a/backend/genesys/gl646_registers.h +++ b/backend/genesys/gl646_registers.h @@ -88,6 +88,7 @@ static constexpr RegMask REG_0x04_ADTYPE = 0x30; static constexpr RegMask REG_0x04_FILTER = 0x0c; static constexpr RegMask REG_0x04_FESET = 0x03; +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; diff --git a/backend/genesys/gl841.cpp b/backend/genesys/gl841.cpp index 470f9ba..731354f 100644 --- a/backend/genesys/gl841.cpp +++ b/backend/genesys/gl841.cpp @@ -63,315 +63,11 @@ namespace gl841 { static int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor, + const MotorProfile& profile, 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) @@ -379,139 +75,232 @@ gl841_init_lide80 (Genesys_Device * dev) 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); - + DBG_HELPER(dbg); - dev->reg.find_reg(0x01).value = 0x20; /* (enable shading), CCD, color, 1M */ + dev->reg.init_reg(0x01, 0x20); if (dev->model->is_cis) { dev->reg.find_reg(0x01).value |= REG_0x01_CISSET; } else { dev->reg.find_reg(0x01).value &= ~REG_0x01_CISSET; } + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x01, 0x82); + } - 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; + dev->reg.init_reg(0x02, 0x38); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x02, 0x10); + } - if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) { - // AD front end - dev->reg.find_reg(0x04).value = (2 << REG_0x04S_AFEMOD) | 0x02; + dev->reg.init_reg(0x03, 0x5f); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x03, 0x50); } - else /* Wolfson front end */ - { - dev->reg.find_reg(0x04).value |= 1 << REG_0x04S_AFEMOD; + + dev->reg.init_reg(0x04, 0x10); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) { + dev->reg.init_reg(0x04, 0x22); + } else if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x04, 0x02); } - const auto& sensor = sanei_genesys_find_sensor_any(dev); + const auto& sensor = sanei_genesys_find_sensor_any(dev); - dev->reg.find_reg(0x05).value = 0x00; /* disable gamma, 24 clocks/pixel */ + dev->reg.init_reg(0x05, 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); + sanei_genesys_set_dpihw(dev->reg, sensor.register_dpihw); - dev->reg.find_reg(0x06).value |= REG_0x06_PWRBIT; - dev->reg.find_reg(0x06).value |= REG_0x06_GAIN4; + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x05, 0x4c); + } - /* 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; + dev->reg.init_reg(0x06, 0x18); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x06, 0x38); } - else + if (dev->model->model_id == ModelId::VISIONEER_STROBE_XP300 || + dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_485 || + dev->model->model_id == ModelId::DCT_DOCKETPORT_487 || + dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_685 || + dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) { - 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.init_reg(0x06, 0xb8); } - 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; + dev->reg.init_reg(0x07, 0x00); + dev->reg.init_reg(0x08, 0x00); -/*BUFSEL*/ - dev->reg.find_reg(0x20).value = 0x20; + dev->reg.init_reg(0x09, 0x10); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x09, 0x11); + } + if (dev->model->model_id == ModelId::VISIONEER_STROBE_XP300 || + dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_485 || + dev->model->model_id == ModelId::DCT_DOCKETPORT_487 || + dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_685 || + dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) + { + dev->reg.init_reg(0x09, 0x00); + } + dev->reg.init_reg(0x0a, 0x00); -/*LAMPPWM*/ - dev->reg.find_reg(0x29).value = 0xff; + // 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_LIDE_80) { + 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); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x17, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x19, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x1a, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x1c, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x1d, 0x01); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x1e, 0xf0); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x1e, 0x10); + } + dev->reg.init_reg(0x1f, 0x01); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x1f, 0x04); + } + dev->reg.init_reg(0x20, 0x20); + dev->reg.init_reg(0x21, 0x01); + dev->reg.init_reg(0x22, 0x01); + dev->reg.init_reg(0x23, 0x01); + dev->reg.init_reg(0x24, 0x01); + dev->reg.init_reg(0x25, 0x00); + dev->reg.init_reg(0x26, 0x00); + dev->reg.init_reg(0x27, 0x00); + dev->reg.init_reg(0x29, 0xff); -/*BWHI*/ - dev->reg.find_reg(0x2e).value = 0x80; + dev->reg.init_reg(0x2c, 0x00); + dev->reg.init_reg(0x2d, 0x00); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x2c, sensor.full_resolution >> 8); + dev->reg.init_reg(0x2d, sensor.full_resolution & 0xff); + } + dev->reg.init_reg(0x2e, 0x80); + dev->reg.init_reg(0x2f, 0x80); -/*BWLOW*/ - dev->reg.find_reg(0x2f).value = 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, 0x00); + dev->reg.init_reg(0x35, 0x00); + dev->reg.init_reg(0x36, 0x00); + dev->reg.init_reg(0x37, 0x00); + dev->reg.init_reg(0x38, 0x4f); + dev->reg.init_reg(0x39, 0xc1); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + 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); + } -/*LPERIOD*/ - dev->reg.find_reg(0x38).value = 0x4f; - dev->reg.find_reg(0x39).value = 0xc1; + dev->reg.init_reg(0x3d, 0x00); + dev->reg.init_reg(0x3e, 0x00); + dev->reg.init_reg(0x3f, 0x00); -/*VSMPW*/ - dev->reg.find_reg(0x58).value |= 3 << REG_0x58S_VSMPW; + dev->reg.init_reg(0x52, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x53, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x54, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x55, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x56, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x57, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x58, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x59, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x5a, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below -/*BSMPW*/ - dev->reg.find_reg(0x59).value |= 3 << REG_0x59S_BSMPW; + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + 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); + } else { + for (unsigned addr = 0x5d; addr <= 0x6f; addr++) { + dev->reg.init_reg(addr, 0); + } + dev->reg.init_reg(0x5e, 0x02); + if (dev->model->model_id == ModelId::CANON_LIDE_60) { + dev->reg.init_reg(0x66, 0xff); + } + } -/*RLCSEL*/ - dev->reg.find_reg(0x5a).value |= REG_0x5A_RLCSEL; + dev->reg.init_reg(0x70, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x71, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x72, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x73, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below -/*STOPTIM*/ - dev->reg.find_reg(0x5e).value |= 0x2 << REG_0x5ES_STOPTIM; + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + 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); + } else { + for (unsigned addr = 0x74; addr <= 0x87; addr++) { + dev->reg.init_reg(addr, 0); + } + } - sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 1); + scanner_setup_sensor(*dev, sensor, dev->reg); // 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; @@ -523,70 +312,43 @@ gl841_init_registers (Genesys_Device * dev) 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; + 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); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + // specific scanner settings, clock and gpio first + 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); + dev->interface->write_register(REG_0x6C, 0x00); + dev->interface->write_register(REG_0x6D, 0x8f); + dev->interface->write_register(REG_0x6B, 0x0e); + dev->interface->write_register(REG_0x6B, 0x0e); + dev->interface->write_register(REG_0x6B, 0x0a); + dev->interface->write_register(REG_0x6B, 0x02); + 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; } - 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; + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; - // write them to analog frontend + // BUG: the following code does not make sense. The addresses are different than AFE_SET + // case 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)); @@ -611,11 +373,7 @@ static void gl841_set_ad_fe(Genesys_Device* dev, uint8_t set) return; } - if (set == AFE_INIT) - { - DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, - static_cast<unsigned>(dev->model->adc_id)); - + if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; // write them to analog frontend @@ -674,15 +432,11 @@ void CommandSetGl841::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, 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; + if (set == AFE_INIT) { + 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__); } @@ -712,71 +466,34 @@ void CommandSetGl841::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, } } -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; + reg->set8(0x3d, (feedl >> 16) & 0xf); + reg->set8(0x3e, (feedl >> 8) & 0xff); + reg->set8(0x3f, feedl & 0xff); + reg->find_reg(0x5e).value &= ~0xe0; - r->value &= ~0x20; + reg->set8(0x25, (scan_lines >> 16) & 0xf); + reg->set8(0x26, (scan_lines >> 8) & 0xff); + reg->set8(0x27, scan_lines & 0xff); - r->value &= ~0x40; + reg->set8(0x02, 0x00); - r = sanei_genesys_get_address (reg, 0x67); - r->value = 0x3f; + reg->set8(0x67, 0x3f); + reg->set8(0x68, 0x3f); - r = sanei_genesys_get_address (reg, 0x68); - r->value = 0x3f; + reg->set8(REG_STEPNO, 1); + reg->set8(REG_FASTNO, 1); - 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; + reg->set8(0x69, 1); + reg->set8(0x6a, 1); + reg->set8(0x5f, 1); } /** @brief write motor table frequency @@ -814,207 +531,122 @@ uint8_t *table; table=tdefault; } dev->interface->write_register(0x66, 0x00); - dev->interface->write_gamma(0x28, 0xc000, table, 128, - ScannerInterface::FLAG_SWAP_REGISTERS); + dev->interface->write_gamma(0x28, 0xc000, table, 128); 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) +static void gl841_init_motor_regs_feed(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set* reg, unsigned int feed_steps,/*1/base_ydpi*/ + ScanFlag flags) { - DBG_HELPER_ARGS(dbg, "feed_steps=%d, action=%d, flags=%x", feed_steps, action, - static_cast<unsigned>(flags)); - unsigned int fast_exposure = 0; + DBG_HELPER_ARGS(dbg, "feed_steps=%d, flags=%x", feed_steps, static_cast<unsigned>(flags)); + unsigned step_multiplier = 2; 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); + scanner_send_slope_table(dev, sensor, 0, table); + scanner_send_slope_table(dev, sensor, 1, table); + scanner_send_slope_table(dev, sensor, 2, table); + scanner_send_slope_table(dev, sensor, 3, table); + scanner_send_slope_table(dev, sensor, 4, table); } 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); - } + // FIXME: use proper scan session + ScanSession session; + session.params.yres = dev->motor.base_ydpi; + session.params.scan_method = dev->model->default_method; - 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; + const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); + if (fast_profile == nullptr) { + fast_profile = get_motor_profile_ptr(dev->motor.profiles, 0, session); } + auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, + *fast_profile); - 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; + // BUG: fast table is counted in base_ydpi / 4 + feedl = feed_steps - fast_table.table.size() * 2; use_fast_fed = 1; + if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { + use_fast_fed = false; + } -/* 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 - */ + reg->set8(0x3d, (feedl >> 16) & 0xf); + reg->set8(0x3e, (feedl >> 8) & 0xff); + reg->set8(0x3f, feedl & 0xff); + reg->find_reg(0x5e).value &= ~0xe0; - 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; + reg->set8(0x25, 0); + reg->set8(0x26, 0); + reg->set8(0x27, 0); + + reg->find_reg(0x02).value &= ~0x01; /*LONGCURV OFF*/ + reg->find_reg(0x02).value &= ~0x80; /*NOT_HOME OFF*/ + + reg->find_reg(0x02).value |= REG_0x02_MTRPWR; if (use_fast_fed) - r->value |= 0x08; + reg->find_reg(0x02).value |= 0x08; else - r->value &= ~0x08; + reg->find_reg(0x02).value &= ~0x08; - if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { - r->value |= 0x20; + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { + reg->find_reg(0x02).value |= 0x20; } else { - r->value &= ~0x20; + reg->find_reg(0x02).value &= ~0x20; } - r->value &= ~0x40; + reg->find_reg(0x02).value &= ~0x40; - if (has_flag(flags, MotorFlag::REVERSE)) { - r->value |= REG_0x02_MTRREV; + if (has_flag(flags, ScanFlag::REVERSE)) { + reg->find_reg(0x02).value |= REG_0x02_MTRREV; + } else { + reg->find_reg(0x02).value &= ~REG_0x02_MTRREV; } - gl841_send_slope_table(dev, 3, fast_table.table, 256); + scanner_send_slope_table(dev, sensor, 3, fast_table.table); - 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); + reg->set8(0x67, 0x3f); + reg->set8(0x68, 0x3f); + reg->set8(REG_STEPNO, 1); + reg->set8(REG_FASTNO, 1); + reg->set8(0x69, 1); + reg->set8(0x6a, fast_table.table.size() / step_multiplier); + reg->set8(0x5f, 1); } static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, - Genesys_Register_Set* reg, + const ScanSession& session, + Genesys_Register_Set* reg, const MotorProfile& motor_profile, 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) + ScanFlag 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_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags)); - unsigned int fast_exposure; + + unsigned step_multiplier = 2; + 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 @@ -1022,30 +654,31 @@ static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor 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); + // At least in LiDE 50, 60 the fast movement table is counted in full steps. + const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); + if (fast_profile == nullptr) { + fast_profile = &motor_profile; + } - auto back_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor, - scan_step_type, 0, scan_yres); + auto slow_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, + scan_exposure_time, step_multiplier, motor_profile); - if (feed_steps < (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) { + if (feed_steps < (slow_table.table.size() >> static_cast<unsigned>(motor_profile.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); + feed_steps = slow_table.table.size() >> static_cast<unsigned>(motor_profile.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); + auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, + *fast_profile); - unsigned max_fast_slope_steps_count = 1; - if (feed_steps > (slow_table.steps_count >> static_cast<unsigned>(scan_step_type)) + 2) { + unsigned max_fast_slope_steps_count = step_multiplier; + if (feed_steps > (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type)) + 2) { max_fast_slope_steps_count = (feed_steps - - (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) / 2; + (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type))) / 2; } - if (fast_table.steps_count > max_fast_slope_steps_count) { - fast_table.slice_steps(max_fast_slope_steps_count); + if (fast_table.table.size() > max_fast_slope_steps_count) { + fast_table.slice_steps(max_fast_slope_steps_count, step_multiplier); } /* fast fed special cases handling */ @@ -1056,8 +689,8 @@ static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor 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))) + else if (feed_steps < fast_table.table.size() * 2 + + (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type))) { use_fast_fed = 0; DBG(DBG_info, "%s: feed too short, slow move forced.\n", __func__); @@ -1071,113 +704,70 @@ static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor /*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; + (fast_table.table.back() << static_cast<unsigned>(fast_profile->step_type)) / 4 * + (feed_steps - fast_table.table.size()*2 - + (slow_table.table.size() >> static_cast<unsigned>(motor_profile.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; + (feed_steps - (slow_table.table.size() >> static_cast<unsigned>(motor_profile.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; + } - use_fast_fed = fast_time < slow_time; + if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { + use_fast_fed = false; } 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 = feed_steps - fast_table.table.size() * 2 - + (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type)); + } else if ((feed_steps << static_cast<unsigned>(motor_profile.step_type)) < slow_table.table.size()) { feedl = 0; } else { - feedl = (feed_steps << static_cast<unsigned>(scan_step_type)) - slow_table.steps_count; + feedl = (feed_steps << static_cast<unsigned>(motor_profile.step_type)) - slow_table.table.size(); } 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; + reg->set8(0x3d, (feedl >> 16) & 0xf); + reg->set8(0x3e, (feedl >> 8) & 0xff); + reg->set8(0x3f, feedl & 0xff); + reg->find_reg(0x5e).value &= ~0xe0; + reg->set8(0x25, (scan_lines >> 16) & 0xf); + reg->set8(0x26, (scan_lines >> 8) & 0xff); + reg->set8(0x27, scan_lines & 0xff); + reg->find_reg(0x02).value = REG_0x02_MTRPWR; + + if (has_flag(flags, ScanFlag::REVERSE)) { + reg->find_reg(0x02).value |= REG_0x02_MTRREV; + } else { + reg->find_reg(0x02).value &= ~REG_0x02_MTRREV; + } if (use_fast_fed) - r->value |= 0x08; + reg->find_reg(0x02).value |= 0x08; else - r->value &= ~0x08; + reg->find_reg(0x02).value &= ~0x08; - if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) - r->value |= 0x20; + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) + reg->find_reg(0x02).value |= 0x20; else - r->value &= ~0x20; + reg->find_reg(0x02).value &= ~0x20; - if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE)) { - r->value |= 0x40; + if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) { + reg->find_reg(0x02).value |= 0x40; } else { - r->value &= ~0x40; + reg->find_reg(0x02).value &= ~0x40; } - gl841_send_slope_table(dev, 0, slow_table.table, 256); - - gl841_send_slope_table(dev, 1, back_table.table, 256); + scanner_send_slope_table(dev, sensor, 0, slow_table.table); + scanner_send_slope_table(dev, sensor, 1, slow_table.table); + scanner_send_slope_table(dev, sensor, 2, slow_table.table); + scanner_send_slope_table(dev, sensor, 3, fast_table.table); + scanner_send_slope_table(dev, sensor, 4, fast_table.table); - 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); - } + gl841_write_freq(dev, scan_yres); /* now reg 0x21 and 0x24 are available, we can calculate reg 0x22 and 0x23, reg 0x60-0x62 and reg 0x63-0x65 @@ -1185,19 +775,18 @@ HOME_FREE: 3 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; + if (min_restep < slow_table.table.size() * 2 + 2) { + min_restep = slow_table.table.size() * 2 + 2; } /* steps of table 1*/ - if (min_restep < back_table.steps_count * 2 + 2) { - min_restep = back_table.steps_count * 2 + 2; + if (min_restep < slow_table.table.size() * 2 + 2) { + min_restep = slow_table.table.size() * 2 + 2; } /* steps of table 0*/ - r = sanei_genesys_get_address(reg, REG_FWDSTEP); - r->value = min_restep - slow_table.steps_count*2; + reg->set8(REG_FWDSTEP, min_restep - slow_table.table.size()*2); + /* steps of table 1*/ - r = sanei_genesys_get_address(reg, REG_BWDSTEP); - r->value = min_restep - back_table.steps_count*2; + reg->set8(REG_BWDSTEP, min_restep - slow_table.table.size()*2); /* for z1/z2: @@ -1214,64 +803,17 @@ HOME_FREE: 3 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; + reg->set24(REG_0x60, 0); + reg->set24(REG_0x63, 0); + reg->find_reg(REG_0x1E).value &= REG_0x1E_WDTIME; + reg->find_reg(REG_0x1E).value |= scan_dummy; + reg->set8(0x67, 0x3f | (static_cast<unsigned>(motor_profile.step_type) << 6)); + reg->set8(0x68, 0x3f | (static_cast<unsigned>(fast_profile->step_type) << 6)); + reg->set8(REG_STEPNO, slow_table.table.size() / step_multiplier); + reg->set8(REG_FASTNO, slow_table.table.size() / step_multiplier); + reg->set8(0x69, slow_table.table.size() / step_multiplier); + reg->set8(0x6a, fast_table.table.size() / step_multiplier); + reg->set8(0x5f, fast_table.table.size() / step_multiplier); } static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -1279,108 +821,99 @@ static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens 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; + if (session.params.xres <= 600) { + reg->find_reg(REG_0x6C).value &= ~0x80; } else { - r->value |= 0x80; + reg->find_reg(REG_0x6C).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; + if (session.params.xres <= 600) { + reg->find_reg(REG_0x6C).value &= ~0x40; + reg->find_reg(REG_0x6C).value |= 0x20; } else { - r->value &= ~0x20; - r->value |= 0x40; + reg->find_reg(REG_0x6C).value &= ~0x20; + reg->find_reg(REG_0x6C).value |= 0x40; } - } + } /* enable shading */ - r = sanei_genesys_get_address (reg, 0x01); - r->value |= REG_0x01_SCAN; + reg->find_reg(0x01).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; + has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { + reg->find_reg(0x01).value &= ~REG_0x01_DVDSET; } else { - r->value |= REG_0x01_DVDSET; + reg->find_reg(0x01).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; + reg->find_reg(0x03).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; + reg->set8(0x2e, 0x7f); + reg->set8(0x2f, 0x7f); /* monochrome / color scan */ - r = sanei_genesys_get_address (reg, 0x04); switch (session.params.depth) { case 8: - r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); + reg->find_reg(0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: - r->value &= ~REG_0x04_LINEART; - r->value |= REG_0x04_BITSET; + reg->find_reg(0x04).value &= ~REG_0x04_LINEART; + reg->find_reg(0x04).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); + reg->find_reg(0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (has_flag(session.params.flags, ScanFlag::ENABLE_LEDADD)) { - r->value |= 0x10; /* no filter */ + reg->find_reg(0x04).value |= 0x10; /* no filter */ } else if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: - r->value |= 0x14; + reg->find_reg(0x04).value |= 0x14; break; case ColorFilter::GREEN: - r->value |= 0x18; + reg->find_reg(0x04).value |= 0x18; break; case ColorFilter::BLUE: - r->value |= 0x1c; + reg->find_reg(0x04).value |= 0x1c; break; default: - r->value |= 0x10; + reg->find_reg(0x04).value |= 0x10; break; } } else { if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) { - r->value |= 0x22; /* slow color pixel by pixel */ + reg->find_reg(0x04).value |= 0x22; /* slow color pixel by pixel */ } else { - r->value |= 0x10; /* color pixel by pixel */ + reg->find_reg(0x04).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; + reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; if (has_flag(session.params.flags, ScanFlag::ENABLE_LEDADD)) { - r->value |= REG_0x87_LEDADD; + reg->find_reg(0x87).value |= REG_0x87_LEDADD; expr = reg->get16(REG_EXPR); expg = reg->get16(REG_EXPG); expb = reg->get16(REG_EXPB); @@ -1405,21 +938,14 @@ static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens } /* 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); + scanner_setup_sensor(*dev, sensor, dev->reg); + reg->set8(0x29, 255); /*<<<"magic" number, only suitable for cis*/ + reg->set16(REG_DPISET, sensor.register_dpiset); 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; + reg->set8(0x34, sensor.dummy_pixel); } static int @@ -1446,56 +972,17 @@ gl841_get_led_exposure(Genesys_Device * dev, const Genesys_Sensor& sensor) /** @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) +static int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor, + const MotorProfile& profile, float slope_dpi, + 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; + return sanei_genesys_exposure_time2(dev, profile, slope_dpi, + start + used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/ + led_exposure); } void CommandSetGl841::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -1511,34 +998,6 @@ void CommandSetGl841::init_regs_for_scan_session(Genesys_Device* dev, const Gene 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 @@ -1577,48 +1036,34 @@ dummy \ scanned lines 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); + const auto& motor_profile = get_motor_profile(dev->motor.profiles, 0, session); + + exposure_time = gl841_exposure_time(dev, sensor, motor_profile, slope_dpi, + session.pixel_startx, session.optical_pixels); 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);*/ +/* move = ((move + dummy) / (dummy + 1)) * (dummy + 1);*/ 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); + gl841_init_motor_regs_off(reg, session.optical_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); + gl841_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure_time, + slope_dpi, session.optical_line_count, dummy, move, + session.params.flags); } - dev->read_buffer.clear(); - dev->read_buffer.alloc(session.buffer_size_read); - - build_image_pipeline(dev, session); + setup_image_pipeline(*dev, session); dev->read_active = true; @@ -1634,32 +1079,62 @@ 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__); + DBG_HELPER(dbg); debug_dump(DBG_info, settings); -/* start */ - start = static_cast<int>(dev->model->x_offset); - start += static_cast<int>(settings.tl_x); + /* 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 + */ + float move = dev->model->y_offset; + move += dev->settings.tl_y; - start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH); + int move_dpi = dev->motor.base_ydpi; + move = static_cast<float>((move * move_dpi) / 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; + float start = dev->model->x_offset; + start += dev->settings.tl_x; + start = static_cast<float>((start * dev->settings.xres) / 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 + 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); return session; @@ -1709,7 +1184,7 @@ void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const 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; + dev->initial_regs.find_reg(0x6b).value &= ~REG_0x6B_GPO17; } set_fe(dev, sensor, AFE_POWER_SAVE); @@ -1741,13 +1216,13 @@ void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const 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; + dev->initial_regs.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; + dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO18; } if (dev->model->gpio_id == GpioId::DP665 @@ -1756,7 +1231,7 @@ void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const 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; + dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO17; } } @@ -1826,47 +1301,6 @@ void CommandSetGl841::set_powersaving(Genesys_Device* dev, int delay /* in minut 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); @@ -1886,7 +1320,6 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const 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; } @@ -1895,22 +1328,21 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const // FIXME: unused result scanner_read_status(*dev); - - gl841_stop_action(dev); + scanner_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); + gl841_init_motor_regs_feed(dev, sensor, &local_reg, 65536, ScanFlag::NONE); dev->interface->write_registers(local_reg); try { scanner_start_action(*dev, true); } catch (...) { - catch_all_exceptions(__func__, [&]() { gl841_stop_action(dev); }); + catch_all_exceptions(__func__, [&]() { scanner_stop_action(*dev); }); // restore original registers catch_all_exceptions(__func__, [&]() { @@ -1921,7 +1353,7 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const if (is_testing_mode()) { dev->interface->test_checkpoint("eject_document"); - gl841_stop_action(dev); + scanner_stop_action(*dev); return; } @@ -1936,10 +1368,9 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const { if (!gl841_get_paper_sensor(dev)) { - DBG(DBG_info, "%s: reached home position\n", __func__); - DBG(DBG_proc, "%s: finished\n", __func__); - break; - } + DBG(DBG_info, "%s: reached home position\n", __func__); + break; + } dev->interface->sleep_ms(100); --loop; } @@ -1948,16 +1379,15 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const { // 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); }); + catch_all_exceptions(__func__, [&](){ scanner_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); + feed_mm = dev->model->eject_feed; + if (dev->document) { + feed_mm += dev->model->post_scan; } sanei_genesys_read_feed_steps(dev, &init_steps); @@ -1981,11 +1411,22 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const ++loop; } - gl841_stop_action(dev); + scanner_stop_action(*dev); dev->document = false; } +void CommandSetGl841::update_home_sensor_gpio(Genesys_Device& dev) const +{ + if (dev.model->gpio_id == GpioId::CANON_LIDE_35) { + dev.interface->read_register(REG_0x6C); + dev.interface->write_register(REG_0x6C, dev.gpo.regs.get_value(0x6c)); + } + if (dev.model->gpio_id == GpioId::CANON_LIDE_80) { + dev.interface->read_register(REG_0x6B); + dev.interface->write_register(REG_0x6B, REG_0x6B_GPO18 | REG_0x6B_GPO17); + } +} void CommandSetGl841::load_document(Genesys_Device* dev) const { @@ -2064,8 +1505,6 @@ void CommandSetGl841::detect_document_end(Genesys_Device* dev) const 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); @@ -2092,6 +1531,21 @@ void CommandSetGl841::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens dev->interface->write_register(REG_0x6B, val); } + if (dev->model->model_id == ModelId::CANON_LIDE_50 || + dev->model->model_id == ModelId::CANON_LIDE_60) + { + if (dev->session.params.yres >= 1200) { + dev->interface->write_register(REG_0x6C, 0x82); + } else { + dev->interface->write_register(REG_0x6C, 0x02); + } + if (dev->session.params.yres >= 600) { + dev->interface->write_register(REG_0x6B, 0x01); + } else { + dev->interface->write_register(REG_0x6B, 0x03); + } + } + if (dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICPRO_3600) { local_reg.init_reg(0x03, reg->get8(0x03) | REG_0x03_LAMPPWR); } else { @@ -2123,439 +1577,53 @@ void CommandSetGl841::end_scan(Genesys_Device* dev, Genesys_Register_Set __sane_ DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); if (!dev->model->is_sheetfed) { - gl841_stop_action(dev); + scanner_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); - } + scanner_move_back_home(*dev, wait_until_home); } -// 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, ®s, 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; + DBG_HELPER(dbg); - unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); - unsigned factor = sensor.optical_res / resolution; + unsigned channels = 3; - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, dev->calib_channels, + unsigned resolution = sensor.shading_resolution; + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); - dev->calib_pixels = calib_sensor.sensor_pixels / factor; - + unsigned calib_lines = + static_cast<unsigned>(dev->model->y_size_calib_dark_white_mm * resolution / MM_PER_INCH); + unsigned starty = + static_cast<unsigned>(dev->model->y_offset_calib_dark_white_mm * dev->motor.base_ydpi / MM_PER_INCH); ScanSession session; session.params.xres = resolution; - session.params.yres = ydpi; + session.params.yres = resolution; session.params.startx = 0; session.params.starty = starty; - session.params.pixels = dev->calib_pixels; - session.params.lines = dev->calib_lines; + session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; + session.params.lines = calib_lines; session.params.depth = 16; - session.params.channels = dev->calib_channels; + 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::DISABLE_BUFFER_FULL_MOVE |*/ - ScanFlag::IGNORE_LINE_DISTANCE; + ScanFlag::DISABLE_GAMMA; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); - dev->interface->write_registers(regs); + dev->calib_session = session; } -// 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 { @@ -2581,216 +1649,7 @@ void CommandSetGl841::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor 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, ®s, 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, ®s, 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; + return scanner_led_calibration(*dev, sensor, regs); } /** @brief calibration for AD frontend devices @@ -2804,9 +1663,6 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& Genesys_Register_Set& regs) { DBG_HELPER(dbg); - int num_pixels; - int total_size; - int i; int average; int turn; int top; @@ -2818,14 +1674,12 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& return; } - unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); - unsigned factor = sensor.optical_res / resolution; + unsigned resolution = sensor.shading_resolution; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, dev->settings.scan_method); - num_pixels = calib_sensor.sensor_pixels / factor; - + unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; ScanSession session; session.params.xres = resolution; session.params.yres = dev->settings.yres; @@ -2841,14 +1695,15 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | - ScanFlag::IGNORE_LINE_DISTANCE; + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; compute_session(dev, session, calib_sensor); dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, ®s, session); - total_size = num_pixels * 3 * 2 * 1; - - std::vector<uint8_t> line(total_size); + // FIXME: we're reading twice as much data for no reason + std::size_t total_size = session.output_line_bytes * 2; + std::vector<uint8_t> line(total_size); dev->frontend.set_gain(0, 0); dev->frontend.set_gain(1, 0); @@ -2873,23 +1728,23 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& if (is_testing_mode()) { dev->interface->test_checkpoint("ad_fe_offset_calibration"); - gl841_stop_action(dev); + scanner_stop_action(*dev); return; } sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - gl841_stop_action (dev); - if (DBG_LEVEL >= DBG_data) { + scanner_stop_action(*dev); + if (dbg_log_image_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); + std::snprintf(fn, 30, "gl841_offset_%02d.tiff", turn); + write_tiff_file(fn, line.data(), 8, 3, num_pixels, 1); } /* search for minimal value */ average=0; - for(i=0;i<total_size;i++) + for (std::size_t i = 0; i < total_size; i++) { - average+=line[i]; + average += line[i]; } average/=total_size; DBG(DBG_data, "%s: average=%d\n", __func__, average); @@ -2919,8 +1774,6 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& /* 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? */ @@ -2928,39 +1781,32 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens 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]; + unsigned 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); + ad_fe_offset_calibration(dev, sensor, regs); + return; } /* offset calibration is always done in color mode */ - channels = 3; + unsigned channels = 3; - unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); - unsigned factor = sensor.optical_res / resolution; + unsigned resolution = sensor.shading_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.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = 1; session.params.depth = 16; session.params.channels = channels; @@ -2970,17 +1816,13 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | - ScanFlag::IGNORE_LINE_DISTANCE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET | ScanFlag::DISABLE_LAMP; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, 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...*/ @@ -3011,12 +1853,14 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens offl[2] = 0x00; turn = 0; + Image first_line; + bool acceptable = false; do { dev->interface->write_registers(regs); - for (j=0; j < channels; j++) { + for (unsigned j = 0; j < channels; j++) { off[j] = (offh[j]+offl[j])/2; dev->frontend.set_offset(j, off[j]); } @@ -3031,57 +1875,51 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens return; } - sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); + first_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes); - 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); - } + if (dbg_log_image_data()) { + char fn[30]; + std::snprintf(fn, 30, "gl841_offset1_%02d.tiff", turn); + write_tiff_file(fn, first_line); + } acceptable = true; - for (j = 0; j < channels; j++) - { - cmin[j] = 0; - cmax[j] = 0; + for (unsigned ch = 0; ch < channels; ch++) { + cmin[ch] = 0; + cmax[ch] = 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]++; - } + for (std::size_t x = 0; x < first_line.get_width(); x++) { + auto value = first_line.get_raw_channel(x, 0, ch); + if (value < 10) { + cmin[ch]++; + } + if (value > 65525) { + cmax[ch]++; + } + } /* 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 (dev->model->sensor_id == SensorId::CCD_DP685) { + cmin[ch] -= 20; + } - if (cmin[j] > num_pixels/100) { + if (cmin[ch] > first_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offl[0] = off[0]; else - offl[j] = off[j]; - } - if (cmax[j] > num_pixels/100) { + offl[ch] = off[ch]; + } + if (cmax[ch] > first_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offh[0] = off[0]; else - offh[j] = off[j]; - } - } + offh[ch] = off[ch]; + } + } 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]); @@ -3091,7 +1929,7 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens offl[2] = offl[1] = offl[0]; } - gl841_stop_action(dev); + scanner_stop_action(*dev); turn++; } while (!acceptable && turn < 100); @@ -3099,26 +1937,19 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens 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]; + for (unsigned ch = 0; ch < channels; ch++) { + off1[ch] = off[ch]; - min1[j] = 65536; + min1[ch] = 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; - } - } + for (std::size_t x = 0; x < first_line.get_width(); x++) { + auto value = first_line.get_raw_channel(x, 0, ch); + + if (min1[ch] > value && value >= 10) { + min1[ch] = value; + } + } + } offl[0] = off[0]; @@ -3126,64 +1957,59 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens offl[2] = off[0]; turn = 0; + Image second_line; do { - for (j=0; j < channels; j++) { + for (unsigned 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, ®s, true); - sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); + second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes); - 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); - } + if (dbg_log_image_data()) { + char fn[30]; + std::snprintf(fn, 30, "gl841_offset2_%02d.tiff", turn); + write_tiff_file(fn, second_line); + } acceptable = true; - for (j = 0; j < channels; j++) - { - cmin[j] = 0; - cmax[j] = 0; + for (unsigned ch = 0; ch < channels; ch++) { + cmin[ch] = 0; + cmax[ch] = 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]++; - } + for (std::size_t x = 0; x < second_line.get_width(); x++) { + auto value = second_line.get_raw_channel(x, 0, ch); - if (cmin[j] > num_pixels/100) { + if (value < 10) { + cmin[ch]++; + } + if (value > 65525) { + cmax[ch]++; + } + } + + if (cmin[ch] > second_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offl[0] = off[0]; else - offl[j] = off[j]; - } - if (cmax[j] > num_pixels/100) { + offl[ch] = off[ch]; + } + if (cmax[ch] > second_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offh[0] = off[0]; else - offh[j] = off[j]; - } - } + offh[ch] = off[ch]; + } + } 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]); @@ -3193,7 +2019,7 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens offl[2] = offl[1] = offl[0]; } - gl841_stop_action(dev); + scanner_stop_action(*dev); turn++; @@ -3202,26 +2028,19 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens 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]; + for (unsigned ch = 0; ch < channels; ch++) { + off2[ch] = off[ch]; - min2[j] = 65536; + min2[ch] = 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; - } - } + for (std::size_t x = 0; x < second_line.get_width(); x++) { + auto value = second_line.get_raw_channel(x, 0, ch); + + if (min2[ch] > value && value != 0) { + min2[ch] = value; + } + } + } 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]); @@ -3247,22 +2066,25 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens off=(min*(off1-off2)+min1*off2-off1*min2)/(min1-min2) */ - for (j = 0; j < channels; j++) - { - if (min2[j]-min1[j] == 0) { + for (unsigned ch = 0; ch < channels; ch++) { + if (min2[ch] - min1[ch] == 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]); + if (mintgt * (off1[ch] - off2[ch]) + min1[ch] * off2[ch] - min2[ch] * off1[ch] >= 0) { + off[ch] = 0x0000; + } else { + off[ch] = 0xffff; + } + } else { + off[ch] = (mintgt * (off1[ch] - off2[ch]) + min1[ch] * off2[ch] - min2[ch] * off1[ch])/(min1[ch]-min2[ch]); + } + if (off[ch] > 255) { + off[ch] = 255; + } + if (off[ch] < 0) { + off[ch] = 0; + } + dev->frontend.set_offset(ch, off[ch]); } DBG(DBG_info, "%s: final offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]); @@ -3297,171 +2119,13 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens 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, ®s, 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, ®s, 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); + scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } // 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 + Genesys_Register_Set* local_reg) const { DBG_HELPER(dbg); int num_pixels = 4 * 300; @@ -3475,51 +2139,34 @@ void CommandSetGl841::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Se dev->frontend.set_offset(1, 0x80); dev->frontend.set_offset(2, 0x80); + auto flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::SINGLE_LINE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + flags |= ScanFlag::USE_XPA; + } + ScanSession session; - session.params.xres = sensor.optical_res; + session.params.xres = sensor.full_resolution; 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.depth = dev->model->bpp_color_values.front(); + session.params.channels = 3; 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.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; + session.params.flags = flags; + 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); } /* @@ -3528,123 +2175,9 @@ static void sanei_gl841_repark_head(Genesys_Device* dev) */ void CommandSetGl841::init(Genesys_Device* dev) const { - size_t size; - - DBG_INIT (); + 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, ®s, 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, ®s, 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, ®s, true); - - regs = dev->reg; - - // Set powersaving(default = 15 minutes) - set_powersaving(dev, 15); - dev->already_initialized = true; + sanei_genesys_asic_init(dev); } void CommandSetGl841::update_hardware_sensors(Genesys_Scanner* s) const @@ -3676,225 +2209,6 @@ void CommandSetGl841::update_hardware_sensors(Genesys_Scanner* s) const } } -/** @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. @@ -3903,42 +2217,30 @@ void CommandSetGl841::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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; + uint32_t length, x, pixels, i; uint8_t *ptr,*src; /* old method if no SHDAREA */ if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) == 0) { + // Note that this requires the sensor pixel offset to be exactly the same as to start + // reading from dummy_pixel + 1 position. 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); + + // turn pixel value into bytes 2x16 bits words + pixels = dev->session.pixel_endx - dev->session.pixel_startx; + pixels *= 4; + + // shading pixel begin is start pixel minus start pixel during shading + // calibration. Currently only cases handled are full and half ccd resolution. + unsigned beginpixel = dev->session.params.startx * dev->session.optical_resolution / + dev->session.params.xres; + beginpixel *= 4; + beginpixel /= sensor.shading_factor; dev->interface->record_key_value("shading_offset", std::to_string(beginpixel)); dev->interface->record_key_value("shading_pixels", std::to_string(pixels)); @@ -3962,7 +2264,7 @@ void CommandSetGl841::send_shading_data(Genesys_Device* dev, const Genesys_Senso for(x=0;x<pixels;x+=4) { /* coefficient source */ - src=data+x+beginpixel+i*length; + src = data + x + beginpixel + i * length; ptr[0]=src[0]; ptr[1]=src[1]; ptr[2]=src[2]; @@ -3988,22 +2290,29 @@ 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"); -} + // reset ASIC in case of cold boot + if (cold) { + dev->interface->write_register(0x0e, 0x01); + dev->interface->write_register(0x0e, 0x00); + } -std::unique_ptr<CommandSet> create_gl841_cmd_set() -{ - return std::unique_ptr<CommandSet>(new CommandSetGl841{}); + gl841_init_registers(dev); + + // Write initial registers + dev->interface->write_registers(dev->reg); + + // FIXME: 0x0b is not set, but on all other backends we do set it + // dev->reg.remove_reg(0x0b); + + if (dev->model->model_id == ModelId::CANON_LIDE_60) { + dev->interface->write_0x8c(0x10, 0xa4); + } + + // FIXME: we probably don't need this + const auto& sensor = sanei_genesys_find_sensor_any(dev); + dev->cmd_set->set_fe(dev, sensor, AFE_INIT); } } // namespace gl841 diff --git a/backend/genesys/gl841.h b/backend/genesys/gl841.h index 5e24249..c9f15ee 100644 --- a/backend/genesys/gl841.h +++ b/backend/genesys/gl841.h @@ -42,7 +42,7 @@ */ #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h" #ifndef BACKEND_GENESYS_GL841_H #define BACKEND_GENESYS_GL841_H @@ -50,7 +50,7 @@ namespace genesys { namespace gl841 { -class CommandSetGl841 : public CommandSet +class CommandSetGl841 : public CommandSetCommon { public: ~CommandSetGl841() override = default; @@ -60,17 +60,11 @@ public: 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; + 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; @@ -86,8 +80,6 @@ public: 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; @@ -103,17 +95,14 @@ public: void update_hardware_sensors(struct Genesys_Scanner* s) const override; + 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; diff --git a/backend/genesys/gl841_registers.h b/backend/genesys/gl841_registers.h index 8e0c204..2fac278 100644 --- a/backend/genesys/gl841_registers.h +++ b/backend/genesys/gl841_registers.h @@ -224,10 +224,12 @@ 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_ZIMOD = 0x1f; static constexpr RegMask REG_0x61_Z1MOD = 0xff; static constexpr RegMask REG_0x62_Z1MOD = 0xff; +static constexpr RegAddr REG_0x63 = 0x63; static constexpr RegMask REG_0x63_Z2MOD = 0x1f; static constexpr RegMask REG_0x64_Z2MOD = 0xff; static constexpr RegMask REG_0x65_Z2MOD = 0xff; diff --git a/backend/genesys/gl842.cpp b/backend/genesys/gl842.cpp new file mode 100644 index 0000000..d5bebe5 --- /dev/null +++ b/backend/genesys/gl842.cpp @@ -0,0 +1,1066 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr> + Copyright (C) 2020 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. +*/ + +#define DEBUG_DECLARE_ONLY + +#include "gl842_registers.h" +#include "gl842.h" +#include "test_settings.h" + +#include <string> +#include <vector> + +namespace genesys { +namespace gl842 { + +static void gl842_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 + // gl842_setup_sensor(). + + DBG_HELPER(dbg); + + dev.reg.clear(); + + if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + dev.reg.init_reg(0x01, 0x00); + dev.reg.init_reg(0x02, 0x78); + dev.reg.init_reg(0x03, 0xbf); + dev.reg.init_reg(0x04, 0x22); + dev.reg.init_reg(0x05, 0x48); + + dev.reg.init_reg(0x06, 0xb8); + + dev.reg.init_reg(0x07, 0x00); + dev.reg.init_reg(0x08, 0x00); + dev.reg.init_reg(0x09, 0x00); + dev.reg.init_reg(0x0a, 0x00); + dev.reg.init_reg(0x0d, 0x01); + } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { + dev.reg.init_reg(0x01, 0x82); + dev.reg.init_reg(0x02, 0x10); + dev.reg.init_reg(0x03, 0x60); + dev.reg.init_reg(0x04, 0x10); + dev.reg.init_reg(0x05, 0x8c); + + dev.reg.init_reg(0x06, 0x18); + + //dev.reg.init_reg(0x07, 0x00); + dev.reg.init_reg(0x08, 0x00); + dev.reg.init_reg(0x09, 0x21); + dev.reg.init_reg(0x0a, 0x00); + dev.reg.init_reg(0x0d, 0x00); + } + + dev.reg.init_reg(0x10, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev.reg.init_reg(0x11, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev.reg.init_reg(0x12, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev.reg.init_reg(0x13, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev.reg.init_reg(0x14, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev.reg.init_reg(0x15, 0x00); // exposure, overwritten in scanner_setup_sensor() below + + // CCD signal settings. + dev.reg.init_reg(0x16, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x17, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x18, 0x00); // SENSOR_DEF + + // EXPDMY[0:7]: Exposure time of dummy lines. + dev.reg.init_reg(0x19, 0x00); // SENSOR_DEF + + // Various CCD clock settings. + dev.reg.init_reg(0x1a, 0x00); // SENSOR_DEF + if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + dev.reg.init_reg(0x1b, 0x00); // SENSOR_DEF + } + dev.reg.init_reg(0x1c, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x1d, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x1e, 0x10); // WDTIME, LINESEL: setup during sensor and motor setup + + if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + dev.reg.init_reg(0x1f, 0x01); + dev.reg.init_reg(0x20, 0x27); // BUFSEL: buffer full condition + } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { + dev.reg.init_reg(0x1f, 0x02); + dev.reg.init_reg(0x20, 0x02); // BUFSEL: buffer full condition + } + + dev.reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup + dev.reg.init_reg(0x22, 0x10); // FWDSTEP: set during motor setup + dev.reg.init_reg(0x23, 0x10); // BWDSTEP: set during motor setup + dev.reg.init_reg(0x24, 0x10); // FASTNO: set during motor setup + dev.reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup + dev.reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup + dev.reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup + + dev.reg.init_reg(0x29, 0xff); // LAMPPWM + + dev.reg.init_reg(0x2c, 0x02); // DPISET: set during sensor setup + dev.reg.init_reg(0x2d, 0x58); // DPISET: set during sensor setup + + dev.reg.init_reg(0x2e, 0x80); // BWHI: black/white low threshdold + dev.reg.init_reg(0x2f, 0x80); // BWLOW: black/white low threshold + + dev.reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup + dev.reg.init_reg(0x31, 0x49); // STRPIXEL: set during sensor setup + dev.reg.init_reg(0x32, 0x53); // ENDPIXEL: set during sensor setup + dev.reg.init_reg(0x33, 0xb9); // ENDPIXEL: set during sensor setup + + dev.reg.init_reg(0x34, 0x13); // DUMMY: SENSOR_DEF + dev.reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup + dev.reg.init_reg(0x36, 0x40); // MAXWD: set during scan setup + dev.reg.init_reg(0x37, 0x00); // MAXWD: set during scan setup + dev.reg.init_reg(0x38, 0x2a); // LPERIOD: SENSOR_DEF + dev.reg.init_reg(0x39, 0xf8); // LPERIOD: SENSOR_DEF + dev.reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup + dev.reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup + dev.reg.init_reg(0x3f, 0x01); // FEEDL: set during motor setup + + dev.reg.init_reg(0x52, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x53, 0x00); // 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, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x59, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x5a, 0x00); // SENSOR_DEF + + if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + dev.reg.init_reg(0x5e, 0x01); // DECSEL, STOPTIM + } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { + dev.reg.init_reg(0x5e, 0x41); // DECSEL, STOPTIM + dev.reg.init_reg(0x5d, 0x20); + } + dev.reg.init_reg(0x5f, 0x10); // FMOVDEC: set during motor setup + + dev.reg.init_reg(0x60, 0x00); // Z1MOD: overwritten during motor setup + dev.reg.init_reg(0x61, 0x00); // Z1MOD: overwritten during motor setup + dev.reg.init_reg(0x62, 0x00); // Z1MOD: overwritten during motor setup + dev.reg.init_reg(0x63, 0x00); // Z2MOD: overwritten during motor setup + dev.reg.init_reg(0x64, 0x00); // Z2MOD: overwritten during motor setup + dev.reg.init_reg(0x65, 0x00); // Z2MOD: overwritten during motor setup + + if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + dev.reg.init_reg(0x67, 0x7f); // STEPSEL, MTRPWM: partially overwritten during motor setup + dev.reg.init_reg(0x68, 0x7f); // FSTPSEL, FASTPWM: partially overwritten during motor setup + } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { + dev.reg.init_reg(0x66, 0x00); // PHFREQ + dev.reg.init_reg(0x67, 0x40); // STEPSEL, MTRPWM: partially overwritten during motor setup + dev.reg.init_reg(0x68, 0x40); // FSTPSEL, FASTPWM: partially overwritten during motor setup + } + dev.reg.init_reg(0x69, 0x10); // FSHDEC: overwritten during motor setup + dev.reg.init_reg(0x6a, 0x10); // FMOVNO: overwritten during motor setup + + // 0x6b, 0x6c, 0x6d, 0x6e, 0x6f - set according to gpio tables. See gl842_init_gpio. + + dev.reg.init_reg(0x70, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x71, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x72, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x73, 0x00); // SENSOR_DEF + 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, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x7a, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x7b, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x7c, 0x00); // SENSOR_DEF + dev.reg.init_reg(0x7d, 0x00); // SENSOR_DEF + + // 0x7e - set according to gpio tables. See gl842_init_gpio. + + dev.reg.init_reg(0x7f, 0x00); // SENSOR_DEF + + // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for + // moving in various situations. + dev.reg.init_reg(0x80, 0x00); // MOTOR_PROFILE + + if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + 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); + } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { + dev.reg.init_reg(0x7e, 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, 0x00); + dev.reg.init_reg(0x88, 0x00); + dev.reg.init_reg(0x89, 0x00); + } + + const auto& sensor = sanei_genesys_find_sensor_any(&dev); + sanei_genesys_set_dpihw(dev.reg, sensor.register_dpihw); + + scanner_setup_sensor(dev, sensor, dev.reg); +} + +// Set values of analog frontend +void CommandSetGl842::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; + + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; + } + + // 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 || dev->model->model_id == ModelId::CANON_LIDE_90) { + for (const auto& reg : dev->frontend.regs) { + dev->interface->write_fe_register(reg.address, reg.value); + } + return; + } + if (fe_type != 0) { + throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type); + } + + for (unsigned i = 1; i <= 3; i++) { + 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 (unsigned i = 0; i < 3; i++) { + dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); + } + + for (unsigned i = 0; i < 3; i++) { + dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); + } +} + +static void gl842_init_motor_regs_scan(Genesys_Device* dev, + const Genesys_Sensor& sensor, + const ScanSession& session, + Genesys_Register_Set* reg, + const MotorProfile& motor_profile, + unsigned int exposure, + unsigned scan_yres, + unsigned int scan_lines, + unsigned int scan_dummy, + unsigned int feed_steps, + ScanFlag flags) +{ + 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)); + + unsigned step_multiplier = 2; + bool use_fast_fed = false; + + if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) { + use_fast_fed = true; + } + if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { + use_fast_fed = false; + } + + reg->set24(REG_LINCNT, scan_lines); + + reg->set8(REG_0x02, 0); + sanei_genesys_set_motor_power(*reg, true); + + std::uint8_t reg02 = reg->get8(REG_0x02); + if (use_fast_fed) { + reg02 |= REG_0x02_FASTFED; + } else { + reg02 &= ~REG_0x02_FASTFED; + } + + // in case of automatic go home, move until home sensor + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { + reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; + } + + // disable backtracking if needed + if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || + (scan_yres >= 2400) || + (scan_yres >= sensor.full_resolution)) + { + reg02 |= REG_0x02_ACDCDIS; + } + + if (has_flag(flags, ScanFlag::REVERSE)) { + reg02 |= REG_0x02_MTRREV; + } else { + reg02 &= ~REG_0x02_MTRREV; + } + reg->set8(REG_0x02, reg02); + + // scan and backtracking slope table + auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure, + step_multiplier, motor_profile); + + scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); + + reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); + + // fast table + const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); + if (fast_profile == nullptr) { + fast_profile = &motor_profile; + } + + auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, + *fast_profile); + + scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); + scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); + + reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); + + if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { + std::uint8_t vref = 0; + vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; + vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; + vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; + vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; + reg->set8(REG_0x80, vref); + } + + // substract acceleration distance from feedl + unsigned feedl = feed_steps; + feedl <<= static_cast<unsigned>(motor_profile.step_type); + + unsigned dist = scan_table.table.size() / step_multiplier; + + if (use_fast_fed) { + dist += (fast_table.table.size() / step_multiplier) * 2; + } + + // make 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); + + // doesn't seem to matter that much + std::uint32_t z1, z2; + sanei_genesys_calculate_zmod(use_fast_fed, + exposure, + scan_table.table, + scan_table.table.size() / step_multiplier, + feedl, + scan_table.table.size() / step_multiplier, + &z1, + &z2); + if (scan_yres > 600) { + z1 = 0; + z2 = 0; + } + + reg->set24(REG_Z1MOD, z1); + reg->set24(REG_Z2MOD, z2); + + reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); + + reg->set8_mask(REG_0x67, static_cast<unsigned>(motor_profile.step_type) << REG_0x67S_STEPSEL, + REG_0x67_STEPSEL); + reg->set8_mask(REG_0x68, static_cast<unsigned>(fast_profile->step_type) << REG_0x68S_FSTPSEL, + REG_0x68_FSTPSEL); + + // steps for STOP table + reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); +} + +static void gl842_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set* reg, unsigned int exposure, + const ScanSession& session) +{ + DBG_HELPER(dbg); + + scanner_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); + if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || + has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || + session.use_host_side_calib) + { + reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; + + } else { + reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; + } + + bool use_shdarea = true; + + if (use_shdarea) { + reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; + } else { + reg->find_reg(REG_0x01).value &= ~REG_0x01_SHDAREA; + } + + if (dev->model->model_id == ModelId::CANON_8600F) { + reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB; + } else { + reg->find_reg(REG_0x03).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 + reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; + if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { + reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; + } + reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); + + // BW threshold + reg->set8(REG_0x2E, 0x7f); + reg->set8(REG_0x2F, 0x7f); + + // monochrome / color scan parameters + std::uint8_t reg04 = reg->get8(REG_0x04); + reg04 = reg04 & REG_0x04_FESET; + + switch (session.params.depth) { + case 8: + break; + case 16: + reg04 |= REG_0x04_BITSET; + break; + } + + if (session.params.channels == 1) { + switch (session.params.color_filter) { + case ColorFilter::RED: reg04 |= 0x14; break; + case ColorFilter::BLUE: reg04 |= 0x1c; break; + case ColorFilter::GREEN: reg04 |= 0x18; break; + default: + break; // should not happen + } + } else { + switch (dev->frontend.layout.type) { + case FrontendType::WOLFSON: + // pixel by pixel + reg04 |= 0x10; + break; + case FrontendType::ANALOG_DEVICES: + // slow color pixel by pixel + reg04 |= 0x20; + break; + default: + throw SaneException("Invalid frontend type %d", + static_cast<unsigned>(dev->frontend.layout.type)); + } + } + + reg->set8(REG_0x04, reg04); + + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, + session.params.channels, + session.params.scan_method); + sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_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; + } + + reg->set16(REG_DPISET, sensor.register_dpiset); + + reg->set16(REG_STRPIXEL, session.pixel_startx); + reg->set16(REG_ENDPIXEL, session.pixel_endx); + + if (dev->model->is_cis) { + reg->set24(REG_MAXWD, session.output_line_bytes_raw * session.params.channels); + } else { + reg->set24(REG_MAXWD, session.output_line_bytes_raw); + } + + unsigned tgtime = exposure / 65536 + 1; + reg->set16(REG_LPERIOD, exposure / tgtime); + + reg->set8(REG_DUMMY, sensor.dummy_pixel); +} + +void CommandSetGl842::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(); + + // we enable true gray for cis scanners only, and just when doing scan since color calibration + // is OK for this mode + + int dummy = 0; + + /* slope_dpi */ + /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ + int slope_dpi = 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); + + int exposure = sensor.exposure_lperiod; + if (exposure < 0) { + throw std::runtime_error("Exposure not defined in sensor definition"); + } + if (dev->model->model_id == ModelId::CANON_LIDE_90) { + exposure *= 2; + } + const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session); + + // now _LOGICAL_ optical values used are known, setup registers + gl842_init_optical_regs_scan(dev, sensor, reg, exposure, session); + gl842_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi, + session.optical_line_count, dummy, session.params.starty, + session.params.flags); + + setup_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; +} + +ScanSession CommandSetGl842::calculate_scan_session(const Genesys_Device* dev, + const Genesys_Sensor& sensor, + const Genesys_Settings& settings) const +{ + DBG_HELPER(dbg); + debug_dump(DBG_info, settings); + + ScanFlag flags = ScanFlag::NONE; + + float move = 0.0f; + if (settings.scan_method == ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + // note: scanner_move_to_ta() function has already been called and the sensor is at the + // transparency adapter + if (!dev->ignore_offsets) { + move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; + } + flags |= ScanFlag::USE_XPA; + } else { + if (!dev->ignore_offsets) { + move = dev->model->y_offset; + } + } + + move += settings.tl_y; + + int move_dpi = dev->motor.base_ydpi; + move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + + float start = 0.0f; + if (settings.scan_method==ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + start = dev->model->x_offset_ta; + } else { + start = dev->model->x_offset; + } + start = start + settings.tl_x; + + start = static_cast<float>((start * settings.xres) / 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 = settings.scan_method; + session.params.scan_mode = settings.scan_mode; + session.params.color_filter = settings.color_filter; + session.params.flags = flags; + compute_session(dev, session, sensor); + + return session; +} + +void CommandSetGl842::save_power(Genesys_Device* dev, bool enable) const +{ + (void) dev; + DBG_HELPER_ARGS(dbg, "enable = %d", enable); +} + +void CommandSetGl842::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const +{ + (void) dev; + DBG_HELPER_ARGS(dbg, "delay = %d", delay); +} + +void CommandSetGl842::eject_document(Genesys_Device* dev) const +{ + (void) dev; + DBG_HELPER(dbg); +} + + +void CommandSetGl842::load_document(Genesys_Device* dev) const +{ + DBG_HELPER(dbg); + (void) dev; +} + +void CommandSetGl842::detect_document_end(Genesys_Device* dev) const +{ + DBG_HELPER(dbg); + (void) dev; + throw SaneException(SANE_STATUS_UNSUPPORTED); +} + +// Send the low-level scan command +void CommandSetGl842::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set* reg, bool start_motor) const +{ + DBG_HELPER(dbg); + (void) sensor; + + if (reg->state.is_xpa_on && reg->state.is_lamp_on && + !has_flag(dev->model->flags, ModelFlag::TA_NO_SECONDARY_LAMP)) + { + dev->cmd_set->set_xpa_lamp_power(*dev, true); + } + if (reg->state.is_xpa_on && !has_flag(dev->model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)) { + dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); + } + + if (dev->model->model_id == ModelId::CANON_LIDE_90) { + if (has_flag(dev->session.params.flags, ScanFlag::REVERSE)) { + dev->interface->write_register(REG_0x6B, 0x01); + dev->interface->write_register(REG_0x6C, 0x02); + } else { + dev->interface->write_register(REG_0x6B, 0x03); + switch (dev->session.params.xres) { + case 150: dev->interface->write_register(REG_0x6C, 0x74); break; + case 300: dev->interface->write_register(REG_0x6C, 0x38); break; + case 600: dev->interface->write_register(REG_0x6C, 0x1c); break; + case 1200: dev->interface->write_register(REG_0x6C, 0x2c); break; + case 2400: dev->interface->write_register(REG_0x6C, 0x0c); break; + default: + break; + } + } + dev->interface->sleep_ms(100); + } + + scanner_clear_scan_and_feed_counts(*dev); + + // enable scan and motor + std::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); + + switch (reg->state.motor_mode) { + case MotorMode::PRIMARY: { + if (reg->state.is_motor_on) { + dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); + } + break; + } + case MotorMode::PRIMARY_AND_SECONDARY: { + if (reg->state.is_motor_on) { + dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); + dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); + } + break; + } + case MotorMode::SECONDARY: { + if (reg->state.is_motor_on) { + dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); + } + break; + } + } +} + +void CommandSetGl842::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, + bool check_stop) const +{ + DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); + + if (reg->state.is_xpa_on) { + dev->cmd_set->set_xpa_lamp_power(*dev, false); + } + + if (!dev->model->is_sheetfed) { + scanner_stop_action(*dev); + } +} + +void CommandSetGl842::move_back_home(Genesys_Device* dev, bool wait_until_home) const +{ + scanner_move_back_home(*dev, wait_until_home); +} + +void CommandSetGl842::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const +{ + DBG_HELPER(dbg); + int move; + + float calib_size_mm = 0; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + calib_size_mm = dev->model->y_size_calib_ta_mm; + } else { + calib_size_mm = dev->model->y_size_calib_mm; + } + + unsigned resolution = sensor.shading_resolution; + + unsigned channels = 3; + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, + dev->settings.scan_method); + + unsigned calib_pixels = 0; + unsigned calib_pixels_offset = 0; + + if (should_calibrate_only_active_area(*dev, dev->settings)) { + float offset = dev->model->x_offset_ta; + // FIXME: we should use resolution here + offset = static_cast<float>((offset * dev->settings.xres) / MM_PER_INCH); + + float size = dev->model->x_size_ta; + size = static_cast<float>((size * dev->settings.xres) / MM_PER_INCH); + + calib_pixels_offset = static_cast<std::size_t>(offset); + calib_pixels = static_cast<std::size_t>(size); + } else { + calib_pixels_offset = 0; + calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; + } + + ScanFlag flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::DISABLE_BUFFER_FULL_MOVE; + + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + // note: scanner_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); + unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH); + + ScanSession session; + session.params.xres = resolution; + session.params.yres = resolution; + session.params.startx = calib_pixels_offset; + session.params.starty = move; + session.params.pixels = calib_pixels; + session.params.lines = calib_lines; + 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; + compute_session(dev, session, calib_sensor); + + init_regs_for_scan_session(dev, calib_sensor, ®s, session); + + dev->calib_session = session; +} + +void CommandSetGl842::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const +{ + DBG_HELPER(dbg); + + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) + return; // No gamma on this model + + unsigned size = 256; + + 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 (unsigned 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); +} + +SensorExposure CommandSetGl842::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const +{ + return scanner_led_calibration(*dev, sensor, regs); +} + +void CommandSetGl842::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const +{ + scanner_offset_calibration(*dev, sensor, regs); +} + +void CommandSetGl842::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs, int dpi) const +{ + scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); +} + +void CommandSetGl842::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set* reg) const +{ + DBG_HELPER(dbg); + (void) sensor; + + unsigned channels = 3; + unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method) + .get_nearest_resolution_x(600); + + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, + dev->settings.scan_method); + unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH / 2; + + *reg = dev->reg; + + auto flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::SINGLE_LINE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; + 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 = (num_pixels / 2) * resolution / calib_sensor.full_resolution; + session.params.starty = 0; + session.params.pixels = num_pixels; + session.params.lines = 1; + session.params.depth = dev->model->bpp_color_values.front(); + 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); + + init_regs_for_scan_session(dev, calib_sensor, reg, session); + + sanei_genesys_set_motor_power(*reg, false); +} + +static void gl842_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); + }); +} + +void CommandSetGl842::asic_boot(Genesys_Device* dev, bool cold) const +{ + DBG_HELPER(dbg); + + if (cold) { + dev->interface->write_register(0x0e, 0x01); + dev->interface->write_register(0x0e, 0x00); + } + + // setup initial register values + gl842_init_registers(*dev); + dev->interface->write_registers(dev->reg); + + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + uint8_t data[32] = { + 0xd0, 0x38, 0x07, 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, 0x010a00, data, 32); + } + + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + dev->interface->write_0x8c(0x10, 0x94); + } + if (dev->model->model_id == ModelId::CANON_LIDE_90) { + dev->interface->write_0x8c(0x10, 0xd4); + } + + // set RAM read address + dev->interface->write_register(REG_0x2A, 0x00); + dev->interface->write_register(REG_0x2B, 0x00); + + // setup gpio + gl842_init_gpio(dev); + dev->interface->sleep_ms(100); +} + +void CommandSetGl842::init(Genesys_Device* dev) const +{ + DBG_INIT(); + DBG_HELPER(dbg); + + sanei_genesys_asic_init(dev); +} + +void CommandSetGl842::update_hardware_sensors(Genesys_Scanner* s) const +{ + DBG_HELPER(dbg); + (void) s; +} + +void CommandSetGl842::update_home_sensor_gpio(Genesys_Device& dev) const +{ + DBG_HELPER(dbg); + if (dev.model->model_id == ModelId::CANON_LIDE_90) { + std::uint8_t val = dev.interface->read_register(REG_0x6C); + val |= 0x02; + dev.interface->write_register(REG_0x6C, val); + } +} + +/** + * Send shading calibration data. The buffer is considered to always hold values + * for all the channels. + */ +void CommandSetGl842::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, + uint8_t* data, int size) const +{ + DBG_HELPER(dbg); + + int offset = 0; + unsigned length = size; + + if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { + offset = dev->session.params.startx * sensor.shading_resolution / + dev->session.params.xres; + + length = dev->session.output_pixels * sensor.shading_resolution / + dev->session.params.xres; + + offset += sensor.shading_pixel_offset; + + // 16 bit words, 2 words per color, 3 color channels + length *= 2 * 2 * 3; + offset *= 2 * 2 * 3; + } else { + offset += sensor.shading_pixel_offset * 2 * 2 * 3; + } + + dev->interface->record_key_value("shading_offset", std::to_string(offset)); + dev->interface->record_key_value("shading_length", std::to_string(length)); + + std::vector<uint8_t> final_data(length, 0); + + unsigned count = 0; + if (offset < 0) { + count += (-offset); + length -= (-offset); + offset = 0; + } + if (static_cast<int>(length) + offset > static_cast<int>(size)) { + length = size - offset; + } + + for (unsigned i = 0; i < length; i++) { + final_data[count++] = data[offset + i]; + count++; + } + + dev->interface->write_buffer(0x3c, 0, final_data.data(), count); +} + +bool CommandSetGl842::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const +{ + (void) dev; + return true; +} + +void CommandSetGl842::wait_for_motor_stop(Genesys_Device* dev) const +{ + (void) dev; +} + +} // namespace gl842 +} // namespace genesys diff --git a/backend/genesys/gl842.h b/backend/genesys/gl842.h new file mode 100644 index 0000000..288d29c --- /dev/null +++ b/backend/genesys/gl842.h @@ -0,0 +1,128 @@ +/* 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_common.h" + +#ifndef BACKEND_GENESYS_GL842_H +#define BACKEND_GENESYS_GL842_H + +namespace genesys { +namespace gl842 { + +class CommandSetGl842 : public CommandSetCommon +{ +public: + ~CommandSetGl842() 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) 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_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 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 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 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 gl842 +} // namespace genesys + +#endif // BACKEND_GENESYS_GL842_H diff --git a/backend/genesys/gl842_registers.h b/backend/genesys/gl842_registers.h new file mode 100644 index 0000000..b6934ce --- /dev/null +++ b/backend/genesys/gl842_registers.h @@ -0,0 +1,285 @@ +/* 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_gl842_REGISTERS_H +#define BACKEND_GENESYS_gl842_REGISTERS_H + +#include <cstdint> + +namespace genesys { +namespace gl842 { + +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_M15DRAM = 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 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_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 RegAddr REG_0x0D = 0x0d; +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 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 RegAddr REG_0x1A = 0x1a; +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 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_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_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 RegShift REG_0x68S_FSTPSEL = 6; +static constexpr RegMask REG_0x68_FSTPSEL = 0xc0; + +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 RegAddr REG_Z1MOD = 0x60; +static constexpr RegAddr REG_Z2MOD = 0x63; + +static constexpr RegAddr REG_0x6C = 0x6c; +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_0x80 = 0x80; +static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; +static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; +static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; +static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; +static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; +static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; +static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; +static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; + +static constexpr RegMask REG_0x87_LEDADD = 0x04; + +} // namespace gl842 +} // namespace genesys + +#endif // BACKEND_GENESYS_gl842_REGISTERS_H diff --git a/backend/genesys/gl843.cpp b/backend/genesys/gl843.cpp index f83ac8d..8233bde 100644 --- a/backend/genesys/gl843.cpp +++ b/backend/genesys/gl843.cpp @@ -54,60 +54,18 @@ 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); + switch (regs->get8(REG_0x9D) & 0x0c) { + case 0x04: return 2; + case 0x08: return 4; + default: return 1; } - 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. @@ -118,9 +76,9 @@ static void gl843_setup_sensor(Genesys_Device* dev, const Genesys_Sensor& sensor 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(). + // Within this function SENSOR_DEF marker documents that a register is part + // of the sensors definition and the actual value is set in + // scanner_setup_sensor(). // 0x6c, 0x6d, 0x6e, 0x6f, 0xa6, 0xa7, 0xa8, 0xa9 are defined in the Gpo sensor struct @@ -158,8 +116,16 @@ gl843_init_registers (Genesys_Device * dev) dev->reg.init_reg(0x05, 0x08); } + auto initial_scan_method = dev->model->default_method; + if (dev->model->model_id == ModelId::CANON_4400F || + dev->model->model_id == ModelId::CANON_8600F) + { + initial_scan_method = ScanMethod::TRANSPARENCY; + } const auto& sensor = sanei_genesys_find_sensor_any(dev); - sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, + 3, initial_scan_method); + sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); // TODO: on 8600F the windows driver turns off GAIN4 which is recommended dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */ @@ -402,11 +368,11 @@ gl843_init_registers (Genesys_Device * dev) // 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); + dev->reg.init_reg(0x67, 0x7f); // MOTOR_PROFILE // 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); + dev->reg.init_reg(0x68, 0x7f); // MOTOR_PROFILE if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x67, 0x80); @@ -415,17 +381,11 @@ gl843_init_registers (Genesys_Device * dev) // 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); - } + dev->reg.init_reg(0x69, 0x01); // MOTOR_PROFILE // 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); - } + dev->reg.init_reg(0x6a, 0x04); // MOTOR_PROFILE // GPIO-related register bits dev->reg.init_reg(0x6b, 0x30); @@ -516,7 +476,7 @@ gl843_init_registers (Genesys_Device * dev) // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for // moving in various situations. - dev->reg.init_reg(0x80, 0x00); + dev->reg.init_reg(0x80, 0x00); // MOTOR_PROFILE if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x80, 0x0c); } @@ -632,7 +592,7 @@ gl843_init_registers (Genesys_Device * dev) dev->reg.init_reg(0xaa, 0x00); } - // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK. Not documented + // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK. if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { @@ -643,10 +603,9 @@ gl843_init_registers (Genesys_Device * dev) } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || + dev->model->model_id == ModelId::CANON_8600F || 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); } @@ -660,8 +619,6 @@ gl843_init_registers (Genesys_Device * dev) 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, @@ -670,48 +627,8 @@ gl843_init_registers (Genesys_Device * dev) 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); + dev->interface->write_buffer(0x3c, 0x3ff000, data, 32); } - - // 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) @@ -728,14 +645,9 @@ void CommandSetGl843::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, 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; + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; } // check analog frontend type @@ -749,153 +661,135 @@ void CommandSetGl843::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, 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 (unsigned i = 1; i <= 3; i++) { + 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)); - } + for (unsigned i = 0; i < 3; i++) { + 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 (unsigned i = 0; i < 3; i++) { + 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)); - } + for (unsigned i = 0; i < 3; i++) { + 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, + const ScanSession& session, Genesys_Register_Set* reg, - const Motor_Profile& motor_profile, + const MotorProfile& motor_profile, unsigned int exposure, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, - MotorFlag flags) + ScanFlag 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; + bool use_fast_fed = false; - if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, MotorFlag::FEED))) { - use_fast_fed = 1; + if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) { + use_fast_fed = true; + } + if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { + use_fast_fed = false; } - lincnt=scan_lines; - reg->set24(REG_LINCNT, lincnt); - DBG(DBG_io, "%s: lincnt=%d\n", __func__, lincnt); + reg->set24(REG_LINCNT, scan_lines); - /* compute register 02 value */ - r = sanei_genesys_get_address(reg, REG_0x02); - r->value = 0x00; - sanei_genesys_set_motor_power(*reg, true); + reg->set8(REG_0x02, 0); + sanei_genesys_set_motor_power(*reg, true); + std::uint8_t reg02 = reg->get8(REG_0x02); if (use_fast_fed) { - r->value |= REG_0x02_FASTFED; + reg02 |= REG_0x02_FASTFED; } else { - r->value &= ~REG_0x02_FASTFED; + reg02 &= ~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; + // in case of automatic go home, move until home sensor + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { + reg02 |= 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)) + if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || + (scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F) || + (scan_yres>=sensor.full_resolution)) { - r->value |= REG_0x02_ACDCDIS; + reg02 |= REG_0x02_ACDCDIS; } - if (has_flag(flags, MotorFlag::REVERSE)) { - r->value |= REG_0x02_MTRREV; + if (has_flag(flags, ScanFlag::REVERSE)) { + reg02 |= REG_0x02_MTRREV; } else { - r->value &= ~REG_0x02_MTRREV; + reg02 &= ~REG_0x02_MTRREV; } + reg->set8(REG_0x02, reg02); - /* 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); + // scan and backtracking slope table + auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure, + 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); + scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); - reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier); - reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier); + reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FSHDEC, scan_table.table.size() / 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); + const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); + if (fast_profile == nullptr) { + fast_profile = &motor_profile; + } - reg->set8(REG_FSHDEC, fast_table.steps_count / step_multiplier); - reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier); + auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, + *fast_profile); + + scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); + scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); + + reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); + + if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { + std::uint8_t vref = 0; + vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; + vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; + vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; + vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; + reg->set8(REG_0x80, vref); + } /* 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; + dist = scan_table.table.size() / step_multiplier; + + if (use_fast_fed) { + dist += (fast_table.table.size() / 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 */ @@ -906,15 +800,15 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev, } reg->set24(REG_FEEDL, feedl); - DBG(DBG_io, "%s: feedl=%d\n", __func__, feedl); - /* doesn't seem to matter that much */ + // doesn't seem to matter that much + std::uint32_t z1, z2; sanei_genesys_calculate_zmod(use_fast_fed, - exposure, + exposure, scan_table.table, - scan_table.steps_count / step_multiplier, - feedl, - scan_table.steps_count / step_multiplier, + scan_table.table.size() / step_multiplier, + feedl, + scan_table.table.size() / step_multiplier, &z1, &z2); if(scan_yres>600) @@ -924,47 +818,46 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev, } 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_0x1E, scan_dummy, 0x0f); 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); + reg->set8_mask(REG_0x68, static_cast<unsigned>(fast_profile->step_type) << REG_0x68S_FSTPSEL, 0xc0); // steps for STOP table - reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier); + reg->set8(REG_FMOVDEC, fast_table.table.size() / 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)) + 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 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { - r->value = 0x50; - coeff = sensor.get_hwdpi_divisor_for_dpi(scan_yres); + // FIXME: take this information from motor struct + std::uint8_t reg_vref = reg->get8(0x80); + reg_vref = 0x50; + unsigned coeff = sensor.full_resolution / scan_yres; if (dev->model->motor_id == MotorId::KVSS080) { - if(coeff>=1) - { - r->value |= 0x05; + if (coeff >= 1) { + reg_vref |= 0x05; + } + } else { + switch (coeff) { + case 4: + reg_vref |= 0x0a; + break; + case 2: + reg_vref |= 0x0f; + break; + case 1: + reg_vref |= 0x0f; + break; } } - else { - switch(coeff) - { - case 4: - r->value |= 0x0a; - break; - case 2: - r->value |= 0x0f; - break; - case 1: - r->value |= 0x0f; - break; - } - } + reg->set8(REG_0x80, reg_vref); } } @@ -981,7 +874,6 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev, * @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 ... */ @@ -990,57 +882,54 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens 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); + // sensor parameters + scanner_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 || - (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE))) + has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || + session.use_host_side_calib) { - r->value &= ~REG_0x01_DVDSET; + reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; + } else { - r->value |= REG_0x01_DVDSET; + reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } - bool use_shdarea = dpihw > 600; + bool use_shdarea = false; 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; + } else if (dev->model->model_id == ModelId::CANON_8600F || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) + { + use_shdarea = true; + } else { + use_shdarea = session.params.xres > 600; } + if (use_shdarea) { - r->value |= REG_0x01_SHDAREA; + reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; } else { - r->value &= ~REG_0x01_SHDAREA; + reg->find_reg(REG_0x01).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; + reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB; } else { - r->value &= ~REG_0x03_AVEENB; + reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; } // FIXME: we probably don't need to set exposure to registers at this point. It was this way @@ -1049,43 +938,40 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); /* select XPA */ - r->value &= ~REG_0x03_XPASEL; + reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { - r->value |= REG_0x03_XPASEL; + reg->find_reg(REG_0x03).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; + // BW threshold + reg->set8(REG_0x2E, 0x7f); + reg->set8(REG_0x2F, 0x7f); /* 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); + reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: - r->value &= ~REG_0x04_LINEART; - r->value |= REG_0x04_BITSET; + reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; + reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } - r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); + reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: - r->value |= 0x14; + reg->find_reg(REG_0x04).value |= 0x14; break; case ColorFilter::BLUE: - r->value |= 0x1c; + reg->find_reg(REG_0x04).value |= 0x1c; break; case ColorFilter::GREEN: - r->value |= 0x18; + reg->find_reg(REG_0x04).value |= 0x18; break; default: break; // should not happen @@ -1093,10 +979,10 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens } else { switch (dev->frontend.layout.type) { case FrontendType::WOLFSON: - r->value |= 0x10; // pixel by pixel + reg->find_reg(REG_0x04).value |= 0x10; // pixel by pixel break; case FrontendType::ANALOG_DEVICES: - r->value |= 0x20; // slow color pixel by pixel + reg->find_reg(REG_0x04).value |= 0x20; // slow color pixel by pixel break; default: throw SaneException("Invalid frontend type %d", @@ -1104,7 +990,10 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens } } - sanei_genesys_set_dpihw(*reg, sensor, dpihw); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, + session.params.channels, + session.params.scan_method); + sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -1112,28 +1001,18 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens 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_DPISET, sensor.register_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); - + // BUG: the division by optical and full resolution factor likely does not make sense + reg->set24(REG_MAXWD, (session.output_line_bytes * + session.optical_resolution / session.full_resolution) >> 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; + reg->set8(REG_DUMMY, sensor.dummy_pixel); } void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -1170,42 +1049,15 @@ void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Gene 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)); + const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session); // now _LOGICAL_ optical values used are known, setup registers gl843_init_optical_regs_scan(dev, sensor, reg, exposure, session); + gl843_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi, + session.optical_line_count, dummy, session.params.starty, + session.params.flags); - /*** 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); + setup_image_pipeline(*dev, session); dev->read_active = true; @@ -1224,33 +1076,46 @@ ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev, 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); + ScanFlag flags = ScanFlag::NONE; + float move = 0.0f; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - start = static_cast<int>(dev->model->x_offset_ta); + // note: scanner_move_to_ta() function has already been called and the sensor is at the + // transparency adapter + if (!dev->ignore_offsets) { + move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; + } + flags |= ScanFlag::USE_XPA; } else { - start = static_cast<int>(dev->model->x_offset); + if (!dev->ignore_offsets) { + move = dev->model->y_offset; + } } - if (dev->model->model_id == ModelId::CANON_8600F) + move += settings.tl_y; + + int move_dpi = dev->motor.base_ydpi; + move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + + float start = 0.0f; + if (settings.scan_method==ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - // FIXME: this is probably just an artifact of a bug elsewhere - start /= ccd_size_divisor; + start = dev->model->x_offset_ta; + } else { + start = dev->model->x_offset; } + start = start + settings.tl_x; - start += static_cast<int>(settings.tl_x); - start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH); + start = static_cast<float>((start * settings.xres) / 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.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; @@ -1259,8 +1124,7 @@ ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev, 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; - + session.params.flags = flags; compute_session(dev, session, sensor); return session; @@ -1352,8 +1216,6 @@ void CommandSetGl843::detect_document_end(Genesys_Device* dev) const 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); @@ -1363,194 +1225,6 @@ void CommandSetGl843::detect_document_end(Genesys_Device* dev) const } } -// 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 @@ -1580,30 +1254,44 @@ void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens } if (reg->state.is_xpa_on && reg->state.is_lamp_on) { - gl843_set_xpa_lamp_power(dev, true); + dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { - gl843_set_xpa_motor_power(dev, *reg, true); + dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } // blinking led dev->interface->write_register(REG_0x7E, 0x01); break; case GpioId::CANON_8400F: + if (dev->session.params.xres == 3200) + { + GenesysRegisterSettingSet reg_settings = { + { 0x6c, 0x00, 0x02 }, + }; + apply_reg_settings_to_device(*dev, reg_settings); + } + if (reg->state.is_xpa_on && reg->state.is_lamp_on) { + dev->cmd_set->set_xpa_lamp_power(*dev, true); + } + if (reg->state.is_xpa_on) { + dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); + } + break; case GpioId::CANON_8600F: if (reg->state.is_xpa_on && reg->state.is_lamp_on) { - gl843_set_xpa_lamp_power(dev, true); + dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { - gl843_set_xpa_motor_power(dev, *reg, true); + dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } 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); + dev->cmd_set->set_xpa_lamp_power(*dev, true); } break; } @@ -1612,8 +1300,7 @@ void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens break; } - // clear scan and feed count - dev->interface->write_register(REG_0x0D, REG_0x0D_CLRLNCNT | REG_0x0D_CLRMCNT); + scanner_clear_scan_and_feed_counts(*dev); // enable scan and motor uint8_t val = dev->interface->read_register(REG_0x01); @@ -1622,11 +1309,26 @@ void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens 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); + switch (reg->state.motor_mode) { + case MotorMode::PRIMARY: { + if (reg->state.is_motor_on) { + dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); + } + break; + } + case MotorMode::PRIMARY_AND_SECONDARY: { + if (reg->state.is_motor_on) { + dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); + dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); + } + break; + } + case MotorMode::SECONDARY: { + if (reg->state.is_motor_on) { + dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); + } + break; + } } } @@ -1640,10 +1342,8 @@ void CommandSetGl843::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, // 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 (reg->state.is_xpa_on) { + dev->cmd_set->set_xpa_lamp_power(*dev, false); } if (!dev->model->is_sheetfed) { @@ -1658,202 +1358,79 @@ void CommandSetGl843::move_back_home(Genesys_Device* dev, bool wait_until_home) 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, ®s, 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; + int move; + float calib_size_mm = 0; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - dev->calib_lines = dev->model->shading_ta_lines; + calib_size_mm = dev->model->y_size_calib_ta_mm; } else { - dev->calib_lines = dev->model->shading_lines; + calib_size_mm = dev->model->y_size_calib_mm; } - dpihw = sensor.get_logical_hwdpi(dev->settings.xres); - factor=sensor.optical_res/dpihw; - resolution=dpihw; + unsigned resolution = sensor.shading_resolution; - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, dev->calib_channels, + unsigned channels = 3; + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 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); + unsigned calib_pixels = 0; + unsigned calib_pixels_offset = 0; - 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); + if (should_calibrate_only_active_area(*dev, dev->settings)) { + float offset = dev->model->x_offset_ta; + // FIXME: we should use resolution here + offset = static_cast<float>((offset * dev->settings.xres) / 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; - } + float size = dev->model->x_size_ta; + size = static_cast<float>((size * dev->settings.xres) / MM_PER_INCH); - dev->calib_resolution = resolution; + calib_pixels_offset = static_cast<std::size_t>(offset); + calib_pixels = static_cast<std::size_t>(size); + } else { + calib_pixels_offset = 0; + calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; + } ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | - ScanFlag::DISABLE_BUFFER_FULL_MOVE | - ScanFlag::IGNORE_LINE_DISTANCE; + ScanFlag::DISABLE_BUFFER_FULL_MOVE; 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 + // note: scanner_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); + if (dev->model->model_id == ModelId::CANON_8600F && resolution == 2400) { + move /= 2; + } + if (dev->model->model_id == ModelId::CANON_8600F && resolution == 4800) { + move /= 4; + } flags |= ScanFlag::USE_XPA; } else { move = static_cast<int>(dev->model->y_offset_calib_white); } move = static_cast<int>((move * resolution) / MM_PER_INCH); + unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; - session.params.startx = dev->calib_pixels_offset; + session.params.startx = calib_pixels_offset; session.params.starty = move; - session.params.pixels = dev->calib_pixels; - session.params.lines = dev->calib_lines; + session.params.pixels = calib_pixels; + session.params.lines = calib_lines; session.params.depth = 16; - session.params.channels = dev->calib_channels; + 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; @@ -1862,89 +1439,7 @@ void CommandSetGl843::init_regs_for_shading(Genesys_Device* dev, const Genesys_S init_regs_for_scan_session(dev, calib_sensor, ®s, 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); } /** @@ -1975,8 +1470,7 @@ void CommandSetGl843::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor 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); + dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3); } /* this function does the led calibration by scanning one line of the calibration @@ -1987,607 +1481,69 @@ void CommandSetGl843::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor 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, ®s, 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, ®s, 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]; + return scanner_led_calibration(*dev, sensor, regs); } -/** @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, ®s, 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, ®s, 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, ®s, 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, ®s, 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)); + scanner_offset_calibration(*dev, sensor, regs); } - -/* 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; + scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); +} - dpihw = sensor.get_logical_hwdpi(dpi); - factor=sensor.optical_res/dpihw; +// 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) const +{ + DBG_HELPER(dbg); + (void) sensor; - // coarse gain calibration is always done in color mode unsigned channels = 3; + unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method) + .get_nearest_resolution_x(600); - /* 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; + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, + dev->settings.scan_method); + unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH / 2; - ScanFlag flags = ScanFlag::DISABLE_SHADING | - ScanFlag::DISABLE_GAMMA | - ScanFlag::SINGLE_LINE | - ScanFlag::IGNORE_LINE_DISTANCE; + *reg = dev->reg; + auto flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::SINGLE_LINE | + ScanFlag::IGNORE_STAGGER_OFFSET | + ScanFlag::IGNORE_COLOR_OFFSET; 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.startx = (num_pixels / 2) * resolution / calib_sensor.full_resolution; session.params.starty = 0; - session.params.pixels = target_pixels; - session.params.lines = lines; - session.params.depth = 8; + session.params.pixels = num_pixels; + session.params.lines = 1; + session.params.depth = dev->model->bpp_color_values.front(); 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, ®s, 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, ®s, 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); } /** @@ -2654,7 +1610,7 @@ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const 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; + dev->reg.find_reg(0x0b).value = val; if (dev->model->model_id == ModelId::CANON_8400F) { dev->interface->write_0x8c(0x1e, 0x01); @@ -2691,18 +1647,11 @@ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const 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; + 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); @@ -2710,8 +1659,6 @@ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const // setup gpio gl843_init_gpio(dev); - - scanner_move(*dev, dev->model->default_method, 300, Direction::FORWARD); dev->interface->sleep_ms(100); } @@ -2724,7 +1671,7 @@ void CommandSetGl843::init(Genesys_Device* dev) const DBG_INIT (); DBG_HELPER(dbg); - sanei_genesys_asic_init(dev, 0); + sanei_genesys_asic_init(dev); } void CommandSetGl843::update_hardware_sensors(Genesys_Scanner* s) const @@ -2754,216 +1701,10 @@ void CommandSetGl843::update_hardware_sensors(Genesys_Scanner* s) const } } -/** @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 +void CommandSetGl843::update_home_sensor_gpio(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"); - } + (void) dev; } /** @@ -2974,43 +1715,27 @@ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Senso uint8_t* data, int size) const { DBG_HELPER(dbg); - uint32_t final_size, length, i; + uint32_t final_size, 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(); - } + int count; + + int offset = 0; + unsigned length = size; + + if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { + offset = dev->session.params.startx * sensor.shading_resolution / + dev->session.params.xres; - /* 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); + length = dev->session.output_pixels * sensor.shading_resolution / + dev->session.params.xres; + + offset += sensor.shading_pixel_offset; + + // 16 bit words, 2 words per color, 3 color channels + length *= 2 * 2 * 3; + offset *= 2 * 2 * 3; + } else { + offset += sensor.shading_pixel_offset * 2 * 2 * 3; } dev->interface->record_key_value("shading_offset", std::to_string(offset)); @@ -3024,6 +1749,14 @@ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Senso /* copy regular shading data to the expected layout */ buffer = final_data.data(); count = 0; + if (offset < 0) { + count += (-offset); + length -= (-offset); + offset = 0; + } + if (static_cast<int>(length) + offset > static_cast<int>(size)) { + length = size - offset; + } /* loop over calibration data */ for (i = 0; i < length; i++) @@ -3036,8 +1769,7 @@ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Senso } } - dev->interface->write_buffer(0x3c, 0, final_data.data(), count, - ScannerInterface::FLAG_SMALL_ADDRESS); + dev->interface->write_buffer(0x3c, 0, final_data.data(), count); } bool CommandSetGl843::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const @@ -3051,10 +1783,5 @@ 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 index 9f0a9e9..5326a2d 100644 --- a/backend/genesys/gl843.h +++ b/backend/genesys/gl843.h @@ -42,7 +42,7 @@ */ #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h" #ifndef BACKEND_GENESYS_GL843_H #define BACKEND_GENESYS_GL843_H @@ -50,7 +50,7 @@ namespace genesys { namespace gl843 { -class CommandSetGl843 : public CommandSet +class CommandSetGl843 : public CommandSetCommon { public: ~CommandSetGl843() override = default; @@ -60,17 +60,11 @@ public: 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; + 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; @@ -86,8 +80,6 @@ public: 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; @@ -103,17 +95,14 @@ public: void update_hardware_sensors(struct Genesys_Scanner* s) const override; + 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; diff --git a/backend/genesys/gl843_registers.h b/backend/genesys/gl843_registers.h index 8ecb0fc..cbc38c0 100644 --- a/backend/genesys/gl843_registers.h +++ b/backend/genesys/gl843_registers.h @@ -338,6 +338,16 @@ static constexpr RegAddr REG_CK4MAP = 0x7a; static constexpr RegAddr REG_0x7E = 0x7e; +static constexpr RegAddr REG_0x80 = 0x80; +static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; +static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; +static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; +static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; +static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; +static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; +static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; +static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; + static constexpr RegAddr REG_0x9D = 0x9d; static constexpr RegShift REG_0x9DS_STEPTIM = 2; diff --git a/backend/genesys/gl846.cpp b/backend/genesys/gl846.cpp index d309d29..cae7414 100644 --- a/backend/genesys/gl846.cpp +++ b/backend/genesys/gl846.cpp @@ -61,38 +61,12 @@ namespace gl846 { /** * compute the step multiplier used */ -static int -gl846_get_step_multiplier (Genesys_Register_Set * regs) +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; + unsigned value = (regs->get8(0x9d) & 0x0f) >> 1; + return 1 << value; } - /** @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. @@ -108,23 +82,56 @@ gl846_init_registers (Genesys_Device * dev) dev->reg.clear(); dev->reg.init_reg(0x01, 0x60); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x01, 0x22); + } dev->reg.init_reg(0x02, 0x38); dev->reg.init_reg(0x03, 0x03); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x03, 0xbf); + } dev->reg.init_reg(0x04, 0x22); dev->reg.init_reg(0x05, 0x60); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x05, 0x48); + } dev->reg.init_reg(0x06, 0x10); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x06, 0xf0); + } dev->reg.init_reg(0x08, 0x60); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x08, 0x00); + } dev->reg.init_reg(0x09, 0x00); dev->reg.init_reg(0x0a, 0x00); dev->reg.init_reg(0x0b, 0x8b); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0x0b, 0x2a); + } + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x0b, 0x4a); + } 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(0x10, 0x00); // exposure, set during sensor setup + dev->reg.init_reg(0x11, 0x00); // exposure, set during sensor setup + dev->reg.init_reg(0x12, 0x00); // exposure, set during sensor setup + dev->reg.init_reg(0x13, 0x00); // exposure, set during sensor setup + dev->reg.init_reg(0x14, 0x00); // exposure, set during sensor setup + dev->reg.init_reg(0x15, 0x00); // exposure, set during sensor setup dev->reg.init_reg(0x16, 0xbb); // SENSOR_DEF dev->reg.init_reg(0x17, 0x13); // SENSOR_DEF dev->reg.init_reg(0x18, 0x10); // SENSOR_DEF @@ -133,33 +140,52 @@ gl846_init_registers (Genesys_Device * dev) 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(0x1e, 0xf0); // WDTIME, LINESEL: set during sensor and motor setup + + // SCANFED dev->reg.init_reg(0x1f, 0x01); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400) { + dev->reg.init_reg(0x1f, 0x00); + } + 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); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x20, 0x55); + } + dev->reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup + dev->reg.init_reg(0x22, 0x60); // FWDSTEP: set during motor setup + dev->reg.init_reg(0x23, 0x60); // BWDSTEP: set during motor setup + dev->reg.init_reg(0x24, 0x60); // FASTNO: set during motor setup + dev->reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup + dev->reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup + dev->reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup + dev->reg.init_reg(0x2c, 0x00); // DPISET: set during sensor setup + dev->reg.init_reg(0x2d, 0x00); // DPISET: set during sensor setup + dev->reg.init_reg(0x2e, 0x80); // BWHI: set during sensor setup + dev->reg.init_reg(0x2f, 0x80); // BWLOW: set during sensor setup + dev->reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup + dev->reg.init_reg(0x31, 0x00); // STRPIXEL: set during sensor setup + dev->reg.init_reg(0x32, 0x00); // ENDPIXEL: set during sensor setup + dev->reg.init_reg(0x33, 0x00); // ENDPIXEL: set during sensor setup + + // DUMMY: the number of CCD dummy pixels 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); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x34, 0x14); + } + + dev->reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup + dev->reg.init_reg(0x36, 0x40); // MAXWD: set during scan setup + dev->reg.init_reg(0x37, 0x00); // MAXWD: set during scan setup + dev->reg.init_reg(0x38, 0x2a); // LPERIOD: set during sensor setup + dev->reg.init_reg(0x39, 0xf8); // LPERIOD: set during sensor setup + dev->reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup + dev->reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup + dev->reg.init_reg(0x3f, 0x01); // FEEDL: set during motor setup dev->reg.init_reg(0x52, 0x02); // SENSOR_DEF dev->reg.init_reg(0x53, 0x04); // SENSOR_DEF dev->reg.init_reg(0x54, 0x06); // SENSOR_DEF @@ -169,22 +195,30 @@ gl846_init_registers (Genesys_Device * dev) dev->reg.init_reg(0x58, 0x59); // SENSOR_DEF dev->reg.init_reg(0x59, 0x31); // SENSOR_DEF dev->reg.init_reg(0x5a, 0x40); // SENSOR_DEF + + // DECSEL, STEPTIM 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); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x5e, 0x01); + } + dev->reg.init_reg(0x5f, 0x01); // FMOVDEC: overwritten during motor setup + dev->reg.init_reg(0x60, 0x00); // STEPSEL, Z1MOD: overwritten during motor setup + dev->reg.init_reg(0x61, 0x00); // Z1MOD: overwritten during motor setup + dev->reg.init_reg(0x62, 0x00); // Z1MOD: overwritten during motor setup + dev->reg.init_reg(0x63, 0x00); // FSTPSEL, Z2MOD: overwritten during motor setup + dev->reg.init_reg(0x64, 0x00); // Z2MOD: overwritten during motor setup + dev->reg.init_reg(0x65, 0x00); // Z2MOD: overwritten during motor setup + dev->reg.init_reg(0x67, 0x7f); // MTRPWM: overwritten during motor setup + dev->reg.init_reg(0x68, 0x7f); // FASTPWM: overwritten during motor setup + dev->reg.init_reg(0x69, 0x01); // FSHDEC: overwritten during motor setup + dev->reg.init_reg(0x6a, 0x01); // FMOVNO: overwritten during motor setup + // 0x6b, 0x6c, 0x6d, 0x6e, 0x6f - gpio + dev->reg.init_reg(0x70, 0x01); // SENSOR_DEF + dev->reg.init_reg(0x71, 0x00); // SENSOR_DEF + dev->reg.init_reg(0x72, 0x02); // SENSOR_DEF + dev->reg.init_reg(0x73, 0x01); // SENSOR_DEF dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF dev->reg.init_reg(0x76, 0x00); // SENSOR_DEF @@ -194,78 +228,80 @@ gl846_init_registers (Genesys_Device * dev) 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(0x7d, 0x20); // SENSOR_DEF 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; -} + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->reg.init_reg(0x7f, 0x00); + } + dev->reg.init_reg(0x80, 0x4f); // overwritten during motor setup + dev->reg.init_reg(0x87, 0x02); // SENSOR_DEF -/**@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]; + // MTRPLS: pulse width of ADF motor trigger signal + dev->reg.init_reg(0x94, 0x00); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0x94, 0xff); + } + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0x98, 0x20); // ONDUR + dev->reg.init_reg(0x99, 0x00); // ONDUR + dev->reg.init_reg(0x9a, 0x90); // OFFDUR + dev->reg.init_reg(0x9b, 0x00); // OFFDUR + } - /* sanity check */ - if(table_nr<0 || table_nr>4) - { - throw SaneException("invalid table number %d", table_nr); + dev->reg.init_reg(0x9d, 0x00); // contains STEPTIM + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0x9d, 0x04); + } + dev->reg.init_reg(0x9e, 0x00); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0xa1, 0xe0); } - std::vector<uint8_t> table(steps * 2); - for (i = 0; i < steps; i++) + // RFHSET (SDRAM refresh time) + dev->reg.init_reg(0xa2, 0x1f); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { - table[i * 2] = slope_table[i] & 0xff; - table[i * 2 + 1] = slope_table[i] >> 8; + dev->reg.init_reg(0xa2, 0x0f); } - if (DBG_LEVEL >= DBG_io) + // 0xa6, 0xa7 0xa8, 0xa9 - gpio + + // Various important settings: GPOM9, MULSTOP, NODECEL, TB3TB1, TB5TB2, FIX16CLK + dev->reg.init_reg(0xab, 0xc0); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { - 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); + dev->reg.init_reg(0xab, 0x01); + } + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0xbb, 0x00); // FIXME: default is the same + } + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0xbc, 0x0f); + dev->reg.init_reg(0xdb, 0xff); + } + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400) { + dev->reg.init_reg(0xbe, 0x07); } - if (dev->interface->is_mock()) { - dev->interface->record_slope_table(table_nr, slope_table); + // 0xd0, 0xd1, 0xd2 - SH0DWN, SH1DWN, SH2DWN - shading bank[0..2] for CCD. + // Set during memory layout setup + + // [0xe0..0xf7] - image buffer addresses. Set during memory layout setup + dev->reg.init_reg(0xf8, 0x05); // MAXSEL, MINSEL + + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0xfe, 0x08); // MOTTGST, AUTO_O + dev->reg.init_reg(0xff, 0x02); // AUTO_S } - // slope table addresses are fixed - dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data()); + + const auto& sensor = sanei_genesys_find_sensor_any(dev); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, + 3, dev->model->default_method); + sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); } /** @@ -283,11 +319,8 @@ static void gl846_set_adi_fe(Genesys_Device* dev, uint8_t set) 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; + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; } // write them to analog frontend @@ -326,115 +359,110 @@ void CommandSetGl846::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, // @brief set up motor related register for scan static void gl846_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + const ScanSession& session, Genesys_Register_Set* reg, - const Motor_Profile& motor_profile, + const MotorProfile& motor_profile, unsigned int scan_exposure_time, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, - MotorFlag flags) + ScanFlag 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; + bool use_fast_fed = false; + if (dev->settings.yres == 4444 && feed_steps > 100 && !has_flag(flags, ScanFlag::FEEDING)) { + use_fast_fed = true; + } + if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { + use_fast_fed = false; } - 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); + reg->set8(REG_0x02, 0); + sanei_genesys_set_motor_power(*reg, true); - if (use_fast_fed) - r->value |= REG_0x02_FASTFED; - else - r->value &= ~REG_0x02_FASTFED; + std::uint8_t reg02 = reg->get8(REG_0x02); + if (use_fast_fed) { + reg02 |= REG_0x02_FASTFED; + } else { + reg02 &= ~REG_0x02_FASTFED; + } - if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { - r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { + reg02 |= 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, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres>=sensor.full_resolution)) { + reg02 |= REG_0x02_ACDCDIS; } - if (has_flag(flags, MotorFlag::REVERSE)) { - r->value |= REG_0x02_MTRREV; + if (has_flag(flags, ScanFlag::REVERSE)) { + reg02 |= REG_0x02_MTRREV; } else { - r->value &= ~REG_0x02_MTRREV; + reg02 &= ~REG_0x02_MTRREV; } + reg->set8(REG_0x02, reg02); - /* 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); + // scan and backtracking slope table + auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, + scan_exposure_time, 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); + scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); - /* fast table */ - fast_dpi=sanei_genesys_get_lowest_ydpi(dev); + reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); - // 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; + // fast table + const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); + if (fast_profile == nullptr) { + fast_profile = &motor_profile; } - Motor_Profile fast_motor_profile = motor_profile; - fast_motor_profile.step_type = fast_step_type; + auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, + *fast_profile); - 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); + scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); + scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); - 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); + reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); + reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); - /* 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; + if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { + std::uint8_t vref = 0; + vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; + vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; + vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; + vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; + reg->set8(REG_0x80, vref); } - else - { + + unsigned feedl = feed_steps; + unsigned dist = 0; + if (use_fast_fed) { + feedl <<= static_cast<unsigned>(fast_profile->step_type); + dist = (scan_table.table.size() + 2 * fast_table.table.size()); + // TODO read and decode REG_0xAB + dist += (reg->get8(0x5e) & 31); + dist += reg->get8(REG_FEDCNT); + } else { feedl <<= static_cast<unsigned>(motor_profile.step_type); - dist = scan_table.steps_count; - if (has_flag(flags, MotorFlag::FEED)) { + dist = scan_table.table.size(); + if (has_flag(flags, ScanFlag::FEEDING)) { dist *= 2; } } - DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist); - /* check for overflow */ + // check for overflow if (dist < feedl) { feedl -= dist; } else { @@ -442,13 +470,9 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev, } 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); + unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1; + unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME); /* hi res motor speed GPIO */ /* @@ -482,56 +506,31 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev, 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; + unsigned min_restep = (scan_table.table.size() / 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; + reg->set8(REG_FWDSTEP, min_restep); + reg->set8(REG_BWDSTEP, min_restep); + + std::uint32_t z1, z2; sanei_genesys_calculate_zmod(use_fast_fed, - scan_exposure_time*ccdlmt*tgtime, + scan_exposure_time * ccdlmt * tgtime, scan_table.table, - scan_table.steps_count, + scan_table.table.size(), 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; + reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); - 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); + reg->set8(REG_0x67, 0x7f); + reg->set8(REG_0x68, 0x7f); } @@ -558,82 +557,69 @@ static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens 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); + scanner_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; + reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || - (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) + has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || + session.use_host_side_calib) { - r->value &= ~REG_0x01_DVDSET; - } - else - { - r->value |= REG_0x01_DVDSET; + reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; + } else { + reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } - r = sanei_genesys_get_address(reg, REG_0x03); - r->value &= ~REG_0x03_AVEENB; + reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); + reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); - /* 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; + // BW threshold + reg->set8(0x2e, 0x7f); + reg->set8(0x2f, 0x7f); /* 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); + reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: - r->value &= ~REG_0x04_LINEART; - r->value |= REG_0x04_BITSET; + reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; + reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } - r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); + reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: - r->value |= 0x24; + reg->find_reg(REG_0x04).value |= 0x24; break; case ColorFilter::BLUE: - r->value |= 0x2c; + reg->find_reg(REG_0x04).value |= 0x2c; break; case ColorFilter::GREEN: - r->value |= 0x28; + reg->find_reg(REG_0x04).value |= 0x28; break; default: break; // should not happen } } else { - r->value |= 0x20; // mono + reg->find_reg(REG_0x04).value |= 0x20; // mono } - sanei_genesys_set_dpihw(*reg, sensor, dpihw); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, + session.params.channels, + session.params.scan_method); + sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -644,38 +630,31 @@ static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens /* 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; + reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; + if (session.enable_ledadd) { - r->value |= REG_0x87_LEDADD; + reg->find_reg(0x87).value |= REG_0x87_LEDADD; } /* RGB weighting - r = sanei_genesys_get_address (reg, 0x01); - r->value &= ~REG_0x01_TRUEGRAY; + reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY; + if (session.enable_ledadd)) { - r->value |= REG_0x01_TRUEGRAY; + reg->find_reg(0x01).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_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); - build_image_pipeline(dev, session); + setup_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; + reg->set8(0x34, sensor.dummy_pixel); } void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -685,13 +664,12 @@ void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Gene DBG_HELPER(dbg); session.assert_computed(); - int move; int exposure_time; int slope_dpi = 0; - int dummy = 0; - dummy = 3-session.params.channels; + // FIXME: on cis scanners we may want to scan at reduced resolution + int dummy = 0; /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color @@ -705,46 +683,18 @@ void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Gene 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)); + const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session); /* 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); + gl846_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure_time, slope_dpi, + session.optical_line_count, dummy, session.params.starty, + session.params.flags); /*** prepares data reordering ***/ - dev->read_buffer.clear(); - dev->read_buffer.alloc(session.buffer_size_read); - dev->read_active = true; dev->session = session; @@ -759,21 +709,50 @@ 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); + ScanFlag flags = ScanFlag::NONE; + + unsigned move_dpi = dev->motor.base_ydpi; + + float move = dev->model->y_offset; + if (settings.scan_method == ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + // note: scanner_move_to_ta() function has already been called and the sensor is at the + // transparency adapter + if (!dev->ignore_offsets) { + move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; + } + flags |= ScanFlag::USE_XPA; + } else { + if (!dev->ignore_offsets) { + move = dev->model->y_offset; + } + } + + move = move + settings.tl_y; + move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + move -= dev->head_pos(ScanHeadId::PRIMARY); + + float start = dev->model->x_offset; + if (settings.scan_method == ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + start = dev->model->x_offset_ta; + } else { + start = dev->model->x_offset; + } + + start = start + dev->settings.tl_x; + start = static_cast<float>((start * settings.xres) / 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.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; @@ -782,7 +761,8 @@ ScanSession CommandSetGl846::calculate_scan_session(const Genesys_Device* dev, 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; + // backtracking isn't handled well, so don't enable it + session.params.flags = flags; compute_session(dev, session, sensor); @@ -809,24 +789,17 @@ void CommandSetGl846::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens 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); - */ + if (reg->state.is_xpa_on && reg->state.is_lamp_on) { + dev->cmd_set->set_xpa_lamp_power(*dev, true); + } - val = REG_0x0D_CLRLNCNT; - dev->interface->write_register(REG_0x0D, val); - val = REG_0x0D_CLRMCNT; - dev->interface->write_register(REG_0x0D, val); + scanner_clear_scan_and_feed_counts(*dev); 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; + reg->set8(REG_0x01, val); scanner_start_action(*dev, start_motor); @@ -841,6 +814,10 @@ void CommandSetGl846::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, (void) reg; DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); + if (reg->state.is_xpa_on) { + dev->cmd_set->set_xpa_lamp_power(*dev, false); + } + if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } @@ -852,260 +829,72 @@ void CommandSetGl846::move_back_home(Genesys_Device* dev, bool wait_until_home) 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, ®s, 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; + unsigned move_dpi = dev->motor.base_ydpi; - /* initial calibration reg values */ - regs = dev->reg; + float calib_size_mm = 0; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + calib_size_mm = dev->model->y_size_calib_ta_mm; + } else { + calib_size_mm = dev->model->y_size_calib_mm; + } - dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres); + unsigned channels = 3; + unsigned resolution = sensor.shading_resolution; - const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution, - dev->calib_channels, + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 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); + float move = 0; + ScanFlag flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::DISABLE_BUFFER_FULL_MOVE; - /* this is aworkaround insufficent distance for slope - * motor acceleration TODO special motor slope for shading */ - move=1; - if(dev->calib_resolution<1200) + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - move=40; + // note: scanner_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<float>((move * move_dpi) / MM_PER_INCH); + + unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH); + ScanSession session; - session.params.xres = dev->calib_resolution; - session.params.yres = dev->calib_resolution; + session.params.xres = resolution; + session.params.yres = 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.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; + session.params.lines = calib_lines; session.params.depth = 16; - session.params.channels = dev->calib_channels; + 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::DISABLE_BUFFER_FULL_MOVE | - ScanFlag::IGNORE_LINE_DISTANCE; + session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); - dev->interface->write_registers(regs); - - /* we use GENESYS_FLAG_SHADING_REPARK */ + /* we use ModelFlag::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); + dev->calib_session = session; } - /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. @@ -1114,39 +903,24 @@ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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; + std::uint32_t addr, i; 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; + unsigned length = static_cast<unsigned>(size / 3); - /* since we're using SHDAREA, substract startx coordinate from shading */ - strpixel -= (sensor.ccd_start_xoffset * 600) / sensor.optical_res; + // we're using SHDAREA, thus we only need to upload part of the line + unsigned offset = dev->session.pixel_count_ratio.apply( + dev->session.params.startx * sensor.full_resolution / dev->session.params.xres); + unsigned pixels = dev->session.pixel_count_ratio.apply(dev->session.optical_pixels_raw); - /* turn pixel value into bytes 2x16 bits words */ - strpixel*=2*2; - pixels*=2*2; + // turn pixel value into bytes 2x16 bits words + offset *= 2 * 2; + pixels *= 2 * 2; - dev->interface->record_key_value("shading_offset", std::to_string(strpixel)); + dev->interface->record_key_value("shading_offset", std::to_string(offset)); 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_factor", std::to_string(sensor.shading_factor)); std::vector<uint8_t> buffer(pixels, 0); @@ -1163,10 +937,9 @@ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Senso ptr = buffer.data(); /* iterate on both sensor segment */ - for(x=0;x<pixels;x+=4*factor) - { - /* coefficient source */ - src=(data+strpixel+i*length)+x; + for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) { + // coefficient source + src = (data + offset + i * length) + x; /* coefficient copy */ ptr[0]=src[0]; @@ -1192,166 +965,7 @@ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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, ®s, 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, ®s, 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] }; + return scanner_led_calibration(*dev, sensor, regs); } /** @@ -1360,29 +974,10 @@ SensorExposure CommandSetGl846::led_calibration(Genesys_Device* dev, const Genes 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) + apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg) { - 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); + dev->interface->write_register(reg.address, reg.value); + }); } /** @@ -1391,32 +986,11 @@ static void gl846_init_gpio(Genesys_Device* dev) 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); - /* 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]); - } + apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /* * @@ -1433,15 +1007,14 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const dev->interface->write_register(0x0e, 0x00); } - if(dev->usb_mode == 1) - { - val = 0x14; - } - else - { - val = 0x11; + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + if (dev->usb_mode == 1) { + val = 0x14; + } else { + val = 0x11; + } + dev->interface->write_0x8c(0x0f, val); } - dev->interface->write_0x8c(0x0f, val); // test CHKVER val = dev->interface->read_register(REG_0x40); @@ -1450,18 +1023,11 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val); } - /* Set default values for registers */ - gl846_init_registers (dev); + 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) { @@ -1470,8 +1036,15 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const } // set up clocks - dev->interface->write_0x8c(0x10, 0x0e); - dev->interface->write_0x8c(0x13, 0x0e); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + dev->interface->write_0x8c(0x10, 0x0c); + dev->interface->write_0x8c(0x13, 0x0c); + } else { + dev->interface->write_0x8c(0x10, 0x0e); + dev->interface->write_0x8c(0x13, 0x0e); + } // setup gpio gl846_init_gpio(dev); @@ -1492,7 +1065,7 @@ void CommandSetGl846::init(Genesys_Device* dev) const DBG_INIT (); DBG_HELPER(dbg); - sanei_genesys_asic_init(dev, 0); + sanei_genesys_asic_init(dev); } void CommandSetGl846::update_hardware_sensors(Genesys_Scanner* s) const @@ -1529,512 +1102,16 @@ void CommandSetGl846::update_home_sensor_gpio(Genesys_Device& dev) const 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, ®s, 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, ®s, 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, ®s, 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, ®s, 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)); + scanner_offset_calibration(*dev, sensor, regs); } 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, ®s, 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, ®s, 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); + scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } bool CommandSetGl846::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const @@ -2044,14 +1121,11 @@ bool CommandSetGl846::needs_home_before_init_regs_for_scan(Genesys_Device* dev) } void CommandSetGl846::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, - Genesys_Register_Set* regs, int* channels, - int* total_size) const + Genesys_Register_Set* regs) const { (void) dev; (void) sensor; (void) regs; - (void) channels; - (void) total_size; throw SaneException("not implemented"); } @@ -2083,16 +1157,5 @@ void CommandSetGl846::eject_document(Genesys_Device* dev) const 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 index 258015a..f794a01 100644 --- a/backend/genesys/gl846.h +++ b/backend/genesys/gl846.h @@ -42,7 +42,7 @@ */ #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h" #ifndef BACKEND_GENESYS_GL846_H #define BACKEND_GENESYS_GL846_H @@ -50,82 +50,7 @@ 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 +class CommandSetGl846 : public CommandSetCommon { public: ~CommandSetGl846() override = default; @@ -135,17 +60,11 @@ public: 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; + 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; @@ -161,8 +80,6 @@ public: 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; @@ -178,8 +95,6 @@ public: 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; @@ -188,11 +103,6 @@ public: 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; diff --git a/backend/genesys/gl846_registers.h b/backend/genesys/gl846_registers.h index 39b3029..e4a8ac5 100644 --- a/backend/genesys/gl846_registers.h +++ b/backend/genesys/gl846_registers.h @@ -194,7 +194,7 @@ 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; @@ -303,6 +303,16 @@ static constexpr RegAddr REG_0x6E = 0x6e; static constexpr RegAddr REG_0x6F = 0x6f; static constexpr RegAddr REG_0x7E = 0x7e; +static constexpr RegAddr REG_0x80 = 0x80; +static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; +static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; +static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; +static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; +static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; +static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; +static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; +static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; + static constexpr RegMask REG_0x87_ACYCNRLC = 0x10; static constexpr RegMask REG_0x87_ENOFFSET = 0x08; static constexpr RegMask REG_0x87_LEDADD = 0x04; diff --git a/backend/genesys/gl847.cpp b/backend/genesys/gl847.cpp index cb0b527..f8f6b1c 100644 --- a/backend/genesys/gl847.cpp +++ b/backend/genesys/gl847.cpp @@ -56,39 +56,12 @@ namespace gl847 { /** * compute the step multiplier used */ -static int -gl847_get_step_multiplier (Genesys_Register_Set * regs) +static unsigned 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; + unsigned value = (regs->get8(0x9d) & 0x0f) >> 1; + return 1 << 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. @@ -111,30 +84,49 @@ gl847_init_registers (Genesys_Device * dev) dev->reg.clear(); dev->reg.init_reg(0x01, 0x82); + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x01, 0x40); + } dev->reg.init_reg(0x02, 0x18); dev->reg.init_reg(0x03, 0x50); dev->reg.init_reg(0x04, 0x12); + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x04, 0x20); + } dev->reg.init_reg(0x05, 0x80); dev->reg.init_reg(0x06, 0x50); // FASTMODE + POWERBIT + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x06, 0xf8); + } dev->reg.init_reg(0x08, 0x10); + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x08, 0x20); + } dev->reg.init_reg(0x09, 0x01); + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x09, 0x00); + } dev->reg.init_reg(0x0a, 0x00); dev->reg.init_reg(0x0b, 0x01); + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x0b, 0x6b); + } dev->reg.init_reg(0x0c, 0x02); + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x0c, 0x00); + } // 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(0x10, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x11, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x12, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x13, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x14, 0x00); // exposure, overwritten in scanner_setup_sensor() below + dev->reg.init_reg(0x15, 0x00); // exposure, overwritten in scanner_setup_sensor() below 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 @@ -142,32 +134,40 @@ gl847_init_registers (Genesys_Device * dev) dev->reg.init_reg(0x1c, 0x02); // SENSOR_DEF dev->reg.init_reg(0x1d, 0x04); // SENSOR_DEF dev->reg.init_reg(0x1e, 0x10); + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x1e, 0xf0); + } 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(0x20, 0x02); // BUFSEL: buffer full condition + dev->reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup + dev->reg.init_reg(0x22, 0x7f); // FWDSTEP: set during motor setup + dev->reg.init_reg(0x23, 0x7f); // BWDSTEP: set during motor setup + dev->reg.init_reg(0x24, 0x10); // FASTNO: set during motor setup + dev->reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup + dev->reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup + dev->reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup + + dev->reg.init_reg(0x2c, 0x09); // DPISET: set during sensor setup + dev->reg.init_reg(0x2d, 0x60); // DPISET: set during sensor setup + + dev->reg.init_reg(0x2e, 0x80); // BWHI: black/white low threshdold + dev->reg.init_reg(0x2f, 0x80); // BWLOW: black/white low threshold + + dev->reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup + dev->reg.init_reg(0x31, 0x10); // STRPIXEL: set during sensor setup + dev->reg.init_reg(0x32, 0x15); // ENDPIXEL: set during sensor setup + dev->reg.init_reg(0x33, 0x0e); // ENDPIXEL: set during sensor setup + + dev->reg.init_reg(0x34, 0x40); // DUMMY: SENSOR_DEF + dev->reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup + dev->reg.init_reg(0x36, 0x2a); // MAXWD: set during scan setup + dev->reg.init_reg(0x37, 0x30); // MAXWD: set during scan setup + dev->reg.init_reg(0x38, 0x2a); // LPERIOD: SENSOR_DEF + dev->reg.init_reg(0x39, 0xf8); // LPERIOD: SENSOR_DEF + dev->reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup + dev->reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup + dev->reg.init_reg(0x3f, 0x00); // FEEDL: set during motor setup + dev->reg.init_reg(0x52, 0x03); // SENSOR_DEF dev->reg.init_reg(0x53, 0x07); // SENSOR_DEF dev->reg.init_reg(0x54, 0x00); // SENSOR_DEF @@ -177,30 +177,27 @@ gl847_init_registers (Genesys_Device * dev) 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(0x5e, 0x41); // DECSEL, STOPTIM + dev->reg.init_reg(0x5f, 0x40); // FMOVDEC: set during motor setup + + dev->reg.init_reg(0x60, 0x00); // Z1MOD: overwritten during motor setup + dev->reg.init_reg(0x61, 0x21); // Z1MOD: overwritten during motor setup + dev->reg.init_reg(0x62, 0x40); // Z1MOD: overwritten during motor setup + dev->reg.init_reg(0x63, 0x00); // Z2MOD: overwritten during motor setup + dev->reg.init_reg(0x64, 0x21); // Z2MOD: overwritten during motor setup + dev->reg.init_reg(0x65, 0x40); // Z2MOD: overwritten during motor setup + dev->reg.init_reg(0x67, 0x80); // STEPSEL, MTRPWM: overwritten during motor setup + dev->reg.init_reg(0x68, 0x80); // FSTPSEL, FASTPWM: overwritten during motor setup + dev->reg.init_reg(0x69, 0x20); // FSHDEC: overwritten during motor setup + dev->reg.init_reg(0x6a, 0x20); // FMOVNO: overwritten during motor setup + 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 @@ -208,11 +205,23 @@ gl847_init_registers (Genesys_Device * dev) 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); + dev->reg.init_reg(0x87, 0x02); // TODO: move to SENSOR_DEF + dev->reg.init_reg(0x9d, 0x06); // RAMDLY, MOTLAG, CMODE, STEPTIM, IFRS + dev->reg.init_reg(0xa2, 0x0f); // misc + + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0xab, 0x31); + dev->reg.init_reg(0xbb, 0x00); + dev->reg.init_reg(0xbc, 0x0f); + } + dev->reg.init_reg(0xbd, 0x18); // misc + dev->reg.init_reg(0xfe, 0x08); // misc + if (dev->model->model_id == ModelId::CANON_5600F) { + dev->reg.init_reg(0x9e, 0x00); // sensor reg, but not in SENSOR_DEF + dev->reg.init_reg(0x9f, 0x00); // sensor reg, but not in SENSOR_DEF + dev->reg.init_reg(0xaa, 0x00); // custom data + dev->reg.init_reg(0xff, 0x00); + } // gamma[0] and gamma[256] values dev->reg.init_reg(0xbe, 0x00); @@ -237,233 +246,180 @@ gl847_init_registers (Genesys_Device * dev) } const auto& sensor = sanei_genesys_find_sensor_any(dev); - sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, + 3, ScanMethod::FLATBED); + sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); - /* initalize calibration reg */ - dev->calib_reg = dev->reg; + if (dev->model->model_id == ModelId::CANON_5600F) { + scanner_setup_sensor(*dev, sensor, 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) +// Set values of analog frontend +void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const { - 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; - } + DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : + set == AFE_SET ? "set" : + set == AFE_POWER_SAVE ? "powersave" : "huh?"); - 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); - } + (void) sensor; - if (dev->interface->is_mock()) { - dev->interface->record_slope_table(table_nr, slope_table); + if (dev->model->model_id != ModelId::CANON_5600F) { + // FIXME: remove the following read + dev->interface->read_register(REG_0x04); } - // 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)); + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; + } - for (i = 0; i < 3; i++) { - dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i)); + if (dev->model->model_id != ModelId::CANON_5600F) { + // reset DAC (BUG: this does completely different thing on Analog Devices ADCs) + dev->interface->write_fe_register(0x00, 0x80); + } else { + if (dev->frontend.layout.type == FrontendType::WOLFSON) { + // reset DAC + dev->interface->write_fe_register(0x04, 0xff); + } } - for (i = 0; i < 3; i++) { - dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i)); + + for (const auto& reg : dev->frontend.regs) { + dev->interface->write_fe_register(reg.address, reg.value); } } -// Set values of analog frontend -void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const +static void gl847_write_motor_phase_table(Genesys_Device& dev, unsigned ydpi) { - 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; + (void) ydpi; + if (dev.model->model_id == ModelId::CANON_5600F) { + std::vector<std::uint8_t> phase_table = { + 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, + 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, + 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, + 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, + 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, + }; + dev.interface->write_ahb(0x01000a00, phase_table.size(), phase_table.data()); } - - 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, + const MotorProfile& motor_profile, unsigned int scan_exposure_time, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, - MotorFlag flags) + ScanFlag 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; + bool use_fast_fed = false; + if (dev->settings.yres == 4444 && feed_steps > 100 && !has_flag(flags, ScanFlag::FEEDING)) { + use_fast_fed = true; + } + if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { + use_fast_fed = false; } - 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); + reg->set8(REG_0x02, 0); + sanei_genesys_set_motor_power(*reg, true); + std::uint8_t reg02 = reg->get8(REG_0x02); if (use_fast_fed) { - r->value |= REG_0x02_FASTFED; + reg02 |= REG_0x02_FASTFED; } else { - r->value &= ~REG_0x02_FASTFED; + reg02 &= ~REG_0x02_FASTFED; } - if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { - r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { + reg02 |= 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, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres >= sensor.full_resolution)) { + reg02 |= REG_0x02_ACDCDIS; } - - if (has_flag(flags, MotorFlag::REVERSE)) { - r->value |= REG_0x02_MTRREV; + if (has_flag(flags, ScanFlag::REVERSE)) { + reg02 |= REG_0x02_MTRREV; } else { - r->value &= ~REG_0x02_MTRREV; + reg02 &= ~REG_0x02_MTRREV; } + reg->set8(REG_0x02, reg02); - /* 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); + // scan and backtracking slope table + auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, + scan_exposure_time, step_multiplier, motor_profile); + scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); + scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); - /* fast table */ - fast_dpi=sanei_genesys_get_lowest_ydpi(dev); + // fast table + unsigned 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; + MotorProfile 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); + auto fast_table = create_slope_table(dev->model->asic_type, dev->motor, fast_dpi, + scan_exposure_time, step_multiplier, fast_motor_profile); + + scanner_send_slope_table(dev, sensor, STOP_TABLE, fast_table.table); + scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); + scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); - 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); + gl847_write_motor_phase_table(*dev, scan_yres); - /* correct move distance by acceleration and deceleration amounts */ - feedl=feed_steps; - if (use_fast_fed) + // correct move distance by acceleration and deceleration amounts + unsigned feedl = feed_steps; + unsigned dist = 0; + 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 - { + dist = (scan_table.table.size() + 2 * fast_table.table.size()); + // TODO read and decode REG_0xAB + dist += (reg->get8(0x5e) & 31); + dist += reg->get8(REG_FEDCNT); + } else { feedl <<= static_cast<unsigned>(motor_profile.step_type); - dist = scan_table.steps_count; - if (has_flag(flags, MotorFlag::FEED)) { + dist = scan_table.table.size(); + if (has_flag(flags, ScanFlag::FEEDING)) { dist *= 2; } } - DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist); - /* check for overflow */ + // check for overflow if (dist < feedl) { feedl -= dist; } else { @@ -471,25 +427,20 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev, } 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); + unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1; + unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME); // hi res motor speed GPIO uint8_t effective = dev->interface->read_register(REG_0x6C); // if quarter step, bipolar Vref2 + std::uint8_t val = effective; 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); @@ -498,45 +449,37 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev, val = effective | REG_0x6C_GPIO10; dev->interface->write_register(REG_0x6C, val); - min_restep = scan_table.steps_count / (2 * step_multiplier) - 1; + unsigned min_restep = scan_table.table.size() / (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; + reg->set8(REG_FWDSTEP, min_restep); + reg->set8(REG_BWDSTEP, min_restep); + + std::uint32_t z1, z2; sanei_genesys_calculate_zmod(use_fast_fed, - scan_exposure_time*ccdlmt*tgtime, + scan_exposure_time * ccdlmt * tgtime, scan_table.table, - scan_table.steps_count, - feedl, + scan_table.table.size(), + 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 */ + reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); - r = sanei_genesys_get_address(reg, REG_0x67); - r->value = REG_0x67_MTRPWM; + reg->set8(REG_0x67, REG_0x67_MTRPWM); + reg->set8(REG_0x68, REG_0x68_FASTPWM); - 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); + reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); + reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); + reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); + reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); } @@ -563,84 +506,84 @@ static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens 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); + scanner_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; + reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || - (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) + has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || + session.use_host_side_calib) { - r->value &= ~REG_0x01_DVDSET; - } - else - { - r->value |= REG_0x01_DVDSET; + reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; + } else { + reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } + reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; - r = sanei_genesys_get_address (reg, REG_0x03); - r->value &= ~REG_0x03_AVEENB; - + reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; + if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { + reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; + } sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); + reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); + + if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { + if (dev->model->model_id == ModelId::CANON_5600F) { + regs_set_exposure(dev->model->asic_type, *reg, sanei_genesys_fixup_exposure({0, 0, 0})); + } + } - /* 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; + // BW threshold + reg->set8(0x2e, 0x7f); + reg->set8(0x2f, 0x7f); /* 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); + reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: - r->value &= ~REG_0x04_LINEART; - r->value |= REG_0x04_BITSET; + reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; + reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } - r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); + reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: - r->value |= 0x14; + reg->find_reg(REG_0x04).value |= 0x14; break; case ColorFilter::BLUE: - r->value |= 0x1c; + reg->find_reg(REG_0x04).value |= 0x1c; break; case ColorFilter::GREEN: - r->value |= 0x18; + reg->find_reg(REG_0x04).value |= 0x18; break; default: break; // should not happen } } else { - r->value |= 0x10; // mono + if (dev->model->model_id == ModelId::CANON_5600F) { + reg->find_reg(REG_0x04).value |= 0x20; + } else { + reg->find_reg(REG_0x04).value |= 0x10; // mono + } } - sanei_genesys_set_dpihw(*reg, sensor, dpihw); + const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, + session.params.channels, + session.params.scan_method); + sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -651,38 +594,30 @@ static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens /* 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; + reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; + if (session.enable_ledadd) { - r->value |= REG_0x87_LEDADD; + reg->find_reg(0x87).value |= REG_0x87_LEDADD; } /* RGB weighting - r = sanei_genesys_get_address (reg, 0x01); - r->value &= ~REG_0x01_TRUEGRAY; + reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY; if (session.enable_ledadd) { - r->value |= REG_0x01_TRUEGRAY; + reg->find_reg(0x01).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_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); - build_image_pipeline(dev, session); + setup_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; + reg->set8(0x34, sensor.dummy_pixel); } void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -692,13 +627,18 @@ void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Gene DBG_HELPER(dbg); session.assert_computed(); - int move; int exposure_time; int slope_dpi = 0; int dummy = 0; - dummy = 3 - session.params.channels; + if (dev->model->model_id == ModelId::CANON_LIDE_100 || + dev->model->model_id == ModelId::CANON_LIDE_200 || + dev->model->model_id == ModelId::CANON_LIDE_700F || + dev->model->model_id == ModelId::HP_SCANJET_N6310) + { + dummy = 3 - session.params.channels; + } /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color @@ -712,40 +652,15 @@ void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Gene 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)); + const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session); /* 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); + session.optical_line_count, dummy, session.params.starty, + session.params.flags); dev->read_active = true; @@ -761,21 +676,59 @@ 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); + // backtracking isn't handled well, so don't enable it + ScanFlag flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE; + + /* 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 + */ + unsigned move_dpi = dev->motor.base_ydpi; + + float move = dev->model->y_offset; + if (settings.scan_method == ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + // note: scanner_move_to_ta() function has already been called and the sensor is at the + // transparency adapter + if (!dev->ignore_offsets) { + move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; + } + flags |= ScanFlag::USE_XPA; + } else { + if (!dev->ignore_offsets) { + move = dev->model->y_offset; + } + } + + move = move + settings.tl_y; + move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + move -= dev->head_pos(ScanHeadId::PRIMARY); + + float start = dev->model->x_offset; + if (settings.scan_method == ScanMethod::TRANSPARENCY || + settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + start = dev->model->x_offset_ta; + } else { + start = dev->model->x_offset; + } + + start = start + dev->settings.tl_x; + start = static_cast<float>((start * settings.xres) / 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.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; @@ -784,7 +737,7 @@ ScanSession CommandSetGl847::calculate_scan_session(const Genesys_Device* dev, 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; + session.params.flags = flags; compute_session(dev, session, sensor); @@ -811,25 +764,61 @@ void CommandSetGl847::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens DBG_HELPER(dbg); (void) sensor; uint8_t val; - GenesysRegister *r; - // clear GPIO 10 - if (dev->model->gpio_id != GpioId::CANON_LIDE_700F) { + if (reg->state.is_xpa_on && reg->state.is_lamp_on) { + dev->cmd_set->set_xpa_lamp_power(*dev, true); + } + + if (dev->model->model_id == ModelId::HP_SCANJET_N6310 || + dev->model->model_id == ModelId::CANON_LIDE_100 || + dev->model->model_id == ModelId::CANON_LIDE_200) + { 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); + if (dev->model->model_id == ModelId::CANON_5600F) { + switch (dev->session.params.xres) { + case 75: + case 150: + case 300: + scanner_register_rw_bits(*dev, REG_0xA6, 0x04, 0x1c); + break; + case 600: + scanner_register_rw_bits(*dev, REG_0xA6, 0x18, 0x1c); + break; + case 1200: + scanner_register_rw_bits(*dev, REG_0xA6, 0x08, 0x1c); + break; + case 2400: + scanner_register_rw_bits(*dev, REG_0xA6, 0x10, 0x1c); + break; + case 4800: + scanner_register_rw_bits(*dev, REG_0xA6, 0x00, 0x1c); + break; + default: + throw SaneException("Unexpected xres"); + } + dev->interface->write_register(0x6c, 0xf0); + dev->interface->write_register(0x6b, 0x87); + dev->interface->write_register(0x6d, 0x5f); + } + + if (dev->model->model_id == ModelId::CANON_5600F) { + scanner_clear_scan_and_feed_counts(*dev); + } else { + // FIXME: use scanner_clear_scan_and_feed_counts() + 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; + reg->set8(REG_0x01, val); scanner_start_action(*dev, start_motor); @@ -844,268 +833,86 @@ void CommandSetGl847::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, (void) reg; DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); + if (reg->state.is_xpa_on) { + dev->cmd_set->set_xpa_lamp_power(*dev, false); + } + 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, ®s, 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; + unsigned move_dpi = dev->motor.base_ydpi; - /* initial calibration reg values */ - regs = dev->reg; + float calib_size_mm = 0; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + calib_size_mm = dev->model->y_size_calib_ta_mm; + } else { + calib_size_mm = dev->model->y_size_calib_mm; + } - dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres); + unsigned channels = 3; + unsigned resolution = sensor.shading_resolution; - const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution, - dev->calib_channels, + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 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; + float move = 0; + ScanFlag flags = ScanFlag::DISABLE_SHADING | + ScanFlag::DISABLE_GAMMA | + ScanFlag::DISABLE_BUFFER_FULL_MOVE; + + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { + // note: scanner_move_to_ta() function has already been called and the sensor is at the + // transparency adapter + move = dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta; + flags |= ScanFlag::USE_XPA; + } else { + move = dev->model->y_offset_calib_white; } - 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); + move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + + unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH); ScanSession session; - session.params.xres = dev->calib_resolution; - session.params.yres = dev->motor.base_ydpi; + session.params.xres = resolution; + session.params.yres = resolution; session.params.startx = 0; - session.params.starty = 20; - session.params.pixels = dev->calib_pixels; - session.params.lines = dev->calib_lines; + session.params.starty = static_cast<unsigned>(move); + session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; + session.params.lines = calib_lines; session.params.depth = 16; - session.params.channels = dev->calib_channels; + 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::DISABLE_BUFFER_FULL_MOVE | - ScanFlag::IGNORE_LINE_DISTANCE; + session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); - dev->interface->write_registers(regs); - - /* we use GENESYS_FLAG_SHADING_REPARK */ + /* we use ModelFlag::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); + dev->calib_session = session; } - /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. @@ -1114,39 +921,24 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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; + std::uint32_t addr, i; 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; + unsigned length = static_cast<unsigned>(size / 3); - /* 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); + // we're using SHDAREA, thus we only need to upload part of the line + unsigned offset = dev->session.pixel_count_ratio.apply( + dev->session.params.startx * sensor.full_resolution / dev->session.params.xres); + unsigned pixels = dev->session.pixel_count_ratio.apply(dev->session.optical_pixels_raw); - pixels=endpixel-strpixel; + // turn pixel value into bytes 2x16 bits words + offset *= 2 * 2; + pixels *= 2 * 2; - /* 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_offset", std::to_string(offset)); 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_factor", std::to_string(sensor.shading_factor)); std::vector<uint8_t> buffer(pixels, 0); @@ -1155,6 +947,10 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address * is 8192*reg value */ + if (dev->model->model_id == ModelId::CANON_5600F) { + return; + } + /* write actual color channel data */ for(i=0;i<3;i++) { @@ -1162,11 +958,10 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso * to the one corresponding to SHDAREA */ ptr = buffer.data(); - /* iterate on both sensor segment */ - for(x=0;x<pixels;x+=4*factor) - { + // iterate on both sensor segment + for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) { /* coefficient source */ - src=(data+strpixel+i*length)+x; + src = (data + offset + i * length) + x; /* coefficient copy */ ptr[0]=src[0]; @@ -1192,160 +987,7 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso 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, ®s, 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, ®s, 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] }; + return scanner_led_calibration(*dev, sensor, regs); } /** @@ -1354,31 +996,37 @@ SensorExposure CommandSetGl847::led_calibration(Genesys_Device* dev, const Genes 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)); - } + if (dev->model->model_id == ModelId::CANON_5600F) { + apply_registers_ordered(dev->gpo.regs, {0xa6, 0xa7, 0x6f, 0x6e}, + [&](const GenesysRegisterSetting& reg) + { + dev->interface->write_register(reg.address, reg.value); + }); + } else { + std::vector<std::uint16_t> order1 = { 0xa7, 0xa6, 0x6e }; + std::vector<std::uint16_t> order2 = { 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0xa8, 0xa9 }; - dev->interface->write_register(REG_0xA7, gpios[idx].ra7); - dev->interface->write_register(REG_0xA6, gpios[idx].ra6); + for (auto addr : order1) { + dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); + } - dev->interface->write_register(REG_0x6E, gpios[idx].r6e); - dev->interface->write_register(REG_0x6C, 0x00); + dev->interface->write_register(REG_0x6C, 0x00); // FIXME: Likely not needed - 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); + for (auto addr : order2) { + dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); + } - dev->interface->write_register(REG_0xA8, gpios[idx].ra8); - dev->interface->write_register(REG_0xA9, gpios[idx].ra9); + for (const auto& reg : dev->gpo.regs) { + if (std::find(order1.begin(), order1.end(), reg.address) != order1.end()) { + continue; + } + if (std::find(order2.begin(), order2.end(), reg.address) != order2.end()) { + continue; + } + dev->interface->write_register(reg.address, reg.value); + } + } } /** @@ -1387,77 +1035,24 @@ static void gl847_init_gpio(Genesys_Device* dev) 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; + // FIXME: move to initial register list + switch (dev->model->model_id) { + case ModelId::CANON_LIDE_100: + case ModelId::CANON_LIDE_200: + dev->interface->write_register(REG_0x0B, 0x29); + break; + case ModelId::CANON_LIDE_700F: + dev->interface->write_register(REG_0x0B, 0x2a); + break; + default: + break; } - /* 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); + // prevent further writings by bulk write register + dev->reg.remove_reg(0x0b); + + apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /* * @@ -1486,15 +1081,17 @@ void CommandSetGl847::asic_boot(Genesys_Device* dev, bool cold) const // 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_5600F) { + // Enable DRAM by setting a rising edge on bit 3 of reg 0x0b + // The initial register write also powers on SDRAM + 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); + // TODO: remove this write + dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value); + } // set up end access dev->interface->write_0x8c(0x10, 0x0b); @@ -1506,8 +1103,11 @@ void CommandSetGl847::asic_boot(Genesys_Device* dev, bool cold) const // 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); + if (dev->model->model_id != ModelId::CANON_5600F) { + // FIXME: move to memory layout + dev->reg.init_reg(0xf8, 0x01); + dev->interface->write_register(0xf8, dev->reg.find_reg(0xf8).value); + } } /** @@ -1519,7 +1119,7 @@ void CommandSetGl847::init(Genesys_Device* dev) const DBG_INIT (); DBG_HELPER(dbg); - sanei_genesys_asic_init(dev, 0); + sanei_genesys_asic_init(dev); } void CommandSetGl847::update_hardware_sensors(Genesys_Scanner* s) const @@ -1566,517 +1166,16 @@ void CommandSetGl847::update_home_sensor_gpio(Genesys_Device& dev) const } } -/** @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, ®s, 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, ®s, 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, ®s, 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, ®s, 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)); + scanner_offset_calibration(*dev, sensor, regs); } 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, ®s, 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, ®s, 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); + scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } bool CommandSetGl847::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const @@ -2086,14 +1185,11 @@ bool CommandSetGl847::needs_home_before_init_regs_for_scan(Genesys_Device* dev) } void CommandSetGl847::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, - Genesys_Register_Set* regs, int* channels, - int* total_size) const + Genesys_Register_Set* regs) const { (void) dev; (void) sensor; (void) regs; - (void) channels; - (void) total_size; throw SaneException("not implemented"); } @@ -2125,16 +1221,5 @@ void CommandSetGl847::eject_document(Genesys_Device* dev) const 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 index a51c293..aa4fb85 100644 --- a/backend/genesys/gl847.h +++ b/backend/genesys/gl847.h @@ -45,75 +45,12 @@ #define BACKEND_GENESYS_GL847_H #include "genesys.h" -#include "command_set.h" +#include "command_set_common.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 +class CommandSetGl847 : public CommandSetCommon { public: ~CommandSetGl847() override = default; @@ -123,17 +60,11 @@ public: 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; + 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; @@ -149,8 +80,6 @@ public: 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; @@ -166,8 +95,6 @@ public: 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; @@ -176,11 +103,6 @@ public: 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; diff --git a/backend/genesys/gl847_registers.h b/backend/genesys/gl847_registers.h index 0603a6a..aa3d43b 100644 --- a/backend/genesys/gl847_registers.h +++ b/backend/genesys/gl847_registers.h @@ -190,6 +190,7 @@ static constexpr RegMask REG_0x1D_CK1LOW = 0x20; static constexpr RegMask REG_0x1D_TGSHLD = 0x1f; static constexpr RegMask REG_0x1DS_TGSHLD = 0; +static constexpr RegAddr REG_0x1E = 0x1e; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegMask REG_0x1ES_WDTIME = 4; static constexpr RegMask REG_0x1E_LINESEL = 0x0f; diff --git a/backend/genesys/image.cpp b/backend/genesys/image.cpp index 7d386c6..793a209 100644 --- a/backend/genesys/image.cpp +++ b/backend/genesys/image.cpp @@ -45,6 +45,10 @@ #include "image.h" +#if defined(HAVE_TIFFIO_H) +#include <tiffio.h> +#endif + #include <array> namespace genesys { @@ -201,4 +205,68 @@ void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format } } +void write_tiff_file(const std::string& filename, const void* 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); +#if defined(HAVE_TIFFIO_H) + auto image = TIFFOpen(filename.c_str(), "w"); + if (!image) { + dbg.log(DBG_error, "Could not save debug image"); + return; + } + TIFFSetField(image, TIFFTAG_IMAGEWIDTH, pixels_per_line); + TIFFSetField(image, TIFFTAG_IMAGELENGTH, lines); + TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, depth); + TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, channels); + if (channels > 1) { + TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + } else { + TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } + TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(image, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + + std::size_t bytes_per_line = (pixels_per_line * channels * depth + 7) / 8; + const std::uint8_t* data_ptr = reinterpret_cast<const std::uint8_t*>(data); + + // we don't need to handle endian because libtiff will handle that + for (int iline = 0; iline < lines; ++iline) { + const auto* line_data = data_ptr + bytes_per_line * iline; + TIFFWriteScanline(image, const_cast<std::uint8_t*>(line_data), iline, 0); + } + TIFFClose(image); + +#else + dbg.log(DBG_error, "Backend has been built without TIFF library support. " + "Debug images will not be saved"); +#endif +} + +bool is_supported_write_tiff_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 write_tiff_file(const std::string& filename, const Image& image) +{ + if (!is_supported_write_tiff_file_image_format(image.get_format())) { + throw SaneException("Unsupported format %d", static_cast<unsigned>(image.get_format())); + } + + write_tiff_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()); +} + } // namespace genesys diff --git a/backend/genesys/image.h b/backend/genesys/image.h index c96b1bb..798594e 100644 --- a/backend/genesys/image.h +++ b/backend/genesys/image.h @@ -82,6 +82,11 @@ private: 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); +void write_tiff_file(const std::string& filename, const void* data, int depth, + int channels, int pixels_per_line, int lines); + +void write_tiff_file(const std::string& filename, const Image& image); + } // namespace genesys #endif // ifndef BACKEND_GENESYS_IMAGE_H diff --git a/backend/genesys/image_buffer.cpp b/backend/genesys/image_buffer.cpp index 07c6987..c4f8019 100644 --- a/backend/genesys/image_buffer.cpp +++ b/backend/genesys/image_buffer.cpp @@ -45,13 +45,13 @@ #include "image_buffer.h" #include "image.h" +#include "utilities.h" namespace genesys { ImageBuffer::ImageBuffer(std::size_t size, ProducerCallback producer) : producer_{producer}, - size_{size}, - buffer_offset_{size} + size_{size} { buffer_.resize(size_); } @@ -81,123 +81,30 @@ bool ImageBuffer::get_data(std::size_t size, std::uint8_t* out_data) 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} -{} + std::size_t size_to_read = size_; + if (remaining_size_ != BUFFER_SIZE_UNSET) { + size_to_read = std::min<std::uint64_t>(size_to_read, remaining_size_); + remaining_size_ -= size_to_read; + } -bool ImageBufferGenesysUsb::get_data(std::size_t size, std::uint8_t* out_data) -{ - const std::uint8_t* out_data_end = out_data + size; + std::size_t aligned_size_to_read = size_to_read; + if (remaining_size_ == 0 && last_read_multiple_ != BUFFER_SIZE_UNSET) { + aligned_size_to_read = align_multiple_ceil(size_to_read, last_read_multiple_); + } - 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; - }; + got_data &= producer_(aligned_size_to_read, buffer_.data()); + curr_size_ = size_to_read; - // 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; + if (remaining_size_ == 0 && out_data < out_data_end) { + got_data = false; } - 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(); + } while (out_data < out_data_end && got_data); - // 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; + return got_data; } } // namespace genesys diff --git a/backend/genesys/image_buffer.h b/backend/genesys/image_buffer.h index 43c3eb7..1910244 100644 --- a/backend/genesys/image_buffer.h +++ b/backend/genesys/image_buffer.h @@ -56,72 +56,33 @@ class ImageBuffer { public: using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>; + static constexpr std::uint64_t BUFFER_SIZE_UNSET = std::numeric_limits<std::uint64_t>::max(); ImageBuffer() {} ImageBuffer(std::size_t size, ProducerCallback producer); - std::size_t size() const { return size_; } - std::size_t available() const { return size_ - buffer_offset_; } + std::size_t available() const { return curr_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); + // allows adjusting the amount of data left so that we don't do a full size read from the + // producer on the last iteration. Set to BUFFER_SIZE_UNSET to ignore buffer size. + std::uint64_t remaining_size() const { return remaining_size_; } + void set_remaining_size(std::uint64_t bytes) { remaining_size_ = bytes; } -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_; } + // May be used to force the last read to be rounded up of a certain number of bytes + void set_last_read_multiple(std::uint64_t bytes) { last_read_multiple_ = bytes; } bool get_data(std::size_t size, std::uint8_t* out_data); private: + ProducerCallback producer_; + std::size_t size_ = 0; + std::size_t curr_size_ = 0; - std::size_t get_read_size(); - - std::size_t remaining_size_ = 0; + std::uint64_t remaining_size_ = BUFFER_SIZE_UNSET; + std::uint64_t last_read_multiple_ = BUFFER_SIZE_UNSET; 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 diff --git a/backend/genesys/image_pipeline.cpp b/backend/genesys/image_pipeline.cpp index c01b7f4..8d67be9 100644 --- a/backend/genesys/image_pipeline.cpp +++ b/backend/genesys/image_pipeline.cpp @@ -53,15 +53,6 @@ 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); @@ -78,7 +69,7 @@ ImagePipelineNodeBufferedCallableSource::ImagePipelineNodeBufferedCallableSource format_{format}, buffer_{input_batch_size, producer} { - set_remaining_bytes(height_ * get_row_bytes()); + buffer_.set_remaining_size(height_ * get_row_bytes()); } bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* out_data) @@ -92,13 +83,7 @@ bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* ou 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); + got_data &= buffer_.get_data(get_row_bytes(), out_data); curr_row_++; if (!got_data) { eof_ = true; @@ -106,37 +91,6 @@ bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* ou 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) : @@ -151,7 +105,6 @@ ImagePipelineNodeArraySource::ImagePipelineNodeArraySource(std::size_t width, st 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) @@ -161,21 +114,11 @@ bool ImagePipelineNodeArraySource::get_next_row_data(std::uint8_t* out_data) 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); + std::memcpy(out_data, data_.data() + row_bytes * next_row_, row_bytes); next_row_++; - if (!got_data) { - eof_ = true; - } - return got_data; + return true; } @@ -319,6 +262,50 @@ bool ImagePipelineNodeSwap16BitEndian::get_next_row_data(std::uint8_t* out_data) return got_data; } +ImagePipelineNodeInvert::ImagePipelineNodeInvert(ImagePipelineNode& source) : + source_(source) +{ +} + +bool ImagePipelineNodeInvert::get_next_row_data(std::uint8_t* out_data) +{ + bool got_data = source_.get_next_row_data(out_data); + auto num_values = get_width() * get_pixel_channels(source_.get_format()); + auto depth = get_pixel_format_depth(source_.get_format()); + + switch (depth) { + case 16: { + auto* data = reinterpret_cast<std::uint16_t*>(out_data); + for (std::size_t i = 0; i < num_values; ++i) { + *data = 0xffff - *data; + data++; + } + break; + } + case 8: { + auto* data = out_data; + for (std::size_t i = 0; i < num_values; ++i) { + *data = 0xff - *data; + data++; + } + break; + } + case 1: { + auto* data = out_data; + auto num_bytes = (num_values + 7) / 8; + for (std::size_t i = 0; i < num_bytes; ++i) { + *data = ~*data; + data++; + } + break; + } + default: + throw SaneException("Unsupported pixel depth"); + } + + return got_data; +} + ImagePipelineNodeMergeMonoLines::ImagePipelineNodeMergeMonoLines(ImagePipelineNode& source, ColorOrder color_order) : source_(source), @@ -456,6 +443,12 @@ ImagePipelineNodeComponentShiftLines::ImagePipelineNodeComponentShiftLines( static_cast<unsigned>(source.get_format())); } extra_height_ = *std::max_element(channel_shifts_.begin(), channel_shifts_.end()); + height_ = source_.get_height(); + if (extra_height_ > height_) { + height_ = 0; + } else { + height_ -= extra_height_; + } } bool ImagePipelineNodeComponentShiftLines::get_next_row_data(std::uint8_t* out_data) @@ -492,18 +485,13 @@ ImagePipelineNodePixelShiftLines::ImagePipelineNodePixelShiftLines( 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()); + height_ = source_.get_height(); + if (extra_height_ > height_) { + height_ = 0; + } else { + height_ -= extra_height_; + } } bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data) @@ -521,7 +509,8 @@ bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data) auto format = get_format(); auto shift_count = pixel_shifts_.size(); - std::array<std::uint8_t*, MAX_SHIFTS> rows; + std::vector<std::uint8_t*> rows; + rows.resize(shift_count, nullptr); for (std::size_t irow = 0; irow < shift_count; ++irow) { rows[irow] = buffer_.get_row_ptr(pixel_shifts_[irow]); @@ -536,6 +525,63 @@ bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data) return got_data; } +ImagePipelineNodePixelShiftColumns::ImagePipelineNodePixelShiftColumns( + ImagePipelineNode& source, const std::vector<std::size_t>& shifts) : + source_(source), + pixel_shifts_{shifts} +{ + width_ = source_.get_width(); + extra_width_ = compute_pixel_shift_extra_width(width_, pixel_shifts_); + if (extra_width_ > width_) { + width_ = 0; + } else { + width_ -= extra_width_; + } + temp_buffer_.resize(source_.get_row_bytes()); +} + +bool ImagePipelineNodePixelShiftColumns::get_next_row_data(std::uint8_t* out_data) +{ + if (width_ == 0) { + throw SaneException("Attempt to read zero-width line"); + } + bool got_data = source_.get_next_row_data(temp_buffer_.data()); + + auto format = get_format(); + auto shift_count = pixel_shifts_.size(); + + for (std::size_t x = 0, width = get_width(); x < width; x += shift_count) { + for (std::size_t ishift = 0; ishift < shift_count && x + ishift < width; ishift++) { + RawPixel pixel = get_raw_pixel_from_row(temp_buffer_.data(), x + pixel_shifts_[ishift], + format); + set_raw_pixel_to_row(out_data, x + ishift, pixel, format); + } + } + return got_data; +} + + +std::size_t compute_pixel_shift_extra_width(std::size_t source_width, + const std::vector<std::size_t>& shifts) +{ + // we iterate across pixel shifts and find the pixel that needs the maximum shift according to + // source_width. + int group_size = shifts.size(); + int non_filled_group = source_width % shifts.size(); + int extra_width = 0; + + for (int i = 0; i < group_size; ++i) { + int shift_groups = shifts[i] / group_size; + int shift_rem = shifts[i] % group_size; + + if (shift_rem < non_filled_group) { + shift_groups--; + } + extra_width = std::max(extra_width, shift_groups * group_size + non_filled_group - i); + } + return extra_width; +} + ImagePipelineNodeExtract::ImagePipelineNodeExtract(ImagePipelineNode& source, std::size_t offset_x, std::size_t offset_y, std::size_t width, std::size_t height) : @@ -666,16 +712,21 @@ bool ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data) ImagePipelineNodeCalibrate::ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector<std::uint16_t>& bottom, - const std::vector<std::uint16_t>& top) : + const std::vector<std::uint16_t>& top, + std::size_t x_start) : source_{source} { - auto size = std::min(bottom.size(), top.size()); + std::size_t size = 0; + if (bottom.size() >= x_start && top.size() >= x_start) { + size = std::min(bottom.size() - x_start, top.size() - x_start); + } + 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])); + offset_.push_back(bottom[i + x_start] / 65535.0f); + multiplier_.push_back(65535.0f / (top[i + x_start] - bottom[i + x_start])); } } @@ -729,10 +780,8 @@ ImagePipelineNodeDebug::~ImagePipelineNodeDebug() 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()); + write_tiff_file(path_, buffer_.get_front_row_ptr(), get_pixel_format_depth(format), + get_pixel_channels(format), get_width(), buffer_.height()); }); } diff --git a/backend/genesys/image_pipeline.h b/backend/genesys/image_pipeline.h index 2986837..d4aef49 100644 --- a/backend/genesys/image_pipeline.h +++ b/backend/genesys/image_pipeline.h @@ -75,18 +75,6 @@ public: 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 { @@ -118,7 +106,7 @@ private: }; // A pipeline node that produces data from a callable requesting fixed-size chunks. -class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNodeBytesSource +class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNode { public: using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>; @@ -135,8 +123,9 @@ public: 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(); } + std::size_t remaining_bytes() const { return buffer_.remaining_size(); } + void set_remaining_bytes(std::size_t bytes) { buffer_.set_remaining_size(bytes); } + void set_last_read_multiple(std::size_t bytes) { buffer_.set_last_read_multiple(bytes); } private: ProducerCallback producer_; @@ -150,39 +139,8 @@ private: 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 +class ImagePipelineNodeArraySource : public ImagePipelineNode { public: ImagePipelineNodeArraySource(std::size_t width, std::size_t height, PixelFormat format, @@ -302,7 +260,7 @@ public: std::size_t pixels_per_chunk); }; -// A pipeline that swaps bytes in 16-bit components on big-endian systems +// A pipeline that swaps bytes in 16-bit components and does nothing otherwise. class ImagePipelineNodeSwap16BitEndian : public ImagePipelineNode { public: @@ -321,6 +279,23 @@ private: bool needs_swapping_ = false; }; +class ImagePipelineNodeInvert : public ImagePipelineNode +{ +public: + ImagePipelineNodeInvert(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_; +}; + // A pipeline node that merges 3 mono lines into a color channel class ImagePipelineNodeMergeMonoLines : public ImagePipelineNode { @@ -377,7 +352,7 @@ public: 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_; } + 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(); } @@ -387,23 +362,23 @@ public: private: ImagePipelineNode& source_; std::size_t extra_height_ = 0; + std::size_t height_ = 0; std::array<unsigned, 3> channel_shifts_; RowBuffer buffer_; }; -// A pipeline node that shifts pixels across lines by the given offsets (performs unstaggering) +// A pipeline node that shifts pixels across lines by the given offsets (performs vertical +// 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_; } + 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(); } @@ -413,12 +388,44 @@ public: private: ImagePipelineNode& source_; std::size_t extra_height_ = 0; + std::size_t height_ = 0; std::vector<std::size_t> pixel_shifts_; RowBuffer buffer_; }; +// A pipeline node that shifts pixels across columns by the given offsets. Each row is divided +// into pixel groups of shifts.size() pixels. For each output group starting at position xgroup, +// the i-th pixel will be set to the input pixel at position xgroup + shifts[i]. +class ImagePipelineNodePixelShiftColumns : public ImagePipelineNode +{ +public: + ImagePipelineNodePixelShiftColumns(ImagePipelineNode& source, + const std::vector<std::size_t>& shifts); + + 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::size_t extra_width_ = 0; + + std::vector<std::size_t> pixel_shifts_; + + std::vector<std::uint8_t> temp_buffer_; +}; + +// exposed for tests +std::size_t compute_pixel_shift_extra_width(std::size_t source_width, + const std::vector<std::size_t>& shifts); + // 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 @@ -476,7 +483,7 @@ class ImagePipelineNodeCalibrate : public ImagePipelineNode public: ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector<std::uint16_t>& bottom, - const std::vector<std::uint16_t>& top); + const std::vector<std::uint16_t>& top, std::size_t x_start); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } @@ -517,6 +524,19 @@ class ImagePipelineStack { public: ImagePipelineStack() {} + ImagePipelineStack(ImagePipelineStack&& other) + { + clear(); + nodes_ = std::move(other.nodes_); + } + + ImagePipelineStack& operator=(ImagePipelineStack&& other) + { + clear(); + nodes_ = std::move(other.nodes_); + return *this; + } + ~ImagePipelineStack() { clear(); } std::size_t get_input_width() const; @@ -536,20 +556,22 @@ public: void clear(); template<class Node, class... Args> - void push_first_node(Args&&... args) + Node& 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)...))); + return static_cast<Node&>(*nodes_.back()); } template<class Node, class... Args> - void push_node(Args&&... args) + Node& push_node(Args&&... args) { ensure_node_exists(); nodes_.emplace_back(std::unique_ptr<Node>(new Node(*nodes_.back(), std::forward<Args>(args)...))); + return static_cast<Node&>(*nodes_.back()); } bool get_next_row_data(std::uint8_t* out_data) diff --git a/backend/genesys/image_pixel.h b/backend/genesys/image_pixel.h index 2dda271..aa9980e 100644 --- a/backend/genesys/image_pixel.h +++ b/backend/genesys/image_pixel.h @@ -51,6 +51,7 @@ namespace genesys { +// 16-bit values are in host endian enum class PixelFormat { UNKNOWN, diff --git a/backend/genesys/low.cpp b/backend/genesys/low.cpp index 7937fcc..05ef46b 100644 --- a/backend/genesys/low.cpp +++ b/backend/genesys/low.cpp @@ -51,12 +51,23 @@ #include "gl124_registers.h" #include "gl646_registers.h" #include "gl841_registers.h" +#include "gl842_registers.h" #include "gl843_registers.h" #include "gl846_registers.h" #include "gl847_registers.h" #include "gl646_registers.h" +#include "gl124.h" +#include "gl646.h" +#include "gl841.h" +#include "gl842.h" +#include "gl843.h" +#include "gl846.h" +#include "gl847.h" +#include "gl646.h" + #include <cstdio> +#include <chrono> #include <cmath> #include <vector> @@ -66,29 +77,17 @@ 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) +std::unique_ptr<CommandSet> create_cmd_set(AsicType asic_type) { - 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; + switch (asic_type) { + case AsicType::GL646: return std::unique_ptr<CommandSet>(new gl646::CommandSetGl646{}); + case AsicType::GL841: return std::unique_ptr<CommandSet>(new gl841::CommandSetGl841{}); + case AsicType::GL842: return std::unique_ptr<CommandSet>(new gl842::CommandSetGl842{}); + case AsicType::GL843: return std::unique_ptr<CommandSet>(new gl843::CommandSetGl843{}); 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; + case AsicType::GL846: return std::unique_ptr<CommandSet>(new gl846::CommandSetGl846{}); + case AsicType::GL847: return std::unique_ptr<CommandSet>(new gl847::CommandSetGl847{}); + case AsicType::GL124: return std::unique_ptr<CommandSet>(new gl124::CommandSetGl124{}); default: throw SaneException(SANE_STATUS_INVAL, "unknown ASIC type"); } } @@ -108,116 +107,6 @@ void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, st 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 */ /* ------------------------------------------------------------------------ */ @@ -276,6 +165,7 @@ Status scanner_read_status(Genesys_Device& dev) case AsicType::GL124: address = 0x101; break; case AsicType::GL646: case AsicType::GL841: + case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: @@ -336,28 +226,23 @@ void debug_print_status(DebugMessageHelper& dbg, Status 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) +void scanner_register_rw_clear_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask) { - int pixels_per_line; - - pixels_per_line = reg->get8(0x32) * 256 + reg->get8(0x33); - pixels_per_line -= (reg->get8(0x30) * 256 + reg->get8(0x31)); + scanner_register_rw_bits(dev, address, 0x00, mask); +} - return pixels_per_line; +void scanner_register_rw_set_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask) +{ + scanner_register_rw_bits(dev, address, mask, mask); } -/* returns dpiset from register set */ -/*candidate for moving into chip specific files?*/ -static int -genesys_dpiset (Genesys_Register_Set * reg) +void scanner_register_rw_bits(Genesys_Device& dev, std::uint16_t address, + std::uint8_t value, std::uint8_t mask) { - return reg->get8(0x2c) * 256 + reg->get8(0x2d); + auto reg_value = dev.interface->read_register(address); + reg_value = (reg_value & ~mask) | (value & mask); + dev.interface->write_register(address, reg_value); } -#endif /** read the number of valid words in scanner's RAM * ie registers 42-43-44 @@ -481,7 +366,7 @@ 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) { + for (unsigned wait_ms = 0; wait_ms < 70000; wait_ms += sleep_time_ms) { sanei_genesys_read_valid_words(dev, &words); if (words != 0) break; @@ -516,7 +401,7 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& 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); + auto height = session.optical_line_count; Image image(width, height, format); @@ -525,7 +410,7 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& 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__, + DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu)\n", __func__, total_bytes, max_bytes); } @@ -534,26 +419,138 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& 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>(); + if (session.segment_count > 1) { + auto output_width = session.output_segment_pixel_group_count * session.segment_count; + pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order, + session.conseq_pixel_dist, + 1, 1); } + if (session.params.depth == 16) { + unsigned num_swaps = 0; + if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { + num_swaps++; + } #ifdef WORDS_BIGENDIAN - if (depth == 16) { - dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); + num_swaps++; +#endif + if (num_swaps % 2 != 0) { + dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); + } } + + if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { + pipeline.push_node<ImagePipelineNodeInvert>(); + } + + if (dev->model->is_cis && session.params.channels == 3) { + pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order); + } + + if (pipeline.get_output_format() == PixelFormat::BGR888) { + pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888); + } + + if (pipeline.get_output_format() == PixelFormat::BGR161616) { + pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); + } + + return pipeline.get_image(); +} + + +Image read_shuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session) +{ + DBG_HELPER(dbg); + + std::size_t total_bytes = 0; + std::size_t pixels_per_line = 0; + + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843 || + dev->model->model_id == ModelId::CANON_5600F) + { + pixels_per_line = session.output_pixels; + } else { + // BUG: this selects incorrect pixel number + pixels_per_line = session.params.pixels; + } + + // FIXME: the current calculation is likely incorrect on non-GL843 implementations, + // but this needs checking. Note the extra line when computing size. + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843 || + dev->model->model_id == ModelId::CANON_5600F) + { + total_bytes = session.output_total_bytes_raw; + } else { + total_bytes = session.params.channels * 2 * pixels_per_line * (session.params.lines + 1); + } + + 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 width = pixels_per_line; + auto height = session.params.lines + 1; // BUG: incorrect + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843 || + dev->model->model_id == ModelId::CANON_5600F) + { + height = session.optical_line_count; + } + + 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 (session.segment_count > 1) { + auto output_width = session.output_segment_pixel_group_count * session.segment_count; + pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order, + session.conseq_pixel_dist, + 1, 1); + } + + if (session.params.depth == 16) { + unsigned num_swaps = 0; + if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { + num_swaps++; + } +#ifdef WORDS_BIGENDIAN + num_swaps++; #endif + if (num_swaps % 2 != 0) { + dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); + } + } + + if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { + pipeline.push_node<ImagePipelineNodeInvert>(); + } if (dev->model->is_cis && session.params.channels == 3) { - dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order); + 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 (pipeline.get_output_format() == PixelFormat::BGR888) { + pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888); } - if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) { - dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); + if (pipeline.get_output_format() == PixelFormat::BGR161616) { + pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); } return pipeline.get_image(); @@ -600,34 +597,27 @@ void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sen 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; - } + // 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->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) && + 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_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0})); 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}); - } + if (dev->model->model_id == ModelId::CANON_5600F) { + regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0})); } } regs.state.is_lamp_on = set; @@ -786,218 +776,144 @@ void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& s } } -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) +void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s, + const Genesys_Sensor& sensor) { - 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} - {} - }; + if (dev->model->asic_type == AsicType::GL646) { + s.pixel_startx += s.output_startx * sensor.full_resolution / s.params.xres; + s.pixel_endx = s.pixel_startx + s.optical_pixels * s.full_resolution / s.optical_resolution; - 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; + } else if (dev->model->asic_type == AsicType::GL841 || + dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843 || + dev->model->asic_type == AsicType::GL845 || + dev->model->asic_type == AsicType::GL846 || + dev->model->asic_type == AsicType::GL847) + { + unsigned startx_xres = s.optical_resolution; + if (dev->model->model_id == ModelId::CANON_5600F || + dev->model->model_id == ModelId::CANON_LIDE_90) + { + if (s.output_resolution == 1200) { + startx_xres /= 2; + } + if (s.output_resolution >= 2400) { + startx_xres /= 4; + } } - 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.pixel_startx = (s.output_startx * startx_xres) / s.params.xres; + s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; - 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) + } else if (dev->model->asic_type == AsicType::GL124) { - s.pipeline_needs_reorder = false; + s.pixel_startx = s.output_startx * sensor.full_resolution / s.params.xres; + s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; } -#endif - if (channels == 3 && depth == 8 && !dev->model->is_cis && - dev->model->line_mode_color_order == ColorOrder::RGB) + + // align pixels to correct boundary for unstaggering + unsigned needed_x_alignment = std::max(s.stagger_x.size(), s.stagger_y.size()); + unsigned aligned_pixel_startx = align_multiple_floor(s.pixel_startx, needed_x_alignment); + s.pixel_endx -= s.pixel_startx - aligned_pixel_startx; + s.pixel_startx = aligned_pixel_startx; + + s.pixel_startx = sensor.pixel_count_ratio.apply(s.pixel_startx); + s.pixel_endx = sensor.pixel_count_ratio.apply(s.pixel_endx); + + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 || + 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.pipeline_needs_reorder = false; + s.pixel_startx = align_multiple_floor(s.pixel_startx, sensor.pixel_count_ratio.divisor()); + s.pixel_endx = align_multiple_floor(s.pixel_endx, sensor.pixel_count_ratio.divisor()); } - 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 session_adjust_output_pixels(unsigned output_pixels, + const Genesys_Device& dev, const Genesys_Sensor& sensor, + unsigned output_xresolution, unsigned output_yresolution, + bool adjust_output_pixels) { - 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; + bool adjust_optical_pixels = !adjust_output_pixels; + if (dev.model->model_id == ModelId::CANON_5600F) { + adjust_optical_pixels = true; + adjust_output_pixels = true; + } + if (adjust_optical_pixels) { + auto optical_resolution = sensor.get_optical_resolution(); - s.pixel_startx += sensor.dummy_pixel + 1; + // FIXME: better way would be to compute and return the required multiplier + unsigned optical_pixels = (output_pixels * optical_resolution) / output_xresolution; - if (s.num_staggered_lines > 0 && (s.pixel_startx & 1) == 0) { - s.pixel_startx++; + if (dev.model->asic_type == AsicType::GL841 || + dev.model->asic_type == AsicType::GL842) + { + optical_pixels = align_multiple_ceil(optical_pixels, 2); } - /* 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); + if (dev.model->asic_type == AsicType::GL646 && output_xresolution == 400) { + optical_pixels = align_multiple_floor(optical_pixels, 6); } - 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++; + 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 + optical_pixels = align_multiple_ceil(optical_pixels, + 2 * sensor.full_resolution / optical_resolution); + if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 || + dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || + dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || + dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I || + dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + optical_pixels = align_multiple_ceil(optical_pixels, 16); } } + output_pixels = (optical_pixels * output_xresolution) / optical_resolution; + } - } 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 (adjust_output_pixels) { + // TODO: the following may no longer be needed but were applied historically. - if (s.num_staggered_lines > 0) { - s.pixel_startx |= 1; + // we need an even pixels number + // TODO invert test logic or generalize behaviour across all ASICs + if (has_flag(dev.model->flags, ModelFlag::SIS_SENSOR) || + dev.model->asic_type == AsicType::GL847 || + dev.model->asic_type == AsicType::GL124 || + dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846 || + dev.model->asic_type == AsicType::GL843) + { + if (output_xresolution <= 1200) { + output_pixels = align_multiple_floor(output_pixels, 4); + } else if (output_xresolution < output_yresolution) { + // 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 + output_pixels = align_multiple_floor(output_pixels, 8); + } else { + output_pixels = align_multiple_floor(output_pixels, 16); + } } - 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; + // corner case for true lineart for sensor with several segments or when xres is doubled + // to match yres */ + if (output_xresolution >= 1200 && ( + dev.model->asic_type == AsicType::GL124 || + dev.model->asic_type == AsicType::GL847 || + dev.session.params.xres < dev.session.params.yres)) + { + if (output_xresolution < output_yresolution) { + // 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 + output_pixels = align_multiple_floor(output_pixels, 8); + } else { + output_pixels = align_multiple_floor(output_pixels, 16); + } } } - s.pixel_count_multiplier = sensor.pixel_count_multiplier; - - s.pixel_startx *= sensor.pixel_count_multiplier; - s.pixel_endx *= sensor.pixel_count_multiplier; + return output_pixels; } void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor) @@ -1011,68 +927,36 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se 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.full_resolution = sensor.full_resolution; + s.optical_resolution = sensor.get_optical_resolution(); s.output_resolution = s.params.xres; + s.pixel_count_ratio = sensor.pixel_count_ratio; + 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; - } + s.output_pixels = session_adjust_output_pixels(s.params.pixels, *dev, sensor, + s.params.xres, s.params.yres, false); - 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); + // Compute the number of optical pixels that will be acquired by the chip. + // The necessary alignment requirements have already been computed by + // get_session_output_pixels_multiplier + s.optical_pixels = (s.output_pixels * s.optical_resolution) / s.output_resolution; - 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); - } - } + if (static_cast<int>(s.params.startx) + sensor.output_pixel_offset < 0) + throw SaneException("Invalid sensor.output_pixel_offset"); + s.output_startx = static_cast<unsigned>( + static_cast<int>(s.params.startx) + sensor.output_pixel_offset); - // 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; + s.stagger_x = sensor.stagger_x; + s.stagger_y = sensor.stagger_y; - // 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); + if (!has_flag(s.params.flags, ScanFlag::IGNORE_STAGGER_OFFSET)) { + s.num_staggered_lines = s.stagger_y.max_shift() * s.params.yres / s.params.xres; } s.color_shift_lines_r = dev->model->ld_shift_r; @@ -1091,12 +975,14 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se 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)) { + if (s.params.channels > 1 && !has_flag(s.params.flags, ScanFlag::IGNORE_COLOR_OFFSET)) { 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.optical_line_count = dev->model->is_cis ? s.output_line_count * s.params.channels + : s.output_line_count; 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; @@ -1107,29 +993,62 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se 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) + // FIXME: Use ModelFlag::SIS_SENSOR + if ((dev->model->asic_type == AsicType::GL845 || + dev->model->asic_type == AsicType::GL846 || + dev->model->asic_type == AsicType::GL847) && + dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7400 && + dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_8200I) { 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; + // in case of multi-segments sensor, we have expand the scan area to sensor boundary + if (dev->model->model_id == ModelId::CANON_5600F) { + unsigned startx_xres = s.optical_resolution; + if (dev->model->model_id == ModelId::CANON_5600F) { + if (s.output_resolution == 1200) { + startx_xres /= 2; + } + if (s.output_resolution >= 2400) { + startx_xres /= 4; + } + } + unsigned optical_startx = s.output_startx * startx_xres / s.params.xres; + unsigned optical_endx = optical_startx + s.optical_pixels; - s.optical_pixels_raw += extra_segment_scan_area; + unsigned multi_segment_size_output = s.segment_count * s.conseq_pixel_dist; + unsigned multi_segment_size_optical = + (multi_segment_size_output * s.optical_resolution) / s.output_resolution; + + optical_endx = align_multiple_ceil(optical_endx, multi_segment_size_optical); + s.optical_pixels_raw = optical_endx - optical_startx; + s.optical_pixels_raw = align_multiple_floor(s.optical_pixels_raw, + 4 * s.optical_resolution / s.output_resolution); + } else { + // BUG: the following code will likely scan too much. Use the CANON_5600F approach + 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.pixel_count_ratio.apply_inverse(extra_segment_scan_area); + + 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->model_id == ModelId::CANON_5600F) { + auto output_pixels_raw = (s.optical_pixels_raw * s.output_resolution) / s.optical_resolution; + auto output_channel_bytes_raw = multiply_by_depth_ceil(output_pixels_raw, s.params.depth); + s.output_line_bytes_raw = output_channel_bytes_raw * s.params.channels; + } else { + s.output_line_bytes_raw = multiply_by_depth_ceil( + (s.optical_pixels_raw * s.output_resolution) / sensor.full_resolution / s.segment_count, + s.params.depth); + } } - if (dev->model->asic_type == AsicType::GL841) { + if (dev->model->asic_type == AsicType::GL841 || + dev->model->asic_type == AsicType::GL842) + { if (dev->model->is_cis) { s.output_line_bytes_raw = s.output_channel_bytes; } @@ -1139,27 +1058,43 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se 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; + s.conseq_pixel_dist = s.output_pixels / (s.full_resolution / s.optical_resolution) / s.segment_count; } - if (dev->model->asic_type == AsicType::GL843) { - s.conseq_pixel_dist = s.output_pixels / s.segment_count; + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { + if (dev->model->is_cis) { + if (s.segment_count > 1) { + s.conseq_pixel_dist = sensor.segment_size; + } + } else { + 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::GL842 || 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); + s.output_segment_pixel_group_count = s.output_pixels / + (s.full_resolution / s.optical_resolution * s.segment_count); } + + if (dev->model->model_id == ModelId::CANON_LIDE_90) { + s.output_segment_pixel_group_count = s.output_pixels / s.segment_count; + } + 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); + if (dev->model->model_id == ModelId::CANON_5600F) { + s.output_segment_pixel_group_count = s.output_pixels / s.segment_count; + } else { + s.output_segment_pixel_group_count = s.pixel_count_ratio.apply(s.optical_pixels); + } } s.output_line_bytes_requested = multiply_by_depth_ceil( @@ -1167,11 +1102,16 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se 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; + if (dev->model->model_id == ModelId::CANON_LIDE_90) { + s.output_total_bytes_raw *= s.params.channels; + s.output_total_bytes *= s.params.channels; + } - compute_session_buffer_sizes(dev->model->asic_type, s); - compute_session_pipeline(dev, s); + s.buffer_size_read = s.output_line_bytes_raw * 64; compute_session_pixel_offsets(dev, s, sensor); + s.shading_pixel_offset = sensor.shading_pixel_offset; + if (dev->model->asic_type == AsicType::GL124 || dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846) @@ -1179,7 +1119,10 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se s.enable_ledadd = (s.params.channels == 1 && dev->model->is_cis && dev->settings.true_gray); } + s.use_host_side_calib = sensor.use_host_side_calib; + if (dev->model->asic_type == AsicType::GL841 || + dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { // no 16 bit gamma for this ASIC @@ -1194,177 +1137,166 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se debug_dump(DBG_info, s); } -static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& session) +ImagePipelineStack build_image_pipeline(const Genesys_Device& dev, const ScanSession& session, + unsigned pipeline_index, bool log_image_data) { - 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); + 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) + auto read_data_from_usb = [&dev](std::size_t size, std::uint8_t* data) { - dev->interface->bulk_read_data(0x45, data, size); + DBG(DBG_info, "read_data_from_usb: reading %zu bytes\n", size); + auto begin = std::chrono::high_resolution_clock::now(); + dev.interface->bulk_read_data(0x45, data, size); + auto end = std::chrono::high_resolution_clock::now(); + float us = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count(); + float speed = size / us; // bytes/us == MB/s + DBG(DBG_info, "read_data_from_usb: reading %zu bytes finished %f MB/s\n", size, speed); return true; }; - auto lines = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1); + auto debug_prefix = "gl_pipeline_" + std::to_string(pipeline_index); - dev->pipeline.clear(); + ImagePipelineStack pipeline; - // 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. + auto lines = session.optical_line_count; + auto buffer_size = session.buffer_size_read; - 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); + // At least GL841 requires reads to be aligned to 2 bytes and will fail on some devices on + // certain circumstances. + buffer_size = align_multiple_ceil(buffer_size, 2); + + auto& src_node = pipeline.push_first_node<ImagePipelineNodeBufferedCallableSource>( + width, lines, format, buffer_size, read_data_from_usb); + src_node.set_last_read_multiple(2); + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_0_from_usb.tiff"); + } + + if (session.segment_count > 1) { auto output_width = session.output_segment_pixel_group_count * session.segment_count; - dev->pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order, + 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 (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_1_after_desegment.tiff"); + } } - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_0_before_swap.pnm"); - } + if (depth == 16) { + unsigned num_swaps = 0; + if (has_flag(dev.model->flags, ModelFlag::SWAP_16BIT_DATA)) { + num_swaps++; + } +#ifdef WORDS_BIGENDIAN + num_swaps++; +#endif + if (num_swaps % 2 != 0) { + pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); - if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && depth == 16) { - dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_2_after_swap.tiff"); + } + } } -#ifdef WORDS_BIGENDIAN - if (depth == 16) { - dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); + if (has_flag(dev.model->flags, ModelFlag::INVERT_PIXEL_DATA)) { + pipeline.push_node<ImagePipelineNodeInvert>(); + + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_3_after_invert.tiff"); + } } -#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) { + pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev.model->line_mode_color_order); + + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_4_after_merge_mono.tiff"); + } } - if (dev->model->is_cis && session.params.channels == 3) { - dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order); + if (pipeline.get_output_format() == PixelFormat::BGR888) { + pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888); } - if (dev->pipeline.get_output_format() == PixelFormat::BGR888) { - dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888); + if (pipeline.get_output_format() == PixelFormat::BGR161616) { + pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); } - if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) { - dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_5_after_format.tiff"); } if (session.max_color_shift_lines > 0 && session.params.channels == 3) { - dev->pipeline.push_node<ImagePipelineNodeComponentShiftLines>( + pipeline.push_node<ImagePipelineNodeComponentShiftLines>( session.color_shift_lines_r, session.color_shift_lines_g, session.color_shift_lines_b); + + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_6_after_color_unshift.tiff"); + } } - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_2_after_shift.pnm"); + if (!session.stagger_x.empty()) { + // FIXME: the image will be scaled to requested pixel count without regard to the reduction + // of image size in this step. + pipeline.push_node<ImagePipelineNodePixelShiftColumns>(session.stagger_x.shifts()); + + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_7_after_x_unstagger.tiff"); + } } if (session.num_staggered_lines > 0) { - std::vector<std::size_t> shifts{0, session.num_staggered_lines}; - dev->pipeline.push_node<ImagePipelineNodePixelShiftLines>(shifts); - } + pipeline.push_node<ImagePipelineNodePixelShiftLines>(session.stagger_y.shifts()); - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_3_after_stagger.pnm"); + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_8_after_y_unstagger.tiff"); + } } - if ((dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) && - !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) + if (session.use_host_side_calib && + !has_flag(dev.model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) && + !has_flag(session.params.flags, ScanFlag::DISABLE_SHADING)) { - dev->pipeline.push_node<ImagePipelineNodeCalibrate>(dev->dark_average_data, - dev->white_average_data); + unsigned offset_pixels = session.params.startx + dev.calib_session.shading_pixel_offset; + unsigned offset_bytes = offset_pixels * dev.calib_session.params.channels; + pipeline.push_node<ImagePipelineNodeCalibrate>(dev.dark_average_data, + dev.white_average_data, offset_bytes); - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_4_after_calibrate.pnm"); + if (log_image_data) { + pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_9_after_calibrate.tiff"); } } - if (session.output_pixels != session.params.get_requested_pixels()) { - dev->pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels()); + if (pipeline.get_output_width() != session.params.get_requested_pixels()) { + pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels()); } - auto read_from_pipeline = [dev](std::size_t size, std::uint8_t* out_data) + return pipeline; +} + +void setup_image_pipeline(Genesys_Device& dev, const ScanSession& session) +{ + static unsigned s_pipeline_index = 0; + + s_pipeline_index++; + + dev.pipeline = build_image_pipeline(dev, session, s_pipeline_index, dbg_log_image_data()); + + 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); + (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(), + dev.pipeline_buffer = ImageBuffer{dev.pipeline.get_output_row_bytes(), read_from_pipeline}; } @@ -1394,6 +1326,32 @@ std::uint8_t compute_frontend_gain_wolfson(float value, float target_value) return clamp(code, 0, 255); } +std::uint8_t compute_frontend_gain_lide_80(float value, float target_value) +{ + int code = static_cast<int>((target_value / value) * 12); + return clamp(code, 0, 255); +} + +std::uint8_t compute_frontend_gain_wolfson_gl841(float value, float target_value) +{ + // this code path is similar to what generic wolfson code path uses and uses similar constants, + // but is likely incorrect. + float inv_gain = target_value / value; + inv_gain *= 0.69f; + int code = static_cast<int>(283 - 208 / inv_gain); + return clamp(code, 0, 255); +} + +std::uint8_t compute_frontend_gain_wolfson_gl846_gl847_gl124(float value, float target_value) +{ + // this code path is similar to what generic wolfson code path uses and uses similar constants, + // but is likely incorrect. + float inv_gain = target_value / value; + int code = static_cast<int>(283 - 208 / inv_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) @@ -1418,13 +1376,22 @@ std::uint8_t compute_frontend_gain_analog_devices(float value, float target_valu 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); + switch (frontend_type) { + case FrontendType::WOLFSON: + return compute_frontend_gain_wolfson(value, target_value); + case FrontendType::ANALOG_DEVICES: + return compute_frontend_gain_analog_devices(value, target_value); + case FrontendType::CANON_LIDE_80: + return compute_frontend_gain_lide_80(value, target_value); + case FrontendType::WOLFSON_GL841: + return compute_frontend_gain_wolfson_gl841(value, target_value); + case FrontendType::WOLFSON_GL846: + case FrontendType::ANALOG_DEVICES_GL847: + case FrontendType::WOLFSON_GL124: + return compute_frontend_gain_wolfson_gl846_gl847_gl124(value, target_value); + default: + throw SaneException("Unknown frontend to compute gain for"); } - throw SaneException("Unknown frontend to compute gain for"); } /** @brief initialize device @@ -1436,7 +1403,7 @@ std::uint8_t compute_frontend_gain(float value, float target_value, * @param dev device to initialize * @param max_regs umber of maximum used registers */ -void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/) +void sanei_genesys_asic_init(Genesys_Device* dev) { DBG_HELPER(dbg); @@ -1486,8 +1453,7 @@ void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/) dev->settings.color_filter = ColorFilter::RED; - /* duplicate initial values into calibration registers */ - dev->calib_reg = dev->reg; + dev->initial_regs = dev->reg; const auto& sensor = sanei_genesys_find_sensor_any(dev); @@ -1497,8 +1463,15 @@ void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/) dev->already_initialized = true; // Move to home if needed + if (dev->model->model_id == ModelId::CANON_8600F) { + if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::SECONDARY)) { + dev->set_head_pos_unknown(ScanHeadId::SECONDARY); + } + if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::PRIMARY)) { + dev->set_head_pos_unknown(ScanHeadId::SECONDARY); + } + } 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); @@ -1510,6 +1483,7 @@ void scanner_start_action(Genesys_Device& dev, bool start_motor) switch (dev.model->asic_type) { case AsicType::GL646: case AsicType::GL841: + case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: @@ -1527,8 +1501,7 @@ void scanner_start_action(Genesys_Device& dev, bool start_motor) } } -void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& sensor, - unsigned dpihw) +void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, unsigned dpihw) { // same across GL646, GL841, GL843, GL846, GL847, GL124 const uint8_t REG_0x05_DPIHW_MASK = 0xc0; @@ -1537,10 +1510,6 @@ void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& s 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: @@ -1583,6 +1552,12 @@ void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs, regs.set16(gl841::REG_EXPB, exposure.blue); break; } + case AsicType::GL842: { + regs.set16(gl842::REG_EXPR, exposure.red); + regs.set16(gl842::REG_EXPG, exposure.green); + regs.set16(gl842::REG_EXPB, exposure.blue); + break; + } case AsicType::GL843: { regs.set16(gl843::REG_EXPR, exposure.red); regs.set16(gl843::REG_EXPG, exposure.green); @@ -1619,6 +1594,10 @@ void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs) regs.find_reg(gl841::REG_0x01).value &= ~gl841::REG_0x01_SCAN; break; } + case AsicType::GL842: { + regs.find_reg(gl842::REG_0x01).value &= ~gl842::REG_0x01_SCAN; + break; + } case AsicType::GL843: { regs.find_reg(gl843::REG_0x01).value &= ~gl843::REG_0x01_SCAN; break; @@ -1648,6 +1627,8 @@ bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& reg 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::GL842: + return static_cast<bool>(regs.get8(gl842::REG_0x06) & gl842::REG_0x06_GAIN4); case AsicType::GL843: return static_cast<bool>(regs.get8(gl843::REG_0x06) & gl843::REG_0x06_GAIN4); case AsicType::GL845: @@ -1706,79 +1687,78 @@ void sanei_genesys_wait_for_home(Genesys_Device* dev) } } -/** @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) +const MotorProfile* get_motor_profile_ptr(const std::vector<MotorProfile>& profiles, + unsigned exposure, + const ScanSession& session) { - int idx; + int best_i = -1; + + for (unsigned i = 0; i < profiles.size(); ++i) { + const auto& profile = profiles[i]; - 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]; + if (!profile.resolutions.matches(session.params.yres)) { + continue; + } + if (!profile.scan_methods.matches(session.params.scan_method)) { + continue; } - // 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; - } + if (profile.max_exposure == exposure) { + return &profile; + } + + if (profile.max_exposure == 0 || profile.max_exposure >= exposure) { + if (best_i < 0) { + // no match found yet + best_i = i; + } else { + // test for better match + if (profiles[i].max_exposure < profiles[best_i].max_exposure) { + best_i = i; } } } } - /* default fallback */ - if(idx<0) - { - DBG (DBG_warn,"%s: using default motor profile\n",__func__); - idx=0; + if (best_i < 0) { + return nullptr; + } + + return &profiles[best_i]; +} + +const MotorProfile& get_motor_profile(const std::vector<MotorProfile>& profiles, + unsigned exposure, + const ScanSession& session) +{ + const auto* profile = get_motor_profile_ptr(profiles, exposure, session); + if (profile == nullptr) { + throw SaneException("Motor slope is not configured"); } - return motors[idx]; + return *profile; } -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(AsicType asic_type, const Genesys_Motor& motor, unsigned ydpi, + unsigned exposure, unsigned step_multiplier, + const MotorProfile& motor_profile) { - unsigned target_speed_w = ((exposure * dpi) / base_dpi); + unsigned target_speed_w = ((exposure * ydpi) / motor.base_ydpi); - 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)); + auto table = create_slope_table_for_speed(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) + const MotorProfile& 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)); + return create_slope_table_for_speed(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 diff --git a/backend/genesys/low.h b/backend/genesys/low.h index d7f5dd2..d67b427 100644 --- a/backend/genesys/low.h +++ b/backend/genesys/low.h @@ -108,39 +108,6 @@ #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 */ @@ -186,66 +153,60 @@ #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 { +class UsbDeviceEntry { +public: + static constexpr std::uint16_t BCD_DEVICE_NOT_SET = 0xffff; + + UsbDeviceEntry(std::uint16_t vendor_id, std::uint16_t product_id, + const Genesys_Model& model) : + vendor_{vendor_id}, product_{product_id}, + bcd_device_{BCD_DEVICE_NOT_SET}, model_{model} + {} - Genesys_USB_Device_Entry(unsigned v, unsigned p, const Genesys_Model& m) : - vendor(v), product(p), model(m) + UsbDeviceEntry(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device, + const Genesys_Model& model) : + vendor_{vendor_id}, product_{product_id}, + bcd_device_{bcd_device}, model_{model} {} + std::uint16_t vendor_id() const { return vendor_; } + std::uint16_t product_id() const { return product_; } + std::uint16_t bcd_device() const { return bcd_device_; } + + const Genesys_Model& model() const { return model_; } + + bool matches(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device) + { + if (vendor_ != vendor_id) + return false; + if (product_ != product_id) + return false; + if (bcd_device_ != BCD_DEVICE_NOT_SET && bcd_device != BCD_DEVICE_NOT_SET && + bcd_device_ != bcd_device) + { + return false; + } + return true; + } + +private: // USB vendor identifier - std::uint16_t vendor; + std::uint16_t vendor_; // USB product identifier - std::uint16_t product; + std::uint16_t product_; + // USB bcdProduct identifier + std::uint16_t bcd_device_; // Scanner model information - Genesys_Model model; + 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); +std::unique_ptr<CommandSet> create_cmd_set(AsicType asic_type); // reads the status of the scanner Status scanner_read_status(Genesys_Device& dev); @@ -259,21 +220,26 @@ void scanner_read_print_status(Genesys_Device& dev); void debug_print_status(DebugMessageHelper& dbg, Status status); +void scanner_register_rw_clear_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask); +void scanner_register_rw_set_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask); +void scanner_register_rw_bits(Genesys_Device& dev, std::uint16_t address, + std::uint8_t value, std::uint8_t mask); + 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, +const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev); +const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method); -bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels, +bool sanei_genesys_has_sensor(const 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); + sanei_genesys_find_sensors_all(const 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); @@ -318,13 +284,9 @@ 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, +SANE_Int sanei_genesys_exposure_time2(Genesys_Device* dev, const MotorProfile& profile, float ydpi, 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); @@ -335,28 +297,42 @@ void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& s 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); +/** 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. -extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, - std::size_t length); + @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 scanner_search_strip(Genesys_Device& dev, bool forward, bool black); + +bool should_calibrate_only_active_area(const Genesys_Device& dev, + const Genesys_Settings& settings); + +void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs); -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 scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs, unsigned dpi); + +SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs); + +void scanner_clear_scan_and_feed_counts(Genesys_Device& dev); -void sanei_genesys_write_pnm_file(const char* filename, const Image& image); +void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr, + const std::vector<uint16_t>& slope_table); -extern void sanei_genesys_write_pnm_file16(const char* filename, const uint16_t *data, unsigned channels, - unsigned pixels_per_line, unsigned lines); +extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, + std::size_t length); void wait_until_buffer_non_empty(Genesys_Device* dev, bool check_status_twice = false); @@ -370,25 +346,13 @@ void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs, 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; -} +void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, unsigned dpihw); 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); + exposure.red = std::max<std::uint16_t>(1, exposure.red); + exposure.green = std::max<std::uint16_t>(1, exposure.green); + exposure.blue = std::max<std::uint16_t>(1, exposure.blue); return exposure; } @@ -396,7 +360,7 @@ bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& reg extern void sanei_genesys_wait_for_home(Genesys_Device* dev); -extern void sanei_genesys_asic_init(Genesys_Device* dev, bool cold); +extern void sanei_genesys_asic_init(Genesys_Device* dev); void scanner_start_action(Genesys_Device& dev, bool start_motor); void scanner_stop_action(Genesys_Device& dev); @@ -404,15 +368,23 @@ 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); +void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs); + +const MotorProfile* get_motor_profile_ptr(const std::vector<MotorProfile>& profiles, + unsigned exposure, + const ScanSession& session); -MotorSlopeTable sanei_genesys_slope_table(AsicType asic_type, int dpi, int exposure, int base_dpi, - unsigned step_multiplier, - const Motor_Profile& motor_profile); +const MotorProfile& get_motor_profile(const std::vector<MotorProfile>& profiles, + unsigned exposure, + const ScanSession& session); + +MotorSlopeTable create_slope_table(AsicType asic_type, const Genesys_Motor& motor, unsigned ydpi, + unsigned exposure, unsigned step_multiplier, + const MotorProfile& motor_profile); MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier, - const Motor_Profile& motor_profile); + const MotorProfile& motor_profile); /** @brief find lowest motor resolution for the device. * Parses the resolution list for motor and @@ -449,52 +421,22 @@ extern void sanei_genesys_generate_gamma_buffer(Genesys_Device* dev, int size, uint8_t* gamma); +unsigned session_adjust_output_pixels(unsigned output_pixels, + const Genesys_Device& dev, const Genesys_Sensor& sensor, + unsigned output_xresolution, unsigned output_yresolution, + bool adjust_output_pixels); + void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor); -void build_image_pipeline(Genesys_Device* dev, const ScanSession& session); +ImagePipelineStack build_image_pipeline(const Genesys_Device& dev, const ScanSession& session, + unsigned pipeline_index, bool log_image_data); + +// sets up a image pipeline for device `dev` +void setup_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 */ /*---------------------------------------------------------------------------*/ @@ -502,15 +444,18 @@ inline T clamp(const T& value, const T& lo, const T& hi) 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<MemoryLayout>> s_memory_layout; extern StaticInit<std::vector<Genesys_Motor>> s_motors; -extern StaticInit<std::vector<Genesys_USB_Device_Entry>> s_usb_devices; +extern StaticInit<std::vector<UsbDeviceEntry>> s_usb_devices; void genesys_init_sensor_tables(); void genesys_init_frontend_tables(); void genesys_init_gpo_tables(); +void genesys_init_memory_layout_tables(); void genesys_init_motor_tables(); -void genesys_init_motor_profile_tables(); void genesys_init_usb_device_tables(); +void verify_sensor_tables(); +void verify_usb_device_tables(); template<class T> void debug_dump(unsigned level, const T& value) diff --git a/backend/genesys/motor.cpp b/backend/genesys/motor.cpp index 910266a..a18d6e1 100644 --- a/backend/genesys/motor.cpp +++ b/backend/genesys/motor.cpp @@ -43,9 +43,11 @@ #define DEBUG_DECLARE_ONLY +#include "low.h" #include "motor.h" #include "utilities.h" #include <cmath> +#include <numeric> namespace genesys { @@ -80,19 +82,38 @@ MotorSlope MotorSlope::create_from_steps(unsigned initial_w, unsigned max_w, return slope; } -void MotorSlopeTable::slice_steps(unsigned count) +void MotorSlopeTable::slice_steps(unsigned count, unsigned step_multiplier) { - if (count >= table.size() || count > steps_count) { - throw SaneException("Excepssive steps count"); + if (count > table.size() || count < step_multiplier) { + throw SaneException("Invalid steps count"); } - steps_count = count; + count = align_multiple_floor(count, step_multiplier); + table.resize(count); + generate_pixeltime_sum(); +} + +void MotorSlopeTable::expand_table(unsigned count, unsigned step_multiplier) +{ + if (table.empty()) { + throw SaneException("Can't expand empty table"); + } + count = align_multiple_ceil(count, step_multiplier); + table.resize(table.size() + count, table.back()); + generate_pixeltime_sum(); +} + +void MotorSlopeTable::generate_pixeltime_sum() +{ + pixeltime_sum_ = std::accumulate(table.begin(), table.end(), + std::size_t{0}, std::plus<std::size_t>()); } unsigned get_slope_table_max_size(AsicType asic_type) { switch (asic_type) { case AsicType::GL646: - case AsicType::GL841: return 255; + case AsicType::GL841: + case AsicType::GL842: return 255; case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: @@ -103,9 +124,9 @@ 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) +MotorSlopeTable create_slope_table_for_speed(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); @@ -120,6 +141,10 @@ MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_spee dbg.log(DBG_warn, "failed to reach target speed"); } + if (target_speed_shifted_w >= std::numeric_limits<std::uint16_t>::max()) { + throw SaneException("Target motor speed is too low"); + } + unsigned final_speed = std::max(target_speed_shifted_w, max_speed_shifted_w); table.table.reserve(max_size); @@ -130,26 +155,20 @@ MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_spee 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); + table.generate_pixeltime_sum(); return table; } @@ -164,15 +183,30 @@ std::ostream& operator<<(std::ostream& out, const MotorSlope& slope) return out; } +std::ostream& operator<<(std::ostream& out, const MotorProfile& profile) +{ + out << "MotorProfile{\n" + << " max_exposure: " << profile.max_exposure << '\n' + << " step_type: " << profile.step_type << '\n' + << " motor_vref: " << profile.motor_vref << '\n' + << " resolutions: " << format_indent_braced_list(4, profile.resolutions) << '\n' + << " scan_methods: " << format_indent_braced_list(4, profile.scan_methods) << '\n' + << " slope: " << format_indent_braced_list(4, profile.slope) << '\n' + << '}'; + return out; +} + std::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor) { out << "Genesys_Motor{\n" - << " id: " << static_cast<unsigned>(motor.id) << '\n' + << " id: " << 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)) + << " profiles: " + << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorProfile", + motor.profiles)) << '\n' + << " fast_profiles: " + << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorProfile", + motor.fast_profiles)) << '\n' << '}'; return out; } diff --git a/backend/genesys/motor.h b/backend/genesys/motor.h index d80da6d..c433c0e 100644 --- a/backend/genesys/motor.h +++ b/backend/genesys/motor.h @@ -44,9 +44,12 @@ #ifndef BACKEND_GENESYS_MOTOR_H #define BACKEND_GENESYS_MOTOR_H +#include <algorithm> #include <cstdint> #include <vector> #include "enums.h" +#include "sensor.h" +#include "value_filter.h" namespace genesys { @@ -123,20 +126,47 @@ struct MotorSlope struct MotorSlopeTable { std::vector<std::uint16_t> table; - unsigned steps_count = 0; - unsigned pixeltime_sum = 0; - void slice_steps(unsigned count); + void slice_steps(unsigned count, unsigned step_multiplier); + + // expands the table by the given number of steps + void expand_table(unsigned count, unsigned step_multiplier); + + std::uint64_t pixeltime_sum() const { return pixeltime_sum_; } + + void generate_pixeltime_sum(); +private: + std::uint64_t pixeltime_sum_ = 0; }; 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); +MotorSlopeTable create_slope_table_for_speed(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 MotorProfile +{ + MotorProfile() = default; + MotorProfile(const MotorSlope& a_slope, StepType a_step_type, unsigned a_max_exposure) : + slope{a_slope}, step_type{a_step_type}, max_exposure{a_max_exposure} + {} + + MotorSlope slope; + StepType step_type = StepType::FULL; + int motor_vref = -1; + + // the resolutions this profile is good for + ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY; + // the scan method this profile is good for. If the list is empty, good for any method. + ValueFilterAny<ScanMethod> scan_methods = VALUE_FILTER_ANY; + + unsigned max_exposure = 0; // 0 - any exposure +}; + +std::ostream& operator<<(std::ostream& out, const MotorProfile& profile); struct Genesys_Motor { @@ -146,27 +176,41 @@ struct Genesys_Motor 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; + std::vector<MotorProfile> profiles; + // slopes to derive individual slopes from for fast moving + std::vector<MotorProfile> fast_profiles; - MotorSlope& get_slope(StepType step_type) + MotorSlope& get_slope_with_step_type(StepType step_type) { - return slopes[static_cast<unsigned>(step_type)]; + for (auto& p : profiles) { + if (p.step_type == step_type) + return p.slope; + } + throw SaneException("No motor profile with step type"); } - const MotorSlope& get_slope(StepType step_type) const + const MotorSlope& get_slope_with_step_type(StepType step_type) const { - return slopes[static_cast<unsigned>(step_type)]; + for (const auto& p : profiles) { + if (p.step_type == step_type) + return p.slope; + } + throw SaneException("No motor profile with step type"); } StepType max_step_type() const { - if (slopes.empty()) { - throw std::runtime_error("Slopes table is empty"); + if (profiles.empty()) { + throw std::runtime_error("Profiles table is empty"); + } + StepType step_type = StepType::FULL; + for (const auto& p : profiles) { + step_type = static_cast<StepType>( + std::max(static_cast<unsigned>(step_type), + static_cast<unsigned>(p.step_type))); } - return static_cast<StepType>(slopes.size() - 1); + return step_type; } }; diff --git a/backend/genesys/register.h b/backend/genesys/register.h index bbc7ec8..51aab90 100644 --- a/backend/genesys/register.h +++ b/backend/genesys/register.h @@ -44,6 +44,7 @@ #ifndef BACKEND_GENESYS_REGISTER_H #define BACKEND_GENESYS_REGISTER_H +#include "enums.h" #include "utilities.h" #include <algorithm> @@ -76,7 +77,7 @@ struct GenesysRegisterSetState bool is_lamp_on = false; bool is_xpa_on = false; bool is_motor_on = false; - bool is_xpa_motor_on = false; + MotorMode motor_mode = MotorMode::PRIMARY; }; template<class Value> @@ -414,6 +415,11 @@ public: } } + bool has_reg(AddressType address) const + { + return find_reg_index(address) != -1; + } + SettingType& find_reg(AddressType address) { int i = find_reg_index(address); diff --git a/backend/genesys/scanner_interface.h b/backend/genesys/scanner_interface.h index 03c7132..70413d1 100644 --- a/backend/genesys/scanner_interface.h +++ b/backend/genesys/scanner_interface.h @@ -56,11 +56,6 @@ namespace genesys { class ScannerInterface { public: - enum Flags { - FLAG_NONE = 0, - FLAG_SWAP_REGISTERS = 1 << 0, - FLAG_SMALL_ADDRESS = 1 << 1 - }; virtual ~ScannerInterface(); @@ -75,12 +70,11 @@ public: 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; + std::size_t size) = 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; + std::size_t size) = 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; diff --git a/backend/genesys/scanner_interface_usb.cpp b/backend/genesys/scanner_interface_usb.cpp index d4d83dd..d405ede 100644 --- a/backend/genesys/scanner_interface_usb.cpp +++ b/backend/genesys/scanner_interface_usb.cpp @@ -101,8 +101,6 @@ std::uint8_t ScannerInterfaceUsb::read_register(std::uint16_t address) 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; } @@ -213,6 +211,7 @@ static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, s uint8_t outdata[8]; if (asic_type == AsicType::GL124 || + asic_type == AsicType::GL845 || asic_type == AsicType::GL846 || asic_type == AsicType::GL847) { @@ -222,7 +221,9 @@ static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, s outdata[2] = 0; outdata[3] = 0x10; } else if (asic_type == AsicType::GL841 || - asic_type == AsicType::GL843) { + asic_type == AsicType::GL842 || + asic_type == AsicType::GL843) + { outdata[0] = BULK_IN; outdata[1] = BULK_RAM; outdata[2] = 0x82; // @@ -246,12 +247,13 @@ static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, s 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 + // currently supported: GL646, GL841, GL843, GL845, 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::GL845 || dev_->model->asic_type == AsicType::GL846 || dev_->model->asic_type == AsicType::GL847) { @@ -351,30 +353,21 @@ void ScannerInterfaceUsb::bulk_write_data(std::uint8_t addr, std::uint8_t* data, } void ScannerInterfaceUsb::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, - std::size_t size, Flags flags) + std::size_t size) { 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::GL842 && 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)); - } - } + write_register(0x2b, ((addr >> 4) & 0xff)); + write_register(0x2a, ((addr >> 12) & 0xff)); + write_register(0x29, ((addr >> 20) & 0xff)); } else { write_register(0x2b, ((addr >> 4) & 0xff)); write_register(0x2a, ((addr >> 12) & 0xff)); @@ -383,24 +376,28 @@ void ScannerInterfaceUsb::write_buffer(std::uint8_t type, std::uint32_t addr, st } void ScannerInterfaceUsb::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, - std::size_t size, Flags flags) + std::size_t size) { 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 && + if (dev_->model->asic_type != AsicType::GL841 && + dev_->model->asic_type != AsicType::GL842 && 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)); - } + write_register(0x5b, ((addr >> 12) & 0xff)); + write_register(0x5c, ((addr >> 4) & 0xff)); bulk_write_data(type, data, size); + + if (dev_->model->asic_type == AsicType::GL842 || + dev_->model->asic_type == AsicType::GL843) + { + // it looks like we need to reset the address so that subsequent buffer operations work. + // Most likely the MTRTBL register is to blame. + write_register(0x5b, 0); + write_register(0x5c, 0); + } } void ScannerInterfaceUsb::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) diff --git a/backend/genesys/scanner_interface_usb.h b/backend/genesys/scanner_interface_usb.h index 06b51ff..33fb8fe 100644 --- a/backend/genesys/scanner_interface_usb.h +++ b/backend/genesys/scanner_interface_usb.h @@ -67,9 +67,9 @@ public: 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; + std::size_t size) override; void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, - std::size_t size, Flags flags) override; + std::size_t size) override; void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) override; diff --git a/backend/genesys/sensor.cpp b/backend/genesys/sensor.cpp index e54af65..ce51403 100644 --- a/backend/genesys/sensor.cpp +++ b/backend/genesys/sensor.cpp @@ -51,10 +51,16 @@ 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' - << "}"; + if (config.shifts().empty()) { + out << "StaggerConfig{}"; + return out; + } + + out << "StaggerConfig{ " << config.shifts().front(); + for (auto it = std::next(config.shifts().begin()); it != config.shifts().end(); ++it) { + out << ", " << *it; + } + out << " }"; return out; } @@ -64,6 +70,11 @@ std::ostream& operator<<(std::ostream& out, const FrontendType& type) case FrontendType::UNKNOWN: out << "UNKNOWN"; break; case FrontendType::WOLFSON: out << "WOLFSON"; break; case FrontendType::ANALOG_DEVICES: out << "ANALOG_DEVICES"; break; + case FrontendType::CANON_LIDE_80: out << "CANON_LIDE_80"; break; + case FrontendType::WOLFSON_GL841: out << "WOLFSON_GL841"; break; + case FrontendType::WOLFSON_GL846: out << "WOLFSON_GL846"; break; + case FrontendType::ANALOG_DEVICES_GL847: out << "ANALOG_DEVICES_GL847"; break; + case FrontendType::WOLFSON_GL124: out << "WOLFSON_GL124"; break; default: out << "(unknown value)"; } return out; @@ -91,7 +102,7 @@ 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' + << " id: " << frontend.id << '\n' << " regs: " << format_indent_braced_list(4, frontend.regs) << '\n' << std::hex << " reg2[0]: " << frontend.reg2[0] << '\n' @@ -112,33 +123,23 @@ std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure) 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' + << " full_resolution: " << sensor.full_resolution << '\n' + << " optical_resolution: " << sensor.get_optical_resolution() << '\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' + << " register_dpihw: " << sensor.register_dpihw << '\n' + << " register_dpiset: " << sensor.register_dpiset << '\n' + << " shading_factor: " << sensor.shading_factor << '\n' + << " shading_pixel_offset: " << sensor.shading_pixel_offset << '\n' + << " pixel_count_ratio: " << sensor.pixel_count_ratio << '\n' + << " output_pixel_offset: " << sensor.output_pixel_offset << '\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' @@ -146,8 +147,9 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor) << " 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' + << " stagger_x: " << sensor.stagger_x << '\n' + << " stagger_y: " << sensor.stagger_y << '\n' + << " use_host_side_calib: " << sensor.use_host_side_calib << '\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' diff --git a/backend/genesys/sensor.h b/backend/genesys/sensor.h index e70728e..ca6fef7 100644 --- a/backend/genesys/sensor.h +++ b/backend/genesys/sensor.h @@ -47,6 +47,7 @@ #include "enums.h" #include "register.h" #include "serialize.h" +#include "value_filter.h" #include <array> #include <functional> @@ -72,31 +73,30 @@ class StaggerConfig { public: StaggerConfig() = default; - StaggerConfig(unsigned min_resolution, unsigned lines_at_min) : - min_resolution_{min_resolution}, - lines_at_min_{lines_at_min} + explicit StaggerConfig(std::initializer_list<std::size_t> shifts) : + shifts_{shifts} { } - unsigned stagger_at_resolution(unsigned xresolution, unsigned yresolution) const + std::size_t max_shift() const { - if (min_resolution_ == 0 || xresolution < min_resolution_) + if (shifts_.empty()) { return 0; - return yresolution / min_resolution_ * lines_at_min_; + } + return *std::max_element(shifts_.begin(), shifts_.end()); } - unsigned min_resolution() const { return min_resolution_; } - unsigned lines_at_min() const { return lines_at_min_; } + bool empty() const { return shifts_.empty(); } + std::size_t size() const { return shifts_.size(); } + const std::vector<std::size_t>& shifts() const { return shifts_; } bool operator==(const StaggerConfig& other) const { - return min_resolution_ == other.min_resolution_ && - lines_at_min_ == other.lines_at_min_; + return shifts_ == other.shifts_; } private: - unsigned min_resolution_ = 0; - unsigned lines_at_min_ = 0; + std::vector<std::size_t> shifts_; template<class Stream> friend void serialize(Stream& str, StaggerConfig& x); @@ -105,8 +105,7 @@ private: template<class Stream> void serialize(Stream& str, StaggerConfig& x) { - serialize(str, x.min_resolution_); - serialize(str, x.lines_at_min_); + serialize(str, x.shifts_); } std::ostream& operator<<(std::ostream& out, const StaggerConfig& config); @@ -114,9 +113,14 @@ std::ostream& operator<<(std::ostream& out, const StaggerConfig& config); enum class FrontendType : unsigned { - UNKNOWN, + UNKNOWN = 0, WOLFSON, - ANALOG_DEVICES + ANALOG_DEVICES, + CANON_LIDE_80, + WOLFSON_GL841, // old code path, likely wrong calculation + WOLFSON_GL846, // old code path, likely wrong calculation + ANALOG_DEVICES_GL847, // old code path, likely wrong calculation + WOLFSON_GL124, // old code path, likely wrong calculation }; inline void serialize(std::istream& str, FrontendType& x) @@ -242,54 +246,6 @@ struct SensorExposure { 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; @@ -300,10 +256,15 @@ struct Genesys_Sensor { // 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; + unsigned full_resolution = 0; + + // sensor resolution in pixel values that are read by the chip. Many scanners make low + // resolutions faster by configuring the timings in such a way that 1/2 or 1/4 of pixel values + // that are read. If zero, then it is equal to `full_resolution`. + unsigned optical_resolution = 0; // the resolution list that the sensor is usable at. - ResolutionFilter resolutions = ResolutionFilter::ANY; + ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY; // the channel list that the sensor is usable at std::vector<unsigned> channels = { 1, 3 }; @@ -313,29 +274,32 @@ struct Genesys_Sensor { // 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; + unsigned register_dpihw = 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; + unsigned register_dpiset = 0; + + // The resolution to use for shading calibration + unsigned shading_resolution = 0; - // CCD may present itself as half or quarter-size CCD on certain resolutions - int ccd_size_divisor = 1; + // How many real pixels correspond to one shading pixel that is sent to the scanner + unsigned shading_factor = 1; - // Some scanners need an additional multiplier over the scan coordinates - int pixel_count_multiplier = 1; + // How many pixels the shading data is offset to the right from the acquired data. Calculated + // in shading resolution. + int shading_pixel_offset = 0; + + // This defines the ratio between logical pixel coordinates and the pixel coordinates sent to + // the scanner. + Ratio pixel_count_ratio = Ratio{1, 1}; + + // The offset in pixels in terms of scan resolution that needs to be applied to scan position. + int output_pixel_offset = 0; 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) @@ -346,8 +310,7 @@ struct Genesys_Sensor { int exposure_lperiod = -1; - // the number of pixels in a single segment. - // only on gl843 + // the number of pixels in a single segment. This is counted in output resolution. unsigned segment_size = 0; // the order of the segments, if any, for the sensor. If the sensor is not segmented or uses @@ -355,31 +318,28 @@ struct Genesys_Sensor { // 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; + // some CCDs use multiple arrays of pixels for double or quadruple resolution. This can result + // in the following effects on the output: + // - every n-th column may be shifted in a vertical direction. + // - the columns themselves may be reordered in arbitrary order and may require shifting + // in X direction. + StaggerConfig stagger_x; + StaggerConfig stagger_y; + + // True if calibration should be performed on host-side + bool use_host_side_calib = false; - 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 + unsigned get_optical_resolution() const { - return get_hwdpi_divisor_fun(*this, xres); + if (optical_resolution != 0) + return optical_resolution; + return full_resolution; } // how many CCD pixels are processed per system pixel time. This corresponds to CKSEL + 1 @@ -405,22 +365,26 @@ struct Genesys_Sensor { bool operator==(const Genesys_Sensor& other) const { return sensor_id == other.sensor_id && - optical_res == other.optical_res && + full_resolution == other.full_resolution && + optical_resolution == other.optical_resolution && resolutions == other.resolutions && method == other.method && - ccd_size_divisor == other.ccd_size_divisor && + shading_resolution == other.shading_resolution && + shading_factor == other.shading_factor && + shading_pixel_offset == other.shading_pixel_offset && + pixel_count_ratio == other.pixel_count_ratio && + output_pixel_offset == other.output_pixel_offset && 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 && + stagger_x == other.stagger_x && + stagger_y == other.stagger_y && + use_host_side_calib == other.use_host_side_calib && custom_regs == other.custom_regs && custom_fe_regs == other.custom_fe_regs && gamma == other.gamma; @@ -431,14 +395,16 @@ template<class Stream> void serialize(Stream& str, Genesys_Sensor& x) { serialize(str, x.sensor_id); - serialize(str, x.optical_res); + serialize(str, x.full_resolution); serialize(str, x.resolutions); serialize(str, x.method); - serialize(str, x.ccd_size_divisor); + serialize(str, x.shading_resolution); + serialize(str, x.shading_factor); + serialize(str, x.shading_pixel_offset); + serialize(str, x.output_pixel_offset); + serialize(str, x.pixel_count_ratio); 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); @@ -451,9 +417,11 @@ void serialize(Stream& str, Genesys_Sensor& x) serialize_newline(str); serialize(str, x.segment_order); serialize_newline(str); - serialize(str, x.stagger_config); + serialize(str, x.stagger_x); + serialize_newline(str); + serialize(str, x.stagger_y); serialize_newline(str); - serialize(str, x.custom_base_regs); + serialize(str, x.use_host_side_calib); serialize_newline(str); serialize(str, x.custom_regs); serialize_newline(str); diff --git a/backend/genesys/settings.cpp b/backend/genesys/settings.cpp index 41c66de..c2b54dc 100644 --- a/backend/genesys/settings.cpp +++ b/backend/genesys/settings.cpp @@ -72,14 +72,20 @@ std::ostream& operator<<(std::ostream& out, const SetupParams& params) { StreamStateSaver state_saver{out}; + bool reverse = has_flag(params.flags, ScanFlag::REVERSE); + 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' + << " xres: " << params.xres + << " startx: " << params.startx + << " pixels per line (actual): " << params.pixels + << " pixels per line (requested): " << params.requested_pixels << '\n' + + << " yres: " << params.yres + << " lines: " << params.lines + << " starty: " << params.starty << (reverse ? " (reverse)" : "") << '\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' @@ -87,16 +93,56 @@ std::ostream& operator<<(std::ostream& out, const SetupParams& params) return out; } +bool ScanSession::operator==(const ScanSession& other) const +{ + return params == other.params && + computed == other.computed && + full_resolution == other.full_resolution && + optical_resolution == other.optical_resolution && + optical_pixels == other.optical_pixels && + optical_pixels_raw == other.optical_pixels_raw && + optical_line_count == other.optical_line_count && + output_resolution == other.output_resolution && + output_startx == other.output_startx && + output_pixels == other.output_pixels && + output_channel_bytes == other.output_channel_bytes && + output_line_bytes == other.output_line_bytes && + output_line_bytes_raw == other.output_line_bytes_raw && + output_line_bytes_requested == other.output_line_bytes_requested && + output_line_count == other.output_line_count && + output_total_bytes_raw == other.output_total_bytes_raw && + output_total_bytes == other.output_total_bytes && + num_staggered_lines == other.num_staggered_lines && + max_color_shift_lines == other.max_color_shift_lines && + color_shift_lines_r == other.color_shift_lines_r && + color_shift_lines_g == other.color_shift_lines_g && + color_shift_lines_b == other.color_shift_lines_b && + stagger_x == other.stagger_x && + stagger_y == other.stagger_y && + segment_count == other.segment_count && + pixel_startx == other.pixel_startx && + pixel_endx == other.pixel_endx && + pixel_count_ratio == other.pixel_count_ratio && + conseq_pixel_dist == other.conseq_pixel_dist && + output_segment_pixel_group_count == other.output_segment_pixel_group_count && + output_segment_start_offset == other.output_segment_start_offset && + shading_pixel_offset == other.shading_pixel_offset && + buffer_size_read == other.buffer_size_read && + enable_ledadd == other.enable_ledadd && + use_host_side_calib == other.use_host_side_calib; +} + 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' + << " full_resolution: " << session.full_resolution << '\n' << " optical_resolution: " << session.optical_resolution << '\n' << " optical_pixels: " << session.optical_pixels << '\n' << " optical_pixels_raw: " << session.optical_pixels_raw << '\n' + << " optical_line_count: " << session.optical_line_count << '\n' << " output_resolution: " << session.output_resolution << '\n' + << " output_startx: " << session.output_startx << '\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' @@ -107,20 +153,19 @@ std::ostream& operator<<(std::ostream& out, const ScanSession& session) << " 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' + << " stagger_x: " << session.stagger_x << '\n' + << " stagger_y: " << session.stagger_y << '\n' << " segment_count: " << session.segment_count << '\n' << " pixel_startx: " << session.pixel_startx << '\n' << " pixel_endx: " << session.pixel_endx << '\n' + << " pixel_count_ratio: " << session.pixel_count_ratio << '\n' << " conseq_pixel_dist: " << session.conseq_pixel_dist << '\n' << " output_segment_pixel_group_count: " << session.output_segment_pixel_group_count << '\n' + << " shading_pixel_offset: " << session.shading_pixel_offset << '\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' + << " enable_ledadd: " << session.enable_ledadd << '\n' + << " use_host_side_calib: " << session.use_host_side_calib << '\n' << " params: " << format_indent_braced_list(4, session.params) << '\n' << "}"; return out; diff --git a/backend/genesys/settings.h b/backend/genesys/settings.h index a697e60..f78845b 100644 --- a/backend/genesys/settings.h +++ b/backend/genesys/settings.h @@ -46,6 +46,8 @@ #include "enums.h" #include "serialize.h" +#include "utilities.h" +#include "sensor.h" namespace genesys { @@ -60,9 +62,9 @@ struct Genesys_Settings unsigned yres = 0; //x start on scan table in mm - double tl_x = 0; + float tl_x = 0; // y start on scan table in mm - double tl_y = 0; + float tl_y = 0; // number of lines at scan resolution unsigned int lines = 0; @@ -79,15 +81,6 @@ struct Genesys_Settings // 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; @@ -116,12 +109,13 @@ struct SetupParams { unsigned xres = NOT_SET; // resolution in y direction unsigned yres = NOT_SET; - // start pixel in X direction, from dummy_pixel + 1 + // start pixel in X direction, from dummy_pixel + 1. Counted in terms of xres. 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() + // the number of pixels in X direction. Counted in terms of xres. + // 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 @@ -144,7 +138,7 @@ struct SetupParams { ColorFilter color_filter = static_cast<ColorFilter>(NOT_SET); - ScanFlag flags; + ScanFlag flags = ScanFlag::NONE; unsigned get_requested_pixels() const { @@ -210,15 +204,10 @@ struct ScanSession { // 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; + // specifies the full resolution of the sensor that is being used. + unsigned full_resolution = 0; - // the optical resolution of the scanner. + // the optical resolution of the sensor that is being used. unsigned optical_resolution = 0; // the number of pixels at the optical resolution, not including segmentation overhead. @@ -228,10 +217,15 @@ struct ScanSession { // only on gl846, g847 unsigned optical_pixels_raw = 0; + // the number of optical scan lines. Equal to output_line_count on CCD scanners. + unsigned optical_line_count = 0; + // the resolution of the output data. - // gl843-only unsigned output_resolution = 0; + // the offset in pixels from the beginning of output data + unsigned output_startx = 0; + // the number of pixels in output data (after desegmentation) unsigned output_pixels = 0; @@ -259,7 +253,7 @@ struct ScanSession { 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) + // thinner than the CCD element). Computed according to stagger_y. unsigned num_staggered_lines = 0; // the number of lines that color channels shift due to different physical positions of @@ -273,6 +267,11 @@ struct ScanSession { // actual line shift of the blue color unsigned color_shift_lines_b = 0; + // The shifts that need to be applied to the output pixels in x direction. + StaggerConfig stagger_x; + // The shifts that need to be applied to the output pixels in y direction. + StaggerConfig stagger_y; + // the number of scanner segments used in the current scan unsigned segment_count = 1; @@ -280,8 +279,18 @@ struct ScanSession { 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; + /* The following defines the ratio between logical pixel count and pixel count setting sent to + the scanner. The ratio is affected by the following: + + - Certain scanners just like to multiply the pixel number by a multiplier that depends on + the resolution. + + - The sensor may be configured to output one value per multiple physical pixels + + - The scanner will automatically average the pixels that come from the sensor using a + certain ratio. + */ + Ratio pixel_count_ratio = Ratio{1, 1}; // Distance in pixels between consecutive pixels, e.g. between odd and even pixels. Note that // the number of segments can be large. @@ -297,19 +306,18 @@ struct ScanSession { // Currently it's always zero. unsigned output_segment_start_offset = 0; - // the sizes of the corresponding buffers + // How many pixels the shading data is offset to the right from the acquired data. Calculated + // in shading resolution. + int shading_pixel_offset = 0; + + // the size of the read buffer. 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; + // whether calibration should be performed host-side + bool use_host_side_calib = false; void assert_computed() const { @@ -317,10 +325,53 @@ struct ScanSession { throw std::runtime_error("ScanSession is not computed"); } } + + bool operator==(const ScanSession& other) const; }; std::ostream& operator<<(std::ostream& out, const ScanSession& session); +template<class Stream> +void serialize(Stream& str, ScanSession& x) +{ + serialize(str, x.params); + serialize_newline(str); + serialize(str, x.computed); + serialize(str, x.full_resolution); + serialize(str, x.optical_resolution); + serialize(str, x.optical_pixels); + serialize(str, x.optical_pixels_raw); + serialize(str, x.optical_line_count); + serialize(str, x.output_resolution); + serialize(str, x.output_startx); + serialize(str, x.output_pixels); + serialize(str, x.output_channel_bytes); + serialize(str, x.output_line_bytes); + serialize(str, x.output_line_bytes_raw); + serialize(str, x.output_line_bytes_requested); + serialize(str, x.output_line_count); + serialize(str, x.output_total_bytes_raw); + serialize(str, x.output_total_bytes); + serialize(str, x.num_staggered_lines); + serialize(str, x.max_color_shift_lines); + serialize(str, x.color_shift_lines_r); + serialize(str, x.color_shift_lines_g); + serialize(str, x.color_shift_lines_b); + serialize(str, x.stagger_x); + serialize(str, x.stagger_y); + serialize(str, x.segment_count); + serialize(str, x.pixel_startx); + serialize(str, x.pixel_endx); + serialize(str, x.pixel_count_ratio); + serialize(str, x.conseq_pixel_dist); + serialize(str, x.output_segment_pixel_group_count); + serialize(str, x.output_segment_start_offset); + serialize(str, x.shading_pixel_offset); + serialize(str, x.buffer_size_read); + serialize(str, x.enable_ledadd); + serialize(str, x.use_host_side_calib); +} + std::ostream& operator<<(std::ostream& out, const SANE_Parameters& params); } // namespace genesys diff --git a/backend/genesys/tables_frontend.cpp b/backend/genesys/tables_frontend.cpp index 1edf32f..5eb6e3c 100644 --- a/backend/genesys/tables_frontend.cpp +++ b/backend/genesys/tables_frontend.cpp @@ -60,7 +60,8 @@ void genesys_init_frontend_tables() GenesysFrontendLayout analog_devices; analog_devices.type = FrontendType::ANALOG_DEVICES; - + analog_devices.offset_addr = { 0x05, 0x06, 0x07 }; + analog_devices.gain_addr = { 0x02, 0x03, 0x04 }; Genesys_Frontend fe; fe.id = AdcId::WOLFSON_UMAX; @@ -198,6 +199,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_35; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON_GL841; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x3d }, @@ -218,6 +220,30 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); + fe.id = AdcId::CANON_LIDE_90; + fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON; + fe.regs = { + { 0x01, 0x23 }, + { 0x02, 0x07 }, + { 0x03, 0x29 }, + { 0x06, 0x0d }, + { 0x08, 0x00 }, + { 0x09, 0x16 }, + { 0x20, 0x4d }, + { 0x21, 0x4d }, + { 0x22, 0x4d }, + { 0x23, 0x4d }, + { 0x28, 0x14 }, + { 0x29, 0x14 }, + { 0x2a, 0x14 }, + { 0x2b, 0x14 }, + }; + fe.reg2 = {0x00, 0x00, 0x00}; + s_frontends->push_back(fe); + + + fe = Genesys_Frontend(); fe.id = AdcId::AD_XP200; fe.layout = wolfson_layout; fe.regs = { @@ -242,6 +268,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_XP300; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON_GL841; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x35 }, @@ -286,6 +313,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_DSM600; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON_GL841; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x35 }, @@ -307,45 +335,35 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_200; - fe.layout = wolfson_layout; + fe.layout = analog_devices; + fe.layout.type = FrontendType::ANALOG_DEVICES_GL847; 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 }, + { 0x02, 0x32 }, + { 0x03, 0x04 }, + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x3f }, + { 0x07, 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.layout = analog_devices; + fe.layout.type = FrontendType::ANALOG_DEVICES_GL847; 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 }, + { 0x02, 0x2f }, + { 0x03, 0x04 }, + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x3f }, + { 0x07, 0x00 }, }; - fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); @@ -396,6 +414,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_110; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON_GL124; fe.regs = { { 0x00, 0x80 }, { 0x01, 0x8a }, @@ -422,6 +441,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_120; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON_GL124; fe.regs = { { 0x00, 0x80 }, { 0x01, 0xa3 }, @@ -464,6 +484,23 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); + fe.id = AdcId::PLUSTEK_OPTICFILM_7200; + fe.layout = analog_devices; + fe.regs = { + { 0x00, 0xf8 }, + { 0x01, 0x80 }, + { 0x02, 0x2e }, + { 0x03, 0x17 }, + { 0x04, 0x20 }, + { 0x05, 0x0109 }, + { 0x06, 0x01 }, + { 0x07, 0x0104 }, + }; + 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 = { @@ -498,6 +535,23 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); + fe.id = AdcId::PLUSTEK_OPTICFILM_7400; + fe.layout = analog_devices; + fe.regs = { + { 0x00, 0xf8 }, + { 0x01, 0x80 }, + { 0x02, 0x1f }, + { 0x03, 0x14 }, + { 0x04, 0x19 }, + { 0x05, 0x1b }, + { 0x06, 0x1e }, + { 0x07, 0x0e }, + }; + 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 = { @@ -515,6 +569,23 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); + fe.id = AdcId::PLUSTEK_OPTICFILM_8200I; + fe.layout = analog_devices; + fe.regs = { + { 0x00, 0xf8 }, + { 0x01, 0x80 }, + { 0x02, 0x28 }, + { 0x03, 0x20 }, + { 0x04, 0x28 }, + { 0x05, 0x2f }, + { 0x06, 0x2d }, + { 0x07, 0x23 }, + }; + fe.reg2 = {0x00, 0x00, 0x00}; + s_frontends->push_back(fe); + + + fe = Genesys_Frontend(); fe.id = AdcId::CANON_4400F; fe.layout = wolfson_layout; fe.regs = { @@ -537,6 +608,26 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); + fe.id = AdcId::CANON_5600F; + fe.layout = wolfson_layout; + fe.regs = { + { 0x01, 0x23 }, + { 0x02, 0x24 }, + { 0x03, 0x2f }, + { 0x06, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x00 }, + { 0x20, 0x60 }, + { 0x21, 0x60 }, + { 0x22, 0x60 }, + { 0x28, 0x77 }, + { 0x29, 0x77 }, + { 0x2a, 0x77 }, + }; + s_frontends->push_back(fe); + + + fe = Genesys_Frontend(); fe.id = AdcId::CANON_8400F; fe.layout = wolfson_layout; fe.regs = { @@ -583,6 +674,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::IMG101; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON_GL846; fe.regs = { { 0x00, 0x78 }, { 0x01, 0xf0 }, @@ -605,6 +697,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICBOOK_3800; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::WOLFSON_GL846; fe.regs = { { 0x00, 0x78 }, { 0x01, 0xf0 }, @@ -631,6 +724,7 @@ void genesys_init_frontend_tables() fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_80; fe.layout = wolfson_layout; + fe.layout.type = FrontendType::CANON_LIDE_80; fe.regs = { { 0x00, 0x70 }, { 0x01, 0x16 }, diff --git a/backend/genesys/tables_gpo.cpp b/backend/genesys/tables_gpo.cpp index 2c9ad5e..5c1c54f 100644 --- a/backend/genesys/tables_gpo.cpp +++ b/backend/genesys/tables_gpo.cpp @@ -131,6 +131,18 @@ void genesys_init_gpo_tables() gpo = Genesys_Gpo(); + gpo.id = GpioId::CANON_LIDE_90; + gpo.regs = { + { 0x6b, 0x03 }, + { 0x6c, 0x74 }, + { 0x6d, 0x80 }, + { 0x6e, 0x7f }, + { 0x6f, 0xe0 }, + }; + s_gpo->push_back(gpo); + + + gpo = Genesys_Gpo(); gpo.id = GpioId::XP200; gpo.regs = { { 0x66, 0x30 }, @@ -188,10 +200,15 @@ void genesys_init_gpo_tables() gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_200; gpo.regs = { - { 0x6c, 0xfb }, // 0xfb when idle , 0xf9/0xe9 (1200) when scanning + { 0x6b, 0x02 }, + { 0x6c, 0xf9 }, // 0xfb when idle , 0xf9/0xe9 (1200) when scanning { 0x6d, 0x20 }, { 0x6e, 0xff }, { 0x6f, 0x00 }, + { 0xa6, 0x04 }, + { 0xa7, 0x04 }, + { 0xa8, 0x00 }, + { 0xa9, 0x00 }, }; s_gpo->push_back(gpo); @@ -199,10 +216,15 @@ void genesys_init_gpo_tables() gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_700F; gpo.regs = { + { 0x6b, 0x06 }, { 0x6c, 0xdb }, { 0x6d, 0xff }, { 0x6e, 0xff }, { 0x6f, 0x80 }, + { 0xa6, 0x15 }, + { 0xa7, 0x07 }, + { 0xa8, 0x20 }, + { 0xa9, 0x10 }, }; s_gpo->push_back(gpo); @@ -293,6 +315,19 @@ void genesys_init_gpo_tables() gpo = Genesys_Gpo(); + gpo.id = GpioId::PLUSTEK_OPTICFILM_7200; + gpo.regs = { + { 0x6b, 0x33 }, + { 0x6c, 0x00 }, + { 0x6d, 0x80 }, + { 0x6e, 0x0c }, + { 0x6f, 0x80 }, + { 0x7e, 0x00 } + }; + s_gpo->push_back(gpo); + + + gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICFILM_7200I; gpo.regs = { { 0x6c, 0x4c }, @@ -320,6 +355,16 @@ void genesys_init_gpo_tables() }; s_gpo->push_back(gpo); + + gpo = Genesys_Gpo(); + gpo.id = GpioId::PLUSTEK_OPTICFILM_7400; + gpo.regs = { + { 0x6b, 0x30 }, { 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_7500I; gpo.regs = { @@ -334,6 +379,16 @@ void genesys_init_gpo_tables() }; s_gpo->push_back(gpo); + + gpo = Genesys_Gpo(); + gpo.id = GpioId::PLUSTEK_OPTICFILM_8200I; + gpo.regs = { + { 0x6b, 0x30 }, { 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::CANON_4400F; gpo.regs = { @@ -350,6 +405,22 @@ void genesys_init_gpo_tables() gpo = Genesys_Gpo(); + gpo.id = GpioId::CANON_5600F; + gpo.regs = { + { 0x6b, 0x87 }, + { 0x6c, 0xf0 }, + { 0x6d, 0x5f }, + { 0x6e, 0x7f }, + { 0x6f, 0xa0 }, + { 0xa6, 0x07 }, + { 0xa7, 0x1c }, + { 0xa8, 0x00 }, + { 0xa9, 0x04 }, + }; + s_gpo->push_back(gpo); + + + gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_8400F; gpo.regs = { { 0x6c, 0x9a }, @@ -382,10 +453,8 @@ void genesys_init_gpo_tables() gpo = Genesys_Gpo(); gpo.id = GpioId::IMG101; gpo.regs = { - { 0x6c, 0x41 }, - { 0x6d, 0xa4 }, - { 0x6e, 0x13 }, - { 0x6f, 0xa7 }, + { 0x6b, 0x72 }, { 0x6c, 0x1f }, { 0x6d, 0xa4 }, { 0x6e, 0x13 }, { 0x6f, 0xa7 }, + { 0xa6, 0x11 }, { 0xa7, 0xff }, { 0xa8, 0x19 }, { 0xa9, 0x05 }, }; s_gpo->push_back(gpo); @@ -393,10 +462,8 @@ void genesys_init_gpo_tables() gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICBOOK_3800; gpo.regs = { - { 0x6c, 0x41 }, - { 0x6d, 0xa4 }, - { 0x6e, 0x13 }, - { 0x6f, 0xa7 }, + { 0x6b, 0x30 }, { 0x6c, 0x01 }, { 0x6d, 0x80 }, { 0x6e, 0x2d }, { 0x6f, 0x80 }, + { 0xa6, 0x0c }, { 0xa7, 0x8f }, { 0xa8, 0x08 }, { 0xa9, 0x04 }, }; s_gpo->push_back(gpo); diff --git a/backend/genesys/tables_memory_layout.cpp b/backend/genesys/tables_memory_layout.cpp new file mode 100644 index 0000000..3eaedd4 --- /dev/null +++ b/backend/genesys/tables_memory_layout.cpp @@ -0,0 +1,164 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2020 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. +*/ + +#define DEBUG_DECLARE_ONLY + +#include "low.h" + +namespace genesys { + +StaticInit<std::vector<MemoryLayout>> s_memory_layout; + +void genesys_init_memory_layout_tables() +{ + s_memory_layout.init(); + + MemoryLayout ml; + ml.models = { ModelId::CANON_IMAGE_FORMULA_101 }; + // FIXME: this scanner does not set all required registers + ml.regs = { + { 0xe0, 0x00 }, { 0xe1, 0xb0 }, { 0xe2, 0x05 }, { 0xe3, 0xe7 }, + { 0xe4, 0x05 }, { 0xe5, 0xe8 }, { 0xe6, 0x0b }, { 0xe7, 0x1f }, + { 0xe8, 0x0b }, { 0xe9, 0x20 }, + }; + s_memory_layout->push_back(ml); + + + ml = MemoryLayout(); + ml.models = { ModelId::PLUSTEK_OPTICBOOK_3800 }; + // FIXME: this scanner does not set all required registers + ml.regs = { + { 0xe0, 0x00 }, { 0xe1, 0x68 }, { 0xe2, 0x03 }, { 0xe3, 0x00 }, + { 0xe4, 0x03 }, { 0xe5, 0x01 }, { 0xe6, 0x05 }, { 0xe7, 0x99 }, + { 0xe8, 0x05 }, { 0xe9, 0x9a }, + }; + s_memory_layout->push_back(ml); + + ml = MemoryLayout(); + ml.models = { ModelId::PLUSTEK_OPTICFILM_7400, ModelId::PLUSTEK_OPTICFILM_8200I }; + ml.regs = { + { 0x81, 0x6d }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x84, 0x00 }, + { 0x85, 0x00 }, { 0x86, 0x00 }, + { 0xd0, 0x0a }, { 0xd1, 0x0a }, { 0xd2, 0x0a }, + { 0xe0, 0x00 }, { 0xe1, 0x68 }, { 0xe2, 0x03 }, { 0xe3, 0x00 }, + { 0xe4, 0x03 }, { 0xe5, 0x01 }, { 0xe6, 0x05 }, { 0xe7, 0x99 }, + { 0xe8, 0x05 }, { 0xe9, 0x9a }, { 0xea, 0x08 }, { 0xeb, 0x32 }, + { 0xec, 0x08 }, { 0xed, 0x33 }, { 0xee, 0x0a }, { 0xef, 0xcb }, + { 0xf0, 0x0a }, { 0xf1, 0xcc }, { 0xf2, 0x0d }, { 0xf3, 0x64 }, + { 0xf4, 0x0d }, { 0xf5, 0x65 }, { 0xf6, 0x0f }, { 0xf7, 0xfd }, + }; + s_memory_layout->push_back(ml); + + + /* On GL847 and GL124, the values of the base address for shading data must be multiplied by + 8192=0x4000 to give address on AHB + + On GL847 and GL124, the values of the base address for scanned data must be multiplied by + 1024*2=0x0800 to give address on AHB + */ + ml = MemoryLayout(); + ml.models = { ModelId::CANON_5600F }; + ml.regs = { + { 0xd0, 0x0a }, + { 0xe0, 0x01 }, { 0xe1, 0x2c }, { 0xe2, 0x06 }, { 0xe3, 0x4e }, + { 0xe4, 0x06 }, { 0xe5, 0x4f }, { 0xe6, 0x0b }, { 0xe7, 0x71 }, + { 0xe8, 0x0b }, { 0xe9, 0x72 }, { 0xea, 0x10 }, { 0xeb, 0x94 }, + { 0xec, 0x10 }, { 0xed, 0x95 }, { 0xee, 0x15 }, { 0xef, 0xb7 }, + { 0xf0, 0x15 }, { 0xf1, 0xb8 }, { 0xf2, 0x1a }, { 0xf3, 0xda }, + { 0xf4, 0x1a }, { 0xf5, 0xdb }, { 0xf6, 0x1f }, { 0xf7, 0xfd }, + { 0xf8, 0x05 } + }; + s_memory_layout->push_back(ml); + + + ml = MemoryLayout(); + ml.models = { ModelId::CANON_LIDE_100 }; + ml.regs = { + { 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 }, + { 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x02 }, { 0xe3, 0x55 }, + { 0xe4, 0x02 }, { 0xe5, 0x56 }, { 0xe6, 0x03 }, { 0xe7, 0xff }, + { 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x02 }, { 0xeb, 0x55 }, + { 0xec, 0x02 }, { 0xed, 0x56 }, { 0xee, 0x03 }, { 0xef, 0xff }, + { 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x02 }, { 0xf3, 0x55 }, + { 0xf4, 0x02 }, { 0xf5, 0x56 }, { 0xf6, 0x03 }, { 0xf7, 0xff }, + }; + s_memory_layout->push_back(ml); + + + ml = MemoryLayout(); + ml.models = { ModelId::CANON_LIDE_200 }; + ml.regs = { + { 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 }, + { 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x02 }, { 0xe3, 0x91 }, + { 0xe4, 0x02 }, { 0xe5, 0x92 }, { 0xe6, 0x03 }, { 0xe7, 0xff }, + { 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x02 }, { 0xeb, 0x91 }, + { 0xec, 0x02 }, { 0xed, 0x92 }, { 0xee, 0x03 }, { 0xef, 0xff }, + { 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x02 }, { 0xf3, 0x91 }, + { 0xf4, 0x02 }, { 0xf5, 0x92 }, { 0xf6, 0x03 }, { 0xf7, 0xff }, + }; + s_memory_layout->push_back(ml); + + + ml = MemoryLayout(); + ml.models = { ModelId::CANON_LIDE_700F }; + ml.regs = { + { 0xd0, 0x0a }, { 0xd1, 0x33 }, { 0xd2, 0x5c }, + { 0xe0, 0x02 }, { 0xe1, 0x14 }, { 0xe2, 0x09 }, { 0xe3, 0x09 }, + { 0xe4, 0x09 }, { 0xe5, 0x0a }, { 0xe6, 0x0f }, { 0xe7, 0xff }, + { 0xe8, 0x02 }, { 0xe9, 0x14 }, { 0xea, 0x09 }, { 0xeb, 0x09 }, + { 0xec, 0x09 }, { 0xed, 0x0a }, { 0xee, 0x0f }, { 0xef, 0xff }, + { 0xf0, 0x02 }, { 0xf1, 0x14 }, { 0xf2, 0x09 }, { 0xf3, 0x09 }, + { 0xf4, 0x09 }, { 0xf5, 0x0a }, { 0xf6, 0x0f }, { 0xf7, 0xff }, + }; + s_memory_layout->push_back(ml); + + + ml = MemoryLayout(); + ml.models = { ModelId::CANON_LIDE_110, ModelId::CANON_LIDE_120 }; + ml.regs = { + { 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 }, + { 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x08 }, { 0xe3, 0x55 }, + { 0xe4, 0x08 }, { 0xe5, 0x56 }, { 0xe6, 0x0f }, { 0xe7, 0xff }, + { 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x08 }, { 0xeb, 0x55 }, + { 0xec, 0x08 }, { 0xed, 0x56 }, { 0xee, 0x0f }, { 0xef, 0xff }, + { 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x08 }, { 0xf3, 0x55 }, + { 0xf4, 0x08 }, { 0xf5, 0x56 }, { 0xf6, 0x0f }, { 0xf7, 0xff }, + + }; + s_memory_layout->push_back(ml); + + + ml = MemoryLayout(); + ml.models = { ModelId::CANON_LIDE_210, ModelId::CANON_LIDE_220 }; + ml.regs = { + { 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 }, + { 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x08 }, { 0xe3, 0x91 }, + { 0xe4, 0x08 }, { 0xe5, 0x92 }, { 0xe6, 0x0f }, { 0xe7, 0xff }, + { 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x08 }, { 0xeb, 0x91 }, + { 0xec, 0x08 }, { 0xed, 0x92 }, { 0xee, 0x0f }, { 0xef, 0xff }, + { 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x08 }, { 0xf3, 0x91 }, + { 0xf4, 0x08 }, { 0xf5, 0x92 }, { 0xf6, 0x0f }, { 0xf7, 0xff }, + }; + s_memory_layout->push_back(ml); +} + +} // namespace genesys diff --git a/backend/genesys/tables_model.cpp b/backend/genesys/tables_model.cpp index 0b3a0af..2c5e6a3 100644 --- a/backend/genesys/tables_model.cpp +++ b/backend/genesys/tables_model.cpp @@ -58,10 +58,44 @@ namespace genesys { -StaticInit<std::vector<Genesys_USB_Device_Entry>> s_usb_devices; +StaticInit<std::vector<UsbDeviceEntry>> s_usb_devices; void genesys_init_usb_device_tables() { + /* Guidelines on calibration area sizes + ------------------------------------ + + on many scanners scanning a single line takes aroung 10ms. In order not to take excessive + amount of time, the sizes of the calibration area are limited as follows: + 2400 dpi or less: 4mm (would take ~4 seconds on 2400 dpi) + 4800 dpi or less: 3mm (would take ~6 seconds on 4800 dpi) + anything more: 2mm (would take ~7 seconds on 9600 dpi) + + Optional properties + ------------------- + + All fields of the Genesys_Model class are defined even if they use default value, with + the following exceptions: + + If the scanner does not have ScanMethod::TRANSPARENCY or ScanMethod::TRANSPARENCY_INFRARED, + the following properties are optional: + + 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.y_size_calib_ta_mm = 0.0; + + If the scanner does not have ModelFlag::DARK_WHITE_CALIBRATION, then the following + properties are optional: + + model.y_offset_calib_dark_white_mm = 0.0; + model.y_size_calib_dark_white_mm = 0.0; + */ + s_usb_devices.init(); Genesys_Model model; @@ -87,15 +121,9 @@ void genesys_init_usb_device_tables() model.y_size = 299.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 228.6; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -112,10 +140,8 @@ void genesys_init_usb_device_tables() model.adc_id = AdcId::WOLFSON_UMAX; model.gpio_id = GpioId::UMAX; model.motor_id = MotorId::UMAX; - model.flags = GENESYS_FLAG_UNTESTED; + model.flags = ModelFlag::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); @@ -144,17 +170,13 @@ void genesys_init_usb_device_tables() model.x_size = 218.0; model.y_size = 299.0; - model.y_offset_calib_white = 6.0; + model.y_offset_calib_white = 3.0; + model.y_size_calib_mm = 3.0; + model.y_offset_calib_dark_white_mm = 1.0; + model.y_size_calib_dark_white_mm = 6.0; + model.x_size_calib_mm = 220.13334; 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; @@ -170,16 +192,12 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_WHITE_CALIBRATION | + ModelFlag::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); @@ -209,15 +227,9 @@ void genesys_init_usb_device_tables() model.y_size = 300.0; model.y_offset_calib_white = 9.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 227.584; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -234,12 +246,8 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::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); @@ -269,15 +277,9 @@ void genesys_init_usb_device_tables() model.y_size = 314.5; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 226.9067; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -294,13 +296,10 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::SHADING_REPARK | + ModelFlag::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); @@ -329,15 +328,9 @@ void genesys_init_usb_device_tables() model.y_size = 315.0; model.y_offset_calib_white = 3.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 226.9067; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -353,13 +346,10 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -389,15 +379,9 @@ void genesys_init_usb_device_tables() model.y_size = 315.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 226.9067; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -414,13 +398,10 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -438,6 +419,10 @@ void genesys_init_usb_device_tables() { ScanMethod::FLATBED }, { 1200, 600, 300 }, { 1200, 600, 300 }, + }, { + { ScanMethod::TRANSPARENCY }, + { 4800, 2400, 1200 }, + { 9600, 4800, 2400, 1200 }, } }; @@ -445,20 +430,23 @@ void genesys_init_usb_device_tables() model.bpp_color_values = { 8, 16 }; model.x_offset = 6.0; - model.y_offset = 12.00; + model.y_offset = 10.00; model.x_size = 215.9; model.y_size = 297.0; - model.y_offset_calib_white = 0.0; + model.y_offset_calib_white = 2.0; + model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; + model.x_size_calib_mm = 241.3; - 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.x_offset_ta = 115.0; + model.y_offset_ta = 37.0; + model.x_size_ta = 35.0; + model.y_size_ta = 230.0; - model.y_offset_sensor_to_ta = 0.0; - model.y_offset_calib_white_ta = 40.0; + model.y_offset_sensor_to_ta = 23.0; + model.y_offset_calib_white_ta = 24.0; + model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -475,15 +463,13 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::DARK_CALIBRATION | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::SHADING_REPARK | + ModelFlag::UTA_NO_SECONDARY_MOTOR; + 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); @@ -515,13 +501,15 @@ void genesys_init_usb_device_tables() model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; - model.x_offset = 3.5; + model.x_offset = 5.5; model.y_offset = 17.00; model.x_size = 219.9; model.y_size = 300.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 10.0; + model.x_size_calib_mm = 225.425; model.x_offset_ta = 75.0; model.y_offset_ta = 45.00; @@ -530,6 +518,7 @@ void genesys_init_usb_device_tables() model.y_offset_sensor_to_ta = 22.0; model.y_offset_calib_white_ta = 25.0; + model.y_size_calib_ta_mm = 3.0; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -546,17 +535,11 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::DARK_CALIBRATION | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -590,15 +573,18 @@ void genesys_init_usb_device_tables() model.y_size = 297.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 8.0; + model.x_size_calib_mm = 240.70734; - model.x_offset_ta = 85.0; - model.y_offset_ta = 26.0; + model.x_offset_ta = 97.0; + model.y_offset_ta = 38.5; 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.y_offset_sensor_to_ta = 23.0; + model.y_offset_calib_white_ta = 25.5; + model.y_size_calib_ta_mm = 3.0; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -615,17 +601,11 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::DARK_CALIBRATION | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -654,16 +634,10 @@ void genesys_init_usb_device_tables() model.x_size = 216.07; model.y_size = 299.0; - model.y_offset_calib_white = 1.0; + model.y_offset_calib_white = 0.4233334; + model.y_size_calib_mm = 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.x_size_calib_mm = 217.4241; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -680,18 +654,14 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::SIS_SENSOR | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::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); @@ -721,15 +691,9 @@ void genesys_init_usb_device_tables() model.y_size = 300.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 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.x_size_calib_mm = 218.7787; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -745,17 +709,13 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::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); @@ -785,15 +745,9 @@ void genesys_init_usb_device_tables() model.y_size = 300.0; model.y_offset_calib_white = 1.0; + model.y_size_calib_mm = 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.x_size_calib_mm = 216.0694; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -808,17 +762,13 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::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); @@ -834,30 +784,23 @@ void genesys_init_usb_device_tables() 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 }, + { 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.x_offset = 2.1; model.y_offset = 8.7; model.x_size = 216.70; model.y_size = 297.5; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 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.x_size_calib_mm = 218.7787; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -874,18 +817,14 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::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); @@ -901,30 +840,23 @@ void genesys_init_usb_device_tables() model.resolutions = { { { ScanMethod::FLATBED }, - // BUG: 4800 resolution crashes - { /*4800,*/ 2400, 1200, 600, 300, 150, 100, 75 }, - { /*4800,*/ 2400, 1200, 600, 300, 150, 100, 75 }, + { 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.x_offset = 2.1; model.y_offset = 8.7; model.x_size = 216.70; model.y_size = 297.5; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 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.x_size_calib_mm = 218.7787; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -940,84 +872,84 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::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.name = "canon-canoscan-5600f"; model.vendor = "Canon"; - model.model = "5600F"; + model.model = "CanoScan 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 }, + { ScanMethod::FLATBED, ScanMethod::TRANSPARENCY }, + { 4800, 2400, 1200, 600, 300, /*150*/ }, + { 4800, 2400, 1200, 600, 300, /*150*/ }, } }; 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.x_offset = 1.5; + model.y_offset = 10.4; + model.x_size = 219.00; + model.y_size = 305.0; - model.y_offset_calib_white = 3.0; + model.y_offset_calib_white = 2.0; + model.y_size_calib_mm = 2.0; model.x_offset_calib_black = 0.0; + model.x_size_calib_mm = 220.5; - 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.x_offset_ta = 93.0; + model.y_offset_ta = 42.4; + model.x_size_ta = 35.0; + model.y_size_ta = 230.0; - model.y_offset_sensor_to_ta = 0.0; - model.y_offset_calib_white_ta = 0.0; + model.y_offset_sensor_to_ta = 0; + model.y_offset_calib_white_ta = 21.4; + model.y_size_calib_ta_mm = 1.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.ld_shift_g = 32; + model.ld_shift_b = 64; model.line_mode_color_order = ColorOrder::RGB; - model.is_cis = true; + model.is_cis = false; 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.sensor_id = SensorId::CCD_CANON_5600F; + model.adc_id = AdcId::CANON_5600F; + model.gpio_id = GpioId::CANON_5600F; + model.motor_id = MotorId::CANON_5600F; + model.flags = ModelFlag::SIS_SENSOR | + ModelFlag::INVERT_PIXEL_DATA | + ModelFlag::DISABLE_ADC_CALIBRATION | + ModelFlag::DISABLE_EXPOSURE_CALIBRATION | + ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::UTA_NO_SECONDARY_MOTOR | + ModelFlag::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); @@ -1032,9 +964,10 @@ void genesys_init_usb_device_tables() model.resolutions = { { + // FIXME: support 2400 ad 4800 dpi { ScanMethod::FLATBED }, - { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 }, - { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 }, + { 1200, 600, 300, 200, 150, 100, 75 }, + { 1200, 600, 300, 200, 150, 100, 75 }, } }; @@ -1046,16 +979,11 @@ void genesys_init_usb_device_tables() model.x_size = 216.07; model.y_size = 297.0; - model.y_offset_calib_white = 1.0; + model.y_offset_calib_white = 0.4233334; + model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; + model.x_size_calib_mm = 219.6254; - 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; @@ -1071,18 +999,14 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::SIS_SENSOR | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::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); @@ -1111,16 +1035,10 @@ void genesys_init_usb_device_tables() model.x_size = 216.07; model.y_size = 299.0; - model.y_offset_calib_white = 0.0; + model.y_offset_calib_white = 0.4233334; + model.y_size_calib_mm = 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.x_size_calib_mm = 217.4241; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1136,18 +1054,14 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::SIS_SENSOR | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::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); @@ -1176,16 +1090,12 @@ void genesys_init_usb_device_tables() model.x_size = 218.0; model.y_size = 299.0; - model.y_offset_calib_white = 6.0; + model.y_offset_calib_white = 3.0; + model.y_size_calib_mm = 3.0; + model.y_offset_calib_dark_white_mm = 1.0; + model.y_size_calib_dark_white_mm = 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.x_size_calib_mm = 220.13334; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1197,23 +1107,18 @@ void genesys_init_usb_device_tables() model.is_cis = true; model.is_sheetfed = false; - model.sensor_id = SensorId::CIS_CANON_LIDE_35; + model.sensor_id = SensorId::CIS_CANON_LIDE_60; 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.motor_id = MotorId::CANON_LIDE_60; + model.flags = ModelFlag::DARK_WHITE_CALIBRATION | + ModelFlag::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); @@ -1240,15 +1145,11 @@ void genesys_init_usb_device_tables() model.y_size = 299.0; model.y_offset_calib_white = 4.5; + model.y_size_calib_mm = 3.0; + model.y_offset_calib_dark_white_mm = 1.0; + model.y_size_calib_dark_white_mm = 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.x_size_calib_mm = 216.7467; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1265,22 +1166,77 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_WHITE_CALIBRATION | + ModelFlag::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 = "canon-lide-90"; + model.vendor = "Canon"; + model.model = "LiDE 90"; + model.model_id = ModelId::CANON_LIDE_90; + model.asic_type = AsicType::GL842; + + model.resolutions = { + { + { ScanMethod::FLATBED }, + { 2400, 1200, 600, 300 }, + { 2400, 1200, 600, 300 }, + } + }; + + model.bpp_gray_values = { 8, 16 }; + model.bpp_color_values = { 8, 16 }; + model.x_offset = 3.50; + model.y_offset = 9.0; + model.x_size = 219.0; + model.y_size = 299.0; + + model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 2.0; + model.y_offset_calib_dark_white_mm = 0.0; + model.y_size_calib_dark_white_mm = 0.0; + model.x_offset_calib_black = 0.0; + model.x_size_calib_mm = 221.5; + + 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_90; + model.adc_id = AdcId::CANON_LIDE_90; + model.gpio_id = GpioId::CANON_LIDE_90; + model.motor_id = MotorId::CANON_LIDE_90; + model.flags = ModelFlag::DISABLE_ADC_CALIBRATION | + ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN | + ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION | + ModelFlag::DISABLE_FAST_FEEDING | + ModelFlag::SHADING_REPARK | + ModelFlag::CUSTOM_GAMMA; + model.buttons = GENESYS_HAS_SCAN_SW | + GENESYS_HAS_FILE_SW | + GENESYS_HAS_EMAIL_SW | + GENESYS_HAS_COPY_SW; + model.search_lines = 400; + + s_usb_devices->emplace_back(0x04a9, 0x1900, model); + + + model = Genesys_Model(); model.name = "hewlett-packard-scanjet-2300c"; model.vendor = "Hewlett Packard"; model.model = "ScanJet 2300c"; @@ -1298,27 +1254,21 @@ void genesys_init_usb_device_tables() model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; - model.x_offset = 2.0; - model.y_offset = 7.5; + model.x_offset = 6.5; + model.y_offset = 8; model.x_size = 215.9; model.y_size = 295.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 227.2454; model.post_scan = 0.0; model.eject_feed = 0.0; - model.ld_shift_r = 16; - model.ld_shift_g = 8; + model.ld_shift_r = 32; + model.ld_shift_g = 16; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; @@ -1328,15 +1278,10 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::GAMMA_14BIT | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -1366,15 +1311,9 @@ void genesys_init_usb_device_tables() model.y_size = 297.2; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 2.0; // FIXME: check if white area is really so small 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.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1391,14 +1330,10 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::GAMMA_14BIT | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -1428,15 +1363,9 @@ void genesys_init_usb_device_tables() model.y_size = 297.2; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 220.1334; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1453,14 +1382,10 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::GAMMA_14BIT | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_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); @@ -1490,15 +1415,9 @@ void genesys_init_usb_device_tables() model.y_size = 300.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1515,14 +1434,11 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::GAMMA_14BIT | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -1552,15 +1468,9 @@ void genesys_init_usb_device_tables() model.y_size = 299.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 229.2774; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1577,10 +1487,8 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::UNTESTED | ModelFlag::GAMMA_14BIT; 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); @@ -1604,20 +1512,14 @@ void genesys_init_usb_device_tables() model.bpp_color_values = { 8, 16 }; model.x_offset = 3.5; - model.y_offset = 7.5; + model.y_offset = 7.5; // FIXME: incorrect, needs updating model.x_size = 218.0; model.y_size = 299.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 228.6; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1634,14 +1536,10 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::UNTESTED | + ModelFlag::GAMMA_14BIT | + ModelFlag::CUSTOM_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, 0x0601, model); @@ -1665,26 +1563,20 @@ void genesys_init_usb_device_tables() model.bpp_color_values = { 8, 16 }; model.x_offset = 0.30; - model.y_offset = 0.80; + model.y_offset = 4.0; // FIXME: incorrect, needs updating model.x_size = 220.0; model.y_size = 296.4; model.y_offset_calib_white = 0.00; + model.y_size_calib_mm = 2.0; 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.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; - model.ld_shift_r = 48; - model.ld_shift_g = 24; + model.ld_shift_r = 96; + model.ld_shift_g = 48; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; @@ -1694,19 +1586,15 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::GAMMA_14BIT | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -1735,15 +1623,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 433.4934; model.post_scan = 26.5; // this is larger than needed -- accounts for second sensor head, which is a calibration item @@ -1760,13 +1642,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -1795,15 +1673,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 105.664; model.post_scan = 17.5; model.eject_feed = 0.0; @@ -1820,13 +1692,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -1855,15 +1723,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; @@ -1880,13 +1742,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -1915,15 +1773,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; @@ -1940,13 +1792,12 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::DISABLE_ADC_CALIBRATION | + ModelFlag::DISABLE_EXPOSURE_CALIBRATION | + ModelFlag::DISABLE_SHADING_CALIBRATION | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -1976,15 +1827,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; @@ -2001,13 +1846,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -2036,15 +1877,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; @@ -2061,13 +1896,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -2098,15 +1929,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; @@ -2122,13 +1947,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -2157,15 +1978,9 @@ void genesys_init_usb_device_tables() model.y_size = 500; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 212.5134; model.post_scan = 26.5; // this is larger than needed -- accounts for second sensor head, which is a calibration item @@ -2182,13 +1997,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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; @@ -2219,15 +2030,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 433.4934; model.post_scan = 26.5; // this is larger than needed -- accounts for second sensor head, which is a calibration item @@ -2244,13 +2049,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -2280,19 +2081,14 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 433.4934; 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; @@ -2301,18 +2097,14 @@ void genesys_init_usb_device_tables() model.is_cis = true; model.is_sheetfed = true; - model.sensor_id = SensorId::CCD_XP300; + model.sensor_id = SensorId::CCD_DOCKETPORT_487; 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.flags = ModelFlag::DARK_CALIBRATION | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -2337,26 +2129,20 @@ void genesys_init_usb_device_tables() model.bpp_color_values = { 8, 16 }; model.x_offset = 4.00; - model.y_offset = 0.80; + model.y_offset = 5.0; // FIXME: incorrect, needs updating model.x_size = 215.9; model.y_size = 296.4; model.y_offset_calib_white = 0.00; + model.y_size_calib_mm = 4.0; 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.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; - model.ld_shift_r = 48; - model.ld_shift_g = 24; + model.ld_shift_r = 96; + model.ld_shift_g = 48; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; @@ -2366,18 +2152,15 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::GAMMA_14BIT | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -2402,26 +2185,20 @@ void genesys_init_usb_device_tables() model.bpp_color_values = { 8, 16 }; model.x_offset = 4.00; - model.y_offset = 0.80; + model.y_offset = 5.0; // FIXME: incorrect, needs updating model.x_size = 215.9; model.y_size = 296.4; model.y_offset_calib_white = 0.00; + model.y_size_calib_mm = 4.0; 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.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; - model.ld_shift_r = 48; - model.ld_shift_g = 24; + model.ld_shift_r = 96; + model.ld_shift_g = 48; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; @@ -2431,18 +2208,15 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::GAMMA_14BIT | + ModelFlag::DARK_CALIBRATION | + ModelFlag::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); @@ -2472,15 +2246,9 @@ void genesys_init_usb_device_tables() model.y_size = 511; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; @@ -2497,13 +2265,9 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); @@ -2533,15 +2297,9 @@ void genesys_init_usb_device_tables() model.y_size = 297.0; model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 213.7834; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -2558,19 +2316,81 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::UNTESTED | // not fully working yet + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_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-7200"; + model.vendor = "PLUSTEK"; + model.model = "OpticFilm 7200"; + model.model_id = ModelId::PLUSTEK_OPTICFILM_7200; + model.asic_type = AsicType::GL842; + + 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.y_size_calib_mm = 0.0; + model.x_offset_calib_black = 6.5; + model.x_size_calib_mm = 35.9834; + + model.x_offset_ta = 0.7f; + model.y_offset_ta = 28.0; + model.x_size_ta = 36.0; + model.y_size_ta = 25.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.y_size_calib_ta_mm = 2.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_7200; + model.adc_id = AdcId::PLUSTEK_OPTICFILM_7200; + model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7200; + model.motor_id = MotorId::PLUSTEK_OPTICFILM_7200; + + model.flags = ModelFlag::WARMUP | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK; + + model.search_lines = 200; + s_usb_devices->emplace_back(0x07b3, 0x0807, model); + + model = Genesys_Model(); model.name = "plustek-opticfilm-7200i"; model.vendor = "PLUSTEK"; @@ -2594,16 +2414,22 @@ void genesys_init_usb_device_tables() model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; + model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; + model.x_size_calib_mm = 35.9834; 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.y_size_calib_ta_mm = 2.0; + model.post_scan = 0.0; model.eject_feed = 0.0; @@ -2621,23 +2447,29 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::SWAP_16BIT_DATA; + model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0c04, model); + // same as 7200i, just without the infrared channel + model.name = "plustek-opticfilm-7200-v2"; + model.model = "OpticFilm 7200 v2"; + model.resolutions = { + { + { ScanMethod::TRANSPARENCY }, + { 7200, 3600, 1800, 900 }, + { 7200, 3600, 1800, 900 }, + } + }; + s_usb_devices->emplace_back(0x07b3, 0x0c07, model); + + model = Genesys_Model(); model.name = "plustek-opticfilm-7300"; model.vendor = "PLUSTEK"; @@ -2661,16 +2493,22 @@ void genesys_init_usb_device_tables() model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; + model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; + model.x_size_calib_mm = 35.9834; 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.y_size_calib_ta_mm = 2.0; + model.post_scan = 0.0; model.eject_feed = 0.0; @@ -2688,21 +2526,91 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK; + model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0c12, model); + // same as 7300, same USB ID as 7400-v2 + model.name = "plustek-opticfilm-7400-v1"; + model.model = "OpticFilm 7400 (v1)"; + s_usb_devices->emplace_back(0x07b3, 0x0c3a, 0x0400, model); + + + model = Genesys_Model(); + model.name = "plustek-opticfilm-7400-v2"; + model.vendor = "PLUSTEK"; + model.model = "OpticFilm 7400 (v2)"; + model.model_id = ModelId::PLUSTEK_OPTICFILM_7400; + model.asic_type = AsicType::GL845; + + model.resolutions = { + { + { ScanMethod::TRANSPARENCY }, + { 7200, 3600, 2400, 1200, 600 }, + { 7200, 3600, 2400, 1200, 600 }, + } + }; + + 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.y_size_calib_mm = 0.0; + model.x_offset_calib_black = 6.5; + model.x_size_calib_mm = 36.83; + + model.x_offset_ta = 0.5; + model.y_offset_ta = 29.0; + model.x_size_ta = 36.33; + model.y_size_ta = 25.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.y_size_calib_ta_mm = 2.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_7400; + model.adc_id = AdcId::PLUSTEK_OPTICFILM_7400; + model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7400; + model.motor_id = MotorId::PLUSTEK_OPTICFILM_7400; + + model.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK; + + model.search_lines = 200; + s_usb_devices->emplace_back(0x07b3, 0x0c3a, 0x0605, model); + + + // same as 7400-v2 + model.name = "plustek-opticfilm-8100"; + model.model = "OpticFilm 8100"; + s_usb_devices->emplace_back(0x07b3, 0x130c, model); + + model = Genesys_Model(); model.name = "plustek-opticfilm-7500i"; model.vendor = "PLUSTEK"; @@ -2726,16 +2634,22 @@ void genesys_init_usb_device_tables() model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; + model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 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.x_size_calib_mm = 35.9834; + 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.y_size_calib_ta_mm = 2.0; + model.post_scan = 0.0; model.eject_feed = 0.0; @@ -2753,22 +2667,91 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::WARMUP | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK; + model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0c13, model); + // same as 7500i + model.name = "plustek-opticfilm-7600i-v1"; + model.model = "OpticFilm 7600i (v1)"; + s_usb_devices->emplace_back(0x07b3, 0x0c3b, 0x0400, model); + + + model = Genesys_Model(); + model.name = "plustek-opticfilm-8200i"; + model.vendor = "PLUSTEK"; + model.model = "OpticFilm 8200i"; + model.model_id = ModelId::PLUSTEK_OPTICFILM_8200I; + model.asic_type = AsicType::GL845; + + 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.y_size_calib_mm = 0.0; + model.x_offset_calib_black = 6.5; + model.x_size_calib_mm = 36.83; + + model.x_offset_ta = 0.5; + model.y_offset_ta = 28.5; + model.x_size_ta = 36.33; + model.y_size_ta = 25.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.y_size_calib_ta_mm = 2.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_8200I; + model.adc_id = AdcId::PLUSTEK_OPTICFILM_8200I; + model.gpio_id = GpioId::PLUSTEK_OPTICFILM_8200I; + model.motor_id = MotorId::PLUSTEK_OPTICFILM_8200I; + + model.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK; + + model.search_lines = 200; + s_usb_devices->emplace_back(0x07b3, 0x130d, model); + + + // same as 8200i + model.name = "plustek-opticfilm-7600i-v2"; + model.model = "OpticFilm 7600i (v2)"; + s_usb_devices->emplace_back(0x07b3, 0x0c3b, 0x0605, model); + + model = Genesys_Model(); model.name = "hewlett-packard-scanjet-N6310"; model.vendor = "Hewlett Packard"; @@ -2792,16 +2775,10 @@ void genesys_init_usb_device_tables() model.x_size = 216; model.y_size = 511; - model.y_offset_calib_white = 3.0; + model.y_offset_calib_white = 0.0; + model.y_size_calib_mm = 4.0; // FIXME: y_offset is liely incorrect 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.x_size_calib_mm = 452.12; model.post_scan = 0; model.eject_feed = 0; @@ -2818,17 +2795,15 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::UNTESTED | + ModelFlag::GAMMA_14BIT | + ModelFlag::DARK_CALIBRATION | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DISABLE_ADC_CALIBRATION | + ModelFlag::DISABLE_EXPOSURE_CALIBRATION | + ModelFlag::DISABLE_SHADING_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); @@ -2858,15 +2833,9 @@ void genesys_init_usb_device_tables() model.y_size = 300.0; model.y_offset_calib_white = 9.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 215.9; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -2883,12 +2852,8 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::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); @@ -2918,15 +2883,9 @@ void genesys_init_usb_device_tables() model.y_size = 300.0; model.y_offset_calib_white = 9.0; + model.y_size_calib_mm = 4.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.x_size_calib_mm = 228.6; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -2943,16 +2902,36 @@ void genesys_init_usb_device_tables() 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.flags = ModelFlag::CUSTOM_GAMMA | + ModelFlag::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); - } +} + +void verify_usb_device_tables() +{ + for (const auto& device : *s_usb_devices) { + const auto& model = device.model(); + + if (model.x_size_calib_mm == 0.0f) { + throw SaneException("Calibration width can't be zero"); + } + + if (model.has_method(ScanMethod::FLATBED)) { + if (model.y_size_calib_mm == 0.0f) { + throw SaneException("Calibration size can't be zero"); + } + } + if (model.has_method(ScanMethod::TRANSPARENCY) || + model.has_method(ScanMethod::TRANSPARENCY_INFRARED)) + { + if (model.y_size_calib_ta_mm == 0.0f) { + throw SaneException("Calibration size can't be zero"); + } + } + } +} } // namespace genesys diff --git a/backend/genesys/tables_motor.cpp b/backend/genesys/tables_motor.cpp index 2484d2d..a452fe5 100644 --- a/backend/genesys/tables_motor.cpp +++ b/backend/genesys/tables_motor.cpp @@ -53,272 +53,586 @@ void genesys_init_motor_tables() { s_motors.init(); + MotorProfile profile; + 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)); + motor.base_ydpi = 2400; + motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); + motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0}); 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)); + motor.base_ydpi = 2400; + motor.profiles.push_back({MotorSlope::create_from_steps(2000, 1375, 128), StepType::FULL, 0}); + motor.profiles.push_back({MotorSlope::create_from_steps(2000, 1375, 128), StepType::HALF, 0}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(2289, 2100, 128), StepType::FULL, 0}); + motor.profiles.push_back({MotorSlope::create_from_steps(2289, 2100, 128), StepType::HALF, 0}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); + motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); + motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0}); 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)); + motor.base_ydpi = 1200; + motor.profiles.push_back({MotorSlope::create_from_steps(3200, 1200, 128), StepType::FULL, 0}); + motor.profiles.push_back({MotorSlope::create_from_steps(3200, 1200, 128), StepType::HALF, 0}); 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)); + + profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 150), StepType::HALF, 0}; + profile.resolutions = { 75, 150, 200, 300, 600 }; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 150), StepType::QUARTER, 0}; + profile.resolutions = { 1200, 2400 }; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::FULL, 0}; + profile.resolutions = { 75, 150, 200, 300 }; + motor.fast_profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(6000, 3000, 100), StepType::FULL, 0}; + profile.resolutions = { 600, 1200, 2400 }; + motor.fast_profiles.push_back(profile); + + s_motors->push_back(std::move(motor)); + + + motor = Genesys_Motor(); + motor.id = MotorId::CANON_LIDE_60; + motor.base_ydpi = 1200; + + profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::HALF, 0}; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::FULL, 0}; + profile.resolutions = { 75, 150, 300 }; + motor.fast_profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(6000, 3000, 100), StepType::FULL, 0}; + profile.resolutions = { 600, 1200, 2400 }; + motor.fast_profiles.push_back(profile); + + s_motors->push_back(std::move(motor)); + + + motor = Genesys_Motor(); + motor.id = MotorId::CANON_LIDE_90; + motor.base_ydpi = 1200; + profile = {MotorSlope::create_from_steps(8000, 3000, 200), StepType::FULL, 0}; + profile.resolutions = { 150, 300 }; + motor.profiles.push_back(profile); + + profile = {MotorSlope::create_from_steps(7000, 3000, 200), StepType::HALF, 0}; + profile.resolutions = { 600, 1200 }; + motor.profiles.push_back(profile); + + profile = {MotorSlope::create_from_steps(7000, 3000, 200), StepType::QUARTER, 0}; + profile.resolutions = { 2400 }; + motor.profiles.push_back(profile); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}); + motor.profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::HALF, 0}); + motor.fast_profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}); 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)); + profile = MotorProfile{MotorSlope::create_from_steps(3700, 3700, 2), StepType::FULL, 0}; + profile.resolutions = {}; // used during fast moves + motor.profiles.push_back(profile); + + // FIXME: this motor profile is useless + profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; + profile.resolutions = {75, 150, 300, 600}; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(3700, 3700, 2), StepType::FULL, 0}; + motor.fast_profiles.push_back(profile); 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)); + + profile = MotorProfile{MotorSlope::create_from_steps(3000, 2500, 10), StepType::FULL, 0}; + profile.resolutions = {75, 150}; + motor.profiles.push_back(profile); + + // FIXME: this motor profile is useless + profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; + profile.resolutions = {300, 600, 1200}; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(3000, 2500, 10), StepType::FULL, 0}; + motor.fast_profiles.push_back(profile); 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)); + + profile = MotorProfile{MotorSlope::create_from_steps(3000, 2600, 10), StepType::FULL, 0}; + profile.resolutions = {75, 150}; + motor.profiles.push_back(profile); + + // FIXME: this motor profile is useless + profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; + profile.resolutions = {300, 600, 1200}; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(3000, 2600, 10), StepType::FULL, 0}; + motor.fast_profiles.push_back(profile); 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)); + + profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::FULL, 0}; + profile.resolutions = {75, 150}; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::HALF, 0}; + profile.resolutions = {300, 600, 1200}; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::FULL, 0}; + motor.fast_profiles.push_back(profile); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 255), + StepType::HALF, 1432}); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 279), + StepType::QUARTER, 2712}); + motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), + StepType::EIGHTH, 5280}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 255), + StepType::HALF, 1432}); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 279), + StepType::QUARTER, 2712}); + motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), + StepType::EIGHTH, 5280}); + motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), + StepType::EIGHTH, 10416}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), + StepType::HALF, 1424}); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), + StepType::HALF, 1504}); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 2022, 127), + StepType::HALF, 2696}); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), + StepType::HALF, 2848}); + motor.profiles.push_back({MotorSlope::create_from_steps(46876, 15864, 2), + StepType::EIGHTH, 10576}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(44444, 500, 489), + StepType::HALF, 8000}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(7842, 320, 602), + StepType::HALF, 8016}); + motor.profiles.push_back({MotorSlope::create_from_steps(9422, 254, 1004), + StepType::HALF, 15624}); + motor.profiles.push_back({MotorSlope::create_from_steps(28032, 2238, 604), + StepType::HALF, 56064}); + motor.profiles.push_back({MotorSlope::create_from_steps(42752, 1706, 610), + StepType::QUARTER, 42752}); 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)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(28597 * 2, 727 * 2, 200); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 1; + profile.resolutions = { 300, 600 }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(28597 * 2, 727 * 2, 200); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + profile.resolutions = { 1200, 2400, 4800, 9600 }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(28597 * 2, 279 * 2, 1000); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + motor.fast_profiles.push_back(std::move(profile)); + + s_motors->push_back(std::move(motor)); + + + motor = Genesys_Motor(); + motor.id = MotorId::CANON_5600F; + motor.base_ydpi = 2400; + + // FIXME: real limit is 134, but for some reason the motor can't acquire that speed. + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(2500 * 2, 134 * 2, 1000); + profile.step_type = StepType::HALF; + profile.motor_vref = 0; + profile.resolutions = { 75, 150 }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(2500 * 2, 200 * 2, 1000); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + profile.resolutions = { 300, 600, 1200, 2400, 4800 }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(2500 * 2, 200 * 2, 1000); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + motor.fast_profiles.push_back(std::move(profile)); + 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)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(20202 * 4, 333 * 4, 100); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + profile.resolutions = VALUE_FILTER_ANY; + profile.scan_methods = { ScanMethod::FLATBED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 100); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 2; + profile.resolutions = VALUE_FILTER_ANY; + profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 200); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 2; + profile.resolutions = VALUE_FILTER_ANY; + profile.scan_methods = VALUE_FILTER_ANY; + motor.fast_profiles.push_back(std::move(profile)); + 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)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 3; + profile.resolutions = { 300, 600 }; + profile.scan_methods = { ScanMethod::FLATBED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 2; + profile.resolutions = { 1200, 2400 }; + profile.scan_methods = { ScanMethod::FLATBED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 2; + profile.resolutions = { 4800 }; + profile.scan_methods = { ScanMethod::FLATBED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 2; + profile.resolutions = { 300, 600 }; + profile.scan_methods = { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 1; + profile.resolutions = { 1200, 2400 }; + profile.scan_methods = { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + profile.resolutions = { 4800 }; + profile.scan_methods = { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(59240, 582, 1020); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 2; + motor.fast_profiles.push_back(std::move(profile)); + 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 255), + StepType::FULL, 2768}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 469), + StepType::HALF, 5360}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2632, 3), + StepType::HALF, 10528}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 3), + StepType::QUARTER, 20864}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 864, 127), + StepType::FULL, 4608}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2010, 63), + StepType::HALF, 5360}); + motor.profiles.push_back({MotorSlope::create_from_steps(62464, 2632, 3), + StepType::QUARTER, 10528}); + motor.profiles.push_back({MotorSlope::create_from_steps(62592, 10432, 5), + StepType::QUARTER, 20864}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 255), + StepType::FULL, 2768}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 469), + StepType::HALF, 5360}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2632, 3), + StepType::HALF, 10528}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 4), + StepType::QUARTER, 20864}); + motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 4), + StepType::EIGHTH, 41536}); 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)); + + profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}; + profile.resolutions = {75, 100, 150, 200}; + motor.profiles.push_back(profile); + + // FIXME: this motor profile is almost useless + profile = MotorProfile{MotorSlope::create_from_steps(3500, 3250, 60), StepType::HALF, 0}; + profile.resolutions = {300, 400, 600, 1200}; + motor.profiles.push_back(profile); + + profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}; + motor.fast_profiles.push_back(profile); + s_motors->push_back(std::move(motor)); + + + motor = Genesys_Motor(); + motor.id = MotorId::PLUSTEK_OPTICFILM_7200; + motor.base_ydpi = 3600; + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(20000 * 2, 600 * 2, 200); + profile.step_type = StepType::HALF; + profile.motor_vref = 0; + motor.profiles.push_back(std::move(profile)); + s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7200I; motor.base_ydpi = 3600; - motor.optical_ydpi = 3600; + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(34722 * 2, 454 * 2, 40); + profile.step_type = StepType::HALF; + profile.motor_vref = 3; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(34722 * 2, 454 * 2, 40); + profile.step_type = StepType::HALF; + profile.motor_vref = 0; + motor.fast_profiles.push_back(std::move(profile)); + s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7300; motor.base_ydpi = 3600; - motor.optical_ydpi = 3600; + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 3; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + motor.fast_profiles.push_back(std::move(profile)); + + s_motors->push_back(std::move(motor)); + + + motor = Genesys_Motor(); + motor.id = MotorId::PLUSTEK_OPTICFILM_7400; + motor.base_ydpi = 3600; + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(64102 * 4, 400 * 4, 30); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 3; + motor.profiles.push_back(profile); + motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7500I; motor.base_ydpi = 3600; - motor.optical_ydpi = 3600; + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 3; + motor.profiles.push_back(std::move(profile)); + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 0; + motor.fast_profiles.push_back(std::move(profile)); + + s_motors->push_back(std::move(motor)); + + + motor = Genesys_Motor(); + motor.id = MotorId::PLUSTEK_OPTICFILM_8200I; + motor.base_ydpi = 3600; + + profile = MotorProfile(); + profile.slope = MotorSlope::create_from_steps(64102 * 4, 400 * 4, 100); + profile.step_type = StepType::QUARTER; + profile.motor_vref = 3; + motor.profiles.push_back(profile); + motor.fast_profiles.push_back(profile); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(22000, 1000, 1017), + StepType::HALF, 11000}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(22000, 1000, 1017), + StepType::HALF, 11000}); 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)); + motor.profiles.push_back({MotorSlope::create_from_steps(9560, 1912, 31), StepType::FULL, 0}); s_motors->push_back(std::move(motor)); } diff --git a/backend/genesys/tables_motor_profile.cpp b/backend/genesys/tables_motor_profile.cpp deleted file mode 100644 index 18f7271..0000000 --- a/backend/genesys/tables_motor_profile.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/* 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 index bbbe441..b90355c 100644 --- a/backend/genesys/tables_sensor.cpp +++ b/backend/genesys/tables_sensor.cpp @@ -44,71 +44,10 @@ #define DEBUG_DECLARE_ONLY #include "low.h" +#include <map> 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() @@ -118,444 +57,231 @@ void genesys_init_sensor_tables() Genesys_Sensor sensor; sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_UMAX; - sensor.optical_res = 1200; + sensor.sensor_id = SensorId::CCD_UMAX; // gl646 + sensor.full_resolution = 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 }, + { 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 150, 4 }, + { { 150 }, 300, 8 }, + { { 300 }, 600, 16 }, + { { 600 }, 1200, 32 }, + { { 1200 }, 2400, 64 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_ST12; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CCD_ST12; // gl646 + sensor.full_resolution = 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 }, + { 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 10 }, + { { 150 }, 21 }, + { { 300 }, 42 }, + { { 600 }, 85 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.resolutions.values()[0]; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_ST24; - sensor.optical_res = 1200; + sensor.sensor_id = SensorId::CCD_ST24; // gl646 + sensor.full_resolution = 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 }, + { 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 150, 4 }, + { { 150 }, 300, 8 }, + { { 300 }, 600, 16 }, + { { 600 }, 1200, 32 }, + { { 1200 }, 2400, 64 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_5345; - sensor.optical_res = 1200; - sensor.ccd_size_divisor = 2; + sensor.sensor_id = SensorId::CCD_5345; // gl646 + sensor.full_resolution = 1200; 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpiset; unsigned exposure_lperiod; - unsigned ccd_size_divisor; + Ratio pixel_count_ratio; + int output_pixel_offset; + StaggerConfig stagger_y; // FIXME: may be incorrect 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 } + { { 50 }, 600, 100, 12000, Ratio{1, 2}, 0, StaggerConfig{}, { + { 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 }, 600, 150, 11000, Ratio{1, 2}, 1, StaggerConfig{}, { + { 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 }, 600, 200, 11000, Ratio{1, 2}, 1, StaggerConfig{}, { + { 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 }, 600, 300, 11000, Ratio{1, 2}, 2, StaggerConfig{}, { + { 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 }, 600, 400, 11000, Ratio{1, 2}, 2, StaggerConfig{}, { + { 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 }, 600, 600, 11000, Ratio{1, 2}, 4, StaggerConfig{}, { + { 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 }, 600, 800, 11000, Ratio{1, 2}, 5, StaggerConfig{}, { + { 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 }, 600, 1200, 11000, Ratio{1, 2}, 8, StaggerConfig{}, { + { 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 }, 1200, 1200, 11000, Ratio{1, 1}, 16, StaggerConfig{4, 0}, { + { 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 } } }, }; @@ -563,8 +289,12 @@ void genesys_init_sensor_tables() for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; - sensor.ccd_size_divisor = setting.ccd_size_divisor; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } @@ -572,223 +302,79 @@ void genesys_init_sensor_tables() sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_HP2400; - sensor.optical_res = 1200; + sensor.sensor_id = SensorId::CCD_HP2400; // gl646 + sensor.full_resolution = 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; unsigned exposure_lperiod; + Ratio pixel_count_ratio; + int output_pixel_offset; + StaggerConfig stagger_y; 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 } + { { 50 }, 200, 7211, Ratio{1, 4}, 0, StaggerConfig{}, { + { 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 }, 400, 7211, Ratio{1, 4}, 1, StaggerConfig{}, { + { 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 }, 600, 7211, Ratio{1, 4}, 1, StaggerConfig{}, { + { 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 }, 1200, 8751, Ratio{1, 4}, 3, StaggerConfig{}, { + { 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 }, 1200, 18760, Ratio{1, 2}, 7, StaggerConfig{}, { + { 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 }, 1200, 21749, Ratio{1, 1}, 15, StaggerConfig{4, 0}, { + { 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 } } }, }; @@ -796,7 +382,11 @@ void genesys_init_sensor_tables() for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } @@ -804,168 +394,61 @@ void genesys_init_sensor_tables() sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_HP2300; - sensor.optical_res = 600; - sensor.ccd_size_divisor = 2; + sensor.sensor_id = SensorId::CCD_HP2300; // gl646 + sensor.full_resolution = 600; 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpiset; unsigned exposure_lperiod; - unsigned ccd_size_divisor; + Ratio pixel_count_ratio; + int output_pixel_offset; 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 } + { { 75 }, 300, 150, 4480, Ratio{1, 2}, 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 }, 300, 300, 4350, Ratio{1, 2}, 5, { + { 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 }, 300, 600, 4350, Ratio{1, 2}, 10, { + { 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 }, 600, 600, 8700, Ratio{1, 1}, 20, { + { 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 } } }, }; @@ -973,8 +456,11 @@ void genesys_init_sensor_tables() for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; - sensor.ccd_size_divisor = setting.ccd_size_divisor; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } @@ -982,399 +468,300 @@ void genesys_init_sensor_tables() sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_CANON_LIDE_35; - sensor.optical_res = 1200; - sensor.ccd_size_divisor = 2; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_35; // gl841 + sensor.full_resolution = 1200; + sensor.register_dpihw = 1200; 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 }, + { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x00 }, { 0x19, 0x50 }, + { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, + { 0x52, 0x05 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x3a }, { 0x59, 0x03 }, { 0x5a, 0x40 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpiset; + unsigned shading_resolution; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 600, 150, 600, 11 }, + { { 100 }, 600, 200, 600, 14 }, + { { 150 }, 600, 300, 600, 22 }, + { { 200 }, 600, 400, 600, 29 }, + { { 300 }, 600, 600, 600, 44 }, + { { 600 }, 600, 1200, 600, 88 }, + { { 1200 }, 1200, 1200, 1200, 88 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_XP200; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_60; // gl841 + sensor.full_resolution = 1200; + sensor.register_dpihw = 1200; + sensor.black_pixels = 87; + sensor.dummy_pixel = 87; + sensor.fau_gain_white_ref = 0; + sensor.gain_white_ref = 0; + sensor.exposure = { 0x0400, 0x0400, 0x0400 }; + sensor.custom_regs = { + { 0x16, 0x00 }, { 0x17, 0x01 }, { 0x18, 0x00 }, { 0x19, 0x50 }, + { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, + { 0x52, 0x05 }, { 0x53, 0x07 }, { 0x54, 0x03 }, { 0x55, 0x05 }, + { 0x56, 0x02 }, { 0x57, 0x05 }, { 0x58, 0x3a }, { 0x59, 0x03 }, { 0x5a, 0x40 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, + }; + sensor.gamma = { 1.0f, 1.0f, 1.0f }; + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpiset; + unsigned shading_resolution; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 600, 150, 600, 11 }, + { { 100 }, 600, 200, 600, 14 }, + { { 150 }, 600, 300, 600, 22 }, + { { 200 }, 600, 400, 600, 29 }, + { { 300 }, 600, 600, 600, 44 }, + { { 600 }, 600, 1200, 600, 88 }, + { { 1200 }, 1200, 1200, 1200, 88 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } + + + sensor = Genesys_Sensor(); + sensor.sensor_id = SensorId::CIS_CANON_LIDE_90; // gl842 + sensor.full_resolution = 2400; + sensor.black_pixels = 20; + sensor.dummy_pixel = 253; + sensor.fau_gain_white_ref = 150; + sensor.gain_white_ref = 150; + sensor.use_host_side_calib = true; + sensor.custom_regs = { + { 0x16, 0x20 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff }, + { 0x1a, 0x24 }, { 0x1c, 0x00 }, { 0x1d, 0x04 }, + { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x02 }, { 0x55, 0x04 }, + { 0x56, 0x02 }, { 0x57, 0x04 }, { 0x58, 0x0a }, { 0x59, 0x71 }, { 0x5a, 0x55 }, + { 0x70, 0x00 }, { 0x71, 0x05 }, { 0x72, 0x07 }, { 0x73, 0x09 }, + { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x3f }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x1e }, { 0x7d, 0x11 }, { 0x7f, 0x50 } + }; + sensor.gamma = { 1.0f, 1.0f, 1.0f }; + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpihw; + unsigned register_dpiset; + unsigned shading_resolution; + unsigned shading_factor; + int output_pixel_offset; + SensorExposure exposure; + unsigned exposure_lperiod; + unsigned segment_size; + std::vector<unsigned> segment_order; + }; + + CustomSensorSettings custom_settings[] = { + { { 300 }, 300, 600, 600, 300, 2, 280, { 955, 1235, 675 }, 6500, 5152, + std::vector<unsigned>{} }, + { { 600 }, 600, 600, 600, 600, 1, 250, { 1655, 2075, 1095 }, 6536, 5152, + std::vector<unsigned>{} }, + { { 1200 }, 1200, 1200, 1200, 1200, 1, 500, { 3055, 4175, 1935 }, 12688, 5152, + {0, 1} }, + { { 2400 }, 2400, 2400, 2400, 2400, 1, 1000, { 5855, 7535, 3615 }, 21500, 5152, + {0, 1, 2, 3} }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; + sensor.shading_factor = setting.shading_factor; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.exposure = setting.exposure; + sensor.exposure_lperiod = setting.exposure_lperiod; + sensor.segment_size = setting.segment_size; + sensor.segment_order = setting.segment_order; + s_sensors->push_back(sensor); + } + } + + + sensor = Genesys_Sensor(); + sensor.sensor_id = SensorId::CIS_XP200; // gl646 + sensor.full_resolution = 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 } + { 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; + ValueFilterAny<unsigned> resolutions; std::vector<unsigned> channels; unsigned exposure_lperiod; SensorExposure exposure; + int output_pixel_offset; }; 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 } }, + { { 75 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e }, 4 }, + { { 100 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e }, 6 }, + { { 200 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e }, 12 }, + { { 300 }, { 3 }, 9000, { 0x1644, 0x0c80, 0x092e }, 19 }, + { { 600 }, { 3 }, 16000, { 0x1644, 0x0c80, 0x092e }, 38 }, + { { 75 }, { 1 }, 16000, { 0x050a, 0x0fa0, 0x1010 }, 4 }, + { { 100 }, { 1 }, 7800, { 0x050a, 0x0fa0, 0x1010 }, 6 }, + { { 200 }, { 1 }, 11000, { 0x050a, 0x0fa0, 0x1010 }, 12 }, + { { 300 }, { 1 }, 13000, { 0x050a, 0x0fa0, 0x1010 }, 19 }, + { { 600 }, { 1 }, 24000, { 0x050a, 0x0fa0, 0x1010 }, 38 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.channels = setting.channels; + sensor.register_dpiset = setting.resolutions.values()[0]; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_HP3670; - sensor.optical_res = 1200; + sensor.sensor_id = SensorId::CCD_HP3670; // gl646 + sensor.full_resolution = 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; unsigned exposure_lperiod; + Ratio pixel_count_ratio; + int output_pixel_offset; + StaggerConfig stagger_y; 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 } + { { 50 }, 200, 5758, Ratio{1, 4}, 0, StaggerConfig{}, { + { 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 }, 300, 4879, Ratio{1, 4}, 1, StaggerConfig{}, { + { 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 }, 400, 4487, Ratio{1, 4}, 1, StaggerConfig{}, { + { 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 }, 600, 4879, Ratio{1, 4}, 2, StaggerConfig{}, { + { 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 }, 1200, 4503, Ratio{1, 4}, 4, StaggerConfig{}, { + { 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 }, 1200, 10251, Ratio{1, 2}, 8, StaggerConfig{}, { + { 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 }, 1200, 12750, Ratio{1, 1}, 16, StaggerConfig{4, 0}, { + { 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 } } }, }; @@ -1382,7 +769,11 @@ void genesys_init_sensor_tables() for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } @@ -1390,251 +781,278 @@ void genesys_init_sensor_tables() sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_DP665; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CCD_DP665; // gl841 + sensor.full_resolution = 600; + sensor.register_dpihw = 600; + sensor.shading_resolution = 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 }, + { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, + { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, + { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 75, 1 }, + { { 150 }, 150, 3 }, + { { 300 }, 300, 7 }, + { { 600 }, 600, 14 }, + { { 1200 }, 1200, 28 }, + }; + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_ROADWARRIOR; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CCD_ROADWARRIOR; // gl841 + sensor.full_resolution = 600; + sensor.register_dpihw = 600; + sensor.shading_resolution = 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 }, + { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, + { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, + { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 75, 1 }, + { { 150 }, 150, 3 }, + { { 300 }, 300, 7 }, + { { 600 }, 600, 14 }, + { { 1200 }, 1200, 28 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_DSMOBILE600; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CCD_DSMOBILE600; // gl841 + sensor.full_resolution = 600; + sensor.register_dpihw = 600; + sensor.shading_resolution = 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 }, + { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, + { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, + { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, + }; + sensor.gamma = { 1.0f, 1.0f, 1.0f }; + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 75, 3 }, + { { 150 }, 150, 7 }, + { { 300 }, 300, 14 }, + { { 600 }, 600, 29 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } + + + sensor = Genesys_Sensor(); + sensor.sensor_id = SensorId::CCD_XP300; // gl841 + sensor.full_resolution = 600; + sensor.register_dpihw = 1200; // FIXME: could be incorrect, but previous code used this value + sensor.shading_resolution = 600; + sensor.black_pixels = 27; + sensor.dummy_pixel = 27; + sensor.fau_gain_white_ref = 210; + sensor.gain_white_ref = 200; + sensor.exposure = { 0x1100, 0x1100, 0x1100 }; + sensor.custom_regs = { + { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, + { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, + { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 150, 3 }, + { { 150 }, 300, 7 }, + { { 300 }, 600, 14 }, + { { 600 }, 1200, 28 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_XP300; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CCD_DOCKETPORT_487; // gl841 + sensor.full_resolution = 600; + sensor.register_dpihw = 600; + sensor.shading_resolution = 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 }, + { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, + { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, + { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 150, 3 }, + { { 150 }, 300, 7 }, + { { 300 }, 600, 14 }, + { { 600 }, 600, 28 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_DP685; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CCD_DP685; // gl841 + sensor.full_resolution = 600; + sensor.register_dpihw = 600; + sensor.shading_resolution = 600; + sensor.full_resolution = 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 }, + { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, + { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, + { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 75, 3 }, + { { 150 }, 150, 6 }, + { { 300 }, 300, 13 }, + { { 600 }, 600, 27 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_CANON_LIDE_200; - sensor.optical_res = 4800; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_200; // gl847 + sensor.full_resolution = 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; int exposure_lperiod; SensorExposure exposure; + Ratio pixel_count_ratio; + unsigned shading_factor; + int output_pixel_offset; unsigned segment_size; std::vector<unsigned> segment_order; GenesysRegisterSettingSet custom_regs; @@ -1642,7 +1060,44 @@ void genesys_init_sensor_tables() 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>{}, { + { { 75 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 8, 40, 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 1424 lperiod and enables dummy line (0x17) + { { 100 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 6, 53, 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 1424 lperiod and enables dummy line (0x17) + { { 150 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 4, 80, 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 1424 lperiod and enables dummy line (0x17) + { { 200 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 3, 106, 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 }, @@ -1653,7 +1108,8 @@ void genesys_init_sensor_tables() } }, // Note: Windows driver uses 788 lperiod and enables dummy line (0x17) - { { 300, 400 }, 1424, { 304, 203, 180 }, 5136, std::vector<unsigned>{}, { + { { 300 }, 600, 1424, { 304, 203, 180 }, Ratio{1, 8}, 2, 160, 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 }, @@ -1663,7 +1119,9 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 600 }, 1432, { 492, 326, 296 }, 5136, std::vector<unsigned>{}, { + // Note: Windows driver uses 788 lperiod and enables dummy line (0x17) + { { 400 }, 600, 1424, { 304, 203, 180 }, Ratio{1, 8}, 1, 213, 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 }, @@ -1673,7 +1131,19 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 1200 }, 2712, { 935, 592, 538 }, 5136, { 0, 1 }, { + { { 600 }, 600, 1432, { 492, 326, 296 }, Ratio{1, 8}, 1, 320, 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 }, 1200, 2712, { 935, 592, 538 }, Ratio{1, 8}, 1, 640, 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 }, @@ -1683,7 +1153,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 2400 }, 5280, { 1777, 1125, 979 }, 5136, { 0, 2, 1, 3 }, { + { { 2400 }, 2400, 5280, { 1777, 1125, 979 }, Ratio{1, 8}, 1, 1280, 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 }, @@ -1693,7 +1164,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 4800 }, 10416, { 3377, 2138, 1780 }, 5136, { 0, 2, 4, 6, 1, 3, 5, 7 }, { + { { 4800 }, 4800, 10416, { 3377, 2138, 1780 }, Ratio{1, 8}, 1, 2560, 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 }, @@ -1707,8 +1179,14 @@ void genesys_init_sensor_tables() for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.resolutions.values()[0]; + sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; + sensor.output_pixel_offset = setting.output_pixel_offset; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; @@ -1718,34 +1196,64 @@ void genesys_init_sensor_tables() sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_CANON_LIDE_700F; - sensor.optical_res = 4800; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_700F; // gl847 + sensor.full_resolution = 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; int exposure_lperiod; SensorExposure exposure; + Ratio pixel_count_ratio; + unsigned shading_factor; + int output_pixel_offset; 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>{}, { + { { 75 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 8, 48, 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 }, + } + }, + { { 100 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 6, 64, 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 }, + } + }, + { { 150 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 4, 96, 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 }, + } + }, + { { 200 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 3, 128, 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 }, @@ -1755,7 +1263,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 300 }, 1424, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, { + { { 300 }, 600, 1424, { 465, 310, 239 }, Ratio{1, 8}, 2, 192, 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 }, @@ -1765,7 +1274,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 600 }, 1504, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, { + { { 600 }, 600, 1504, { 465, 310, 239 }, Ratio{1, 8}, 1, 384, 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 }, @@ -1775,7 +1285,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 1200 }, 2696, { 1464, 844, 555 }, 5187, { 0, 1 }, { + { { 1200 }, 1200, 2696, { 1464, 844, 555 }, Ratio{1, 8}, 1, 768, 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 }, @@ -1785,7 +1296,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 2400 }, 10576, { 2798, 1558, 972 }, 5187, { 0, 1, 2, 3 }, { + { { 2400 }, 2400, 10576, { 2798, 1558, 972 }, Ratio{1, 8}, 1, 1536, 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 }, @@ -1795,7 +1307,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 4800 }, 10576, { 2798, 1558, 972 }, 5187, { 0, 1, 4, 5, 2, 3, 6, 7 }, { + { { 4800 }, 4800, 10576, { 2798, 1558, 972 }, Ratio{1, 8}, 1, 3072, 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 }, @@ -1809,8 +1322,14 @@ void genesys_init_sensor_tables() for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.resolutions.values()[0]; + sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; + sensor.output_pixel_offset = setting.output_pixel_offset; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; @@ -1820,33 +1339,32 @@ void genesys_init_sensor_tables() sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_CANON_LIDE_100; - sensor.optical_res = 2400; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_100; // gl847 + sensor.full_resolution = 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; int exposure_lperiod; SensorExposure exposure; + Ratio pixel_count_ratio; + unsigned shading_factor; + int output_pixel_offset; 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>{}, { + { { 75 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 8, 40, 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 }, @@ -1856,7 +1374,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 300 }, 1728, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, { + { { 100 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 6, 53, 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 }, @@ -1866,7 +1385,41 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 600 }, 1432, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, { + { { 150 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 4, 80, 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 }, + } + }, + { { 200 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 3, 106, 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 }, 600, 1728, { 423, 294, 242 }, Ratio{1, 4}, 2, 160, 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 }, 600, 1432, { 423, 294, 242 }, Ratio{1, 4}, 1, 320, 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 }, @@ -1876,7 +1429,7 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, }, }, - { { 1200 }, 2712, { 791, 542, 403 }, 5136, {0, 1}, { + { { 1200 }, 1200, 2712, { 791, 542, 403 }, Ratio{1, 4}, 1, 640, 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 }, @@ -1886,7 +1439,7 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 2400 }, 5280, { 1504, 1030, 766 }, 5136, {0, 2, 1, 3}, { + { { 2400 }, 2400, 5280, { 1504, 1030, 766 }, Ratio{1, 4}, 1, 1280, 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 }, @@ -1900,8 +1453,14 @@ void genesys_init_sensor_tables() for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.resolutions.values()[0]; + sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; + sensor.output_pixel_offset = setting.output_pixel_offset; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; @@ -1910,12 +1469,12 @@ void genesys_init_sensor_tables() } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_KVSS080; - sensor.optical_res = 600; + sensor.sensor_id = SensorId::CCD_KVSS080; // gl843 + sensor.full_resolution = 600; + sensor.register_dpihw = 600; + sensor.shading_resolution = 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 }; @@ -1946,191 +1505,170 @@ void genesys_init_sensor_tables() { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 }, + { 0x7d, 0x90 }, }; 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + Ratio pixel_count_ratio; + int output_pixel_offset; + }; + CustomSensorSettings custom_settings[] = { + { { 75 }, 75, Ratio{1, 1}, 4 }, + { { 100 }, 100, Ratio{1, 1}, 6 }, + { { 150 }, 150, Ratio{1, 1}, 9 }, + { { 200 }, 200, Ratio{1, 1}, 12 }, + { { 300 }, 300, Ratio{1, 1}, 19 }, + { { 600 }, 600, Ratio{1, 1}, 38 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.register_dpiset; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_G4050; - sensor.optical_res = 4800; + sensor.sensor_id = SensorId::CCD_G4050; // gl843 + sensor.full_resolution = 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; + unsigned register_dpiset; int exposure_lperiod; ScanMethod method; + Ratio pixel_count_ratio; + int output_pixel_offset; + StaggerConfig stagger_y; // FIXME: may be incorrect GenesysRegisterSettingSet extra_custom_regs; }; + GenesysRegisterSettingSet regs_100_to_600 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0x00 }, + { 0xaa, 0x00 }, + }; + + GenesysRegisterSettingSet regs_1200 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0xc0 }, + { 0xaa, 0x05 }, + }; + + GenesysRegisterSettingSet regs_2400 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0xc0 }, + { 0xaa, 0x05 }, + }; + + GenesysRegisterSettingSet regs_4800 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0xc0 }, + { 0xaa, 0x07 }, + }; + + GenesysRegisterSettingSet regs_ta_any = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0x00 }, + { 0xaa, 0x00 }, + }; + 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 }, - } - } + { { 100 }, 600, 100, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, + StaggerConfig{}, regs_100_to_600 }, + { { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, + StaggerConfig{}, regs_100_to_600 }, + { { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, + StaggerConfig{}, regs_100_to_600 }, + { { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, + StaggerConfig{}, regs_100_to_600 }, + { { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, + StaggerConfig{}, regs_100_to_600 }, + { { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, + StaggerConfig{}, regs_100_to_600 }, + { { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, + StaggerConfig{}, regs_1200 }, + { { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, + StaggerConfig{4, 0}, regs_2400 }, + { { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, + StaggerConfig{8, 0}, regs_4800 }, + { { 100, 150, 200, 300, 400, 600, 1200 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, + Ratio{1, 1}, 58, StaggerConfig{}, regs_ta_any }, // FIXME: may be incorrect + { { 2400 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, + Ratio{1, 1}, 58, StaggerConfig{4, 0}, regs_ta_any }, // FIXME: may be incorrect + { { 4800 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, + Ratio{1, 1}, 58, StaggerConfig{8, 0}, regs_ta_any }, // FIXME: may be incorrect }; auto base_custom_regs = sensor.custom_regs; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.method = setting.method; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.stagger_y = setting.stagger_y; sensor.custom_regs = base_custom_regs; sensor.custom_regs.merge(setting.extra_custom_regs); s_sensors->push_back(sensor); @@ -2138,110 +1676,136 @@ void genesys_init_sensor_tables() } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_HP_4850C; - sensor.optical_res = 4800; + sensor.sensor_id = SensorId::CCD_HP_4850C; // gl843 + sensor.full_resolution = 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; + unsigned register_dpiset; int exposure_lperiod; ScanMethod method; + Ratio pixel_count_ratio; + int output_pixel_offset; + int shading_pixel_offset; + StaggerConfig stagger_y; // FIXME: review, may be incorrect GenesysRegisterSettingSet extra_custom_regs; }; + GenesysRegisterSettingSet regs_100_to_600 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0x00 }, + { 0xaa, 0x00 }, + }; + GenesysRegisterSettingSet regs_1200 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0xc0 }, + { 0xaa, 0x05 }, + }; + GenesysRegisterSettingSet regs_2400 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0xc0 }, + { 0xaa, 0x05 }, + }; + GenesysRegisterSettingSet regs_4800 = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0xc0 }, + { 0xaa, 0x07 }, + }; + GenesysRegisterSettingSet regs_ta_any = { + { 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 }, { 0x7d, 0x90 }, + { 0x9e, 0x00 }, + { 0xaa, 0x00 }, + }; + 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 }, - } - } + { { 100 }, 600, 100, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, 50, StaggerConfig{}, + regs_100_to_600 }, + { { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, 50, StaggerConfig{}, + regs_100_to_600 }, + { { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, 50, StaggerConfig{}, + regs_100_to_600 }, + { { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, 50, StaggerConfig{}, + regs_100_to_600 }, + { { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, 50, StaggerConfig{}, + regs_100_to_600 }, + { { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, 50, StaggerConfig{}, + regs_100_to_600 }, + { { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, 0, + StaggerConfig{}, regs_1200 }, + { { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, 0, + StaggerConfig{0, 4}, regs_2400 }, + { { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, 0, + StaggerConfig{0, 8}, regs_4800 }, + { { 100, 150, 200, 300, 400, 600, 1200}, 600, 600, 15624, ScanMethod::TRANSPARENCY, + Ratio{1, 1}, 58, 0, StaggerConfig{}, regs_ta_any }, // FIXME: review + { { 2400 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, + Ratio{1, 1}, 58, 0, StaggerConfig{0, 4}, regs_ta_any }, // FIXME: review + { { 4800 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, + Ratio{1, 1}, 58, 0, StaggerConfig{0, 8}, regs_ta_any }, // FIXME: review }; auto base_custom_regs = sensor.custom_regs; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.method = setting.method; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.shading_pixel_offset = setting.shading_pixel_offset; + sensor.stagger_y = setting.stagger_y; sensor.custom_regs = base_custom_regs; sensor.custom_regs.merge(setting.extra_custom_regs); s_sensors->push_back(sensor); @@ -2249,142 +1813,250 @@ void genesys_init_sensor_tables() } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_CANON_4400F; - sensor.optical_res = 4800; - sensor.ccd_size_divisor = 4; + sensor.sensor_id = SensorId::CCD_CANON_4400F; // gl843 + sensor.full_resolution = 4800; + sensor.register_dpihw = 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 = 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpiset; int exposure_lperiod; + bool use_host_side_calib; + int output_pixel_offset; std::vector<ScanMethod> methods; + StaggerConfig stagger_y; GenesysRegisterSettingSet extra_custom_regs; + GenesysRegisterSettingSet extra_custom_fe_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 }, + { { 300 }, 1200, 1200, 11640, false, 197, { ScanMethod::FLATBED }, StaggerConfig{}, { + { 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 }, - } + }, {} + }, + { { 600 }, 1200, 2400, 11640, false, 392, { ScanMethod::FLATBED }, StaggerConfig{}, { + { 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 }, + }, {} + }, + { { 1200 }, 1200, 4800, 11640, false, 794, { ScanMethod::FLATBED }, StaggerConfig{}, { + { 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 }, + { { 1200 }, 1200, 4800, 33300, true, 5, { ScanMethod::TRANSPARENCY }, + StaggerConfig{}, { + { 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 }, + { { 2400 }, 2400, 4800, 33300, true, 10, { ScanMethod::TRANSPARENCY }, + StaggerConfig{}, { + { 0x16, 0x13 }, { 0x17, 0x15 }, { 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 }, + }, { + { 0x03, 0x1f }, } }, - { { 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 }, + { { 4800 }, 4800, 4800, 33300, true, -2063, { ScanMethod::TRANSPARENCY }, + StaggerConfig{0, 8}, { + { 0x16, 0x13 }, { 0x17, 0x15 }, { 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) { + for (auto resolution : setting.resolutions.values()) { + sensor.resolutions = { resolution }; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = resolution; + sensor.exposure_lperiod = setting.exposure_lperiod; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.use_host_side_calib = setting.use_host_side_calib; + sensor.method = method; + sensor.stagger_y = setting.stagger_y; + sensor.custom_regs = setting.extra_custom_regs; + sensor.custom_fe_regs = setting.extra_custom_fe_regs; + s_sensors->push_back(sensor); + } + } + } + } + + + sensor = Genesys_Sensor(); + sensor.sensor_id = SensorId::CCD_CANON_5600F; // gl847 + sensor.full_resolution = 4800; + sensor.register_dpihw = 4800; + sensor.black_pixels = 50*8; + sensor.dummy_pixel = 10; + 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.use_host_side_calib = true; + { + struct CustomSensorSettings { + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpihw; + unsigned register_dpiset; + int exposure_lperiod; + SensorExposure exposure; + Ratio pixel_count_ratio; + int output_pixel_offset; + unsigned segment_size; + std::vector<unsigned> segment_order; + StaggerConfig stagger_x; + StaggerConfig stagger_y; + GenesysRegisterSettingSet custom_regs; + }; + + CustomSensorSettings custom_settings[] = { + { { 150 }, 2400, 600, 300, 4288, { 3983/2, 3983/2, 3983/2 }, Ratio{1, 8}, 10, + 5418, std::vector<unsigned>{}, StaggerConfig{}, StaggerConfig{}, { + { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, + { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, + { 0x52, 0x0e }, { 0x53, 0x00 }, { 0x54, 0x02 }, { 0x55, 0x04 }, + { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x52 }, { 0x59, 0x3a }, { 0x5a, 0x40 }, + { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, + } + }, + { { 300 }, 2400, 600, 600, 5472, { 4558/2, 4558/2, 4558/2 }, Ratio{1, 8}, 110, + 5418, std::vector<unsigned>{}, StaggerConfig{}, StaggerConfig{}, { + { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, + { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, + { 0x52, 0x0e }, { 0x53, 0x00 }, { 0x54, 0x02 }, { 0x55, 0x04 }, + { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x52 }, { 0x59, 0x3a }, { 0x5a, 0x40 }, + { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, + } + }, + { { 600 }, 2400, 600, 600, 10944, { 8701/2, 8701/2, 8701/2 }, Ratio{1, 4}, 155, + 5418, std::vector<unsigned>{}, StaggerConfig{}, StaggerConfig{}, { + { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, + { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, + { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, + { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, + { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, + } + }, + { { 1200 }, 2400, 1200, 1200, 29120, { 17120/2, 17120/2, 17120/2 }, Ratio{1, 2}, 295, + 5418, { 1, 0 }, StaggerConfig{}, StaggerConfig{}, { + { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, + { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, + { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, + { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, + { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, + } + }, + { { 2400 }, 2400, 2400, 2400, 43776, { 36725/2, 36725/2, 36725/2 }, Ratio{1, 1}, 600, + 5418, { 0, 1, 2, 3 }, + StaggerConfig{10, 15, 4, 9, 14, 19, 8, 13}, StaggerConfig{}, { + { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, + { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, + { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, + { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, + { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, + } + }, + { { 4800 }, 4800, 4800, 4800, 43776, { 36725/2, 36725/2, 36725/2 }, Ratio{1, 1}, 1000, + 10784, { 0, 1, 2, 3 }, + StaggerConfig{5, 9, 6, 10, 3, 7, 16, 20, 13, 17, 14, 18, 11, 15, 24, 28}, + StaggerConfig{6, 0}, { + { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, + { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, + { 0x52, 0x0a }, { 0x53, 0x0c }, { 0x54, 0x0e }, { 0x55, 0x00 }, + { 0x56, 0x02 }, { 0x57, 0x04 }, { 0x58, 0x32 }, { 0x59, 0x1a }, { 0x5a, 0x40 }, + { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, + } + } + }; + + for (const auto& setting : custom_settings) { + for (auto method : { ScanMethod::FLATBED, ScanMethod::TRANSPARENCY }) { + sensor.method = method; sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.resolutions.values().front(); sensor.exposure_lperiod = setting.exposure_lperiod; - sensor.method = method; - sensor.custom_regs = setting.extra_custom_regs; + sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.segment_size = setting.segment_size; + sensor.segment_order = setting.segment_order; + sensor.stagger_x = setting.stagger_x; + sensor.stagger_y = setting.stagger_y; + sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } @@ -2392,57 +2064,39 @@ void genesys_init_sensor_tables() 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.sensor_id = SensorId::CCD_CANON_8400F; // gl843 + sensor.full_resolution = 3200; + sensor.register_dpihw = 4800; 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; + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + Ratio pixel_count_ratio; int exposure_lperiod; + int output_pixel_offset; + int shading_pixel_offset; std::vector<ScanMethod> methods; + StaggerConfig stagger_y; 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 }, + { { 400 }, 2400, Ratio{1, 4}, 7200, 2, 0, { ScanMethod::FLATBED }, + StaggerConfig{}, { + { 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 }, @@ -2450,25 +2104,12 @@ void genesys_init_sensor_tables() { 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 }, + { { 800 }, 4800, Ratio{1, 4}, 7200, 5, 13, { ScanMethod::FLATBED }, + StaggerConfig{}, { + { 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 }, @@ -2476,26 +2117,13 @@ void genesys_init_sensor_tables() { 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 }, + { { 1600 }, 4800, Ratio{1, 2}, 14400, 10, 8, { ScanMethod::FLATBED }, + StaggerConfig{}, { + { 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, 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 }, @@ -2504,25 +2132,12 @@ void genesys_init_sensor_tables() { 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 }, + { { 3200 }, 4800, Ratio{1, 1}, 28800, 20, -2, { ScanMethod::FLATBED }, + StaggerConfig{0, 6}, { + { 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 }, @@ -2532,26 +2147,13 @@ void genesys_init_sensor_tables() { 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 }, + { { 400 }, 2400, Ratio{1, 4}, 14400, 2, 0, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{}, { + { 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 }, @@ -2559,53 +2161,27 @@ void genesys_init_sensor_tables() { 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 }, + { { 800 }, 4800, Ratio{1, 4}, 14400, 5, 13, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{}, { + { 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, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 }, { 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 }, + { { 1600 }, 4800, Ratio{1, 2}, 28800, 10, 8, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{}, { + { 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 }, @@ -2615,26 +2191,13 @@ void genesys_init_sensor_tables() { 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 }, + { { 3200 }, 4800, Ratio{1, 1}, 28800, 20, 10, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{0, 6}, { + { 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 }, @@ -2648,51 +2211,68 @@ void genesys_init_sensor_tables() 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); + for (auto method : setting.methods) + {for (auto resolution : setting.resolutions.values()) { + sensor.resolutions = { resolution }; + sensor.shading_resolution = resolution; + sensor.register_dpiset = setting.register_dpiset; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.exposure_lperiod = setting.exposure_lperiod; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.shading_pixel_offset = setting.shading_pixel_offset; + sensor.method = method; + sensor.stagger_y = setting.stagger_y; + 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.sensor_id = SensorId::CCD_CANON_8600F; // gl843 + sensor.full_resolution = 4800; + sensor.register_dpihw = 4800; 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpiset; int exposure_lperiod; + int output_pixel_offset; std::vector<ScanMethod> methods; + StaggerConfig stagger_y; GenesysRegisterSettingSet extra_custom_regs; GenesysRegisterSettingSet custom_fe_regs; }; CustomSensorSettings custom_settings[] = { - { { 300, 600, 1200 }, 24000, { ScanMethod::FLATBED }, { + { { 300 }, 1200, 1200, 24000, 1, { ScanMethod::FLATBED }, StaggerConfig{}, { + { 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 }, + }, + {}, + }, + { { 600 }, 1200, 2400, 24000, 2, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, @@ -2707,8 +2287,24 @@ void genesys_init_sensor_tables() }, {}, }, - { { 300, 600, 1200 }, 45000, { ScanMethod::TRANSPARENCY, - ScanMethod::TRANSPARENCY_INFRARED }, { + { { 1200 }, 1200, 4800, 24000, 5, { ScanMethod::FLATBED }, StaggerConfig{}, { + { 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 }, 1200, 1200, 45000, 6, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, @@ -2723,8 +2319,43 @@ void genesys_init_sensor_tables() }, {}, }, - { { 2400 }, 45000, { ScanMethod::TRANSPARENCY, - ScanMethod::TRANSPARENCY_INFRARED }, { + { { 600 }, 1200, 2400, 45000, 11, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{}, { + { 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 }, + }, + {}, + }, + { { 1200 }, 1200, 4800, 45000, 23, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{}, { + { 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 }, 2400, 4800, 45000, 10, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x01 }, { 0x1d, 0x75 }, @@ -2739,8 +2370,9 @@ void genesys_init_sensor_tables() }, {}, }, - { { 4800 }, 45000, { ScanMethod::TRANSPARENCY, - ScanMethod::TRANSPARENCY_INFRARED }, { + { { 4800 }, 4800, 4800, 45000, -1982, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, + StaggerConfig{8, 0}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x61 }, { 0x1d, 0x75 }, @@ -2760,25 +2392,30 @@ void genesys_init_sensor_tables() 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); + for (auto resolution : setting.resolutions.values()) { + sensor.resolutions = { resolution }; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = resolution; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.method = method; + sensor.exposure_lperiod = setting.exposure_lperiod; + sensor.stagger_y = setting.stagger_y; + 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.sensor_id = SensorId::CCD_HP_N6310; // gl847 + sensor.full_resolution = 2400; sensor.black_pixels = 96; sensor.dummy_pixel = 26; - sensor.ccd_start_xoffset = 128; - sensor.sensor_pixels = 42720; + sensor.pixel_count_ratio = Ratio{1, 4}; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; @@ -2802,41 +2439,102 @@ void genesys_init_sensor_tables() { 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; + unsigned shading_factor; + int output_pixel_offset; + }; + CustomSensorSettings custom_settings[] = { + { { 75 }, 600, 8, 4 }, + { { 100 }, 600, 6, 5 }, + { { 150 }, 600, 4, 8 }, + { { 200 }, 600, 3, 10 }, + { { 300 }, 600, 2, 16 }, + { { 600 }, 600, 1, 32 }, + { { 1200 }, 1200, 1, 64 }, + { { 2400 }, 2400, 1, 128 }, + }; + + auto base_custom_regs = sensor.custom_regs; + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpiset = setting.resolutions.values()[0]; + sensor.register_dpihw = setting.register_dpihw; + sensor.shading_resolution = setting.register_dpihw; + sensor.shading_factor = setting.shading_factor; + sensor.output_pixel_offset = setting.output_pixel_offset; + 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.sensor_id = SensorId::CIS_CANON_LIDE_110; // gl124 + sensor.full_resolution = 2400; 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpihw; + unsigned register_dpiset; + unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; + Ratio pixel_count_ratio; + unsigned shading_factor; std::vector<unsigned> segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150 }, 4608, { 462, 609, 453 }, std::vector<unsigned>{}, { + { { 75 }, 1200, 600, 150, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 4, + 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 }, + } + }, + { { 100 }, 1200, 600, 200, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 3, + 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 }, + } + }, + { { 150 }, 1200, 600, 300, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 2, + 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 }, @@ -2853,7 +2551,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 300 }, 4608, { 462, 609, 453 }, std::vector<unsigned>{}, { + { { 300 }, 1200, 600, 600, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 1, + 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 }, @@ -2870,7 +2569,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 600 }, 5360, { 823, 1117, 805 }, std::vector<unsigned>{}, { + { { 600 }, 2400, 600, 600, 600, 5360, { 823, 1117, 805 }, Ratio{1, 4}, 1, + 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 }, @@ -2887,7 +2587,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, }, }, - { { 1200 }, 10528, { 6071, 6670, 6042 }, { 0, 1 }, { + { { 1200 }, 2400, 1200, 1200, 1200, 10528, { 6071, 6670, 6042 }, Ratio{1, 4}, 1, + { 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 }, @@ -2904,7 +2605,8 @@ void genesys_init_sensor_tables() { 0x98, 0x22 }, } }, - { { 2400 }, 20864, { 7451, 8661, 7405 }, { 0, 2, 1, 3 }, { + { { 2400 }, 2400, 2400, 2400, 2400, 20864, { 7451, 8661, 7405 }, Ratio{1, 4}, 1, + { 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 }, @@ -2925,8 +2627,14 @@ void genesys_init_sensor_tables() for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); @@ -2934,34 +2642,69 @@ void genesys_init_sensor_tables() } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_CANON_LIDE_120; - sensor.optical_res = 2400; - sensor.ccd_size_divisor = 2; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_120; // gl124 + sensor.full_resolution = 2400; 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpihw; + unsigned register_dpiset; + unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; + Ratio pixel_count_ratio; + unsigned shading_factor; std::vector<unsigned> segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 300 }, 4608, { 1244, 1294, 1144 }, std::vector<unsigned>{}, { + { { 75 }, 1200, 600, 150, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 4, + 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 }, + }, + }, + { { 100 }, 1200, 600, 200, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 3, + 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 }, + }, + }, + { { 150 }, 1200, 600, 300, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 2, + 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 }, @@ -2978,7 +2721,26 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, }, }, - { { 600 }, 5360, { 2394, 2444, 2144 }, std::vector<unsigned>{}, { + { { 300 }, 1200, 600, 600, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 1, + 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 }, 2400, 600, 600, 600, 5360, { 2394, 2444, 2144 }, Ratio{1, 4}, 1, + 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 }, @@ -2995,7 +2757,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, }, }, - { { 1200 }, 10528, { 4694, 4644, 4094 }, std::vector<unsigned>{}, { + { { 1200 }, 2400, 1200, 1200, 1200, 10528, { 4694, 4644, 4094 }, Ratio{1, 2}, 1, + 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 }, @@ -3012,7 +2775,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, }, }, - { { 2400 }, 20864, { 8944, 8144, 7994 }, std::vector<unsigned>{}, { + { { 2400 }, 2400, 2400, 2400, 2400, 20864, { 8944, 8144, 7994 }, Ratio{1, 1}, 1, + 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 }, @@ -3033,8 +2797,14 @@ void genesys_init_sensor_tables() for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); @@ -3042,33 +2812,71 @@ void genesys_init_sensor_tables() } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_CANON_LIDE_210; - sensor.optical_res = 2400; - sensor.ccd_size_divisor = 2; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_210; // gl124 + sensor.full_resolution = 4800; 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpihw; + unsigned register_dpiset; + unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; + Ratio pixel_count_ratio; + unsigned shading_factor; std::vector<unsigned> segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector<unsigned>{}, { + { { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, + 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 }, + } + }, + { { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, + 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 }, + } + }, + { { 150 }, 2400, 600, 300, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 2, + 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 }, @@ -3086,7 +2894,27 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 600 }, 5360, { 388, 574, 393 }, std::vector<unsigned>{}, { + { { 300 }, 2400, 600, 600, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 1, + 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 }, 4800, 600, 600, 600, 5360, { 388, 574, 393 }, Ratio{1, 8}, 1, + 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 }, @@ -3104,7 +2932,7 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 1200 }, 10528, { 388, 574, 393 }, {0, 1}, { + { { 1200 }, 4800, 1200, 1200, 1200, 10528, { 388, 574, 393 }, Ratio{1, 8}, 1, {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 }, @@ -3122,7 +2950,8 @@ void genesys_init_sensor_tables() { 0x98, 0x22 }, }, }, - { { 2400 }, 20864, { 6839, 8401, 6859 }, {0, 2, 1, 3}, { + { { 2400 }, 4800, 2400, 2400, 2400, 20864, { 6839, 8401, 6859 }, Ratio{1, 8}, 1, + {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 }, @@ -3139,13 +2968,38 @@ void genesys_init_sensor_tables() { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x24 }, }, + }, + { { 4800 }, 4800, 4800, 4800, 4800, 41536, { 9735, 14661, 11345 }, Ratio{1, 8}, 1, + { 0, 2, 4, 6, 1, 3, 5, 7 }, { + // { 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, 0x04 }, + { 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, 0xa5 }, + { 0x98, 0x28 }, + }, } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); @@ -3153,33 +3007,33 @@ void genesys_init_sensor_tables() } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_CANON_LIDE_220; - sensor.optical_res = 2400; - sensor.ccd_size_divisor = 2; + sensor.sensor_id = SensorId::CIS_CANON_LIDE_220; // gl124 + sensor.full_resolution = 4800; 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; + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpihw; + unsigned register_dpiset; + unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; + Ratio pixel_count_ratio; + unsigned shading_factor; std::vector<unsigned> segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector<unsigned>{}, { + { { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, + 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 }, @@ -3197,7 +3051,65 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 600 }, 5360, { 388, 574, 393 }, std::vector<unsigned>{}, { + { { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, + 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 }, + } + }, + { { 150 }, 2400, 600, 300, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 2, + 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 }, + } + }, + { { 300 }, 2400, 600, 600, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 1, + 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 }, 4800, 600, 600, 600, 5360, { 388, 574, 393 }, Ratio{1, 8}, 1, + 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 }, @@ -3215,7 +3127,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 1200 }, 10528, { 388, 574, 393 }, {0, 1}, { + { { 1200 }, 4800, 1200, 1200, 1200, 10528, { 388, 574, 393 }, Ratio{1, 8}, 1, + {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 }, @@ -3233,7 +3146,8 @@ void genesys_init_sensor_tables() { 0x98, 0x22 }, } }, - { { 2400 }, 20864, { 6839, 8401, 6859 }, {0, 2, 1, 3}, { + { { 2400 }, 4800, 2400, 2400, 2400, 20864, { 6839, 8401, 6859 }, Ratio{1, 8}, 1, + {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 }, @@ -3250,13 +3164,38 @@ void genesys_init_sensor_tables() { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x24 }, }, + }, + { { 4800 }, 4800, 4800, 4800, 4800, 41536, { 9735, 14661, 11345 }, Ratio{1, 8}, 1, + { 0, 2, 4, 6, 1, 3, 5, 7 }, { + // { 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, 0x04 }, + { 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, 0xa5 }, + { 0x98, 0x28 }, + }, } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); @@ -3264,63 +3203,116 @@ void genesys_init_sensor_tables() } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600; - sensor.optical_res = 1200; - sensor.ccd_size_divisor = 2; + sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600; // gl841 + sensor.full_resolution = 1200; 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 }, + { 0x16, 0x33 }, { 0x17, 0x0b }, { 0x18, 0x11 }, { 0x19, 0x2a }, + { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0xc4 }, + { 0x52, 0x07 }, { 0x53, 0x0a }, { 0x54, 0x0c }, { 0x55, 0x00 }, + { 0x56, 0x02 }, { 0x57, 0x06 }, { 0x58, 0x22 }, { 0x59, 0x69 }, { 0x5a, 0x40 }, + { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, + }; + sensor.gamma = { 1.0f, 1.0f, 1.0f }; + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpihw; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 600, 600, 150, 11 }, + { { 100 }, 600, 600, 200, 14 }, + { { 150 }, 600, 600, 300, 22 }, + { { 200 }, 600, 600, 400, 29 }, + { { 300 }, 600, 600, 600, 44 }, + { { 600 }, 600, 600, 1200, 88 }, + { { 1200 }, 1200, 1200, 1200, 88 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.register_dpihw; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } + + + sensor = Genesys_Sensor(); + sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200; // gl842 + sensor.full_resolution = 7200; + sensor.register_dpihw = 1200; + sensor.black_pixels = 88; // TODO + sensor.dummy_pixel = 19; + sensor.fau_gain_white_ref = 210; + sensor.gain_white_ref = 230; + sensor.exposure = { 0x2b00, 0x2b00, 0x2b00 }; + sensor.exposure_lperiod = 0x694e; + sensor.use_host_side_calib = true; + sensor.custom_regs = { + { 0x16, 0x3b }, { 0x17, 0x4b }, { 0x18, 0x10 }, { 0x19, 0x00 }, + { 0x1a, 0x24 }, { 0x1b, 0x00 }, { 0x1c, 0x40 }, { 0x1d, 0x84 }, + { 0x52, 0x09 }, { 0x53, 0x0c }, { 0x54, 0x0e }, { 0x55, 0x02 }, + { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x22 }, { 0x59, 0x69 }, { 0x5a, 0xc0 }, + { 0x70, 0x08 }, { 0x71, 0x09 }, { 0x72, 0x0b }, { 0x73, 0x0c }, + { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, + { 0x77, 0x00 }, { 0x78, 0x7f }, { 0x79, 0xff }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, { 0x7f, 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + ScanMethod method; + Ratio pixel_count_ratio; + int output_pixel_offset; + unsigned register_dpiset; + StaggerConfig stagger_y; + }; + + CustomSensorSettings custom_settings[] = { + { { 900 }, ScanMethod::TRANSPARENCY, Ratio{8, 8}, 2, 150, StaggerConfig{} }, + { { 1800 }, ScanMethod::TRANSPARENCY, Ratio{4, 4}, 10, 300, StaggerConfig{} }, + { { 3600 }, ScanMethod::TRANSPARENCY, Ratio{2, 2}, 10, 600, StaggerConfig{} }, + { { 7200 }, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 20, 1200, StaggerConfig{0, 4} }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.method = setting.method; + sensor.shading_resolution = setting.resolutions.values().front(); + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.register_dpiset = setting.register_dpiset; + sensor.stagger_y = setting.stagger_y; + 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.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200I; // gl843 + sensor.full_resolution = 7200; + sensor.register_dpihw = 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.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, @@ -3351,47 +3343,53 @@ void genesys_init_sensor_tables() { 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; + ValueFilterAny<unsigned> resolutions; ScanMethod method; - unsigned ccd_size_divisor; - unsigned logical_dpihw_override; - unsigned pixel_count_multiplier; + unsigned shading_resolution; + Ratio pixel_count_ratio; + int output_pixel_offset; unsigned exposure_lperiod; - unsigned dpiset_override; + unsigned register_dpiset; + StaggerConfig stagger_y; 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, { + { { 900 }, ScanMethod::TRANSPARENCY, 900, Ratio{8, 8}, 2, 0x2538, 150, + StaggerConfig{}, {} }, + { { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2538, 300, + StaggerConfig{}, {} }, + { { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2538, 600, + StaggerConfig{}, {} }, + { { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x19c8, 1200, + StaggerConfig{4, 0}, { { 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, {} }, + { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x1f54, 150, + StaggerConfig{}, {} }, + { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x1f54, 300, + StaggerConfig{}, {} }, + { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x1f54, 600, + StaggerConfig{}, {}}, + { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x1f54, 1200, + StaggerConfig{4, 0}, {} }, }; 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.shading_resolution = setting.shading_resolution; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; sensor.exposure_lperiod = setting.exposure_lperiod; - sensor.dpiset_override = setting.dpiset_override; + sensor.register_dpiset = setting.register_dpiset; + sensor.stagger_y = setting.stagger_y; sensor.custom_fe_regs = setting.custom_fe_regs; s_sensors->push_back(sensor); } @@ -3399,19 +3397,17 @@ void genesys_init_sensor_tables() sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300; - sensor.optical_res = 7200; + sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300; // gl843 + sensor.full_resolution = 7200; sensor.method = ScanMethod::TRANSPARENCY; - sensor.register_dpihw_override = 1200; + sensor.register_dpihw = 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.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, @@ -3442,50 +3438,98 @@ void genesys_init_sensor_tables() { 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; + ValueFilterAny<unsigned> resolutions; + unsigned shading_resolution; + Ratio pixel_count_ratio; + int output_pixel_offset; + unsigned register_dpiset; + StaggerConfig stagger_y; }; CustomSensorSettings custom_settings[] = { - { { 900 }, 1, 900, 8, 150 }, - { { 1800 }, 1, 1800, 4, 300 }, - { { 3600 }, 1, 3600, 2, 600 }, - { { 7200 }, 1, 7200, 1, 1200 }, + { { 900 }, 900, Ratio{8, 8}, 2, 150, StaggerConfig{} }, + { { 1800 }, 1800, Ratio{4, 4}, 5, 300, StaggerConfig{} }, + { { 3600 }, 3600, Ratio{2, 2}, 10, 600, StaggerConfig{} }, + { { 7200 }, 7200, Ratio{1, 1}, 20, 1200, StaggerConfig{4, 0} }, }; 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; + sensor.shading_resolution = setting.shading_resolution; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.register_dpiset = setting.register_dpiset; + sensor.stagger_y = setting.stagger_y; 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.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7400; // gl845 + sensor.full_resolution = 7200; + sensor.method = ScanMethod::TRANSPARENCY; + sensor.register_dpihw = 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.exposure_lperiod = 14000; + sensor.use_host_side_calib = true; + 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, 0x09 }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, + { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 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 }, { 0x7d, 0x00 }, + { 0x87, 0x00 }, + }; + sensor.gamma = { 1.0f, 1.0f, 1.0f }; + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpiset; + int output_pixel_offset; + StaggerConfig stagger_y; + }; + + CustomSensorSettings custom_settings[] = { + { { 600 }, 100, 10, StaggerConfig{} }, + { { 1200 }, 200, 20, StaggerConfig{} }, + { { 2400 }, 400, 40, StaggerConfig{} }, + { { 3600 }, 600, 60, StaggerConfig{} }, + { { 7200 }, 1200, 120, StaggerConfig{4, 0} }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.shading_resolution = setting.resolutions.values()[0]; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.stagger_y = setting.stagger_y; + s_sensors->push_back(sensor); + } + } + + sensor = Genesys_Sensor(); + sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7500I; // gl843 + sensor.full_resolution = 7200; + sensor.register_dpihw = 1200; + sensor.black_pixels = 88; // TODO + sensor.dummy_pixel = 20; + sensor.fau_gain_white_ref = 210; + sensor.gain_white_ref = 230; + sensor.exposure = { 0x0000, 0x0000, 0x0000 }; + sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, @@ -3516,57 +3560,119 @@ void genesys_init_sensor_tables() { 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; + ValueFilterAny<unsigned> resolutions; ScanMethod method; - unsigned ccd_size_divisor; - unsigned logical_dpihw_override; - unsigned pixel_count_multiplier; + unsigned shading_resolution; + Ratio pixel_count_ratio; + int output_pixel_offset; unsigned exposure_lperiod; - unsigned dpiset_override; + unsigned register_dpiset; + StaggerConfig stagger_y; }; 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 }, + { { 900 }, ScanMethod::TRANSPARENCY, 900, Ratio{8, 8}, 2, 0x2f44, 150, + StaggerConfig{} }, + { { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2f44, 300, + StaggerConfig{} }, + { { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2f44, 600, + StaggerConfig{} }, + { { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x2f44, 1200, + StaggerConfig{4, 0} }, + { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x2af8, 150, + StaggerConfig{} }, + { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x2af8, 300, + StaggerConfig{} }, + { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x2af8, 600, + StaggerConfig{} }, + { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x2af8, 1200, + StaggerConfig{4, 0} }, }; 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.shading_resolution = setting.shading_resolution; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.output_pixel_offset = setting.output_pixel_offset; sensor.exposure_lperiod = setting.exposure_lperiod; - sensor.dpiset_override = setting.dpiset_override; + sensor.register_dpiset = setting.register_dpiset; + sensor.stagger_y = setting.stagger_y; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CCD_IMG101; + sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_8200I; // gl845 + sensor.full_resolution = 7200; + sensor.method = ScanMethod::TRANSPARENCY; + sensor.register_dpihw = 1200; + sensor.black_pixels = 88; // TODO + sensor.dummy_pixel = 20; + sensor.fau_gain_white_ref = 210; + sensor.gain_white_ref = 230; + sensor.exposure = { 0x0000, 0x0000, 0x0000 }; + sensor.exposure_lperiod = 14000; + sensor.use_host_side_calib = true; + 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, 0x09 }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, + { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 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 }, { 0x7d, 0x00 }, + { 0x87, 0x00 }, + }; + sensor.gamma = { 1.0f, 1.0f, 1.0f }; + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + ScanMethod method; + unsigned register_dpiset; + int output_pixel_offset; + StaggerConfig stagger_y; + }; + + CustomSensorSettings custom_settings[] = { + { { 900 }, ScanMethod::TRANSPARENCY, 150, 15, StaggerConfig{} }, + { { 1800 }, ScanMethod::TRANSPARENCY, 300, 30, StaggerConfig{} }, + { { 3600 }, ScanMethod::TRANSPARENCY, 600, 60, StaggerConfig{} }, + { { 7200 }, ScanMethod::TRANSPARENCY, 1200, 120, StaggerConfig{4, 0} }, + { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 150, 15, StaggerConfig{} }, + { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 300, 30, StaggerConfig{} }, + { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 600, 60, StaggerConfig{} }, + { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1200, 120, StaggerConfig{4, 0} }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.method = setting.method; + sensor.shading_resolution = setting.resolutions.values()[0]; + sensor.register_dpiset = setting.register_dpiset; + sensor.output_pixel_offset = setting.output_pixel_offset; + sensor.stagger_y = setting.stagger_y; + s_sensors->push_back(sensor); + } + } + + + sensor = Genesys_Sensor(); + sensor.sensor_id = SensorId::CCD_IMG101; // gl846 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.full_resolution = 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 }; @@ -3580,21 +3686,47 @@ void genesys_init_sensor_tables() { 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; + Ratio pixel_count_ratio; + unsigned shading_factor; + GenesysRegisterSettingSet extra_custom_regs; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 600, Ratio{1, 4}, 8, { { 0x7e, 0x00 } } }, + { { 100 }, 600, Ratio{1, 4}, 6, { { 0x7e, 0x00 } } }, + { { 150 }, 600, Ratio{1, 4}, 4, { { 0x7e, 0x00 } } }, + { { 300 }, 600, Ratio{1, 4}, 2, { { 0x7e, 0x00 } } }, + { { 600 }, 600, Ratio{1, 4}, 1, { { 0x7e, 0x01 } } }, + { { 1200 }, 1200, Ratio{1, 2}, 1, { { 0x7e, 0x01 } } }, + }; + + auto base_custom_regs = sensor.custom_regs; + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.resolutions.values()[0]; + sensor.shading_resolution = setting.register_dpihw; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; + 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_PLUSTEK_OPTICBOOK_3800; + sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICBOOK_3800; // gl845 sensor.resolutions = { 75, 100, 150, 300, 600, 1200 }; sensor.exposure_lperiod = 11000; - sensor.optical_res = 1200; + sensor.full_resolution = 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 }; @@ -3603,66 +3735,150 @@ void genesys_init_sensor_tables() { 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 }, + { 0x70, 0x01 }, { 0x71, 0x00 }, { 0x72, 0x02 }, { 0x73, 0x01 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, - { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x7d, 0x20 }, + { 0x87, 0x02 }, }; 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned register_dpihw; + Ratio pixel_count_ratio; + unsigned shading_factor; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 600, Ratio{1, 2}, 8 }, + { { 100 }, 600, Ratio{1, 2}, 6 }, + { { 150 }, 600, Ratio{1, 2}, 4 }, + { { 300 }, 600, Ratio{1, 2}, 2 }, + { { 600 }, 600, Ratio{1, 2}, 1 }, + { { 1200 }, 1200, Ratio{1, 1}, 1 }, + }; + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.register_dpihw = setting.register_dpihw; + sensor.register_dpiset = setting.resolutions.values()[0]; + sensor.shading_resolution = setting.register_dpihw; + sensor.pixel_count_ratio = setting.pixel_count_ratio; + sensor.shading_factor = setting.shading_factor; + 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.sensor_id = SensorId::CIS_CANON_LIDE_80; // gl841 + sensor.full_resolution = 1200; // real hardware limit is 2400 + sensor.register_dpihw = 1200; 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 }, + { 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 }, + { 0x70, 0x00 }, { 0x71, 0x05 }, { 0x72, 0x07 }, { 0x73, 0x09 }, }; 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); + { + struct CustomSensorSettings + { + ValueFilterAny<unsigned> resolutions; + unsigned optical_resolution; + unsigned register_dpiset; + unsigned shading_resolution; + unsigned shading_factor; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 75 }, 600, 150, 600, 8, 2 }, + { { 100 }, 600, 200, 600, 6, 3 }, + { { 150 }, 600, 300, 600, 4, 4 }, + { { 200 }, 600, 400, 600, 3, 6 }, + { { 300 }, 600, 600, 600, 2, 9 }, + { { 600 }, 600, 1200, 600, 1, 17 }, + { { 1200 }, 1200, 1200, 1200, 1, 35 }, + }; + + for (const CustomSensorSettings& setting : custom_settings) { + sensor.resolutions = setting.resolutions; + sensor.optical_resolution = setting.optical_resolution; + sensor.register_dpiset = setting.register_dpiset; + sensor.shading_resolution = setting.shading_resolution; + sensor.shading_factor = setting.shading_factor; + sensor.output_pixel_offset = setting.output_pixel_offset; + s_sensors->push_back(sensor); + } + } +} + +void verify_sensor_tables() +{ + std::map<SensorId, AsicType> sensor_to_asic; + for (const auto& device : *s_usb_devices) { + sensor_to_asic[device.model().sensor_id] = device.model().asic_type; + } + for (const auto& sensor : *s_sensors) { + if (sensor_to_asic.count(sensor.sensor_id) == 0) { + throw SaneException("Unknown asic for sensor"); + } + auto asic_type = sensor_to_asic[sensor.sensor_id]; + + if (sensor.full_resolution == 0) { + throw SaneException("full_resolution is not defined"); + } + + if (sensor.register_dpiset == 0) { + throw SaneException("register_dpiset is not defined"); + } + + if (asic_type != AsicType::GL646) { + if (sensor.register_dpihw == 0) { + throw SaneException("register_dpihw is not defined"); + } + if (sensor.shading_resolution == 0) { + throw SaneException("shading_resolution is not defined"); + } + } + + if (asic_type == AsicType::GL841) { + auto required_registers = { + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, + 0x70, 0x71, 0x72, 0x73, + }; + for (auto address : required_registers) { + if (!sensor.custom_regs.has_reg(address)) { + throw SaneException("Required register is not present"); + } + } + } + + if (asic_type == AsicType::GL842) { + auto required_registers = { + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, + 0x7f + }; + for (auto address : required_registers) { + if (!sensor.custom_regs.has_reg(address)) { + throw SaneException("Required register is not present"); + } + } + } + } } + } // namespace genesys diff --git a/backend/genesys/test_scanner_interface.cpp b/backend/genesys/test_scanner_interface.cpp index 12f726f..e8af494 100644 --- a/backend/genesys/test_scanner_interface.cpp +++ b/backend/genesys/test_scanner_interface.cpp @@ -49,7 +49,10 @@ namespace genesys { -TestScannerInterface::TestScannerInterface(Genesys_Device* dev) : dev_{dev} +TestScannerInterface::TestScannerInterface(Genesys_Device* dev, uint16_t vendor_id, + uint16_t product_id, uint16_t bcd_device) : + dev_{dev}, + usb_dev_{vendor_id, product_id, bcd_device} { // initialize status registers if (dev_->model->asic_type == AsicType::GL124) { @@ -58,6 +61,7 @@ TestScannerInterface::TestScannerInterface(Genesys_Device* dev) : dev_{dev} write_register(0x41, 0x00); } if (dev_->model->asic_type == AsicType::GL841 || + dev_->model->asic_type == AsicType::GL842 || dev_->model->asic_type == AsicType::GL843 || dev_->model->asic_type == AsicType::GL845 || dev_->model->asic_type == AsicType::GL846 || @@ -137,23 +141,21 @@ void TestScannerInterface::bulk_write_data(std::uint8_t addr, std::uint8_t* data } void TestScannerInterface::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, - std::size_t size, Flags flags) + std::size_t size) { (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) + std::size_t size) { (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) diff --git a/backend/genesys/test_scanner_interface.h b/backend/genesys/test_scanner_interface.h index acf0f6d..fc8128c 100644 --- a/backend/genesys/test_scanner_interface.h +++ b/backend/genesys/test_scanner_interface.h @@ -56,7 +56,8 @@ namespace genesys { class TestScannerInterface : public ScannerInterface { public: - TestScannerInterface(Genesys_Device* dev); + TestScannerInterface(Genesys_Device* dev, std::uint16_t vendor_id, std::uint16_t product_id, + std::uint16_t bcd_device); ~TestScannerInterface() override; @@ -74,9 +75,9 @@ public: 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; + std::size_t size) override; void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, - std::size_t size, Flags flags) override; + std::size_t size) 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; diff --git a/backend/genesys/test_settings.cpp b/backend/genesys/test_settings.cpp index 425f09c..f328709 100644 --- a/backend/genesys/test_settings.cpp +++ b/backend/genesys/test_settings.cpp @@ -52,6 +52,7 @@ namespace { bool s_testing_mode = false; std::uint16_t s_vendor_id = 0; std::uint16_t s_product_id = 0; +std::uint16_t s_bcd_device = 0; TestCheckpointCallback s_checkpoint_callback; } // namespace @@ -66,15 +67,17 @@ void disable_testing_mode() s_testing_mode = false; s_vendor_id = 0; s_product_id = 0; - + s_bcd_device = 0; } void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id, + std::uint16_t bcd_device, TestCheckpointCallback checkpoint_callback) { s_testing_mode = true; s_vendor_id = vendor_id; s_product_id = product_id; + s_bcd_device = bcd_device; s_checkpoint_callback = checkpoint_callback; } @@ -88,6 +91,11 @@ std::uint16_t get_testing_product_id() return s_product_id; } +std::uint16_t get_testing_bcd_device() +{ + return s_bcd_device; +} + std::string get_testing_device_name() { std::string name; diff --git a/backend/genesys/test_settings.h b/backend/genesys/test_settings.h index 8ac03e0..38cc3b3 100644 --- a/backend/genesys/test_settings.h +++ b/backend/genesys/test_settings.h @@ -58,9 +58,11 @@ using TestCheckpointCallback = std::function<void(const Genesys_Device&, bool is_testing_mode(); void disable_testing_mode(); void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id, + std::uint16_t bcd_device, TestCheckpointCallback checkpoint_callback); std::uint16_t get_testing_vendor_id(); std::uint16_t get_testing_product_id(); +std::uint16_t get_testing_bcd_device(); std::string get_testing_device_name(); TestCheckpointCallback get_testing_checkpoint_callback(); diff --git a/backend/genesys/test_usb_device.cpp b/backend/genesys/test_usb_device.cpp index de2399e..1612eae 100644 --- a/backend/genesys/test_usb_device.cpp +++ b/backend/genesys/test_usb_device.cpp @@ -48,9 +48,11 @@ namespace genesys { -TestUsbDevice::TestUsbDevice(std::uint16_t vendor, std::uint16_t product) : +TestUsbDevice::TestUsbDevice(std::uint16_t vendor, std::uint16_t product, + std::uint16_t bcd_device) : vendor_{vendor}, - product_{product} + product_{product}, + bcd_device_{bcd_device} { } @@ -94,12 +96,25 @@ void TestUsbDevice::close() name_ = ""; } -void TestUsbDevice::get_vendor_product(int& vendor, int& product) +std::uint16_t TestUsbDevice::get_vendor_id() { DBG_HELPER(dbg); assert_is_open(); - vendor = vendor_; - product = product_; + return vendor_; +} + +std::uint16_t TestUsbDevice::get_product_id() +{ + DBG_HELPER(dbg); + assert_is_open(); + return product_; +} + +std::uint16_t TestUsbDevice::get_bcd_device() +{ + DBG_HELPER(dbg); + assert_is_open(); + return bcd_device_; } void TestUsbDevice::control_msg(int rtype, int reg, int value, int index, int length, diff --git a/backend/genesys/test_usb_device.h b/backend/genesys/test_usb_device.h index abbd78a..03b49cc 100644 --- a/backend/genesys/test_usb_device.h +++ b/backend/genesys/test_usb_device.h @@ -50,9 +50,7 @@ namespace genesys { class TestUsbDevice : public IUsbDevice { public: - TestUsbDevice(std::uint16_t vendor, std::uint16_t product); - TestUsbDevice() = default; - + TestUsbDevice(std::uint16_t vendor, std::uint16_t product, std::uint16_t bcd_device); ~TestUsbDevice() override; bool is_open() const override { return is_open_; } @@ -65,7 +63,9 @@ public: void reset() override; void close() override; - void get_vendor_product(int& vendor, int& product) override; + std::uint16_t get_vendor_id() override; + std::uint16_t get_product_id() override; + std::uint16_t get_bcd_device() override; void control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) override; @@ -78,6 +78,7 @@ private: bool is_open_ = false; std::uint16_t vendor_ = 0; std::uint16_t product_ = 0; + std::uint16_t bcd_device_ = 0; }; } // namespace genesys diff --git a/backend/genesys/usb_device.cpp b/backend/genesys/usb_device.cpp index 2d02219..d6cbaed 100644 --- a/backend/genesys/usb_device.cpp +++ b/backend/genesys/usb_device.cpp @@ -101,11 +101,33 @@ void UsbDevice::close() sanei_usb_close(device_num); } -void UsbDevice::get_vendor_product(int& vendor, int& product) +std::uint16_t UsbDevice::get_vendor_id() { DBG_HELPER(dbg); assert_is_open(); + int vendor = 0; + int product = 0; TIE(sanei_usb_get_vendor_product(device_num_, &vendor, &product)); + return static_cast<std::uint16_t>(vendor); +} + +std::uint16_t UsbDevice::get_product_id() +{ + DBG_HELPER(dbg); + assert_is_open(); + int vendor = 0; + int product = 0; + TIE(sanei_usb_get_vendor_product(device_num_, &vendor, &product)); + return static_cast<std::uint16_t>(product); +} + +std::uint16_t UsbDevice::get_bcd_device() +{ + DBG_HELPER(dbg); + assert_is_open(); + sanei_usb_dev_descriptor desc; + TIE(sanei_usb_get_descriptor(device_num_, &desc)); + return desc.bcd_dev; } void UsbDevice::control_msg(int rtype, int reg, int value, int index, int length, diff --git a/backend/genesys/usb_device.h b/backend/genesys/usb_device.h index 265c57c..aa8b89a 100644 --- a/backend/genesys/usb_device.h +++ b/backend/genesys/usb_device.h @@ -71,7 +71,9 @@ public: virtual void reset() = 0; virtual void close() = 0; - virtual void get_vendor_product(int& vendor, int& product) = 0; + virtual std::uint16_t get_vendor_id() = 0; + virtual std::uint16_t get_product_id() = 0; + virtual std::uint16_t get_bcd_device() = 0; virtual void control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) = 0; @@ -96,7 +98,9 @@ public: void reset() override; void close() override; - void get_vendor_product(int& vendor, int& product) override; + std::uint16_t get_vendor_id() override; + std::uint16_t get_product_id() override; + std::uint16_t get_bcd_device() override; void control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) override; diff --git a/backend/genesys/utilities.h b/backend/genesys/utilities.h index 1e268b5..fdab770 100644 --- a/backend/genesys/utilities.h +++ b/backend/genesys/utilities.h @@ -46,12 +46,81 @@ #include "error.h" #include <algorithm> +#include <cstdint> #include <iostream> #include <sstream> #include <vector> + namespace genesys { +// just like SANE_FIX and SANE_UNFIX except that the conversion is done by a function and argument +// precision is handled correctly +inline SANE_Word double_to_fixed(double v) +{ + return static_cast<SANE_Word>(v * (1 << SANE_FIXED_SCALE_SHIFT)); +} + +inline SANE_Word float_to_fixed(float v) +{ + return static_cast<SANE_Word>(v * (1 << SANE_FIXED_SCALE_SHIFT)); +} + +inline float fixed_to_float(SANE_Word v) +{ + return static_cast<float>(v) / (1 << SANE_FIXED_SCALE_SHIFT); +} + +inline double fixed_to_double(SANE_Word v) +{ + return static_cast<double>(v) / (1 << SANE_FIXED_SCALE_SHIFT); +} + +template<class T> +inline T abs_diff(T a, T b) +{ + if (a < b) { + return b - a; + } else { + return a - b; + } +} + +inline std::uint64_t align_multiple_floor(std::uint64_t x, std::uint64_t multiple) +{ + if (multiple == 0) { + return x; + } + return (x / multiple) * multiple; +} + +inline std::uint64_t align_multiple_ceil(std::uint64_t x, std::uint64_t multiple) +{ + if (multiple == 0) { + return x; + } + return ((x + multiple - 1) / multiple) * multiple; +} + +inline std::uint64_t multiply_by_depth_ceil(std::uint64_t pixels, std::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; +} + template<class T> void compute_array_percentile_approx(T* result, const T* data, std::size_t line_count, std::size_t elements_per_line, @@ -85,6 +154,75 @@ void compute_array_percentile_approx(T* result, const T* data, } } +class Ratio +{ +public: + Ratio() : multiplier_{1}, divisor_{1} + { + } + + Ratio(unsigned multiplier, unsigned divisor) : multiplier_{multiplier}, divisor_{divisor} + { + } + + unsigned multiplier() const { return multiplier_; } + unsigned divisor() const { return divisor_; } + + unsigned apply(unsigned arg) const + { + return static_cast<std::uint64_t>(arg) * multiplier_ / divisor_; + } + + int apply(int arg) const + { + return static_cast<std::int64_t>(arg) * multiplier_ / divisor_; + } + + float apply(float arg) const + { + return arg * multiplier_ / divisor_; + } + + unsigned apply_inverse(unsigned arg) const + { + return static_cast<std::uint64_t>(arg) * divisor_ / multiplier_; + } + + int apply_inverse(int arg) const + { + return static_cast<std::int64_t>(arg) * divisor_ / multiplier_; + } + + float apply_inverse(float arg) const + { + return arg * divisor_ / multiplier_; + } + + bool operator==(const Ratio& other) const + { + return multiplier_ == other.multiplier_ && divisor_ == other.divisor_; + } +private: + unsigned multiplier_; + unsigned divisor_; + + template<class Stream> + friend void serialize(Stream& str, Ratio& x); +}; + +template<class Stream> +void serialize(Stream& str, Ratio& x) +{ + serialize(str, x.multiplier_); + serialize(str, x.divisor_); +} + +inline std::ostream& operator<<(std::ostream& out, const Ratio& ratio) +{ + out << ratio.multiplier() << "/" << ratio.divisor(); + return out; +} + template<class Char, class Traits> class BasicStreamStateSaver { diff --git a/backend/genesys/value_filter.h b/backend/genesys/value_filter.h new file mode 100644 index 0000000..ba55567 --- /dev/null +++ b/backend/genesys/value_filter.h @@ -0,0 +1,140 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2020 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. +*/ + +#ifndef BACKEND_GENESYS_VALUE_FILTER_H +#define BACKEND_GENESYS_VALUE_FILTER_H + +#include <algorithm> +#include <initializer_list> +#include <iostream> +#include <vector> + +namespace genesys { + +struct AnyTag {}; +constexpr AnyTag VALUE_FILTER_ANY{}; + +template<class T> +class ValueFilterAny +{ +public: + ValueFilterAny() : matches_any_{false} {} + ValueFilterAny(AnyTag) : matches_any_{true} {} + ValueFilterAny(std::initializer_list<T> values) : + matches_any_{false}, + values_{values} + {} + + bool matches(T value) const + { + if (matches_any_) + return true; + auto it = std::find(values_.begin(), values_.end(), value); + return it != values_.end(); + } + + bool operator==(const ValueFilterAny& other) const + { + return matches_any_ == other.matches_any_ && values_ == other.values_; + } + + bool matches_any() const { return matches_any_; } + const std::vector<T>& values() const { return values_; } + +private: + bool matches_any_ = false; + std::vector<T> values_; + + template<class Stream, class U> + friend void serialize(Stream& str, ValueFilterAny<U>& x); +}; + +template<class T> +std::ostream& operator<<(std::ostream& out, const ValueFilterAny<T>& values) +{ + if (values.matches_any()) { + out << "ANY"; + return out; + } + out << format_vector_indent_braced(4, "", values.values()); + return out; +} + +template<class Stream, class T> +void serialize(Stream& str, ValueFilterAny<T>& x) +{ + serialize(str, x.matches_any_); + serialize_newline(str); + serialize(str, x.values_); +} + + +template<class T> +class ValueFilter +{ +public: + ValueFilter() = default; + ValueFilter(std::initializer_list<T> values) : + values_{values} + {} + + bool matches(T value) const + { + auto it = std::find(values_.begin(), values_.end(), value); + return it != values_.end(); + } + + bool operator==(const ValueFilter& other) const + { + return values_ == other.values_; + } + + const std::vector<T>& values() const { return values_; } + +private: + std::vector<T> values_; + + template<class Stream, class U> + friend void serialize(Stream& str, ValueFilter<U>& x); +}; + +template<class T> +std::ostream& operator<<(std::ostream& out, const ValueFilter<T>& values) +{ + if (values.values().empty()) { + out << "(none)"; + return out; + } + out << format_vector_indent_braced(4, "", values.values()); + return out; +} + +template<class Stream, class T> +void serialize(Stream& str, ValueFilter<T>& x) +{ + serialize_newline(str); + serialize(str, x.values_); +} + +} // namespace genesys + +#endif // BACKEND_GENESYS_VALUE_FILTER_H diff --git a/backend/gt68xx.c b/backend/gt68xx.c index 00190fe..f657d42 100644 --- a/backend/gt68xx.c +++ b/backend/gt68xx.c @@ -2130,6 +2130,7 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, && s->byte_count >= s->reader->params.pixel_xs) { DBG (4, "sane_read: nothing more to scan: EOF\n"); + gt68xx_scanner_stop_scan(s); return SANE_STATUS_EOF; } @@ -2343,8 +2344,10 @@ sane_cancel (SANE_Handle handle) gt68xx_device_carriage_home (s->dev); } if (s->gamma_table) - free (s->gamma_table); - s->gamma_table = 0; + { + free (s->gamma_table); + s->gamma_table = 0; + } } else { diff --git a/backend/gt68xx.conf.in b/backend/gt68xx.conf.in index e9a9706..af5ebe3 100644 --- a/backend/gt68xx.conf.in +++ b/backend/gt68xx.conf.in @@ -1,5 +1,5 @@ -# gt68xx.conf: Configuration file for GT68XX based scanners (@PACKAGEVERSION@) +# gt68xx.conf: Configuration file for GT68XX based scanners # Read man sane-gt68xx for documentation # Put the firmware file into "@DATADIR@/sane/gt68xx/". diff --git a/backend/gt68xx_high.c b/backend/gt68xx_high.c index 782b4f3..563323c 100644 --- a/backend/gt68xx_high.c +++ b/backend/gt68xx_high.c @@ -97,7 +97,7 @@ gt68xx_calibrator_new (SANE_Int width, cal->white_line = (double *) malloc (width * sizeof (double)); cal->black_line = (double *) malloc (width * sizeof (double)); - if (!cal->k_white || !cal->k_black | !cal->white_line || !cal->black_line) + if (!cal->k_white || !cal->k_black || !cal->white_line || !cal->black_line) { DBG (5, "gt68xx_calibrator_new: no memory for calibration data\n"); gt68xx_calibrator_free (cal); diff --git a/backend/gt68xx_mid.c b/backend/gt68xx_mid.c index 0d5cbe4..1d6a5c6 100644 --- a/backend/gt68xx_mid.c +++ b/backend/gt68xx_mid.c @@ -1006,6 +1006,7 @@ gt68xx_line_reader_new (GT68xx_Device * dev, DBG (3, "gt68xx_line_reader_new: cannot allocate line buffers: %s\n", sane_strstatus (status)); free (reader); + reader = NULL; return status; } @@ -1105,6 +1106,7 @@ gt68xx_line_reader_new (GT68xx_Device * dev, reader->params.depth); gt68xx_line_reader_free_delays (reader); free (reader); + reader = NULL; return SANE_STATUS_UNSUPPORTED; } @@ -1119,6 +1121,7 @@ gt68xx_line_reader_new (GT68xx_Device * dev, DBG (3, "gt68xx_line_reader_new: cannot allocate pixel buffer\n"); gt68xx_line_reader_free_delays (reader); free (reader); + reader = NULL; return SANE_STATUS_NO_MEM; } @@ -1135,6 +1138,7 @@ gt68xx_line_reader_new (GT68xx_Device * dev, free (reader->pixel_buffer); gt68xx_line_reader_free_delays (reader); free (reader); + reader = NULL; return status; } @@ -1150,6 +1154,13 @@ gt68xx_line_reader_free (GT68xx_Line_Reader * reader) DBG (6, "gt68xx_line_reader_free: enter\n"); + if (reader == NULL) + { + DBG (3, "gt68xx_line_reader_free: already freed\n"); + DBG (6, "gt68xx_line_reader_free: leave\n"); + return SANE_STATUS_INVAL; + } + gt68xx_line_reader_free_delays (reader); if (reader->pixel_buffer) @@ -1167,6 +1178,7 @@ gt68xx_line_reader_free (GT68xx_Line_Reader * reader) } free (reader); + reader = NULL; DBG (6, "gt68xx_line_reader_free: leave\n"); return status; diff --git a/backend/hp5400.h b/backend/hp5400.h index b0efb4f..78244e0 100644 --- a/backend/hp5400.h +++ b/backend/hp5400.h @@ -1,4 +1,5 @@ /* sane - Scanner Access Now Easy. + Copyright (C) 20020 Ralph Little <skelband@gmail.com> Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org> Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net> @@ -138,6 +139,16 @@ typedef struct } TScanParams; +/* + * Panel settings. We can read and set these. + * + */ +typedef struct +{ + SANE_Word copycount; // 0..99 LCD display value + SANE_Word bwcolour; // 1=Colour or 2=Black/White from scan type LEDs +} +TPanelInfo; #endif /* NO _HP5400_H_ */ diff --git a/backend/hp5400_internal.c b/backend/hp5400_internal.c index 34bf55d..1d81358 100644 --- a/backend/hp5400_internal.c +++ b/backend/hp5400_internal.c @@ -1,4 +1,5 @@ /* sane - Scanner Access Now Easy. + Copyright (C) 2020 Ralph Little <skelband@gmail.com> Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org> Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net> Copyright (c) 2003 Henning Meier-Geinitz, <henning@meier-geinitz.de> @@ -149,11 +150,138 @@ SetLamp (THWParams * pHWParams, int fLampOn) if (fLampOn) { if (WriteByte (pHWParams->iXferHandle, 0x0000, 0x01) == 0) - return 0; + return 0; } return -1; } + +HP5400_SANE_STATIC +int +GetSensors(THWParams * pHWParams, uint16_t *sensorMap) +{ + /* + * Read until we get 0. + * Max 10 iterations for safety. + * + */ + *sensorMap = 0; + uint16_t thisSensorMap = 0; + size_t iterCount = 10; + do + { + if (hp5400_command_read + (pHWParams->iXferHandle, CMD_GETSENSORS, sizeof (uint16_t), &thisSensorMap) < 0) + { + HP5400_DBG (DBG_MSG, "failed to read sensors\n"); + return -1; + } + *sensorMap |= thisSensorMap; + } while (iterCount-- && (thisSensorMap > 0)); + + return 0; +} + +HP5400_SANE_STATIC +int +GetPanelInfo (THWParams * pHWParams, TPanelInfo *panelInfo) +{ + struct PanelInfo info; + if (hp5400_command_read (pHWParams->iXferHandle, CMD_READPANEL, + sizeof(info), &info) < 0) + { + HP5400_DBG (DBG_MSG, "failed to read panel info\n"); + return -1; + } + + panelInfo->copycount = (SANE_Word)info.copycount; + panelInfo->bwcolour = (SANE_Word)info.bwcolour; + + return 0; +} + +HP5400_SANE_STATIC +int +SetCopyCount(THWParams * pHWParams, SANE_Word copyCount) +{ + + /* + * I don't know what most of these things are but it is + * necessary to send something sane otherwise we get an error from the scanner. + * I got these settings from a USB trace. + * Hopefully, we will learn what it is all about at some point + * and hopefully it doesn't screw with other settings. + * + */ + uint8_t packetImage[] = {0x02, 0x06, 0x32, 0x01, + 0xf2, 0x40, 0x16, 0x01, + 0x7b, 0x41, 0x16, 0x01, + 0xdc, 0x06, 0x32, 0x01, + 0xd7, 0x5b, 0x16, 0x01, + 0xac, 0x06, 0x32, 0x01, + 0xf8, 0xd7, 0x18, 0x01, + 0xd8, 0x06, 0x32, 0x01, + 0x2c, 0xf3, 0x12, 0x00, + 0x70, 0x8d, 0x18, 0x01, + 0x7b, 0x00, 0x00, 0x00}; + + struct PanelInfo workingInfo; + (void)memcpy(&workingInfo, packetImage, sizeof(workingInfo)); + + workingInfo.copycount = (uint8_t)copyCount; + + if (hp5400_command_write (pHWParams->iXferHandle, CMD_WRITEPANEL, + sizeof(workingInfo), &workingInfo) < 0) + { + HP5400_DBG (DBG_MSG, "failed to write panel info\n"); + return -1; + } + + return 0; +} + +HP5400_SANE_STATIC +int +SetColourBW(THWParams * pHWParams, SANE_Word colourBW) +{ + + /* + * I don't know what most of these things are but it is + * necessary to send something sane otherwise we get an error from the scanner. + * I got these settings from a USB trace. + * Hopefully, we will learn what it is all about at some point + * and hopefully it doesn't screw with other settings. + * + */ + uint8_t packetImage[] = {0x03, 0x06, 0x32, 0x01, + 0xf2, 0x40, 0x16, 0x01, + 0x7b, 0x41, 0x16, 0x01, + 0xdc, 0x06, 0x32, 0x01, + 0xd7, 0x5b, 0x16, 0x01, + 0xac, 0x06, 0x32, 0x01, + 0xf8, 0xd7, 0x18, 0x01, + 0xd8, 0x06, 0x32, 0x01, + 0x68, 0xf5, 0x12, 0x00, + 0x70, 0x8d, 0x18, 0x01, + 0x7b, 0x00, 0x00, 0x00}; + + struct PanelInfo workingInfo; + (void)memcpy(&workingInfo, packetImage, sizeof(workingInfo)); + + workingInfo.bwcolour = (uint8_t)colourBW; + + if (hp5400_command_write (pHWParams->iXferHandle, CMD_WRITEPANEL, + sizeof(workingInfo), &workingInfo) < 0) + { + HP5400_DBG (DBG_MSG, "failed to write panel info\n"); + return -1; + } + + return 0; +} + + + HP5400_SANE_STATIC int WarmupLamp (int iHandle) diff --git a/backend/hp5400_internal.h b/backend/hp5400_internal.h index 981ce0b..aa40da0 100644 --- a/backend/hp5400_internal.h +++ b/backend/hp5400_internal.h @@ -2,6 +2,7 @@ #define _HP5400_INTERNAL_H_ /* sane - Scanner Access Now Easy. + Copyright (C) 2020 Ralph Little <skelband@gmail.com> (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net> (c) 2003 Martijn van Oosterhout, kleptog@svana.org (c) 2002 Bertrik Sikken, bertrik@zonnet.nl @@ -73,6 +74,9 @@ #define CMD_SCANREQUEST 0x2505 /* This is for previews */ #define CMD_SCANREQUEST2 0x2500 /* This is for real scans */ #define CMD_SCANRESPONSE 0x3400 +#define CMD_GETSENSORS 0x2000 +#define CMD_READPANEL 0x2100 // Reads info from the scanner. BW/Col + Copy Count. Others, not sure. +#define CMD_WRITEPANEL 0x2200 // Ditto for setting. /* Testing stuff to make it work */ #define CMD_SETDPI 0x1500 /* ??? */ @@ -130,11 +134,40 @@ PACKED; struct ScanResponse { - uint16_t x1; /* Usually 0x0000 or 0x4000 */ - uint32_t transfersize; /* Number of bytes to be transferred */ - uint32_t xsize; /* Shape of returned bitmap */ - uint16_t ysize; /* Why does the X get more bytes? */ - uint16_t pad[2]; /* Zero padding to 16 bytes??? */ + uint16_t x1; /* Usually 0x0000 or 0x4000 */ + uint32_t transfersize; /* Number of bytes to be transferred */ + uint32_t xsize; /* Shape of returned bitmap */ + uint16_t ysize; /* Why does the X get more bytes? */ + uint16_t pad[2]; /* Zero padding to 16 bytes??? */ +} +PACKED; + +/* + * Note: this is the structure of the response we get from CMD_READPANEL. + * We only know about two items for the moment. The rest will be ignored + * until we understand it better. + * + * 44 bytes in total. + * + * Since we don't know what the other things mean, I will assume that they + * mean the same things for Get and SET. For SET, we will have to GET, change + * what we wish change and SET it back otherwise goodness knows what evil + * we will unleash. + * + * Note that for setting, different values in the buffer seem to apply betwen the copy count + * and the colour/BW switch setting. I don't know what that means at the moment. + * + * I'm calling it PanelInfo because I can't think of anything better. + * That may change as the other values are revealed. + * + */ +struct PanelInfo +{ + uint32_t unknown1[10]; + uint8_t unknown2; + uint8_t copycount; // 0..99 from the LCD display. + uint8_t bwcolour; // 1 or 2 from the Colour/BW leds. + uint8_t unknown3; } PACKED; @@ -158,6 +191,22 @@ SetLamp (THWParams * pHWParams, int fLampOn); HP5400_SANE_STATIC int +GetSensors (THWParams * pHWParams, uint16_t *sensorMap); + +HP5400_SANE_STATIC +int +GetPanelInfo (THWParams * pHWParams, TPanelInfo *panelInfo); + +HP5400_SANE_STATIC +int +SetCopyCount(THWParams * pHWParams, SANE_Word copyCount); + +HP5400_SANE_STATIC +int +SetColourBW(THWParams * pHWParams, SANE_Word colourBW); + +HP5400_SANE_STATIC +int WarmupLamp (int iHandle); HP5400_SANE_STATIC diff --git a/backend/hp5400_sane.c b/backend/hp5400_sane.c index e5fdf43..b6fa6da 100644 --- a/backend/hp5400_sane.c +++ b/backend/hp5400_sane.c @@ -1,4 +1,5 @@ /* sane - Scanner Access Now Easy. + Copyright (C) 2020 Ralph Little <skelband@gmail.com> Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org> Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net> @@ -71,28 +72,6 @@ #include "hp5400.h" -/* includes for data transfer methods */ -#include "hp5400.h" - -#ifdef STANDALONE -#include "hp5400_scanner.h" -#endif - -#if defined(LINUX_USB_SUPPORT) - #include "hp5400_linux.c" -#endif -#if defined(USCANNER_SUPPORT) - #include "hp5400_uscanner.c" -#endif -#if defined(LIBUSB_SUPPORT) - #include "hp5400_libusb.c" -#endif -#if defined(LIBIEEE1284_SUPPORT) - #include "hp5400_ieee1284.c" -#endif - - - /* other definitions */ #ifndef min #define min(A,B) (((A)<(B)) ? (A) : (B)) @@ -115,30 +94,91 @@ typedef enum { optCount = 0, + optDPI, + optGroupGeometry, optTLX, optTLY, optBRX, optBRY, - optDPI, - optGroupImage, + optGroupEnhancement, optGammaTableRed, /* Gamma Tables */ optGammaTableGreen, optGammaTableBlue, - optLast, /* Disable the offset code */ + optGroupSensors, - optGroupMisc, - optOffsetX, optOffsetY + optSensorScanTo, + optSensorWeb, + optSensorReprint, + optSensorEmail, + optSensorCopy, + optSensorMoreOptions, + optSensorCancel, + optSensorPowerSave, + optSensorCopiesUp, + optSensorCopiesDown, + optSensorColourBW, + optSensorColourBWState, + optSensorCopyCount, -/* put temporarily disabled options here after optLast */ -/* - optLamp, -*/ + // Unsupported as yet. + //optGroupMisc, + //optLamp, + //optCalibrate, + optLast, /* Disable the offset code */ } EOptionIndex; +/* + * Array mapping (optSensor* - optGroupSensors - 1) to the bit mask of the + * corresponding sensor bit that we get from the scanner. + * All sensor bits are reported as a complete 16-bit word with individual bits set + * to indicate that the sensor has been activated. + * They seem to be latched so that they are picked up on next query and a number + * of bits can be set in any one query. + * + */ + +#define SENSOR_BIT_SCAN 0x0400 +#define SENSOR_BIT_WEB 0x0200 +#define SENSOR_BIT_REPRINT 0x0002 +#define SENSOR_BIT_EMAIL 0x0080 +#define SENSOR_BIT_COPY 0x0040 +#define SENSOR_BIT_MOREOPTIONS 0x0004 +#define SENSOR_BIT_CANCEL 0x0100 +#define SENSOR_BIT_POWERSAVE 0x2000 +#define SENSOR_BIT_COPIESUP 0x0008 +#define SENSOR_BIT_COPIESDOWN 0x0020 +#define SENSOR_BIT_COLOURBW 0x0010 + + +uint16_t sensorMaskMap[] = +{ + SENSOR_BIT_SCAN, + SENSOR_BIT_WEB, + SENSOR_BIT_REPRINT, + SENSOR_BIT_EMAIL, + SENSOR_BIT_COPY, + SENSOR_BIT_MOREOPTIONS, + SENSOR_BIT_CANCEL, + + // Special buttons. + // These affect local machine settings, but we can still detect them being pressed. + SENSOR_BIT_POWERSAVE, + SENSOR_BIT_COPIESUP, + SENSOR_BIT_COPIESDOWN, + SENSOR_BIT_COLOURBW, + + // Extra entries to make the array up to the 16 possible bits. + 0x0000, // Unused + 0x0000, // Unused + 0x0000, // Unused + 0x0000, // Unused + 0x0000 // Unused +}; + typedef union { SANE_Word w; @@ -165,6 +205,8 @@ typedef struct int fScanning; /* TRUE if actively scanning */ int fCanceled; + + uint16_t sensorMap; /* Contains the current unreported sensor bits. */ } TScanner; @@ -191,18 +233,19 @@ static const SANE_Device **_pSaneDevList = 0; /* option constraints */ static const SANE_Range rangeGammaTable = {0, 65535, 1}; +static const SANE_Range rangeCopyCountTable = {0, 99, 1}; +static SANE_String_Const modeSwitchList[] = { + SANE_VALUE_SCAN_MODE_COLOR, + SANE_VALUE_SCAN_MODE_GRAY, + NULL +}; #ifdef SUPPORT_2400_DPI static const SANE_Int setResolutions[] = {6, 75, 150, 300, 600, 1200, 2400}; #else static const SANE_Int setResolutions[] = {5, 75, 150, 300, 600, 1200}; #endif -static const SANE_Range rangeXmm = {0, 220, 1}; -static const SANE_Range rangeYmm = {0, 300, 1}; -static const SANE_Range rangeXoffset = {0, 20, 1}; -static const SANE_Range rangeYoffset = {0, 70, 1}; -static const SANE_Int offsetX = 5; -static const SANE_Int offsetY = 52; - +static const SANE_Range rangeXmm = {0, 216, 1}; +static const SANE_Range rangeYmm = {0, 297, 1}; static void _InitOptions(TScanner *s) { @@ -248,8 +291,22 @@ static void _InitOptions(TScanner *s) pVal->w = (SANE_Word)optLast; break; + case optDPI: + pDesc->name = SANE_NAME_SCAN_RESOLUTION; + pDesc->title = SANE_TITLE_SCAN_RESOLUTION; + pDesc->desc = SANE_DESC_SCAN_RESOLUTION; + pDesc->unit = SANE_UNIT_DPI; + pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST; + pDesc->constraint.word_list = setResolutions; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->w = setResolutions[1]; + break; + + //--------------------------------- case optGroupGeometry: - pDesc->title = "Geometry"; + pDesc->name = SANE_NAME_GEOMETRY; + pDesc->title = SANE_TITLE_GEOMETRY; + pDesc->desc = SANE_DESC_GEOMETRY; pDesc->type = SANE_TYPE_GROUP; pDesc->size = 0; break; @@ -262,7 +319,7 @@ static void _InitOptions(TScanner *s) pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeXmm; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = rangeXmm.min + offsetX; + pVal->w = rangeXmm.min; break; case optTLY: @@ -273,7 +330,7 @@ static void _InitOptions(TScanner *s) pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeYmm; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = rangeYmm.min + offsetY; + pVal->w = rangeYmm.min; break; case optBRX: @@ -284,7 +341,7 @@ static void _InitOptions(TScanner *s) pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeXmm; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = rangeXmm.max + offsetX; + pVal->w = rangeXmm.max; break; case optBRY: @@ -295,22 +352,14 @@ static void _InitOptions(TScanner *s) pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeYmm; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = rangeYmm.max + offsetY; - break; - - case optDPI: - pDesc->name = SANE_NAME_SCAN_RESOLUTION; - pDesc->title = SANE_TITLE_SCAN_RESOLUTION; - pDesc->desc = SANE_DESC_SCAN_RESOLUTION; - pDesc->unit = SANE_UNIT_DPI; - pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST; - pDesc->constraint.word_list = setResolutions; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = setResolutions[1]; + pVal->w = rangeYmm.max; break; - case optGroupImage: - pDesc->title = SANE_I18N("Image"); + //--------------------------------- + case optGroupEnhancement: + pDesc->name = SANE_NAME_ENHANCEMENT; + pDesc->title = SANE_TITLE_ENHANCEMENT; + pDesc->desc = SANE_DESC_ENHANCEMENT; pDesc->type = SANE_TYPE_GROUP; pDesc->size = 0; break; @@ -348,34 +397,130 @@ static void _InitOptions(TScanner *s) pVal->wa = s->aGammaTableB; break; - case optGroupMisc: - pDesc->title = SANE_I18N("Miscellaneous"); + //--------------------------------- + case optGroupSensors: + pDesc->name = SANE_NAME_SENSORS; + pDesc->title = SANE_TITLE_SENSORS; pDesc->type = SANE_TYPE_GROUP; + pDesc->desc = SANE_DESC_SENSORS; pDesc->size = 0; break; - case optOffsetX: - pDesc->title = SANE_I18N("offset X"); - pDesc->desc = SANE_I18N("Hardware internal X position of the scanning area."); - pDesc->unit = SANE_UNIT_MM; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeXoffset; - pDesc->cap = SANE_CAP_SOFT_SELECT; - pVal->w = offsetX; + case optSensorScanTo: + pDesc->name = SANE_NAME_SCAN; + pDesc->title = SANE_TITLE_SCAN; + pDesc->desc = SANE_DESC_SCAN; + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; - case optOffsetY: - pDesc->title = SANE_I18N("offset Y"); - pDesc->desc = SANE_I18N("Hardware internal Y position of the scanning area."); - pDesc->unit = SANE_UNIT_MM; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeYoffset; - pDesc->cap = SANE_CAP_SOFT_SELECT; - pVal->w = offsetY; + case optSensorWeb: + pDesc->name = SANE_I18N("web"); + pDesc->title = SANE_I18N("Share-To-Web button"); + pDesc->desc = SANE_I18N("Scan an image and send it on the web"); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorReprint: + pDesc->name = SANE_I18N("reprint"); + pDesc->title = SANE_I18N("Reprint Photos button"); + pDesc->desc = SANE_I18N("Button for reprinting photos"); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorEmail: + pDesc->name = SANE_NAME_EMAIL; + pDesc->title = SANE_TITLE_EMAIL; + pDesc->desc = SANE_DESC_EMAIL; + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorCopy: + pDesc->name = SANE_NAME_COPY; + pDesc->title = SANE_TITLE_COPY; + pDesc->desc = SANE_DESC_COPY; + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorMoreOptions: + pDesc->name = SANE_I18N("more-options"); + pDesc->title = SANE_I18N("More Options button"); + pDesc->desc = SANE_I18N("Button for additional options/configuration"); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorCancel: + pDesc->name = SANE_NAME_CANCEL; + pDesc->title = SANE_TITLE_CANCEL; + pDesc->desc = SANE_DESC_CANCEL; + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorPowerSave: + pDesc->name = SANE_I18N("power-save"); + pDesc->title = SANE_I18N("Power Save button"); + pDesc->desc = SANE_I18N("Puts the scanner in an energy-conservation mode"); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorCopiesUp: + pDesc->name = SANE_I18N("copies-up"); + pDesc->title = SANE_I18N("Increase Copies button"); + pDesc->desc = SANE_I18N("Increase the number of copies"); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorCopiesDown: + pDesc->name = SANE_I18N("copies-down"); + pDesc->title = SANE_I18N("Decrease Copies button"); + pDesc->desc = SANE_I18N("Decrease the number of copies"); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; + case optSensorColourBW: + pDesc->name = SANE_I18N("color-bw"); + pDesc->title = SANE_I18N("Select color/BW button"); + pDesc->desc = SANE_I18N("Alternates between color and black/white scanning"); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorColourBWState: + pDesc->name = SANE_I18N("color-bw-state"); + pDesc->title = SANE_I18N("Read color/BW button state"); + pDesc->desc = SANE_I18N("Reads state of BW/colour panel setting"); + pDesc->type = SANE_TYPE_STRING; + pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; + pDesc->constraint.string_list = modeSwitchList; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + break; + + case optSensorCopyCount: + pDesc->name = SANE_I18N("copies-count"); + pDesc->title = SANE_I18N("Read copy count value"); + pDesc->desc = SANE_I18N("Reads state of copy count panel setting"); + pDesc->type = SANE_TYPE_INT; + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeCopyCountTable; + pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + break; #if 0 + case optGroupMisc: + pDesc->title = SANE_I18N("Miscellaneous"); + pDesc->type = SANE_TYPE_GROUP; + pDesc->size = 0; + break; + case optLamp: pDesc->name = "lamp"; pDesc->title = SANE_I18N("Lamp status"); @@ -385,8 +530,7 @@ static void _InitOptions(TScanner *s) /* switch the lamp on when starting for first the time */ pVal->w = SANE_TRUE; break; -#endif -#if 0 + case optCalibrate: pDesc->name = "calibrate"; pDesc->title = SANE_I18N("Calibrate"); @@ -467,7 +611,7 @@ sane_init (SANE_Int * piVersion, SANE_Auth_Callback pfnAuth) SANE_String_Const proper_str; int nline = 0; - /* prevent compiler from complaing about unused parameters */ + /* prevent compiler from complaining about unused parameters */ pfnAuth = pfnAuth; strcpy(usb_devfile, "/dev/usb/scanner0"); @@ -531,7 +675,6 @@ sane_init (SANE_Int * piVersion, SANE_Auth_Callback pfnAuth) *piVersion = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD); } - return SANE_STATUS_GOOD; } @@ -694,7 +837,7 @@ sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, /* Get options of type SANE_Word */ case optBRX: case optTLX: - *(SANE_Word *) pVal = s->aValues[n].w; /* Not needed anymore - s->aValues[optOffsetX].w; */ + *(SANE_Word *) pVal = s->aValues[n].w; HP5400_DBG (DBG_MSG, "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n, *(SANE_Word *) pVal); @@ -702,14 +845,12 @@ sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, case optBRY: case optTLY: - *(SANE_Word *) pVal = s->aValues[n].w; /* Not needed anymore - - s->aValues[optOffsetY].w; */ + *(SANE_Word *) pVal = s->aValues[n].w; HP5400_DBG (DBG_MSG, "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n, *(SANE_Word *) pVal); break; - case optOffsetX: - case optOffsetY: case optCount: case optDPI: HP5400_DBG (DBG_MSG, @@ -726,14 +867,94 @@ sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size); break; + case optSensorScanTo: + case optSensorWeb: + case optSensorReprint: + case optSensorEmail: + case optSensorCopy: + case optSensorMoreOptions: + case optSensorCancel: + case optSensorPowerSave: + case optSensorCopiesUp: + case optSensorCopiesDown: + case optSensorColourBW: + { + HP5400_DBG (DBG_MSG, "Reading sensor state\n"); + + uint16_t sensorMap; + if (GetSensors(&s->HWParams, &sensorMap) != 0) + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE could not retrieve sensors\n"); + return SANE_STATUS_IO_ERROR; + + } + + HP5400_DBG (DBG_MSG, "Sensor state=%x\n", sensorMap); + + // Add read flags to what we already have so that we can report them when requested. + s->sensorMap |= sensorMap; + + // Look up the mask based on the option number. + uint16_t mask = sensorMaskMap[n - optGroupSensors - 1]; + *(SANE_Word *) pVal = (s->sensorMap & mask)? 1:0; + s->sensorMap &= ~mask; + break; + } + + case optSensorCopyCount: + { + HP5400_DBG (DBG_MSG, "Reading copy count\n"); + + TPanelInfo panelInfo; + if (GetPanelInfo(&s->HWParams, &panelInfo) != 0) + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE could not retrieve panel info\n"); + return SANE_STATUS_IO_ERROR; + + } + + HP5400_DBG (DBG_MSG, "Copy count setting=%u\n", panelInfo.copycount); + *(SANE_Word *) pVal = panelInfo.copycount; + break; + } + + case optSensorColourBWState: + { + HP5400_DBG (DBG_MSG, "Reading BW/Colour setting\n"); + + TPanelInfo panelInfo; + if (GetPanelInfo(&s->HWParams, &panelInfo) != 0) + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE could not retrieve panel info\n"); + return SANE_STATUS_IO_ERROR; + + } + + HP5400_DBG (DBG_MSG, "BW/Colour setting=%u\n", panelInfo.bwcolour); + + // Just for safety: + if (panelInfo.bwcolour < 1) + { + panelInfo.bwcolour = 1; + } + else if (panelInfo.bwcolour > 2) + { + panelInfo.bwcolour = 2; + } + (void)strcpy((SANE_String)pVal, modeSwitchList[panelInfo.bwcolour - 1]); + break; + } + #if 0 /* Get options of type SANE_Bool */ case optLamp: GetLamp (&s->HWParams, &fLampIsOn); *(SANE_Bool *) pVal = fLampIsOn; break; -#endif -#if 0 + case optCalibrate: /* although this option has nothing to read, it's added here to avoid a warning when running scanimage --help */ @@ -761,26 +982,70 @@ sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, case optBRX: case optTLX: - info |= SANE_INFO_RELOAD_PARAMS; - s->ScanParams.iLines = 0; /* Forget actual image settings */ - s->aValues[n].w = *(SANE_Word *) pVal; /* Not needed anymore - + s->aValues[optOffsetX].w; */ - break; - - case optBRY: - case optTLY: - info |= SANE_INFO_RELOAD_PARAMS; - s->ScanParams.iLines = 0; /* Forget actual image settings */ - s->aValues[n].w = *(SANE_Word *) pVal; /* Not needed anymore - + s->aValues[optOffsetY].w; */ - break; - case optDPI: - info |= SANE_INFO_RELOAD_PARAMS; - s->ScanParams.iLines = 0; /* Forget actual image settings */ -#ifdef SUPPORT_2400_DPI - (s->aValues[n].w) = *(SANE_Word *) pVal; -#else - (s->aValues[n].w) = min (1200, *(SANE_Word *) pVal); -#endif - break; + { + // Check against legal values. + SANE_Word value = *(SANE_Word *) pVal; + if ((value < s->aOptions[n].constraint.range->min) || + (value > s->aOptions[n].constraint.range->max)) + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE out of range X value\n"); + return SANE_STATUS_INVAL; + } + + info |= SANE_INFO_RELOAD_PARAMS; + s->ScanParams.iLines = 0; /* Forget actual image settings */ + s->aValues[n].w = value; + break; + } + + case optBRY: + case optTLY: + { + // Check against legal values. + SANE_Word value = *(SANE_Word *) pVal; + if ((value < s->aOptions[n].constraint.range->min) || + (value > s->aOptions[n].constraint.range->max)) + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE out of range Y value\n"); + return SANE_STATUS_INVAL; + } + + info |= SANE_INFO_RELOAD_PARAMS; + s->ScanParams.iLines = 0; /* Forget actual image settings */ + s->aValues[n].w = value; + break; + } + + case optDPI: + { + // Check against legal values. + SANE_Word dpiValue = *(SANE_Word *) pVal; + + // First check too large. + SANE_Word maxRes = setResolutions[setResolutions[0]]; + if (dpiValue > maxRes) + { + dpiValue = maxRes; + } + else // Check smaller values: if not exact match, pick next higher available. + { + for (SANE_Int resIdx = 1; resIdx <= setResolutions[0]; resIdx++) + { + if (dpiValue <= setResolutions[resIdx]) + { + dpiValue = setResolutions[resIdx]; + break; + } + } + } + + info |= SANE_INFO_RELOAD_PARAMS; + s->ScanParams.iLines = 0; /* Forget actual image settings */ + (s->aValues[n].w) = dpiValue; + break; + } case optGammaTableRed: case optGammaTableGreen: @@ -788,6 +1053,70 @@ sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, HP5400_DBG (DBG_MSG, "Writing gamma table\n"); memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size); break; + + case optSensorColourBWState: + { + SANE_String bwColour = (SANE_String)pVal; + SANE_Word bwColourValue; + + if (strcmp(bwColour, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + bwColourValue = 1; + } + else if (strcmp(bwColour, SANE_VALUE_SCAN_MODE_GRAY) == 0) + { + bwColourValue = 2; + } + else + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE invalid colour/bw mode\n"); + return SANE_STATUS_INVAL; + } + + HP5400_DBG (DBG_MSG, "Setting BW/Colour state=%d\n", bwColourValue); + + /* + * Now write it with the other panel settings back to the scanner. + * + */ + if (SetColourBW(&s->HWParams, bwColourValue) != 0) + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE could not set colour/BW mode\n"); + return SANE_STATUS_IO_ERROR; + } + break; + } + + case optSensorCopyCount: + { + SANE_Word copyCount = *(SANE_Word *) pVal; + if (copyCount < 0) + { + copyCount = 0; + } + else if (copyCount > 99) + { + copyCount = 99; + } + + HP5400_DBG (DBG_MSG, "Setting Copy Count=%d\n", copyCount); + + /* + * Now write it with the other panel settings back to the scanner. + * + */ + if (SetCopyCount(&s->HWParams, copyCount) != 0) + { + HP5400_DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE could not set copy count\n"); + return SANE_STATUS_IO_ERROR; + + } + break; + } + /* case optLamp: fVal = *(SANE_Bool *)pVal; @@ -924,6 +1253,7 @@ sane_start (SANE_Handle h) s->ScanParams.iLinesRead = 0; s->fScanning = TRUE; + s->fCanceled = FALSE; return SANE_STATUS_GOOD; } @@ -944,6 +1274,11 @@ sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) /* nothing has been read for the moment */ *len = 0; + if (!s->fScanning || s->fCanceled) + { + HP5400_DBG (DBG_MSG, "sane_read: we're not scanning.\n"); + return SANE_STATUS_EOF; + } /* if we read all the lines return EOF */ diff --git a/backend/kodakaio.c b/backend/kodakaio.c index d5c2857..9a7a8b4 100644 --- a/backend/kodakaio.c +++ b/backend/kodakaio.c @@ -18,13 +18,13 @@ * The connection is now made in sane_start and ended in sane_cancel. * 01/01/13 Now with adf, the scan can be padded to make up the full page length, * or the page can terminate at the end of the paper. This is a selectable option. - * 25/11/12 Using avahi now for net autodiscovery. Use configure option --enable-avahi + * 25/11/12 Using avahi now for net autodiscovery. Use configure option --with-avahi to make sure it's enabled * 1/5/17 patched to use local pointer for avahi callback */ /* Packages to add to a clean ubuntu install -libavahi-common-dev +libavahi-client-dev libusb-dev libsnmp-dev @@ -32,13 +32,13 @@ convenient lines to paste export SANE_DEBUG_KODAKAIO=20 for ubuntu prior to 12.10 -./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-avahi --without-api-spec BACKENDS="kodakaio test" +./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var BACKENDS="kodakaio test" for ubuntu 12.10 -./configure --prefix=/usr --libdir=/usr/lib/i386-linux-gnu --sysconfdir=/etc --localstatedir=/var --enable-avahi --without-api-spec BACKENDS="kodakaio test" +./configure --prefix=/usr --libdir=/usr/lib/i386-linux-gnu --sysconfdir=/etc --localstatedir=/var BACKENDS="kodakaio test" for ubuntu 14.10 up to at least 17.04 -./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --sysconfdir=/etc --localstatedir=/var --enable-avahi --without-api-spec BACKENDS="kodakaio test" +./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --sysconfdir=/etc --localstatedir=/var BACKENDS="kodakaio test" If you want to use the test backend, for example with sane-troubleshoot, you should enable it in /etc/sane.d/dll.conf diff --git a/backend/kvs1025.c b/backend/kvs1025.c index c0e1fa3..fc89d87 100644 --- a/backend/kvs1025.c +++ b/backend/kvs1025.c @@ -34,8 +34,8 @@ #include "../include/sane/sanei_debug.h" -/* SANE backend operations, see Sane standard 1.04 documents (sane_dev.pdf) - for details */ +/* SANE backend operations, see SANE Standard for details + https://sane-project.gitlab.io/standard/ */ /* Init the KV-S1025 SANE backend. This function must be called before any other SANE function can be called. */ diff --git a/backend/kvs20xx_opt.c b/backend/kvs20xx_opt.c index e4b841b..3e82764 100644 --- a/backend/kvs20xx_opt.c +++ b/backend/kvs20xx_opt.c @@ -736,8 +736,8 @@ kvs20xx_init_window (struct scanner *s, struct window *wnd, int wnd_id) s->val[GAMMA_CORRECTION].s)]; wnd->mcd_lamp_dfeed_sens = str_index (lamp_list, s->val[LAMP].s) << 4 | 2; - wnd->document_size = (paper != 0) << 7 - | s->val[LENGTHCTL].b << 6 | s->val[LANDSCAPE].b << 4 | paper_val[paper]; + wnd->document_size = ((paper != 0) << 7) | (s->val[LENGTHCTL].b << 6) + | (s->val[LANDSCAPE].b << 4) | paper_val[paper]; wnd->ahead_deskew_dfeed_scan_area_fspeed_rshad = s->val[DBLFEED].b << 4 | s->val[FIT_TO_PAGE].b << 2; diff --git a/backend/kvs40xx_opt.c b/backend/kvs40xx_opt.c index c812f2c..8c37711 100644 --- a/backend/kvs40xx_opt.c +++ b/backend/kvs40xx_opt.c @@ -1344,9 +1344,9 @@ kvs40xx_init_window (struct scanner *s, struct window *wnd, int wnd_id) str_index (lamp_list, s->val[LAMP].s) << 4 | str_index (dfeed_sence_list, s->val[DFEED_SENCE].s); - wnd->document_size = (paper != 0) << 7 - | s->val[LENGTHCTL].b << 6 - | s->val[LONG_PAPER].b << 5 | s->val[LANDSCAPE].b << 4 | paper_val[paper]; + wnd->document_size = ((paper != 0) << 7) | (s->val[LENGTHCTL].b << 6) + | (s->val[LONG_PAPER].b << 5) | (s->val[LANDSCAPE].b << 4) + | paper_val[paper]; wnd->ahead_deskew_dfeed_scan_area_fspeed_rshad = (s->val[DESKEW].b || s->val[CROP].b ? 2 : 0) << 5 | /*XXX*/ diff --git a/backend/mustek.c b/backend/mustek.c index eafdb99..6a9aa86 100644 --- a/backend/mustek.c +++ b/backend/mustek.c @@ -4433,7 +4433,7 @@ init_options (Mustek_Scanner * s) s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range; - if (!s->hw->flags & MUSTEK_FLAG_THREE_PASS) + if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) /* 1-pass scanners don't support brightness in multibit mode */ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->val[OPT_BRIGHTNESS].w = 0; diff --git a/backend/net.c b/backend/net.c index df19192..4ad2e1b 100644 --- a/backend/net.c +++ b/backend/net.c @@ -67,7 +67,7 @@ #include <netinet/in.h> #include <netdb.h> /* OS/2 needs this _after_ <netinet/in.h>, grrr... */ -#ifdef WITH_AVAHI +#if WITH_AVAHI # include <avahi-client/client.h> # include <avahi-client/lookup.h> @@ -695,7 +695,7 @@ do_authorization (Net_Device * dev, SANE_String resource) } -#ifdef WITH_AVAHI +#if WITH_AVAHI static void net_avahi_resolve_callback (AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, @@ -964,7 +964,7 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) first_device = NULL; first_handle = NULL; -#ifdef WITH_AVAHI +#if WITH_AVAHI net_avahi_init (); #endif /* WITH_AVAHI */ @@ -1044,12 +1044,12 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) continue; } -#ifdef WITH_AVAHI +#if WITH_AVAHI avahi_threaded_poll_lock (avahi_thread); #endif /* WITH_AVAHI */ DBG (2, "sane_init: trying to add %s\n", device_name); add_device (device_name, 0); -#ifdef WITH_AVAHI +#if WITH_AVAHI avahi_threaded_poll_unlock (avahi_thread); #endif /* WITH_AVAHI */ } @@ -1095,12 +1095,12 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) if (host[0] == '\0') continue; #endif /* ENABLE_IPV6 */ -#ifdef WITH_AVAHI +#if WITH_AVAHI avahi_threaded_poll_lock (avahi_thread); #endif /* WITH_AVAHI */ DBG (2, "sane_init: trying to add %s\n", host); add_device (host, 0); -#ifdef WITH_AVAHI +#if WITH_AVAHI avahi_threaded_poll_unlock (avahi_thread); #endif /* WITH_AVAHI */ } @@ -1132,7 +1132,7 @@ sane_exit (void) DBG (1, "sane_exit: exiting\n"); -#ifdef WITH_AVAHI +#if WITH_AVAHI net_avahi_cleanup (); #endif /* WITH_AVAHI */ @@ -1518,11 +1518,11 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle) DBG (1, "sane_open: device %s not found, trying to register it anyway\n", nd_name); -#ifdef WITH_AVAHI +#if WITH_AVAHI avahi_threaded_poll_lock (avahi_thread); #endif /* WITH_AVAHI */ status = add_device (nd_name, &dev); -#ifdef WITH_AVAHI +#if WITH_AVAHI avahi_threaded_poll_unlock (avahi_thread); #endif /* WITH_AVAHI */ if (status != SANE_STATUS_GOOD) diff --git a/backend/pixma/.gitignore b/backend/pixma/.gitignore new file mode 100644 index 0000000..fe87a57 --- /dev/null +++ b/backend/pixma/.gitignore @@ -0,0 +1 @@ +pixma_sane_options.[ch] diff --git a/backend/pixma/pixma.c b/backend/pixma/pixma.c index f763496..c32907c 100644 --- a/backend/pixma/pixma.c +++ b/backend/pixma/pixma.c @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> @@ -67,6 +67,7 @@ # include "../include/sane/sanei_backend.h" # include "../include/sane/sanei_config.h" # include "../include/sane/sanei_jpeg.h" +# include "../include/sane/sanei_usb.h" #ifdef NDEBUG # define PDBG(x) @@ -91,7 +92,7 @@ */ #include "pixma_sane_options.h" -#define BUTTON_GROUP_SIZE ( opt_scan_resolution - opt_button_1 + 1 ) +#define BUTTON_GROUP_SIZE ( opt_adf_orientation - opt_button_1 + 1 ) #define BUTTON_GROUP_INDEX(x) ( x - opt_button_1 ) typedef struct pixma_sane_t @@ -317,6 +318,9 @@ update_button_state (pixma_sane_t * ss, SANE_Int * info) OVAL (opt_original).w = GET_EV_ORIGINAL(ev); OVAL (opt_target).w = GET_EV_TARGET(ev); OVAL (opt_scan_resolution).w = GET_EV_DPI(ev); + OVAL (opt_document_type).w = GET_EV_DOC(ev); + OVAL (opt_adf_status).w = GET_EV_STAT(ev); + OVAL (opt_adf_orientation).w = GET_EV_ORIENT(ev); } mark_all_button_options_cached(ss); } @@ -469,7 +473,7 @@ create_dpi_list (pixma_sane_t * ss) || ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_GRAY_16)) { /* 48 bits flatbed */ /*PDBG (pixma_dbg (4, "*create_dpi_list***** 48 bits flatbed mode\n"));*/ - min_dpi = 150; + min_dpi = (cfg->min_xdpi_16) ? cfg->min_xdpi_16 : 75; } /* set j for min. dpi @@ -541,6 +545,8 @@ control_scalar_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, option_descriptor_t *opt = &(OPT_IN_CTX[n]); SANE_Word val; + /* PDBG (pixma_dbg (4, "*control_scalar_option***** n = %u, a = %u\n", n, a)); */ + switch (a) { case SANE_ACTION_GET_VALUE: @@ -604,6 +610,8 @@ control_string_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, const SANE_String_Const *slist = opt->sod.constraint.string_list; SANE_String str = (SANE_String) v; + /* PDBG (pixma_dbg (4, "*control_string_option***** n = %u, a = %u\n", n, a)); */ + if (opt->sod.constraint_type == SANE_CONSTRAINT_NONE) { switch (a) @@ -656,6 +664,7 @@ static SANE_Status control_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, SANE_Int * info) { + SANE_Option_Descriptor *sod = &SOD (n); int result, i; const pixma_config_t *cfg; SANE_Int dummy; @@ -673,25 +682,59 @@ control_option (pixma_sane_t * ss, SANE_Int n, switch (n) { case opt_gamma_table: - switch (a) - { - case SANE_ACTION_SET_VALUE: - clamp_value (ss, n, v, info); - for (i = 0; i != 4096; i++) - ss->gamma_table[i] = *((SANE_Int *) v + i); - break; - case SANE_ACTION_GET_VALUE: - for (i = 0; i != 4096; i++) - *((SANE_Int *) v + i) = ss->gamma_table[i]; - break; - case SANE_ACTION_SET_AUTO: - pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, - sizeof (ss->gamma_table)); - break; - default: - return SANE_STATUS_UNSUPPORTED; - } - return SANE_STATUS_GOOD; + { + int table_size = sod->size / sizeof (SANE_Word); + int byte_cnt = table_size == 1024 ? 2 : 1; + + switch (a) + { + case SANE_ACTION_SET_VALUE: + PDBG (pixma_dbg (4, "*control_option***** opt_gamma_table: SANE_ACTION_SET_VALUE with %d values ***** \n", table_size)); + clamp_value (ss, n, v, info); + if (byte_cnt == 1) + { + for (i = 0; i < table_size; i++) + ss->gamma_table[i] = *((SANE_Int *) v + i); + } + else + { + for (i = 0; i < table_size; i++) + { + ss->gamma_table[i * 2] = *((SANE_Int *) v + i); + ss->gamma_table[i * 2 + 1] = *((uint8_t *)((SANE_Int *) v + i) + 1); + } + } + /* PDBG (pixma_hexdump (4, (uint8_t *)v, table_size * 4)); */ + /* PDBG (pixma_hexdump (4, ss->gamma_table, table_size * byte_cnt)); */ + break; + case SANE_ACTION_GET_VALUE: + PDBG (pixma_dbg (4, "*control_option***** opt_gamma_table: SANE_ACTION_GET_VALUE ***** \n")); + if (byte_cnt == 1) + { + for (i = 0; i < table_size; i++) + *((SANE_Int *) v + i) = ss->gamma_table[i]; + } + else + { + for (i = 0; i < table_size; i++) + { + *((SANE_Int *) v + i) = ss->gamma_table[i * 2]; + *((uint8_t *)((SANE_Int *) v + i) + 1) = ss->gamma_table[i * 2 + 1]; + } + } + break; + case SANE_ACTION_SET_AUTO: + PDBG (pixma_dbg (4, "*control_option***** opt_gamma_table: SANE_ACTION_SET_AUTO with gamma=%f ***** \n", + SANE_UNFIX (OVAL (opt_gamma).w))); + pixma_fill_gamma_table (SANE_UNFIX (OVAL (opt_gamma).w), + ss->gamma_table, table_size); + /* PDBG (pixma_hexdump (4, ss->gamma_table, table_size * byte_cnt)); */ + break; + default: + return SANE_STATUS_UNSUPPORTED; + } + return SANE_STATUS_GOOD; + } case opt_button_update: if (a == SANE_ACTION_SET_VALUE) @@ -709,6 +752,9 @@ control_option (pixma_sane_t * ss, SANE_Int n, case opt_original: case opt_target: case opt_scan_resolution: + case opt_document_type: + case opt_adf_status: + case opt_adf_orientation: /* poll scanner if option is not cached */ if (! ss->button_option_is_cached[ BUTTON_GROUP_INDEX(n) ] ) update_button_state (ss, info); @@ -744,15 +790,24 @@ control_option (pixma_sane_t * ss, SANE_Int n, { if (enable_option (ss, opt_gamma_table, OVAL (opt_custom_gamma).b)) *info |= SANE_INFO_RELOAD_OPTIONS; + if (OVAL (opt_custom_gamma).b) + sane_control_option (ss, opt_gamma_table, SANE_ACTION_SET_AUTO, + NULL, NULL); + } break; case opt_gamma: if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO) { - /* PDBG (pixma_dbg (4, "*control_option***** gamma = %f *\n", - SANE_UNFIX (OVAL (opt_gamma).w))); */ + int table_size = SOD (opt_gamma_table).size / sizeof(SANE_Word); + PDBG (pixma_dbg (4, "*control_option***** gamma = %f *\n", + SANE_UNFIX (OVAL (opt_gamma).w))); + PDBG (pixma_dbg (4, "*control_option***** table size = %d *\n", + (int)(SOD (opt_gamma_table).size / sizeof (SANE_Word)))); pixma_fill_gamma_table (SANE_UNFIX (OVAL (opt_gamma).w), - ss->gamma_table, sizeof (ss->gamma_table)); + ss->gamma_table, table_size); + /* PDBG (pixma_hexdump (4, ss->gamma_table, + table_size == 1024 ? 2048 : table_size)); */ } break; case opt_mode: @@ -826,8 +881,8 @@ print_scan_param (int level, const pixma_scan_param_t * sp) sp->line_size, sp->image_size, sp->channels, sp->depth); pixma_dbg (level, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n", sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h); - pixma_dbg (level, " gamma_table=%p source=%d\n", sp->gamma_table, - sp->source); + pixma_dbg (level, " gamma=%f gamma_table=%p source=%d\n", sp->gamma, + sp->gamma_table, sp->source); pixma_dbg (level, " adf-wait=%d\n", sp->adf_wait); } #endif @@ -872,7 +927,8 @@ calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp) sp->h = 1; sp->tpu_offset_added = 0; - sp->gamma_table = (OVAL (opt_custom_gamma).b) ? ss->gamma_table : NULL; + sp->gamma = SANE_UNFIX (OVAL (opt_gamma).w); + sp->gamma_table = ss->gamma_table; sp->source = ss->source_map[OVAL (opt_source).w]; sp->mode = ss->mode_map[OVAL (opt_mode).w]; sp->adf_pageid = ss->page_count; @@ -897,6 +953,8 @@ init_option_descriptors (pixma_sane_t * ss) cfg = pixma_get_config (ss->s); + /* PDBG (pixma_dbg (4, "*init_option_descriptors*****\n")); */ + /* setup range for the scan area. */ ss->xrange.min = SANE_FIX (0); ss->xrange.max = SANE_FIX (cfg->width / 75.0 * 25.4); @@ -944,11 +1002,32 @@ init_option_descriptors (pixma_sane_t * ss) /* Enable options that are available only in some scanners. */ if (cfg->cap & PIXMA_CAP_GAMMA_TABLE) { + SANE_Option_Descriptor *sod = &SOD (opt_gamma_table); + + /* some scanners have a large gamma table with 4096 entries */ + if (cfg->cap & PIXMA_CAP_GT_4096) + { + static const SANE_Range constraint_gamma_table_4096 = { 0,0xff,0 }; + sod->desc = SANE_I18N("Gamma-correction table with 4096 entries. In color mode this option equally affects the red, green, and blue channels simultaneously (i.e., it is an intensity gamma table)."); + sod->size = 4096 * sizeof(SANE_Word); + sod->constraint.range = &constraint_gamma_table_4096; + } + + /* PDBG (pixma_dbg (4, "*%s***** PIXMA_CAP_GAMMA_TABLE ***** \n", + __func__)); */ + /* PDBG (pixma_dbg (4, "%s: gamma_table_contraint.max = %d\n", + __func__, sod->constraint.range->max)); */ + /* PDBG (pixma_dbg (4, "%s: gamma_table_size = %d\n", + __func__, sod->size / sizeof(SANE_Word))); */ + + /* activate option gamma */ enable_option (ss, opt_gamma, SANE_TRUE); + sane_control_option (ss, opt_gamma, SANE_ACTION_SET_AUTO, + NULL, NULL); + /* activate option custom gamma table */ enable_option (ss, opt_custom_gamma, SANE_TRUE); sane_control_option (ss, opt_custom_gamma, SANE_ACTION_SET_AUTO, - NULL, NULL); - pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, 4096); + NULL, NULL); } enable_option (ss, opt_button_controlled, ((cfg->cap & PIXMA_CAP_EVENTS) != 0)); @@ -1597,6 +1676,7 @@ sane_exit (void) sane_close (first_scanner); cleanup_device_list (); pixma_cleanup (); + sanei_usb_exit (); } SANE_Status @@ -1624,7 +1704,11 @@ sane_open (SANE_String_Const name, SANE_Handle * h) nscanners = pixma_find_scanners (conf_devices, SANE_FALSE); if (nscanners == 0) return SANE_STATUS_INVAL; - if (name[0] == '\0') + + /* also get device id if we replay a xml file + * otherwise name contains the xml filename + * and further replay will fail */ + if (name[0] == '\0' || strstr (name, ".xml")) name = pixma_get_device_id (0); /* Have we already opened the scanner? */ @@ -1995,7 +2079,13 @@ sane_get_select_fd (SANE_Handle h, SANE_Int * fd) return SANE_STATUS_GOOD; } -/* +/* CAUTION! + * Remove generated files pixma_sane_options.[ch] after editing SANE option + * descriptors below OR do a 'make clean' OR manually generate them as described + * below. + * However, make drops the circular dependency and the files won't be generated + * again (see merge request sane-project/backends!491). + BEGIN SANE_Option_Descriptor rem ------------------------------------------- @@ -2037,15 +2127,15 @@ type group title Gamma type bool custom-gamma - default SANE_TRUE + default SANE_FALSE title @SANE_TITLE_CUSTOM_GAMMA desc @SANE_DESC_CUSTOM_GAMMA cap soft_select soft_detect automatic inactive -type int gamma-table[4096] - constraint (0,255,0) +type int gamma-table[1024] + constraint (0,0xffff,0) title @SANE_TITLE_GAMMA_VECTOR - desc @SANE_DESC_GAMMA_VECTOR + desc Gamma-correction table with 1024 entries. In color mode this option equally affects the red, green, and blue channels simultaneously (i.e., it is an intensity gamma table). cap soft_select soft_detect automatic inactive type fixed gamma @@ -2128,6 +2218,21 @@ type int scan-resolution title Scan resolution cap soft_detect advanced +type int document-type + default 0 + title Document type + cap soft_detect advanced + +type int adf-status + default 0 + title ADF status + cap soft_detect advanced + +type int adf-orientation + default 0 + title ADF orientation + cap soft_detect advanced + rem ------------------------------------------- type group title Extras @@ -2150,7 +2255,7 @@ type int adf-wait default 0 constraint (0,3600,1) title ADF Waiting Time - desc When set, the scanner searches the waiting time in seconds for a new document inserted into the automatic document feeder. + desc When set, the scanner waits upto the specified time in seconds for a new document inserted into the automatic document feeder. cap soft_select soft_detect automatic inactive rem ------------------------------------------- diff --git a/backend/pixma/pixma.h b/backend/pixma/pixma.h index c2df3cc..c9026a7 100644 --- a/backend/pixma/pixma.h +++ b/backend/pixma/pixma.h @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> @@ -119,8 +119,8 @@ typedef uint32_t uint32_t; /** \name Version of the driver */ /**@{*/ #define PIXMA_VERSION_MAJOR 0 -#define PIXMA_VERSION_MINOR 27 -#define PIXMA_VERSION_BUILD 0 +#define PIXMA_VERSION_MINOR 28 +#define PIXMA_VERSION_BUILD 5 /**@}*/ /** \name Error codes */ @@ -158,6 +158,10 @@ typedef uint32_t uint32_t; #define PIXMA_CAP_TPUIR ((1 << 11) | PIXMA_CAP_TPU) #define PIXMA_CAP_ADF_WAIT (1 << 12) #define PIXMA_CAP_ADF_JPEG (1 << 13) +#define PIXMA_CAP_GT_4096 (1 << 14) /* gamma table has 4096 8-bit values + * only generation 1 scanners + * usually gamma table has 1024 16-bit values + */ #define PIXMA_CAP_EXPERIMENT (1 << 31) /**@}*/ @@ -167,13 +171,19 @@ typedef uint32_t uint32_t; #define PIXMA_EV_ACTION_MASK (0xffffff) #define PIXMA_EV_BUTTON1 (1 << 24) #define PIXMA_EV_BUTTON2 (2 << 24) -#define PIXMA_EV_TARGET_MASK (0xff) -#define PIXMA_EV_ORIGINAL_MASK (0xff00) -#define PIXMA_EV_DPI_MASK (0xff0000) +#define PIXMA_EV_TARGET_MASK (0x0f) +#define PIXMA_EV_ORIGINAL_MASK (0x0f00) +#define PIXMA_EV_DPI_MASK (0x0f0000) +#define PIXMA_EV_DOC_MASK (0xf000) +#define PIXMA_EV_STAT_MASK (0xf00000) +#define PIXMA_EV_ORIENT_MASK (0xf0) #define GET_EV_TARGET(x) (x & PIXMA_EV_TARGET_MASK) #define GET_EV_ORIGINAL(x) ( (x & PIXMA_EV_ORIGINAL_MASK) >> 8 ) #define GET_EV_DPI(x) ( (x & PIXMA_EV_DPI_MASK) >> 16 ) +#define GET_EV_DOC(x) ( (x & PIXMA_EV_DOC_MASK) >> 12 ) +#define GET_EV_STAT(x) ( (x & PIXMA_EV_STAT_MASK) >> 20 ) +#define GET_EV_ORIENT(x) ( (x & PIXMA_EV_ORIENT_MASK) >> 4 ) /**@}*/ /** @} end of API group */ @@ -340,6 +350,9 @@ struct pixma_scan_param_t * specified by subdriver will be used. */ const uint8_t *gamma_table; + /** value for auto generated gamma table */ + double gamma; + /** \see #pixma_paper_source_t */ pixma_paper_source_t source; @@ -365,7 +378,8 @@ 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 min_xdpi; /**< Minimum horizontal resolution[DPI] */ + unsigned min_xdpi_16;/**< Minimum horizontal resolution[DPI] for 16-bit scans */ unsigned xdpi; /**< Maximum horizontal resolution[DPI] */ unsigned ydpi; /**< Maximum vertical resolution[DPI] */ unsigned adftpu_min_dpi; /**< Maximum horizontal resolution[DPI] for adf/tpu diff --git a/backend/pixma/pixma_bjnp.c b/backend/pixma/pixma_bjnp.c index 34ba918..4e83714 100644 --- a/backend/pixma/pixma_bjnp.c +++ b/backend/pixma/pixma_bjnp.c @@ -109,6 +109,13 @@ #ifndef SSIZE_MAX # define SSIZE_MAX LONG_MAX #endif +#ifndef HOST_NAME_MAX +# ifdef _POSIX_HOST_NAME_MAX +# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +# else +# define HOST_NAME_MAX 255 +# endif +#endif /* static data */ static bjnp_device_t device[BJNP_NO_DEVICES]; @@ -454,69 +461,6 @@ determine_scanner_serial (const char *hostname, const char * mac_address, char * } static int -bjnp_open_tcp (int devno) -{ - int sock; - int val; - 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", - host, port ) ); - - if ((sock = socket (get_protocol_family( addr ) , SOCK_STREAM, 0)) < 0) - { - PDBG (bjnp_dbg (LOG_CRIT, "bjnp_open_tcp: ERROR - Can not create socket: %s\n", - strerror (errno))); - return -1; - } - - val = 1; - setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof (val)); - -#if 0 - val = 1; - setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, &val, sizeof (val)); - - val = 1; -#endif - - /* - * Using TCP_NODELAY improves responsiveness, especially on systems - * with a slow loopback interface... - */ - - val = 1; - setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof (val)); - -/* - * Close this socket when starting another process... - */ - - fcntl (sock, F_SETFD, FD_CLOEXEC); - - while (connect_timeout > 0) - { - 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; - } - PDBG (bjnp_dbg - (LOG_CRIT, "bjnp_open_tcp: ERROR - Can not connect to scanner, giving up!")); - return -1; -} - -static int split_uri (const char *devname, char *method, char *host, char *port, char *args) { @@ -1565,6 +1509,7 @@ bjnp_init_device_structure(int dn, bjnp_sockaddr_t *sa, bjnp_protocol_defs_t *pr #endif device[dn].protocol = protocol_defs->protocol_version; device[dn].protocol_string = protocol_defs->proto_string; + device[dn].single_tcp_session = protocol_defs->single_tcp_session; device[dn].tcp_socket = -1; device[dn].addr = (bjnp_sockaddr_t *) malloc(sizeof ( bjnp_sockaddr_t) ); @@ -1694,6 +1639,98 @@ bjnp_recv_data (int devno, SANE_Byte * buffer, size_t start_pos, size_t * len) return SANE_STATUS_GOOD; } +static int +bjnp_open_tcp (int devno) +{ + int sock; + int val; + char my_hostname[HOST_NAME_MAX]; + char pid_str[64]; + bjnp_sockaddr_t *addr = device[devno].addr; + char host[BJNP_HOST_MAX]; + int port; + int connect_timeout = BJNP_TIMEOUT_TCP_CONNECT; + + if (device[devno].tcp_socket != -1) + { + PDBG (bjnp_dbg( LOG_DEBUG, "bjnp_open_tcp: socket alreeady opened, nothing to do\n")); + return 0; + } + get_address_info( addr, host, &port); + PDBG (bjnp_dbg (LOG_DEBUG, "bjnp_open_tcp: Setting up a TCP socket, dest: %s port %d\n", + host, port ) ); + + gethostname (my_hostname, HOST_NAME_MAX); + my_hostname[HOST_NAME_MAX - 1] = '\0'; + sprintf (pid_str, "Process ID = %d", getpid ()); + bjnp_send_job_details (devno, my_hostname, getusername (), pid_str); + + if ((sock = socket (get_protocol_family( addr ) , SOCK_STREAM, 0)) < 0) + { + PDBG (bjnp_dbg (LOG_CRIT, "bjnp_open_tcp: ERROR - Can not create socket: %s\n", + strerror (errno))); + return -1; + } + + val = 1; + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof (val)); + +#if 0 + val = 1; + setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, &val, sizeof (val)); + + val = 1; +#endif + + /* + * Using TCP_NODELAY improves responsiveness, especially on systems + * with a slow loopback interface... + */ + + val = 1; + setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof (val)); + +/* + * Close this socket when starting another process... + */ + + fcntl (sock, F_SETFD, FD_CLOEXEC); + + while (connect_timeout > 0) + { + if (connect + (sock, &(addr->addr), sa_size(device[devno].addr)) == 0) + { + device[devno].tcp_socket = sock; + PDBG( bjnp_dbg(LOG_INFO, "bjnp_open_tcp: created socket %d\n", 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; + } + PDBG (bjnp_dbg + (LOG_CRIT, "bjnp_open_tcp: ERROR - Can not connect to scanner, giving up!")); + return -1; +} + +static void bjnp_close_tcp(int devno) +{ + if ( device[devno].tcp_socket != -1) + { + PDBG( bjnp_dbg( LOG_INFO, "bjnp_close_tcp - closing tcp-socket %d\n", device[devno].tcp_socket)); + bjnp_finish_job (devno); + close (device[devno].tcp_socket); + device[devno].tcp_socket = -1; + } + else + { + PDBG( bjnp_dbg( LOG_INFO, "bjnp_close_tcp: socket not open, nothing to do.\n")); + } + device[devno].open = 0; +} + static BJNP_Status bjnp_allocate_device (SANE_String_Const devname, SANE_Int * dn, char *resulting_host) @@ -1762,7 +1799,7 @@ bjnp_allocate_device (SANE_String_Const devname, if (result != 0 ) { PDBG (bjnp_dbg (LOG_CRIT, "bjnp_allocate_device: ERROR - Cannot resolve host: %s port %s\n", host, port)); - return SANE_STATUS_INVAL; + return BJNP_STATUS_INVAL; } /* Check if a device number is already allocated to any of the scanner's addresses */ @@ -2273,6 +2310,13 @@ sanei_bjnp_open (SANE_String_Const devname, SANE_Int * dn) if ( (result != BJNP_STATUS_GOOD) && (result != BJNP_STATUS_ALREADY_ALLOCATED ) ) { return SANE_STATUS_INVAL; } + + if (device[*dn].single_tcp_session && bjnp_open_tcp (*dn) != 0) + { + PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_opening TCP connection failed.\n\n")); + return SANE_STATUS_INVAL; + } + PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_open done.\n\n")); return SANE_STATUS_GOOD; } @@ -2286,8 +2330,8 @@ sanei_bjnp_close (SANE_Int dn) { PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_close(%d):\n", dn)); - device[dn].open = 0; - sanei_bjnp_deactivate(dn); + bjnp_close_tcp( dn ); + PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_close done.\n\n")); } /** Activate BJNP device connection @@ -2298,21 +2342,13 @@ sanei_bjnp_close (SANE_Int dn) SANE_Status sanei_bjnp_activate (SANE_Int dn) { - char hostname[256]; - char pid_str[64]; - PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_activate (%d)\n", dn)); - gethostname (hostname, 256); - hostname[255] = '\0'; - sprintf (pid_str, "Process ID = %d", getpid ()); - - bjnp_send_job_details (dn, hostname, getusername (), pid_str); - - if (bjnp_open_tcp (dn) != 0) + if (!(device[dn].single_tcp_session) && bjnp_open_tcp (dn) != 0) { + PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_activate: open TCP connection failed.\n\n")); return SANE_STATUS_INVAL; } - + PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_activate done.\n\n")); return SANE_STATUS_GOOD; } @@ -2325,12 +2361,11 @@ SANE_Status sanei_bjnp_deactivate (SANE_Int dn) { PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_deactivate (%d)\n", dn)); - if ( device[dn].tcp_socket != -1) - { - bjnp_finish_job (dn); - close (device[dn].tcp_socket); - device[dn].tcp_socket = -1; - } + if (!device[dn].single_tcp_session) + { + bjnp_close_tcp(dn); + } + PDBG (bjnp_dbg (LOG_INFO, "sanei_bjnp_deactivate done.\n\n")); return SANE_STATUS_GOOD; } diff --git a/backend/pixma/pixma_bjnp_private.h b/backend/pixma/pixma_bjnp_private.h index edfb330..19ba496 100644 --- a/backend/pixma/pixma_bjnp_private.h +++ b/backend/pixma/pixma_bjnp_private.h @@ -131,13 +131,14 @@ typedef struct int default_port; char * proto_string; char * method_string; + int single_tcp_session; } bjnp_protocol_defs_t; bjnp_protocol_defs_t bjnp_protocol_defs[] = { - {PROTOCOL_BJNP, BJNP_PORT_SCAN,"BJNP", "bjnp"}, - {PROTOCOL_MFNP, MFNP_PORT_SCAN,"MFNP", "mfnp"}, - {PROTOCOL_NONE, -1, NULL, NULL} + {PROTOCOL_BJNP, BJNP_PORT_SCAN,"BJNP", "bjnp", SANE_FALSE}, + {PROTOCOL_MFNP, MFNP_PORT_SCAN,"MFNP", "mfnp", SANE_TRUE}, + {PROTOCOL_NONE, -1, NULL, NULL, SANE_FALSE} }; /* commands */ @@ -346,9 +347,10 @@ typedef struct device_s { int open; /* connection to scanner is opened */ - /* protocol version */ + /* protocol information */ int protocol; char *protocol_string; + char single_tcp_session; /* sockets */ diff --git a/backend/pixma/pixma_common.c b/backend/pixma/pixma_common.c index 7b7ecec..436311a 100644 --- a/backend/pixma/pixma_common.c +++ b/backend/pixma/pixma_common.c @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> @@ -48,11 +48,18 @@ #include <stdlib.h> #include <string.h> #include <stdarg.h> +#include <ctype.h> #include <math.h> /* pow(C90) */ #include <sys/time.h> /* gettimeofday(4.3BSD) */ #include <unistd.h> /* usleep */ +#if defined(HAVE_LIBXML2) +# include <libxml/parser.h> +#else +# error "The pixma backend requires libxml2" +#endif + #include "pixma_rename.h" #include "pixma_common.h" #include "pixma_io.h" @@ -143,6 +150,24 @@ pixma_hexdump (int level, const void *d_, unsigned len) p++; } } + for (c = 0; c < 4; c++) + { + p[0] = ' '; + p++; + } + for (c = 0; c != 16 && (ofs + c) < plen; c++) + { + if (isprint(d[ofs + c])) + p[0] = d[ofs + c]; + else + p[0] = '.'; + p++; + if (c == 7) + { + p[0] = ' '; + p++; + } + } p[0] = '\0'; pixma_dbg (level, "%s\n", line); ofs += c; @@ -335,7 +360,7 @@ pixma_r_to_ir (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c) /* convert 24/48 bit RGB to 8/16 bit grayscale * - * Formular: g = (R + G + B) / 3 + * Formular: Y' = 0,2126 R' + 0,7152 G' + 0,0722 B' * * sptr: source color scale buffer * gptr: destination gray scale buffer @@ -345,19 +370,28 @@ pixma_r_to_ir (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c) uint8_t * pixma_rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w, unsigned c) { - unsigned i, j, g; + unsigned i, g; /* PDBG (pixma_dbg (4, "*pixma_rgb_to_gray*****\n")); */ for (i = 0; i < w; i++) { - for (j = 0, g = 0; j < 3; j++) - { - g += *sptr++; - if (c == 6) g += (*sptr++ << 8); /* 48 bit RGB: high byte */ + if (c == 6) + { /* 48 bit RGB */ + unsigned r = sptr[0] + (sptr[1] << 8); + unsigned y = sptr[2] + (sptr[3] << 8); + unsigned b = sptr[4] + (sptr[5] << 8); + + g = (r * 2126) + (y * 7152) + (b * 722); + sptr += 6; } + else + { /* 24 bit RGB */ + g = (sptr[0] * 2126) + (sptr[1] * 7152) + (sptr[2] * 722); + sptr += 3; + } + g /= 10000; /* 8 and 16 bit gray */ - g /= 3; /* 8 or 16 bit gray */ *gptr++ = g; if (c == 6) *gptr++ = (g >> 8); /* 16 bit gray: high byte */ } @@ -846,7 +880,7 @@ pixma_scan (pixma_t * s, pixma_scan_param_t * sp) sp->line_size, sp->image_size, sp->channels, sp->depth); pixma_dbg (3, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n", sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h); - pixma_dbg (3, " gamma_table=%p source=%d\n", sp->gamma_table, sp->source); + pixma_dbg (3, " gamma=%f gamma_table=%p source=%d\n", sp->gamma, sp->gamma_table, sp->source); pixma_dbg (3, " threshold=%d threshold_curve=%d\n", sp->threshold, sp->threshold_curve); pixma_dbg (3, " adf-wait=%d\n", sp->adf_wait); pixma_dbg (3, " ADF page count: %d\n", sp->adf_pageid); @@ -1152,14 +1186,35 @@ pixma_get_config (pixma_t * s) void pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n) { - int i; + unsigned i; double r_gamma = 1.0 / gamma; - double out_scale = 255.0; double in_scale = 1.0 / (n - 1); - for (i = 0; (unsigned) i != n; i++) + /* 8-bits gamma table + * for generation 1 scanners + */ + if (n == 4096) + { + double out_scale = 255.0; + + for (i = 0; (unsigned) i != n; i++) + { + table[i] = (int) (out_scale * pow (i * in_scale, r_gamma) + 0.5); + } + } + + /* 16-bits gamma table */ + else { - table[i] = (int) (out_scale * pow (i * in_scale, r_gamma) + 0.5); + double out_scale = 65535.0; + uint16_t value; + + for (i = 0; i < n; i++) + { + value = (uint16_t) (out_scale * pow (i * in_scale, r_gamma) + 0.5); + table[2 * i] = (uint8_t) (value & 0xff); + table[2 * i + 1] = (uint8_t) (value >> 8); + } } } @@ -1185,3 +1240,97 @@ pixma_get_device_status (pixma_t * s, pixma_device_status_t * status) memset (status, 0, sizeof (*status)); return s->ops->get_status (s, status); } + +static const char * +format_xml_response(const char *resp_details) +{ + if (strcmp(resp_details, "DeviceBusy") == 0) + /* https://cromwell-intl.com/open-source/canon-pixma-printer-scanner.html */ + return "DeviceBusy - Device not initialized (yet). " \ + "Please check the USB power, try a different port or install the Ink Cartridges if the device supports them."; + else if (strcmp(resp_details, "ScannerCarriageLockError") == 0) + return "ScannerCarriageLockError - Please consult the manual to unlock the Carriage Lock."; + else if (strcmp(resp_details, "PCScanning") == 0) + return "PCScanning - Previous scan attempt was not completed. Try disconnecting and reconnecting the scanner. " \ + "If the problem persists, consider reporting it as a bug at http://www.sane-project.org/bugs.html."; + else if (strcmp(resp_details, "DeviceCheckError") == 0) + return "DeviceCheckError - Device detected a fault. Contact the repair center."; + else + return resp_details; +} + +int +pixma_parse_xml_response(const char *xml_message) +{ + int status = PIXMA_EPROTO; + xmlDoc *doc = NULL; + xmlNode *node = NULL; + xmlChar *content = NULL; + + doc = xmlReadMemory(xml_message, strlen(xml_message), "mem:device-resp.xml", NULL, 0); + if (doc == NULL) { + PDBG(pixma_dbg(10, "unable to parse xml response\n")); + status = PIXMA_EINVAL; + goto clean; + } + + node = xmlDocGetRootElement(doc); + if (node == NULL) { + status = PIXMA_EPROTO; + goto clean; + } + + /* /cmd */ + for (; node; node = node->next) { + if (strcmp((const char*)node->name, "cmd") == 0) + break; + } + if (!node) { + status = PIXMA_EPROTO; + goto clean; + } + + /* /cmd/contents */ + for (node = node->children; node; node = node->next) { + if (strcmp((const char*)node->name, "contents") == 0) + break; + } + if (!node) { + status = PIXMA_EPROTO; + goto clean; + } + + /* /cmd/contents/param_set */ + for (node = node->children; node; node = node->next) { + if (strcmp((const char*)node->name, "param_set") == 0) + break; + } + if (!node) { + status = PIXMA_EPROTO; + goto clean; + } + + /* /cmd/contents/param_set/response... */ + for (node = node->children; node; node = node->next) + { + if (strcmp((const char*)node->name, "response") == 0) { + content = xmlNodeGetContent(node); + if (strcmp((const char*)content, "OK") == 0) + status = PIXMA_STATUS_OK; + else + status = PIXMA_EINVAL; + xmlFree(content); + } else if (strcmp((const char*)node->name, "response_detail") == 0) { + content = xmlNodeGetContent(node); + if (strlen((const char*)content) > 0) { + PDBG(pixma_dbg(0, "device response: %s\n", + format_xml_response((const char*)content))); + } + xmlFree(content); + } + } + +clean: + xmlFreeDoc(doc); + return status; +} diff --git a/backend/pixma/pixma_common.h b/backend/pixma/pixma_common.h index c0ed4ba..3e4e5bd 100644 --- a/backend/pixma/pixma_common.h +++ b/backend/pixma/pixma_common.h @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> This file is part of the SANE package. @@ -205,6 +205,7 @@ uint8_t *pixma_newcmd (pixma_cmdbuf_t *, unsigned cmd, int pixma_exec (pixma_t *, pixma_cmdbuf_t *); int pixma_exec_short_cmd (pixma_t *, pixma_cmdbuf_t *, unsigned cmd); int pixma_map_status_errno (unsigned status); +int pixma_parse_xml_response(const char *xml_message); /**@}*/ #define pixma_fill_checksum(start, end) do { \ diff --git a/backend/pixma/pixma_imageclass.c b/backend/pixma/pixma_imageclass.c index ce0c37d..be483b2 100644 --- a/backend/pixma/pixma_imageclass.c +++ b/backend/pixma/pixma_imageclass.c @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2007-2009 Nicolas Martin, <nicols-guest at alioth dot debian dot org> Copyright (C) 2008 Dennis Lou, dlou 99 at yahoo dot com @@ -105,6 +105,7 @@ #define MF220_PID 0x27a8 #define MF210_PID 0x27a9 #define MF620_PID 0x27b4 +#define MF720_PID 0x27b5 #define MF410_PID 0x27c0 #define MF510_PID 0x27c2 #define MF230_PID 0x27d1 @@ -122,6 +123,7 @@ #define MF743_PID 0x27fc #define MF640_PID 0x27fe #define MF645_PID 0x27fd +#define MF440_PID 0x2823 enum iclass_state_t @@ -915,7 +917,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 */ \ + 0, 0, /* min_xdpi & min_xdpi_16 not used in this subdriver */ \ dpi, dpi, /* xdpi, ydpi */ \ 0, /* adftpu_min_dpi not used in this subdriver */ \ adftpu_max_dpi, /* adftpu_max_dpi */ \ @@ -961,6 +963,7 @@ const pixma_config_t pixma_iclass_devices[] = { DEV ("Canon i-SENSYS MF220 Series", "MF220", MF220_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), /* max. w = 216mm */ DEV ("Canon i-SENSYS MF210 Series", "MF210", MF210_PID, 600, 0, 637, 1050, PIXMA_CAP_ADF), /* max. w = 216mm */ DEV ("Canon i-SENSYS MF620 Series", "MF620", MF620_PID, 600, 0, 637, 1050, PIXMA_CAP_ADF), + DEV ("Canon i-SENSYS MF720 Series", "MF720", MF720_PID, 600, 300, 637, 877, PIXMA_CAP_ADFDUP), DEV ("Canon i-SENSYS MF410 Series", "MF410", MF410_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), /* max. w = 216mm */ DEV ("Canon i-SENSYS MF510 Series", "MF510", MF510_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), DEV ("Canon i-SENSYS MF230 Series", "MF230", MF230_PID, 600, 0, 637, 1050, PIXMA_CAP_ADF), /* max. w = 216mm */ @@ -973,7 +976,7 @@ const pixma_config_t pixma_iclass_devices[] = { 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 MF110/910 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, PIXMA_CAP_ADFDUP), @@ -981,5 +984,6 @@ const pixma_config_t pixma_iclass_devices[] = { 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 ("Canon i-SENSYS MF440 Series", "MF440", MF440_PID, 600, 300, 637, 877, PIXMA_CAP_ADFDUP), DEV (NULL, NULL, 0, 0, 0, 0, 0, 0) }; diff --git a/backend/pixma/pixma_io_sanei.c b/backend/pixma/pixma_io_sanei.c index c30b404..c7b7a29 100644 --- a/backend/pixma/pixma_io_sanei.c +++ b/backend/pixma/pixma_io_sanei.c @@ -1,7 +1,7 @@ /* SANE - Scanner Access Now Easy. * For limitations, see function sanei_usb_get_vendor_product(). - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> This file is part of the SANE package. diff --git a/backend/pixma/pixma_mp150.c b/backend/pixma/pixma_mp150.c index 3973702..b438c1b 100644 --- a/backend/pixma/pixma_mp150.c +++ b/backend/pixma/pixma_mp150.c @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2007-2009 Nicolas Martin, <nicols-guest at alioth dot debian dot org> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> @@ -85,7 +85,6 @@ 4096 = size of gamma table. 24 = header + checksum */ #define IMAGE_BLOCK_SIZE (512*1024) #define CMDBUF_SIZE (4096 + 24) -#define DEFAULT_GAMMA 2.0 /***** Gamma different from 1.0 is potentially impacting color profile generation *****/ #define UNKNOWN_PID 0xffff @@ -282,8 +281,10 @@ #define TS8230_PID 0x185b #define TS9580_PID 0x185d #define TR9530_PID 0x185e +#define G7000_PID 0x1863 #define G6000_PID 0x1865 #define G6080_PID 0x1866 +#define GM4000_PID 0x1869 #define XK80_PID 0x1873 #define TS5300_PID 0x188b #define TS5380_PID 0x188c @@ -321,8 +322,6 @@ <ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\ </ivec:param_set></ivec:contents></cmd>" -#define XML_OK "<ivec:response>OK</ivec:response>" - enum mp150_state_t { state_idle, @@ -460,7 +459,7 @@ send_xml_dialog (pixma_t * s, const char * xml_message) PDBG (pixma_dbg (10, "XML message sent to scanner:\n%s\n", xml_message)); PDBG (pixma_dbg (10, "XML response back from scanner:\n%s\n", mp->cb.buf)); - return (strcasestr ((const char *) mp->cb.buf, XML_OK) != NULL); + return pixma_parse_xml_response((const char*)mp->cb.buf) == PIXMA_STATUS_OK; } static int @@ -567,42 +566,45 @@ send_gamma_table (pixma_t * s) const uint8_t *lut = s->param->gamma_table; uint8_t *data; - if (mp->generation == 1) + if (s->cfg->cap & PIXMA_CAP_GT_4096) { data = pixma_newcmd (&mp->cb, cmd_gamma, 4096 + 8, 0); data[0] = (s->param->channels == 3) ? 0x10 : 0x01; pixma_set_be16 (0x1004, data + 2); if (lut) - memcpy (data + 4, lut, 4096); + { + /* PDBG (pixma_dbg (4, "*send_gamma_table***** Use 4096 bytes from LUT ***** \n")); */ + /* PDBG (pixma_hexdump (4, lut, 4096)); */ + memcpy (data + 4, lut, 4096); + } else - pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096); + { + /* fallback: we should never see this */ + PDBG (pixma_dbg (4, "*send_gamma_table***** Generate 4096 bytes Table with %f ***** \n", + s->param->gamma)); + pixma_fill_gamma_table (s->param->gamma, data + 4, 4096); + /* PDBG (pixma_hexdump (4, data + 4, 4096)); */ + } } else { - /* FIXME: Gamma table for 2nd generation: 1024 * uint16_le */ - data = pixma_newcmd (&mp->cb, cmd_gamma, 2048 + 8, 0); + /* Gamma table for 2nd+ generation: 1024 * uint16_le */ + data = pixma_newcmd (&mp->cb, cmd_gamma, 1024 * 2 + 8, 0); data[0] = 0x10; pixma_set_be16 (0x0804, data + 2); if (lut) { - int i; - for (i = 0; i < 1024; i++) - { - int j = (i << 2) + (i >> 8); - data[4 + 2 * i + 0] = lut[j]; - data[4 + 2 * i + 1] = lut[j]; - } + /* PDBG (pixma_dbg (4, "*send_gamma_table***** Use 1024 * 2 bytes from LUT ***** \n")); */ + /* PDBG (pixma_hexdump (4, lut, 1024 * 2)); */ + memcpy (data + 4, lut, 1024 * 2); } else { - int i; - pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 2048); - for (i = 0; i < 1024; i++) - { - int j = (i << 1) + (i >> 9); - data[4 + 2 * i + 0] = data[4 + j]; - data[4 + 2 * i + 1] = data[4 + j]; - } + /* fallback: we should never see this */ + PDBG (pixma_dbg (4, "*send_gamma_table***** Generate 1024 * 2 Table with %f ***** \n", + s->param->gamma)); + pixma_fill_gamma_table (s->param->gamma, data + 4, 1024); + /* PDBG (pixma_hexdump (4, data + 4, 1024 * 2)); */ } } return pixma_exec (s, &mp->cb); @@ -631,6 +633,12 @@ calc_raw_width (const mp150_t * mp, const pixma_scan_param_t * param) return raw_width; } +static int +is_gray_16 (pixma_t * s) +{ + return (s->param->mode == PIXMA_SCAN_MODE_GRAY_16); +} + static unsigned get_cis_line_size (pixma_t * s) { @@ -640,7 +648,9 @@ get_cis_line_size (pixma_t * s) __func__, s->param->line_size, s->param->w, s->param->wx, mp->scale));*/ return (s->param->wx ? s->param->line_size / s->param->w * s->param->wx - : s->param->line_size) * mp->scale; + : s->param->line_size) + * mp->scale + * (is_gray_16(s) ? 3 : 1); } static int @@ -705,10 +715,12 @@ send_scan_param (pixma_t * s) pixma_set_be32 (y, data + 0x10); pixma_set_be32 (wx, data + 0x14); pixma_set_be32 (h, data + 0x18); - data[0x1c] = (s->param->channels != 1) ? 0x08 : 0x04; + /*PDBG (pixma_dbg (4, "*send_scan_param gen. 3+ ***** Setting: channels=%hi depth=%hi ***** \n", + s->param->channels, s->param->depth));*/ + data[0x1c] = ((s->param->channels != 1) || (is_gray_16(s)) ? 0x08 : 0x04); data[0x1d] = ((s->param->software_lineart) ? 8 : s->param->depth) - * s->param->channels; /* bits per pixel */ + * (is_gray_16(s) ? 3 : 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; @@ -902,7 +914,8 @@ handle_interrupt (pixma_t * s, int timeout) || s->cfg->pid == MX920_PID || s->cfg->pid == MB2300_PID || s->cfg->pid == MB5000_PID - || s->cfg->pid == MB5400_PID) + || s->cfg->pid == MB5400_PID + || s->cfg->pid == TR4500_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 @@ -910,18 +923,45 @@ handle_interrupt (pixma_t * s, int timeout) * target = format; original = size; scan-resolution = dpi */ { if (buf[7] & 1) - s->events = PIXMA_EV_BUTTON1 | buf[11] | buf[10]<<8 | buf[12]<<16; /* color scan */ + { + /* color scan */ + s->events = PIXMA_EV_BUTTON1 | (buf[11] & 0x0f) | (buf[10] & 0x0f) << 8 + | (buf[12] & 0x0f) << 16; + } if (buf[7] & 2) - s->events = PIXMA_EV_BUTTON2 | buf[11] | buf[10]<<8 | buf[12]<<16; /* b/w scan */ + { + /* b/w scan */ + s->events = PIXMA_EV_BUTTON2 | (buf[11] & 0x0f) | (buf[10] & 0x0f) << 8 + | (buf[12] & 0x0f) << 16; + } + + /* some scanners provide additional information: + * document type in buf[6] 01=Document; 02=Photo; 03=Auto Scan + * ADF status in buf[8] 01 = ADF empty; 02 = ADF filled + * ADF orientation in buf[16] 01=Portrait; 02=Landscape */ + if (s->cfg->pid == TR4500_PID) + { + s->events |= (buf[6] & 0x0f) << 12; + s->events |= (buf[8] & 0x0f) << 20; + s->events |= (buf[16] & 0x0f) << 4; + } } else if (s->cfg->pid == LIDE300_PID || s->cfg->pid == LIDE400_PID) /* unknown value in buf[4] - * target in buf[0x13] - * always set button-1 */ + * target in buf[0x13] 01=copy; 02=auto; 03=send; 05=start PDF; 06=finish PDF + * "Finish PDF" is Button-2, all others are Button-1 */ { - if (buf[0x13]) - s->events = PIXMA_EV_BUTTON1 | buf[0x13]; + if (buf[0x13] == 0x06) + { + /* button 2 = cancel / end scan */ + s->events = PIXMA_EV_BUTTON2 | (buf[0x13] & 0x0f); + } + else if (buf[0x13]) + { + /* button 1 = start scan */ + s->events = PIXMA_EV_BUTTON1 | (buf[0x13] & 0x0f); + } } else /* button no. in buf[0] @@ -937,9 +977,15 @@ handle_interrupt (pixma_t * s, int timeout) if (buf[9] & 2) query_status (s); if (buf[0] & 2) - s->events = PIXMA_EV_BUTTON2 | buf[1] | ((buf[0] & 0xf0) << 4); /* b/w scan */ + { + /* b/w scan */ + s->events = PIXMA_EV_BUTTON2 | (buf[1] & 0x0f) | (buf[0] & 0xf0) << 4; + } if (buf[0] & 1) - s->events = PIXMA_EV_BUTTON1 | buf[1] | ((buf[0] & 0xf0) << 4); /* color scan */ + { + /* color scan */ + s->events = PIXMA_EV_BUTTON1 | (buf[1] & 0x0f) | ((buf[0] & 0xf0) << 4); + } } return 1; } @@ -1062,7 +1108,7 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) } /* process image sizes */ - c = s->param->channels + c = (is_gray_16(s) ? 3 : 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 */ @@ -1104,7 +1150,6 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) * MP220, MX360 and generation 5 scanners are exceptions */ if (n > 1 && s->cfg->pid != MP220_PID - && s->cfg->pid != MP490_PID && s->cfg->pid != MX360_PID && (mp->generation < 5 /* generation 5 scanners *with* special image format */ @@ -1136,6 +1181,9 @@ post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) /* 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 16bit gray */ + else if (is_gray_16(s)) + cptr = gptr = pixma_rgb_to_gray (gptr, cptr, s->param->w, c); else cptr += cw; } @@ -1218,22 +1266,41 @@ mp150_check_param (pixma_t * s, pixma_scan_param_t * sp) { mp150_t *mp = (mp150_t *) s->subdriver; - /* PDBG (pixma_dbg (4, "*mp150_check_param***** Initially: 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)); */ + /* PDBG (pixma_dbg (4, "*mp150_check_param***** Initially: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u, gamma=%f *****\n", + sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx, sp->gamma)); */ - /* MP150 only supports 8 bit per channel in color and grayscale mode */ - if (sp->depth != 1) - { - sp->software_lineart = 0; + sp->channels = 3; + sp->software_lineart = 0; + switch (sp->mode) + { + /* standard scan modes + * 8 bit per channel in color and grayscale mode */ + case PIXMA_SCAN_MODE_GRAY: + sp->channels = 1; + /* fall through */ + case PIXMA_SCAN_MODE_COLOR: sp->depth = 8; - } - else - { - /* software lineart */ + break; + /* extended scan modes for 48 bit flatbed scanners + * 16 bit per channel in color and grayscale mode */ + case PIXMA_SCAN_MODE_GRAY_16: + sp->channels = 1; + sp->depth = 16; + break; + case PIXMA_SCAN_MODE_COLOR_48: + sp->channels = 3; + sp->depth = 16; + break; + /* software lineart + * 1 bit per channel */ + case PIXMA_SCAN_MODE_LINEART: sp->software_lineart = 1; - sp->depth = 1; sp->channels = 1; - } + sp->depth = 1; + break; + default: + break; + } /* for software lineart w must be a multiple of 8 */ if (sp->software_lineart == 1 && sp->w % 8) @@ -1596,6 +1663,7 @@ static const pixma_scan_ops_t pixma_mp150_ops = { 0, /* iface */ \ &pixma_mp150_ops, /* ops */ \ min_dpi, /* min_xdpi */ \ + 0, /* min_xdpi_16 not used in this subdriver */ \ 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 */ \ @@ -1610,11 +1678,11 @@ static const pixma_scan_ops_t pixma_mp150_ops = { const pixma_config_t pixma_mp150_devices[] = { /* Generation 1: CIS */ - 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), + DEVICE ("Canon PIXMA MP150", "MP150", MP150_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_GT_4096), + DEVICE ("Canon PIXMA MP170", "MP170", MP170_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_GT_4096), + DEVICE ("Canon PIXMA MP450", "MP450", MP450_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_GT_4096), + DEVICE ("Canon PIXMA MP500", "MP500", MP500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_GT_4096), + DEVICE ("Canon PIXMA MP530", "MP530", MP530_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_GT_4096 | PIXMA_CAP_ADF), /* Generation 2: CIS */ DEVICE ("Canon PIXMA MP140", "MP140", MP140_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS), @@ -1675,7 +1743,7 @@ const pixma_config_t pixma_mp150_devices[] = { /* 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 MP495", "MP495", MP495_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS), /* ToDo: max. scan resolution = 1200x600dpi */ 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), @@ -1765,12 +1833,12 @@ const pixma_config_t pixma_mp150_devices[] = { /* Latest devices (2018) Generation 5 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 MAXIFY MB5100 Series", "MB5100", MB5100_PID, 0, 1200, 0, 0, 638, 1050, PIXMA_CAP_CIS | PIXMA_CAP_ADFDUP | PIXMA_CAP_ADF_JPEG), 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 TR8500 Series", "TR8500", TR8500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG), 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 400", "LIDE400", LIDE400_PID, 300, 4800, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_48BIT), DEVICE ("CanoScan LiDE 300", "LIDE300", LIDE300_PID, 300, 2400, 0, 0, 638, 877, PIXMA_CAP_CIS), /* Latest devices (2019) Generation 5 CIS */ @@ -1788,7 +1856,7 @@ const pixma_config_t pixma_mp150_devices[] = { 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 TR4500 Series", "TR4500", TR4500_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG), /* ToDo: max. scan resolution = 600x1200dpi */ 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), @@ -1798,8 +1866,10 @@ const pixma_config_t pixma_mp150_devices[] = { 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 G7000 Series", "G7000", G7000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF), /* ToDo: ADF has legal paper length */ 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 PIXMA GM4000 Series", "GM4000", GM4000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF), /* ToDo: ADF has legal paper length */ 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), diff --git a/backend/pixma/pixma_mp730.c b/backend/pixma/pixma_mp730.c index 93d518b..fcc9ae8 100644 --- a/backend/pixma/pixma_mp730.c +++ b/backend/pixma/pixma_mp730.c @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> @@ -815,7 +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 */ \ + 0, 0, /* min_xdpi & min_xdpi_16 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/pixma_mp750.c b/backend/pixma/pixma_mp750.c index 7f00023..cc1c3ad 100644 --- a/backend/pixma/pixma_mp750.c +++ b/backend/pixma/pixma_mp750.c @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> This file is part of the SANE package. @@ -955,7 +955,7 @@ 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 */ \ + 0, 0, /* min_xdpi & min_xdpi_16 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 */ \ diff --git a/backend/pixma/pixma_mp800.c b/backend/pixma/pixma_mp800.c index feef611..905c246 100644 --- a/backend/pixma/pixma_mp800.c +++ b/backend/pixma/pixma_mp800.c @@ -1,6 +1,6 @@ /* SANE - Scanner Access Now Easy. - Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de> + Copyright (C) 2011-2020 Rolf Bensch <rolf at bensch hyphen online dot de> Copyright (C) 2007-2009 Nicolas Martin, <nicols-guest at alioth dot debian dot org> Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de> @@ -91,7 +91,6 @@ 4096 = size of gamma table. 24 = header + checksum */ #define IMAGE_BLOCK_SIZE (512*1024) #define CMDBUF_SIZE (4096 + 24) -#define DEFAULT_GAMMA 2.0 /***** Gamma different from 1.0 is potentially impacting color profile generation *****/ #define UNKNOWN_PID 0xffff #define CANON_VID 0x04a9 @@ -153,8 +152,6 @@ <ivec:param_set servicetype=\"scan\"><ivec:jobID>00000001</ivec:jobID>\ </ivec:param_set></ivec:contents></cmd>" -#define XML_OK "<ivec:response>OK</ivec:response>" - enum mp810_state_t { state_idle, @@ -294,7 +291,7 @@ static int send_xml_dialog (pixma_t * s, const char * xml_message) PDBG(pixma_dbg (10, "XML message sent to scanner:\n%s\n", xml_message)); PDBG(pixma_dbg (10, "XML response back from scanner:\n%s\n", mp->cb.buf)); - return (strcasestr ((const char *) mp->cb.buf, XML_OK) != NULL); + return pixma_parse_xml_response((const char*)mp->cb.buf) == PIXMA_STATUS_OK; } static void new_cmd_tpu_msg (pixma_t *s, pixma_cmdbuf_t * cb, uint16_t cmd) @@ -438,44 +435,47 @@ static int send_gamma_table (pixma_t * s) const uint8_t *lut = s->param->gamma_table; uint8_t *data; - if (mp->generation == 1) + if (s->cfg->cap & PIXMA_CAP_GT_4096) { data = pixma_newcmd (&mp->cb, cmd_gamma, 4096 + 8, 0); data[0] = (s->param->channels == 3) ? 0x10 : 0x01; pixma_set_be16 (0x1004, data + 2); if (lut) - memcpy (data + 4, lut, 4096); - else - pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096); - } - else - { - /* FIXME: Gamma table for 2nd generation: 1024 * uint16_le */ - data = pixma_newcmd (&mp->cb, cmd_gamma, 2048 + 8, 0); - data[0] = 0x10; - pixma_set_be16 (0x0804, data + 2); - if (lut) - { - int i; - for (i = 0; i < 1024; i++) { - int j = (i << 2) + (i >> 8); - data[4 + 2 * i + 0] = lut[j]; - data[4 + 2 * i + 1] = lut[j]; + /* PDBG (pixma_dbg (4, "*send_gamma_table***** Use 4096 bytes from LUT ***** \n")); */ + /* PDBG (pixma_hexdump (4, lut, 4096)); */ + memcpy (data + 4, lut, 4096); } - } else - { - int i; - pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 2048); - for (i = 0; i < 1024; i++) { - int j = (i << 1) + (i >> 9); - data[4 + 2 * i + 0] = data[4 + j]; - data[4 + 2 * i + 1] = data[4 + j]; + /* fallback: we should never see this */ + PDBG (pixma_dbg (4, "*send_gamma_table***** Generate 4096 bytes Table with %f ***** \n", + s->param->gamma)); + pixma_fill_gamma_table (s->param->gamma, data + 4, 4096); + /* PDBG (pixma_hexdump (4, data + 4, 4096)); */ } - } } + else + { + /* Gamma table for 2nd+ generation: 1024 * uint16_le */ + data = pixma_newcmd (&mp->cb, cmd_gamma, 1024 * 2 + 8, 0); + data[0] = 0x10; + pixma_set_be16 (0x0804, data + 2); + if (lut) + { + /* PDBG (pixma_dbg (4, "*send_gamma_table***** Use 1024 * 2 bytes from LUT ***** \n")); */ + /* PDBG (pixma_hexdump (4, lut, 1024 * 2)); */ + memcpy (data + 4, lut, 1024 * 2); + } + else + { + /* fallback: we should never see this */ + PDBG (pixma_dbg (4, "*send_gamma_table***** Generate 1024 * 2 bytes Table with %f ***** \n", + s->param->gamma)); + pixma_fill_gamma_table (s->param->gamma, data + 4, 1024); + /* PDBG (pixma_hexdump (4, data + 4, 1024 * 2)); */ + } + } return pixma_exec (s, &mp->cb); } @@ -1172,9 +1172,17 @@ static int handle_interrupt (pixma_t * s, int timeout) * target = format; original = size; scan-resolution = dpi */ { if (buf[7] & 1) - s->events = PIXMA_EV_BUTTON1 | buf[11] | buf[10]<<8 | buf[12]<<16; /* color scan */ + { + /* color scan */ + s->events = PIXMA_EV_BUTTON1 | (buf[11] & 0x0f) | (buf[10] & 0x0f) << 8 + | (buf[12] & 0x0f) << 16; + } if (buf[7] & 2) - s->events = PIXMA_EV_BUTTON2 | buf[11] | buf[10]<<8 | buf[12]<<16; /* b/w scan */ + { + /* b/w scan */ + s->events = PIXMA_EV_BUTTON2 | (buf[11] & 0x0f) | (buf[10] & 0x0f) << 8 + | (buf[12] & 0x0f) << 16; + } } else if (s->cfg->pid == CS8800F_PID || s->cfg->pid == CS9000F_PID @@ -1185,9 +1193,15 @@ static int handle_interrupt (pixma_t * s, int timeout) { if ((s->cfg->pid == CS8800F_PID && buf[1] == 0x70) || (s->cfg->pid != CS8800F_PID && buf[1] == 0x50)) - s->events = PIXMA_EV_BUTTON2 | buf[1] >> 4; /* button 2 = cancel / end scan */ + { + /* button 2 = cancel / end scan */ + s->events = PIXMA_EV_BUTTON2 | buf[1] >> 4; + } else - s->events = PIXMA_EV_BUTTON1 | buf[1] >> 4; /* button 1 = start scan */ + { + /* button 1 = start scan */ + s->events = PIXMA_EV_BUTTON1 | buf[1] >> 4; + } } else /* button no. in buf[0] @@ -1204,9 +1218,15 @@ static int handle_interrupt (pixma_t * s, int timeout) query_status (s); if (buf[0] & 2) - s->events = PIXMA_EV_BUTTON2 | buf[1] | ((buf[0] & 0xf0) << 4); /* b/w scan */ + { + /* b/w scan */ + s->events = PIXMA_EV_BUTTON2 | (buf[1] & 0x0f) | (buf[0] & 0xf0) << 4; + } if (buf[0] & 1) - s->events = PIXMA_EV_BUTTON1 | buf[1] | ((buf[0] & 0xf0) << 4); /* color scan */ + { + /* color scan */ + s->events = PIXMA_EV_BUTTON1 | (buf[1] & 0x0f) | (buf[0] & 0xf0) << 4; + } } return 1; } @@ -1871,8 +1891,8 @@ static int mp810_check_param (pixma_t * s, pixma_scan_param_t * sp) mp810_t *mp = (mp810_t *) s->subdriver; unsigned w_max; - /* PDBG (pixma_dbg (4, "*mp810_check_param***** Initially: 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)); */ + /* PDBG (pixma_dbg (4, "*mp810_check_param***** Initially: channels=%u, depth=%u, x=%u, y=%u, w=%u, h=%u, xs=%u, wx=%u, gamma=%f *****\n", + sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->h, sp->xs, sp->wx, sp->gamma)); */ sp->channels = 3; sp->software_lineart = 0; @@ -2066,9 +2086,7 @@ static int mp810_check_param (pixma_t * s, pixma_scan_param_t * sp) k = MAX (sp->xdpi, 300) / sp->xdpi; else if (sp->source == PIXMA_SOURCE_TPU || sp->mode == PIXMA_SCAN_MODE_COLOR_48 || sp->mode == PIXMA_SCAN_MODE_GRAY_16) - /* TPU mode and 16 bit flatbed scans - * TODO: either the frontend (xsane) cannot handle 48 bit flatbed scans @ 75 dpi (prescan) - * or there is a bug in this subdriver */ + /* TPU mode and 16 bit flatbed scans */ k = MAX (sp->xdpi, 150) / sp->xdpi; else /* default */ @@ -2375,13 +2393,14 @@ static const pixma_scan_ops_t pixma_mp800_ops = mp810_get_status }; -#define DEVICE(name, model, pid, dpi, adftpu_min_dpi, adftpu_max_dpi, tpuir_min_dpi, tpuir_max_dpi, w, h, cap) { \ +#define DEVICE(name, model, pid, min_dpi_16, dpi, adftpu_min_dpi, adftpu_max_dpi, tpuir_min_dpi, tpuir_max_dpi, w, h, cap) { \ name, /* name */ \ model, /* model */ \ CANON_VID, pid, /* vid pid */ \ 0, /* iface */ \ &pixma_mp800_ops, /* ops */ \ 0, /* min_xdpi not used in this subdriver */ \ + min_dpi_16, /* min_xdpi_16 */ \ 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 */ \ @@ -2393,42 +2412,42 @@ static const pixma_scan_ops_t pixma_mp800_ops = PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \ } -#define END_OF_DEVICE_LIST DEVICE(NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0) +#define END_OF_DEVICE_LIST DEVICE(NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 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), + DEVICE ("Canon PIXMA MP800", "MP800", MP800_PID, 0, 2400, 150, 0, 0, 0, 638, 877, PIXMA_CAP_TPU | PIXMA_CAP_GT_4096), + DEVICE ("Canon PIXMA MP800R", "MP800R", MP800R_PID, 0, 2400, 150, 0, 0, 0, 638, 877, PIXMA_CAP_TPU | PIXMA_CAP_GT_4096), + DEVICE ("Canon PIXMA MP830", "MP830", MP830_PID, 0, 2400, 150, 0, 0, 0, 638, 877, PIXMA_CAP_ADFDUP | PIXMA_CAP_GT_4096), /* Generation 2: CCD */ - 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), + DEVICE ("Canon PIXMA MP810", "MP810", MP810_PID, 0, 4800, 300, 0, 0, 0, 638, 877, PIXMA_CAP_TPU), + DEVICE ("Canon PIXMA MP960", "MP960", MP960_PID, 0, 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_TPU), + DEVICE ("Canon Pixma MP970", "MP970", MP970_PID, 0, 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_TPU /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT), + DEVICE ("Canoscan 8800F", "8800F", CS8800F_PID, 150, 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_TPU), + DEVICE ("Canon MP980 series", "MP980", MP980_PID, 0, 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_TPU), + DEVICE ("Canon MP990 series", "MP990", MP990_PID, 0, 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_TPUIR /*| PIXMA_CAP_NEGATIVE*/ | PIXMA_CAP_48BIT), + DEVICE ("Canoscan 9000F", "9000F", CS9000F_PID, 150, 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_TPU), + DEVICE ("Canon PIXMA MG8100", "MG8100", MG8100_PID, 0, 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_TPU), + DEVICE ("Canon PIXMA MG8200", "MG8200", MG8200_PID, 0, 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_TPUIR | PIXMA_CAP_48BIT), + DEVICE ("Canoscan 9000F Mark II", "9000FMarkII", CS9000F_MII_PID, 150, 4800, 300, 9600, 600, 2400, 638, 877, PIXMA_CAP_TPUIR | PIXMA_CAP_48BIT), END_OF_DEVICE_LIST }; diff --git a/backend/pixma/pixma_sane_options.c b/backend/pixma/pixma_sane_options.c deleted file mode 100644 index 2b8f609..0000000 --- a/backend/pixma/pixma_sane_options.c +++ /dev/null @@ -1,362 +0,0 @@ -/* Automatically generated from pixma_sane.c */ -static const SANE_Range constraint_gamma_table = - { 0,255,0 }; -static const SANE_Range constraint_gamma = - { SANE_FIX(0.3),SANE_FIX(5),SANE_FIX(0) }; -static const SANE_Range constraint_threshold = - { 0,100,1 }; -static const SANE_Range constraint_threshold_curve = - { 0,127,1 }; -static const SANE_Range constraint_adf_wait = - { 0,3600,1 }; - - -static -int find_string_in_list(SANE_String_Const str, const SANE_String_Const *list) -{ - int i; - for (i = 0; list[i] && strcmp(str, list[i]) != 0; i++) {} - return i; -} - -static -int build_option_descriptors(struct pixma_sane_t *ss) -{ - SANE_Option_Descriptor *sod; - option_descriptor_t *opt; - - memset(OPT_IN_CTX, 0, sizeof(OPT_IN_CTX)); - - opt = &(OPT_IN_CTX[opt_opt_num_opts]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_TITLE_NUM_OPTIONS; - sod->desc = SANE_DESC_NUM_OPTIONS; - sod->name = ""; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_DETECT; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_opt_num_opts].info = 0; - opt->def.w = opt_last; - opt->val.w = opt_last; - - opt = &(OPT_IN_CTX[opt__group_1]); - sod = &opt->sod; - sod->type = SANE_TYPE_GROUP; - sod->title = SANE_I18N("Scan mode"); - sod->desc = sod->title; - - opt = &(OPT_IN_CTX[opt_resolution]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_TITLE_SCAN_RESOLUTION; - sod->desc = SANE_DESC_SCAN_RESOLUTION; - sod->name = "resolution"; - sod->unit = SANE_UNIT_DPI; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC; - sod->constraint_type = SANE_CONSTRAINT_WORD_LIST; - sod->constraint.word_list = ss->dpi_list; - OPT_IN_CTX[opt_resolution].info = SANE_INFO_RELOAD_PARAMS; - opt->def.w = 75; - opt->val.w = 75; - - opt = &(OPT_IN_CTX[opt_mode]); - sod = &opt->sod; - sod->type = SANE_TYPE_STRING; - sod->title = SANE_TITLE_SCAN_MODE; - sod->desc = SANE_DESC_SCAN_MODE; - sod->name = "mode"; - sod->unit = SANE_UNIT_NONE; - sod->size = 31; - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC; - sod->constraint_type = SANE_CONSTRAINT_STRING_LIST; - sod->constraint.string_list = ss->mode_list; - OPT_IN_CTX[opt_mode].info = SANE_INFO_RELOAD_PARAMS; - opt->def.s = SANE_VALUE_SCAN_MODE_COLOR; - opt->val.w = find_string_in_list(opt->def.s, sod->constraint.string_list); - - opt = &(OPT_IN_CTX[opt_source]); - sod = &opt->sod; - sod->type = SANE_TYPE_STRING; - sod->title = SANE_TITLE_SCAN_SOURCE; - sod->desc = SANE_I18N("Selects the scan source (such as a document-feeder). Set source before mode and resolution. Resets mode and resolution to auto values."); - sod->name = "source"; - sod->unit = SANE_UNIT_NONE; - sod->size = 31; - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT; - sod->constraint_type = SANE_CONSTRAINT_STRING_LIST; - sod->constraint.string_list = ss->source_list; - OPT_IN_CTX[opt_source].info = 0; - opt->def.s = SANE_I18N("Flatbed"); - opt->val.w = find_string_in_list(opt->def.s, sod->constraint.string_list); - - opt = &(OPT_IN_CTX[opt_button_controlled]); - sod = &opt->sod; - sod->type = SANE_TYPE_BOOL; - sod->title = SANE_I18N("Button-controlled scan"); - sod->desc = SANE_I18N("When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button."); - sod->name = "button-controlled"; - sod->unit = SANE_UNIT_NONE; - sod->size = sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_INACTIVE; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_button_controlled].info = 0; - opt->def.w = SANE_FALSE; - opt->val.w = SANE_FALSE; - - opt = &(OPT_IN_CTX[opt__group_2]); - sod = &opt->sod; - sod->type = SANE_TYPE_GROUP; - sod->title = SANE_I18N("Gamma"); - sod->desc = sod->title; - - opt = &(OPT_IN_CTX[opt_custom_gamma]); - sod = &opt->sod; - sod->type = SANE_TYPE_BOOL; - sod->title = SANE_TITLE_CUSTOM_GAMMA; - sod->desc = SANE_DESC_CUSTOM_GAMMA; - sod->name = "custom-gamma"; - sod->unit = SANE_UNIT_NONE; - sod->size = sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_custom_gamma].info = 0; - opt->def.w = SANE_TRUE; - opt->val.w = SANE_TRUE; - - opt = &(OPT_IN_CTX[opt_gamma_table]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_TITLE_GAMMA_VECTOR; - sod->desc = SANE_DESC_GAMMA_VECTOR; - sod->name = "gamma-table"; - sod->unit = SANE_UNIT_NONE; - sod->size = 4096 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &constraint_gamma_table; - OPT_IN_CTX[opt_gamma_table].info = 0; - - opt = &(OPT_IN_CTX[opt_gamma]); - sod = &opt->sod; - sod->type = SANE_TYPE_FIXED; - sod->title = SANE_I18N("Gamma function exponent"); - sod->desc = SANE_I18N("Changes intensity of midtones"); - sod->name = "gamma"; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &constraint_gamma; - OPT_IN_CTX[opt_gamma].info = 0; - opt->def.w = SANE_FIX(AUTO_GAMMA); - opt->val.w = SANE_FIX(AUTO_GAMMA); - - opt = &(OPT_IN_CTX[opt__group_3]); - sod = &opt->sod; - sod->type = SANE_TYPE_GROUP; - sod->title = SANE_I18N("Geometry"); - sod->desc = sod->title; - - opt = &(OPT_IN_CTX[opt_tl_x]); - sod = &opt->sod; - sod->type = SANE_TYPE_FIXED; - sod->title = SANE_TITLE_SCAN_TL_X; - sod->desc = SANE_DESC_SCAN_TL_X; - sod->name = "tl-x"; - sod->unit = SANE_UNIT_MM; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &ss->xrange; - OPT_IN_CTX[opt_tl_x].info = SANE_INFO_RELOAD_PARAMS; - opt->def.w = SANE_FIX(0); - opt->val.w = SANE_FIX(0); - - opt = &(OPT_IN_CTX[opt_tl_y]); - sod = &opt->sod; - sod->type = SANE_TYPE_FIXED; - sod->title = SANE_TITLE_SCAN_TL_Y; - sod->desc = SANE_DESC_SCAN_TL_Y; - sod->name = "tl-y"; - sod->unit = SANE_UNIT_MM; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &ss->yrange; - OPT_IN_CTX[opt_tl_y].info = SANE_INFO_RELOAD_PARAMS; - opt->def.w = SANE_FIX(0); - opt->val.w = SANE_FIX(0); - - opt = &(OPT_IN_CTX[opt_br_x]); - sod = &opt->sod; - sod->type = SANE_TYPE_FIXED; - sod->title = SANE_TITLE_SCAN_BR_X; - sod->desc = SANE_DESC_SCAN_BR_X; - sod->name = "br-x"; - sod->unit = SANE_UNIT_MM; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &ss->xrange; - OPT_IN_CTX[opt_br_x].info = SANE_INFO_RELOAD_PARAMS; - opt->def.w = sod->constraint.range->max; - opt->val.w = sod->constraint.range->max; - - opt = &(OPT_IN_CTX[opt_br_y]); - sod = &opt->sod; - sod->type = SANE_TYPE_FIXED; - sod->title = SANE_TITLE_SCAN_BR_Y; - sod->desc = SANE_DESC_SCAN_BR_Y; - sod->name = "br-y"; - sod->unit = SANE_UNIT_MM; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &ss->yrange; - OPT_IN_CTX[opt_br_y].info = SANE_INFO_RELOAD_PARAMS; - opt->def.w = sod->constraint.range->max; - opt->val.w = sod->constraint.range->max; - - opt = &(OPT_IN_CTX[opt__group_4]); - sod = &opt->sod; - sod->type = SANE_TYPE_GROUP; - sod->title = SANE_I18N("Buttons"); - sod->desc = sod->title; - - opt = &(OPT_IN_CTX[opt_button_update]); - sod = &opt->sod; - sod->type = SANE_TYPE_BUTTON; - sod->title = SANE_I18N("Update button state"); - sod->desc = sod->title; - sod->name = "button-update"; - sod->unit = SANE_UNIT_NONE; - sod->size = 0; - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_button_update].info = 0; - - opt = &(OPT_IN_CTX[opt_button_1]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_I18N("Button 1"); - sod->desc = sod->title; - sod->name = "button-1"; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_button_1].info = 0; - opt->def.w = 0; - opt->val.w = 0; - - opt = &(OPT_IN_CTX[opt_button_2]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_I18N("Button 2"); - sod->desc = sod->title; - sod->name = "button-2"; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_button_2].info = 0; - opt->def.w = 0; - opt->val.w = 0; - - opt = &(OPT_IN_CTX[opt_original]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_I18N("Type of original to scan"); - sod->desc = sod->title; - sod->name = "original"; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_original].info = 0; - opt->def.w = 0; - opt->val.w = 0; - - opt = &(OPT_IN_CTX[opt_target]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_I18N("Target operation type"); - sod->desc = sod->title; - sod->name = "target"; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_target].info = 0; - opt->def.w = 0; - opt->val.w = 0; - - opt = &(OPT_IN_CTX[opt_scan_resolution]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_I18N("Scan resolution"); - sod->desc = sod->title; - sod->name = "scan-resolution"; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; - sod->constraint_type = SANE_CONSTRAINT_NONE; - OPT_IN_CTX[opt_scan_resolution].info = 0; - opt->def.w = 0; - opt->val.w = 0; - - opt = &(OPT_IN_CTX[opt__group_5]); - sod = &opt->sod; - sod->type = SANE_TYPE_GROUP; - sod->title = SANE_I18N("Extras"); - sod->desc = sod->title; - - opt = &(OPT_IN_CTX[opt_threshold]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_TITLE_THRESHOLD; - sod->desc = SANE_DESC_THRESHOLD; - sod->name = "threshold"; - sod->unit = SANE_UNIT_PERCENT; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &constraint_threshold; - OPT_IN_CTX[opt_threshold].info = 0; - opt->def.w = 50; - opt->val.w = 50; - - opt = &(OPT_IN_CTX[opt_threshold_curve]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_I18N("Threshold curve"); - sod->desc = SANE_I18N("Dynamic threshold curve, from light to dark, normally 50-65"); - sod->name = "threshold-curve"; - sod->unit = SANE_UNIT_NONE; - sod->size = 1 * sizeof(SANE_Word); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &constraint_threshold_curve; - OPT_IN_CTX[opt_threshold_curve].info = 0; - - opt = &(OPT_IN_CTX[opt_adf_wait]); - sod = &opt->sod; - sod->type = SANE_TYPE_INT; - sod->title = SANE_I18N("ADF Waiting Time"); - 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); - sod->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_AUTOMATIC|SANE_CAP_INACTIVE; - sod->constraint_type = SANE_CONSTRAINT_RANGE; - sod->constraint.range = &constraint_adf_wait; - OPT_IN_CTX[opt_adf_wait].info = 0; - opt->def.w = 0; - opt->val.w = 0; - - return 0; - -} diff --git a/backend/pixma/pixma_sane_options.h b/backend/pixma/pixma_sane_options.h deleted file mode 100644 index 1472f1f..0000000 --- a/backend/pixma/pixma_sane_options.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Automatically generated from pixma_sane.c */ - -typedef union { - SANE_Word w; - SANE_Int i; - SANE_Bool b; - SANE_Fixed f; - SANE_String s; - void *ptr; -} option_value_t; - -typedef enum { - opt_opt_num_opts, - opt__group_1, - opt_resolution, - opt_mode, - opt_source, - opt_button_controlled, - opt__group_2, - opt_custom_gamma, - opt_gamma_table, - opt_gamma, - opt__group_3, - opt_tl_x, - opt_tl_y, - opt_br_x, - opt_br_y, - opt__group_4, - opt_button_update, - opt_button_1, - opt_button_2, - opt_original, - opt_target, - opt_scan_resolution, - opt__group_5, - opt_threshold, - opt_threshold_curve, - opt_adf_wait, - opt_last -} option_t; - - -typedef struct { - SANE_Option_Descriptor sod; - option_value_t val,def; - SANE_Word info; -} option_descriptor_t; - - -struct pixma_sane_t; -static int build_option_descriptors(struct pixma_sane_t *ss); diff --git a/backend/pixma/scripts/pixma_gen_options.py b/backend/pixma/scripts/pixma_gen_options.py index c4c75e0..cee2c58 100755 --- a/backend/pixma/scripts/pixma_gen_options.py +++ b/backend/pixma/scripts/pixma_gen_options.py @@ -1,6 +1,8 @@ #!/usr/bin/env python +from __future__ import print_function import sys,os,re +import functools class Error(Exception): pass @@ -181,40 +183,35 @@ def parseFile(f): def genHeader(options): - print """ -typedef union { - SANE_Word w; - SANE_Int i; - SANE_Bool b; - SANE_Fixed f; - SANE_String s; - void *ptr; -} option_value_t; -""" - print 'typedef enum {' + print ("\ntypedef union {") + print (" SANE_Word w;") + print (" SANE_Int i;") + print (" SANE_Bool b;") + print (" SANE_Fixed f;") + print (" SANE_String s;") + print (" void *ptr;") + print ("} option_value_t;") + print ("\ntypedef enum {") for o in options: - print ' %(cname_opt)s,' % o - print ' ' + opt_prefix + 'last' - print '} option_t;' - print """ + print (" %(cname_opt)s," % o) + print (" " + opt_prefix + "last") + print ("} option_t;") -typedef struct { - SANE_Option_Descriptor sod; - option_value_t val,def; - SANE_Word info; -} option_descriptor_t; + print ("\ntypedef struct {") + print (" SANE_Option_Descriptor sod;") + print (" option_value_t val,def;") + print (" SANE_Word info;") + print ("} option_descriptor_t;") - -struct pixma_sane_t; -static int build_option_descriptors(struct pixma_sane_t *ss); -""" + print ("\nstruct pixma_sane_t;") + print ("static int build_option_descriptors(struct pixma_sane_t *ss);\n") def genMinMaxRange(n, t, r): if t == 'SANE_TYPE_FIXED': r = ['SANE_FIX(%s)' % x for x in r] - print 'static const SANE_Range ' + n + ' = ' - print ' { ' + r[0] + ',' + r[1] + ',' + r[2] + ' };' + print ("static const SANE_Range " + n + " =") + print (" { " + r[0] + "," + r[1] + "," + r[2] + " };") def genList(n, t, l): @@ -227,13 +224,14 @@ def genList(n, t, l): elif t == 'SANE_TYPE_STRING': etype = 'SANE_String_Const' l = ['SANE_I18N("%s")' % x for x in l] + ['NULL'] - print 'static const %s %s[%d] = {' % (etype, n, len(l)) + print ("static const %s %s[%d] = {" % (etype, n, len(l))) for x in l[0:-1]: - print '\t' + x + ',' - print '\t' + l[-1] + ' };' + print ("\t" + x + ",") + print ("\t" + l[-1] + " };") def genConstraints(options): + print ("") for o in options: if 'constraint' not in o: continue c = o['constraint'] @@ -243,7 +241,6 @@ def genConstraints(options): genMinMaxRange(oname, otype, c) elif isinstance(c, list): genList(oname, otype, c) - print def buildCodeVerbatim(o): for f in ('name', 'title', 'desc', 'type', 'unit', 'size', 'cap', @@ -283,12 +280,12 @@ def ccode(o): o['code_size'] = code if ('code_cap' not in o) and ('cap' in o): - o['code_cap'] = reduce(lambda a,b: a+'|'+b, o['cap']) + o['code_cap'] = functools.reduce(lambda a,b: a+'|'+b, o['cap']) else: o['code_cap'] = '0' if ('code_info' not in o) and ('info' in o): - o['code_info'] = reduce(lambda a,b: a+'|'+b, o['info']) + o['code_info'] = functools.reduce(lambda a,b: a+'|'+b, o['info']) else: o['code_info'] = '0' @@ -335,22 +332,21 @@ def ccode(o): return o def genBuildOptions(options): - print """ -static -int find_string_in_list(SANE_String_Const str, const SANE_String_Const *list) -{ - int i; - for (i = 0; list[i] && strcmp(str, list[i]) != 0; i++) {} - return i; -} - -static -int build_option_descriptors(struct pixma_sane_t *ss) -{ - SANE_Option_Descriptor *sod; - option_descriptor_t *opt; - - memset(OPT_IN_CTX, 0, sizeof(OPT_IN_CTX));""" + print ("\nstatic") + print ("int find_string_in_list(SANE_String_Const str, const SANE_String_Const *list)") + print ("{") + print (" int i;") + print (" for (i = 0; list[i] && strcmp(str, list[i]) != 0; i++) {}") + print (" return i;") + print ("}") + print ("") + print ("static") + print ("int build_option_descriptors(struct pixma_sane_t *ss)") + print ("{") + print (" SANE_Option_Descriptor *sod;") + print (" option_descriptor_t *opt;") + print ("") + print (" memset(OPT_IN_CTX, 0, sizeof(OPT_IN_CTX));") for o in options: o = ccode(o) @@ -370,10 +366,9 @@ int build_option_descriptors(struct pixma_sane_t *ss) ' OPT_IN_CTX[%(cname_opt)s].info = %(code_info)s;\n' \ '%(full_code_default)s' sys.stdout.write(code % o) - print - print ' return 0;\n' - print '}' - print + print ("") + print (" return 0;") + print ("}\n") g = Struct() g.ngroups = 0 @@ -381,7 +376,8 @@ opt_prefix = 'opt_' con_prefix = 'constraint_' cnameMap = createCNameMap() options = parseFile(sys.stdin) -print "/* Automatically generated from pixma_sane.c */" +print ("/* DO NOT EDIT THIS FILE! */") +print ("/* Automatically generated from pixma.c */") if (len(sys.argv) == 2) and (sys.argv[1] == 'h'): genHeader(options) else: diff --git a/backend/plustek-usbshading.c b/backend/plustek-usbshading.c index 98a28d9..e789b43 100644 --- a/backend/plustek-usbshading.c +++ b/backend/plustek-usbshading.c @@ -882,7 +882,7 @@ TOGAIN: if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { RGBULongDef rgb, rgbSum; - u_long dwLoop = len / 20 * 20; + u_long dwLoop = (len - start) / 20 * 20; u_long dw10, dwGray, dwGrayMax; rgb.Red = rgb.Green = rgb.Blue = dwGrayMax = 0; @@ -923,7 +923,7 @@ TOGAIN: } else { u_long dwMax = 0, dwSum; - u_long dwLoop = len / 20 * 20; + u_long dwLoop = (len - start) / 20 * 20; u_long dw10; for( dw = start; dwLoop; dwLoop-- ) { @@ -951,7 +951,7 @@ TOGAIN: RGBUShortDef max_rgb, min_rgb, tmp_rgb; u_long dwR, dwG, dwB; u_long dwDiv = 10; - u_long dwLoop1 = len / dwDiv, dwLoop2; + u_long dwLoop1 = (len - start) / dwDiv, dwLoop2; max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0; min_rgb.Red = min_rgb.Green = min_rgb.Blue = 0xffff; diff --git a/backend/ricoh2_buffer.c b/backend/ricoh2_buffer.c index e79a7f3..8cf86f3 100644 --- a/backend/ricoh2_buffer.c +++ b/backend/ricoh2_buffer.c @@ -44,14 +44,8 @@ #include "../include/sane/config.h" -#include <memory.h> #include <assert.h> - -#if defined(__APPLE__) && defined(__MACH__) -#include <malloc/malloc.h> -#else -#include <malloc.h> -#endif +#include <stdlib.h> #include "../include/sane/sanei_debug.h" diff --git a/backend/test-picture.c b/backend/test-picture.c index 46407dc..66374c7 100644 --- a/backend/test-picture.c +++ b/backend/test-picture.c @@ -155,8 +155,8 @@ init_picture_buffer (Test_Device * test_device, SANE_Byte ** buffer, if (xfull < ppl) { if ((((SANE_Word) (xfull / p_size)) % 2) - ^ !(line_count > - (SANE_Word) (p_size + 0.5))) + ^ (!(line_count > + (SANE_Word) (p_size + 0.5)))) color = 0x0; else color = 0x1; diff --git a/backend/test.c b/backend/test.c index 3ead456..a1e186e 100644 --- a/backend/test.c +++ b/backend/test.c @@ -116,6 +116,12 @@ static SANE_Range int_constraint_range = { 2 }; +static SANE_Range gamma_range = { + 0, + 255, + 1 +}; + static SANE_Range fixed_constraint_range = { SANE_FIX (-42.17), SANE_FIX (32767.9999), @@ -184,6 +190,42 @@ static SANE_Int int_array_constraint_range[] = { 48, 6, 4, 92, 190, 16 }; +#define GAMMA_RED_SIZE 256 +#define GAMMA_GREEN_SIZE 256 +#define GAMMA_BLUE_SIZE 256 +#define GAMMA_ALL_SIZE 4096 +static SANE_Int gamma_red[GAMMA_RED_SIZE]; // initialized in init_options() +static SANE_Int gamma_green[GAMMA_GREEN_SIZE]; +static SANE_Int gamma_blue[GAMMA_BLUE_SIZE]; +static SANE_Int gamma_all[GAMMA_ALL_SIZE]; + +static void +init_gamma_table(SANE_Int *tablePtr, SANE_Int count, SANE_Int max) +{ + for (int i=0; i<count; ++i) { + tablePtr[i] = (SANE_Int)(((double)i * max)/(double)count); + } +} + +static void +print_gamma_table(SANE_Int *tablePtr, SANE_Int count) +{ + char str[200]; + str[0] = '\0'; + DBG (5, "Gamma Table Size: %d\n", count); + for (int i=0; i<count; ++i) { + if (i%16 == 0 && strlen(str) > 0) { + DBG (5, "%s\n", str); + str[0] = '\0'; + } + sprintf (str + strlen(str), " %04X", tablePtr[i]); + } + if (strlen(str) > 0) { + DBG (5, "%s\n", str); + } +} + + static SANE_Int int_array_constraint_word_list[] = { -42, 0, -8, 17, 42, 42 }; @@ -923,6 +965,63 @@ init_options (Test_Device * test_device) test_device->val[opt_int_array_constraint_range].wa = &int_array_constraint_range[0]; + /* opt_gamma_red */ + init_gamma_table(gamma_red, GAMMA_RED_SIZE, gamma_range.max); + od = &test_device->opt[opt_gamma_red]; + od->name = SANE_NAME_GAMMA_VECTOR_R; + od->title = SANE_TITLE_GAMMA_VECTOR_R; + od->desc = SANE_DESC_GAMMA_VECTOR_R; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = 256 * sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &gamma_range; + test_device->val[opt_gamma_red].wa = &gamma_red[0]; + + /* opt_gamma_green */ + init_gamma_table(gamma_green, GAMMA_GREEN_SIZE, gamma_range.max); + od = &test_device->opt[opt_gamma_green]; + od->name = SANE_NAME_GAMMA_VECTOR_G; + od->title = SANE_TITLE_GAMMA_VECTOR_G; + od->desc = SANE_DESC_GAMMA_VECTOR_G; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = 256 * sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &gamma_range; + test_device->val[opt_gamma_green].wa = &gamma_green[0]; + + /* opt_gamma_blue */ + init_gamma_table(gamma_blue, GAMMA_BLUE_SIZE, gamma_range.max); + od = &test_device->opt[opt_gamma_blue]; + od->name = SANE_NAME_GAMMA_VECTOR_B; + od->title = SANE_TITLE_GAMMA_VECTOR_B; + od->desc = SANE_DESC_GAMMA_VECTOR_B; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = 256 * sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &gamma_range; + test_device->val[opt_gamma_blue].wa = &gamma_blue[0]; + + /* opt_gamma_all */ + init_gamma_table(gamma_all, GAMMA_ALL_SIZE, gamma_range.max); + print_gamma_table(gamma_all, GAMMA_ALL_SIZE); + od = &test_device->opt[opt_gamma_all]; + od->name = SANE_NAME_GAMMA_VECTOR; + od->title = SANE_TITLE_GAMMA_VECTOR; + od->desc = SANE_DESC_GAMMA_VECTOR; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = GAMMA_ALL_SIZE * sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &gamma_range; + test_device->val[opt_gamma_all].wa = &gamma_all[0]; + /* opt_int_array_constraint_word_list */ od = &test_device->opt[opt_int_array_constraint_word_list]; od->name = "int-constraint-array-constraint-word-list"; @@ -2071,11 +2170,21 @@ sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, break; case opt_int_array: /* Word array */ case opt_int_array_constraint_range: + case opt_gamma_red: + case opt_gamma_green: + case opt_gamma_blue: + case opt_gamma_all: case opt_int_array_constraint_word_list: memcpy (test_device->val[option].wa, value, test_device->opt[option].size); DBG (4, "sane_control_option: set option %d (%s) to %p\n", option, test_device->opt[option].name, (void *) value); + if (option == opt_gamma_all) { + print_gamma_table(gamma_all, GAMMA_ALL_SIZE); + } + if (option == opt_gamma_red) { + print_gamma_table(gamma_red, GAMMA_RED_SIZE); + } break; /* options with side-effects */ case opt_print_options: @@ -2298,6 +2407,10 @@ sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, break; case opt_int_array: /* Int array */ case opt_int_array_constraint_range: + case opt_gamma_red: + case opt_gamma_green: + case opt_gamma_blue: + case opt_gamma_all: case opt_int_array_constraint_word_list: memcpy (value, test_device->val[option].wa, test_device->opt[option].size); diff --git a/backend/test.h b/backend/test.h index dcd54b6..5b1b82b 100644 --- a/backend/test.h +++ b/backend/test.h @@ -100,6 +100,10 @@ typedef enum opt_int_constraint_word_list, opt_int_array, opt_int_array_constraint_range, + opt_gamma_red, + opt_gamma_green, + opt_gamma_blue, + opt_gamma_all, opt_int_array_constraint_word_list, opt_fixed_group, opt_fixed, diff --git a/backend/umax_pp_low.c b/backend/umax_pp_low.c index ddcf3da..569f824 100644 --- a/backend/umax_pp_low.c +++ b/backend/umax_pp_low.c @@ -935,7 +935,7 @@ sanei_umax_pp_initPort (int port, const char *name) char strmodes[160]; # endif # endif -# ifdef HAVE_DEV_PPBUS_PP_H +# ifdef HAVE_DEV_PPBUS_PPI_H int found = 0; int fd; # endif diff --git a/backend/v4l.c b/backend/v4l.c index 006e7f7..f9245d0 100644 --- a/backend/v4l.c +++ b/backend/v4l.c @@ -72,7 +72,6 @@ #include "../include/sane/saneopts.h" #include <sys/ioctl.h> -#include <asm/types.h> /* XXX glibc */ #define BACKEND_NAME v4l #include "../include/sane/sanei_backend.h" @@ -1046,7 +1045,7 @@ sane_start (SANE_Handle handle) /* v4l1 actually returns BGR when we ask for RGB, so convert it */ if (s->pict.palette == VIDEO_PALETTE_RGB24) { - __u32 loop; + uint32_t loop; DBG (3, "sane_start: converting from BGR to RGB\n"); for (loop = 0; loop < (s->window.width * s->window.height * 3); loop += 3) { diff --git a/backend/v4l.h b/backend/v4l.h index e6673d0..7698be1 100644 --- a/backend/v4l.h +++ b/backend/v4l.h @@ -29,145 +29,6 @@ #ifndef v4l_h #define v4l_h -#ifndef __LINUX_VIDEODEV_H -/* Kernel interface */ -/* Only the stuff we need. For more features, more defines are needed */ - -#define VID_TYPE_CAPTURE 1 /* Can capture */ -#define VID_TYPE_TUNER 2 /* Can tune */ -#define VID_TYPE_TELETEXT 4 /* Does teletext */ -#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ -#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ -#define VID_TYPE_CLIPPING 32 /* Can clip */ -#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ -#define VID_TYPE_SCALES 128 /* Scalable */ -#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ -#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ -#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ -#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ -#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ -#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ - -struct video_capability -{ - char name[32]; - int type; - int channels; /* Num channels */ - int audios; /* Num audio devices */ - int maxwidth; /* Supported width */ - int maxheight; /* And height */ - int minwidth; /* Supported width */ - int minheight; /* And height */ -}; - -struct video_picture -{ - __u16 brightness; - __u16 hue; - __u16 colour; - __u16 contrast; - __u16 whiteness; /* Black and white only */ - __u16 depth; /* Capture depth */ - __u16 palette; /* Palette in use */ -#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ -#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ -#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ -#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ -#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ -#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ -#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ -#define VIDEO_PALETTE_YUYV 8 -#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ -#define VIDEO_PALETTE_YUV420 10 -#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ -#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ -#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ -#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ -#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ -#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ -#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ -#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ -}; - -struct video_window -{ - __u32 x,y; /* Position of window */ - __u32 width,height; /* Its size */ - __u32 chromakey; - __u32 flags; - struct video_clip *clips; /* Set only */ - int clipcount; -#define VIDEO_WINDOW_INTERLACE 1 -#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ -#define VIDEO_CLIP_BITMAP -1 -/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ -#define VIDEO_CLIPMAP_SIZE (128 * 625) -}; - -#define VIDEO_MAX_FRAME 32 - -struct video_mbuf -{ - int size; /* Total memory to map */ - int frames; /* Frames */ - int offsets[VIDEO_MAX_FRAME]; -}; - -struct video_mmap -{ - unsigned int frame; /* Frame (0 - n) for double buffer */ - int height,width; - unsigned int format; /* should be VIDEO_PALETTE_* */ -}; - -struct video_channel -{ - int channel; - char name[32]; - int tuners; - __u32 flags; -#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ -#define VIDEO_VC_AUDIO 2 /* Channel has audio */ - __u16 type; -#define VIDEO_TYPE_TV 1 -#define VIDEO_TYPE_CAMERA 2 - __u16 norm; /* Norm set by channel */ -}; - -#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ -#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ -#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ -#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ -#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ -#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ -#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ -#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ -#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ -#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ -#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ -#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ -#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ -#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ -#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ -#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ -#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ -#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ -#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ -#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ -#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ -#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ -#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ -#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ -#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ -#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ -#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ -#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ -#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ - - -/* end of kernel interface */ -#endif /* !__LINUX_VIDEODEV_H */ - #include <../include/sane/sane.h> #define MAX_CHANNELS 32 diff --git a/backend/xerox_mfp.conf.in b/backend/xerox_mfp.conf.in index 39bf669..4fcbeb6 100644 --- a/backend/xerox_mfp.conf.in +++ b/backend/xerox_mfp.conf.in @@ -245,6 +245,9 @@ usb 0x0924 0x4293 #Xerox WorkCentre 3220 usb 0x0924 0x4294 +#Xerox WorkCentre 3225 +usb 0x0924 0x42dc + ################### ### Dell Models ### ################### |