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 ###  ###################  | 
