diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5c8758ebb..417565d1c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,12 +1,12 @@ # .gitlab-ci.yml -- to test some source code build scenarios -# Copyright (C) 2016-2019 Olaf Meeuwissen +# Copyright (C) 2016-2020 Olaf Meeuwissen # # License: GPL-3.0+ variables: REGISTRY_HUB: "registry.gitlab.com/sane-project/ci-envs" CONFIGURE_MINI: "--enable-silent-rules" - CONFIGURE_FULL: "--with-usb --enable-avahi --enable-pnm-backend --with-libcurl" + CONFIGURE_FULL: "--with-usb --with-avahi --enable-pnm-backend --with-libcurl --with-poppler-glib" stages: - tarball @@ -76,8 +76,8 @@ debian-10-full: - doc/sanei-html expire_in: 1 day -fedora-31-clang: - image: $REGISTRY_HUB:fedora-31-clang +fedora-32-clang: + image: $REGISTRY_HUB:fedora-32-clang variables: CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" <<: *compile_definition @@ -88,6 +88,13 @@ alpine-3.11-musl: CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" <<: *compile_definition +ubuntu-16.04-lts: + image: $REGISTRY_HUB:ubuntu-xenial-dist + variables: + CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" + MAKE_FLAGS: "CFLAGS=-Werror CXXFLAGS=-Werror" + <<: *compile_definition + # This snapshot stage job makes sure that the source tarball has all # it needs to rebuild itself, install everything built and cleans up # without leaving any droppings behind when uninstalling. The build diff --git a/AUTHORS b/AUTHORS index bb2bbfea4..0f3489dd9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,7 +1,3 @@ -Authors of the SANE standard: - - Andreas Beck and David Mosberger - Backends: abaton: David Huggins-Daines @@ -17,6 +13,7 @@ Backends: Mitsuru Okaniwa, Ulrich Deiters canon630u: Nathan Rutman canon_dr: m. allan noah (*) + canon_lide70: Juergen Ernst, pimvantend (*) canon_pp: Matthew Duggan, Simon Krix cardscan: m. allan noah (*) coolscan: Didier Carlier, Andreas Rick diff --git a/INSTALL.linux b/INSTALL.linux index 703b9e35a..316887171 100644 --- a/INSTALL.linux +++ b/INSTALL.linux @@ -24,6 +24,8 @@ $ make install - libpng-dev or similar - libcurl4-gnutls-dev or similar - libxml2-dev or similar + - libsnmp-dev or similar + - libpoppler-glib-dev or similar 2.2. Get the latest SANE backend from git: You can download "daily git snapshot" from here: diff --git a/LICENSE b/LICENSE index 442aa2631..73d018ea7 100644 --- a/LICENSE +++ b/LICENSE @@ -30,6 +30,9 @@ terms: to implement SANE interface conforming applications or libraries in any way he or she sees fit. + The standard is maintained at https://gitlab.com/sane-project/standard + and published at https://sane-project.gitlab.io/standard/. + Frequently Asked Questions about the SANE licensing: * Why don't you use the GNU LPGL ? diff --git a/NEWS b/NEWS index e9829e87e..1c6e6a5fb 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,26 @@ +## New with the next release + +### Backends + +- adds an `canon_lide70` backend + +### Documentation + +- removes the SANE Standard. This is now maintained as a separate + project at https://gitlab.com/sane-project/standard. HTML and PDF + versions can be found at https://sane-project.gitlab.io/standard/. + +### Build + +- removes the `--with-api-spec` option from `configure` +- replaces the `--enable-avahi` option with an `--with-avahi` that + defaults to enabling if possible. If the option is given and the + required support is not available, `configure` will exit with an + error. + + ## New with 1.0.30 (released 2020-05-17) This release fixes several security related issues and a build issue. diff --git a/README b/README index a51683eba..3614b8d9d 100644 --- a/README +++ b/README @@ -57,6 +57,8 @@ installed. - libgphoto2 (>=2.0): For the gphoto2 backend. + - a C++11 compliant C++ compiler for the genesys backend. + If you got the source straight from the git repository, as opposed to a source tarball, you will need a few more utilities. These utilities should normally *not* be needed for source archives downloaded from diff --git a/acinclude.m4 b/acinclude.m4 index 7c90c64f7..e10f8ceb2 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -613,7 +613,7 @@ for be in ${BACKENDS}; do ;; escl) - if test "x${enable_avahi}" != "xyes"; then + if test "x${with_avahi}" != "xyes"; then echo "*** $be backend requires AVAHI library - $DISABLE_MSG" backend_supported="no" fi @@ -629,7 +629,14 @@ for be in ${BACKENDS}; do if test "x${sane_cv_use_libjpeg}" != "xyes"; then echo "*** $be backend currently requires JPEG library - $DISABLE_MSG" backend_supported="no" + else + if test "x${ac_cv_func_jpeg_crop_scanline}" != "xyes" \ + || test "x${ac_cv_func_jpeg_skip_scanlines}" != "xyes"; then + echo "*** $be backend requires a newer JPEG library - $DISABLE_MSG" + backend_supported="no" + fi fi + ;; gphoto2) diff --git a/backend/Makefile.am b/backend/Makefile.am index 4a947bfa0..fceed172c 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 @@ -504,6 +531,7 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \ genesys/buffer.h genesys/buffer.cpp \ genesys/calibration.h \ genesys/command_set.h \ + genesys/command_set_common.h genesys/command_set_common.cpp \ genesys/conv.h genesys/conv.cpp \ genesys/device.h genesys/device.cpp \ genesys/enums.h genesys/enums.cpp \ @@ -512,6 +540,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 +561,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 @@ -682,10 +712,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,12 +934,12 @@ 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 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 diff --git a/backend/avision.c b/backend/avision.c index 55b5f4f1e..aa08119a5 100644 --- a/backend/avision.c +++ b/backend/avision.c @@ -4088,6 +4088,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]; @@ -6221,7 +6223,12 @@ do_cancel (Avision_Scanner* s) 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; @@ -6716,6 +6723,7 @@ reader_process (void *data) sigset_t sigterm_set; sigset_t ignore_set; struct SIGACTION act; + int old; FILE* fp; FILE* rear_fp = 0; /* used to store the deinterlaced rear data */ @@ -6757,21 +6765,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); @@ -6958,9 +6975,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 */ @@ -8332,7 +8362,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 +8590,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/canon_lide70-common.c b/backend/canon_lide70-common.c new file mode 100644 index 000000000..a0eb5c090 --- /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 +#include /* open */ +#include +#include +#include +#include /* usleep */ +#include +#include /* exp() */ +#ifdef HAVE_OS2_H +#include /* mode_t */ +#endif +#include + +#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 000000000..100a45fb3 --- /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 +#include +#include +#include +#include +#include + +#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 000000000..97b551c86 --- /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.conf.in b/backend/dll.conf.in index 92091cba5..cc7dfecf2 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 ddc76c317..363a33ff1 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.c b/backend/epsonds.c index fb9694a88..56d8bc631 100644 --- a/backend/epsonds.c +++ b/backend/epsonds.c @@ -47,6 +47,8 @@ #ifdef HAVE_SYS_TIME_H # include #endif +#include +#include #include #include "sane/saneopts.h" diff --git a/backend/escl.conf.in b/backend/escl.conf.in index 2aa625771..9c482b573 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 8df6c5c0e..c40fd985f 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->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->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"); - else - handler->scanner->default_color = strdup("RGB24"); - for (i = 1; i < handler->scanner->SupportedResolutionsSize; i++) + 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) { - if (val > handler->scanner->SupportedResolutions[i]) - val = handler->scanner->SupportedResolutions[i]; + 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; } - handler->scanner->default_resolution = val; + else + { + 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->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 82910bdfe..53ce7c725 100644 --- a/backend/escl/escl.h +++ b/backend/escl/escl.h @@ -43,6 +43,7 @@ #include "../include/sane/sane.h" #include +#include #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 690ff1eff..fdd5cfe83 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")) { - if (scanner->default_format) - free(scanner->default_format); - scanner->default_format = strdup("image/tiff"); + have_tiff = SANE_TRUE; + } +#endif +#if(defined HAVE_POPPLER_GLIB) + else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "application/pdf")) + { + 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 000000000..8740d2204 --- /dev/null +++ b/backend/escl/escl_crop.c @@ -0,0 +1,102 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2020 Thierry HUCHARD + + 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 +#include + +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 d6287efb0..8d6b6b636 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 000000000..93992182a --- /dev/null +++ b/backend/escl/escl_mupdf.c @@ -0,0 +1,256 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2019 Thierry HUCHARD + + 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 +#include +#include + +#include + +#if(defined HAVE_MUPDF) +#include +#endif + +#include + + +#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 279b9df8c..ee8c03c6c 100644 --- a/backend/escl/escl_newjob.c +++ b/backend/escl/escl_newjob.c @@ -68,18 +68,11 @@ static const char settings[] = " %s" \ " %d" \ " %d" \ - " Platen" \ + " %s" \ + " %s" \ + "%s" \ ""; -static char formatExtJPEG[] = - " image/jpeg"; - -static char formatExtPNG[] = - " image/png"; - -static char formatExtTIFF[] = - " image/tiff"; - /** * \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), + " %s", + 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), + " %s", + 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 000000000..ae85a3af7 --- /dev/null +++ b/backend/escl/escl_pdf.c @@ -0,0 +1,223 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2019 Thierry HUCHARD + + 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 +#include +#include +#include +#include + +#include + +#if(defined HAVE_POPPLER_GLIB) +#include +#endif + +#include + + +#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 18f6f3510..cf92449c1 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; - 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); + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + components = 4; + else + 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 7722d898d..64d779a24 100644 --- a/backend/escl/escl_reset.c +++ b/backend/escl/escl_reset.c @@ -31,13 +31,22 @@ #include +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 8f077a132..9fce801f6 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 68b51dcfa..9f4347a63 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, "State") == 0) - x = 1; + 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); + } + } } - if (x == 1 && strcmp((const char *)xmlNodeGetContent(node), "Idle") == 0) - *status = SANE_STATUS_GOOD; } - print_xml_s(node->children, status); + 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) { + printf ("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); + } + } + } + } + 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 52aec204a..98bc5f31e 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 38425a67d..c2b28dd49 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 5dc466c87..d24975e0f 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 4f2b1a9a5..0676def3a 100644 --- a/backend/fujitsu.conf.in +++ b/backend/fujitsu.conf.in @@ -255,3 +255,6 @@ usb 0x04c5 0x1522 #ScanSnap iX1500 usb 0x04c5 0x159f + +#fi-800R +usb 0x04c5 0x15fc diff --git a/backend/fujitsu.h b/backend/fujitsu.h index 4c2047477..3b3ce54ca 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 786ccd56b..b14a19f62 100644 --- a/backend/genesys.conf.in +++ b/backend/genesys.conf.in @@ -124,15 +124,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/calibration.h b/backend/genesys/calibration.h index f14aaa368..81d94eaf6 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 white_average_data; std::vector 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 ab3a4b607..4e22a562c 100644 --- a/backend/genesys/command_set.h +++ b/backend/genesys/command_set.h @@ -67,14 +67,12 @@ 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; + virtual void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) 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 +96,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, @@ -134,11 +131,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; @@ -159,6 +151,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 000000000..5576f68b3 --- /dev/null +++ b/backend/genesys/command_set_common.cpp @@ -0,0 +1,247 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2019 Povilas Kanapickas + + 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_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 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 000000000..784fcd732 --- /dev/null +++ b/backend/genesys/command_set_common.h @@ -0,0 +1,48 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2019 Povilas Kanapickas + + 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 index a87c46324..2f89e7303 100644 --- a/backend/genesys/conv.cpp +++ b/backend/genesys/conv.cpp @@ -146,9 +146,7 @@ void genesys_gray_lineart(Genesys_Device* dev, { 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); + (void) threshold; for (y = 0; y < lines; y++) { @@ -200,7 +198,7 @@ void genesys_deskew(Genesys_Scanner *s, const Genesys_Sensor& sensor) bg=0xff; } TIE(sanei_magic_findSkew(&s->params, dev->img_buffer.data(), - sensor.optical_res, sensor.optical_res, + sensor.full_resolution, sensor.full_resolution, &x, &y, &slope)); DBG(DBG_info, "%s: slope=%f => %f\n", __func__, slope, slope * 180 / M_PI); diff --git a/backend/genesys/device.cpp b/backend/genesys/device.cpp index ba035fd16..a9ad6e1dd 100644 --- a/backend/genesys/device.cpp +++ b/backend/genesys/device.cpp @@ -62,15 +62,24 @@ std::vector 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(method)); } @@ -80,6 +89,12 @@ std::vector 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(); @@ -124,10 +139,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 +224,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(dev.control[0]) << ' ' @@ -220,13 +242,7 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev) << static_cast(dev.control[4]) << ' ' << static_cast(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' @@ -260,13 +276,36 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev) return out; } -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 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); + 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) { + 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(old_val & reg.mask), + reg.mask}); + } + return backup; +} + } // namespace genesys diff --git a/backend/genesys/device.h b/backend/genesys/device.h index 6c744c9bd..eaf85cf73 100644 --- a/backend/genesys/device.h +++ b/backend/genesys/device.h @@ -55,6 +55,7 @@ #include "register.h" #include "usb_device.h" #include "scanner_interface.h" +#include "utilities.h" #include namespace genesys { @@ -77,22 +78,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 models; - double value() const { return SANE_UNFIX(value_); } - -private: - SANE_Fixed value_ = 0; + GenesysRegisterSettingSet regs; }; struct MethodResolutions @@ -106,6 +100,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(lhs) - static_cast(resolution)) < + std::abs(static_cast(rhs) - static_cast(resolution)); + }); + } + unsigned get_min_resolution_y() const { return *std::min_element(resolutions_y.begin(), resolutions_y.end()); @@ -143,51 +147,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 +230,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 get_resolutions(ScanMethod method) const; + + bool has_method(ScanMethod method) const; }; /** @@ -243,8 +265,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 +283,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 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 gamma_override_tables[3]; @@ -360,7 +365,7 @@ struct Genesys_Device 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 +387,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 f515cfdd0..0d18b914c 100644 --- a/backend/genesys/enums.cpp +++ b/backend/genesys/enums.cpp @@ -109,6 +109,188 @@ 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_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(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_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_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_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(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_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_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(id); break; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, MotorId id) +{ + switch (id) { + case MotorId::UNKNOWN: out << "UNKNOWN"; 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_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_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(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 810c4ca34..98a13da5d 100644 --- a/backend/genesys/enums.h +++ b/backend/genesys/enums.h @@ -201,9 +201,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,6 +225,21 @@ enum class ModelId : unsigned XEROX_TRAVELSCANNER_100, }; +inline void serialize(std::istream& str, ModelId& x) +{ + unsigned value; + serialize(str, value); + x = static_cast(value); +} + +inline void serialize(std::ostream& str, ModelId& x) +{ + unsigned value = static_cast(x); + serialize(str, value); +} + +std::ostream& operator<<(std::ostream& out, ModelId id); + enum class SensorId : unsigned { UNKNOWN = 0, @@ -232,6 +250,7 @@ enum class SensorId : unsigned CCD_DP665, CCD_DP685, CCD_DSMOBILE600, + CCD_DOCKETPORT_487, CCD_G4050, CCD_HP2300, CCD_HP2400, @@ -241,9 +260,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,6 +273,7 @@ enum class SensorId : unsigned CCD_UMAX, CCD_XP300, CIS_CANON_LIDE_35, + CIS_CANON_LIDE_60, CIS_CANON_LIDE_80, CIS_CANON_LIDE_100, CIS_CANON_LIDE_110, @@ -293,9 +316,12 @@ enum class AdcId : unsigned 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,6 +347,8 @@ 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, @@ -345,9 +373,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 +387,8 @@ enum class GpioId : unsigned XP300, }; +std::ostream& operator<<(std::ostream& out, GpioId id); + enum class MotorId : unsigned { UNKNOWN = 0, @@ -365,6 +398,7 @@ enum class MotorId : unsigned CANON_LIDE_200, CANON_LIDE_210, CANON_LIDE_35, + CANON_LIDE_60, CANON_LIDE_700, CANON_LIDE_80, CANON_4400F, @@ -380,9 +414,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 +428,8 @@ enum class MotorId : unsigned XP300, }; +std::ostream& operator<<(std::ostream& out, MotorId id); + enum class StepType : unsigned { FULL = 0, @@ -423,6 +462,7 @@ enum class AsicType : unsigned UNKNOWN = 0, GL646, GL841, + GL842, GL843, GL845, GL846, @@ -431,6 +471,83 @@ 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, + + // whether scanner must wait for the head while parking + MUST_WAIT = 1 << 10, + + // do dark and white calibration in one run + DARK_WHITE_CALIBRATION = 1 << 12, + + // allow custom gamma tables + CUSTOM_GAMMA = 1 << 13, + + // 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(static_cast(left) | static_cast(right)); +} + +inline ModelFlag& operator|=(ModelFlag& left, ModelFlag right) +{ + left = left | right; + return left; +} + +inline ModelFlag operator&(ModelFlag left, ModelFlag right) +{ + return static_cast(static_cast(left) & static_cast(right)); +} + +inline bool has_flag(ModelFlag flags, ModelFlag which) +{ + return (flags & which) == which; +} + + enum class ScanFlag : unsigned { NONE = 0, @@ -438,14 +555,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 +612,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(static_cast(left) | static_cast(right)); -} - -inline MotorFlag& operator|=(MotorFlag& left, MotorFlag right) -{ - left = left | right; - return left; -} - -inline MotorFlag operator&(MotorFlag left, MotorFlag right) -{ - return static_cast(static_cast(left) & static_cast(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 6c921c11c..46d79c962 100644 --- a/backend/genesys/error.cpp +++ b/backend/genesys/error.cpp @@ -45,6 +45,7 @@ #include "error.h" #include +#include 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 5aba8cf54..9c17aff7e 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 SANE_Status wrap_exceptions_to_status_code(const char* func, F&& function) { diff --git a/backend/genesys/fwd.h b/backend/genesys/fwd.h index 2d55f981b..ee45a44d4 100644 --- a/backend/genesys/fwd.h +++ b/backend/genesys/fwd.h @@ -56,7 +56,6 @@ struct Genesys_Calibration_Cache; class CommandSet; // device.h -class FixedFloat; struct Genesys_Gpo; struct MethodResolutions; struct Genesys_Model; @@ -75,7 +74,6 @@ class Image; // image_buffer.h class ImageBuffer; -class FakeBufferModel; class ImageBufferGenesysUsb; // image_pipeline.h @@ -88,12 +86,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 +111,6 @@ class ScannerInterfaceUsb; class TestScannerInterface; // sensor.h -class ResolutionFilter; struct GenesysFrontendLayout; struct Genesys_Frontend; struct SensorExposure; @@ -124,6 +121,10 @@ struct Genesys_Settings; struct SetupParams; struct ScanSession; +// value_filter.h +template class ValueFilter; +template class ValueFilterAny; + // test_usb_device.h class TestUsbDevice; diff --git a/backend/genesys/genesys.cpp b/backend/genesys/genesys.cpp index 7c25168d1..242738635 100644 --- a/backend/genesys/genesys.cpp +++ b/backend/genesys/genesys.cpp @@ -64,6 +64,7 @@ #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" @@ -162,9 +163,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 */ + float_to_fixed(0), // minimum + float_to_fixed(100), // maximum + float_to_fixed(1) // quantization }; static const SANE_Range threshold_curve_range = { @@ -308,6 +309,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(dev->model->sensor_id), @@ -316,33 +335,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 +374,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,25 +403,24 @@ 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((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(ydpi), static_cast(step_type), endpixel, - exposure_by_led, exposure); - return exposure; + return exposure; } @@ -474,6 +465,7 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S && 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_DOCKETPORT_487 && dev->model->sensor_id != SensorId::CCD_DP665 && dev->model->sensor_id != SensorId::CCD_DP685 && dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80 @@ -500,41 +492,25 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S 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 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 +521,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 image2(size, 0); - std::vector 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 +528,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 +564,117 @@ 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& 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); - break; - } - case AsicType::GL847: { - dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRLNCNT); - dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRMCNT); - break; - } + case AsicType::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); + max_table_nr = 4; break; } default: - throw SaneException("Unsupported asic type"); + throw SaneException("Unsupported ASIC type"); } + + if (table_nr > max_table_nr) { + throw SaneException("invalid table number %d", table_nr); + } + + std::vector 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) { + // BUG: some motor setup setting is wrong on GL841 scanners, as they need full motor table + // to be written even though this is not observed in the official 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: { + 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::GL842: { + // slope table addresses are fixed : 0x40000, 0x48000, 0x50000, 0x58000, 0x60000 + // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { + dev->interface->write_buffer(0x3c, 0x010000 + 0x200 * table_nr, table.data(), + table.size()); + } else { + dev->interface->write_gamma(0x28, 0x40000 + 0x8000 * table_nr, 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: { + // slope table addresses are fixed + dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, table.size(), + table.data()); + break; + } + default: + throw SaneException("Unsupported ASIC type"); + } + } bool scanner_is_motor_stopped(Genesys_Device& dev) @@ -794,9 +685,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 +732,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: @@ -878,6 +798,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 +829,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 +857,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 +884,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 +914,20 @@ 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) { + if (dev.cmd_set->needs_update_home_sensor_gpio()) { + dev.cmd_set->update_home_sensor_gpio(dev); + } + } + // FIXME: should porbably wait for some timeout Status status; for (unsigned i = 0;; ++i) { @@ -1015,12 +940,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); @@ -1037,6 +959,8 @@ 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 +971,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); } @@ -1076,15 +1006,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 +1014,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; } @@ -1174,18 +1089,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 +1145,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 +1157,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 +1180,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 +1198,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 +1212,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,6 +1225,1095 @@ void scanner_move_back_home_ta(Genesys_Device& dev) throw SaneException("Timeout waiting for XPA lamp to park"); } +void scanner_search_strip(Genesys_Device& dev, bool forward, bool black) +{ + DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); + + 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); + } + + // 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; + + 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(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((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(((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.pnm", + black ? "black" : "white", forward ? "fwd" : "bwd", pass); + sanei_genesys_write_pnm_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.pnm", + black ? "black" : "white", + forward ? "fwd" : "bwd", static_cast(pass)); + sanei_genesys_write_pnm_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 { + throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", + black ? "black" : "white"); + } +} + +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) +{ + 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; + } + } + + 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; + } + } + + 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; + + unsigned channels = 3; + unsigned lines = 1; + unsigned resolution = sensor.full_resolution; + + const Genesys_Sensor* calib_sensor = &sensor; + if (dev.model->asic_type == AsicType::GL843) { + lines = 8; + + // 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; + + calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, + dev.settings.scan_method); + + target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; + black_pixels = calib_sensor->black_pixels / factor; + + if (should_calibrate_only_active_area(dev, dev.settings)) { + float offset = dev.model->x_offset_ta; + start_pixel = static_cast((offset * calib_sensor->get_optical_resolution()) / MM_PER_INCH); + + float size = dev.model->x_size_ta; + target_pixels = static_cast((size * calib_sensor->get_optical_resolution()) / MM_PER_INCH); + } + + if (dev.model->model_id == ModelId::CANON_4400F && + dev.settings.scan_method == ScanMethod::FLATBED) + { + return; + } + } + + 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 = 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 { + first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + } + + if (dbg_log_image_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_info, "%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); + + Image second_line; + 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); + } + + 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 debug_image; + std::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++; + + 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 (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(debug_image_info.data()), + debug_image_info.size()); + sanei_genesys_write_pnm_file("gl_offset_all.pnm", + 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)); +} + +/* 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) +{ + DBG_HELPER_ARGS(dbg, "dpi = %d", dpi); + + 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; + } + } + + 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( + (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) + { + const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dpi, channels, + dev.settings.scan_method); + resolution = dpihw_sensor.shading_resolution; + } + + 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) + { + if (dev.settings.xres < sensor.full_resolution) { + coeff = 0.9f; + } + } + + unsigned lines = 10; + if (dev.model->asic_type == AsicType::GL841) { + lines = 1; + } + + 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) + { + calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, + dev.settings.scan_method); + } + + 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()) { + sanei_genesys_write_pnm_file("gl_coarse_gain.pnm", 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 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(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"); + } + } + + 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(DBG_info, "%s: gain=(%d,%d,%d)\n", __func__, + 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 + +SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) +{ + DBG_HELPER(dbg); + + float move = 0; + + 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(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((move * (dev.motor.base_ydpi / 4)) / MM_PER_INCH); + if (move > 20) { + scanner_move(dev, dev.model->default_method, static_cast(move), + Direction::FORWARD); + } + } else if (dev.model->asic_type == AsicType::GL124) { + gl124::move_to_calibration_area(&dev, sensor, regs); + } + + + 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]; + + 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; + + std::uint16_t min_exposure = 500; // only gl841 + std::uint16_t max_exposure = ((exp[0] + exp[1] + exp[2]) / 3) * 2; // only gl841 + + std::uint16_t top[3] = {}; + std::uint16_t bottom[3] = {}; + + if (dev.model->asic_type == AsicType::GL845 || + dev.model->asic_type == AsicType::GL846) + { + 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); + } + + dev.interface->write_registers(regs); + + 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 { + scanner_stop_action(dev); + dev.cmd_set->move_back_home(&dev, true); + return calib_sensor.exposure; + } + } + + auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes); + + scanner_stop_action(dev); + + if (dbg_log_image_data()) { + char fn[30]; + std::snprintf(fn, 30, "gl_led_%02d.pnm", i_test); + sanei_genesys_write_pnm_file(fn, image); + } + + 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(); + } + + dbg.vlog(DBG_info, "average: %d, %d, %d", avg[0], avg[1], avg[2]); + + acceptable = true; + + 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; + } + + // 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; + } + + } + } + } 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; + } + } + } + } + + 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] }); + } + + 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); + } + } + + dbg.vlog(DBG_info,"acceptable exposure: %d, %d, %d\n", exp[0], exp[1], exp[2]); + + return { exp[0], exp[1], exp[2] }; +} + void sanei_genesys_calculate_zmod(bool two_table, uint32_t exposure_time, const std::vector& slope_table, @@ -1295,8 +2322,6 @@ void sanei_genesys_calculate_zmod(bool two_table, unsigned buffer_acceleration_steps, uint32_t* out_z1, uint32_t* out_z2) { - DBG(DBG_info, "%s: two_table=%d\n", __func__, two_table); - // acceleration total time unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps, 0, std::plus()); @@ -1321,315 +2346,45 @@ void sanei_genesys_calculate_zmod(bool two_table, *out_z2 = sum % exposure_time; } -static uint8_t genesys_adjust_gain(double* applied_multi, double multi, uint8_t gain) -{ - double voltage, original_voltage; - uint8_t new_gain = 0; - - DBG(DBG_proc, "%s: multi=%f, gain=%d\n", __func__, multi, gain); - - voltage = 0.5 + gain * 0.25; - original_voltage = voltage; - - voltage *= multi; - - new_gain = static_cast((voltage - 0.5) * 4); - if (new_gain > 0x0e) - new_gain = 0x0e; - - voltage = 0.5 + (new_gain) * 0.25; - - *applied_multi = voltage / original_voltage; - - 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); - - return new_gain; -} - - -// todo: is return status necessary (unchecked?) -static void genesys_average_white(Genesys_Device* dev, Genesys_Sensor& sensor, int channels, - int channel, uint8_t* data, int size, int *max_average) -{ - - DBG_HELPER_ARGS(dbg, "channels=%d, channel=%d, size=%d", channels, channel, size); - int gain_white_ref, sum, range; - int average; - int i; - - range = size / 50; - - if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || - dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) - { - gain_white_ref = sensor.fau_gain_white_ref * 256; - } else { - gain_white_ref = sensor.gain_white_ref * 256; - } - - if (range < 1) - range = 1; - - size = size / (2 * range * channels); - - data += (channel * 2); - - *max_average = 0; - - while (size--) - { - sum = 0; - for (i = 0; i < range; i++) - { - sum += (*data); - sum += *(data + 1) * 256; - data += (2 * channels); /* byte based */ - } - - average = (sum / range); - if (average > *max_average) - *max_average = average; - } - - DBG(DBG_proc, "%s: max_average=%d, gain_white_ref = %d, finished\n", __func__, *max_average, - gain_white_ref); - - if (*max_average >= gain_white_ref) - throw SaneException(SANE_STATUS_INVAL); -} - -/* todo: understand, values are too high */ -static int -genesys_average_black (Genesys_Device * dev, int channel, - uint8_t * data, int pixels) -{ - int i; - int sum; - int pixel_step; - - DBG(DBG_proc, "%s: channel=%d, pixels=%d\n", __func__, channel, pixels); - - sum = 0; - - if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) - { - data += (channel * 2); - pixel_step = 3 * 2; - } - else - { - pixel_step = 2; - } - - for (i = 0; i < pixels; i++) - { - sum += *data; - sum += *(data + 1) * 256; - - data += pixel_step; - } - - DBG(DBG_proc, "%s = %d\n", __func__, sum / pixels); - - return sum / pixels; -} - - -// todo: check; it works but the lines 1, 2, and 3 are too dark even with the -// same offset and gain settings? -static void genesys_coarse_calibration(Genesys_Device* dev, Genesys_Sensor& sensor) -{ - DBG_HELPER_ARGS(dbg, "scan_mode = %d", static_cast(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; - - black_pixels = sensor.black_pixels - * dev->settings.xres / sensor.optical_res; - - unsigned channels = dev->settings.get_channels(); - - DBG(DBG_info, "channels %d y_size %f xres %d\n", channels, dev->model->y_size.value(), - dev->settings.xres); - unsigned size = static_cast(channels * 2 * dev->model->y_size * dev->settings.xres / - MM_PER_INCH); - /* 1 1 mm 1/inch inch/mm */ - - std::vector calibration_data(size); - std::vector all_data(size * 4, 1); - - dev->cmd_set->set_fe(dev, sensor, AFE_INIT); - - 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]); - - for (i = 0; i < 4; i++) /* read 4 lines */ - { - 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]); - } - - if (i == 1) /* second line */ - { - double applied_multi; - double gain_white_ref; - - if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || - dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) - { - gain_white_ref = sensor.fau_gain_white_ref * 256; - } else { - gain_white_ref = sensor.gain_white_ref * 256; - } - - // white and black are defined downwards - - uint8_t gain0 = genesys_adjust_gain(&applied_multi, - gain_white_ref / (white[0] - dark[0]), - 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)); - } - - if (i == 3) /* last line */ - { - double x, y, rate; - - for (j = 0; j < 3; j++) - { - - x = static_cast(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; - - uint8_t curr_offset = static_cast(rate); - - 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)); - - 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)); - - - dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false); - - if (is_testing_mode()) { - dev->interface->test_checkpoint("coarse_calibration"); - dev->cmd_set->end_scan(dev, &dev->calib_reg, true); - return; - } - - sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size); - std::memcpy(all_data.data() + i * size, calibration_data.data(), size); - if (i == 3) /* last line */ - { - std::vector all_data_8(size * 4 / 2); - unsigned int count; - - for (count = 0; count < static_cast(size * 4 / 2); count++) { - all_data_8[count] = all_data[count * 2 + 1]; - } - sanei_genesys_write_pnm_file("gl_coarse.pnm", all_data_8.data(), 8, channels, size / 6, 4); - } - - dev->cmd_set->end_scan(dev, &dev->calib_reg, true); - - if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) - { - for (j = 0; j < 3; j++) - { - genesys_average_white(dev, sensor, 3, j, calibration_data.data(), size, &white_average); - 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)); -} - /** * scans a white area with motor and lamp off to get the per CCD pixel offset * that will be used to compute shading coefficient * @param dev scanner's device */ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& local_reg, std::vector& 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) + { + pixels_per_line = dev->calib_session.output_pixels; + } else { + 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 +2399,54 @@ 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) + { + 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 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(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); @@ -1707,17 +2455,18 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_ } 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) { + if (dbg_log_image_data()) { sanei_genesys_write_pnm_file16((log_filename_prefix + "_shading.pnm").c_str(), calibration_data.data(), - channels, pixels_per_line, dev->calib_lines); + channels, pixels_per_line, dev->calib_session.params.lines); sanei_genesys_write_pnm_file16((log_filename_prefix + "_average.pnm").c_str(), out_average_data.data(), channels, out_pixels_per_line, 1); @@ -1725,10 +2474,12 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_ } -static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor) +static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& local_reg) { DBG_HELPER(dbg); - genesys_shading_calibration_impl(dev, sensor, dev->dark_average_data, true, "gl_black_"); + genesys_shading_calibration_impl(dev, sensor, local_reg, dev->dark_average_data, true, + "gl_black"); } /* * this function builds dummy dark calibration data so that we can @@ -1741,14 +2492,24 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_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; + } - 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; dev->dark_average_data.clear(); @@ -1756,8 +2517,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; } @@ -1811,7 +2571,7 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor 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 || @@ -1825,34 +2585,57 @@ 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_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& local_reg) { DBG_HELPER(dbg); - genesys_shading_calibration_impl(dev, sensor, dev->white_average_data, false, "gl_white_"); + 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 +2645,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; + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { + 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 calibration_data(size); - bool motor = true; - if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) - { - motor = false; - } - // turn on motor and lamp power - sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true); - sanei_genesys_set_motor_power(dev->calib_reg, motor); + 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) { + sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", calibration_data.data(), + 16, 1, pixels_per_line*channels, + dev->calib_session.params.lines); + } else { + sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", 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 +2727,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,7 +2754,7 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev, *average_white++ = white_sum; } - if (DBG_LEVEL >= DBG_data) { + if (dbg_log_image_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(), @@ -2085,13 +2865,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 +3155,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 +3231,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 +3295,27 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_ // contains 16bit words in little endian std::vector 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 +3329,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 +3364,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 +3379,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 +3426,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, @@ -2684,6 +3480,7 @@ 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: compute_averaged_planar (dev, sensor, shading_data.data(), pixels_per_line, @@ -2756,9 +3553,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 +3608,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 +3616,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 +3636,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 +3667,62 @@ 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((dev->model->x_size * dev->settings.xres) / MM_PER_INCH); - } - else - { - pixels_per_line = sensor.sensor_pixels; + } else { + pixels_per_line = static_cast((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) - { + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) + { dev->cmd_set->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); + 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_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 (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); + } - 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); + dev->interface->record_progress_message("genesys_white_shading_calibration"); + genesys_white_shading_calibration(dev, sensor, local_reg); - dev->interface->record_progress_message("genesys_dark_shading_calibration"); - genesys_dark_shading_calibration(dev, sensor); - genesys_repark_sensor_before_shading(dev); - } + genesys_repark_sensor_after_white_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); - 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)) { + genesys_dummy_dark_shading(dev, sensor); + } } } @@ -2982,68 +3746,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); + // 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); - /* since all the registers are set up correctly, just use them */ + 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); - 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); - - 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 +3812,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 +3820,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); }); @@ -3074,14 +3830,14 @@ 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(); + if (!has_flag(dev->model->flags, ModelFlag::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, + (sensor.x_size_calib_mm * dev->settings.xres) / MM_PER_INCH, dev->calib_lines, */ } @@ -3099,7 +3855,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 +3885,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 first_line(total_size); std::vector 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 +3912,46 @@ 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); - } - - dev->cmd_set->end_scan(dev, &dev->reg, true); - - dev->interface->sleep_ms(1000); - seconds++; - - dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false); - - wait_until_buffer_non_empty(dev); - sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); dev->cmd_set->end_scan(dev, &dev->reg, true); - /* compute difference between the two scans */ - for (pixel = 0; pixel < total_size; pixel++) - { + // 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]; - } - } - if (dev->session.params.depth == 16) { - first_average /= pixel; - second_average /= pixel; - difference = static_cast(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)); + 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 (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; - } + first_average /= total_pixels; + second_average /= total_pixels; + + if (dbg_log_image_data()) { + sanei_genesys_write_pnm_file("gl_warmup1.pnm", first_line.data(), + dev->session.params.depth, channels, + total_size / (lines * channels), lines); + sanei_genesys_write_pnm_file("gl_warmup2.pnm", second_line.data(), + dev->session.params.depth, channels, + total_size / (lines * channels), lines); + } + + DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average, + second_average); + + 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); @@ -3243,6 +3974,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,31 +3986,23 @@ 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) + { + dev->cmd_set->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 */ @@ -3304,9 +4028,13 @@ 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); } @@ -3334,7 +4062,7 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) dev->cmd_set->move_to_ta(dev); } - dev->cmd_set->init_regs_for_scan(dev, sensor); + dev->cmd_set->init_regs_for_scan(dev, sensor, dev->reg); /* no lamp during scan */ if (lamp_off) { @@ -3344,7 +4072,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); } @@ -3439,7 +4167,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); @@ -3579,12 +4307,12 @@ static unsigned pick_resolution(const std::vector& resolutions, unsign static void calc_parameters(Genesys_Scanner* s) { DBG_HELPER(dbg); - double tl_x = 0, tl_y = 0, br_x = 0, br_y = 0; + float 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); + tl_x = fixed_to_float(s->pos_top_left_x); + tl_y = fixed_to_float(s->pos_top_left_y); + br_x = fixed_to_float(s->pos_bottom_right_x); + br_y = fixed_to_float(s->pos_bottom_right_y); s->params.last_frame = true; /* only single pass scanning supported */ @@ -3612,10 +4340,10 @@ static void calc_parameters(Genesys_Scanner* s) const auto& sensor = sanei_genesys_find_sensor_any(s->dev); // hardware settings - if (static_cast(s->resolution) > sensor.optical_res && + if (static_cast(s->resolution) > sensor.full_resolution && s->dev->settings.disable_interpolation) { - s->dev->settings.xres = sensor.optical_res; + s->dev->settings.xres = sensor.full_resolution; } else { s->dev->settings.xres = s->resolution; } @@ -3631,7 +4359,7 @@ static void calc_parameters(Genesys_Scanner* s) /* 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) || + if (has_flag(s->dev->model->flags, ModelFlag::SIS_SENSOR) || s->dev->model->asic_type == AsicType::GL847 || s->dev->model->asic_type == AsicType::GL124 || s->dev->model->asic_type == AsicType::GL845 || @@ -3698,7 +4426,7 @@ static void calc_parameters(Genesys_Scanner* s) s->dev->settings.tl_y = tl_y; // threshold setting - s->dev->settings.threshold = static_cast(2.55 * (SANE_UNFIX(s->threshold))); + s->dev->settings.threshold = static_cast(2.55f * (fixed_to_float(s->threshold))); // color filter if (s->color_filter == "Red") { @@ -3724,9 +4452,9 @@ static void calc_parameters(Genesys_Scanner* s) /* 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)) + if ((s->swdespeck || s->swcrop || s->swdeskew || s->swderotate ||(fixed_to_float(s->swskip)>0)) + && (!s->preview) + && (s->bit_depth <= 8)) { s->dev->buffer_image = true; } @@ -3760,7 +4488,7 @@ static void create_bpp_list (Genesys_Scanner * s, const std::vector& 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 +4504,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 +4529,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 +4582,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 +4648,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(s.dev->model->x_size)); - s.opt_y_range = create_range(static_cast(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(s.dev->model->x_size_ta)); - s.opt_y_range = create_range(static_cast(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 +4672,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 +4765,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(model->x_size)); - s->opt_y_range = create_range(static_cast(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 +4843,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__); @@ -4222,7 +4948,7 @@ static void init_options(Genesys_Scanner* s) 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); + s->threshold = float_to_fixed(50); /* BW threshold curve */ s->opt[OPT_THRESHOLD_CURVE].name = "threshold-curve"; @@ -4508,36 +5234,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 +5289,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 +5348,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 +5360,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 +5377,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 = 30; bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration, const std::string& path) @@ -4781,8 +5521,8 @@ static void genesys_buffer_image(Genesys_Scanner *s) * 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); + dev->cmd_set->move_back_home(dev, has_flag(dev->model->flags, ModelFlag::MUST_WAIT)); + dev->parking = !has_flag(s->dev->model->flags, ModelFlag::MUST_WAIT); } /* in case of dynamic lineart, we have buffered gray data which @@ -4805,9 +5545,9 @@ static void genesys_buffer_image(Genesys_Scanner *s) dev->total_bytes_read = 0; /* update params */ - s->params.lines = total / s->params.bytes_per_line; - if (DBG_LEVEL >= DBG_io2) - { + s->params.lines = total / s->params.bytes_per_line; + + if (dbg_log_image_data()) { 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); @@ -4850,8 +5590,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 +5604,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 +5644,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 +5711,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,27 +5733,43 @@ 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"); - } - - dbg.vstatus("open device '%s'", dev->file_name.c_str()); - if (is_testing_mode()) { - auto interface = std::unique_ptr{new TestScannerInterface{dev}}; + // 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(); + + dev->model = &get_matching_usb_dev(vendor_id, product_id, bcd_device).model(); + + auto interface = std::unique_ptr{ + 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{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(); @@ -5031,7 +5789,9 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle) init_options(s); - sanei_genesys_init_cmd_set(s->dev); + DBG_INIT(); + + s->dev->cmd_set = create_cmd_set(s->dev->model->asic_type); // FIXME: we create sensor tables for the sensor, this should happen when we know which sensor // we will select @@ -5039,21 +5799,6 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle) // 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); - }); - } } SANE_GENESYS_API_LINKAGE @@ -5174,7 +5919,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(val))); + dbg.vlog(DBG_proc, "value: %f", fixed_to_float(*reinterpret_cast(val))); return; } case SANE_TYPE_STRING: { @@ -5639,9 +6384,7 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int case OPT_EXPIRATION_TIME: if (*reinterpret_cast(val) != s->expiration_time) { s->expiration_time = *reinterpret_cast(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: @@ -5873,6 +6616,20 @@ void sane_start_impl(SANE_Handle handle) throw SaneException("top left y >= bottom right y"); } + // fetch stored calibration + 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); + }); + } + /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ @@ -5902,7 +6659,7 @@ void sane_start_impl(SANE_Handle handle) if (s->swskip > 0 && IS_ACTIVE(OPT_SWSKIP)) { auto status = sanei_magic_isBlank(&s->params, s->dev->img_buffer.data(), - SANE_UNFIX(s->swskip)); + fixed_to_float(s->swskip)); if (status == SANE_STATUS_NO_DOCS && s->dev->model->is_sheetfed) { DBG(DBG_info, "%s: blank page, recurse\n", __func__); @@ -5986,7 +6743,7 @@ 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); @@ -6086,10 +6843,10 @@ void sane_cancel_impl(SANE_Handle handle) /* 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->cmd_set->move_back_home (s->dev, has_flag(s->dev->model->flags, + ModelFlag::MUST_WAIT)); - s->dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT); + s->dev->parking = !has_flag(s->dev->model->flags, ModelFlag::MUST_WAIT); } } else diff --git a/backend/genesys/gl124.cpp b/backend/genesys/gl124.cpp index 054f1ef0b..31d0b2740 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& 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 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(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(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(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,119 +587,54 @@ 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(motor_profile.step_type) << REG_0xA0S_STEPSEL) | (static_cast(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, @@ -737,37 +645,39 @@ static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens dev->interface->write_register(REG_0x115, dev->settings.threshold); /* monochrome / color scan */ - r = sanei_genesys_get_address (reg, REG_0x04); switch (session.params.depth) { case 8: - r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); + 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, sensor.register_dpiset); - reg->set16(REG_DPISET, dpiset_reg); - DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset_reg); - - r = sanei_genesys_get_address(reg, REG_0x06); - r->value |= REG_0x06_GAIN4; + 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,18 +704,21 @@ 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; @@ -823,11 +727,9 @@ static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens // 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(motor_profile.step_type)); @@ -870,24 +769,10 @@ 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 ***/ @@ -909,21 +794,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(dev->model->x_offset); - start += static_cast(settings.tl_x); - start = static_cast((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((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((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(start); + session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; @@ -953,17 +841,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 +917,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 +954,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 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; + unsigned channels = 3; + unsigned resolution = sensor.shading_resolution; - 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 calib_lines = + static_cast(dev->model->y_size_calib_mm * resolution / MM_PER_INCH); - unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres); - - resolution /= ccd_size_divisor; - dev->calib_lines /= ccd_size_divisor; // reducing just because we reduced the resolution - - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, - dev->calib_channels, + 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(dev->model->y_offset_calib_white); move = static_cast((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 +1001,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 @@ -1274,52 +1025,21 @@ void CommandSetGl124::wait_for_motor_stop(Genesys_Device* dev) const /** @brief set up registers for the actual scan */ -void CommandSetGl124::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const +void CommandSetGl124::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const { DBG_HELPER(dbg); - float move; - int move_dpi; - float start; + auto session = calculate_scan_session(dev, sensor, dev->settings); - 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(dev->model->y_offset); - move += static_cast(dev->settings.tl_y); - move = static_cast((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(move - 500), + if (dev->settings.get_channels() * dev->settings.yres >= 600 && session.params.starty > 700) { + scanner_move(*dev, dev->model->default_method, + static_cast(session.params.starty - 500), Direction::FORWARD); - move=500; + session.params.starty = 500; } - DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - - /* start */ - start = static_cast(dev->model->x_offset); - start += static_cast(dev->settings.tl_x); - start /= sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres); - start = static_cast((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(start); - session.params.starty = static_cast(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); + init_regs_for_scan_session(dev, sensor, ®s, session); } /** @@ -1330,8 +1050,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 +1058,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 +1068,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 +1084,18 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso ptr = buffer.data(); /* iterate on both sensor segment */ - for(x=0;xsession.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 +1113,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 +1133,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 +1143,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 line(size); - // write registers and scan data dev->interface->write_registers(regs); @@ -1486,14 +1161,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()) { + sanei_genesys_write_pnm_file("gl124_movetocalarea.pnm", image); } } @@ -1505,513 +1179,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 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 first_line(total_size); - std::vector 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.xressettings.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 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(sensor.gain_white_ref) * coeff) / max[j]; - - /* turn logical gain value into gain code, checking for overflow */ - code = static_cast(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 +1270,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 +1283,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 +1409,11 @@ 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 create_gl124_cmd_set() -{ - return std::unique_ptr(new CommandSetGl124{}); -} - } // namespace gl124 } // namespace genesys diff --git a/backend/genesys/gl124.h b/backend/genesys/gl124.h index cdf8faf5a..b3a34dbe0 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& slope_table, int steps); - -class CommandSetGl124 : public CommandSet +class CommandSetGl124 : public CommandSetCommon { public: ~CommandSetGl124() override = default; @@ -122,16 +60,13 @@ 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(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, @@ -148,8 +83,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; @@ -175,9 +108,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, diff --git a/backend/genesys/gl646.cpp b/backend/genesys/gl646.cpp index 04ee85ec2..853bdde50 100644 --- a/backend/genesys/gl646.cpp +++ b/backend/genesys/gl646.cpp @@ -63,9 +63,345 @@ namespace { constexpr unsigned CALIBRATION_LINES = 10; } // namespace -static void gl646_send_slope_table(Genesys_Device* dev, int table_nr, - const std::vector& 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); + +/** + * 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); + +static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + const ScanSession& session, bool move, + std::vector& 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 @@ -104,44 +440,6 @@ static void gl646_stop_motor(Genesys_Device* dev) dev->interface->write_register(0x0f, 0x00); } -/** - * find the closest match in mode tables for the given resolution and scan mode. - * @param sensor id of the sensor - * @param required required resolution - * @param color true is color mode - * @return the closest resolution for the sensor and mode - */ -static unsigned get_closest_resolution(SensorId sensor_id, int required, unsigned channels) -{ - unsigned best_res = 0; - unsigned best_diff = 9600; - - for (const auto& sensor : *s_sensors) { - if (sensor_id != sensor.sensor_id) - continue; - - // exit on perfect match - if (sensor.resolutions.matches(required) && sensor.matches_channel_count(channels)) { - DBG(DBG_info, "%s: match found for %d\n", __func__, required); - return required; - } - - // computes distance and keep mode if it is closer than previous - if (sensor.matches_channel_count(channels)) { - for (auto res : sensor.resolutions.resolutions()) { - unsigned curr_diff = std::abs(static_cast(res) - static_cast(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 @@ -157,7 +455,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 +474,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 +481,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(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 +570,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 +600,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 +648,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 +674,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 +693,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 +705,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 +719,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 +798,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 +813,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 +820,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 +830,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); @@ -578,32 +870,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 +900,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 +916,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 +932,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 +947,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 +1055,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& 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 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(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 +1116,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 +1174,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(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 +1398,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 +1517,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 +1555,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 +1699,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 +1715,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 +1746,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 +1779,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 +1791,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"); } @@ -1575,166 +1799,61 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) DBG(DBG_info, "%s: scanhead is still moving\n", __func__); } -/** - * Automatically set top-left edge of the scan area by scanning an - * area at 300 dpi from very top of scanner - * @param dev device stucture describing the scanner - */ -void CommandSetGl646::search_start_position(Genesys_Device* dev) const -{ - DBG_HELPER(dbg); - Genesys_Settings settings; - unsigned int resolution, x, y; - - /* we scan at 300 dpi */ - resolution = get_closest_resolution(dev->model->sensor_id, 300, 1); - - // FIXME: the current approach of doing search only for one resolution does not work on scanners - // whith employ different sensors with potentially different settings. - const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 1, - dev->model->default_method); - - /* fill settings for a gray level scan */ - settings.scan_method = dev->model->default_method; - settings.scan_mode = ScanColorMode::GRAY; - settings.xres = resolution; - settings.yres = resolution; - settings.tl_x = 0; - settings.tl_y = 0; - settings.pixels = 600; - settings.requested_pixels = settings.pixels; - settings.lines = dev->model->search_lines; - settings.depth = 8; - settings.color_filter = ColorFilter::RED; - - settings.disable_interpolation = 0; - settings.threshold = 0; - - // scan the desired area - std::vector 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; + unsigned calib_lines = + static_cast(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; } - 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; + compute_session(dev, session, calib_sensor); - settings.disable_interpolation = dev->settings.disable_interpolation; - settings.threshold = dev->settings.threshold; + dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); - // 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; - } + 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 @@ -1748,7 +1867,8 @@ 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 +void CommandSetGl646::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const { DBG_HELPER(dbg); @@ -1756,95 +1876,7 @@ void CommandSetGl646::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sens 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(dev->model->y_offset); - } - - // add tl_y to base movement - } - move += static_cast(settings.tl_y); - - if (move < 0) { - DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move); - move = 0; - } - } - move = static_cast((move * dev->motor.optical_ydpi) / MM_PER_INCH); - DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - - float start = static_cast(settings.tl_x); - if (xcorrection) { - if (settings.scan_method == ScanMethod::FLATBED) { - start += static_cast(dev->model->x_offset); - } else { - start += static_cast(dev->model->x_offset_ta); - } - } - start = static_cast((start * sensor.optical_res) / MM_PER_INCH); - - ScanSession session; - session.params.xres = settings.xres; - session.params.yres = settings.yres; - session.params.startx = static_cast(start); - session.params.starty = static_cast(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); + init_regs_for_scan_session(dev, sensor, ®s, session); } /** @@ -1857,9 +1889,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 +1933,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; + ScanColorMode scan_mode = ScanColorMode::COLOR_SINGLE_PASS; + if (dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS) { + scan_mode = ScanColorMode::GRAY; } - else - { - settings.scan_mode = ScanColorMode::GRAY; + + // offset calibration is always done in color mode + unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; + + 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; } - resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels); + compute_session(dev, session, sensor); - /* offset calibration is always done in color mode */ - settings.scan_method = dev->model->default_method; - settings.xres = resolution; - settings.yres = resolution; - settings.tl_x = 0; - settings.tl_y = 0; - settings.pixels = (sensor.sensor_pixels * resolution) / sensor.optical_res; - settings.requested_pixels = settings.pixels; - settings.lines = 1; - settings.depth = 16; - settings.color_filter = ColorFilter::RED; - - settings.disable_interpolation = 0; - settings.threshold = 0; - - /* colors * bytes_per_color * scan lines */ - total_size = settings.pixels * channels * 2 * 1; + // colors * bytes_per_color * scan lines + unsigned total_size = pixels * channels * 2 * 1; std::vector line(total_size); @@ -1968,38 +1995,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]; + if (dbg_log_image_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); - } + sanei_genesys_write_pnm_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 +2111,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; + // 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; - settings.disable_interpolation = 0; - settings.threshold = 0; + 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 +2161,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(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.pnm", static_cast(bottom)); + sanei_genesys_write_pnm_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 +2188,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 +2216,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 +2225,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); + unsigned resolution = dev->settings.xres; + unsigned channels = 3; - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED); - black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res; + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, + ScanMethod::FLATBED); + black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.full_resolution; - DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels); + 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.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; + 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 +2272,32 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens std::vector 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]; + if (dbg_log_image_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); + sanei_genesys_write_pnm_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]; + if (dbg_log_image_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); + sanei_genesys_write_pnm_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 +2314,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]; + if (dbg_log_image_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); - } + sanei_genesys_write_pnm_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 +2346,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 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 +2358,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( + (dev->model->x_size_ta * dev->settings.xres) / MM_PER_INCH); } - else - { - settings.tl_x = dev->model->x_offset_ta; - settings.pixels = static_cast((dev->model->x_size_ta * resolution) / MM_PER_INCH); - } - settings.requested_pixels = settings.pixels; - settings.lines = CALIBRATION_LINES; - settings.depth = 8; - settings.color_filter = ColorFilter::RED; - settings.disable_interpolation = 0; - settings.threshold = 0; + 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; + } + + start = static_cast((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(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 line; @@ -2509,75 +2428,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++; + if (dbg_log_image_data()) { + std::sprintf(title, "gl646_gain%02d.pnm", pass); + sanei_genesys_write_pnm_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 (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; - } - } + // 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(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; + // 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; - /* adjusts gain for the channel */ - if (average[k] < calib_sensor.gain_white_ref) - dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1); + // adjusts gain for the channel + if (average[k] < calib_sensor.gain_white_ref) { + dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1); + } - DBG(DBG_proc, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k], - dev->frontend.get_gain(k)); - } + DBG(DBG_info, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k], + dev->frontend.get_gain(k)); + } } - if (channels < 3) { - dev->frontend.set_gain(1, dev->frontend.get_gain(0)); - dev->frontend.set_gain(2, dev->frontend.get_gain(0)); - } - - DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__, - dev->frontend.get_gain(0), - dev->frontend.get_gain(1), - dev->frontend.get_gain(2)); + 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 +2489,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; + // set up for a full width 2 lines gray scan without moving + unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; - 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 = 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); - // setup for scan - setup_for_scan(dev, local_sensor, &dev->reg, settings, true, false, false, false); + dev->cmd_set->init_regs_for_scan_session(dev, local_sensor, &dev->reg, session); /* we are not going to move, so clear these bits */ - dev->reg.find_reg(0x02).value &= ~(REG_0x02_FASTFED | REG_0x02_AGOHOME); - - /* don't enable any correction for this scan */ - dev->reg.find_reg(0x01).value &= ~REG_0x01_DVDSET; + dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED; /* copy to local_reg */ *local_reg = dev->reg; @@ -2632,66 +2533,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 +2574,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 +2631,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 +2654,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,16 +2669,7 @@ 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 */ @@ -2851,81 +2683,37 @@ void CommandSetGl646::move_to_ta(Genesys_Device* dev) const simple_move(dev, static_cast(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& data, - const char* scan_identifier) + const ScanSession& session, bool move, + std::vector& 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); + gl646_set_fe(dev, sensor, AFE_SET, session.params.xres); - /* no shading correction and not watch dog for simple scan */ - dev->reg.find_reg(0x01).value &= ~(REG_0x01_DVDSET | REG_0x01_DOGENB); - if (shading) { - dev->reg.find_reg(0x01).value |= REG_0x01_DVDSET; - } - - /* enable gamma table for the scan */ - dev->reg.find_reg(0x05).value |= REG_0x05_GMMENB; + // 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 +2734,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 buffer(settings.pixels * 3 * bpp); + if (dev->model->is_cis && session.params.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) { + auto pixels_count = session.params.pixels; - /* 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); - } - } + std::vector 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 @@ -3002,30 +2782,42 @@ static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, 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); + 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((distance * settings.xres) / MM_PER_INCH); - settings.depth = 8; - settings.color_filter = ColorFilter::RED; + unsigned lines = static_cast((distance * resolution) / MM_PER_INCH); - settings.disable_interpolation = 0; - settings.threshold = 0; + // round up to multiple of 3 in case of CIS scanner + if (dev->model->is_cis) { + lines = ((lines + 2) / 3) * 3; + } - std::vector data; - simple_scan(dev, sensor, settings, true, true, false, data, "simple_move"); + auto* regs = &dev->reg; + + ScanSession session; + session.params.xres = resolution; + session.params.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 = 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::NONE; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { + session.params.flags |= ScanFlag::USE_XPA; + } + compute_session(dev, session, sensor); + + dev->cmd_set->init_regs_for_scan_session(dev, sensor, regs, session); + + std::vector data; + simple_scan(dev, sensor, session, true, data, "simple_move"); } /** @@ -3130,22 +2922,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"); @@ -3167,7 +2953,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 +2989,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((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((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 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 +3013,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(dev->model->y_offset); + move = dev->model->y_offset; // add tl_y to base movement } - move += static_cast(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((move * dev->motor.optical_ydpi) / MM_PER_INCH); - float start = static_cast(settings.tl_x); + move = static_cast((move * dev->motor.base_ydpi) / MM_PER_INCH); + float start = settings.tl_x; if (settings.scan_method == ScanMethod::FLATBED) { - start += static_cast(dev->model->x_offset); + start += dev->model->x_offset; } else { - start += static_cast(dev->model->x_offset_ta); + start += dev->model->x_offset_ta; } - start = static_cast((start * sensor.optical_res) / MM_PER_INCH); + start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; @@ -3411,7 +3046,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 +3062,5 @@ void CommandSetGl646::asic_boot(Genesys_Device *dev, bool cold) const throw SaneException("not implemented"); } -std::unique_ptr create_gl646_cmd_set() -{ - return std::unique_ptr(new CommandSetGl646{}); -} - } // namespace gl646 } // namespace genesys diff --git a/backend/genesys/gl646.h b/backend/genesys/gl646.h index afcfa0588..b05377838 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& 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,16 +64,13 @@ 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(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, @@ -472,8 +87,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; @@ -495,9 +108,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, diff --git a/backend/genesys/gl646_registers.h b/backend/genesys/gl646_registers.h index 2fe8f19e3..6ee9549eb 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 470f9bab6..b07c8a102 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_HELPER(dbg); - DBG(DBG_proc, "%s\n", __func__); - - dev->reg.clear(); - if (dev->model->model_id == ModelId::CANON_LIDE_80) { - gl841_init_lide80(dev); - return ; - } - - for (addr = 1; addr <= 0x0a; addr++) { - dev->reg.init_reg(addr, 0); - } - for (addr = 0x10; addr <= 0x27; addr++) { - dev->reg.init_reg(addr, 0); - } - dev->reg.init_reg(0x29, 0); - for (addr = 0x2c; addr <= 0x39; addr++) - dev->reg.init_reg(addr, 0); - for (addr = 0x3d; addr <= 0x3f; addr++) - dev->reg.init_reg(addr, 0); - for (addr = 0x52; addr <= 0x5a; addr++) - dev->reg.init_reg(addr, 0); - for (addr = 0x5d; addr <= 0x87; addr++) - dev->reg.init_reg(addr, 0); - - - dev->reg.find_reg(0x01).value = 0x20; /* (enable shading), CCD, color, 1M */ + 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; } - - dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */ - dev->reg.find_reg(0x02).value |= REG_0x02_AGOHOME; - sanei_genesys_set_motor_power(dev->reg, true); - dev->reg.find_reg(0x02).value |= REG_0x02_FASTFED; - - dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ; /* lamp on */ - dev->reg.find_reg(0x03).value |= REG_0x03_AVEENB; - - if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) { - // AD front end - dev->reg.find_reg(0x04).value = (2 << REG_0x04S_AFEMOD) | 0x02; + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x01, 0x82); } - else /* Wolfson front end */ + + dev->reg.init_reg(0x02, 0x38); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x02, 0x10); + } + + dev->reg.init_reg(0x03, 0x5f); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x03, 0x50); + } + + 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); + + dev->reg.init_reg(0x05, 0x00); // disable gamma, 24 clocks/pixel + + sanei_genesys_set_dpihw(dev->reg, sensor.register_dpihw); + + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x05, 0x4c); + } + + dev->reg.init_reg(0x06, 0x18); + if (dev->model->model_id == ModelId::CANON_LIDE_80) { + dev->reg.init_reg(0x06, 0x38); + } + 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(0x04).value |= 1 << REG_0x04S_AFEMOD; + dev->reg.init_reg(0x06, 0xb8); } - const auto& sensor = sanei_genesys_find_sensor_any(dev); + dev->reg.init_reg(0x07, 0x00); + dev->reg.init_reg(0x08, 0x00); - dev->reg.find_reg(0x05).value = 0x00; /* disable gamma, 24 clocks/pixel */ + 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); - 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; + // 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); + + 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); + + 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); + } + + dev->reg.init_reg(0x3d, 0x00); + dev->reg.init_reg(0x3e, 0x00); + dev->reg.init_reg(0x3f, 0x00); + + 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 + + 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 { - throw SaneException("Cannot handle sensor pixel count %d", sensor.sensor_pixels); - } - sanei_genesys_set_dpihw(dev->reg, sensor, dpihw); - - dev->reg.find_reg(0x06).value |= REG_0x06_PWRBIT; - dev->reg.find_reg(0x06).value |= REG_0x06_GAIN4; - - /* XP300 CCD needs different clock and clock/pixels values */ - if (dev->model->sensor_id != SensorId::CCD_XP300 && - dev->model->sensor_id != SensorId::CCD_DP685 && - dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICPRO_3600) - { - dev->reg.find_reg(0x06).value |= 0 << REG_0x06S_SCANMOD; - dev->reg.find_reg(0x09).value |= 1 << REG_0x09S_CLKSET; - } - else - { - dev->reg.find_reg(0x06).value |= 0x05 << REG_0x06S_SCANMOD; /* 15 clocks/pixel */ - dev->reg.find_reg(0x09).value = 0; /* 24 MHz CLKSET */ + 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); + } } - dev->reg.find_reg(0x1e).value = 0xf0; /* watch-dog time */ + 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 - dev->reg.find_reg(0x17).value |= 1 << REG_0x17S_TGW; + 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); + } + } - dev->reg.find_reg(0x19).value = 0x50; - - dev->reg.find_reg(0x1d).value |= 1 << REG_0x1DS_TGSHLD; - - dev->reg.find_reg(0x1e).value |= 1 << REG_0x1ES_WDTIME; - -/*SCANFED*/ - dev->reg.find_reg(0x1f).value = 0x01; - -/*BUFSEL*/ - dev->reg.find_reg(0x20).value = 0x20; - -/*LAMPPWM*/ - dev->reg.find_reg(0x29).value = 0xff; - -/*BWHI*/ - dev->reg.find_reg(0x2e).value = 0x80; - -/*BWLOW*/ - dev->reg.find_reg(0x2f).value = 0x80; - -/*LPERIOD*/ - dev->reg.find_reg(0x38).value = 0x4f; - dev->reg.find_reg(0x39).value = 0xc1; - -/*VSMPW*/ - dev->reg.find_reg(0x58).value |= 3 << REG_0x58S_VSMPW; - -/*BSMPW*/ - dev->reg.find_reg(0x59).value |= 3 << REG_0x59S_BSMPW; - -/*RLCSEL*/ - dev->reg.find_reg(0x5a).value |= REG_0x5A_RLCSEL; - -/*STOPTIM*/ - dev->reg.find_reg(0x5e).value |= 0x2 << REG_0x5ES_STOPTIM; - - sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 1); + 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__); -} + 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); -// 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& 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*/ + dev->interface->write_0x8c(0x10, 0x94); + dev->interface->write_register(0x09, 0x10); - 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"); + // 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; } - - std::vector table(steps * 2); - for(i = 0; i < steps; i++) { - table[i * 2] = slope_table[i] & 0xff; - table[i * 2 + 1] = slope_table[i] >> 8; - } - - if (DBG_LEVEL >= DBG_io) - { - std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); - for (i = 0; i < steps; i++) { - std::sprintf (msg+strlen(msg), ",%d", slope_table[i]); - } - DBG(DBG_io, "%s: %s\n", __func__, msg); - } - - if (dev->interface->is_mock()) { - dev->interface->record_slope_table(table_nr, slope_table); - } - dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(), steps * 2); } static void gl841_set_lide80_fe(Genesys_Device* dev, uint8_t set) { DBG_HELPER(dbg); - if (set == AFE_INIT) - { - DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, - static_cast(dev->model->adc_id)); + if (set == AFE_INIT) { + dev->frontend = dev->frontend_initial; - 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(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(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; + 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, 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; + reg->set8(0x25, (scan_lines >> 16) & 0xf); + reg->set8(0x26, (scan_lines >> 8) & 0xff); + reg->set8(0x27, scan_lines & 0xff); - r = sanei_genesys_get_address (reg, 0x02); - r->value &= ~0x01; /*LONGCURV OFF*/ - r->value &= ~0x80; /*NOT_HOME OFF*/ + reg->set8(0x02, 0x00); - r->value &= ~0x10; + reg->set8(0x67, 0x3f); + reg->set8(0x68, 0x3f); - r->value &= ~0x06; + reg->set8(REG_STEPNO, 1); + reg->set8(REG_FASTNO, 1); - r->value &= ~0x08; - - r->value &= ~0x20; - - r->value &= ~0x40; - - r = sanei_genesys_get_address (reg, 0x67); - r->value = 0x3f; - - r = sanei_genesys_get_address (reg, 0x68); - r->value = 0x3f; - - r = sanei_genesys_get_address(reg, REG_STEPNO); - r->value = 0; - - r = sanei_genesys_get_address(reg, REG_FASTNO); - r->value = 0; - - r = sanei_genesys_get_address (reg, 0x69); - r->value = 0; - - r = sanei_genesys_get_address (reg, 0x6a); - r->value = 0; - - r = sanei_genesys_get_address (reg, 0x5f); - r->value = 0; + reg->set8(0x69, 1); + reg->set8(0x6a, 1); + reg->set8(0x5f, 1); } /** @brief write motor table frequency @@ -814,207 +531,119 @@ 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(flags)); - unsigned int fast_exposure = 0; + DBG_HELPER_ARGS(dbg, "feed_steps=%d, flags=%x", feed_steps, static_cast(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 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; -/* 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; + reg->set8(0x25, 0); + reg->set8(0x26, 0); + reg->set8(0x27, 0); - 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; + reg->find_reg(0x02).value &= ~0x01; /*LONGCURV OFF*/ + reg->find_reg(0x02).value &= ~0x80; /*NOT_HOME OFF*/ - 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->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(scan_step_type), + scan_exposure_time, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(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 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 +651,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(scan_step_type))) { + if (feed_steps < (slow_table.table.size() >> static_cast(motor_profile.step_type))) { /*TODO: what should we do here?? go back to exposure calculation?*/ - feed_steps = slow_table.steps_count >> static_cast(scan_step_type); + feed_steps = slow_table.table.size() >> static_cast(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(scan_step_type)) + 2) { + unsigned max_fast_slope_steps_count = step_multiplier; + if (feed_steps > (slow_table.table.size() >> static_cast(motor_profile.step_type)) + 2) { max_fast_slope_steps_count = (feed_steps - - (slow_table.steps_count >> static_cast(scan_step_type))) / 2; + (slow_table.table.size() >> static_cast(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 +686,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(scan_step_type))) + else if (feed_steps < fast_table.table.size() * 2 + + (slow_table.table.size() >> static_cast(motor_profile.step_type))) { use_fast_fed = 0; DBG(DBG_info, "%s: feed too short, slow move forced.\n", __func__); @@ -1071,113 +701,66 @@ 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(scan_step_type))) - + fast_table.pixeltime_sum*2 + slow_table.pixeltime_sum; + (fast_table.table.back() << static_cast(fast_profile->step_type)) / 4 * + (feed_steps - fast_table.table.size()*2 - + (slow_table.table.size() >> static_cast(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(scan_step_type))) - + slow_table.pixeltime_sum; + (feed_steps - (slow_table.table.size() >> static_cast(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 (use_fast_fed) { - feedl = feed_steps - fast_table.steps_count * 2 - - (slow_table.steps_count >> static_cast(scan_step_type)); - } else if ((feed_steps << static_cast(scan_step_type)) < slow_table.steps_count) { + feedl = feed_steps - fast_table.table.size() * 2 - + (slow_table.table.size() >> static_cast(motor_profile.step_type)); + } else if ((feed_steps << static_cast(motor_profile.step_type)) < slow_table.table.size()) { feedl = 0; } else { - feedl = (feed_steps << static_cast(scan_step_type)) - slow_table.steps_count; + feedl = (feed_steps << static_cast(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 - */ + 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; - r = sanei_genesys_get_address (reg, 0x3d); - r->value = (feedl >> 16) & 0xf; - r = sanei_genesys_get_address (reg, 0x3e); - r->value = (feedl >> 8) & 0xff; - r = sanei_genesys_get_address (reg, 0x3f); - r->value = feedl & 0xff; - r = sanei_genesys_get_address (reg, 0x5e); - r->value &= ~0xe0; - - r = sanei_genesys_get_address (reg, 0x25); - r->value = (scan_lines >> 16) & 0xf; - r = sanei_genesys_get_address (reg, 0x26); - r->value = (scan_lines >> 8) & 0xff; - r = sanei_genesys_get_address (reg, 0x27); - r->value = scan_lines & 0xff; - - r = sanei_genesys_get_address (reg, 0x02); - r->value &= ~0x01; /*LONGCURV OFF*/ - r->value &= ~0x80; /*NOT_HOME OFF*/ - r->value |= 0x10; - - r->value &= ~0x06; + if (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); + 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, 1, back_table.table, 256); - - gl841_send_slope_table(dev, 2, slow_table.table, 256); - - if (use_fast_fed) { - gl841_send_slope_table(dev, 3, fast_table.table, 256); - } - - if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { - gl841_send_slope_table(dev, 4, fast_table.table, 256); - } + 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 +768,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 +796,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(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(motor_profile.step_type) << 6)); + reg->set8(0x68, 0x3f | (static_cast(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 +814,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, dev->settings.threshold); + reg->set8(0x2f, dev->settings.threshold); /* monochrome / color scan */ - r = sanei_genesys_get_address (reg, 0x04); switch (session.params.depth) { case 8: - r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); + 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 +931,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 +965,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 +991,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,42 +1029,31 @@ 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(); @@ -1634,32 +1075,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(dev->model->x_offset); - start += static_cast(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 - start = static_cast((start * sensor.optical_res) / MM_PER_INCH); + 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; + + int move_dpi = dev->motor.base_ydpi; + move = static_cast((move * move_dpi) / MM_PER_INCH); + + float start = dev->model->x_offset; + start += dev->settings.tl_x; + start = static_cast((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 = 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; - + session.params.xres = dev->settings.xres; + session.params.yres = dev->settings.yres; + session.params.startx = static_cast(start); + session.params.starty = static_cast(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 +1180,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 +1212,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 +1227,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 +1297,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 +1316,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 +1324,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 +1349,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 +1364,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 +1375,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(dev->model->eject_feed); - if (dev->document) - { - feed_mm += static_cast(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 +1407,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 +1501,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 +1527,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,436 +1573,63 @@ 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__); + 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 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 data(size); - - dev->cmd_set->begin_scan(dev, sensor, &local_reg, true); - - if (is_testing_mode()) { - dev->interface->test_checkpoint("search_start_position"); - dev->cmd_set->end_scan(dev, &local_reg, true); - dev->reg = local_reg; - return; - } - - wait_until_buffer_non_empty(dev); - - // now we're on target, we can read data - sanei_genesys_read_data_from_scanner(dev, data.data(), size); - - if (DBG_LEVEL >= DBG_data) { - sanei_genesys_write_pnm_file("gl841_search_position.pnm", data.data(), 8, 1, pixels, - dev->model->search_lines); - } - - dev->cmd_set->end_scan(dev, &local_reg, true); - - /* update regs to copy ASIC internal state */ - dev->reg = local_reg; - - for (auto& sensor_update : - sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method)) - { - sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels, - dev->model->search_lines); - } -} - -// sets up register for coarse gain calibration -// todo: check it for scanners using it -void CommandSetGl841::init_regs_for_coarse_calibration(Genesys_Device* dev, - const Genesys_Sensor& sensor, - Genesys_Register_Set& regs) const -{ - DBG_HELPER(dbg); - - ScanSession session; - session.params.xres = dev->settings.xres; - session.params.yres = dev->settings.yres; - session.params.startx = 0; - session.params.starty = 0; - session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel(); - session.params.lines = 20; - session.params.depth = 16; - session.params.channels = dev->settings.get_channels(); - session.params.scan_method = dev->settings.scan_method; - session.params.scan_mode = dev->settings.scan_mode; - session.params.color_filter = dev->settings.color_filter; - session.params.flags = ScanFlag::DISABLE_SHADING | - ScanFlag::DISABLE_GAMMA | - ScanFlag::SINGLE_LINE | - ScanFlag::IGNORE_LINE_DISTANCE; - compute_session(dev, session, sensor); - - init_regs_for_scan_session(dev, sensor, ®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; + DBG_HELPER(dbg); - /* initial calibration reg values */ - regs = dev->reg; + unsigned channels = 3; - ydpi = dev->motor.base_ydpi; - if (dev->model->motor_id == MotorId::PLUSTEK_OPTICPRO_3600) /* TODO PLUSTEK_3600: 1200dpi not yet working, produces dark bar */ - { - ydpi = 600; - } - if (dev->model->motor_id == MotorId::CANON_LIDE_80) { - ydpi = gl841_get_dpihw(dev); - /* get over extra dark area for this model. - It looks like different devices have dark areas of different width - due to manufacturing variability. The initial value of starty was 140, - but it moves the sensor almost past the dark area completely in places - on certain devices. - - On a particular device the black area starts at roughly position - 160 to 230 depending on location (the dark area is not completely - parallel to the frame). - */ - starty = 70; - } - - dev->calib_channels = 3; - dev->calib_lines = dev->model->shading_lines; - - unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); - unsigned factor = sensor.optical_res / resolution; - - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, dev->calib_channels, + 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(dev->model->y_size_calib_dark_white_mm * resolution / MM_PER_INCH); + unsigned starty = + static_cast(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 +void CommandSetGl841::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) 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(dev->model->y_offset_calib_white); - } - - DBG(DBG_info, "%s move=%f steps\n", __func__, move); - - move += static_cast(dev->model->y_offset); - DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - - move += static_cast(dev->settings.tl_y); - DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - - move = static_cast((move * move_dpi) / MM_PER_INCH); - -/* start */ - start = static_cast(dev->model->x_offset); - - start += static_cast(dev->settings.tl_x); - - start = static_cast((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(start); - session.params.starty = static_cast(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); + auto session = calculate_scan_session(dev, sensor, dev->settings); + init_regs_for_scan_session(dev, sensor, ®s, session); } @@ -2581,216 +1658,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(dev->model->y_offset_calib_white); - move = static_cast((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 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 +1672,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 +1683,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 +1704,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 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 line(total_size); dev->frontend.set_gain(0, 0); dev->frontend.set_gain(1, 0); @@ -2873,13 +1737,13 @@ 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); @@ -2887,9 +1751,9 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& /* search for minimal value */ average=0; - for(i=0;ireg.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 +1825,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 first_line(total_size); - std::vector 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 +1862,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 +1884,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.pnm", turn); + sanei_genesys_write_pnm_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 +1938,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 +1946,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 +1966,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.pnm", turn); + sanei_genesys_write_pnm_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 +2028,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 +2037,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 +2075,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 +2128,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(dev->model->y_offset_calib_white); - move = static_cast((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 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(283 - 208 / gain[j]); - } else if (dev->model->adc_id == AdcId::CANON_LIDE_80) { - out_gain = static_cast(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 +2148,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 +2184,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 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 +2218,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((10 * dpi) / MM_PER_INCH); - - pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res; - - /* 20 cm max length for calibration sheet */ - length = static_cast(((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 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 +2226,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(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 + pixels = dev->session.pixel_endx - dev->session.pixel_startx; + pixels *= 4; - /* 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); + // 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 +2273,7 @@ void CommandSetGl841::send_shading_data(Genesys_Device* dev, const Genesys_Senso for(x=0;xinterface->write_register(0x0e, 0x01); + dev->interface->write_register(0x0e, 0x00); + } -std::unique_ptr create_gl841_cmd_set() -{ - return std::unique_ptr(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 5e24249db..769065f8e 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,16 +60,13 @@ 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(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, @@ -86,8 +83,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,15 +98,16 @@ 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; 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, diff --git a/backend/genesys/gl841_registers.h b/backend/genesys/gl841_registers.h index 8e0c20414..2fac27823 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 000000000..439b9750e --- /dev/null +++ b/backend/genesys/gl842.cpp @@ -0,0 +1,1018 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2010-2013 Stéphane Voltz + Copyright (C) 2020 Povilas Kanapickas + + 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 +#include + +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(); + + 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); + + 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 + 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 + + dev.reg.init_reg(0x1f, 0x01); + + dev.reg.init_reg(0x20, 0x27); // 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 + + dev.reg.init_reg(0x5e, 0x01); // DECSEL, STOPTIM + 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 + + dev.reg.init_reg(0x67, 0x7f); // STEPSEL, MTRPWM: overwritten during motor setup + dev.reg.init_reg(0x68, 0x7f); // FSTPSEL, FASTPWM: 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 + + 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); + + 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); +} + +static void gl842_set_ad_fe(Genesys_Device* dev) +{ + for (const auto& reg : dev->frontend.regs) { + dev->interface->write_fe_register(reg.address, reg.value); + } +} + +// Set values of analog frontend +void 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) { + gl842_set_ad_fe(dev); + 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)); + } + + if (dev->model->sensor_id == SensorId::CCD_KVSS080) { + for (unsigned i = 0; i < 3; i++) { + 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(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(motor_profile.step_type), + scan_lines, scan_dummy, feed_steps, static_cast(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; + } + + 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(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(motor_profile.step_type) << REG_0x67S_STEPSEL, + REG_0x67_STEPSEL); + reg->set8_mask(REG_0x68, static_cast(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, dev->settings.threshold); + reg->set8(REG_0x2F, dev->settings.threshold); + + // 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(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); + + 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"); + } + 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); + + dev->read_buffer.clear(); + dev->read_buffer.alloc(session.buffer_size_read); + + build_image_pipeline(dev, session); + + dev->read_active = true; + + dev->session = session; + + dev->total_bytes_read = 0; + dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines; +} + +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: 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((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((start * settings.xres) / MM_PER_INCH); + + ScanSession session; + session.params.xres = settings.xres; + session.params.yres = settings.yres; + session.params.startx = static_cast(start); + session.params.starty = static_cast(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); + } + + 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((offset * dev->settings.xres) / MM_PER_INCH); + + float size = dev->model->x_size_ta; + size = static_cast((size * dev->settings.xres) / MM_PER_INCH); + + calib_pixels_offset = static_cast(offset); + calib_pixels = static_cast(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: move_to_ta() function has already been called and the sensor is at the + // transparency adapter + move = static_cast(dev->model->y_offset_calib_white_ta - + dev->model->y_offset_sensor_to_ta); + flags |= ScanFlag::USE_XPA; + } else { + move = static_cast(dev->model->y_offset_calib_white); + } + + move = static_cast((move * resolution) / MM_PER_INCH); + unsigned calib_lines = static_cast(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::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const +{ + DBG_HELPER(dbg); + ScanSession session = calculate_scan_session(dev, sensor, dev->settings); + + init_regs_for_scan_session(dev, sensor, ®s, 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 gamma(size * 2 * 3); + + std::vector rgamma = get_gamma_table(dev, sensor, GENESYS_RED); + std::vector ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN); + std::vector 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); + } + + // 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; +} + +/** @brief move sensor to transparency adaptor + * Move sensor to the calibration of the transparency adapator (XPA). + * @param dev device to use + */ +void CommandSetGl842::move_to_ta(Genesys_Device* dev) const +{ + DBG_HELPER(dbg); + + const auto& resolution_settings = dev->model->get_resolution_settings(dev->model->default_method); + float resolution = resolution_settings.get_min_resolution_y(); + + unsigned multiplier = 16; + unsigned feed = static_cast(multiplier * (dev->model->y_offset_sensor_to_ta * resolution) / + MM_PER_INCH); + scanner_move(*dev, dev->model->default_method, feed, Direction::FORWARD); +} + +/** + * 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 final_data(length, 0); + + unsigned count = 0; + if (offset < 0) { + count += (-offset); + length -= (-offset); + offset = 0; + } + if (static_cast(length) + offset > static_cast(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 000000000..c49d90f6e --- /dev/null +++ b/backend/genesys/gl842.h @@ -0,0 +1,131 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2010-2013 Stéphane Voltz + + 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(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 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 move_to_ta(Genesys_Device* dev) const override; + + void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data, + int size) const override; + + ScanSession calculate_scan_session(const Genesys_Device* dev, + const Genesys_Sensor& sensor, + const Genesys_Settings& settings) const override; + + void asic_boot(Genesys_Device* dev, bool cold) const override; +}; + +enum SlopeTable +{ + SCAN_TABLE = 0, // table 1 at 0x4000 + BACKTRACK_TABLE = 1, // table 2 at 0x4800 + STOP_TABLE = 2, // table 3 at 0x5000 + FAST_TABLE = 3, // table 4 at 0x5800 + HOME_TABLE = 4, // table 5 at 0x6000 +}; + +} // namespace 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 000000000..b6934ceec --- /dev/null +++ b/backend/genesys/gl842_registers.h @@ -0,0 +1,285 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2019 Povilas Kanapickas + + 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 + +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 f83ac8d46..5a0c50a65 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; - } + switch (regs->get8(REG_0x9D) & 0x0c) { + case 0x04: return 2; + case 0x08: return 4; + default: return 1; } - DBG(DBG_io, "%s: step multiplier is %d\n", __func__, value); - return value; } -/** copy sensor specific settings */ -static void gl843_setup_sensor(Genesys_Device* dev, const Genesys_Sensor& sensor, - Genesys_Register_Set* regs) -{ - DBG_HELPER(dbg); - for (const auto& custom_reg : sensor.custom_regs) { - regs->set8(custom_reg.address, custom_reg.value); - } - if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE) && - dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && - dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300 && - dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7500I) - { - regs->set8(0x7d, 0x90); - } - - dev->segment_order = sensor.segment_order; -} - - /** @brief set all registers to default values . * This function is called only once at the beginning and * fills register startup values for registers reused across scans. @@ -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,50 +627,10 @@ 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); + dev->interface->write_buffer(0x3c, 0x3ff000, data, 32); } } -// 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& slope_table, - int steps) -{ - DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); - - int i; - char msg[10000]; - - std::vector table(steps * 2); - for (i = 0; i < steps; i++) - { - table[i * 2] = slope_table[i] & 0xff; - table[i * 2 + 1] = slope_table[i] >> 8; - } - - if (DBG_LEVEL >= DBG_io) - { - std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); - for (i = 0; i < steps; i++) { - std::sprintf (msg+strlen(msg), "%d", slope_table[i]); - } - DBG(DBG_io, "%s: %s\n", __func__, msg); - } - - if (dev->interface->is_mock()) { - dev->interface->record_slope_table(table_nr, slope_table); - } - - // slope table addresses are fixed : 0x40000, 0x48000, 0x50000, 0x58000, 0x60000 - // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14); - dev->interface->write_gamma(0x28, 0x40000 + 0x8000 * table_nr, table.data(), steps * 2, - ScannerInterface::FLAG_SWAP_REGISTERS); - - // FIXME: remove this when updating tests - gl843_set_buffer_address(dev, 0); -} - static void gl843_set_ad_fe(Genesys_Device* dev) { for (const auto& reg : dev->frontend.regs) { @@ -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(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,132 @@ 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(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(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; } - 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(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 +797,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 +815,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(motor_profile.step_type) << REG_0x67S_STEPSEL, 0xc0); - reg->set8_mask(REG_0x68, static_cast(motor_profile.step_type) << REG_0x68S_FSTPSEL, 0xc0); + reg->set8_mask(REG_0x68, static_cast(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 +871,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 +879,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; - } - if (use_shdarea) { - r->value |= REG_0x01_SHDAREA; + } 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 { - r->value &= ~REG_0x01_SHDAREA; + use_shdarea = session.params.xres > 600; } - r = sanei_genesys_get_address (reg, REG_0x03); - if (dev->model->model_id == ModelId::CANON_8600F) { - r->value |= REG_0x03_AVEENB; + if (use_shdarea) { + reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; } else { - r->value &= ~REG_0x03_AVEENB; + 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 @@ -1049,43 +935,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, dev->settings.threshold); + reg->set8(REG_0x2F, dev->settings.threshold); /* monochrome / color scan */ - r = sanei_genesys_get_address(reg, REG_0x04); switch (session.params.depth) { case 8: - r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); + 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 +976,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 +987,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 +998,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,37 +1046,13 @@ 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(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); - - /*** 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); + 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); dev->read_buffer.clear(); dev->read_buffer.alloc(session.buffer_size_read); @@ -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(dev->model->x_offset_ta); + // note: 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(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((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(settings.tl_x); - start = static_cast((start * sensor.optical_res) / MM_PER_INCH); + start = static_cast((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(start); + session.params.starty = static_cast(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: - case GpioId::CANON_8600F: + 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) { - 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::CANON_8600F: + 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::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,179 +1358,49 @@ 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(dev->model->x_offset_ta); - offset /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); - offset = static_cast((offset * calib_sensor.optical_res) / MM_PER_INCH); + unsigned calib_pixels = 0; + unsigned calib_pixels_offset = 0; - float size = static_cast(dev->model->x_size_ta); - size /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); - size = static_cast((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((offset * dev->settings.xres) / MM_PER_INCH); - dev->calib_pixels_offset = static_cast(offset); - dev->calib_pixels = static_cast(size); + float size = dev->model->x_size_ta; + size = static_cast((size * dev->settings.xres) / MM_PER_INCH); + + calib_pixels_offset = static_cast(offset); + calib_pixels = static_cast(size); + } else { + calib_pixels_offset = 0; + calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; } - else - { - dev->calib_pixels_offset = 0; - dev->calib_pixels = calib_sensor.sensor_pixels / factor; - } - - dev->calib_resolution = resolution; ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | - ScanFlag::DISABLE_BUFFER_FULL_MOVE | - ScanFlag::IGNORE_LINE_DISTANCE; + ScanFlag::DISABLE_BUFFER_FULL_MOVE; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) @@ -1838,22 +1408,29 @@ void CommandSetGl843::init_regs_for_shading(Genesys_Device* dev, const Genesys_S // note: move_to_ta() function has already been called and the sensor is at the // transparency adapter move = static_cast(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(dev->model->y_offset_calib_white); } move = static_cast((move * resolution) / MM_PER_INCH); + unsigned calib_lines = static_cast(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,18 @@ 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 +void CommandSetGl843::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const { DBG_HELPER(dbg); - float move; - int move_dpi; - float start; + ScanSession session = calculate_scan_session(dev, sensor, dev->settings); - 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(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(dev->model->y_offset); - } - } - - move += static_cast(dev->settings.tl_y); - move = static_cast((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(dev->model->x_offset_ta); - } else { - start = static_cast(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(start + dev->settings.tl_x); - start = static_cast((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(start); - session.params.starty = static_cast(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); + init_regs_for_scan_session(dev, sensor, ®s, session); } /** @@ -1975,8 +1481,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 +1492,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; + return scanner_led_calibration(*dev, sensor, regs); } - - -/** - * average dark pixels of a 8 bits scan of a given channel - */ -static int dark_average_channel(const Image& image, unsigned black, unsigned channel) -{ - auto channels = get_pixel_channels(image.get_format()); - - unsigned avg[3]; - - // computes average values on black margin - for (unsigned ch = 0; ch < channels; ch++) { - avg[ch] = 0; - unsigned count = 0; - // FIXME: start with the second line because the black pixels often have noise on the first - // line; the cause is probably incorrectly cleaned up previous scan - for (std::size_t y = 1; y < image.get_height(); y++) { - for (unsigned j = 0; j < black; j++) { - avg[ch] += image.get_raw_channel(j, y, ch); - count++; - } - } - if (count > 0) { - avg[ch] /= count; - } - DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]); - } - DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]); - return avg[channel]; -} - -/** @brief calibrate AFE offset - * Iterate doing scans at target dpi until AFE offset if correct. One - * color line is scanned at a time. Scanning head doesn't move. - * @param dev device to calibrate - */ void CommandSetGl843::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { - DBG_HELPER(dbg); - - if (dev->frontend.layout.type != FrontendType::WOLFSON) - return; - - unsigned channels; - int pass, resolution, lines; - int topavg[3], bottomavg[3], avg[3]; - int top[3], bottom[3], black_pixels, pixels, factor, dpihw; - - /* offset calibration is always done in color mode */ - channels = 3; - lines = 8; - - // compute divider factor to compute final pixels number - dpihw = sensor.get_logical_hwdpi(dev->settings.xres); - factor = sensor.optical_res / dpihw; - resolution = dpihw; - - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, - dev->settings.scan_method); - - int target_pixels = calib_sensor.sensor_pixels / factor; - int start_pixel = 0; - black_pixels = calib_sensor.black_pixels / factor; - - if ((dev->settings.scan_method == ScanMethod::TRANSPARENCY || - dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) && - dev->model->model_id == ModelId::CANON_8600F && - dev->settings.xres == 4800) - { - start_pixel = static_cast(dev->model->x_offset_ta); - start_pixel /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); - start_pixel = static_cast((start_pixel * calib_sensor.optical_res) / MM_PER_INCH); - - target_pixels = static_cast(dev->model->x_size_ta); - target_pixels /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); - target_pixels = static_cast((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 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(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; - - dpihw = sensor.get_logical_hwdpi(dpi); - factor=sensor.optical_res/dpihw; - - // coarse gain calibration is always done in color mode - unsigned channels = 3; - - /* follow CKSEL */ - if (dev->model->sensor_id == SensorId::CCD_KVSS080) { - if(dev->settings.xressettings.scan_method == ScanMethod::TRANSPARENCY || - dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) - { - flags |= ScanFlag::USE_XPA; - } - - const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, - dev->settings.scan_method); - - ScanSession session; - session.params.xres = resolution; - session.params.yres = resolution; - session.params.startx = 0; - session.params.starty = 0; - session.params.pixels = target_pixels; - session.params.lines = lines; - session.params.depth = 8; - session.params.channels = channels; - session.params.scan_method = dev->settings.scan_method; - session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; - session.params.color_filter = dev->settings.color_filter; - session.params.flags = flags; - compute_session(dev, session, calib_sensor); - std::size_t pixels = session.output_pixels; - - try { - init_regs_for_scan_session(dev, calib_sensor, ®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 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(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); + 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 CommandSetGl843::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; - int dpihw; - int resolution; - int factor; + (void) sensor; - /* setup scan */ - *channels=3; - resolution=600; - dpihw = sensor.get_logical_hwdpi(resolution); - resolution=dpihw; + 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, + 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; + 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; + 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 = 8; - session.params.channels = *channels; + 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 = ScanFlag::DISABLE_SHADING | - ScanFlag::DISABLE_GAMMA | - ScanFlag::SINGLE_LINE | - ScanFlag::IGNORE_LINE_DISTANCE; + 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); - dev->interface->write_registers(*reg); } /** @@ -2654,7 +1621,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 +1658,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 +1670,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 +1682,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 @@ -2766,7 +1724,8 @@ void CommandSetGl843::move_to_ta(Genesys_Device* dev) const float resolution = resolution_settings.get_min_resolution_y(); unsigned multiplier = 16; - if (dev->model->model_id == ModelId::CANON_8400F) { + if (dev->model->model_id == ModelId::CANON_8400F || + dev->model->model_id == ModelId::CANON_4400F) { multiplier = 4; } unsigned feed = static_cast(multiplier * (dev->model->y_offset_sensor_to_ta * resolution) / @@ -2774,198 +1733,6 @@ void CommandSetGl843::move_to_ta(Genesys_Device* dev) const scanner_move(*dev, dev->model->default_method, feed, Direction::FORWARD); } - -/** @brief search for a full width black or white strip. - * This function searches for a black or white stripe across the scanning area. - * When searching backward, the searched area must completely be of the desired - * color since this area will be used for calibration which scans forward. - * @param dev scanner device - * @param forward true if searching forward, false if searching backward - * @param black true if searching for a black strip, false for a white strip - */ -void CommandSetGl843::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, - bool forward, bool black) const -{ - DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); - unsigned int pixels, lines, channels; - Genesys_Register_Set local_reg; - int dpi; - unsigned int pass, count, found, x, y; - - dev->cmd_set->set_fe(dev, sensor, AFE_SET); - scanner_stop_action(*dev); - - /* set up for a gray scan at lowest dpi */ - dpi = sanei_genesys_get_lowest_dpi(dev); - channels = 1; - - const auto& calib_sensor = sanei_genesys_find_sensor(dev, dpi, channels, - dev->settings.scan_method); - - /* 10 MM */ - /* lines = (10 * dpi) / MM_PER_INCH; */ - /* shading calibation is done with dev->motor.base_ydpi */ - lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; - pixels = (calib_sensor.sensor_pixels * dpi) / calib_sensor.optical_res; - - dev->set_head_pos_zero(ScanHeadId::PRIMARY); - - local_reg = dev->reg; - - ScanSession session; - session.params.xres = dpi; - session.params.yres = dpi; - session.params.startx = 0; - session.params.starty = 0; - session.params.pixels = pixels; - session.params.lines = lines; - session.params.depth = 8; - session.params.channels = channels; - session.params.scan_method = dev->settings.scan_method; - session.params.scan_mode = ScanColorMode::GRAY; - session.params.color_filter = ColorFilter::RED; - session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_SHADING; - if (!forward) { - session.params.flags = ScanFlag::REVERSE; - } - compute_session(dev, session, calib_sensor); - - init_regs_for_scan_session(dev, calib_sensor, &local_reg, session); - - dev->interface->write_registers(local_reg); - - dev->cmd_set->begin_scan(dev, calib_sensor, &local_reg, true); - - if (is_testing_mode()) { - dev->interface->test_checkpoint("search_strip"); - scanner_stop_action(*dev); - return; - } - - wait_until_buffer_non_empty(dev); - - // now we're on target, we can read data - auto data = read_unshuffled_image_from_scanner(dev, session, - session.output_total_bytes_raw); - - scanner_stop_action(*dev); - - pass = 0; - if (DBG_LEVEL >= DBG_data) - { - char fn[40]; - std::snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm", - black ? "black" : "white", forward ? "fwd" : "bwd", pass); - sanei_genesys_write_pnm_file(fn, data); - } - - /* loop until strip is found or maximum pass number done */ - found = 0; - while (pass < 20 && !found) - { - dev->interface->write_registers(local_reg); - - // now start scan - dev->cmd_set->begin_scan(dev, calib_sensor, &local_reg, true); - - wait_until_buffer_non_empty(dev); - - // now we're on target, we can read data - data = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw); - - scanner_stop_action(*dev); - - if (DBG_LEVEL >= DBG_data) - { - char fn[40]; - std::snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm", - black ? "black" : "white", forward ? "fwd" : "bwd", pass); - sanei_genesys_write_pnm_file(fn, data); - } - - /* search data to find black strip */ - /* when searching forward, we only need one line of the searched color since we - * will scan forward. But when doing backward search, we need all the area of the - * same color */ - if (forward) - { - for (y = 0; y < lines && !found; y++) - { - count = 0; - /* count of white/black pixels depending on the color searched */ - for (x = 0; x < pixels; x++) - { - /* when searching for black, detect white pixels */ - if (black && data.get_raw_channel(x, y, 0) > 90) { - count++; - } - /* when searching for white, detect black pixels */ - if (!black && data.get_raw_channel(x, y, 0) < 60) { - count++; - } - } - - /* at end of line, if count >= 3%, line is not fully of the desired color - * so we must go to next line of the buffer */ - /* count*100/pixels < 3 */ - if ((count * 100) / pixels < 3) - { - found = 1; - DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__, - pass, y); - } - else - { - DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, - (100 * count) / pixels); - } - } - } - else /* since calibration scans are done forward, we need the whole area - to be of the required color when searching backward */ - { - count = 0; - for (y = 0; y < lines; y++) - { - /* count of white/black pixels depending on the color searched */ - for (x = 0; x < pixels; x++) - { - // when searching for black, detect white pixels - if (black && data.get_raw_channel(x, y, 0) > 90) { - count++; - } - // when searching for white, detect black pixels - if (!black && data.get_raw_channel(x, y, 0) < 60) { - count++; - } - } - } - - /* at end of area, if count >= 3%, area is not fully of the desired color - * so we must go to next buffer */ - if ((count * 100) / (pixels * lines) < 3) - { - found = 1; - DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); - } - else - { - DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, - (100 * count) / pixels); - } - } - pass++; - } - if (found) - { - DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white"); - } - else - { - throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white"); - } -} - /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. @@ -2974,43 +1741,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; + int count; - 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 */ + int offset = 0; + unsigned length = size; - // 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; + if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { + offset = dev->session.params.startx * sensor.shading_resolution / + dev->session.params.xres; - /* current scan coordinates */ - strpixel = dev->session.pixel_startx; - endpixel = dev->session.pixel_endx; + length = dev->session.output_pixels * sensor.shading_resolution / + dev->session.params.xres; - 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(); - } + offset += sensor.shading_pixel_offset; - /* 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); + // 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 +1775,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(length) + offset > static_cast(size)) { + length = size - offset; + } /* loop over calibration data */ for (i = 0; i < length; i++) @@ -3036,8 +1795,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 +1809,5 @@ void CommandSetGl843::wait_for_motor_stop(Genesys_Device* dev) const (void) dev; } -std::unique_ptr create_gl843_cmd_set() -{ - return std::unique_ptr(new CommandSetGl843{}); -} - } // namespace gl843 } // namespace genesys diff --git a/backend/genesys/gl843.h b/backend/genesys/gl843.h index 9f0a9e9d3..d227af2d9 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,16 +60,13 @@ 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(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, @@ -86,8 +83,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; @@ -109,9 +104,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, diff --git a/backend/genesys/gl843_registers.h b/backend/genesys/gl843_registers.h index 8ecb0fc04..cbc38c039 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 d309d29ca..0b6616d6a 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; + unsigned value = (regs->get8(0x9d) & 0x0f) >> 1; + return 1 << value; } -/** @brief sensor specific settings -*/ -static void gl846_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor, - Genesys_Register_Set* regs) -{ - DBG_HELPER(dbg); - - for (const auto& reg : sensor.custom_regs) { - regs->set8(reg.address, reg.value); - } - - regs->set16(REG_EXPR, sensor.exposure.red); - regs->set16(REG_EXPG, sensor.exposure.green); - regs->set16(REG_EXPB, sensor.exposure.blue); - - dev->segment_order = sensor.segment_order; -} - - /** @brief set all registers to default values . * This function is called only once at the beginning and * fills register startup values for registers reused across scans. @@ -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); + 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 + + // 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 + } + + 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); - dev->reg.init_reg(0xa1, 0xe0); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { + dev->reg.init_reg(0xa1, 0xe0); + } + + // 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) + { + dev->reg.init_reg(0xa2, 0x0f); + } + + // 0xa6, 0xa7 0xa8, 0xa9 - gpio + + // Various important settings: GPOM9, MULSTOP, NODECEL, TB3TB1, TB5TB2, FIX16CLK 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); + if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) + { + 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); + } + + // 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 + } const auto& sensor = sanei_genesys_find_sensor_any(dev); - sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); - - /* initalize calibration reg */ - dev->calib_reg = dev->reg; -} - -/**@brief send slope table for motor movement - * Send slope_table in machine byte order - * @param dev device to send slope table - * @param table_nr index of the slope table in ASIC memory - * Must be in the [0-4] range. - * @param slope_table pointer to 16 bit values array of the slope table - * @param steps number of elements in the slope table - */ -static void gl846_send_slope_table(Genesys_Device* dev, int table_nr, - const std::vector& slope_table, - int steps) -{ - DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); - int i; - char msg[10000]; - - /* sanity check */ - if(table_nr<0 || table_nr>4) - { - throw SaneException("invalid table number %d", table_nr); - } - - std::vector table(steps * 2); - for (i = 0; i < steps; i++) - { - table[i * 2] = slope_table[i] & 0xff; - table[i * 2 + 1] = slope_table[i] >> 8; - } - - if (DBG_LEVEL >= DBG_io) - { - std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); - for (i = 0; i < steps; i++) - { - std::sprintf(msg+strlen(msg), "%d", slope_table[i]); - } - DBG (DBG_io, "%s: %s\n", __func__, msg); - } - - if (dev->interface->is_mock()) { - dev->interface->record_slope_table(table_nr, slope_table); - } - // slope table addresses are fixed - dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data()); + 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(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,108 @@ 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(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(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; } - 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); + 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; - - if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { - r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; - } - - if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) ||(scan_yres>=sensor.optical_res)) { - r->value |= REG_0x02_ACDCDIS; - } - if (has_flag(flags, MotorFlag::REVERSE)) { - r->value |= REG_0x02_MTRREV; + std::uint8_t reg02 = reg->get8(REG_0x02); + if (use_fast_fed) { + reg02 |= REG_0x02_FASTFED; } else { - r->value &= ~REG_0x02_MTRREV; + reg02 &= ~REG_0x02_FASTFED; } - /* scan and backtracking slope table */ - auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres, - scan_exposure_time, dev->motor.base_ydpi, - step_multiplier, motor_profile); - - gl846_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count); - gl846_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count); - - /* fast table */ - fast_dpi=sanei_genesys_get_lowest_ydpi(dev); - - // BUG: looks like for fast moves we use inconsistent step type - StepType fast_step_type = motor_profile.step_type; - if (static_cast(motor_profile.step_type) >= static_cast(StepType::QUARTER)) { - fast_step_type = StepType::QUARTER; + if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { + reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; } - Motor_Profile fast_motor_profile = motor_profile; - fast_motor_profile.step_type = fast_step_type; - - auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_dpi, - scan_exposure_time, dev->motor.base_ydpi, - step_multiplier, fast_motor_profile); - - gl846_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count); - gl846_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count); - gl846_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count); - - /* correct move distance by acceleration and deceleration amounts */ - feedl=feed_steps; - if (use_fast_fed) - { - feedl <<= static_cast(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 (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres>=sensor.full_resolution)) { + reg02 |= REG_0x02_ACDCDIS; } - else - { + 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, + 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); + 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); + reg->set8(REG_FMOVDEC, 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); + } + + unsigned feedl = feed_steps; + unsigned dist = 0; + if (use_fast_fed) { + feedl <<= static_cast(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(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 +468,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 +504,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(motor_profile.step_type) << (16 + REG_0x60S_STEPSEL))); - - DBG(DBG_info, "%s: z2 = %d\n", __func__, z2); reg->set24(REG_0x63, z2 | (static_cast(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 = 0x7f; - - r = sanei_genesys_get_address(reg, REG_0x68); - r->value = 0x7f; - - reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier); - reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier); - reg->set8(REG_FSHDEC, scan_table.steps_count / step_multiplier); - reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier); - reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier); + reg->set8(REG_0x67, 0x7f); + reg->set8(REG_0x68, 0x7f); } @@ -558,82 +555,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, dev->settings.threshold); + reg->set8(0x2f, dev->settings.threshold); /* monochrome / color scan */ - r = sanei_genesys_get_address(reg, REG_0x04); switch (session.params.depth) { case 8: - r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); + 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,24 +628,21 @@ 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); @@ -670,12 +651,8 @@ static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens /* 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 +662,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,40 +681,15 @@ 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(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 ***/ @@ -759,21 +710,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(dev->model->x_offset); - start += static_cast(settings.tl_x); - start = static_cast((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: 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((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((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(start); + session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; @@ -782,7 +762,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 +790,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 +815,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,257 +830,96 @@ 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 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; - - dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres); - - const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution, - dev->calib_channels, - dev->settings.scan_method); - dev->calib_total_bytes_to_read = 0; - dev->calib_lines = dev->model->shading_lines; - if (dev->calib_resolution==4800) { - dev->calib_lines *= 2; - } - dev->calib_pixels = (calib_sensor.sensor_pixels * dev->calib_resolution) / - calib_sensor.optical_res; - - DBG(DBG_io, "%s: calib_lines = %zu\n", __func__, dev->calib_lines); - DBG(DBG_io, "%s: calib_pixels = %zu\n", __func__, dev->calib_pixels); - - /* this is aworkaround insufficent distance for slope - * motor acceleration TODO special motor slope for shading */ - move=1; - if(dev->calib_resolution<1200) + float calib_size_mm = 0; + if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || + dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { - move=40; + calib_size_mm = dev->model->y_size_calib_ta_mm; + } else { + calib_size_mm = dev->model->y_size_calib_mm; } + unsigned channels = 3; + unsigned resolution = sensor.shading_resolution; + + const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, + dev->settings.scan_method); + + 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: move_to_ta() function has already been called and the sensor is at the + // transparency adapter + move = static_cast(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta); + flags |= ScanFlag::USE_XPA; + } else { + move = static_cast(dev->model->y_offset_calib_white); + } + + move = static_cast((move * move_dpi) / MM_PER_INCH); + + unsigned calib_lines = static_cast(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(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); + + dev->calib_session = session; } /** @brief set up registers for the actual scan */ -void CommandSetGl846::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const +void CommandSetGl846::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const { DBG_HELPER(dbg); - float move; - int move_dpi; - float start; - debug_dump(DBG_info, dev->settings); + auto session = calculate_scan_session(dev, sensor, 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 + /* Fast move to scan 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(dev->model->y_offset); - move = static_cast(move + dev->settings.tl_y); - move = static_cast((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(move - 500), + 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(session.params.starty - 500), Direction::FORWARD); - move=500; + session.params.starty = 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(dev->model->x_offset); - start = static_cast(start + dev->settings.tl_x); - start = static_cast((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(start); - session.params.starty = static_cast(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); + init_regs_for_scan_session(dev, sensor, ®s, session); } @@ -1114,39 +931,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(size / 3); - unsigned strpixel = dev->session.pixel_startx; - unsigned endpixel = dev->session.pixel_endx; + unsigned length = static_cast(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 buffer(pixels, 0); @@ -1163,10 +965,9 @@ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Senso ptr = buffer.data(); /* iterate on both sensor segment */ - for(x=0;x(dev->model->y_offset_calib_white); - move = static_cast((move * (dev->motor.base_ydpi / 4)) / MM_PER_INCH); - if(move>20) - { - scanner_move(*dev, dev->model->default_method, static_cast(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 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]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 +1002,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(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 +1014,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); - } + // prevent further writings by bulk write register + dev->reg.remove_reg(0x0b); - /* CLKSET and DRAMSEL */ - val = layouts[idx].dramsel; - dev->interface->write_register(REG_0x0B, val); - dev->reg.find_reg(0x0b).value = val; - - /* prevent further writings by bulk write register */ - dev->reg.remove_reg(0x0b); - - /* setup base address for shading and scanned data. */ - for(i=0;i<10;i++) - { - dev->interface->write_register(0xe0+i, layouts[idx].rx[i]); - } + apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /* * @@ -1433,15 +1035,14 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const dev->interface->write_register(0x0e, 0x00); } - if(dev->usb_mode == 1) - { - val = 0x14; + 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); } - else - { - val = 0x11; - } - dev->interface->write_0x8c(0x0f, val); // test CHKVER val = dev->interface->read_register(REG_0x40); @@ -1450,18 +1051,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 +1064,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 +1093,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 +1130,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 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 first_line(total_size); - std::vector 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.xressettings.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 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(sensor.gain_white_ref) * coeff) / max[j]; - - /* turn logical gain value into gain code, checking for overflow */ - code = static_cast(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 +1149,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"); } @@ -2085,13 +1187,11 @@ void CommandSetGl846::eject_document(Genesys_Device* dev) const void CommandSetGl846::move_to_ta(Genesys_Device* dev) const { - (void) dev; - throw SaneException("not implemented"); -} + DBG_HELPER(dbg); -std::unique_ptr create_gl846_cmd_set() -{ - return std::unique_ptr(new CommandSetGl846{}); + unsigned feed = static_cast((dev->model->y_offset_sensor_to_ta * dev->motor.base_ydpi) / + MM_PER_INCH); + scanner_move(*dev, dev->model->default_method, feed, Direction::FORWARD); } } // namespace gl846 diff --git a/backend/genesys/gl846.h b/backend/genesys/gl846.h index 258015aa2..742a7c5f1 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,16 +60,13 @@ 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(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, @@ -161,8 +83,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; @@ -188,9 +108,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, diff --git a/backend/genesys/gl846_registers.h b/backend/genesys/gl846_registers.h index 39b302921..e4a8ac52f 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 cb0b52704..9c2e2fec2 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. @@ -237,95 +210,9 @@ 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); - - /* initalize calibration reg */ - dev->calib_reg = dev->reg; -} - -/**@brief send slope table for motor movement - * Send slope_table in machine byte order - * @param dev device to send slope table - * @param table_nr index of the slope table in ASIC memory - * Must be in the [0-4] range. - * @param slope_table pointer to 16 bit values array of the slope table - * @param steps number of elements in the slope table - */ -static void gl847_send_slope_table(Genesys_Device* dev, int table_nr, - const std::vector& slope_table, - int steps) -{ - DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); - int i; - char msg[10000]; - - /* sanity check */ - if(table_nr<0 || table_nr>4) - { - throw SaneException("invalid table number %d", table_nr); - } - - std::vector table(steps * 2); - for (i = 0; i < steps; i++) - { - table[i * 2] = slope_table[i] & 0xff; - table[i * 2 + 1] = slope_table[i] >> 8; - } - - if (DBG_LEVEL >= DBG_io) - { - std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); - for (i = 0; i < steps; i++) - { - std::sprintf(msg + std::strlen(msg), "%d", slope_table[i]); - } - DBG (DBG_io, "%s: %s\n", __func__, msg); - } - - if (dev->interface->is_mock()) { - dev->interface->record_slope_table(table_nr, slope_table); - } - // slope table addresses are fixed - dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data()); -} - -/** - * Set register values of Analog Device type frontend - * */ -static void gl847_set_ad_fe(Genesys_Device* dev, uint8_t set) -{ - DBG_HELPER(dbg); - int i; - - // wait for FE to be ready - auto status = scanner_read_status(*dev); - while (status.is_front_end_busy) { - dev->interface->sleep_ms(10); - status = scanner_read_status(*dev); - }; - - if (set == AFE_INIT) - { - DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, - static_cast(dev->model->adc_id)); - - dev->frontend = dev->frontend_initial; - } - - // reset DAC - dev->interface->write_fe_register(0x00, 0x80); - - // write them to analog frontend - dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); - - dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); - - for (i = 0; i < 3; i++) { - dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i)); - } - for (i = 0; i < 3; i++) { - dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i)); - } + 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); } // Set values of analog frontend @@ -340,13 +227,27 @@ void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& 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; + if (frontend_type != 0x02) { + throw SaneException("unsupported frontend type %d", frontend_type); } - throw SaneException("unsupported frontend type %d", frontend_type); + // 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) { + dev->frontend = dev->frontend_initial; + } + + // reset DAC + dev->interface->write_fe_register(0x00, 0x80); + + for (const auto& reg : dev->frontend.regs) { + dev->interface->write_fe_register(reg.address, reg.value); + } } @@ -354,116 +255,96 @@ void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, 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(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(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; } - 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(motor_profile.step_type) >= static_cast(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); - 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); + 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); - /* 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(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(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 +352,21 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev, } reg->set24(REG_FEEDL, feedl); - DBG(DBG_io ,"%s: feedl=%d\n", __func__, 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(motor_profile.step_type) > static_cast(StepType::QUARTER)) { val = effective | REG_0x6C_GPIO13; - } else { - val = effective; } dev->interface->write_register(REG_0x6C, val); @@ -498,45 +375,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(motor_profile.step_type) << (16+REG_0x60S_STEPSEL))); - - DBG(DBG_info, "%s: z2 = %d\n", __func__, z2); reg->set24(REG_0x63, z2 | (static_cast(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 +432,71 @@ 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)) { - 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); - 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)); - /* 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, dev->settings.threshold); + reg->set8(0x2f, dev->settings.threshold); /* monochrome / color scan */ - r = sanei_genesys_get_address (reg, REG_0x04); switch (session.params.depth) { case 8: - r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); + 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 + 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,24 +507,20 @@ 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); @@ -677,12 +529,8 @@ static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens /* 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,7 +540,6 @@ 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; @@ -712,37 +559,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(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); + session.optical_line_count, dummy, session.params.starty, + session.params.flags); dev->read_buffer.clear(); dev->read_buffer.alloc(session.buffer_size_read); @@ -761,21 +586,33 @@ 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(dev->model->x_offset); - start = static_cast(start + settings.tl_x); - start = static_cast((start * sensor.optical_res) / MM_PER_INCH); + /* 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; + move = move + settings.tl_y; + move = static_cast((move * move_dpi) / MM_PER_INCH); + move -= dev->head_pos(ScanHeadId::PRIMARY); + + float start = dev->model->x_offset; + start = start + dev->settings.tl_x; + start = static_cast((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(start); + session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; @@ -784,7 +621,8 @@ 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; + // backtracking isn't handled well, so don't enable it + session.params.flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE; compute_session(dev, session, sensor); @@ -811,7 +649,6 @@ 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) { @@ -828,8 +665,7 @@ void CommandSetGl847::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens 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); @@ -860,249 +696,71 @@ void CommandSetGl847::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 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 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 channels = 3; - /* initial calibration reg values */ - regs = dev->reg; + unsigned resolution = sensor.shading_resolution; - dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres); - - const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution, - dev->calib_channels, + 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); + unsigned calib_lines = + static_cast(dev->model->y_size_calib_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.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_BUFFER_FULL_MOVE; 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); + + dev->calib_session = session; } /** @brief set up registers for the actual scan */ -void CommandSetGl847::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const +void CommandSetGl847::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs) const { DBG_HELPER(dbg); - float move; - int move_dpi; - float start; - debug_dump(DBG_info, dev->settings); + auto session = calculate_scan_session(dev, sensor, 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 + /* Fast move to scan 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(dev->model->y_offset); - move = static_cast(move + dev->settings.tl_y); - move = static_cast((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(move - 500), + 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(session.params.starty - 500), Direction::FORWARD); - move=500; + session.params.starty = 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(dev->model->x_offset); - start = static_cast(start + dev->settings.tl_x); - start = static_cast((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(start); - session.params.starty = static_cast(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); + init_regs_for_scan_session(dev, sensor, ®s, session); } @@ -1114,39 +772,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(size / 3); - std::uint32_t strpixel = dev->session.pixel_startx; - std::uint32_t endpixel = dev->session.pixel_endx; + unsigned length = static_cast(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 buffer(pixels, 0); @@ -1162,11 +805,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(dev->model->y_offset_calib_white); - move = static_cast((move * (dev->motor.base_ydpi / 4)) / MM_PER_INCH); - if (move > 20) { - scanner_move(*dev, dev->model->default_method, static_cast(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 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 +843,29 @@ 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(dev->model->sensor_id)); + std::vector order1 = { 0xa7, 0xa6, 0x6e }; + std::vector order2 = { 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0xa8, 0xa9 }; + + for (auto addr : order1) { + dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); } - dev->interface->write_register(REG_0xA7, gpios[idx].ra7); - dev->interface->write_register(REG_0xA6, gpios[idx].ra6); + dev->interface->write_register(REG_0x6C, 0x00); // FIXME: Likely not needed - dev->interface->write_register(REG_0x6E, gpios[idx].r6e); - dev->interface->write_register(REG_0x6C, 0x00); + for (auto addr : order2) { + dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); + } - 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); + 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 +874,25 @@ 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; + // TODO: move to initial register list + switch (dev->model->model_id) { + case ModelId::CANON_LIDE_100: + case ModelId::CANON_LIDE_200: + case ModelId::CANON_5600F: + dev->interface->write_register(REG_0x0B, 0x29); + break; + case ModelId::CANON_LIDE_700F: + dev->interface->write_register(REG_0x0B, 0x2a); + break; + default: + throw SaneException("Unknown device"); } - /* 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); - /* 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); + apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /* * @@ -1519,7 +954,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 +1001,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 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(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 first_line(total_size); - std::vector 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.xressettings.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 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(sensor.gain_white_ref) * coeff) / max[j]; - - /* turn logical gain value into gain code, checking for overflow */ - code = static_cast(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 +1020,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"); } @@ -2131,10 +1062,5 @@ void CommandSetGl847::move_to_ta(Genesys_Device* dev) const throw SaneException("not implemented"); } -std::unique_ptr create_gl847_cmd_set() -{ - return std::unique_ptr(new CommandSetGl847{}); -} - } // namespace gl847 } // namespace genesys diff --git a/backend/genesys/gl847.h b/backend/genesys/gl847.h index a51c2936f..d3acde269 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,16 +60,13 @@ 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(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, @@ -149,8 +83,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; @@ -176,9 +108,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, diff --git a/backend/genesys/gl847_registers.h b/backend/genesys/gl847_registers.h index 0603a6a65..aa3d43b32 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_buffer.cpp b/backend/genesys/image_buffer.cpp index 07c698725..fe4d75517 100644 --- a/backend/genesys/image_buffer.cpp +++ b/backend/genesys/image_buffer.cpp @@ -89,47 +89,11 @@ bool ImageBuffer::get_data(std::size_t size, std::uint8_t* out_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, + std::size_t buffer_size, ProducerCallback producer) : remaining_size_{total_size}, - buffer_model_{buffer_model}, + buffer_size_{buffer_size}, producer_{producer} {} @@ -179,7 +143,7 @@ bool ImageBufferGenesysUsb::get_data(std::size_t size, std::uint8_t* out_data) std::size_t ImageBufferGenesysUsb::get_read_size() { - std::size_t size = buffer_model_.available_space(); + std::size_t size = buffer_size_; // never read an odd number. exception: last read // the chip internal counter does not count half words. @@ -195,8 +159,6 @@ std::size_t ImageBufferGenesysUsb::get_read_size() size &= ~0xff; } - buffer_model_.simulate_read(size); - return size; } diff --git a/backend/genesys/image_buffer.h b/backend/genesys/image_buffer.h index 43c3eb751..0b3c4f844 100644 --- a/backend/genesys/image_buffer.h +++ b/backend/genesys/image_buffer.h @@ -73,23 +73,6 @@ private: std::vector buffer_; }; -class FakeBufferModel -{ -public: - FakeBufferModel() {} - - void push_step(std::size_t buffer_size, std::size_t row_bytes); - - std::size_t available_space() const; - - void simulate_read(std::size_t size); - -private: - std::vector sizes_; - std::vector available_sizes_; - std::vector row_bytes_; -}; - // This class is similar to ImageBuffer, but preserves historical peculiarities of buffer handling // in the backend to preserve exact behavior class ImageBufferGenesysUsb @@ -98,7 +81,7 @@ public: using ProducerCallback = std::function; ImageBufferGenesysUsb() {} - ImageBufferGenesysUsb(std::size_t total_size, const FakeBufferModel& buffer_model, + ImageBufferGenesysUsb(std::size_t total_size, std::size_t buffer_size, ProducerCallback producer); std::size_t remaining_size() const { return remaining_size_; } @@ -115,12 +98,12 @@ private: std::size_t remaining_size_ = 0; + std::size_t buffer_size_ = 0; + std::size_t buffer_offset_ = 0; std::size_t buffer_end_ = 0; std::vector buffer_; - FakeBufferModel buffer_model_; - ProducerCallback producer_; }; diff --git a/backend/genesys/image_pipeline.cpp b/backend/genesys/image_pipeline.cpp index c01b7f487..18e6b5d34 100644 --- a/backend/genesys/image_pipeline.cpp +++ b/backend/genesys/image_pipeline.cpp @@ -109,11 +109,11 @@ bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* ou ImagePipelineNodeBufferedGenesysUsb::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 buffer_size, ProducerCallback producer) : width_{width}, height_{height}, format_{format}, - buffer_{total_size, buffer_model, producer} + buffer_{total_size, buffer_size, producer} { set_remaining_bytes(total_size); } @@ -319,6 +319,49 @@ 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(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; + for (std::size_t i = 0; i < num_values; ++i) { + *data = ~*data; + data++; + } + break; + } + default: + throw SaneException("Unsupported pixel depth"); + } + + return got_data; +} + ImagePipelineNodeMergeMonoLines::ImagePipelineNodeMergeMonoLines(ImagePipelineNode& source, ColorOrder color_order) : source_(source), @@ -666,16 +709,21 @@ bool ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data) ImagePipelineNodeCalibrate::ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector& bottom, - const std::vector& top) : + const std::vector& 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])); } } diff --git a/backend/genesys/image_pipeline.h b/backend/genesys/image_pipeline.h index 29868374d..5c373ac43 100644 --- a/backend/genesys/image_pipeline.h +++ b/backend/genesys/image_pipeline.h @@ -157,8 +157,7 @@ public: 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 buffer_size, ProducerCallback producer); std::size_t get_width() const override { return width_; } std::size_t get_height() const override { return height_; } @@ -302,7 +301,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 +320,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 { @@ -476,7 +492,7 @@ class ImagePipelineNodeCalibrate : public ImagePipelineNode public: ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector& bottom, - const std::vector& top); + const std::vector& 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(); } diff --git a/backend/genesys/image_pixel.h b/backend/genesys/image_pixel.h index 2dda271cf..aa9980e34 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 7937fcc34..4ae8d9e20 100644 --- a/backend/genesys/low.cpp +++ b/backend/genesys/low.cpp @@ -51,11 +51,21 @@ #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 #include #include @@ -66,29 +76,17 @@ namespace genesys { -/** - * setup the hardware dependent functions - */ - -namespace gl124 { std::unique_ptr create_gl124_cmd_set(); } -namespace gl646 { std::unique_ptr create_gl646_cmd_set(); } -namespace gl841 { std::unique_ptr create_gl841_cmd_set(); } -namespace gl843 { std::unique_ptr create_gl843_cmd_set(); } -namespace gl846 { std::unique_ptr create_gl846_cmd_set(); } -namespace gl847 { std::unique_ptr create_gl847_cmd_set(); } - -void sanei_genesys_init_cmd_set(Genesys_Device* dev) +std::unique_ptr 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(new gl646::CommandSetGl646{}); + case AsicType::GL841: return std::unique_ptr(new gl841::CommandSetGl841{}); + case AsicType::GL842: return std::unique_ptr(new gl842::CommandSetGl842{}); + case AsicType::GL843: return std::unique_ptr(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(new gl846::CommandSetGl846{}); + case AsicType::GL847: return std::unique_ptr(new gl847::CommandSetGl847{}); + case AsicType::GL124: return std::unique_ptr(new gl124::CommandSetGl124{}); default: throw SaneException(SANE_STATUS_INVAL, "unknown ASIC type"); } } @@ -276,6 +274,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,29 +335,6 @@ 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) -{ - int pixels_per_line; - - pixels_per_line = reg->get8(0x32) * 256 + reg->get8(0x33); - pixels_per_line -= (reg->get8(0x30) * 256 + reg->get8(0x31)); - - return pixels_per_line; -} - -/* returns dpiset from register set */ -/*candidate for moving into chip specific files?*/ -static int -genesys_dpiset (Genesys_Register_Set * reg) -{ - return reg->get8(0x2c) * 256 + reg->get8(0x2d); -} -#endif - /** read the number of valid words in scanner's RAM * ie registers 42-43-44 */ @@ -516,7 +492,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); @@ -534,26 +510,37 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& ImagePipelineStack pipeline; pipeline.push_first_node(image); - if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && session.params.depth == 16) { - dev->pipeline.push_node(); + if (session.segment_count > 1) { + auto output_width = session.output_segment_pixel_group_count * session.segment_count; + pipeline.push_node(output_width, dev->segment_order, + session.conseq_pixel_dist, + 1, 1); + } + + if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA) && session.params.depth == 16) { + pipeline.push_node(); + } + + if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { + pipeline.push_node(); } #ifdef WORDS_BIGENDIAN - if (depth == 16) { - dev->pipeline.push_node(); + if (session.params.depth == 16) { + pipeline.push_node(); } #endif if (dev->model->is_cis && session.params.channels == 3) { - dev->pipeline.push_node(dev->model->line_mode_color_order); + pipeline.push_node(dev->model->line_mode_color_order); } - if (dev->pipeline.get_output_format() == PixelFormat::BGR888) { - dev->pipeline.push_node(PixelFormat::RGB888); + if (pipeline.get_output_format() == PixelFormat::BGR888) { + pipeline.push_node(PixelFormat::RGB888); } - if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) { - dev->pipeline.push_node(PixelFormat::RGB161616); + if (pipeline.get_output_format() == PixelFormat::BGR161616) { + pipeline.push_node(PixelFormat::RGB161616); } return pipeline.get_image(); @@ -600,35 +587,25 @@ 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}); - } - } } regs.state.is_lamp_on = set; } @@ -794,70 +771,9 @@ static unsigned align_int_up(unsigned num, unsigned alignment) return num; } -void compute_session_buffer_sizes(AsicType asic, ScanSession& s) +std::size_t compute_session_buffer_sizes(const ScanSession& s) { - size_t line_bytes = s.output_line_bytes; - size_t line_bytes_stagger = s.output_line_bytes; - - if (asic != AsicType::GL646) { - // BUG: this is historical artifact and should be removed. Note that buffer sizes affect - // how often we request the scanner for data and thus change the USB traffic. - line_bytes_stagger = - multiply_by_depth_ceil(s.optical_pixels, s.params.depth) * s.params.channels; - } - - struct BufferConfig { - size_t* result_size = nullptr; - size_t lines = 0; - size_t lines_mult = 0; - size_t max_size = 0; // does not apply if 0 - size_t stagger_lines = 0; - - BufferConfig() = default; - BufferConfig(std::size_t* rs, std::size_t l, std::size_t lm, std::size_t ms, - std::size_t sl) : - result_size{rs}, - lines{l}, - lines_mult{lm}, - max_size{ms}, - stagger_lines{sl} - {} - }; - - std::array configs; - if (asic == AsicType::GL124 || asic == AsicType::GL843) { - configs = { { - { &s.buffer_size_read, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, - { &s.buffer_size_lines, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, - { &s.buffer_size_shrink, 16, 1, 0, 0 }, - { &s.buffer_size_out, 8, 1, 0, 0 }, - } }; - } else if (asic == AsicType::GL841) { - size_t max_buf = sanei_genesys_get_bulk_max_size(asic); - configs = { { - { &s.buffer_size_read, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines }, - { &s.buffer_size_lines, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines }, - { &s.buffer_size_shrink, 8, 1, max_buf, 0 }, - { &s.buffer_size_out, 8, 1, 0, 0 }, - } }; - } else { - configs = { { - { &s.buffer_size_read, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, - { &s.buffer_size_lines, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, - { &s.buffer_size_shrink, 8, 1, 0, 0 }, - { &s.buffer_size_out, 8, 1, 0, 0 }, - } }; - } - - for (BufferConfig& config : configs) { - size_t buf_size = line_bytes * config.lines; - if (config.max_size > 0 && buf_size > config.max_size) { - buf_size = (config.max_size / line_bytes) * line_bytes; - } - buf_size *= config.lines_mult; - buf_size += line_bytes_stagger * config.stagger_lines; - *config.result_size = buf_size; - } + return s.output_line_bytes * (32 + s.max_color_shift_lines + s.num_staggered_lines); } void compute_session_pipeline(const Genesys_Device* dev, ScanSession& s) @@ -891,61 +807,58 @@ void compute_session_pipeline(const Genesys_Device* dev, ScanSession& s) void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor) { - unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel(); + if (dev->model->asic_type == AsicType::GL646) { + 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; + + } else if (dev->model->asic_type == AsicType::GL841 || + dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { + s.pixel_startx = (s.output_startx * s.optical_resolution) / s.params.xres; + s.pixel_endx = s.pixel_startx + s.optical_pixels; + + } else 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) + { + s.pixel_startx = s.output_startx * sensor.full_resolution / s.params.xres; + s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; + } + + 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.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()); + } if (dev->model->asic_type == AsicType::GL646) { - - // startx cannot be below dummy pixel value - s.pixel_startx = sensor.dummy_pixel; - if (has_flag(s.params.flags, ScanFlag::USE_XCORRECTION) && sensor.ccd_start_xoffset > 0) { - s.pixel_startx = sensor.ccd_start_xoffset; - } - s.pixel_startx += s.params.startx; - - if (sensor.stagger_config.stagger_at_resolution(s.params.xres, s.params.yres) > 0) { - s.pixel_startx |= 1; - } - - s.pixel_endx = s.pixel_startx + s.optical_pixels; - - s.pixel_startx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor; - s.pixel_endx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor; - - } else if (dev->model->asic_type == AsicType::GL841) { - s.pixel_startx = ((sensor.ccd_start_xoffset + s.params.startx) * s.optical_resolution) - / sensor.optical_res; - - s.pixel_startx += sensor.dummy_pixel + 1; - - if (s.num_staggered_lines > 0 && (s.pixel_startx & 1) == 0) { + if (sensor.stagger_config.stagger_at_resolution(s.params.xres, s.params.yres) > 0 && + (s.pixel_startx & 1) == 0) + { s.pixel_startx++; + s.pixel_endx++; } - - /* In case of SHDAREA, we need to align start on pixel average factor, startx is - different than 0 only when calling for function to setup for scan, where shading data - needs to be align. - - NOTE: we can check the value of the register here, because we don't set this bit - anywhere except in initialization. - */ - const uint8_t REG_0x01_SHDAREA = 0x02; - if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) != 0) { - unsigned average_factor = s.optical_resolution / s.params.xres; - s.pixel_startx = align_multiple_floor(s.pixel_startx, average_factor); - } - - s.pixel_endx = s.pixel_startx + s.optical_pixels; - - } else if (dev->model->asic_type == AsicType::GL843) { - - s.pixel_startx = (s.params.startx + sensor.dummy_pixel) / ccd_pixels_per_system_pixel; - s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel; - - s.pixel_startx /= s.hwdpi_divisor; - s.pixel_endx /= s.hwdpi_divisor; - + } else if (dev->model->asic_type == AsicType::GL841 || + dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { // in case of stagger we have to start at an odd coordinate - bool stagger_starts_even = dev->model->model_id == ModelId::CANON_8400F; + // FIXME: we should probably just configure the image pipeline accordingly + bool stagger_starts_even = false; + if (dev->model->model_id == ModelId::CANON_4400F || + dev->model->model_id == ModelId::CANON_8400F) + { + stagger_starts_even = true; + } + if (s.num_staggered_lines > 0) { if (!stagger_starts_even && (s.pixel_startx & 1) == 0) { s.pixel_startx++; @@ -955,49 +868,20 @@ void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s, s.pixel_endx++; } } - } else if (dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846 || dev->model->asic_type == AsicType::GL847) { - s.pixel_startx = s.params.startx; - - if (s.num_staggered_lines > 0) { - s.pixel_startx |= 1; + if (s.num_staggered_lines > 0 && (s.pixel_startx & 1) == 0) { + s.pixel_startx++; + s.pixel_endx++; } - - s.pixel_startx += sensor.ccd_start_xoffset * ccd_pixels_per_system_pixel; - s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; - - s.pixel_startx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel; - s.pixel_endx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel; - } else if (dev->model->asic_type == AsicType::GL124) { - s.pixel_startx = s.params.startx; - - if (s.num_staggered_lines > 0) { - s.pixel_startx |= 1; - } - - s.pixel_startx /= ccd_pixels_per_system_pixel; - // FIXME: should we add sensor.dummy_pxel to pixel_startx at this point? - s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel; - - s.pixel_startx /= s.hwdpi_divisor * s.segment_count; - s.pixel_endx /= s.hwdpi_divisor * s.segment_count; - - std::uint32_t segcnt = (sensor.custom_regs.get_value(gl124::REG_SEGCNT) << 16) + - (sensor.custom_regs.get_value(gl124::REG_SEGCNT + 1) << 8) + - sensor.custom_regs.get_value(gl124::REG_SEGCNT + 2); - if (s.pixel_endx == segcnt) { - s.pixel_endx = 0; + if (s.num_staggered_lines > 0 && (s.pixel_startx & 1) == 0) { + s.pixel_startx++; + s.pixel_endx++; } } - - s.pixel_count_multiplier = sensor.pixel_count_multiplier; - - s.pixel_startx *= sensor.pixel_count_multiplier; - s.pixel_endx *= sensor.pixel_count_multiplier; } void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor) @@ -1011,53 +895,42 @@ 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::GL841 || + dev->model->asic_type == AsicType::GL842) + { + s.optical_pixels = align_int_up(s.optical_pixels, 2); } if (dev->model->asic_type == AsicType::GL646 && s.params.xres == 400) { - s.optical_pixels = (s.optical_pixels / 6) * 6; + s.optical_pixels = align_multiple_floor(s.optical_pixels, 6); } if (dev->model->asic_type == AsicType::GL843) { // ensure the number of optical pixels is divisible by 2. // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number - s.optical_pixels = align_int_up(s.optical_pixels, 2 * s.ccd_size_divisor); + s.optical_pixels = align_int_up(s.optical_pixels, + 2 * s.full_resolution / s.optical_resolution); - if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || + 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) + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I || + dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { s.optical_pixels = align_int_up(s.optical_pixels, 16); } @@ -1067,9 +940,13 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se // to retrieve from the chip s.output_pixels = (s.optical_pixels * s.output_resolution) / s.optical_resolution; - // Note: staggering is not applied for calibration. Staggering starts at 2400 dpi + if (static_cast(s.params.startx) + sensor.output_pixel_offset < 0) + throw SaneException("Invalid sensor.output_pixel_offset"); + s.output_startx = static_cast( + static_cast(s.params.startx) + sensor.output_pixel_offset); + s.num_staggered_lines = 0; - if (!has_flag(s.params.flags, ScanFlag::IGNORE_LINE_DISTANCE)) + if (!has_flag(s.params.flags, ScanFlag::IGNORE_STAGGER_OFFSET)) { s.num_staggered_lines = sensor.stagger_config.stagger_at_resolution(s.params.xres, s.params.yres); @@ -1091,12 +968,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,9 +986,11 @@ 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) + 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; @@ -1118,14 +999,13 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se // 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; + 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.optical_pixels_raw * s.output_resolution) / sensor.full_resolution / s.segment_count, s.params.depth); } @@ -1139,27 +1019,28 @@ 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) { + if (dev->model->asic_type == AsicType::GL842 || + dev->model->asic_type == AsicType::GL843) + { s.conseq_pixel_dist = s.output_pixels / s.segment_count; } s.output_segment_pixel_group_count = 0; if (dev->model->asic_type == AsicType::GL124 || + dev->model->asic_type == AsicType::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->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846 || dev->model->asic_type == AsicType::GL847) { - s.output_segment_pixel_group_count = multiply_by_depth_ceil( - s.optical_pixels / (s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel), - s.params.depth); + s.output_segment_pixel_group_count = s.pixel_count_ratio.apply(s.optical_pixels); } s.output_line_bytes_requested = multiply_by_depth_ceil( @@ -1168,7 +1049,7 @@ 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; - compute_session_buffer_sizes(dev->model->asic_type, s); + s.buffer_size_read = compute_session_buffer_sizes(s); compute_session_pipeline(dev, s); compute_session_pixel_offsets(dev, s, sensor); @@ -1179,7 +1060,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 @@ -1202,8 +1086,9 @@ static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& se 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; + // BUG: we shouldn't multiply by channels here nor adjuct by resolution factor + return session.output_line_bytes_raw * session.optical_resolution / session.full_resolution + * session.params.channels; case AsicType::GL845: case AsicType::GL846: @@ -1211,6 +1096,7 @@ static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& se // BUG: we shouldn't multiply by channels here return session.output_line_bytes_raw * session.params.channels; + case AsicType::GL842: case AsicType::GL843: return session.output_line_bytes_raw * 2; @@ -1219,24 +1105,6 @@ static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& se } } -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; @@ -1255,7 +1123,7 @@ void build_image_pipeline(Genesys_Device* dev, const ScanSession& session) return true; }; - auto lines = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1); + auto lines = session.optical_line_count; dev->pipeline.clear(); @@ -1269,10 +1137,22 @@ void build_image_pipeline(Genesys_Device* dev, const ScanSession& session) width, lines + 1, format, get_usb_buffer_read_size(dev->model->asic_type, session), read_data_from_usb); + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_0_from_usb.pnm"); + } + auto output_width = session.output_segment_pixel_group_count * session.segment_count; dev->pipeline.push_node(output_width, dev->segment_order, session.conseq_pixel_dist, 1, 1); + + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_1_after_desegment.pnm"); + } } else { auto read_bytes_left_after_deseg = session.output_line_bytes * session.output_line_count; if (dev->model->asic_type == AsicType::GL646) { @@ -1281,33 +1161,52 @@ void build_image_pipeline(Genesys_Device* dev, const ScanSession& session) dev->pipeline.push_first_node( width, lines, format, read_bytes_left_after_deseg, - get_fake_usb_buffer_model(session), read_data_from_usb); + session.buffer_size_read, read_data_from_usb); + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_0_from_usb.pnm"); + } } - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_0_before_swap.pnm"); - } - if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && depth == 16) { - dev->pipeline.push_node(); - } - -#ifdef WORDS_BIGENDIAN if (depth == 16) { - dev->pipeline.push_node(); - } + 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(); - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_1_after_swap.pnm"); + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_2_after_swap.pnm"); + } + } + } + + if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { + dev->pipeline.push_node(); + + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_3_after_invert.pnm"); + } } if (dev->model->is_cis && session.params.channels == 3) { dev->pipeline.push_node(dev->model->line_mode_color_order); + + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_4_after_merge_mono.pnm"); + } } if (dev->pipeline.get_output_format() == PixelFormat::BGR888) { @@ -1318,40 +1217,49 @@ void build_image_pipeline(Genesys_Device* dev, const ScanSession& session) dev->pipeline.push_node(PixelFormat::RGB161616); } + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_5_after_format.pnm"); + } + if (session.max_color_shift_lines > 0 && session.params.channels == 3) { dev->pipeline.push_node( session.color_shift_lines_r, session.color_shift_lines_g, session.color_shift_lines_b); - } - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_2_after_shift.pnm"); + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_6_after_color_unshift.pnm"); + } } if (session.num_staggered_lines > 0) { std::vector shifts{0, session.num_staggered_lines}; dev->pipeline.push_node(shifts); - } - if (DBG_LEVEL >= DBG_io2) { - dev->pipeline.push_node("gl_pipeline_" + - std::to_string(s_pipeline_index) + - "_3_after_stagger.pnm"); - } - - if ((dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) && - !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) - { - dev->pipeline.push_node(dev->dark_average_data, - dev->white_average_data); - - if (DBG_LEVEL >= DBG_io2) { + if (dbg_log_image_data()) { dev->pipeline.push_node("gl_pipeline_" + std::to_string(s_pipeline_index) + - "_4_after_calibrate.pnm"); + "_7_after_unstagger.pnm"); + } + } + + 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(dev->dark_average_data, + dev->white_average_data, + session.params.startx * + dev->calib_session.params.channels); + + if (dbg_log_image_data()) { + dev->pipeline.push_node("gl_pipeline_" + + std::to_string(s_pipeline_index) + + "_8_after_calibrate.pnm"); } } @@ -1394,6 +1302,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((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(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(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 +1352,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); + 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"); } - if (frontend_type == FrontendType::ANALOG_DEVICES) { - return compute_frontend_gain_analog_devices(value, target_value); - } - throw SaneException("Unknown frontend to compute gain for"); } /** @brief initialize device @@ -1436,7 +1379,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 +1429,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 +1439,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 +1459,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 +1477,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 +1486,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 +1528,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 +1570,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 +1603,8 @@ bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& reg return static_cast(regs.get8(gl646::REG_0x06) & gl646::REG_0x06_GAIN4); case AsicType::GL841: return static_cast(regs.get8(gl841::REG_0x06) & gl841::REG_0x06_GAIN4); + case AsicType::GL842: + return static_cast(regs.get8(gl842::REG_0x06) & gl842::REG_0x06_GAIN4); case AsicType::GL843: return static_cast(regs.get8(gl843::REG_0x06) & gl843::REG_0x06_GAIN4); case AsicType::GL845: @@ -1706,79 +1663,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& motors, - MotorId motor_id, int exposure) +const MotorProfile* get_motor_profile_ptr(const std::vector& profiles, + unsigned exposure, + const ScanSession& session) { - int idx; + int best_i = -1; - 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]; + for (unsigned i = 0; i < profiles.size(); ++i) { + const auto& profile = profiles[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= 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 motors[idx]; + return &profiles[best_i]; } -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& profiles, + unsigned exposure, + const ScanSession& session) { - unsigned target_speed_w = ((exposure * dpi) / base_dpi); + const auto* profile = get_motor_profile_ptr(profiles, exposure, session); + if (profile == nullptr) { + throw SaneException("Motor slope is not configured"); + } - auto table = create_slope_table(motor_profile.slope, target_speed_w, motor_profile.step_type, - step_multiplier, 2 * step_multiplier, - get_slope_table_max_size(asic_type)); + return *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 * ydpi) / motor.base_ydpi); + + 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 d7f5dd2d7..94f579f59 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; - 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, + const Genesys_Model& model) : + vendor_{vendor_id}, product_{product_id}, + bcd_device_{BCD_DEVICE_NOT_SET}, model_{model} {} + 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> gl843_motor_profiles; -extern StaticInit> gl846_motor_profiles; -extern StaticInit> gl847_motor_profiles; -extern StaticInit> 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 create_cmd_set(AsicType asic_type); // reads the status of the scanner Status scanner_read_status(Genesys_Device& dev); @@ -318,13 +279,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& gamma_table, float gamma); @@ -335,18 +292,40 @@ 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); +/** 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 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); + +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 scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr, + const std::vector& slope_table); + extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, std::size_t length); @@ -370,25 +349,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(1, exposure.red); + exposure.green = std::max(1, exposure.green); + exposure.blue = std::max(1, exposure.blue); return exposure; } @@ -396,7 +363,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 +371,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& motors, - MotorId motor_id, int exposure); +void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor, + Genesys_Register_Set& regs); -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_ptr(const std::vector& profiles, + unsigned exposure, + const ScanSession& session); + +const MotorProfile& get_motor_profile(const std::vector& 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 @@ -502,15 +477,18 @@ inline T clamp(const T& value, const T& lo, const T& hi) extern StaticInit> s_sensors; extern StaticInit> s_frontends; extern StaticInit> s_gpo; +extern StaticInit> s_memory_layout; extern StaticInit> s_motors; -extern StaticInit> s_usb_devices; +extern StaticInit> 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 void debug_dump(unsigned level, const T& value) diff --git a/backend/genesys/motor.cpp b/backend/genesys/motor.cpp index 910266aa7..cc3f89c11 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 +#include 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()); } 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(step_type), steps_alignment, min_size); @@ -130,26 +151,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 +179,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(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 d80da6dce..c433c0ecc 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 #include #include #include "enums.h" +#include "sensor.h" +#include "value_filter.h" namespace genesys { @@ -123,20 +126,47 @@ struct MotorSlope struct MotorSlopeTable { std::vector 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 resolutions = VALUE_FILTER_ANY; + // the scan method this profile is good for. If the list is empty, good for any method. + ValueFilterAny 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 slopes; + std::vector profiles; + // slopes to derive individual slopes from for fast moving + std::vector fast_profiles; - MotorSlope& get_slope(StepType step_type) + MotorSlope& get_slope_with_step_type(StepType step_type) { - return slopes[static_cast(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(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"); } - return static_cast(slopes.size() - 1); + StepType step_type = StepType::FULL; + for (const auto& p : profiles) { + step_type = static_cast( + std::max(static_cast(step_type), + static_cast(p.step_type))); + } + return step_type; } }; diff --git a/backend/genesys/register.h b/backend/genesys/register.h index bbc7ec8a4..51aab90be 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 @@ -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 @@ -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 03c713249..70413d108 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 d4d83dd99..d405ede34 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 06b51ffb0..33fb8fed2 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 e54af654d..79fc62a8b 100644 --- a/backend/genesys/sensor.cpp +++ b/backend/genesys/sensor.cpp @@ -64,6 +64,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 +96,7 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Frontend& frontend) StreamStateSaver state_saver{out}; out << "Genesys_Frontend{\n" - << " id: " << static_cast(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 +117,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(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' @@ -147,7 +142,7 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor) << " 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' + << " 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 e70728e66..face0f8b0 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 #include @@ -114,9 +115,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 +248,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 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& resolutions() const { return resolutions_; } - -private: - bool matches_any_ = false; - std::vector resolutions_; - - template - friend void serialize(Stream& str, ResolutionFilter& x); -}; - -std::ostream& operator<<(std::ostream& out, const ResolutionFilter& resolutions); - -template -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 +258,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 resolutions = VALUE_FILTER_ANY; // the channel list that the sensor is usable at std::vector channels = { 1, 3 }; @@ -313,29 +276,31 @@ 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; - // CCD may present itself as half or quarter-size CCD on certain resolutions - int ccd_size_divisor = 1; + // The resolution to use for shading calibration + unsigned shading_resolution = 0; - // Some scanners need an additional multiplier over the scan coordinates - int pixel_count_multiplier = 1; + // How many real pixels correspond to one shading pixel that is sent to the scanner + unsigned shading_factor = 1; + + // How many pixels the shading data is offset from the acquired data + 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) @@ -359,27 +324,20 @@ struct Genesys_Sensor { // high-enough resolution, every other pixel column is shifted StaggerConfig stagger_config; - GenesysRegisterSettingSet custom_base_regs; // gl646-specific + // True if calibration should be performed on host-side + bool use_host_side_calib = false; + GenesysRegisterSettingSet custom_regs; GenesysRegisterSettingSet custom_fe_regs; // red, green and blue gamma coefficient for default gamma tables AssignableArray gamma; - std::function get_logical_hwdpi_fun; - std::function get_register_hwdpi_fun; - std::function get_ccd_size_divisor_fun; - std::function 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 + unsigned get_optical_resolution() const { - return get_ccd_size_divisor_fun(*this, xres); - } - unsigned get_hwdpi_divisor_for_dpi(unsigned xres) const - { - return get_hwdpi_divisor_fun(*this, xres); + 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,14 +363,17 @@ 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 && @@ -420,7 +381,7 @@ struct Genesys_Sensor { segment_size == other.segment_size && segment_order == other.segment_order && stagger_config == other.stagger_config && - custom_base_regs == other.custom_base_regs && + 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 +392,16 @@ template 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); @@ -453,7 +416,7 @@ void serialize(Stream& str, Genesys_Sensor& x) serialize_newline(str); serialize(str, x.stagger_config); 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 41c66de78..c80bc71be 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 && + 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 && + buffer_size_read == other.buffer_size_read && + enable_ledadd == other.enable_ledadd && + use_host_side_calib == other.use_host_side_calib && + pipeline_needs_reorder == other.pipeline_needs_reorder && + pipeline_needs_ccd == other.pipeline_needs_ccd && + pipeline_needs_shrink == other.pipeline_needs_shrink; +} + 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' @@ -110,13 +156,13 @@ std::ostream& operator<<(std::ostream& out, const ScanSession& session) << " 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' << " 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' + << " enable_ledadd: " << session.enable_ledadd << '\n' + << " use_host_side_calib: " << session.use_host_side_calib << '\n' << " filters: " << (session.pipeline_needs_reorder ? " reorder": "") << (session.pipeline_needs_ccd ? " ccd": "") diff --git a/backend/genesys/settings.h b/backend/genesys/settings.h index a697e604a..0836d6b59 100644 --- a/backend/genesys/settings.h +++ b/backend/genesys/settings.h @@ -46,6 +46,7 @@ #include "enums.h" #include "serialize.h" +#include "utilities.h" namespace genesys { @@ -60,9 +61,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; @@ -116,12 +117,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 @@ -210,15 +212,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 full resolution of the sensor that is being used. + unsigned full_resolution = 0; - // specifies the reduction (if any) of CCD effective dpi which is performed by latching the - // data coming from CCD in such a way that 1/2 or 3/4 of pixel data is ignored. - unsigned ccd_size_divisor = 1; - - // the optical resolution of the scanner. + // 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 +225,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; @@ -280,8 +282,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,15 +309,15 @@ struct ScanSession { // Currently it's always zero. unsigned output_segment_start_offset = 0; - // the sizes of the corresponding buffers + // 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; + // whether calibration should be performed host-side + bool use_host_side_calib = false; + // what pipeline modifications are needed bool pipeline_needs_reorder = false; bool pipeline_needs_ccd = false; @@ -317,10 +329,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 +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.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.buffer_size_read); + serialize(str, x.enable_ledadd); + serialize(str, x.use_host_side_calib); + serialize(str, x.pipeline_needs_reorder); + serialize(str, x.pipeline_needs_ccd); + serialize(str, x.pipeline_needs_shrink); +} + 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 1edf32fd1..4c9b30357 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 }, @@ -242,6 +244,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 +289,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 +311,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 +390,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 +417,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 }, @@ -463,6 +459,23 @@ void genesys_init_frontend_tables() s_frontends->push_back(fe); + 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; @@ -497,6 +510,23 @@ void genesys_init_frontend_tables() s_frontends->push_back(fe); + 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; @@ -514,6 +544,23 @@ void genesys_init_frontend_tables() s_frontends->push_back(fe); + 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; @@ -583,6 +630,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 +653,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 +680,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 2c9ad5e95..215ded7bc 100644 --- a/backend/genesys/tables_gpo.cpp +++ b/backend/genesys/tables_gpo.cpp @@ -188,10 +188,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 +204,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); @@ -292,6 +302,19 @@ void genesys_init_gpo_tables() s_gpo->push_back(gpo); + 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 = { @@ -320,6 +343,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 +367,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 = { @@ -382,10 +425,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 +434,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 000000000..d219bbaf0 --- /dev/null +++ b/backend/genesys/tables_memory_layout.cpp @@ -0,0 +1,150 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2020 Povilas Kanapickas + + 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> 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_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(); + // BUG: we shouldn't use LIDE_200 data for 5600F + ml.models = { ModelId::CANON_LIDE_200, ModelId::CANON_5600F }; + 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 0b3a0af33..caef6e852 100644 --- a/backend/genesys/tables_model.cpp +++ b/backend/genesys/tables_model.cpp @@ -58,10 +58,44 @@ namespace genesys { -StaticInit> s_usb_devices; +StaticInit> 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 = 60.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 = 46.0; + model.y_offset_calib_white_ta = 47.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 = 27.0; model.x_size_ta = 70.0; model.y_size_ta = 230.0; model.y_offset_sensor_to_ta = 11.5; model.y_offset_calib_white_ta = 14.0; + model.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); @@ -655,15 +635,9 @@ void genesys_init_usb_device_tables() model.y_size = 299.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 = 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,27 +872,23 @@ 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; @@ -981,15 +909,9 @@ void genesys_init_usb_device_tables() model.y_size = 299.0; model.y_offset_calib_white = 3.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 = 217.4241; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1006,18 +928,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_UNTESTED | - GENESYS_FLAG_SKIP_WARMUP | - GENESYS_FLAG_SIS_SENSOR | - GENESYS_FLAG_DARK_CALIBRATION | - GENESYS_FLAG_OFFSET_CALIBRATION | - GENESYS_FLAG_CUSTOM_GAMMA; + model.flags = ModelFlag::UNTESTED | + ModelFlag::SIS_SENSOR | + ModelFlag::DARK_CALIBRATION | + 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 +950,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 }, } }; @@ -1047,15 +966,10 @@ void genesys_init_usb_device_tables() model.y_size = 297.0; model.y_offset_calib_white = 1.0; + 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 +985,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); @@ -1112,15 +1022,9 @@ void genesys_init_usb_device_tables() model.y_size = 299.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 = 217.4241; model.post_scan = 0.0; model.eject_feed = 0.0; @@ -1136,18 +1040,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 +1076,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 +1093,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 +1131,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,16 +1152,12 @@ 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); @@ -1298,27 +1181,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 +1205,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 +1238,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 +1257,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 +1290,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 +1309,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 +1342,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 +1361,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 +1395,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 +1414,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 +1439,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 +1463,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 +1490,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 +1513,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 +1550,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 +1569,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 +1600,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 +1619,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 +1650,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 +1669,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 +1700,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 +1719,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 +1754,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 +1773,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 +1804,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 +1823,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 +1856,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 +1874,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 +1905,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 +1924,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 +1957,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 +1976,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 +2008,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 +2024,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 +2056,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 +2079,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 +2112,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 +2135,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 +2173,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 +2192,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 +2224,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 +2243,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 +2341,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 +2374,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.flags = ModelFlag::WARMUP | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK | + ModelFlag::SWAP_16BIT_DATA; - model.shading_lines = 7; - model.shading_ta_lines = 50; 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 +2420,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 +2453,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.flags = ModelFlag::WARMUP | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK; - model.shading_lines = 7; - model.shading_ta_lines = 50; 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 +2561,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 +2594,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.flags = ModelFlag::WARMUP | + ModelFlag::CUSTOM_GAMMA | + ModelFlag::DARK_CALIBRATION | + ModelFlag::SHADING_REPARK; - model.shading_lines = 7; - model.shading_ta_lines = 50; 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 +2702,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 +2722,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 +2760,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 +2779,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 +2810,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 +2829,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 2484d2d59..d78b7cedd 100644 --- a/backend/genesys/tables_motor.cpp +++ b/backend/genesys/tables_motor.cpp @@ -53,272 +53,541 @@ 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::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_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 18f727129..000000000 --- a/backend/genesys/tables_motor_profile.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/* sane - Scanner Access Now Easy. - - Copyright (C) 2019 Povilas Kanapickas - - 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> 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> 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> 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> 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 bbbe441ae..ffda95717 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 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(sensor.optical_res) / 4) { - return sensor.optical_res / 4; - } - if (xres <= static_cast(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(sensor.optical_res)) { - return 4; - } - if (sensor.ccd_size_divisor >= 2 && xres * 2 <= static_cast(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> 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 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 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 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 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[] = { - { { 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 } + { { 50 }, 600, 100, 12000, Ratio{1, 2}, 0, { + { 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 } + { { 75 }, 600, 150, 11000, Ratio{1, 2}, 1, { + { 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 } + { { 100 }, 600, 200, 11000, Ratio{1, 2}, 1, { + { 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 } + { { 150 }, 600, 300, 11000, Ratio{1, 2}, 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 } + { { 200 }, 600, 400, 11000, Ratio{1, 2}, 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 } + { { 300 }, 600, 600, 11000, Ratio{1, 2}, 4, { + { 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 } + { { 400 }, 600, 800, 11000, Ratio{1, 2}, 5, { + { 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 } + { { 600 }, 600, 1200, 11000, Ratio{1, 2}, 8, { + { 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 } + { { 1200 }, 1200, 1200, 11000, Ratio{1, 1}, 16, { + { 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,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); } @@ -572,223 +301,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 resolutions; + unsigned register_dpiset; unsigned exposure_lperiod; + Ratio pixel_count_ratio; + int output_pixel_offset; 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 } + { { 50 }, 200, 7211, Ratio{1, 4}, 0, { + { 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 } + { { 100 }, 400, 7211, Ratio{1, 4}, 1, { + { 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 } + { { 150 }, 600, 7211, Ratio{1, 4}, 1, { + { 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 } + { { 300 }, 1200, 8751, Ratio{1, 4}, 3, { + { 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 } + { { 600 }, 1200, 18760, Ratio{1, 2}, 7, { + { 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 } + { { 1200 }, 1200, 21749, Ratio{1, 1}, 15, { + { 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 +381,10 @@ 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.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } @@ -804,168 +392,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 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 } + { { 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 }, 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 } + { { 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 }, 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 }, 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 }, 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 } + { { 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 +454,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 +466,237 @@ 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); - - - sensor = Genesys_Sensor(); - sensor.sensor_id = SensorId::CIS_XP200; - sensor.optical_res = 600; - sensor.black_pixels = 5; - sensor.dummy_pixel = 38; - sensor.ccd_start_xoffset = 0; - sensor.sensor_pixels = 5200; - sensor.fau_gain_white_ref = 200; - sensor.gain_white_ref = 200; - sensor.exposure = { 0x1450, 0x0c80, 0x0a28 }; - sensor.custom_base_regs = { - { 0x08, 0x16 }, - { 0x09, 0x00 }, - { 0x0a, 0x01 }, - { 0x0b, 0x03 }, - { 0x16, 0xb7 }, - { 0x17, 0x0a }, - { 0x18, 0x20 }, - { 0x19, 0x2a }, - { 0x1a, 0x6a }, - { 0x1b, 0x8a }, - { 0x1c, 0x00 }, - { 0x1d, 0x05 }, - { 0x52, 0x0f }, - { 0x53, 0x13 }, - { 0x54, 0x17 }, - { 0x55, 0x03 }, - { 0x56, 0x07 }, - { 0x57, 0x0b }, - { 0x58, 0x83 }, - { 0x59, 0x00 }, - { 0x5a, 0xc1 }, - { 0x5b, 0x06 }, - { 0x5c, 0x0b }, - { 0x5d, 0x10 }, - { 0x5e, 0x16 }, - }; - sensor.custom_regs = { - { 0x08, 0x06 }, - { 0x09, 0x07 }, - { 0x0a, 0x0a }, - { 0x0b, 0x04 }, - { 0x16, 0x24 }, - { 0x17, 0x04 }, - { 0x18, 0x00 }, - { 0x19, 0x2a }, - { 0x1a, 0x0a }, - { 0x1b, 0x0a }, - { 0x1c, 0x00 }, - { 0x1d, 0x11 }, - { 0x52, 0x08 }, - { 0x53, 0x02 }, - { 0x54, 0x00 }, - { 0x55, 0x00 }, - { 0x56, 0x00 }, - { 0x57, 0x00 }, - { 0x58, 0x1a }, - { 0x59, 0x51 }, - { 0x5a, 0x00 }, - { 0x5b, 0x00 }, - { 0x5c, 0x00 }, - { 0x5d, 0x00 }, - { 0x5e, 0x00 } - }; - sensor.gamma = { 2.1f, 2.1f, 2.1f }; - sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; - sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; - sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; - sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact; - { - struct CustomSensorSettings { - ResolutionFilter resolutions; - std::vector channels; - unsigned exposure_lperiod; - SensorExposure exposure; + struct CustomSensorSettings + { + ValueFilterAny resolutions; + unsigned optical_resolution; + unsigned register_dpiset; + unsigned shading_resolution; + 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 }, 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_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 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; // gl646 + sensor.full_resolution = 600; + sensor.black_pixels = 5; + sensor.dummy_pixel = 38; + sensor.fau_gain_white_ref = 200; + sensor.gain_white_ref = 200; + sensor.exposure = { 0x1450, 0x0c80, 0x0a28 }; + sensor.custom_regs = { + { 0x08, 0x06 }, { 0x09, 0x07 }, { 0x0a, 0x0a }, { 0x0b, 0x04 }, + { 0x16, 0x24 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x2a }, + { 0x1a, 0x0a }, { 0x1b, 0x0a }, { 0x1c, 0x00 }, { 0x1d, 0x11 }, + { 0x52, 0x08 }, { 0x53, 0x02 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x1a }, { 0x59, 0x51 }, { 0x5a, 0x00 }, + { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } + }; + sensor.gamma = { 2.1f, 2.1f, 2.1f }; + + { + struct CustomSensorSettings { + ValueFilterAny resolutions; + std::vector channels; + unsigned exposure_lperiod; + SensorExposure exposure; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 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 resolutions; + unsigned register_dpiset; unsigned exposure_lperiod; + Ratio pixel_count_ratio; + int output_pixel_offset; 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 } + { { 50 }, 200, 5758, Ratio{1, 4}, 0, { + { 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 } + { { 75 }, 300, 4879, Ratio{1, 4}, 1, { + { 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 } + { { 100 }, 400, 4487, Ratio{1, 4}, 1, { + { 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 } + { { 150 }, 600, 4879, Ratio{1, 4}, 2, { + { 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 } + { { 300 }, 1200, 4503, Ratio{1, 4}, 4, { + { 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 } + { { 600 }, 1200, 10251, Ratio{1, 2}, 8, { + { 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 } + { { 1200 }, 1200, 12750, Ratio{1, 1}, 16, { + { 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 +704,10 @@ 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.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } @@ -1390,251 +715,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 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 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 }; - 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 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; - sensor.optical_res = 600; + 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.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 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_DP685; - 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 = 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 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::CIS_CANON_LIDE_200; - sensor.optical_res = 4800; + 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.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 }; + { + struct CustomSensorSettings + { + ValueFilterAny 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; // 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 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 segment_order; GenesysRegisterSettingSet custom_regs; @@ -1642,7 +994,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{}, { + { { 75 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 8, 40, 5136, + std::vector{}, { + { 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{}, { + { 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{}, { + { 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{}, { { 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 +1042,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{}, { + { { 300 }, 600, 1424, { 304, 203, 180 }, Ratio{1, 8}, 2, 160, 5136, + std::vector{}, { { 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 +1053,9 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 600 }, 1432, { 492, 326, 296 }, 5136, std::vector{}, { + // 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{}, { { 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 +1065,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{}, { + { 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 +1087,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 +1098,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 +1113,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 +1130,31 @@ 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 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 segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 200 }, 2848, { 465, 310, 239 }, 5187, std::vector{}, { + { { 75 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 8, 48, 5187, + std::vector{}, { { 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 +1164,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 300 }, 1424, { 465, 310, 239 }, 5187, std::vector{}, { + { { 100 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 6, 64, 5187, + std::vector{}, { { 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 +1175,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 600 }, 1504, { 465, 310, 239 }, 5187, std::vector{}, { + { { 150 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 4, 96, 5187, + std::vector{}, { { 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 +1186,41 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 1200 }, 2696, { 1464, 844, 555 }, 5187, { 0, 1 }, { + { { 200 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 3, 128, 5187, + std::vector{}, { + { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, + { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, + { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, + { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, + } + }, + { { 300 }, 600, 1424, { 465, 310, 239 }, Ratio{1, 8}, 2, 192, 5187, + std::vector{}, { + { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, + { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, + { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, + { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, + } + }, + { { 600 }, 600, 1504, { 465, 310, 239 }, Ratio{1, 8}, 1, 384, 5187, + std::vector{}, { + { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, + { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, + { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, + { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, + { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, + } + }, + { { 1200 }, 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 +1230,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 +1241,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 +1256,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 +1273,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 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 segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 200 }, 2304, { 423, 294, 242 }, 5136, std::vector{}, { + { { 75 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 8, 40, 5136, + std::vector{}, { { 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 +1308,8 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 300 }, 1728, { 423, 294, 242 }, 5136, std::vector{}, { + { { 100 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 6, 53, 5136, + std::vector{}, { { 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 +1319,41 @@ void genesys_init_sensor_tables() { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, - { { 600 }, 1432, { 423, 294, 242 }, 5136, std::vector{}, { + { { 150 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 4, 80, 5136, + std::vector{}, { + { 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{}, { + { 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{}, { + { 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{}, { { 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 +1363,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 +1373,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 +1387,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 +1403,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,182 +1439,143 @@ 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 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 resolutions; + unsigned register_dpihw; + unsigned register_dpiset; int exposure_lperiod; ScanMethod method; + Ratio pixel_count_ratio; + int output_pixel_offset; 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, regs_100_to_600 }, + { { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, regs_100_to_600 }, + { { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, regs_100_to_600 }, + { { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, regs_100_to_600 }, + { { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, regs_100_to_600 }, + { { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, regs_100_to_600 }, + { { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, regs_1200 }, + { { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, regs_2400 }, + { { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, regs_4800 }, + { VALUE_FILTER_ANY, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, + regs_ta_any } }; @@ -2129,8 +1583,13 @@ void genesys_init_sensor_tables() 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.custom_regs = base_custom_regs; sensor.custom_regs.merge(setting.extra_custom_regs); s_sensors->push_back(sensor); @@ -2138,110 +1597,121 @@ 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 resolutions; + unsigned register_dpihw; + unsigned register_dpiset; int exposure_lperiod; ScanMethod method; + Ratio pixel_count_ratio; + int output_pixel_offset; + int shading_pixel_offset; 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, regs_100_to_600 }, + { { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, 50, regs_100_to_600 }, + { { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, 50, regs_100_to_600 }, + { { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, 50, regs_100_to_600 }, + { { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, 50, regs_100_to_600 }, + { { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, 50, regs_100_to_600 }, + { { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, 0, regs_1200 }, + { { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, 0, regs_2400 }, + { { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, 0, regs_4800 }, + { VALUE_FILTER_ANY, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, 0, regs_ta_any } }; 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.custom_regs = base_custom_regs; sensor.custom_regs.merge(setting.extra_custom_regs); s_sensors->push_back(sensor); @@ -2249,200 +1719,162 @@ 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.stagger_config = StaggerConfig{4800, 8}; 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 resolutions; + unsigned optical_resolution; + unsigned register_dpiset; int exposure_lperiod; + bool use_host_side_calib; + int output_pixel_offset; std::vector methods; 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 }, { + { 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 }, + { { 600 }, 1200, 2400, 11640, false, 392, { ScanMethod::FLATBED }, { + { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, + { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, + { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, + { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 }, + { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, + { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, + { 0x9e, 0x2d }, + }, {} + }, + { { 1200 }, 1200, 4800, 11640, false, 794, { ScanMethod::FLATBED }, { + { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, + { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, + { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, + { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 }, + { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, + { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, + { 0x9e, 0x2d }, + }, {} + }, + { { 1200 }, 1200, 4800, 33300, true, 5, { ScanMethod::TRANSPARENCY }, { + { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, + { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, + { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x00 }, { 0x73, 0x02 }, { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, { 0x9e, 0x2d }, - } + }, {} }, - { { 2400 }, 33300, { ScanMethod::TRANSPARENCY }, { - { 0x16, 0x13 }, - { 0x17, 0x0a }, - { 0x18, 0x10 }, - { 0x19, 0x2a }, - { 0x1a, 0x30 }, - { 0x1b, 0x00 }, - { 0x1c, 0x01 }, - { 0x1d, 0x75 }, - { 0x52, 0x0b }, - { 0x53, 0x0d }, - { 0x54, 0x00 }, - { 0x55, 0x03 }, - { 0x56, 0x06 }, - { 0x57, 0x09 }, - { 0x58, 0x53 }, - { 0x59, 0x00 }, - { 0x5a, 0x40 }, + { { 2400 }, 2400, 4800, 33300, true, 10, { ScanMethod::TRANSPARENCY }, { + { 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 }, { + { 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) { - sensor.resolutions = setting.resolutions; - sensor.exposure_lperiod = setting.exposure_lperiod; - sensor.method = method; - sensor.custom_regs = setting.extra_custom_regs; - s_sensors->push_back(sensor); + 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.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_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 resolutions; + unsigned register_dpiset; + Ratio pixel_count_ratio; int exposure_lperiod; + int output_pixel_offset; + int shading_pixel_offset; std::vector methods; GenesysRegisterSettingSet extra_custom_regs; GenesysRegisterSettingSet custom_fe_regs; }; CustomSensorSettings custom_settings[] = { - { { 400 }, 2400, 1, 7200, { ScanMethod::FLATBED }, { - { 0x16, 0x33 }, - { 0x17, 0x0c }, - { 0x18, 0x13 }, - { 0x19, 0x2a }, - { 0x1a, 0x30 }, - { 0x1b, 0x00 }, - { 0x1c, 0x00 }, - { 0x1d, 0x84 }, - { 0x1e, 0xa0 }, - { 0x52, 0x0d }, - { 0x53, 0x10 }, - { 0x54, 0x01 }, - { 0x55, 0x04 }, - { 0x56, 0x07 }, - { 0x57, 0x0a }, - { 0x58, 0x6b }, - { 0x59, 0x00 }, - { 0x5a, 0x40 }, + { { 400 }, 2400, Ratio{1, 4}, 7200, 2, 0, { ScanMethod::FLATBED }, { + { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, + { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, + { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 }, { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, @@ -2450,25 +1882,11 @@ 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 }, { + { 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 +1894,12 @@ 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 }, { + { 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 +1908,11 @@ 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 }, { + { 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 +1922,12 @@ 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 }, { + { 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 +1935,25 @@ 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 }, { + { 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 }, { + { 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 +1963,12 @@ 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 }, { + { 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 +1982,52 @@ 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.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 resolutions; + unsigned optical_resolution; + unsigned register_dpiset; int exposure_lperiod; + int output_pixel_offset; std::vector methods; GenesysRegisterSettingSet extra_custom_regs; GenesysRegisterSettingSet custom_fe_regs; }; CustomSensorSettings custom_settings[] = { - { { 300, 600, 1200 }, 24000, { ScanMethod::FLATBED }, { + { { 300 }, 1200, 1200, 24000, 1, { ScanMethod::FLATBED }, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, @@ -2707,8 +2042,38 @@ void genesys_init_sensor_tables() }, {}, }, - { { 300, 600, 1200 }, 45000, { ScanMethod::TRANSPARENCY, - ScanMethod::TRANSPARENCY_INFRARED }, { + { { 600 }, 1200, 2400, 24000, 2, { ScanMethod::FLATBED }, { + { 0x0c, 0x00 }, + { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, + { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, + { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, + { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, + { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, + { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, + { 0x9e, 0x2d }, + { 0xaa, 0x00 }, + }, + {}, + }, + { { 1200 }, 1200, 4800, 24000, 5, { ScanMethod::FLATBED }, { + { 0x0c, 0x00 }, + { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, + { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, + { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, + { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, + { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, + { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, + { 0x9e, 0x2d }, + { 0xaa, 0x00 }, + }, + {}, + }, + { { 300 }, 1200, 1200, 45000, 6, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, @@ -2723,8 +2088,40 @@ void genesys_init_sensor_tables() }, {}, }, - { { 2400 }, 45000, { ScanMethod::TRANSPARENCY, - ScanMethod::TRANSPARENCY_INFRARED }, { + { { 600 }, 1200, 2400, 45000, 11, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, { + { 0x0c, 0x00 }, + { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, + { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, + { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, + { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, + { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, + { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, + { 0x9e, 0x2d }, + { 0xaa, 0x00 }, + }, + {}, + }, + { { 1200 }, 1200, 4800, 45000, 23, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, { + { 0x0c, 0x00 }, + { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, + { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, + { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, + { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, + { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, + { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, + { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, + { 0x9e, 0x2d }, + { 0xaa, 0x00 }, + }, + {}, + }, + { { 2400 }, 2400, 4800, 45000, 10, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x01 }, { 0x1d, 0x75 }, @@ -2739,8 +2136,8 @@ void genesys_init_sensor_tables() }, {}, }, - { { 4800 }, 45000, { ScanMethod::TRANSPARENCY, - ScanMethod::TRANSPARENCY_INFRARED }, { + { { 4800 }, 4800, 4800, 45000, -1982, { ScanMethod::TRANSPARENCY, + ScanMethod::TRANSPARENCY_INFRARED }, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x61 }, { 0x1d, 0x75 }, @@ -2760,25 +2157,29 @@ 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.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 +2203,66 @@ 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 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 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 segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150 }, 4608, { 462, 609, 453 }, std::vector{}, { + { { 75 }, 1200, 600, 150, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 4, + std::vector{}, { { 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 +2279,44 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 300 }, 4608, { 462, 609, 453 }, std::vector{}, { + { { 100 }, 1200, 600, 200, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 3, + std::vector{}, { + { 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{}, { + { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, + { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, + { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, + { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, + { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, + { 0x61, 0x20 }, + { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, + { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, + { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, + { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, + { 0x88, 0x00 }, { 0x89, 0x65 }, + { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, + { 0x96, 0x00 }, { 0x97, 0x9a }, + { 0x98, 0x21 }, + } + }, + { { 300 }, 1200, 600, 600, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 1, + std::vector{}, { { 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 +2333,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 600 }, 5360, { 823, 1117, 805 }, std::vector{}, { + { { 600 }, 2400, 600, 600, 600, 5360, { 823, 1117, 805 }, Ratio{1, 4}, 1, + std::vector{}, { { 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 +2351,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 +2369,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 +2391,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 +2406,33 @@ 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 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 segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 300 }, 4608, { 1244, 1294, 1144 }, std::vector{}, { + { { 75 }, 1200, 600, 150, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 4, + std::vector{}, { { 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 +2449,62 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, }, }, - { { 600 }, 5360, { 2394, 2444, 2144 }, std::vector{}, { + { { 100 }, 1200, 600, 200, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 3, + std::vector{}, { + { 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{}, { + { 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 }, + }, + }, + { { 300 }, 1200, 600, 600, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 1, + std::vector{}, { + { 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{}, { { 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 +2521,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, }, }, - { { 1200 }, 10528, { 4694, 4644, 4094 }, std::vector{}, { + { { 1200 }, 2400, 1200, 1200, 1200, 10528, { 4694, 4644, 4094 }, Ratio{1, 2}, 1, + std::vector{}, { { 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 +2539,8 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, }, }, - { { 2400 }, 20864, { 8944, 8144, 7994 }, std::vector{}, { + { { 2400 }, 2400, 2400, 2400, 2400, 20864, { 8944, 8144, 7994 }, Ratio{1, 1}, 1, + std::vector{}, { { 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 +2561,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 +2576,33 @@ 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 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 segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector{}, { + { { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, + std::vector{}, { // { 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 +2620,65 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 600 }, 5360, { 388, 574, 393 }, std::vector{}, { + { { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, + std::vector{}, { + // { 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{}, { + // { 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 }, + } + }, + { { 300 }, 2400, 600, 600, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 1, + std::vector{}, { + // { 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{}, { // { 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 +2696,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 +2714,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 +2732,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 +2771,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 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 segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { - { { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector{}, { + { { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, + std::vector{}, { // { 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 +2815,65 @@ void genesys_init_sensor_tables() { 0x98, 0x21 }, } }, - { { 600 }, 5360, { 388, 574, 393 }, std::vector{}, { + { { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, + std::vector{}, { + // { 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{}, { + // { 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{}, { + // { 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{}, { // { 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 +2891,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 +2910,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 +2928,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 +2967,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 }; - 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 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_7200I; - sensor.optical_res = 7200; - sensor.register_dpihw_override = 1200; + 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.stagger_config = StaggerConfig{7200, 4}; + 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 }; + { + struct CustomSensorSettings + { + ValueFilterAny resolutions; + ScanMethod method; + Ratio pixel_count_ratio; + int output_pixel_offset; + unsigned register_dpiset; + }; + + CustomSensorSettings custom_settings[] = { + { { 900 }, ScanMethod::TRANSPARENCY, Ratio{8, 8}, 2, 150, }, + { { 1800 }, ScanMethod::TRANSPARENCY, Ratio{4, 4}, 10, 300, }, + { { 3600 }, ScanMethod::TRANSPARENCY, Ratio{2, 2}, 10, 600, }, + { { 7200 }, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 20, 1200, }, + }; + + 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; + s_sensors->push_back(sensor); + } + } + + + sensor = Genesys_Sensor(); + 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 +3107,43 @@ 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 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; 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, {} }, + { { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2538, 300, {} }, + { { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2538, 600, {} }, + { { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x19c8, 1200, { { 0x02, 0x1b }, { 0x03, 0x14 }, { 0x04, 0x20 }, } }, - { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 900, 8, 0x1f54, 150, {} }, - { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 1800, 4, 0x1f54, 300, {} }, - { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 3600, 2, 0x1f54, 600, {} }, - { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 7200, 1, 0x1f54, 1200, {} }, + { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x1f54, 150, {} }, + { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x1f54, 300, {} }, + { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x1f54, 600, {} }, + { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x1f54, 1200, {} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.method = setting.method; - sensor.ccd_size_divisor = setting.ccd_size_divisor; - sensor.logical_dpihw_override = setting.logical_dpihw_override; - sensor.pixel_count_multiplier = setting.pixel_count_multiplier; + sensor.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.custom_fe_regs = setting.custom_fe_regs; s_sensors->push_back(sensor); } @@ -3399,19 +3151,18 @@ 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 +3193,96 @@ 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 resolutions; + unsigned shading_resolution; + Ratio pixel_count_ratio; + int output_pixel_offset; + unsigned register_dpiset; }; 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 }, + { { 1800 }, 1800, Ratio{4, 4}, 5, 300 }, + { { 3600 }, 3600, Ratio{2, 2}, 10, 600 }, + { { 7200 }, 7200, Ratio{1, 1}, 20, 1200 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; - sensor.ccd_size_divisor = setting.ccd_size_divisor; - sensor.logical_dpihw_override = setting.logical_dpihw_override; - sensor.pixel_count_multiplier = setting.pixel_count_multiplier; - sensor.dpiset_override = setting.dpiset_override; + 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; 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.fau_gain_white_ref = 210; + sensor.gain_white_ref = 230; + sensor.exposure = { 0x0000, 0x0000, 0x0000 }; + sensor.exposure_lperiod = 14000; + sensor.stagger_config = StaggerConfig{7200, 4}; + 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 resolutions; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 600 }, 100, 10 }, + { { 1200 }, 200, 20 }, + { { 2400 }, 400, 40 }, + { { 3600 }, 600, 60 }, + { { 7200 }, 1200, 120 }, + }; + + 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; + 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.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 }, @@ -3516,57 +3313,108 @@ 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 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; }; 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 }, + { { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2f44, 300 }, + { { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2f44, 600 }, + { { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x2f44, 1200 }, + { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x2af8, 150 }, + { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x2af8, 300 }, + { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x2af8, 600 }, + { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x2af8, 1200 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.method = setting.method; - sensor.ccd_size_divisor = setting.ccd_size_divisor; - sensor.logical_dpihw_override = setting.logical_dpihw_override; - sensor.pixel_count_multiplier = setting.pixel_count_multiplier; + sensor.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; 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.stagger_config = StaggerConfig{7200, 4}; + 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 resolutions; + ScanMethod method; + unsigned register_dpiset; + int output_pixel_offset; + }; + + CustomSensorSettings custom_settings[] = { + { { 900 }, ScanMethod::TRANSPARENCY, 150, 15 }, + { { 1800 }, ScanMethod::TRANSPARENCY, 300, 30 }, + { { 3600 }, ScanMethod::TRANSPARENCY, 600, 60 }, + { { 7200 }, ScanMethod::TRANSPARENCY, 1200, 120 }, + { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 150, 15 }, + { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 300, 30 }, + { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 600, 60 }, + { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1200, 120 }, + }; + + 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; + 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 +3428,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 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 +3477,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 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 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 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, 0x1b, 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 12f726f32..e8af49410 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 acf0f6d4a..fc8128ca2 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 425f09cc2..f3287095b 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 8ac03e010..38cc3b389 100644 --- a/backend/genesys/test_settings.h +++ b/backend/genesys/test_settings.h @@ -58,9 +58,11 @@ using TestCheckpointCallback = std::function(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(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 265c57c13..aa8b89ab2 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 1e268b566..2f4ceb73e 100644 --- a/backend/genesys/utilities.h +++ b/backend/genesys/utilities.h @@ -46,12 +46,36 @@ #include "error.h" #include +#include #include #include #include + 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(v * (1 << SANE_FIXED_SCALE_SHIFT)); +} + +inline SANE_Word float_to_fixed(float v) +{ + return static_cast(v * (1 << SANE_FIXED_SCALE_SHIFT)); +} + +inline float fixed_to_float(SANE_Word v) +{ + return static_cast(v) / (1 << SANE_FIXED_SCALE_SHIFT); +} + +inline double fixed_to_double(SANE_Word v) +{ + return static_cast(v) / (1 << SANE_FIXED_SCALE_SHIFT); +} + template void compute_array_percentile_approx(T* result, const T* data, std::size_t line_count, std::size_t elements_per_line, @@ -85,6 +109,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(arg) * multiplier_ / divisor_; + } + + int apply(int arg) const + { + return static_cast(arg) * multiplier_ / divisor_; + } + + float apply(float arg) const + { + return arg * multiplier_ / divisor_; + } + + unsigned apply_inverse(unsigned arg) const + { + return static_cast(arg) * divisor_ / multiplier_; + } + + int apply_inverse(int arg) const + { + return static_cast(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 + friend void serialize(Stream& str, Ratio& x); +}; + +template +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 BasicStreamStateSaver { diff --git a/backend/genesys/value_filter.h b/backend/genesys/value_filter.h new file mode 100644 index 000000000..ba555672e --- /dev/null +++ b/backend/genesys/value_filter.h @@ -0,0 +1,140 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2020 Povilas Kanapickas + + 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 +#include +#include +#include + +namespace genesys { + +struct AnyTag {}; +constexpr AnyTag VALUE_FILTER_ANY{}; + +template +class ValueFilterAny +{ +public: + ValueFilterAny() : matches_any_{false} {} + ValueFilterAny(AnyTag) : matches_any_{true} {} + ValueFilterAny(std::initializer_list 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& values() const { return values_; } + +private: + bool matches_any_ = false; + std::vector values_; + + template + friend void serialize(Stream& str, ValueFilterAny& x); +}; + +template +std::ostream& operator<<(std::ostream& out, const ValueFilterAny& values) +{ + if (values.matches_any()) { + out << "ANY"; + return out; + } + out << format_vector_indent_braced(4, "", values.values()); + return out; +} + +template +void serialize(Stream& str, ValueFilterAny& x) +{ + serialize(str, x.matches_any_); + serialize_newline(str); + serialize(str, x.values_); +} + + +template +class ValueFilter +{ +public: + ValueFilter() = default; + ValueFilter(std::initializer_list 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& values() const { return values_; } + +private: + std::vector values_; + + template + friend void serialize(Stream& str, ValueFilter& x); +}; + +template +std::ostream& operator<<(std::ostream& out, const ValueFilter& values) +{ + if (values.values().empty()) { + out << "(none)"; + return out; + } + out << format_vector_indent_braced(4, "", values.values()); + return out; +} + +template +void serialize(Stream& str, ValueFilter& x) +{ + serialize_newline(str); + serialize(str, x.values_); +} + +} // namespace genesys + +#endif // BACKEND_GENESYS_VALUE_FILTER_H diff --git a/backend/gt68xx_high.c b/backend/gt68xx_high.c index 782b4f363..563323c8e 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/hp5400.h b/backend/hp5400.h index b0efb4f5e..78244e0f1 100644 --- a/backend/hp5400.h +++ b/backend/hp5400.h @@ -1,4 +1,5 @@ /* sane - Scanner Access Now Easy. + Copyright (C) 20020 Ralph Little Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon @@ -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 34bf55dbd..0e94d9df8 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 Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Copyright (c) 2003 Henning Meier-Geinitz, @@ -149,11 +150,137 @@ 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. + * + */ + 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 981ce0b04..aa40da02b 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 (C) 2003 Thomas Soumarmon (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; @@ -156,6 +189,22 @@ HP5400_SANE_STATIC int 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); diff --git a/backend/hp5400_sane.c b/backend/hp5400_sane.c index e5fdf43c0..b6fa6da82 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 Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon @@ -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, - optGroupGeometry, - optTLX, optTLY, optBRX, optBRY, optDPI, - optGroupImage, + optGroupGeometry, + optTLX, optTLY, optBRX, optBRY, + + optGroupEnhancement, optGammaTableRed, /* Gamma Tables */ optGammaTableGreen, optGammaTableBlue, + optGroupSensors, + + optSensorScanTo, + optSensorWeb, + optSensorReprint, + optSensorEmail, + optSensorCopy, + optSensorMoreOptions, + optSensorCancel, + optSensorPowerSave, + optSensorCopiesUp, + optSensorCopiesDown, + optSensorColourBW, + + optSensorColourBWState, + optSensorCopyCount, + + // Unsupported as yet. + //optGroupMisc, + //optLamp, + //optCalibrate, + optLast, /* Disable the offset code */ - - optGroupMisc, - optOffsetX, optOffsetY - - -/* put temporarily disabled options here after optLast */ -/* - optLamp, -*/ - } 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; + pVal->w = rangeYmm.max; 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 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 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 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 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 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; - 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; - break; - - -#if 0 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; + { + // 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; + } - 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; + 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 d5c2857c0..9a7a8b441 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 c0e1fa394..fc89d8738 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 e4b841b1f..3e8276452 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 c812f2ca7..8c3771141 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 eafdb997a..6a9aa8694 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 df191922b..4ad2e1b0a 100644 --- a/backend/net.c +++ b/backend/net.c @@ -67,7 +67,7 @@ #include #include /* OS/2 needs this _after_ , grrr... */ -#ifdef WITH_AVAHI +#if WITH_AVAHI # include # include @@ -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/pixma.c b/backend/pixma/pixma.c index f763496bb..aed9e7013 100644 --- a/backend/pixma/pixma.c +++ b/backend/pixma/pixma.c @@ -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) @@ -1597,6 +1598,7 @@ sane_exit (void) sane_close (first_scanner); cleanup_device_list (); pixma_cleanup (); + sanei_usb_exit (); } SANE_Status @@ -1624,7 +1626,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? */ diff --git a/backend/pixma/pixma.h b/backend/pixma/pixma.h index c2df3cc03..7a417c031 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 + Copyright (C) 2011-2020 Rolf Bensch Copyright (C) 2007-2008 Nicolas Martin, Copyright (C) 2006-2007 Wittawat Yamwong @@ -120,7 +120,7 @@ typedef uint32_t uint32_t; /**@{*/ #define PIXMA_VERSION_MAJOR 0 #define PIXMA_VERSION_MINOR 27 -#define PIXMA_VERSION_BUILD 0 +#define PIXMA_VERSION_BUILD 7 /**@}*/ /** \name Error codes */ diff --git a/backend/pixma/pixma_bjnp.c b/backend/pixma/pixma_bjnp.c index 34ba918f2..4e837147b 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]; @@ -453,69 +460,6 @@ determine_scanner_serial (const char *hostname, const char * mac_address, char * return serial; } -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 edfb330d6..19ba4968c 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 7b7ecec7d..171df1a34 100644 --- a/backend/pixma/pixma_common.c +++ b/backend/pixma/pixma_common.c @@ -48,11 +48,18 @@ #include #include #include +#include #include /* pow(C90) */ #include /* gettimeofday(4.3BSD) */ #include /* usleep */ +#if defined(HAVE_LIBXML2) +# include +#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 */ } @@ -1185,3 +1219,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 c0ed4bac0..ae4ceace9 100644 --- a/backend/pixma/pixma_common.h +++ b/backend/pixma/pixma_common.h @@ -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 ce0c37d85..737816826 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 + Copyright (C) 2011-2020 Rolf Bensch Copyright (C) 2007-2009 Nicolas Martin, 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 @@ -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_mp150.c b/backend/pixma/pixma_mp150.c index 397370275..ce2c33b8c 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 + Copyright (C) 2011-2020 Rolf Bensch Copyright (C) 2007-2009 Nicolas Martin, Copyright (C) 2006-2007 Wittawat Yamwong @@ -282,8 +282,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 +323,6 @@ 00000001\ " -#define XML_OK "OK" - enum mp150_state_t { state_idle, @@ -460,7 +460,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 @@ -917,11 +917,13 @@ handle_interrupt (pixma_t * s, int timeout) 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) + s->events = PIXMA_EV_BUTTON2 | buf[0x13]; /* button 2 = cancel / end scan */ + else if (buf[0x13]) + s->events = PIXMA_EV_BUTTON1 | buf[0x13]; /* button 1 = start scan */ } else /* button no. in buf[0] @@ -1765,7 +1767,7 @@ 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 TR7500 Series", "TR7500", TR7500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF), @@ -1798,8 +1800,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_mp800.c b/backend/pixma/pixma_mp800.c index feef6112c..e050bc02d 100644 --- a/backend/pixma/pixma_mp800.c +++ b/backend/pixma/pixma_mp800.c @@ -153,8 +153,6 @@ 00000001\ " -#define XML_OK "OK" - enum mp810_state_t { state_idle, @@ -294,7 +292,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) diff --git a/backend/plustek-usbshading.c b/backend/plustek-usbshading.c index 98a28d9a4..e789b4356 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 e79a7f3b9..8cf86f3c3 100644 --- a/backend/ricoh2_buffer.c +++ b/backend/ricoh2_buffer.c @@ -44,14 +44,8 @@ #include "../include/sane/config.h" -#include #include - -#if defined(__APPLE__) && defined(__MACH__) -#include -#else -#include -#endif +#include #include "../include/sane/sanei_debug.h" diff --git a/backend/test-picture.c b/backend/test-picture.c index 46407dcd6..66374c74a 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/umax_pp_low.c b/backend/umax_pp_low.c index ddcf3da56..569f82430 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 006e7f70a..f9245d077 100644 --- a/backend/v4l.c +++ b/backend/v4l.c @@ -72,7 +72,6 @@ #include "../include/sane/saneopts.h" #include -#include /* 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 e6673d0de..7698be11a 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 39bf66996..4fcbeb6f5 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 ### ################### diff --git a/configure.ac b/configure.ac index bed7e1aff..bf7615463 100644 --- a/configure.ac +++ b/configure.ac @@ -129,17 +129,26 @@ AC_ARG_WITH(v4l, # as "$with_v4l" will be set to "no"]) if test "$with_v4l" != "no" ; then - PKG_CHECK_MODULES(LIBV4L, libv4l1, have_libv4l1=yes, have_libv4l1=no) + PKG_CHECK_MODULES(LIBV4L, [libv4l1 >= 0.8.3], have_libv4l1=yes, have_libv4l1=no) fi -AC_ARG_ENABLE(avahi, - AS_HELP_STRING([--enable-avahi], [enable Avahi support for saned and the net backend]), - [enable_avahi=$enableval], [enable_avahi=no]) - -if test "$enable_avahi" = "yes"; then - PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6.24 ], - [AC_DEFINE(WITH_AVAHI, 1, [define if Avahi support is enabled for saned and the net backend])], enable_avahi=no) -fi +AC_ARG_WITH(avahi, + AS_HELP_STRING([--with-avahi], + [enable Avahi support @<:@default=check@:>@]), + [], + [with_avahi=check]) +AC_DEFINE(WITH_AVAHI, + [0], [Define to 1 if Avahi support is available]) +AS_IF([test xno != "x$with_avahi"], + [PKG_CHECK_MODULES(AVAHI, [avahi-client >= 0.6.24], + [AC_DEFINE([WITH_AVAHI], [1]) + with_avahi=yes + ], + [AS_IF([test xcheck != "x$with_avahi"], + [AC_MSG_ERROR([Avahi support requested but not found])]) + with_avahi=no + ]) + ]) AM_CONDITIONAL([have_libavahi], [test x != "x$AVAHI_LIBS"]) dnl check sane to make sure we don't have two installations @@ -453,6 +462,28 @@ AS_IF([test xno != "x$with_libcurl"], ]) AM_CONDITIONAL([have_libcurl], [test x != "x$libcurl_LIBS"]) +dnl ****************************************************************** +dnl Check for poppler-glib availability +dnl ****************************************************************** +AC_ARG_WITH(poppler-glib, + AS_HELP_STRING([--with-poppler-glib], + [enable functionality that needs poppler-glib @<:@default=check@:>@]), + [], + [with_poppler_glib=check]) +AC_DEFINE(HAVE_POPPLER_GLIB, + [0], [Define to 1 if libpoppler-glib is available]) +AS_IF([test xno != "x$with_poppler_glib"], + [PKG_CHECK_MODULES(POPPLER_GLIB, [poppler-glib], + [AC_DEFINE([HAVE_POPPLER_GLIB], [1]) + with_poppler_glib=yes + ], + [AS_IF([test xcheck != "x$with_poppler_glib"], + [AC_MSG_ERROR([poppler-glib requested but not found])]) + with_poppler_glib=no + ]) + ]) +AM_CONDITIONAL([have_poppler_glib], [test x != "x$POPPLER_GLIB_LIBS"]) + dnl ****************************************************************** dnl Check for USB record/replay support dnl ****************************************************************** @@ -634,7 +665,7 @@ AC_ARG_ENABLE(local-backends, [turn off compilation of all backends but net])) ALL_BACKENDS="abaton agfafocus apple artec artec_eplus48u as6e \ - avision bh canon canon630u canon_dr canon_pp cardscan \ + avision bh canon canon630u canon_dr canon_lide70 canon_pp cardscan \ coolscan coolscan2 coolscan3 dc25 dc210 dc240 \ dell1600n_net dmc epjitsu epson epson2 epsonds escl fujitsu \ genesys gphoto2 gt68xx hp hp3500 hp3900 hp4200 hp5400 \ @@ -664,14 +695,18 @@ else fi fi -SANE_CHECK_BACKENDS - if test "${sane_cv_use_libjpeg}" = "yes"; then SANEI_SANEI_JPEG_LO="../sanei/sanei_jpeg.lo" + saved_LIBS="${LIBS}" + LIBS="${JPEG_LIBS}" + AC_CHECK_FUNCS(jpeg_crop_scanline jpeg_skip_scanlines) + LIBS="${saved_LIBS}" fi AM_CONDITIONAL(HAVE_JPEG, test x$sane_cv_use_libjpeg = xyes) AC_SUBST(SANEI_SANEI_JPEG_LO) +SANE_CHECK_BACKENDS + AC_ARG_ENABLE(pnm-backend, AS_HELP_STRING([--enable-pnm-backend], [enable the pnm backend for testing frontends (possible security risk, see PROBLEMS file)]), @@ -769,54 +804,6 @@ AC_ARG_ENABLE(parport-directio, fi ]) -dnl ****************************************************************** -dnl SANE API specification format conversion support -dnl ****************************************************************** -AC_ARG_WITH(api-spec, - AS_HELP_STRING([--with-api-spec], - [convert API spec to supported output formats @<:@default=check@:>@]), - [], - [with_api_spec=check]) -dnl Test for all tools that may be involved. These tests are fast and -dnl running them allows for the Makefile targets to be formulated such -dnl that any non-requested formats can be made using a one-off without -dnl the need to reconfigure. -AC_PATH_PROG(MAKEINDEX, makeindex, no) -AC_PATH_PROG(DVIPS, dvips, no) -AC_PATH_PROG(LATEX, latex, no) -AC_PATH_PROG(PDFLATEX, pdflatex, no) -AC_PATH_PROG(FIG2DEV, fig2dev, no) -AC_PATH_PROG(GS, gs, no) -AC_PATH_PROG(DLH, dlh, no) -AC_PATH_PROG(PPMTOGIF, ppmtogif, no) -AS_IF([test xno != "x$with_api_spec"], - [dnl Flag formats for which all required tools have been found - AS_IF([ test xno != "x$MAKEINDEX" \ - && test xno != "x$DVIPS" \ - && test xno != "x$FIG2DEV" \ - && test xno != "x$LATEX"], [with_api_ps=yes]) - AS_IF([ test xno != "x$MAKEINDEX" \ - && test xno != "x$PDFLATEX" \ - && test xno != "x$FIG2DEV" \ - && test xno != "x$GS"], [with_api_pdf=yes]) - AS_IF([ test xno != "x$MAKEINDEX" \ - && test xno != "x$DVIPS" \ - && test xno != "x$FIG2DEV" \ - && test xno != "x$DLH" \ - && test xno != "x$GS" \ - && test xno != "x$PPMTOFIG" ], [with_api_html=yes]) - - AS_IF([test xyes = "x$with_api_spec" \ - && test xyes != "x$with_api_ps" \ - && test xyes != "x$with_api_pdf" \ - && test xyes != "x$with_api_html"], - [AC_MSG_ERROR([tools to convert the API spec are missing]) - ]) - ]) -AM_CONDITIONAL(WITH_API_PS, [test x$with_api_ps = xyes]) -AM_CONDITIONAL(WITH_API_PDF, [test x$with_api_pdf = xyes]) -AM_CONDITIONAL(WITH_API_HTML, [test x$with_api_html = xyes]) - dnl *********************************************************************** dnl Write output files dnl *********************************************************************** @@ -865,8 +852,9 @@ else echo "Build saned: no" fi echo "IPv6 support: `eval eval echo ${ipv6}`" -echo "Avahi support: `eval eval echo ${enable_avahi}`" +echo "Avahi support: `eval eval echo ${with_avahi}`" echo "cURL support: `eval eval echo ${with_libcurl}`" +echo "POPPLER_GLIB support: `eval eval echo ${with_poppler_glib}`" echo "SNMP support: `eval eval echo ${with_snmp}`" echo "-> The following backends will be built:" for backend in ${BACKENDS} ; do diff --git a/doc/.gitignore b/doc/.gitignore index 076d9753b..3abf4673f 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -2,24 +2,11 @@ *.5 *.7 *.8 -*.eps *.html -*.pdf descriptions-external.db descriptions.db doxygen-genesys.conf doxygen-sanei.conf doxygen_sqlite3.db genesys-html -sane.aux -sane.cb -sane.dvi -sane.idx -sane.ilg -sane.ind -sane.lof -sane.log -sane.lot -sane.ps -sane.toc sanei-html diff --git a/doc/Makefile.am b/doc/Makefile.am index 56d35173c..5cf301128 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -18,6 +18,7 @@ EXTRA_DIST = scanimage.man sane-config.man sane-find-scanner.man \ # custom install/uninstall if we required man pages for every backend. BACKEND_5MANS = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 \ + sane-canon_lide70.5 \ sane-dll.5 sane-dc25.5 sane-dmc.5 sane-epson.5 sane-epson2.5 sane-epsonds.5 \ sane-escl.5 sane-hp.5 sane-gphoto2.5 sane-leo.5 sane-lexmark.5 \ sane-matsushita.5 sane-microtek.5 sane-microtek2.5 sane-mustek.5 \ @@ -40,6 +41,7 @@ BACKEND_5MANS = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 \ sane-kvs40xx.5 sane-p5.5 sane-magicolor.5 EXTRA_DIST += sane-abaton.man sane-agfafocus.man sane-apple.man sane-as6e.man \ + sane-canon_lide70.man \ sane-dll.man sane-dc25.man sane-dmc.man sane-epson.man \ sane-epson2.man sane-epsonds.man sane-escl.man sane-hp.man sane-gphoto2.man \ sane-leo.man sane-lexmark.man sane-matsushita.man sane-microtek.man \ @@ -80,11 +82,10 @@ HTML_PAGES = sane-backends.html sane-backends-external.html \ endif doc_DATA = $(HTML_PAGES) -all: bemans $(API_SPECS) html-pages +all: bemans html-pages dist_doc_DATA = backend-writing.txt nobase_dist_doc_DATA = $(BEDOCS) -doc_DATA += $(API_SPECS) EXTRA_DIST += descriptions.txt releases.txt sane-logo2.jpg sane-logo.png \ sane.png @@ -151,6 +152,7 @@ DESC_FILES = descriptions/abaton.desc descriptions/agfafocus.desc \ descriptions/artec_eplus48u.desc descriptions/as6e.desc \ descriptions/avision.desc descriptions/bh.desc descriptions/canon630u.desc \ descriptions/canon.desc descriptions/canon_dr.desc \ + descriptions/canon_lide70.desc \ descriptions/canon_pp.desc descriptions/cardscan.desc \ descriptions/coolscan2.desc descriptions/coolscan.desc \ descriptions/coolscan3.desc \ @@ -215,89 +217,6 @@ install-data-local: install-beman5 uninstall-local: rm -rf $(DESTDIR)$(beman5dir)/sane-*.5 -## SANE API specification format conversion support - -API_SPECS = -if WITH_API_PS -API_SPECS += sane.ps -endif -if WITH_API_PDF -API_SPECS += sane.pdf -endif -if WITH_API_HTML -API_SPECS += sane-html -endif - -API_SPEC_INPUTS = $(srcdir)/sane.tex -API_SPEC_INPUTS += $(srcdir)/net.tex -EXTRA_DIST += $(API_SPEC_INPUTS) - -API_SPEC_TEX_FIGS = -API_SPEC_TEX_FIGS += figs/area.fig -API_SPEC_TEX_FIGS += figs/flow.fig -API_SPEC_TEX_FIGS += figs/hierarchy.fig -API_SPEC_TEX_FIGS += figs/image-data.fig -API_SPEC_TEX_FIGS += figs/xfer.fig -EXTRA_DIST += $(API_SPEC_TEX_FIGS) - -API_SPEC_EPS_FIGS = -API_SPEC_EPS_FIGS += figs/area.eps -API_SPEC_EPS_FIGS += figs/flow.eps -API_SPEC_EPS_FIGS += figs/hierarchy.eps -API_SPEC_EPS_FIGS += figs/image-data.eps -API_SPEC_EPS_FIGS += figs/xfer.eps - -API_SPEC_PDF_FIGS = -API_SPEC_PDF_FIGS += figs/area.pdf -API_SPEC_PDF_FIGS += figs/flow.pdf -API_SPEC_PDF_FIGS += figs/hierarchy.pdf -API_SPEC_PDF_FIGS += figs/image-data.pdf -API_SPEC_PDF_FIGS += figs/xfer.pdf - -## These icons are referred to in the generated HTML output. -API_SPEC_HTML_ICONS = -API_SPEC_HTML_ICONS += icons/contents.gif -API_SPEC_HTML_ICONS += icons/index.gif -API_SPEC_HTML_ICONS += icons/next.gif icons/next_gr.gif -API_SPEC_HTML_ICONS += icons/previous.gif icons/previous_gr.gif -API_SPEC_HTML_ICONS += icons/references.gif icons/references_gr.gif -API_SPEC_HTML_ICONS += icons/up.gif icons/up_gr.gif -EXTRA_DIST += $(API_SPEC_HTML_ICONS) - -am_TEXINPUTS = TEXINPUTS="$(builddir):$(srcdir):$$TEXINPUTS" - -sane.ind: $(API_SPEC_INPUTS) - @echo Generating index for $<... - @touch sane.ind - @$(am_TEXINPUTS) $(LATEX) $< /dev/null && \ - $(MAKEINDEX) -q sane.idx && \ - $(am_TEXINPUTS) $(LATEX) $< /dev/null - -.fig.eps: - @test -d $(@D) || $(MKDIR_P) $(@D) - $(FIG2DEV) -L eps $< $@ - -sane.dvi: $(API_SPEC_INPUTS) $(API_SPEC_EPS_FIGS) sane.ind - @echo Generating $@ from $<... - @$(am_TEXINPUTS) $(LATEX) $< /dev/null - -sane.ps: sane.dvi - @echo Generating $@ from $<... - @$(am_TEXINPUTS) $(DVIPS) -q $< -o $@ - -.fig.pdf: - @test -d $(@D) || $(MKDIR_P) $(@D) - $(FIG2DEV) -L pdf $< $@ - -sane.pdf: $(API_SPEC_INPUTS) $(API_SPEC_PDF_FIGS) sane.ind - @echo Generating $@ from $<... - @$(am_TEXINPUTS) $(PDFLATEX) $< >/dev/null - -sane-html: sane.dvi - $(am_TEXINPUTS) $(DLH) $(srcdir)/sane.tex - -## ^^ - html-man: $(MANPAGES) @for page in $(MANPAGES); do \ echo "translating $${page} to $${page}.html..."; \ @@ -344,20 +263,12 @@ descriptions-external.db: $(DESC_EXT_FILES) ../tools/sane-desc > descriptions-external.db html-pages: $(HTML_PAGES) -html-local: html-pages html-man sane-html - -clean-local: - rm -f *.toc *.aux *.log *.cp *.fn *.tp *.vr *.pg *.ky *.blg *.idx *.cb - rm -f *.ilg - rm -f $(API_SPEC_EPS_FIGS) $(API_SPEC_PDF_FIGS) - -rmdir figs +html-local: html-pages html-man distclean-local: rm -f $(MANPAGES) - rm -f *.lot *.lof *.ind - rm -f sane.dvi sane.ps sane-backends.html sane-backends-external.html + rm -f sane-backends.html sane-backends-external.html rm -f sane-mfgs.html sane-mfgs-external.html - rm -f sane/*.html sane/*.gif rm -f doxygen-sanei.conf doxygen-genesys.conf -rm -rf sane sanei-html for manpage in $(MANPAGES) ; do \ diff --git a/doc/backend-writing.txt b/doc/backend-writing.txt index 736fcec40..5823661a6 100644 --- a/doc/backend-writing.txt +++ b/doc/backend-writing.txt @@ -12,6 +12,7 @@ GETTING STARTED about it. You should mention that the code will be open-source, however. * Read the SANE standard. + See https://sane-project.gitlab.io/standard/ * One approach is to write a stand-alone scanning program first. Debugging this program is usually easier than using the SANE libraries. However, keep @@ -157,8 +158,6 @@ sane-backends/doc/ Used by doxygen to create the documentation of the sanei code. * releases.txt: Explains how to make releases of sane-backends. - * sane.tex, net.tex: - Contains the LaTeX source of the SANE standard. * descriptions/ (directory) Contains the .desc files for every backend that is included into sane-backends. diff --git a/doc/descriptions-external/epkowa.desc b/doc/descriptions-external/epkowa.desc index 9217aaf4f..5bc43bf41 100644 --- a/doc/descriptions-external/epkowa.desc +++ b/doc/descriptions-external/epkowa.desc @@ -1,5 +1,6 @@ ;;; epkowa.desc -*- emacs-lisp -*- (eh, sort of) ;;; Copyright (C) 2004--2015 Olaf Meeuwissen +;;; Copyright (C) 2019 SEIKO EPSON Corporation ;;; ;;; This file is part of the "Image Scan!" documentation. ;;; @@ -40,7 +41,7 @@ ;; Backend data. ;; :backend "epkowa" -:version "iscan 2.30.1/iscan-data 1.36.0" +:version "iscan 2.30.4/iscan-data 1.39.1" :url "http://download.ebz.epson.net/dsc/search/01/search/?OSC=LX" @@ -223,6 +224,11 @@ :status :complete :comment "requires DFSG non-free iscan-plugin-ds-30" +:model "DS-G20000" +:interface "USB" +:usbid "0x04b8" "0x015b" +:status :good + :model "EP-702A" :interface "USB" :usbid "0x04b8" "0x0850" @@ -650,6 +656,12 @@ :status :good :comment "overseas version of the ES-G11000" +:model "Expression 12000XL" +:interface "USB" +:usbid "0x04b8" "0x015b" +:status :good +:comment "overseas version of the DS-G20000" + :model "F-3200" ; product spec (JP) :interface "USB IEEE1394" :usbid "0x04b8" "0x080a" @@ -1198,19 +1210,25 @@ :interface "USB" :usbid "0x04b8" "0x08ad" :status :good -:comment "network interface not supported
business all-in-one" +:comment "network interface supported via DFSG non-free iscan-network-nt package
business all-in-one" :model "LP-M8040A" :interface "USB" :usbid "0x04b8" "0x08ad" :status :good -:comment "network interface not supported
business all-in-one" +:comment "network interface supported via DFSG non-free iscan-network-nt package
business all-in-one" :model "LP-M8040F" :interface "USB" :usbid "0x04b8" "0x08ad" :status :good -:comment "network interface not supported
business all-in-one" +:comment "network interface supported via DFSG non-free iscan-network-nt package
business all-in-one" + +:model "LP-M8170" +:interface "USB" +:usbid "0x04b8" "0x1111" +:status :good +:comment "network interface supported via DFSG non-free iscan-network-nt package
business all-in-one" :model "M200 Series" :interface "USB" diff --git a/doc/descriptions/canon_lide70.desc b/doc/descriptions/canon_lide70.desc new file mode 100644 index 000000000..6b50fe4d4 --- /dev/null +++ b/doc/descriptions/canon_lide70.desc @@ -0,0 +1,29 @@ +; +; SANE Backend specification file +; +; It's basically emacs-lisp --- so ";" indicates comment to end of line. +; All syntactic elements are keyword tokens, followed by a string or +; keyword argument, as specified. +; +; ":backend" *must* be specified. +; All other information is optional (but what good is the file without it?). +; + +:backend "canon_lide70" ; name of backend +:new :yes +:url "http://www.juergen-ernst.de/info_sane.html" +:version "0" ; version of backend +:manpage "sane-canon_lide70" ; name of manpage (if it exists) +:devicetype :scanner ; start of a list of devices.... + ; other types: :stillcam, :vidcam, + ; :meta, :api + +:mfg "Canon" ; name a manufacturer +:url "http://www.canon.com/" + +;================================================== +:model "CanoScan LiDE 70" +:interface "USB" +:usbid "0x04a9" "0x2225" +:status :basic +:comment "Please test!" diff --git a/doc/descriptions/escl.desc b/doc/descriptions/escl.desc index f654f7568..fe9b15395 100644 --- a/doc/descriptions/escl.desc +++ b/doc/descriptions/escl.desc @@ -1,5 +1,4 @@ :backend "escl" -:new :yes :manpage "sane-escl" :url "https://support.apple.com/en-us/HT201311" :comment "The eSCL backend for sane supports AirScan/eSCL devices that announce themselves on mDNS as _uscan._utcp or _uscans._utcp" diff --git a/doc/descriptions/fujitsu.desc b/doc/descriptions/fujitsu.desc index b858415b4..be47ee27b 100644 --- a/doc/descriptions/fujitsu.desc +++ b/doc/descriptions/fujitsu.desc @@ -657,3 +657,9 @@ :status :good :usbid "0x04c5" "0x159f" :comment "small, current, WiFi not supported." + +:model "fi-800R" +:interface "USB" +:status :good +:usbid "0x04c5" "0x15fc" +:comment "small, current, both feed methods are supported." diff --git a/doc/descriptions/genesys.desc b/doc/descriptions/genesys.desc index 888f25279..366e280df 100644 --- a/doc/descriptions/genesys.desc +++ b/doc/descriptions/genesys.desc @@ -17,20 +17,59 @@ :status :basic :comment "Has a Primax USB ID" +:model "OpticFilm 7200" +:interface "USB" +:usbid "0x07b3" "0x0807" +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported" + +:model "OpticFilm 7200 (v2)" +:interface "USB" +:usbid "0x07b3" "0x0c07" +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported" + :model "OpticFilm 7200i" :interface "USB" :usbid "0x07b3" "0x0c04" -:status :basic +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported in both regular transparency and infrared modes" :model "OpticFilm 7300" :interface "USB" :usbid "0x07b3" "0x0c12" -:status :basic +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported" + +:model "OpticFilm 7400" +:interface "USB" +:usbid "0x07b3" "0x0c3a" +:status :complete +:comment "900, 1800, 3600, 7200 or 600, 1200, 1800, 3600 and 7200 dpi resolutions are supported (depending on exact scanner model)" :model "OpticFilm 7500i" :interface "USB" :usbid "0x07b3" "0x0c13" -:status :basic +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported in both regular transparency and infrared modes" + +:model "OpticFilm 7600i" +:interface "USB" +:usbid "0x07b3" "0x0c3b" +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported in both regular transparency and infrared modes" + +:model "OpticFilm 8100" +:interface "USB" +:usbid "0x07b3" "0x130c" +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported" + +:model "OpticFilm 8200i" +:interface "USB" +:usbid "0x07b3" "0x130d" +:status :complete +:comment "900, 1800, 3600 and 7200 dpi resolutions are supported in both regular transparency and infrared modes" ; ----------------------------------------------------------------------------- @@ -152,43 +191,43 @@ :interface "USB" :usbid "0x04a9" "0x1904" :status :complete -:comment "GL847 based, resolution from 75 to 2400 dpi" +:comment "resolutions from 75 to 2400 dpi are supported" :model "CanoScan LiDE 110" :interface "USB" :usbid "0x04a9" "0x1909" :status :complete -:comment "GL124 based, resolution from 75 to 2400 dpi" +:comment "resolutions from 75 to 2400 dpi are supported" :model "CanoScan LiDE 120" :interface "USB" :usbid "0x04a9" "0x190e" :status :complete -:comment "GL124+ based, resolution from 75 to 2400 dpi" +:comment "resolutions from 75 to 2400 dpi are supported" :model "CanoScan LiDE 200" :interface "USB" :usbid "0x04a9" "0x1905" :status :complete -:comment "GL847 based, resolution from 75 to 4800 dpi" +:comment "resolutions from 75 to 4800 dpi are supported" :model "CanoScan LiDE 210" :interface "USB" :usbid "0x04a9" "0x190a" :status :complete -:comment "GL124 based, resolution from 75 to 4800 dpi" +:comment "resolutions from 75 to 4800 dpi are supported" :model "CanoScan LiDE 220" :interface "USB" :usbid "0x04a9" "0x190f" :status :complete -:comment "GL124+ based, resolution from 75 to 4800 dpi" +:comment "resolutions from 75 to 4800 dpi are supported" :model "CanoScan 4400F" :interface "USB" :usbid "0x04a9" "0x2228" -:status :basic -:comment "GL843 based" +:status :complete +:comment "300, 600, 1200 dpi resolutions are supported in flatbed mode; 1200, 2400, 4800 dpi resolutions are supported in transparency scanning mode" :model "CanoScan 5600F" :interface "USB" @@ -200,27 +239,27 @@ :url "unsupported/canon-8400f.html" :interface "USB" :usbid "0x04a9" "0x221e" -:status :basic -:comment "GL841 based, to be added to genesys backend" +:status :complete +:comment "400, 800, 1600, 3200 dpi resolutions are supported in flatbed, transparency and infrared transparency scanning modes" :model "CanoScan 8600F" :url "unsupported/canon-8600.html" :interface "USB" :usbid "0x04a9" "0x2229" -:status :basic -:comment "normal and transparency scans work up to 1200 dpi resolution" +:status :complete +:comment "300, 600, 1200 dpi resolutions supported in flatbed scanning mode; 300, 600, 1200, 2400, 4800 dpi resolutions are supported in transparency and infrared transparency scanning modes" :model "CanoScan 700F" :interface "USB" :usbid "0x04a9" "0x1907" -:status :good -:comment "GL847 based, resolution from 75 to 4800 dpi" +:status :basic +:comment "GL847 based, resolutions from 75 to 1200 dpi are supported" :model "Canon Image Formula 101" :interface "USB" :usbid "0x1083" "0x162e" :status :unsupported -:comment "GL846 based, work in progress" +:comment "GL846 based" ; ----------------------------------------------------------------------------- diff --git a/doc/descriptions/pixma.desc b/doc/descriptions/pixma.desc index 436689120..0ed1769a9 100644 --- a/doc/descriptions/pixma.desc +++ b/doc/descriptions/pixma.desc @@ -11,7 +11,7 @@ ; See doc/descriptions.txt for details. :backend "pixma" ; name of backend -:version "0.27.0" ; version of backend (or "unmaintained") +:version "0.27.7" ; version of backend (or "unmaintained") :manpage "sane-pixma" ; name of manpage (if it exists) ;:comment "Devices marked as experimantal are disabled by default. See the manual page for how to enable them." @@ -154,6 +154,18 @@ :status :untested :comment "Testers needed!" +:model "PIXMA G7000 Series" +:interface "USB Ethernet WiFi" +:usbid "0x04a9" "0x1863" +:status :untested +:comment "Testers needed!" + +:model "PIXMA GM4000 Series" +:interface "USB Ethernet WiFi" +:usbid "0x04a9" "0x1869" +:status :untested +:comment "Testers needed!" + :model "PIXMA MG2100 Series" :interface "USB" :usbid "0x04a9" "0x1751" @@ -902,8 +914,8 @@ :model "PIXMA TS3300 Series" :interface "USB WiFi" :usbid "0x04a9" "0x18a2" -:status :untested -:comment "Testers needed!" +:status :good +:comment "All resolutions supported (up to 1200DPI). WiFi not working." :model "PIXMA TS5000 Series" :interface "USB WiFi" @@ -1199,6 +1211,12 @@ :status :untested :comment "Testers needed!" +:model "i-SENSYS MF440 Series" +:interface "USB Ethernet WiFi" +:usbid "0x04a9" "0x2823" +:status :complete +:comment "Flatbed and ADF scan. All resolutions supported (up to 600DPI, ADF up to 300DPI)." + :model "i-SENSYS MF510 Series" :interface "USB Ethernet WiFi" :usbid "0x04a9" "0x27c2" @@ -1241,6 +1259,12 @@ :status :complete :comment "Flatbed and ADF scan. All resolutions supported (up to 600DPI)." +:model "i-SENSYS MF720 Series" +:interface "USB Ethernet WiFi" +:usbid "0x04a9" "0x27b5" +:status :untested +:comment "Testers needed!" + :model "i-SENSYS MF730 Series" :interface "USB Ethernet WiFi" :usbid "0x04a9" "0x27e4" @@ -1407,7 +1431,7 @@ :interface "USB Ethernet" :usbid "0x04a9" "0x2774" :status :good -:comment "Flatbed scan. All resolutions supported (up to 600DPI). ADF buggy." +:comment "Flatbed scan. All resolutions supported (up to 600DPI)." :model "i-SENSYS MF4800 Series" :interface "USB Ethernet" @@ -1568,8 +1592,8 @@ :model "MAXIFY MB5100 Series" :interface "USB Ethernet WiFi" :usbid "0x04a9" "0x1790" -:status :untested -:comment "Testers needed!" +:status :complete +:comment "Flatbed and ADF scan. All resolutions supported (up to 1200DPI)." :model "MAXIFY MB5300 Series" :interface "USB Ethernet" diff --git a/doc/descriptions/unsupported.desc b/doc/descriptions/unsupported.desc index e06268221..aa392c54f 100644 --- a/doc/descriptions/unsupported.desc +++ b/doc/descriptions/unsupported.desc @@ -395,13 +395,6 @@ :status :unsupported :comment "Not supported. However, a stand-alone program for FreeBSD is available." -:model "CanoScan LiDE 70" -:url "http://www.juergen-ernst.de/info_sane.html" -:interface "USB" -:usbid "0x04a9" "0x2225" -:status :unsupported -:comment "Philips chip. Backend started, see link" - :model "CanoScan LiDE 90" :url "unsupported/canon-lide-90.html" :interface "USB" @@ -1745,13 +1738,6 @@ :url "http://www.plustek.de/" :url "http://www.plustek.com/" -:model "OpticFilm 7200" -:url "unsupported/plustek-opticfilm-7200.html" -:interface "USB" -:usbid "0x07b3" "0x0807" -:status :unsupported -:comment "GL842 based, maybe to be added to genesys backend" - :model "OpticPro A3U" :interface "USB" :status :unsupported diff --git a/doc/descriptions/xerox_mfp.desc b/doc/descriptions/xerox_mfp.desc index 46c2867f4..b259db96d 100644 --- a/doc/descriptions/xerox_mfp.desc +++ b/doc/descriptions/xerox_mfp.desc @@ -38,6 +38,11 @@ :usbid "0x0924" "0x4294" :status :good +:model "WorkCentre 3225" +:interface "USB" +:usbid "0x0924" "0x42dc" +:status :good + :mfg "Dell" :url "http://www.dell.com/" diff --git a/doc/figs/area.fig b/doc/figs/area.fig deleted file mode 100644 index d0e62e458..000000000 --- a/doc/figs/area.fig +++ /dev/null @@ -1,36 +0,0 @@ -#FIG 3.1 -Portrait -Center -Inches -1200 2 -6 1650 1650 1800 1800 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 1725 1650 1725 1800 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 1650 1725 1800 1725 --6 -6 3300 2700 3450 2850 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 3375 2700 3375 2850 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 3300 2775 3450 2775 --6 -6 1725 1725 3375 2775 -2 2 0 0 7 7 10 0 18 0.000 0 0 -1 0 0 5 - 1725 1725 3375 1725 3375 2775 1725 2775 1725 1725 -4 1 -1 10 0 16 12 0.0000 4 105 840 2550 2302 scan area\001 --6 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 1200 675 4275 675 4275 3375 1200 3375 1200 675 -2 1 0 1 -1 7 10 0 16 0.000 0 0 -1 1 0 2 - 2 1 1.00 60.00 120.00 - 1200 525 1200 3825 -2 1 0 1 -1 7 10 0 16 0.000 0 0 -1 1 0 2 - 2 1 1.00 60.00 120.00 - 1050 675 4650 675 -4 1 -1 10 0 16 12 0.0000 4 180 1020 3375 3150 bottom-right\001 -4 1 -1 10 0 16 12 0.0000 4 180 615 1725 1500 top-left\001 -4 1 -1 10 0 16 12 0.0000 4 135 1080 2700 1050 scan surface\001 -4 1 -1 10 0 16 12 0.0000 4 150 105 1050 3600 y\001 -4 1 -1 10 0 16 12 0.0000 4 105 90 4425 525 x\001 -4 1 -1 10 0 16 12 0.0000 4 135 105 1080 585 0\001 diff --git a/doc/figs/flow.fig b/doc/figs/flow.fig deleted file mode 100644 index 3abcc0b70..000000000 --- a/doc/figs/flow.fig +++ /dev/null @@ -1,40 +0,0 @@ -#FIG 3.1 -Portrait -Center -Inches -1200 2 -6 4200 7305 9945 7575 -4 0 -1 0 0 17 18 0.0000 4 270 1500 4200 7515 - go back to\001 -4 0 -1 0 0 16 18 0.0000 4 270 1440 5775 7515 sane_start()\001 -4 0 -1 0 0 17 18 0.0000 4 210 2670 7275 7515 if more frames desired\001 --6 -2 2 0 1 -1 7 10 0 19 0.000 0 0 -1 0 0 5 - 2700 600 10200 600 10200 9600 2700 9600 2700 600 -2 2 0 1 -1 7 8 0 18 0.000 0 0 -1 0 0 5 - 3300 2400 10200 2400 10200 8925 3300 8925 3300 2400 -2 2 0 1 -1 7 8 0 17 0.000 0 0 -1 0 0 5 - 3900 2925 10200 2925 10200 4650 3900 4650 3900 2925 -2 2 0 1 -1 7 8 0 17 0.000 0 0 -1 0 0 5 - 3900 4800 10200 4800 10200 8250 3900 8250 3900 4800 -2 1 0 1 -1 7 8 0 -1 0.000 0 0 -1 0 0 4 - 10350 3000 10425 3075 10425 4500 10350 4575 -2 1 0 1 -1 7 8 0 -1 0.000 0 0 -1 0 0 4 - 10350 4875 10425 4950 10425 8100 10350 8175 -4 0 -1 0 0 17 18 0.0000 4 150 735 4200 3300 - use:\001 -4 0 -1 0 0 16 18 0.0000 4 270 1680 4200 5100 - sane_start()\001 -4 0 -1 0 0 17 18 0.0000 4 270 4950 4500 4500 repeatedly to configure device as desired\001 -4 0 -1 0 0 16 18 0.0000 4 270 2715 5400 4080 sane_control_option()\001 -4 0 -1 0 0 16 18 0.0000 4 270 3660 5400 3600 sane_get_option_descriptor()\001 -4 0 -1 0 0 17 18 0.0000 4 150 735 4200 5700 - use:\001 -4 0 -1 0 0 17 18 0.0000 4 270 4080 4500 6900 repeatedly until read returns EOF\001 -4 0 -1 0 0 16 18 0.0000 4 270 2805 5400 6000 sane_get_parameters()\001 -4 0 -1 0 0 16 18 0.0000 4 270 1440 5400 6450 sane_read()\001 -4 0 -1 0 0 16 18 0.0000 4 270 1935 4200 8100 - sane_cancel()\001 -4 0 -1 0 0 16 18 0.0000 4 270 1500 3000 1200 - sane_init()\001 -4 0 -1 0 0 16 18 0.0000 4 270 1590 3000 9300 - sane_exit()\001 -4 0 -1 0 0 17 18 0.0000 4 270 4845 3600 1800 - pick desired device, possibly by using\001 -4 0 -1 0 0 16 18 0.0000 4 270 1770 3600 2700 - sane_open()\001 -4 0 -1 0 0 16 18 0.0000 4 270 1800 3600 8700 - sane_close()\001 -4 0 -1 0 0 16 18 0.0000 4 270 2415 4800 2175 sane_get_devices()\001 -4 0 -1 8 0 17 18 0.0000 4 270 2070 10575 6600 image acquisition\001 -4 0 -1 8 0 17 18 0.0000 4 270 1500 10575 3825 device setup\001 diff --git a/doc/figs/hierarchy.fig b/doc/figs/hierarchy.fig deleted file mode 100644 index 5545b8d95..000000000 --- a/doc/figs/hierarchy.fig +++ /dev/null @@ -1,79 +0,0 @@ -#FIG 3.1 -Landscape -Center -Inches -1200 2 -6 10500 4500 12300 5400 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 10650 4785 12150 4785 12150 5385 10650 5385 10650 4785 -4 1 -1 0 0 16 18 0.0000 4 210 660 11399 5182 qcam\001 --6 -6 7200 4500 9000 5400 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 7350 4785 8850 4785 8850 5385 7350 5385 7350 4785 -4 1 -1 0 0 16 18 0.0000 4 270 315 8099 5182 hp\001 --6 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 2250 1185 3750 1185 3750 1785 2250 1785 2250 1185 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 450 2985 1950 2985 1950 3585 450 3585 450 2985 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 2250 2985 3750 2985 3750 3585 2250 3585 2250 2985 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 4050 2985 5550 2985 5550 3585 4050 3585 4050 2985 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 8850 1185 10350 1185 10350 1785 8850 1785 8850 1185 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 2700 1800 1200 3000 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 3000 1800 3000 3000 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 3300 1800 4800 3000 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 9600 1800 9600 2100 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 9450 2700 8100 4800 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 11400 4200 11400 4800 -2 2 0 0 0 0 10 0 2 0.000 0 0 -1 0 0 5 - 5700 3825 300 3825 300 300 5700 300 5700 3825 -2 2 0 0 0 0 10 0 2 0.000 0 0 -1 0 0 5 - 12300 5550 7200 5550 7200 300 12300 300 12300 5550 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 1200 3600 1200 4200 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 3000 3600 3000 4125 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 7875 5400 7350 5850 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 8250 5400 8775 5850 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 11475 5400 11475 5850 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 8850 2100 10350 2100 10350 2700 8850 2700 8850 2100 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 10650 3600 12150 3600 12150 4200 10650 4200 10650 3600 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 - 9750 2700 11400 3600 -3 2 0 1 -1 7 0 0 -1 0.000 0 0 0 7 - 4800 3600 4275 4500 5025 5475 6150 4575 6525 1350 9450 900 - 9600 1200 - 0.00 0.00 4390.23 4024.37 4258.98 4249.38 4300.21 4894.51 - 4554.60 5418.20 5575.94 5541.53 5962.09 4914.18 6573.46 3810.63 - 5758.15 2145.68 7223.99 624.74 8573.68 446.85 9524.49 938.52 - 9561.99 1013.52 0.00 0.00 -4 1 -1 0 0 16 18 0.0000 4 210 525 1199 3382 pnm\001 -4 1 -1 0 0 16 18 0.0000 4 210 870 2999 3382 mustek\001 -4 1 -1 0 0 17 14 0.0000 4 210 855 1200 4425 pnm files\001 -4 1 -1 0 0 17 14 0.0000 4 120 765 3000 4380 scanner\001 -4 1 -1 0 0 17 14 0.0000 4 150 945 7350 6165 scanner 1\001 -4 1 -1 0 0 17 14 0.0000 4 150 945 8925 6165 scanner 2\001 -4 1 -1 0 0 17 14 0.0000 4 165 1290 11475 6135 video camera\001 -4 1 -1 0 0 17 14 0.0000 4 165 1035 3000 600 machine A\001 -4 1 -1 0 0 17 14 0.0000 4 165 1020 9600 630 machine B\001 -4 1 -1 0 0 17 14 0.0000 4 165 1860 4725 5850 network connection\001 -4 1 -1 0 0 16 18 0.0000 4 210 285 2999 1582 dll\001 -4 1 -1 0 0 16 18 0.0000 4 195 390 4799 3382 net\001 -4 1 -1 0 0 16 18 0.0000 4 210 735 9599 1582 saned\001 -4 1 -1 0 0 16 18 0.0000 4 210 285 9599 2482 dll\001 -4 1 -1 0 0 16 18 0.0000 4 210 960 11399 3982 autolum\001 diff --git a/doc/figs/image-data.fig b/doc/figs/image-data.fig deleted file mode 100644 index d52a90eee..000000000 --- a/doc/figs/image-data.fig +++ /dev/null @@ -1,63 +0,0 @@ -#FIG 3.1 -Portrait -Center -Inches -1200 2 -6 1725 450 5925 1650 -6 1800 975 3150 1350 -2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 1800 1200 3150 1200 3150 1350 1800 1350 1800 1200 -4 0 -1 0 0 16 12 0.0000 4 135 1260 1875 1125 7 6 5 4 3 2 1 0\001 --6 -6 3150 975 4500 1350 -2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 3150 1200 4500 1200 4500 1350 3150 1350 3150 1200 -4 0 -1 0 0 16 12 0.0000 4 135 1260 3225 1125 7 6 5 4 3 2 1 0\001 --6 -6 4500 975 5850 1350 -2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 4500 1200 5850 1200 5850 1350 4500 1350 4500 1200 -4 0 -1 0 0 16 12 0.0000 4 135 1260 4575 1125 7 6 5 4 3 2 1 0\001 --6 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 1800 1200 5850 1200 5850 1350 1800 1350 1800 1200 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 7 - 1800 900 1950 825 3750 825 3825 750 3900 825 5700 825 - 5850 900 -4 0 -1 0 0 16 12 0.0000 4 105 60 2475 1575 r\001 -4 1 -1 0 0 16 12 0.0000 4 150 105 3825 1575 g\001 -4 1 -1 0 0 16 12 0.0000 4 135 105 5175 1575 b\001 -4 1 -1 0 0 16 12 0.0000 4 180 555 3825 600 pixel 0\001 --6 -6 5850 975 7200 1350 -2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 5850 1200 7200 1200 7200 1350 5850 1350 5850 1200 -4 0 -1 0 0 16 12 0.0000 4 135 1260 5925 1125 7 6 5 4 3 2 1 0\001 --6 -6 7200 975 8550 1350 -2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 7200 1200 8550 1200 8550 1350 7200 1350 7200 1200 -4 0 -1 0 0 16 12 0.0000 4 135 1260 7275 1125 7 6 5 4 3 2 1 0\001 --6 -6 8550 975 9900 1350 -2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 8550 1200 9900 1200 9900 1350 8550 1350 8550 1200 -4 0 -1 0 0 16 12 0.0000 4 135 1260 8625 1125 7 6 5 4 3 2 1 0\001 --6 -2 2 0 2 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 5850 1200 9900 1200 9900 1350 5850 1350 5850 1200 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 7 - 5850 900 6000 825 7800 825 7875 750 7950 825 9750 825 - 9900 900 -4 0 -1 0 0 16 12 0.0000 4 105 60 6525 1575 r\001 -4 1 -1 0 0 16 12 0.0000 4 150 105 7875 1575 g\001 -4 1 -1 0 0 16 12 0.0000 4 135 105 9225 1575 b\001 -4 1 -1 0 0 16 12 0.0000 4 180 555 7875 600 pixel 1\001 -4 1 -1 0 0 16 12 0.0000 4 180 525 9225 1950 byte 5\001 -4 1 -1 0 0 16 12 0.0000 4 180 525 7875 1950 byte 4\001 -4 1 -1 0 0 16 12 0.0000 4 180 525 6525 1950 byte 3\001 -4 1 -1 0 0 16 12 0.0000 4 180 525 5175 1950 byte 2\001 -4 1 -1 0 0 16 12 0.0000 4 180 465 3825 1950 byte1\001 -4 1 -1 0 0 16 12 0.0000 4 180 465 2475 1950 byte0\001 -4 1 -1 0 0 16 12 0.0000 4 15 180 10050 1275 ....\001 -4 2 -1 0 0 16 12 0.0000 4 135 240 1725 1125 bit:\001 diff --git a/doc/figs/xfer.fig b/doc/figs/xfer.fig deleted file mode 100644 index c4d8921d9..000000000 --- a/doc/figs/xfer.fig +++ /dev/null @@ -1,32 +0,0 @@ -#FIG 3.1 -Portrait -Center -Inches -1200 2 -6 2325 3150 8175 3750 -2 1 0 2 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 - 2 1 2.00 120.00 240.00 - 2400 3300 8100 3300 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 - 2 1 2.00 120.00 240.00 - 8100 3375 2400 3675 --6 -6 2325 3600 8175 4200 -2 1 0 2 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 - 2 1 2.00 120.00 240.00 - 2400 3750 8100 3750 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 - 2 1 2.00 120.00 240.00 - 8100 3825 2400 4125 --6 -2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 - 2250 3150 8250 3150 8250 6150 2250 6150 2250 3150 -2 1 0 2 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 - 2 1 2.00 120.00 240.00 - 2400 4200 8100 4200 -2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 - 2 1 2.00 120.00 240.00 - 8100 4275 2400 4575 -2 1 0 2 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 - 2 1 2.00 120.00 240.00 - 2400 4650 8100 4650 diff --git a/doc/icons/contents.gif b/doc/icons/contents.gif deleted file mode 100644 index 7b3c904b2..000000000 Binary files a/doc/icons/contents.gif and /dev/null differ diff --git a/doc/icons/index.gif b/doc/icons/index.gif deleted file mode 100644 index b9b3108aa..000000000 Binary files a/doc/icons/index.gif and /dev/null differ diff --git a/doc/icons/next.gif b/doc/icons/next.gif deleted file mode 100644 index 7a2dbe955..000000000 Binary files a/doc/icons/next.gif and /dev/null differ diff --git a/doc/icons/next_gr.gif b/doc/icons/next_gr.gif deleted file mode 100644 index 1416b1c10..000000000 Binary files a/doc/icons/next_gr.gif and /dev/null differ diff --git a/doc/icons/previous.gif b/doc/icons/previous.gif deleted file mode 100644 index aef90f1fc..000000000 Binary files a/doc/icons/previous.gif and /dev/null differ diff --git a/doc/icons/previous_gr.gif b/doc/icons/previous_gr.gif deleted file mode 100644 index c6acaabc9..000000000 Binary files a/doc/icons/previous_gr.gif and /dev/null differ diff --git a/doc/icons/references.gif b/doc/icons/references.gif deleted file mode 100644 index 68c6331fe..000000000 Binary files a/doc/icons/references.gif and /dev/null differ diff --git a/doc/icons/references_gr.gif b/doc/icons/references_gr.gif deleted file mode 100644 index 682548c06..000000000 Binary files a/doc/icons/references_gr.gif and /dev/null differ diff --git a/doc/icons/up.gif b/doc/icons/up.gif deleted file mode 100644 index 3d1aebf32..000000000 Binary files a/doc/icons/up.gif and /dev/null differ diff --git a/doc/icons/up_gr.gif b/doc/icons/up_gr.gif deleted file mode 100644 index a8b463aef..000000000 Binary files a/doc/icons/up_gr.gif and /dev/null differ diff --git a/doc/net.tex b/doc/net.tex deleted file mode 100644 index d6bc110d5..000000000 --- a/doc/net.tex +++ /dev/null @@ -1,479 +0,0 @@ -\chapter{Network Protocol}\label{chap:net} - -The SANE interface has been designed to facilitate network access to -image acquisition devices. In particular, most SANE implementations -are expected to support a network backend (net client) and a -corresponding network daemon (net server) that allows accessing image -acquisition devices through a network connection. Network access is -useful in several situations: -\begin{itemize} - -\item To provide controlled access to resources that are inaccessible - to a regular user. For example, a user may want to access a device - on a host where she has no account on. With the network protocol, - it is possible to allow certain users to access scanners without - giving them full access to the system. - - Controlling access through the network daemon can be useful even in - the local case: for example, certain backends may require root - privileges to access a device. Rather than installing each frontend - as setuid-root, a system administrator could instead install the - SANE network daemon as setuid-root. This enables regular users to - access the privileged device through the SANE daemon (which, - presumably, supports a more fine-grained access control mechanism - than the simple setuid approach). This has the added benefit that - the system administrator only needs to trust the SANE daemon, not - each and every frontend that may need access to the privileged - device. - -\item Network access provides a sense of ubiquity of the available - image acquisition devices. For example, in a local area network - environment, this allows a user to log onto any machine and have - convenient access to any resource available to any machine on the - network (subject to permission constraints). - -\item For devices that do not require physical access when used (e.g., - video cameras), network access allows a user to control and use - these devices without being in physical proximity. Indeed, if such - devices are connected to the Internet, access from any place in the - world is possible. - -\end{itemize} - -The network protocol described in this chapter has been design with -the following goals in mind: -\begin{enumerate} - -\item Image transmission should be efficient (have low encoding - overhead). - -\item Accessing option descriptors on the client side must be - efficient (since this is a very common operation). - -\item Other operations, such as setting or inquiring the value of an - option are less performance critical since they typically require - explicit user action. - -\item The network protocol should be simple and easy to implement on - any host architecture and any programming language. - -\end{enumerate} -The SANE protocol can be run across any transport protocol that -provides reliable data delivery. While SANE does not specify a -specific transport protocol, it is expected that TCP/IP will be among -the most commonly used protocols. - -\section{Data Type Encoding} - -\subsection{Primitive Data Types} - -The four primitive types of the SANE standard are encoded as follows: -\begin{description} - -\item[\code{\defn{SANE\_Byte}}:] A byte is encoded as an 8 bit value. - Since the transport protocol is assumed to be byte-orientd, the bit - order is irrelevant. - -\item[\code{\defn{SANE\_Word}}:] A word is encoded as 4 bytes (32 - bits). The bytes are ordered from most-significant to - least-significant byte (big-endian byte-order). - -\item[\code{\defn{SANE\_Char}}:] A character is currently encoded as an 8-bit - ISO LATIN-1 value. An extension to support wider character sets (16 or 32 - bits) is planned for the future, but not supported at this point. - -\item[\code{\defn{SANE\_String}}:] A string pointer is encoded as a - \code{SANE\_Char} array. The trailing NUL byte is considered part - of the array and a \code{NULL} pointer is encoded as a zero-length - array. - -\item[\code{\defn{SANE\_Handle}}:] A handle is encoded like a word. - The network backend needs to take care of converting these integer - values to the opaque pointer values that are presented to the user - of the network backend. Similarly, the SANE daemon needs to take - care of converting the opaque pointer values it receives from its - backends into 32-bit integers suitable for use for network encoding. - -\item[{\em\defn{enumeration types}}:] Enumeration types are encoded - like words. - -\end{description} - -\subsection{Type Constructors} - -Closely following the type constructors of the C language, the SANE network -protocol supports the following four constructors: -\begin{description} - -\item[{\em\defn{pointer}}:] A pointer is encoded by a word that indicates - whether the pointer is a NULL-pointer which is then followed by the - value that the pointer points to (in the case of a non-NULL pointer; - in the case of a NULL pointer, no bytes are encoded for the pointer - value). - -\item[{\em\defn{array}}:] An array is encoded by a word that indicates - the length of the array followed by the values of the elements in - the array. The length may be zero in which case no bytes are - encoded for the element values. - -\item[{\em\defn{structure}}:] A structure is encoded by simply encoding the - structure members in the order in which they appear in the - corresponding C type declaration. - -\item[{\em\defn{union}}:] A union must always be accompanied by a tag - value that indicates which of the union members is the currently the - active one. For this reason, the union itself is encoded simply by - encoding the value of the currently active member. - -\end{description} - -Note that for type constructors, the pointer, element, or member -values themselves may have a constructed type. Thus, the above rules -should be applied recursively until a sequence of primitive types has -been found. - -Also SANE had no need for encoding of circular structures. This -greatly simplifies the network protocol. - -\section{Remote Procedure Call Requests} - -The SANE network protocol is a client/server-style remote procedure -call (RPC) protocol. This means that all activity is initiated by the -client side (the network backend)---a server is restricted to -answering requests sent by the client. - -The data transferred from the client to the server is comprised of the RPC code -(as a \code{SANE\_WORD}), followed by arguments defined in the {\bf request} -column below. The format of the server's answer is given in the {\bf reply} -column. - -\subsection{\code{\defn{SANE\_NET\_INIT}}} - -RPC Code: 0 - -This RPC establishes a connection to a particular SANE network daemon. -It must be the first call in a SANE network session. The parameter -and reply arguments for this call are shown in the table below: -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_Word version\_code} & \code{SANE\_Word status} \\ - \code{SANE\_String user\_name} & \code{SANE\_Word version\_code} \\ -\end{tabular} -\end{center} -The \code{version\_code} argument in the request is the SANE -version-code of the network backend that is contacting the network -daemon (see Section~\ref{sec:saneversioncode}). The -``build-revision'' in the version code is used to hold the network -protocol version. The SANE network daemon receiving such a request -must make sure that the network protocol version corresponds to a -supported version since otherwise the encoding of the network stream -may be incompatible (even though the SANE interface itself may be -compatible). The \code{user\_name} argument is the name of the user -on whose behalf this call is being performed. If the network backend -cannot determine a user-name, it passes a \code{NULL} pointer for this -argument. No trust should be placed in the authenticity of this -user-name. The intent of this string is to provide more convenience -to the user. E.g., it could be used as the default-user name in -subsequent authentication calls. - -In the reply, \code{status} indicates the completion status. If the -value is anything other than \code{SANE\_STA\-TUS\_GOOD}, the -remainder of the reply has undefined values.\footnote{The sane network - daemon should be careful not to leak information in the undefined - portion of the reply.} The \code{version\_code} argument returns the -SANE version-code that the network daemon supports. See the comments -in the previous paragraph on the meaning of the build-revision in this -version code. - -\subsection{\code{\defn{SANE\_NET\_GET\_DEVICES}}} - -RPC Code: 1 - -This RPC is used to obtain the list of devices accessible by the SANE -daemon. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{void} & \code{SANE\_Word status} \\ - & \code{SANE\_Device ***device\_list} \\ -\end{tabular} -\end{center} -There are no arguments in the request for this call. - -In the reply, \code{status} indicates the completion status. If the -value is anything other than \code{SANE\_STA\-TUS\_GOOD}, the -remainder of the reply has undefined values. The \code{device\_list} -argument is a pointer to a \code{NULL}-terminated array of -\code{SANE\_Device} pointers. - -\subsection{\code{\defn{SANE\_NET\_OPEN}}} - -RPC Code: 2 - -This RPC is used to open a connection to a remote SANE device. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_String device\_name} & \code{SANE\_Word status} \\ - & \code{SANE\_Word handle} \\ - & \code{SANE\_String resource} \\ -\end{tabular} -\end{center} -The \code{device\_name} argument specifies the name of the device to -open. - -In the reply, \code{status} indicates the completion status. If the -value is anything other than \code{SANE\_STA\-TUS\_GOOD}, the -remainder of the reply has undefined values. The \code{handle} -argument specifies the device handle that uniquely identifies the -connection. The \code{resource} argument is used to request -authentication. If it has a non-\code{NULL} value, the network -backend should authenticate the specified resource and then retry this -operation (see Section~\ref{sec:authorization} for details on how to -authorize a resource). - -\subsection{\code{\defn{SANE\_NET\_CLOSE}}} - -RPC Code: 3 - -This RPC is used to close a connection to a remote SANE device. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_Word handle} & \code{SANE\_Word dummy} \\ -\end{tabular} -\end{center} -The \code{handle} argument identifies the connection that should be -closed. - -In the reply, the \code{dummy} argument is unused. Its purpose is to -ensure proper synchronization (without it, a net client would not be -able to determine when the RPC has completed). - -\subsection{\code{\defn{SANE\_NET\_GET\_OPTION\_DESCRIPTORS}}} - -RPC Code: 4 - -This RPC is used to obtain {\em all\/} the option descriptors for a -remote SANE device. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_Word handle} & \code{Option\_Descriptor\_Array odesc} \\ -\end{tabular} -\end{center} -The \code{handle} argument identifies the remote device whose option -descriptors should be obtained. - -In the reply, the \code{odesc} argument is used to return the array of -option descriptors. The option descriptor array has the following -structure: -\begin{quote}\index{Option\_Descriptor\_Array} -\begin{verbatim} -struct Option_Descriptor_Array - { - SANE_Word num_options; - SANE_Option_Descriptor **desc; - }; -\end{verbatim} -\end{quote} - - -\subsection{\code{\defn{SANE\_NET\_CONTROL\_OPTION}}} - -RPC Code: 5 - -This RPC is used to control (inquire, set, or set to automatic) a -specific option of a remote SANE device. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_Word handle} & \code{SANE\_Status status} \\ - \code{SANE\_Word option} & \code{SANE\_Word info} \\ - \code{SANE\_Word action} & \code{SANE\_Word value\_type} \\ - \code{SANE\_Word value\_type} & \code{SANE\_Word value\_size} \\ - \code{SANE\_Word value\_size} & \code{void *value} \\ - \code{void *value} & \code{SANE\_String *resource} \\ -\end{tabular} -\end{center} -The \code{handle} argument identifies the remote device whose option -should be controlled. Argument \code{option} is the number (index) of -the option that should be controlled. Argument \code{action} -specifies what action should be taken (get, set, or set automatic). -Argument \code{value\_type} specifies the type of the option value -(must be one of \code{SANE\_TYPE\_BOOL}, \code{SANE\_TYPE\_INT}, -\code{SANE\_TYPE\_FIXED}, \code{SANE\_TYPE\_STR\-ING}, -\code{SANE\_TYPE\_BUTTON}). Argument \code{value\_size} specifies -the size of the option value in number of bytes (see -Section~\ref{sec:valuesize} for the precise meaning of this value). -Finally, argument \code{value} is a pointer to the option value. It -must be a writeable area that is at least \code{value\_size} bytes -large. (Note that this area must be writable even if the action is to -set the option value. This is because the backend may not be able to -set the exact option value, in which case the option value is used to -return the next best value that the backend has chosen.) - -In the reply, argument \code{resource} is set to the name of the -resource that must be authorized before this call can be retried. If -this value is non-\code{NULL}, all other arguments have undefined -values (see Section~\ref{sec:authorization} for details on how to -authorize a resource). Argument \code{status} indicates the -completion status. If the value is anything other than -\code{SANE\_STA\-TUS\_GOOD}, the remainder of the reply has undefined -values. The \code{info} argument returns the information on how well -the backend was able to satisfy the request. For details, see the -description of the corresponding argument in -Section~\ref{sec:control}. Arguments \code{value\_type} and -\code{value\_size} have the same values as the arguments by the same -name in corresponding request. The values are repeated here to ensure -that both the request and the reply are self-contained (i.e., they can -be encoded and decoded independently). Argument \code{value} is holds -the value of the option that has become effective as a result of this -RPC. - - -\subsection{\code{\defn{SANE\_NET\_GET\_PARAMETERS}}} - -RPC Code: 6 - -This RPC is used to obtain the scan parameters of a remote SANE -device. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_Word handle} & \code{SANE\_Status status} \\ - & \code{SANE\_Parameters params} \\ -\end{tabular} -\end{center} -The \code{handle} argument identifies the connection to the remote -device whose scan parameters should be returned. - -In the reply, \code{status} indicates the completion status. If the -value is anything other than \code{SANE\_STA\-TUS\_GOOD}, the -remainder of the reply has undefined values. The argument -\code{params} is used to return the scan parameters. - -\subsection{\code{\defn{SANE\_NET\_START}}} - -RPC Code: 7 - -This RPC is used to start image acquisition (scanning). -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_Word handle} & \code{SANE\_Status status} \\ - & \code{SANE\_Word port} \\ - & \code{SANE\_Word byte\_order} \\ - & \code{SANE\_String resource} \\ -\end{tabular} -\end{center} -The \code{handle} argument identifies the connection to the remote -device from which the image should be acquired. - -In the reply, argument \code{resource} is set to the name of the -resource that must be authorized before this call can be retried. If -this value is non-\code{NULL}, all other arguments have undefined -values (see Section~\ref{sec:authorization} for details on how to -authorize a resource). Argument, \code{status} indicates the -completion status. If the value is anything other than -\code{SANE\_STA\-TUS\_GOOD}, the remainder of the reply has -undefined values. The argument \code{port} returns the port number -from which the image data will be available. To read the image data, -a network client must connect to the remote host at the indicated port -number. Through this port, the image data is transmitted as a -sequence of data records. Each record starts with the data length in -bytes. The data length is transmitted as a sequence of four bytes. -These bytes should be interpreted as an unsigned integer in big-endian -format. The four length bytes are followed by the number of data -bytes indicated by the length. Except for byte-order, the data is in -the same format as defined for \code{sane\_read()}. Since some -records may contain no data at all, a length value of zero is -perfectly valid. The special length value of \code{0xffffffff} is -used to indicate the end of the data stream. That is, after receiving -a record length of \code{0xffffffff}, the network client should close -the data connection and stop reading data. - -Argument \code{byte\_order} specifies the byte-order of the image -data. A value of 0x1234 indicates little-endian format, a value of -0x4321 indicates big-endian format. All other values are presently -undefined and reserved for future enhancements of this protocol. The -intent is that a network server sends data in its own byte-order and -the client is responsible for adjusting the byte-order, if necessary. -This approach causes no unnecessary overheads in the case where the -server and client byte-order match and puts the extra burden on the -client side when there is a byte-order mismatch. Putting the burden -on the client-side improves the scalability properties of this -protocol. - -\subsection{\code{\defn{SANE\_NET\_CANCEL}}} - -RPC Code: 8 - -This RPC is used to cancel the current operation of a remote SANE -device. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_Word handle} & \code{SANE\_Word dummy} \\ -\end{tabular} -\end{center} -The \code{handle} argument identifies the connection whose operation -should be cancelled. - -In the reply, the \code{dummy} argument is unused. Its purpose is to -ensure proper synchronization (without it, a net client would not be -able to determine when the RPC has completed). - -\subsection{\code{\defn{SANE\_NET\_AUTHORIZE}}}\label{sec:authorization} -\index{network authorization} - -RPC Code: 9 - -This RPC is used to pass authorization data from the net client to the -net server. -\begin{center} -\begin{tabular}{ll} - {\bf request:} & {\bf reply:} \\ - \code{SANE\_String resource} & \code{SANE\_Word dummy} \\ - \code{SANE\_String username} & \\ - \code{SANE\_String password} & \\ -\end{tabular} -\end{center} -The \code{resource} argument specifies the name of the resource to be -authorized. This argument should be set to the string returned in the -\code{resource} argument of the RPC reply that required this -authorization call. The \code{username} and \code{password} are the -name of the user that is accessing the resource and the password for -the specified resource/user pair. - -Since the password is not encrypted during network transmission, it is -recommended to use the following extension: - -If the server adds the string `\code{\$MD5\$}' to the resource-name followed -by a random string not longer then 128 bytes, the client may answer with the -MD5 digest of the concatenation of the password and the random string. To -differentiate between the MD5 digest and a strange password the client prepends -the MD5 digest with the string `\code{\$MD5\$}'. - -In the reply, \code{dummy} is completely unused. Note that there is -no direct failure indication. This is unnecessary since a net client -will retry the RPC that resulted in the authorization request until -that call succeeds (or until the request is cancelled). The RPC that resulted -in the authorization request continues after the reply from the client and may -fail with \code{SANE\_STATUS\_ACCESS\_DENIED}. - - -\subsection{\code{\defn{SANE\_NET\_EXIT}}} - -RPC Code: 10 - -This RPC is used to disconnect a net client from a net server. There -are no request or reply arguments in this call. As a result of this -call, the connection between the client and the server that was -established by the \code{SANE\_NET\_INIT} call will be closed. - -% Local Variables: -% mode: latex -% TeX-master: "sane.tex" -% End: diff --git a/doc/sane-canon_lide70.man b/doc/sane-canon_lide70.man new file mode 100644 index 000000000..e03cf9411 --- /dev/null +++ b/doc/sane-canon_lide70.man @@ -0,0 +1,105 @@ +.TH sane\-canon_lide70 5 "26 Nov 2019" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" +.IX sane\-canon_lide70 +.SH NAME +sane\-canon_lide70 \- SANE backend for the Canon LiDE 70 USB flatbed scanner +.SH DESCRIPTION +The +.B canon_lide70 +library implements a SANE (Scanner Access Now Easy) backend that +provides access to the Canon Inc. CanoScan LiDE 70 flatbed scanner. +.PP +Due to Canon's unwillingness to provide scanner documentation, this +software was developed by analyzing the USB traffic of the Windows +XP driver. The precise meaning of the individual commands that are sent +to the scanner is known only to a very limited extent. Some sophistication +present in the Windows XP driver has been left out. There is, for example, +no active calibration. +.PP +TESTERS ARE WELCOME. Send your bug reports and comments to +the sane\-devel mailing list +.PP +The +.B Canoscan LiDE 600 +(or 600f, with film unit) is closely related to the LiDE 70, but +it probably will not work with this backend. The framework of this canon_lide70 +backend could likely be used to develop a backend for the LiDE 600 without too +much difficulty. +.PP +.SH CONFIGURATION +The +.I @CONFIGDIR@/canon_lide70.conf +file identifies the LiDE 70 by its vendor code 0x04a9 and its +product code 0x2225. For the LiDE 600(f) the product code would be 0x2224. +.PP +.SH BACKEND SPECIFIC OPTIONS +.PP +.B Scan Mode: +.TP +\-\-resolution 75|150|300|600|1200 [default 600] +.BR +Sets the resolution of the scanned image in dots per inch. Scanning at 1200 dpi is very slow. +.TP +\-\-mode Color|Gray|Lineart [default: Color] +.BR +Selects the scan mode. Lineart means fully black and fully white pixels only. +.TP +\-\-threshold 0..100 (in steps of 1) [default 75] +.BR +Select minimum-brightness percentage to get a white point, relevant only for Lineart +.TP +\-\-non-blocking[=(yes|no)] [inactive] +.BR +This option has not yet been implemented. Scans are captured in a temporary file with a typical size of 100MB. +.PP +.B Geometry: +.TP +\-l 0..216.069 [default 0] + Top-left x position of scan area in millimeters. +.TP +\-t 0..297 [default 0] + Top-left y position of scan area in millimeters. +.TP +\-x 0..216.069 [default 80] + Width of scan-area in millimeters. +.TP +\-y 0..297 [default 100] + Height of scan-area in millimeters. +.PP +.SH FILES +.TP +.I @CONFIGDIR@/canon_lide70.conf +The backend configuration file +.TP +.I @LIBDIR@/libsane\-canon_lide70.a +The static library implementing this backend. +.TP +.I @LIBDIR@/libsane\-canon_lide70.so +The shared library implementing this backend (present on systems that +support dynamic loading). +.SH ENVIRONMENT +.TP +.B SANE_DEBUG_CANON_LIDE70 +If the library was compiled with debug support enabled, this +environment variable controls the debug level for this backend. Higher +debug levels increase the verbosity of the output. + +Example: +.br +SANE_DEBUG_CANON_LIDE70=128 scanimage > /dev/null +.SH KNOWN PROBLEMS +At low resolutions (75 and 150 dpi, implying high slider speeds) +the scanner misses the top one millimeter of the scan area. This can +be remedied by shifting the document one millimeter downward, in cases +where such precision matters. Note that xsane uses the 75 dpi mode for +prescans. +.PP +It is recommended that in xsane the gamma value be set to approximately 2.0 +to get more realistic colors. This also wipes out some artifacts caused by +the lack of real calibration. +.SH "SEE ALSO" +sane(7), sane\-usb(5), sane\-find\-scanner(1), scanimage(1) +.br +http://www.juergen-ernst.de/info_sane.html +.br +.SH AUTHOR +pimvantend, building upon pioneering work by Juergen Ernst. diff --git a/doc/sane-fujitsu.man b/doc/sane-fujitsu.man index ccc3d896e..400a35eec 100644 --- a/doc/sane-fujitsu.man +++ b/doc/sane-fujitsu.man @@ -1,4 +1,4 @@ -.TH sane\-fujitsu 5 "08 Apr 2017" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" +.TH sane\-fujitsu 5 "07 Feb 2020" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" .IX sane\-fujitsu .SH NAME @@ -10,7 +10,7 @@ The library implements a SANE (Scanner Access Now Easy) backend which provides access to most Fujitsu flatbed and ADF scanners. -This document describes backend version 134, which shipped with SANE 1.0.28. +This document describes backend version 136, which shipped with SANE 1.0.30. .SH SUPPORTED HARDWARE This version supports every known model which speaks the Fujitsu SCSI and @@ -54,7 +54,7 @@ Effort has been made to expose all hardware options, including: source s .RS Selects the source for the scan. Options -may include "Flatbed", "ADF Front", "ADF Back", "ADF Duplex". +may include "Flatbed", "ADF Front", "ADF Back", "ADF Duplex", "Card Front", "Card Back", "Card Duplex". .RE .PP mode m diff --git a/doc/sane-genesys.man b/doc/sane-genesys.man index 65407542d..837a5f693 100644 --- a/doc/sane-genesys.man +++ b/doc/sane-genesys.man @@ -226,38 +226,15 @@ increase the verbosity of the output. If the debug level is set to 1 or higher, some debug options become available that are normally hidden. Handle them with care. This will print messages related to core genesys functions. .TP -.B SANE_DEBUG_GENESYS_LOW -This environment variable controls the debug level for low level functions -common to all genesys ASICs. -.TP -.B SANE_DEBUG_GENESYS_GL646 -This environment variable controls the debug level for the specific GL646 code -part. -.TP -.B SANE_DEBUG_GENESYS_GL841 -This environment variable controls the debug level for the specific GL841 code -part. -.TP -.B SANE_DEBUG_GENESYS_GL843 -This environment variable controls the debug level for the specific GL843 code -part. -.TP -.B SANE_DEBUG_GENESYS_GL847 -This environment variable controls the debug level for the specific GL847 code -part. -.TP -.B SANE_DEBUG_GENESYS_GL124 -This environment variable controls the debug level for the specific GL124 code -part. +.B SANE_DEBUG_GENESYS_IMAGE +If the library was compiled with debug support enabled, this environment +variable enables logging of intermediate image data. To enable this mode, +set the environmental variable to 1. Example (full and highly verbose output for gl646): .br export SANE_DEBUG_GENESYS=255 -.br -export SANE_DEBUG_GENESYS_LOW=255 -.br -export SANE_DEBUG_GENESYS_GL646=255 .SH CREDITS diff --git a/doc/sane-pixma.man b/doc/sane-pixma.man index ea85ec566..d6a0545ad 100644 --- a/doc/sane-pixma.man +++ b/doc/sane-pixma.man @@ -1,4 +1,4 @@ -.TH "sane\-pixma" "5" "28 Dec 2019" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" +.TH "sane\-pixma" "5" "25 Mar 2020" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" .IX sane\-pixma .SH NAME sane\-pixma \- SANE backend for Canon Multi-Function Printers and CanoScan Scanners @@ -51,7 +51,7 @@ PIXMA MX410, MX420, MX470, MX510, MX520, MX530, MX700, MX720 .br PIXMA MX850, MX860, MX870, MX882, MX885, MX890, MX920, MX7600 .br -PIXMA TS3100, TS5000, TS6100, TS6200, TS8000, TS8200 +PIXMA TS3100, TS3300, TS5000, TS6100, TS6200, TS8000, TS8200 .br PIXUS MP10 .br @@ -69,11 +69,13 @@ imageCLASS MF5730, MF5770, MF6550, MPC200 .br imageCLASS D420, D480, D530, D570 .br -i-SENSYS MF210, MF230, MF240, MF620, MF630, MF640, MF645C, MF730 +i-SENSYS MF210, MF230, MF240, MF440, MF620, MF630, MF640 .br -i-SENSYS MF731/733, MF741/743, MF3010, MF4320d, MF4330d, MF4500 +i-SENSYS MF645C, MF730, MF731/733, MF741/743 .br -i-SENSYS MF4700, MF4800, MF6100, MF8030, MF8200C, MF8300 +i-SENSYS MF3010, MF4320d, MF4330d, MF4500, MF4700, MF4800 +.br +i-SENSYS MF6100, MF8030, MF8200C, MF8300 .br imageRUNNER 1020/1024/1025, 1133 .br @@ -81,7 +83,7 @@ CanoScan 8800F, 9000F, 9000F Mark II .br CanoScan LiDE 300, 400 .br -MAXIFY MB2000, MB2100, MB2300, MB2700, MB5000, MB5400 +MAXIFY MB2000, MB2100, MB2300, MB2700, MB5000, MB5100, MB5400 .RE .PP The following models are not well tested and/or the scanner sometimes hangs @@ -107,7 +109,7 @@ PIXMA MP375R, MP493, MP495, MP740 .br PIXMA MX320, MX390, MX430, MX450, MX490, MX710 .br -PIXMA G3000, G3010, G4000, G4010, G6000, G6080 +PIXMA G3000, G3010, G4000, G4010, G6000, G6080, G7000, GM4000 .br PIXMA TR4500, TR7500, TR7530, TR8500, TR8530, TR8580, TR9530 .br @@ -115,13 +117,13 @@ PIXMA TS5100, TS6000, TS6130, TS6180, TS6230, TS6280, TS6300 .br PIXMA TS6330, TS6380, TS7330, TS8100, TS8130, TS8180, TS8230 .br -PIXMA TS8280,, TS8300, TS8330, TS8380, TS9000, TS9100, TS9180 +PIXMA TS8280, TS8300, TS8330, TS8380, TS9000, TS9100, TS9180 .br PIXMA TS9500, TS9580 .br PIXUS MP5, XK50, XK60, XK70, XK80 .br -imageCLASS MF810/820, MF5630, MF5650, MF5750, MF8170c +imageCLASS MF720, MF810/820, MF5630, MF5650, MF5750, MF8170c .br imageCLASS MPC190, D550 .br @@ -129,7 +131,7 @@ i-SENSYS MF110, MF220, MF260, MF410, MF420, MF510, MF520, MF740 .br i-SENSYS MF5880dn, MF5900, MF6680dn, MF8500C .br -MAXIFY MB5100, MB5300 +MAXIFY MB5300 .RE .PP The following models may use partly the same Pixma protocol as other devices diff --git a/doc/sane.man b/doc/sane.man index 070a993bc..ed8116cef 100644 --- a/doc/sane.man +++ b/doc/sane.man @@ -40,9 +40,8 @@ provides some means to manage one or more other backends. .SH "SOFTWARE PACKAGES" The package .RB ` sane\-backends ' -contains a lot of backends, documentation (including the -.B SANE -standard), networking support, and the command line frontend +contains a lot of backends, documentation, networking support, and the +command line frontend .RB ` scanimage '. The frontends .RB ` xscanimage "', `" xcam "', and `" scanadf ' @@ -65,7 +64,7 @@ A name with a number in parenthesis (e.g. points to a manual page. In this case .RB ` "man 5 sane\-dll" ' will display the page. Entries like -.RI ` @DOCDIR@/sane.tex ' +.RI ` @DOCDIR@/README ' are references to text files that were copied to the .B SANE documentation directory @@ -218,6 +217,11 @@ The canon_dr backend supports the Canon DR-Series ADF SCSI and USB scanners. See .BR sane\-canon_dr (5) for details. .TP +.B canon_lide70 +The canon_lide70 backend supports the CanoScan LiDE 70 USB scanner. See +.BR sane\-canon_lide70 (5) +for details. +.TP .B canon_pp The canon_pp backend supports the CanoScan FB330P, FB630P, N340P and N640P parallel port scanners. See @@ -665,14 +669,7 @@ The .B SANE standard defines the application programming interface (API) that is used to communicate between frontends and backends. It can be found at -.I @DOCDIR@/sane.ps -(if latex is installed on your system) and on the -.B SANE -website: -.I http://www.sane\-project.org/html/ -(HTML), or -.I http://www.sane\-project.org/sane.ps -(Postscript). +.I http://sane\-project.gitlab.io/standard/ . .PP There is some more information for programmers in .IR @DOCDIR@/backend\-writing.txt . @@ -712,7 +709,7 @@ support dynamic loading). .TP .I @DOCDIR@/* .B SANE -documentation: The standard, READMEs, text files for backends etc. +documentation: The READMEs, text files for backends etc. .SH "PROBLEMS" If your device isn't found but you know that it is supported, make diff --git a/doc/sane.tex b/doc/sane.tex deleted file mode 100644 index 71ff6fc31..000000000 --- a/doc/sane.tex +++ /dev/null @@ -1,1892 +0,0 @@ -\documentclass[11pt]{report} - -\usepackage{times,graphicx,url} -% Not Currently using changebar package so comment out to reduce -% external dependencies. -%\usepackage{changebar} - -\setlength{\parindent}{0pt} -\setlength{\parskip}{1.5ex plus 0.5ex minus 0.5ex} -\setlength{\textwidth}{6.5in} -\setlength{\textheight}{8.5in} -\setlength{\marginparwidth}{0pt} -\setlength{\oddsidemargin}{0pt} -\setlength{\evensidemargin}{0pt} -\setlength{\marginparsep}{0pt} -\addtolength{\topmargin}{-0.75in} - -\title{\huge SANE Standard Version 1.06} -\author{} -\date{2008-05-03} - -\makeindex - -\begin{document} - -\newcommand{\filename}[1]{{\tt #1}} -\newcommand{\code}[1]{{\tt #1}} -\newcommand{\var}[1]{{\it #1}} -\newcommand{\defn}[1]{#1\index{#1}} - -% Uncomment if adding changebars to text -%\begin{latexonly} -% \setcounter{changebargrey}{0} % black change bars -%\end{latexonly} - -\maketitle -\tableofcontents -\listoffigures -\listoftables - - -\chapter{Preface} - -The SANE standard is being developed by a group of free-software -developers. The process is open to the public and comments as well as -suggestions for improvements are welcome. Information on how to join -the SANE development process can be found in Chapter -\ref{chap:contact}. - -The SANE standard is intended to streamline software development by -providing a standard application programming interface to access -raster scanner hardware. This should reduce the number of different -driver implementations, thereby reducing the need for reimplementing -similar code. - - -\section{About This Document} - -This document is intended for developers who are creating either an -application that requires access to raster scanner hardware and for -developers who are implementing a SANE driver. It does not cover -specific implementations of SANE components. Its sole purpose is to -describe and define the SANE application interface that will enable -any application on any platform to interoperate with any SANE backend -for that platform. - -The remainder of this document is organized as follows. -Chapter~\ref{chap:intro} provides introductional material. -Chapter~\ref{chap:environ} presents the environment SANE is designed -for. Chapter~\ref{chap:api} details the SANE Application Programmer -Interface. Chapter~\ref{chap:net} specifies the network protocol that -can be used to implement the SANE API in a network transparent -fashion. Finally, Chapter~\ref{chap:contact} gives information on how -to join the SANE development process. - -\subsection{Typographic Conventions} - -Changes since the last revision of this document are highlighted -like this: - -% \begin{changebar} -% Paragraphs that changed since the last revision of the documention -% are marked like this paragraph. -% \end{changebar} - -\chapter{Introduction}\label{chap:intro} - -SANE is an application programming interface (API) that provides -standardized access to any raster image scanner hardware. The -standardized interface allows to write just one driver for each -scanner device instead of one driver for each scanner and application. -The reduction in the number of required drivers provides significant -savings in development time. More importantly, SANE raises the level -at which applications can work. As such, it will enable applications -that were previously unheard of in the UNIX world. While SANE is -primarily targeted at a UNIX environment, the standard has been -carefully designed to make it possible to implement the API on -virtually any hardware or operating system. - -SANE is an acronym for ``Scanner Access Now Easy.'' Also, the hope is -that SANE is sane in the sense that it will allow easy implementation -of the API while accommodating all features required by today's -scanner hardware and applications. Specifically, SANE should be broad -enough to accommodate devices such as scanners, digital still and -video cameras, as well as virtual devices like image file filters. - -\section{Terminology} - -An application that uses the SANE interface is called a SANE {\em - frontend}. A driver that implements the SANE interface is called a -SANE {\em backend}. A {\em meta backend\/} provides some means to -manage one or more other backends. - - -\chapter{The SANE Environment}\label{chap:environ} - -SANE is defined as a C-callable library interface. Accessing a raster -scanner device typically consists of two phases: first, various -controls of the scanner need to be setup or queried. In the second -phase, one or more images are acquired. - -Since the device controls are widely different from device to device, -SANE provides a generic interface that makes it easy for a frontend to -give a user access to all controls without having to understand each -and every device control. The design principle used here is to -abstract each device control into a SANE {\em option\/}. An option is -a self-describing name/value pair. For example, the brightness -control of a camera might be represented by an option called -\code{brightness} whose value is an integer in the range from 0 to -255. - -With self-describing options, a backend need not be concerned with -{\em presentation\/} issues: the backend simply provides a list of -options that describe all the controls available in the device. -Similarly, there are benefits to the frontend: it need not be -concerned with the {\em meaning\/} of each option. It simply provides -means to present and alter the options defined by the backend. - - -\section{Attaching to a SANE backend} - -The process through which a SANE frontend connects to a backend is -platform dependent. Several possibilities exist: -\begin{itemize} - -\item {\bf Static linking:} A SANE backend may be linked directly into - a frontend. While the simplest method of attaching to a backend, it - is somewhat limited in functionality since the available devices is - limited to the ones for which support has been linked in when the - frontend was built. But even so static linking can be quite useful, - particularly when combined with a backend that can access scanners - via a network. Also, it is possible to support multiple backends - simultaneously by implementing a meta backend that manages several - backends that have been compiled in such a manner that they export - unique function names. For example, a backend called \code{be} - would normally export a function called \code{sane\_read()}. If - each backend would provide such a function, static linking would - fail due to multiple conflicting definitions of the same symbol. - This can be resolved by having backend \code{be} include a - header file that has lines of the form: - \begin{quote} -\begin{verbatim} -#define sane_read be_sane_read -\end{verbatim} - \end{quote} - With definitions of this kind, backend \code{be} will export - function name \code{be\_sane\_read()}. Thus, all backends will - export unique names. As long as a meta backend knows about these - names, it is possible to combine several backends at link time and - select and use them dynamically at runtime. - -\item {\bf Dynamic linking:} A simpler yet more powerful way to - support multiple backends is to exploit dynamic linking on platforms - that support it. In this case, a frontend is linked against a - shared library that implements any SANE backend. Since each - dynamically linked backend exports the same set of global symbols - (all starting with the prefix \code{sane\_}), the dynamic library - that gets loaded at runtime does not necessarily have to be the same - one as one the frontend got linked against. In other words, it is - possible to switch the backend by installing the appropriate backend - dynamic library. - - More importantly, dynamic linking makes it easy to implement a meta - backend that loads other backends {\em on demand}. This is a - powerful mechanism since it allows adding new backends merely by - installing a shared library and updating a configuration file. - -\item {\bf Network connection:} Arguably the ultimate way to attach to - a scanner is by using the network to connect to a backend on a - remote machine. This makes it possible to scan images from any host - in the universe, as long as there is a network connection to that - host and provided the user is permitted to access that scanner. - -\end{itemize} - -\begin{figure}[htbp] - \begin{center} - \leavevmode - \includegraphics[width=\textwidth]{figs/hierarchy} - \caption{Example SANE Hiearchy} - \label{fig:hierarchy} - \end{center} -\end{figure} - -The above discussion lists just a few ways for frontends to attach to -a backend. It is of course possible to combine these solutions to -provide an entire hierarchy of SANE backends. Such a hierarchy is -depicted in Figure~\ref{fig:hierarchy}. The figure shows that machine -A uses a dynamic-linking based meta backend called \code{dll} to -access the backends called \code{pnm}, \code{mustek}, and \code{net}. -The first two are real backends, whereas the last one is a meta -backend that provides network transparent access to remote scanners. -In the figure, machine B provides non-local access to its scanners -through the SANE frontend called \code{saned}. The \code{saned} in -turn has access to the \code{hp} and \code{autolum} backends through -another instance of the \code{dll} backend. The \code{autolum} meta -backend is used to automatically adjust the luminance (brightness) of -the image data acquired by the camera backend called \code{qcam}. - -Note that a meta backend really is both a frontend and a backend at -the same time. It is a frontend from the viewpoint of the backends -that it manages and a backend from the viewpoint of the frontends that -access it. The name ``meta backend'' was chosen primarily because the -SANE standard describes the interface from the viewpoint of a (real) -frontend. - - -\section{Image Data Format}\label{sec:imageformat}\index{image data format} - -Arguably the most important aspect of an image acquisition system is -how images are represented. The SANE approach is to define a simple -yet powerful representation that is sufficient for vast majority of -applications and devices. While the representation is simple, the -interface has been defined carefully to allow extending it in the -future without breaking backwards compatibility. Thus, it will be -possible to accommodate future applications or devices that were not -anticipated at the time this standard was created. - -A SANE image is a rectangular area. The rectangular area is -subdivided into a number of rows and columns. At the intersection of -each row and column is a quadratic pixel. A pixel consists of one or -more sample values. Each sample value represents one channel (e.g., -the red channel). Each sample value has a certain bit depth. The bit -depth is fixed for the entire image and can be as small as one bit. -Valid bit depths are 1, 8, or 16 bits per sample. If a device's -natural bit depth is something else, it is up to the driver to scale -the sample values appropriately (e.g., a 4 bit sample could be scaled -by a factor of four to represent a sample value of depth 8). - -\subsection{Image Transmission} - -The SANE API transmits an image as a sequence of frames. Each frame -covers the same rectangular area as the entire image, but may contain -only a subset of the channels in the final image. For example, a -red/green/blue image could either be transmitted as a single frame -that contains the sample values for all three channels or it could be -transmitted as a sequence of three frames: the first frame containing -the red channel, the second the green channel, and the third the blue -channel. - -Conceptually, each frame is transmitted a byte at a time. Each byte -may contain 8 sample values (for an image bit depth of 1), one full -sample value (for an image bit depth of 8), or a partial sample value -(for an image bit depth of 16 or bigger). In the latter case, the -bytes of each sample value are transmitted in the machine's native -byte order. For depth 1, the leftmost pixel is stored in the most -significant bit, and the rightmost pixel in the least significant bit. -\begin{quote} - \begin{center} - {\bf Backend Implementation Note} - \end{center} - A network-based meta backend will have to ensure that the byte order - in image data is adjusted appropriately if necessary. For example, - when the meta backend attaches to the server proxy, the proxy may - inform the backend of the server's byte order. The backend can then - apply the adjustment if necessary. In essence, this implements a - ``receiver-makes-right'' approach. -\end{quote} - -\begin{figure}[htbp] - \begin{center} - \leavevmode - \includegraphics[width=0.5\textwidth]{figs/xfer} - \caption{Transfer order of image data bytes} - \label{fig:xfer} - \end{center} -\end{figure} - -The order in which the sample values in a frame are transmitted is -illustrated in Figure~\ref{fig:xfer}. As can be seen, the values are -transmitted row by row and each row is transmitted from left-most to -right-most column. The left-to-right, top-to-bottom transmission -order applies when the image is viewed in its normal orientation (as -it would be displayed on a screen, for example). - -If a frame contains multiple channels, then the channels are -transmitted in an interleaved fashion. Figure~\ref{fig:pixels} -illustrates this for the case where a frame contains a complete -red/green/blue image with a bit-depth of 8. For a bit depth of 1, -each byte contains 8 sample values of a {\em single\/} channel. In -other words, a bit depth 1 frame is transmitted in a byte interleaved -fashion. - -\begin{figure}[htbp] - \begin{center} - \leavevmode - \includegraphics[width=0.8\textwidth]{figs/image-data} - \caption{Bit and byte order or image data} - \label{fig:pixels} - \end{center} -\end{figure} - -When transmitting an image frame by frame, the frontend needs to know -what part of the image a frame represents (and how many frames it -should expect). For that purpose, the SANE API tags every frame with -a type. This version of the SANE standard supports the following -frame types: -\begin{quote} -\begin{description} - -\item[\code{\defn{SANE\_FRAME\_GRAY}}:] The frame contains a single - channel of data that represents sample values from a spectral band - that covers the human visual range. The image consists of this - frame only. - -\item[\code{\defn{SANE\_FRAME\_RGB}}:] The frame contains three - channels of data that represent sample values from the red, green, - and blue spectral bands. The sample values are interleaved in the - order red, green, and blue. The image consists of this frame only. - -\item[\code{\defn{SANE\_FRAME\_RED}}:] The frame contains one channel - of data that represents sample values from the red spectral band. - The complete image consists of three frames: - \code{SANE\_\-FRA\-ME\_RED}, \code{SANE\_FRAME\_GREEN}, and - \code{SANE\_FRAME\_BLUE}. The order in which the frames are - transmitted chosen by the backend. - -\item[\code{\defn{SANE\_FRAME\_GREEN}}:] The frame contains one - channel of data that represents sample values from the green - spectral band. The complete image consists of three frames: - \code{SANE\_\-FRA\-ME\_RED}, \code{SANE\_FRAME\_GREEN}, and - \code{SANE\_FRAME\_BLUE}. The order in which the frames are - transmitted chosen by the backend. - -\item[\code{\defn{SANE\_FRAME\_BLUE}}:] The frame contains one channel - of data that represents sample values from the blue spectral band. - The complete image consists of three frames: - \code{SANE\_\-FRA\-ME\_RED}, \code{SANE\_FRAME\_GREEN}, and - \code{SANE\_FRAME\_BLUE}. The order in which the frames are - transmitted chosen by the backend. - -\end{description} -\end{quote} - -In frames of type \code{SANE\_FRAME\_GRAY}, when the bit depth is 1 there are -only two sample values possible, 1 represents minimum intensity -(black) and 0 represents maximum intensity (white). For all other bit -depth and frame type combinations, a sample value of 0 represents -minimum intensity and larger values represent increasing intensity. - -The combination of bit depth 1 and \code{SANE\_FRAME\_RGB} (or -\code{SANE\_FRAME\_RED}, \code{SANE\_FRAME\_GREEN}, \code{SANE\_FRAME\_BLUE}) -is rarely used and may not be supported by every frontend. - -\chapter{The SANE Application Programmer Interface (API)}\label{chap:api} - -This Section defines version 1 of the SANE application -programmer interface (API). Any SANE frontend must depend on the -interface defined in this section only. Converseley, any SANE backend -must implement its functionality in accordance with this -specification. The interface as documented here is declared as a C -callable interface in a file called \filename{sane/sane.h}. This file should -normally be included via a C pre-processor directive of the form: -\begin{verbatim} - #include -\end{verbatim} - - -\section{Version Control} - -The SANE standard is expected to evolve over time. Whenever a change -to the SANE standard is made that may render an existing frontend or -backend incompatible with the new standard, the major version number -must be increased. Thus, any frontend/backend pair is compatible -provided the major version number of the SANE standard they implement -is the same. A frontend may implement backwards compatiblity by -allowing major numbers that are smaller than the expected major number -(provided the frontend really can cope with the older version). In -contrast, a backend always provides support for one and only one -version of the standard. If a specific application does require that -two different versions of the same backend are accessible at the same -time, it is possible to do so by installing the two versions under -different names. - -SANE version control also includes a minor version number and a build -revision. While control of these numbers remains with the implementor -of a backend, the recommended use is as follows. The minor version is -incremented with each official release of a backend. The build -revision is increased with each build of a backend. - -The SANE API provides the following five macros to manage version -numbers. -\begin{quote} - \begin{description} - \item[\code{\defn{SANE\_CURRENT\_MAJOR}}:] The value of this macro is the - number of the SANE standard that the interface implements. - - \item[\code{\defn{SANE\_VERSION\_CODE}(\var{maj},\var{min},\var{bld})}:] - \label{sec:saneversioncode} - This macro can be used to build a monotonically increasing version - code. A SANE version code consists of the SANE standard major - version number (\var{maj}), the minor version number \var{min}, - and the build revision of a backend (\var{bld}). The major and - minor version numbers must be in the range 0\ldots255 and the - build revision must be in the range 0\ldots65535. - - Version codes are monotonic in the sense that it is possible to - apply relational operators (e.g., equality or less-than test) - directly on the version code rather than individually on the three - components of the version code. - - Note that the major version number alone determines whether a - frontend/backend pair is compatible. The minor version and the - build revision are used for informational and bug-fixing purposes - only. - - \item[\code{\defn{SANE\_VERSION\_MAJOR}(\var{vc})}:] This macro returns the - major version number component of the version code passed in - argument \var{vc}. - \item[\code{SANE\_VERSION\_MINOR(\var{vc})}:] This macro returns the - minor version number component of the version code passed in - argument \var{vc}. - \item[\code{SANE\_VERSION\_BUILD(\var{vc})}:] This macro returns the - build revision component of the version code passed in argument - \var{vc}. - \end{description} -\end{quote} - - -\section{Data Types} - -\subsection{Base Types} - -The SANE standard is based on just two SANE-specific base types: the -SANE byte and word. -\begin{quote} - \code{typedef \var{some-scalar-type\/} \defn{SANE\_Byte};} \\ - \code{typedef \var{some-scalar-type\/} \defn{SANE\_Word};} -\end{quote} -\verb|SANE_Byte| must correspond to some scalar C type that is capable -of holding values in the range 0 to 255. \verb|SANE_Word| must be -capable of holding any of the following: -\begin{itemize} - \item the truth values \verb|SANE_FALSE| and \verb|SANE_TRUE| - \item signed integers in the range $-2^{31}\ldots2^{31}-1$ - \item fixed point values in the range $-32768\ldots32767.9999$ with - a resolution of $1/65536$ - \item 32 bits (for bit sets) -\end{itemize} -Note that the SANE standard does not define what C type -\verb|SANE_Byte| and \verb|SANE_Word| map to. For example, on some -platforms, the latter may map to \verb|long int| whereas on others it -may map to \verb|int|. A portable SANE frontend or backend must -therefore not depend on a particular mapping. - -\subsection{Boolean Type} - -\code{\defn{SANE\_Bool}} is used for variables that can take one of -the two truth values \code{\defn{SANE\_FALSE}} and -\code{\defn{SANE\_TRUE}}. The former value is defined to be 0, -whereas the latter is 1.\footnote{This is different from ANSI C where - any non-zero integer value represents logical TRUE.} The C -declarations for this type are given below. -\begin{quote} -\begin{verbatim} -#define SANE_FALSE 0 -#define SANE_TRUE 1 -typedef SANE_Word SANE_Bool; -\end{verbatim} -\end{quote} -Note that \verb|SANE_Bool| is simply an alias of \verb|SANE_Word|. It -is therefore always legal to use the latter type in place of the -former. However, for clarity, it is recommended to use -\verb|SANE_Bool| whenever a given variable or formal argument has a -fixed interpretation as a boolean object. - -\subsection{Integer Type} - -\code{\defn{SANE\_Int}} is used for variables that can take integer -values in the range $-2^{32}$ to $2^{31}-1$. Its C declaration is -given below. -\begin{quote} -\begin{verbatim} -typedef SANE_Word SANE_Int; -\end{verbatim} -\end{quote} -Note that \verb|SANE_Int| is simply an alias of \verb|SANE_Word|. It -is therefore always legal to use the latter type in place of the -former. However, for clarity, it is recommended to use -\verb|SANE_Int| whenever a given variable or formal argument has a -fixed interpretation as an integer object. - - -\subsection{Fixed-point Type} - -\code{\defn{SANE\_Fixed}} is used for variables that can take fixed -point values in the range $-32768$ to $32767.9999$ with a resolution -of $1/65535$. The C declarations relating to this type are given -below. -\begin{quote} -\begin{verbatim} -#define SANE_FIXED_SCALE_SHIFT 16 -typedef SANE_Word SANE_Fixed; -\end{verbatim} -\end{quote} -The macro \code{\defn{SANE\_FIXED\_SCALE\_SHIFT}} gives the location -of the fixed binary point. This standard defines that value to be 16, -which yields a resolution of $1/65536$. - -Note that \verb|SANE_Fixed| is simply an alias of \verb|SANE_Word|. -It is therefore always legal to use the latter type in place of the -former. However, for clarity, it is recommended to use -\verb|SANE_Fixed| whenever a given variable or formal argument has a -fixed interpretation as a fixed-point object. - -For convenience, SANE also defines two macros that convert fixed-point -values to and from C double floating point values. -\begin{quote} - \begin{description} - - \item[\code{\defn{SANE\_FIX}(\var{d})}:] Returns the largest SANE - fixed-point value that is smaller than the double value \var{d}. - No range checking is performed. If the value of \var{d} is out of - range, the result is undefined. - - \item[\code{\defn{SANE\_UNFIX}(\var{w})}:] Returns the nearest - double machine number that corresponds to fixed-point value - \var{w}. - - \end{description} -\end{quote} -SANE does {\em not\/} require that the following two expressions hold -true (even if the values of \var{w} and \var{d} are in range): -\begin{quote} -\begin{verbatim} -SANE_UNFIX(SANE_FIX(d)) == d -SANE_FIX(SANE_UNFIX(w)) == w -\end{verbatim} -\end{quote} -In other words, conversion between fixed and double values may be -lossy. It is therefore recommended to avoid repeated conversions -between the two representations. - - -\subsection{Text} - -\subsubsection{Character Type} - -Type \code{\defn{SANE\_Char}} represents a single text character or -symbol. At present, this type maps directly to the underlying C -\verb|char| type (typically one byte). The encoding for such -characters is currently fixed as ISO LATIN-1. Future versions of this -standard may map this type to a wider type and allow multi-byte -encodings to support internationalization. As a result of this, care -should be taken to avoid the assumption that -\verb|sizeof(SANE_Char)==sizeof(char)|. -\begin{quote} -\begin{verbatim} -typedef char SANE_Char; -\end{verbatim} -\end{quote} - -\subsubsection{String Type} - -Type \code{\defn{SANE\_String}} represents a text string as a sequence -of C \verb|char| values. The end of the sequence is indicated by a -\verb|'\0'| (\defn{NUL}) character. -\begin{quote} -\begin{verbatim} -typedef SANE_Char *SANE_String; -typedef const SANE_Char *SANE_String_Const; -\end{verbatim} -\end{quote} -The type \code{\defn{SANE\_String\_Const}} is provided by SANE to -enable declaring strings whose contents is unchangable. Note that in -ANSI C, the declaration -\begin{quote} -\begin{verbatim} -const SANE_String str; -\end{verbatim} -\end{quote} -declares a string pointer that is constant (not a string pointer that -points to a constant value). - - -\subsection{Scanner Handle Type} - -Access to a scanner is provided through an opaque type called -\code{\defn{SANE\_Handle}}. The C declaration of this type is given -below. -\begin{quote} -\begin{verbatim} -typedef void *SANE_Handle; -\end{verbatim} -\end{quote} -While this type is declared to be a void pointer, an application must -not attempt to interpret the value of a \verb|SANE_Handle|. In -particular, SANE does not require that a value of this type is a legal -pointer value. - - -\subsection{Status Type} - -Most SANE operations return a value of type \code{\defn{SANE\_Status}} -to indicate whether the completion status of the operation. If an -operation completes successfully, \verb|SANE_STATUS_GOOD| is returned. -In case of an error, a value is returned that indicates the nature of -the problem. The complete list of available status codes is listed in -Table \ref{tab:status}. It is recommended to use function -\code{sane\_strstatus()} to convert status codes into a legible -string. - -\begin{table}[htbp] - \begin{center} - \begin{tabular}{|l|r|l|} - \hline - \multicolumn{1}{|c|}{\bf Symbol} & \multicolumn{1}{c|}{\bf Code} & - \multicolumn{1}{c|}{\bf Description} \\ - \hline\hline -\code{\defn{SANE\_STATUS\_GOOD}} - & 0 & Operation completed succesfully. \\ -\code{\defn{SANE\_STATUS\_UNSUPPORTED}} - & 1 & Operation is not supported. \\ -\code{\defn{SANE\_STATUS\_CANCELLED}} - & 2 & Operation was cancelled. \\ -\code{\defn{SANE\_STATUS\_DEVICE\_BUSY}} - & 3 & Device is busy---retry later. \\ -\code{\defn{SANE\_STATUS\_INVAL}} - & 4 & Data or argument is invalid. \\ -\code{\defn{SANE\_STATUS\_EOF}} - & 5 & No more data available (end-of-file). \\ -\code{\defn{SANE\_STATUS\_JAMMED}} - & 6 & Document feeder jammed. \\ -\code{\defn{SANE\_STATUS\_NO\_DOCS}} - & 7 & Document feeder out of documents. \\ -\code{\defn{SANE\_STATUS\_COVER\_OPEN}} - & 8 & Scanner cover is open. \\ -\code{\defn{SANE\_STATUS\_IO\_ERROR}} - & 9 & Error during device I/O. \\ -\code{\defn{SANE\_STATUS\_NO\_MEM}} - & 10 & Out of memory. \\ -\code{\defn{SANE\_STATUS\_ACCESS\_DENIED}} - & 11 & Access to resource has been denied. \\ - \hline - \end{tabular} - \caption{Status Codes}\label{tab:status} - \end{center} -\end{table} - - -\subsection{Device Descriptor Type} - -Each SANE device is represented by a structure of type -\code{\defn{SANE\_Device}}. The C declaration of this type is given -below. -\begin{quote} -\begin{verbatim} -typedef struct - { - SANE_String_Const name; - SANE_String_Const vendor; - SANE_String_Const model; - SANE_String_Const type; - } -SANE_Device; -\end{verbatim} -\end{quote} -\index{device-name} -The structure provides the unique name of the scanner in member -\code{name}. It is this unique name that should be passed in a call -to \code{sane\_open()}. The format of this name is completely up to -the backend. The only constraints are that the name is unique among -all devices supported by the backend and that the name is a legal SANE -text string. To simplify presentation of unique names, their length -should not be excessive. It is {\em recommended\/} that backends keep -unique names below 32 characters in length. However, applications -{\em must\/} be able to cope with arbitrary length unique names. - -The remaining members in the device structure provide additional -information on the device corresponding to the unique name. -Specifically, members \code{vendor}, \code{model}, and \code{type} are -single-line strings that give information on the vendor -(manufacturer), model, and the type of the device. For consistency's -sake, the following strings should be used when appropriate (the lists -will be expanded as need arises): - -\begin{table}[htbp] - \begin{center} - \leavevmode - \hspace{\fill} - \begin{tabular}[t]{|ll|} - \hline - \multicolumn{2}{|c|}{\bf \defn{Vendor Strings}} \\ - \hline\hline - \code{AGFA} & \code{Microtek} \\ - \code{Abaton} & \code{Minolta} \\ - \code{Acer} & \code{Mitsubishi} \\ - \code{Apple} & \code{Mustek} \\ - \code{Artec} & \code{NEC} \\ - \code{Avision} & \code{Nikon} \\ - \code{CANON} & \code{Plustek} \\ - \code{Connectix} & \code{Polaroid} \\ - \code{Epson} & \code{Relisys} \\ - \code{Fujitsu} & \code{Ricoh} \\ - \code{Hewlett-Packard} & \code{Sharp} \\ - \code{IBM} & \code{Siemens} \\ - \code{Kodak} & \code{Tamarack} \\ - \code{Lexmark} & \code{UMAX} \\ - \code{Logitech} & \code{Noname} \\ - \hline - \end{tabular} - \hspace{\fill} - \begin{tabular}[t]{|l|} - \hline - \multicolumn{1}{|c|}{\bf \defn{Type Strings}} \\ - \hline\hline - \code{film scanner} \\ - \code{flatbed scanner} \\ - \code{frame grabber} \\ - \code{handheld scanner} \\ - \code{multi-function peripheral} \\ - \code{sheetfed scanner} \\ - \code{still camera} \\ - \code{video camera} \\ - \code{virtual device} \\ - \hline - \end{tabular} - \hspace{\fill} - \caption{Predefined Device Information Strings} - \label{tab:devinfo} - \end{center} -\end{table} -Note that vendor string \code{Noname} can be used for virtual devices -that have no physical vendor associated. Also, there are no -predefined model name strings since those are vendor specific and -therefore completely under control of the respective backends. - - -\subsection{Option Descriptor Type}\label{sec:odesc} - -Option descriptors are at the same time the most intricate and -powerful type in the SANE standard. Options are used to control -virtually all aspects of device operation. Much of the power of the -SANE API stems from the fact that most device controls are completely -described by their respective option descriptor. Thus, a frontend can -control a scanner abstractly, without requiring knowledge as to what -the purpose of any given option is. Conversely, a scanner can -describe its controls without requiring knowledge of how the frontend -operates. The C declaration of the -\code{\defn{SANE\_Option\_Descriptor}} type is given below. -\begin{quote} -\begin{verbatim} -typedef struct - { - SANE_String_Const name; - SANE_String_Const title; - SANE_String_Const desc; - SANE_Value_Type type; - SANE_Unit unit; - SANE_Int size; - SANE_Int cap; - SANE_Constraint_Type constraint_type; - union - { - const SANE_String_Const *string_list; - const SANE_Word *word_list; - const SANE_Range *range; - } - constraint; - } -SANE_Option_Descriptor; -\end{verbatim} -\end{quote} - -\subsubsection{Option Name} - -Member \code{name} is a string that uniquely identifies the option. -The name must be unique for a given device (i.e., the option names -across different backends or devices need not be unique). The option -name must consist of lower-case ASCII letters (\code{a}--\code{z}), -digits (\code{0}--\code{9}), or the dash character (\code{-}) only. -The first character must be a lower-case ASCII character (i.e., not a -digit or a dash). - -\subsubsection{Option Title} - -Member \code{title} is a single-line string that can be used by the -frontend as a title string. This should typically be a short (one or -two-word) string that is chosen based on the function of the option. - -\subsubsection{Option Description} - -Member \code{desc} is a (potentially very) long string that can be -used as a help text to describe the option. It is the responsibility -of the frontend to break the string into managable-length lines. -Newline characters in this string should be interpreted as paragraph -breaks. - -\subsubsection{Option Value Type} - -Member \code{type} specifies the type of the option value. The -possible values for type \code{\defn{SANE\_Value\_Type}} are described -in Table \ref{tab:valuetype}. - -\begin{table}[htbp] - \begin{center} - \leavevmode - \begin{tabular}{|l|l|p{0.6\textwidth}|} -\hline -\multicolumn{1}{|c|}{\bf Symbol} & -\multicolumn{1}{c|}{\bf Code} & -\multicolumn{1}{c|}{\bf Description} \\ -\hline\hline - -\code{\defn{SANE\_TYPE\_BOOL}} & 0 & Option value is of type - \verb|SANE_Bool|. \\ - -\code{\defn{SANE\_TYPE\_INT}} & 1 & Option value is of type - \verb|SANE_Int|. \\ - -\code{\defn{SANE\_TYPE\_FIXED}}&2 & Option value is of type - \verb|SANE_Fixed|. \\ - -\code{\defn{SANE\_TYPE\_STRING}}&3 & Option value is of type - \verb|SANE_String|. \\ - -\code{\defn{SANE\_TYPE\_BUTTON}} & 4 & An option of this type has no value. -Instead, setting an option of this type has an option-specific -side-effect. For example, a button-typed option could be used by a -backend to provide a means to select default values or to the tell an -automatic document feeder to advance to the next sheet of paper. \\ - -\code{\defn{SANE\_TYPE\_GROUP}} & 5 & An option of this type has no value. -This type is used to group logically related options. A group option -is in effect up to the point where another group option is encountered -(or up to the end of the option list, if there are no other group -options). For group options, only members \code{title} and -\code{type} are valid in the option descriptor. \\ - - \hline - \end{tabular} - \caption{Option Value Types (\code{SANE\_Value\_Type})} - \label{tab:valuetype} - \end{center} -\end{table} - -\subsubsection{Option Value Unit} - -Member \code{unit} specifies what the physical unit of the option -value is. The possible values for type \code{\defn{SANE\_U\-nit}} are -described in Table \ref{tab:units}. Note that the specified unit is -what the SANE backend expects. It is entirely up to a frontend as to -how these units a presented to the user. For example, SANE expresses -all lengths in millimeters. A frontend is generally expected to -provide appropriate conversion routines so that a user can express -quantities in a customary unit (e.g., inches or centimeters). - -\begin{table}[htbp] - \begin{center} - \leavevmode - \begin{tabular}{|l|l|l|} -\hline -\multicolumn{1}{|c|}{\bf Symbol} & -\multicolumn{1}{|c|}{\bf Code} & -\multicolumn{1}{|c|}{\bf Description} \\ - -\hline\hline - -\code{\defn{SANE\_UNIT\_NONE}} & 0 & Value is unit-less (e.g., page count).\\ -\code{\defn{SANE\_UNIT\_PIXEL}} & 1 & Value is in number of pixels. \\ -\code{\defn{SANE\_UNIT\_BIT}} & 2 & Value is in number of bits. \\ -\code{\defn{SANE\_UNIT\_MM}} & 3 & Value is in millimeters. \\ -\code{\defn{SANE\_UNIT\_DPI}} & 4 & Value is a resolution in dots/inch. \\ -\code{\defn{SANE\_UNIT\_PERCENT}}& 5 & Value is a percentage. \\ -\code{\defn{SANE\_UNIT\_MICROSECOND}}& 6 & Value is time in $\mu$-seconds. \\ - -\hline - \end{tabular} - \caption{Physical Units (\code{SANE\_Unit})} - \label{tab:units} - \end{center} -\end{table} - -\subsubsection{Option Value Size}\label{sec:valuesize} - -Member \code{size} specifies the size of the option value (in bytes). -This member has a slightly different interpretation depending on the -type of the option value: -\begin{quote} - \begin{description} - \item[\code{SANE\_TYPE\_STRING}:] The size is the maximum size of - the string. For the purpose of string size calcuations, the - terminating \code{NUL} character is considered to be part of the - string. Note that the terminating \code{NUL} character must - always be present in string option values. - \item[\code{SANE\_TYPE\_INT}, \code{SANE\_TYPE\_FIXED}:] The size - must be a positive integer multiple of the size of a - \verb|SANE_Word|. The option value is a vector of length - \[ \code{size}/\code{sizeof(SANE\_Word)}. \] - \item[\code{SANE\_TYPE\_BOOL}:] The size must be set to - \code{sizeof(SANE\_Word)}. - \item[\code{SANE\_TYPE\_BUTTON}, \code{SANE\_TYPE\_GROUP}:] The - option size is ignored. - \end{description} -\end{quote} - -\subsubsection{Option Capabilities} - -Member \code{cap} describes what capabilities the option posseses. -This is a bitset that is formed as the inclusive logical OR of the -capabilities described in Table \ref{tab:capabilities}. The SANE API -provides the following to macros to test certain features of a given -capability bitset: -\begin{quote} - \begin{description} - - \item[\code{\defn{SANE\_OPTION\_IS\_ACTIVE}(\var{cap})}:] This macro - returns \code{SANE\_TRUE} if and only if the option with the - capability set \var{cap} is currently active. - - \item[\code{\defn{SANE\_OPTION\_IS\_SETTABLE}(\var{cap})}:] This - macro returns \code{SANE\_TRUE} if and only if the option with the - capability set \var{cap} is software settable. - \end{description} -\end{quote} - -\begin{table}[htbp] - \begin{center} - \leavevmode - \begin{tabular}{|l|r|p{0.59\textwidth}|} -\hline -\multicolumn{1}{|c|}{\bf Symbol} & -\multicolumn{1}{c|}{\bf Code} & -\multicolumn{1}{c|}{\bf Description} \\ -\hline\hline - -\code{\defn{SANE\_CAP\_SOFT\_SELECT}} & 1 & The option - value can be set by a call to \code{sane\_con\-trol\_opt\-ion()}.\\ - -\code{\defn{SANE\_CAP\_HARD\_SELECT}} & 2 & The option value can be set by - user-intervention (e.g., by flipping a switch). The user-interface - should prompt the user to execute the appropriate action to set such - an option. This capability is mutually exclusive with - SANE\_CAP\_SOFT\_SELECT (either one of them can be set, but not both - simultaneously). \\ - -\code{\defn{SANE\_CAP\_SOFT\_DETECT}} & 4 & The option - value can be detected by software. If - \code{SANE\_\-CAP\_\-SO\-FT\_SEL\-ECT} is set, this capability {\em must\/} - be set. If \code{SANE\_CAP\_HARD\_SELECT} is set, this capability - may or may not be set. If this capability is set but neither - \code{SANE\_CAP\_SO\-FT\_SEL\-ECT} nor \code{SANE\_CAP\_HA\-RD\_SEL\-ECT} - are, then there is no way to control the option. That is, the - option provides read-out of the current value only. \\ - -\code{\defn{SANE\_CAP\_EMULATED}} & 8 & If set, this capability indicates - that an option is not directly supported by the device and is - instead emulated in the backend. A sophisticated frontend may - elect to use its own (presumably better) emulation in lieu of an emulated - option. \\ - -\code{\defn{SANE\_CAP\_AUTOMATIC}} & 16 & If set, this capability indicates - that the backend (or the device) is capable to picking a reasonable - option value automatically. For such options, it is possible to - select automatic operation by calling \code{sane\_control\_option()} - with an action value of \code{SANE\_ACTION\_SET\_AUTO}. \\ - -\code{\defn{SANE\_CAP\_INACTIVE}} & 32 & If set, this capability indicates - that the option is not currently active (e.g., because it's - meaningful only if another option is set to some other value). \\ - -\code{\defn{SANE\_CAP\_ADVANCED}} & 64 & - If set, this capability indicates that the option should be - considered an ``advanced user option.'' A frontend typically - displays such options in a less conspicuous way than regular options - (e.g., a command line interface may list such options last or a - graphical interface may make them available in a seperate ``advanced - settings'' dialog). - \\ - -\hline - \end{tabular} - \caption{Option Capabilities} - \label{tab:capabilities} - \end{center} -\end{table} - -\subsubsection{Option Value Constraints} - -It is often useful to constrain the values that an option can take. -For example, constraints can be used by a frontend to determine how to -represent a given option. Member \code{constraint\_type} indicates -what constraint is in effect for the option. The constrained values -that are allowed for the option are described by one of the union -members of member \code{constraint}. The possible values of type -\code{\defn{SANE\_Constraint\_Type}} and the interpretation of the -\code{constraint} union is described in Table~\ref{tab:constraints}. - -\begin{table}[htbp] - \begin{center} - \leavevmode - \begin{tabular}{|l|r|p{0.5\textwidth}|} -\hline -\multicolumn{1}{|c|}{\bf Symbol} & -\multicolumn{1}{|c|}{\bf Code} & -\multicolumn{1}{|c|}{\bf Description} \\ - -\hline\hline - -\code{\defn{SANE\_CONSTRAINT\_NONE}} & 0 & The value is unconstrained. - The option can take any of the values possible for the option's - type. \\ - - \code{\defn{SANE\_CONSTRAINT\_RANGE}} & 1 & This constraint is - applicable to integer and fixed-point valued options only. It - constrains the option value to a possibly quantized range of - numbers. Option descriptor member \code{constraint.range} points to - a range of the type \code{\defn{SANE\_Range}}. This type is illustrated - below: - \begin{quote} -\begin{verbatim} -typedef struct - { - SANE_Word min; - SANE_Word max; - SANE_Word quant; - } -SANE_Range; -\end{verbatim} - \end{quote} - All three members in this structure are interpreted according to the - option value type (\verb|SANE_TYPE_INT| or \verb|SANE_TYPE_FIXED|). - Members \code{min} and \code{max} specify the minimum and maximum - values, respectively. If member \code{quant} is non-zero, it - specifies the quantization value. If $l$ is the minimum value, $u$ - the maximum value and $q$ the (non-zero) quantization of a range, - then the legal values are $v=k\cdot q+l$ for all non-negative - integer values of $k$ such that $v<=u$. \\ - -\code{\defn{SANE\_CONSTRAINT\_WORD\_LIST}} & 2 & This constraint is applicable - to integer and fixed-point valued options only. It constrains the - option value to a list of numeric values. Option descriptor member - \code{constraint.word\_list} points to a list of words that - enumerates the legal values. The first element in that list is an - integer (\verb|SANE_Int|) that specifies the length of the list (not - counting the length itself). The remaining elements in the list are - interpreted according to the type of the option value - (\verb|SANE_TYPE_INT| or \verb|SANE_TYPE_FIXED|). \\ - -\code{\defn{SANE\_CONSTRAINT\_STRING\_LIST}} & 3 & This constraint is - applicable to string-valued options only. It constrains the option - value to a list of strings. The option descriptor member - \code{con\-strai\-nt.str\-ing\_list} points to a \code{NULL} terminated - list of strings that enumerate the legal values for the option - value. -\\\hline - \end{tabular} - \caption{Option Value Constraints} - \label{tab:constraints} - \end{center} -\end{table} - - -\section{Operations} - -\subsection{\code{sane\_init}} - -This function must be called before any other SANE function can be called. -The behavior of a SANE backend is undefined if this function is not called -first or if the status code returned by \code{sane\_init} is different from -\code{\defn{SANE\_STATUS\_GOOD}}. The version code of the backend is returned -in the value pointed to by \code{version\_code}. If that pointer is -\code{NULL}, no version code is returned. Argument \code{authorize} is either -a pointer to a function that is invoked when the backend requires -authentication for a specific resource or \code{NULL} if the frontend does not -support authentication. -\begin{quote}\index{sane\_init} -\begin{verbatim} -SANE_Status sane_init (SANE_Int * version_code, - SANE_Authorization_Callback authorize); -\end{verbatim} -\end{quote} - -The authorization function may be called by a backend in response to -any of the following calls: -\begin{quote} - \code{sane\_open}, \code{sane\_control\_option}, \code{sane\_start} -\end{quote} -If a backend was initialized without authorization function, then -authorization requests that cannot be handled by the backend itself -will fail automatically and the user may be prevented from accessing -protected resources. Backends are encouraged to implement means of -authentication that do not require user assistance. E.g., on a -multi-user system that authenticates users through a login process a -backend could automatically lookup the apporpriate password based on -resource- and user-name. - -The authentication function type has the following declaration: -\begin{quote}\index{SANE\_Authorization\_Callback} - \index{domain}\index{username}\index{password} -\begin{verbatim} -#define SANE_MAX_USERNAME_LEN 128 -#define SANE_MAX_PASSWORD_LEN 128 - -typedef void (*SANE_Authorization_Callback) - (SANE_String_Const resource, - SANE_Char username[SANE_MAX_USERNAME_LEN], - SANE_Char password[SANE_MAX_PASSWORD_LEN]); -\end{verbatim} -\end{quote} -Three arguments are passed to the authorization function: -\code{resource} is a string specifying the name of the resource that -requires authorization. A frontend should use this string to build a -user-prompt requesting a username and a password. The \code{username} -and \code{password} arguments are (pointers to) an array of -\code{SANE\_MAX\_USERNAME\_LEN} and \code{SANE\_MAX\_PASSWORD\_LEN} -characters, respectively. The authorization call should place the -entered username and password in these arrays. The returned strings -{\em must\/} be ASCII-NUL terminated. - -\subsection{\code{sane\_exit}} - -This function must be called to terminate use of a backend. The -function will first close all device handles that still might be open -(it is recommended to close device handles explicitly through a call -to \code{sane\_clo\-se()}, but backends are required to release all -resources upon a call to this function). After this function returns, -no function other than \code{sane\_init()} may be called (regardless -of the status value returned by \code{sane\_exit()}. Neglecting to -call this function may result in some resources not being released -properly. -\begin{quote}\index{sane\_exit} -\begin{verbatim} -void sane_exit (void); -\end{verbatim} -\end{quote} - - -\subsection{\code{sane\_get\_devices}} - -This function can be used to query the list of devices that are -available. If the function executes successfully, it stores a pointer -to a \code{NULL} terminated array of pointers to \verb|SANE_Device| -structures in \code{*device\_list}. The returned list is guaranteed -to remain unchanged and valid until (a) another call to this function -is performed or (b) a call to \code{sane\_exit()} is performed. This -function can be called repeatedly to detect when new devices become -available. If argument \code{local\_only} is true, only local devices -are returned (devices directly attached to the machine that SANE is -running on). If it is false, the device list includes all remote -devices that are accessible to the SANE library. -\begin{quote}\index{sane\_get\_devices} -\begin{verbatim} -SANE_Status sane_get_devices (const SANE_Device *** device_list, - SANE_Bool local_only); -\end{verbatim} -\end{quote} - -This function may fail with \code{SANE\_STATUS\_NO\_MEM} if an -insufficient amount of memory is available. - -\begin{quote} - \begin{center} - {\bf Backend Implementation Note} - \end{center} - SANE does not require that this function is called before a - \code{sane\_open()} call is performed. A device name may be - specified explicitly by a user which would make it unnecessary and - undesirable to call this function first. -\end{quote} - - -\subsection{\code{sane\_open}} - -This function is used to establish a connection to a particular -device. The name of the device to be opened is passed in argument -\code{name}. If the call completes successfully, a handle for the -device is returned in \code{*h}. As a special case, specifying a -zero-length string as the device requests opening the first available -device (if there is such a device). -\begin{quote}\index{sane\_open} -\begin{verbatim} -SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h); -\end{verbatim} -\end{quote} - -This function may fail with one of the following status codes. -\begin{quote} -\begin{description} -\item[\code{SANE\_STATUS\_DEVICE\_BUSY}:] The device is currently - busy (in use by somebody else). -\item[\code{SANE\_STATUS\_INVAL}:] The device name is not valid. -\item[\code{SANE\_STATUS\_IO\_ERROR}:] An error occured while - communicating with the device. -\item[\code{SANE\_STATUS\_NO\_MEM}:] An insufficent amount of memory - is available. -\item[\code{SANE\_STATUS\_ACCESS\_DENIED}:] Access to the device has - been denied due to insufficient or invalid authentication. -\end{description} -\end{quote} - - -\subsection{\code{sane\_close}} - -This function terminates the association between the device handle -passed in argument \code{h} and the device it represents. If the -device is presently active, a call to \code{sane\_cancel()} is -performed first. After this function returns, handle \code{h} must -not be used anymore. - -\begin{quote}\index{sane\_close} -\begin{verbatim} -void sane_close (SANE_Handle h); -\end{verbatim} -\end{quote} - -\subsection{\code{sane\_get\_option\_descriptor}} - -This function is used to access option descriptors. The function -returns the option descriptor for option number \code{n} of the device -represented by handle \code{h}. Option number 0 is guaranteed to be a -valid option. Its value is an integer that specifies the number of -options that are available for device handle \code{h} (the count -includes option 0). If $n$ is not a valid option index, the function -returns \code{NULL}. The returned option descriptor is guaranteed to -remain valid (and at the returned address) until the device is closed. - -\begin{quote}\index{sane\_get\_option\_descriptor} -\begin{verbatim} -const SANE_Option_Descriptor * - sane_get_option_descriptor (SANE_Handle h, SANE_Int n); -\end{verbatim} -\end{quote} - -\subsection{\code{sane\_control\_option}}\label{sec:control} - -This function is used to set or inquire the current value of option -number \code{n} of the device represented by handle \code{h}. The -manner in which the option is controlled is specified by parameter -\code{a}. The possible values of this parameter are described in more -detail below. The value of the option is passed through argument -\code{v}. It is a pointer to the memory that holds the option value. -The memory area pointed to by \code{v} must be big enough to hold the -entire option value (determined by member \code{size} in the -corresponding option descriptor). The only exception to this rule is -that when setting the value of a string option, the string pointed to -by argument \code{v} may be shorter since the backend will stop -reading the option value upon encountering the first \code{NUL} -terminator in the string. If argument \code{i} is not \code{NULL}, -the value of \code{*i} will be set to provide details on how well the -request has been met. The meaning of this argument is described in -more detail below. -\begin{quote}\index{sane\_control\_option} -\begin{verbatim} -SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, - SANE_Action a, void *v, - SANE_Int * i); -\end{verbatim} -\end{quote} - -The way the option is affected by a call to this function is -controlled by parameter \code{a} which is a value of type -\code{\defn{SANE\_Action}}. The possible values and their meaning is -described in Table~\ref{tab:actions}. - -\begin{table}[h] - \begin{center} - \leavevmode - \begin{tabular}{|l|r|p{0.5\textwidth}|} -\hline -\multicolumn{1}{|c|}{\bf Symbol} & -\multicolumn{1}{|c|}{\bf Code} & -\multicolumn{1}{|c|}{\bf Description} \\ - -\hline\hline - -\code{\defn{SANE\_ACTION\_GET\_VALUE}} & 0 & Get current option value. \\ - -\code{\defn{SANE\_ACTION\_SET\_VALUE}} & 1 & Set option value. The - option value passed through argument \code{v} may be modified by the - backend if the value cannot be set exactly. \\ - -\code{\defn{SANE\_ACTION\_SET\_AUTO}} & 2 & Turn on automatic mode. Backend - or device will automatically select an appropriate value. This mode - remains effective until overridden by an explicit set value request. - The value of parameter \code{v} is completely ignored in this case and - may be \code{NULL}. \\ - -\hline - \end{tabular} - \caption{Action Values (\code{SANE\_Action})} - \label{tab:actions} - \end{center} -\end{table} - -After setting a value via an action value of -\verb|SANE_ACTION_SET_VALUE|, additional information on how well the -request has been met is returned in \code{*i} (if \code{i} is -non-\code{NULL}). The returned value is a bitset that may contain any -combination of the values described in Table~\ref{tab:info}. -\begin{table}[htbp] - \begin{center} - \leavevmode - \begin{tabular}{|l|r|p{0.5\textwidth}|} -\hline -\multicolumn{1}{|c|}{\bf Symbol} & -\multicolumn{1}{|c|}{\bf Code} & -\multicolumn{1}{|c|}{\bf Description} \\ - -\hline\hline - -\code{\defn{SANE\_INFO\_INEXACT}} & 1 & This value is returned when - setting an option value resulted in a value being selected that does - not exactly match the requested value. For example, if a scanner - can adjust the resolution in increments of 30dpi only, setting the - resolution to 307dpi may result in an actual setting of 300dpi. - When this happens, the bitset returned in \code{*i} has this member - set. In addition, the option value is modified to reflect the - actual (rounded) value that was used by the backend. Note that - inexact values are admissible for strings as well. A backend may - choose to ``round'' a string to the closest matching legal string - for a constrained string value. \\ - - \code{\defn{SANE\_INFO\_RELOAD\_OPTIONS}} & 2 & The setting of an - option may affect the value or availability of one or more {\em - other\/} options. When this happens, the SANE backend sets this - member in \code{*i} to indicate that the application should reload - all options. This member may be set if and only if at least one - option changed. \\ - -\code{\defn{SANE\_INFO\_RELOAD\_PARAMS}} & 4 & The setting of an option may - affect the parameter values (see \code{sane\_get\_parameters()}). - If setting an option affects the parameter values, this member will - be set in \code{*i}. Note that this member may be set even if the - parameters did not actually change. However, it is guaranteed that - the parameters never change without this member being set. \\ - -\hline - \end{tabular} - \caption{Additional Information Returned When Setting an Option} - \label{tab:info} - \end{center} -\end{table} - -This function may fail with one of the following status codes. -\begin{quote} -\begin{description} -\item[\code{SANE\_STATUS\_UNSUPPORTED}:] The operation is not - supported for the specified handle and option number. -\item[\code{SANE\_STATUS\_INVAL}:] The option value is not valid. -\item[\code{SANE\_STATUS\_IO\_ERROR}:] An error occured while - communicating with the device. -\item[\code{SANE\_STATUS\_NO\_MEM}:] An insufficent amount of memory - is available. -\item[\code{SANE\_STATUS\_ACCESS\_DENIED}:] Access to the option has - been denied due to insufficient or invalid authentication. -\end{description} -\end{quote} - - - -\subsection{\code{sane\_get\_parameters}} - -This function is used to obtain the current scan parameters. The -returned parameters are guaranteed to be accurate between the time a -scan has been started (\code{sane\_start()} has been called) and the -completion of that request. Outside of that window, the returned -values are best-effort estimates of what the parameters will be when -\code{sane\_start()} gets invoked. Calling this function before a -scan has actually started allows, for example, to get an estimate of -how big the scanned image will be. The parameters passed to this -function are the handle \code{h} of the device for which the -parameters should be obtained and a pointer \code{p} to a parameter -structure. The parameter structure is described in more detail below. - -\begin{quote}\index{sane\_get\_parameters} -\begin{verbatim} -SANE_Status sane_get_parameters (SANE_Handle h, - SANE_Parameters * p); -\end{verbatim} -\end{quote} - -The scan parameters are returned in a structure of type -\code{\defn{SANE\_Parameters}}. The C declaration of this structure -is given below. -\begin{quote} -\begin{verbatim} -typedef struct - { - SANE_Frame format; - SANE_Bool last_frame; - SANE_Int bytes_per_line; - SANE_Int pixels_per_line; - SANE_Int lines; - SANE_Int depth; - } -SANE_Parameters; -\end{verbatim} -\end{quote} - -Member \code{format} specifies the format of the next frame to be -returned. The possible values for type \code{\defn{SANE\_Frame}} are -described in Table~\ref{tab:frameformat}. The meaning of these -values is described in more detail in Section~\ref{sec:imageformat}. -\begin{table}[htbp] - \begin{center} - \leavevmode - \begin{tabular}{|l|r|l|} -\hline -\multicolumn{1}{|c|}{\bf Symbol} & -\multicolumn{1}{|c|}{\bf Code} & -\multicolumn{1}{|c|}{\bf Description} \\ - -\hline\hline - -\code{\defn{SANE\_FRAME\_GRAY}} & 0 & Band covering human visual range. \\ -\code{\defn{SANE\_FRAME\_RGB}} & 1 & Pixel-interleaved red/green/blue bands. \\ -\code{\defn{SANE\_FRAME\_RED}} & 2 & Red band of a red/green/blue image. \\ -\code{\defn{SANE\_FRAME\_GREEN}} & 3 & Green band of a red/green/blue image. \\ -\code{\defn{SANE\_FRAME\_BLUE}} & 4 & Blue band of a red/green/blue image. \\ - -\hline - \end{tabular} - \caption{Frame Format (\code{SANE\_Frame})} - \label{tab:frameformat} - \end{center} -\end{table} - -Member \code{last\_frame} is set to \code{SANE\_TRUE} if and only if -the frame that is currently being acquired (or the frame that will be -acquired next if there is no current frame) is the last frame of a -multi frame image (e.g., the current frame is the blue component of a -red, green, blue image). - -Member \code{lines} specifies how many scan lines the frame is -comprised of. If this value is -1, the number of lines is not known a -priori and the frontend should call \code{sane\_read()} until it -returns a status of \code{SANE\_STATUS\_EOF}. - -Member \code{bytes\_per\_line} specifies the number of bytes that -comprise one scan line. - -Member \code{depth} specifies the number of bits per sample. - -Member \code{pixels\_per\_line} specifies the number of pixels that -comprise one scan line. - -Assume $B$ is the number of channels in the frame, then the bit depth -$d$ (as given by member \code{depth}) and the number of pixels per -line $n$ (as given by this member \code{pixels\_per\_line}) are -related to $c$, the number of bytes per line (as given by member -\code{bytes\_per\_line}) as follows: -\[ - c >= \left\{ - \begin{array}{ll} - B\cdot \lfloor (n + 7) / 8\rfloor & \mbox{if $d=1$}\\ - B\cdot n \cdot d / 8 & \mbox{if $d>1$} - \end{array} - \right. -\] -Note that the number of bytes per line can be larger than the minimum -value imposed by the right side of this equation. A frontend must be -able to properly cope with such ``padded'' image formats. - - -\subsection{\code{sane\_start}} - -This function initiates aquisition of an image from the device -represented by handle \code{h}. -\begin{quote}\index{sane\_start} -\begin{verbatim} -SANE_Status sane_start (SANE_Handle h); -\end{verbatim} -\end{quote} -This function may fail with one of the following status codes. -\begin{quote} -\begin{description} -\item[\code{SANE\_STATUS\_CANCELLED}:] The operation was cancelled through - a call to \code{sane\_cancel}. -\item[\code{SANE\_STATUS\_DEVICE\_BUSY}:] The device is busy. The - operation should be retried later. -\item[\code{SANE\_STATUS\_JAMMED}:] The document feeder is jammed. -\item[\code{SANE\_STATUS\_NO\_DOCS}:] The document feeder is out of - documents. -\item[\code{SANE\_STATUS\_COVER\_OPEN}:] The scanner cover is open. -\item[\code{SANE\_STATUS\_IO\_ERROR}:] An error occurred while communicating - with the device. -\item[\code{SANE\_STATUS\_NO\_MEM}:] An insufficent amount of memory - is available. -\item[\code{SANE\_STATUS\_INVAL}:] The scan cannot be started with the current - set of options. The frontend should reload the option descriptors, as if - \code{\defn{SANE\_INFO\_RELOAD\_OPTIONS}} had been returned from a call to - \code{sane\_control\_option()}, since the device's capabilities may have - changed. -\end{description} -\end{quote} - - -\subsection{\code{sane\_read}} - -This function is used to read image data from the device represented -by handle \code{h}. Argument \code{buf} is a pointer to a memory area -that is at least \code{maxlen} bytes long. The number of bytes -returned is stored in \code{*len}. A backend must set this to zero -when a status other than \code{SANE\_STA\-TUS\_GOOD} is returned. -When the call succeeds, the number of bytes returned can be anywhere in -the range from 0 to \code{maxlen} bytes. -\begin{quote}\index{sane\_read} -\begin{verbatim} -SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, - SANE_Int maxlen, SANE_Int * len); -\end{verbatim} -\end{quote} -If this function is called when no data is available, one of two -things may happen, depending on the I/O mode that is in effect for -handle \code{h}. -\begin{enumerate} -\item If the device is in blocking I/O mode (the default mode), the - call blocks until at least one data byte is available (or until some - error occurs). - -\item If the device is in non-blocking I/O mode, the call returns - immediately with status \code{SANE\_STA\-TUS\_GOOD} and with - \code{*len} set to zero. -\end{enumerate} -The I/O mode of handle \code{h} can be set via a call to -\code{sane\_set\_io\_mode()}. - -This function may fail with one of the following status codes. -\begin{quote} -\begin{description} -\item[\code{SANE\_STATUS\_CANCELLED}:] The operation was cancelled through - a call to \code{sane\_cancel}. -\item[\code{SANE\_STATUS\_EOF}:] No more data is available for the - current frame. -\item[\code{SANE\_STATUS\_JAMMED}:] The document feeder is jammed. -\item[\code{SANE\_STATUS\_NO\_DOCS}:] The document feeder is out of - documents. -\item[\code{SANE\_STATUS\_COVER\_OPEN}:] The scanner cover is open. -\item[\code{SANE\_STATUS\_IO\_ERROR}:] An error occurred while communicating - with the device. -\item[\code{SANE\_STATUS\_NO\_MEM}:] An insufficent amount of memory - is available. -\item[\code{SANE\_STATUS\_ACCESS\_DENIED}:] Access to the device has - been denied due to insufficient or invalid authentication. -\end{description} -\end{quote} - - -\subsection{\code{sane\_cancel}} - -This function is used to immediately or as quickly as possible cancel -the currently pending operation of the device represented by handle -\code{h}. -\begin{quote}\index{sane\_cancel} -\begin{verbatim} -void sane_cancel (SANE_Handle h); -\end{verbatim} -\end{quote} -This function can be called at any time (as long as handle \code{h} is -a valid handle) but usually affects long-running operations only (such -as image is acquisition). It is safe to call this function -asynchronously (e.g., from within a signal handler). It is important -to note that completion of this operaton does {\em not\/} imply that -the currently pending operation has been cancelled. It only -guarantees that cancellation has been {\em initiated}. Cancellation -completes only when the cancelled call returns (typically with a -status value of \code{SANE\_STATUS\_CANCELLED}). Since the SANE API -does not require any other operations to be re-entrant, this implies -that a frontend must {\em not\/} call any other operation until the -cancelled operation has returned. - - -\subsection{\code{sane\_set\_io\_mode}} - -This function is used to set the I/O mode of handle \code{h}. The I/O mode -can be either blocking or non-blocking. If argument \code{m} is -\code{SANE\_TRUE}, the mode is set to non-blocking mode, otherwise it's set to -blocking mode. This function can be called only after a call to -\code{sane\_start()} has been performed. -\begin{quote}\index{sane\_set\_io\_mode} -\begin{verbatim} -SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m); -\end{verbatim} -\end{quote} -By default, newly opened handles operate in blocking mode. A backend -may elect not to support non-blocking I/O mode. In such a case the -status value \code{SANE\_STATUS\_UNSUPPORTED} is returned. Blocking -I/O must be supported by all backends, so calling this function with -argument \code{m} set to \code{SANE\_FALSE} is guaranteed to complete -successfully. - -This function may fail with one of the following status codes: -\begin{quote} -\begin{description} -\item[\code{SANE\_STATUS\_INVAL}:] No image acquisition is pending. -\item[\code{SANE\_STATUS\_UNSUPPORTED}:] The backend does not support - the requested I/O mode. -\end{description} -\end{quote} - - -\subsection{\code{sane\_get\_select\_fd}} - -This function is used to obtain a (platform-specific) file-descriptor -for handle \code{h} that is readable if and only if image data is -available (i.e., when a call to \code{sane\_read()} will return at -least one byte of data). If the call completes successfully, the -select file-descriptor is returned in \code{*fd}. -\begin{quote}\index{sane\_get\_select\_fd} -\begin{verbatim} -SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int *fd); -\end{verbatim} -\end{quote} -This function can be called only after a call to \code{sane\_start()} -has been performed and the returned file-descriptor is guaranteed to -remain valid for the duration of the current image acquisition (i.e., -until \code{sane\_cancel()} or \code{sane\_start()} get called again -or until \code{sane\_read()} returns with status -\code{SANE\_STA\-TUS\_EOF}). Indeed, a backend must guarantee to -close the returned select file descriptor at the point when the next -\code{sane\_read()} call would return \code{SANE\_STA\-TUS\_EOF}. -This is necessary to ensure the application can detect when this -condition occurs without actually having to call \code{sane\_read()}. - -A backend may elect not to support this operation. In such a case, -the function returns with status code -\code{SANE\_STATUS\_UNSUPPORTED}. - -Note that the only operation supported by the returned file-descriptor -is a host operating-system dependent test whether the file-descriptor -is readable (e.g., this test can be implemented using \code{select()} -or \code{poll()} under UNIX). If any other operation is performed on -the file descriptor, the behavior of the backend becomes -unpredictable. Once the file-descriptor signals ``readable'' status, -it will remain in that state until a call to \code{sane\_read()} is -performed. Since many input devices are very slow, support for this -operation is strongly encouraged as it permits an application to do -other work while image acquisition is in progress. - -This function may fail with one of the following status codes: -\begin{quote} -\begin{description} -\item[\code{SANE\_STATUS\_INVAL}:] No image acquisition is pending. -\item[\code{SANE\_STATUS\_UNSUPPORTED}:] The backend does not support - this operation. -\end{description} -\end{quote} - - -\subsection{\code{sane\_strstatus}} - -This function can be used to translate a SANE status code into a -printable string. The returned string is a single line of text that -forms a complete sentence, but without the trailing period -(full-stop). The function is guaranteed to never return \code{NULL}. -The returned pointer is valid at least until the next call to this -function is performed. -\begin{quote}\index{sane\_strstatus} -\begin{verbatim} -const SANE_String_Const sane_strstatus (SANE_Status status); -\end{verbatim} -\end{quote} - -\section{Code Flow}\index{code flow} - -The code flow for the SANE API is illustrated in -Figure~\ref{fig:flow}. Functions \code{sane\_init()} and -\code{sane\_exit()} initialize and exit the backend, respectively. -All other calls must be performed after initialization and before -exiting the backend. - -\begin{figure}[htb] - \begin{center} - \leavevmode - \includegraphics[height=0.5\textheight]{figs/flow} - \caption{Code flow} - \label{fig:flow} - \end{center} -\end{figure} - -Function \code{sane\_get\_devices()} can be called any time after -\code{sane\_init()} has been called. It returns the list of the -devices that are known at the time of the call. This list may change -over time since some devices may be turned on or off or a remote host -may boot or shutdown between different calls. It should be noted that -this operation may be relatively slow since it requires contacting all -configured devices (some of which may be on remote hosts). A frontend -may therefore want to provide the ability for a user to directly -select a desired device without requiring a call to this function. - -Once a device has been chosen, it is opened using a call to -\code{sane\_open()}. Multiple devices can be open at any given time. -A SANE backend must not impose artificial constraints on how many -devices can be open at any given time. - -An opened device can be setup through the corresponding device handle -using functions \code{sane\_get\_opt\-ion\_desc\-riptor()} and -\code{sane\_control\_option()}. While setting up a device, obtaining -option descriptors and setting and reading of option values can be -mixed freely. It is typical for a frontend to read out all available -options at the beginning and then build a dialog (either graphical or -a command-line oriented option list) that allows to control the -available options. It should be noted that the number of options is -fixed for a given handle. However, as options are set, other options -may become active or inactive. Thus, after setting an option, it -maybe necessary to re-read some or all option descriptors. While -setting up the device, it is also admissible to call -\code{sane\_get\_parameters()} to get an estimate of what the image -parameters will look like once image acquisition begins. - -The device handle can be put in blocking or non-blocking mode by a -call to \code{sane\_set\_io\_mode()}. Devices are required to support -blocking mode (which is the default mode), but support for -non-blocking I/O is strongly encouraged for operating systems such as -UNIX. - -After the device is setup properly, image acquisition can be started -by a call to \code{sane\_start()}. The backend calculates the exact -image parameters at this point. So future calls to -\code{sane\_get\_parameters()} will return the exact values, rather -than estimates. Whether the physical image acquisition starts at this -point or during the first call to \code{sane\_read()} is unspecified -by the SANE API. If non-blocking I/O and/or a select-style interface -is desired, the frontend may attempt to call -\code{sane\_set\_io\_mode()} and/or \code{sane\_get\_select\_fd()} at -this point. Either of these functions may fail if the backend does -not support the requested operation. - -Image data is collected by repeatedly calling \code{sane\_read()}. -Eventually, this function will return an end-of-file status -(\code{SANE\_STATUS\_EOF}). This indicates the end of the current -frame. If the frontend expects additional frames (e.g., the -individual channels in of a red/green/blue image or multiple images), -it can call \code{sane\_start()} again. Once all desired frames have -been acquired, function \code{sane\_cancel()} must be called. This -operation can also be called at any other time to cancel a pending -operation. Note that \code{sane\_cancel()} must be called even if the -last read operation returned \code{SANE\_STATUS\_EOF}. - -When done using the device, the handle should be closed by a call to -\code{sane\_close()}. Finally, before exiting the application, -function \code{sane\_exit()} must be called. It is important not to -forget to call this function since otherwise some resources (e.g., -temporary files or locks) may remain unclaimed. - - -\section{Well-Known Options}\index{well-known options} - -While most backend options are completely self-describing, there are a -cases where a user interface might want to special-case the handling -of certain options. For example, the scan area is typically defined -by four options that specify the top-left and bottom-right corners of -the area. With a graphical user interface, it would be tedious to -force the user to type in these four numbers. Instead, most such -interfaces will want to present to the user a preview (low-resolution -scan) of the scanner surface and let the user pick the scan area by -dragging a rectangle into the desired position. For this reason, the -SANE API specifies a small number of option names that have -well-defined meanings. - -\subsection{Option Number Count}\index{option count} - -Option number 0 has an empty string as its name. The value of this -option is of type \code{SANE\_TYPE\_INT} and it specifies the total -number of options available for a given device (the count includes -option number 0). This means that there are two ways of counting the -number of options available: a frontend can either cycle through all -option numbers starting at one until -\code{sane\_get\_option\_descriptor()} returns \code{NULL}, or a -frontend can directly read out the value of option number 0. - -\subsection{Scan Resolution Option}\index{scan resolution}\index{resolution option} - -Option \code{resolution} is used to select the resolution at which an -image should be acquired. The type of this option is either -\code{SANE\_TYPE\_INT} or \code{SANE\_TYPE\_FIXED}. The unit is -\code{SANE\_UNIT\_DPI} (dots/inch). - -This option is not mandatory, but if a backend does support it, it -must implement it in a manner consistent with the above definition. - -\subsection{Preview Mode Option}\index{preview mode} - -The boolean option \code{preview} is used by a frontend to inform the -backend when image acquisition should be optimized for speed, rather -than quality (``preview mode''). When set to \code{SANE\_TRUE}, -preview mode is in effect, when set to \code{SANE\_FALSE} image -acquisition should proceed in normal quality mode. The setting of -this option \emph{must not\/} affect any other option. That is, as -far as the other options are concerned, the preview mode is completely -side effect free. A backend can assume that the frontend will take -care of appropriately setting the scan resolution for preview mode -(through option \code{resolution}). A backend is free to override the -\code{resolution} value with its own choice for preview mode, but it -is advised to leave this choice to the frontend wherever possible. - -This option is not mandatory, but if a backend does support it, it -must implement it in a manner consistent with the above definition. - -\subsection{Scan Area Options}\index{scan area options} - -The four most important well-known options are the ones that define -the scan area. The scan area is defined by two points (x/y coordinate -pairs) that specify the top-left and the bottom-right corners. This -is illustrated in Figure~\ref{fig:area}. Note that the origin of the -coordinate system is at the top-left corner of the scan surface as -seen by the sensor (which typically is a mirror image of the scan -surface seen by the user). For this reason, the top-left corner is -the corner for which the abscissa and ordinate values are -simultaneously the {\em smallest} and the bottom-right corner is the -corner for which the abscissa and ordinate values are simulatenously -the {\em largest}. If this coordinate system is not natural for a -given device, it is the job of the backend to perform the necessary -conversions. -\begin{figure}[tbp] - \begin{center} - \leavevmode - \includegraphics[height=0.3\textheight]{figs/area} - \caption{Scan area options} - \label{fig:area} - \end{center} -\end{figure} - -The names of the four options that define the scan area are given in -the table below: -\begin{center} -\begin{tabular}{ll} -{\bf Name} & {\bf Description} \\ -\code{\defn{tl-x}} & Top-left $x$ coordinate value \\ -\code{\defn{tl-y}} & Top-left $y$ coordinate value \\ -\code{\defn{br-x}} & Bottom-right $x$ coordinate value \\ -\code{\defn{br-y}} & Bottom-right $y$ coordinate value \\ -\end{tabular} -\end{center} -There are several rules that should be followed by front and backends -regarding these options: -\begin{itemize} - -\item Backends must attach a unit of either pixels - (\code{SANE\_UNIT\_PIXEL}) or millimeters (\code{SANE\_UNIT\_MM}) to - these options. The unit of all four options must be identical. - -\item Whenever meaningful, a backend should attach a range or a - word-list constraint to these options. - -\item A frontend can determine the size of the scan surface by first - checking that the options have range constraints associated. If a - range or word-list constraints exist, the frontend can take the - minimum and maximum values of one of the x and y option - range-constraints to determine the scan surface size. - -\item A frontend must work properly with any or all of these options - missing. - -\end{itemize} - -\input{net.tex} - -\chapter{Contact Information}\label{chap:contact} - -The SANE standard is discussed and evolved via a mailing list. -Anybody with email access to the Internet can automatically join and -leave the discussion group by sending mail to the following address. -\begin{quote}\index{mailing list} -\url{sane-devel-request@alioth-lists.debian.net} -\end{quote} -To subscribe, send a mail with the body ``\verb|subscribe sane-devel|'' to the -above address. - -A complete list of commands supported can be obtained by sending a -mail with a subject of ``\code{help}'' to the above address. The -mailing list is archived and available through the SANE home page at -URL: -\begin{quote} -\url{http://www.sane-project.org/} -\end{quote} - -\newpage -\input{sane.ind} - -\end{document} diff --git a/doc/scanimage.man b/doc/scanimage.man index 83624a4f9..b439c4571 100644 --- a/doc/scanimage.man +++ b/doc/scanimage.man @@ -44,7 +44,7 @@ normally proceeds to acquire an image. The image data is written to standard output in one of the PNM (portable aNyMaP) formats (PBM for black-and-white images, PGM for grayscale images, and PPM for color images), TIFF format (black-and-white, grayscale or color), PNG format, -or JPEG format. +or JPEG format (compression level 75). .B scanimage accesses image acquisition devices through the .B SANE diff --git a/frontend/Makefile.am b/frontend/Makefile.am index 525953f6b..d27acf280 100644 --- a/frontend/Makefile.am +++ b/frontend/Makefile.am @@ -21,6 +21,7 @@ scanimage_LDADD = ../backend/libsane.la ../sanei/libsanei.la ../lib/liblib.la \ $(PNG_LIBS) $(JPEG_LIBS) saned_SOURCES = saned.c +saned_CPPFLAGS = $(AM_CPPFLAGS) $(AVAHI_CFLAGS) saned_LDADD = ../backend/libsane.la ../sanei/libsanei.la ../lib/liblib.la \ $(SYSLOG_LIBS) $(SYSTEMD_LIBS) $(AVAHI_LIBS) diff --git a/frontend/saned.c b/frontend/saned.c index 6c3c40ddc..03175428c 100644 --- a/frontend/saned.c +++ b/frontend/saned.c @@ -155,7 +155,7 @@ poll (struct pollfd *ufds, unsigned int nfds, int timeout) } #endif /* HAVE_SYS_POLL_H && HAVE_POLL */ -#ifdef WITH_AVAHI +#if WITH_AVAHI # include # include @@ -2286,7 +2286,7 @@ wait_child (pid_t pid, int *status, int options) if (ret <= 0) return ret; -#ifdef WITH_AVAHI +#if WITH_AVAHI if ((avahi_pid > 0) && (ret == avahi_pid)) { avahi_pid = -1; @@ -2428,7 +2428,7 @@ bail_out (int error) { DBG (DBG_ERR, "%sbailing out, waiting for children...\n", (error) ? "FATAL ERROR; " : ""); -#ifdef WITH_AVAHI +#if WITH_AVAHI if (avahi_pid > 0) kill (avahi_pid, SIGTERM); #endif /* WITH_AVAHI */ @@ -2457,7 +2457,7 @@ sig_int_term_handler (int signum) } -#ifdef WITH_AVAHI +#if WITH_AVAHI static void saned_avahi (struct pollfd *fds, int nfds); @@ -3221,7 +3221,7 @@ run_standalone (char *user) if (user) runas_user(user); -#ifdef WITH_AVAHI +#if WITH_AVAHI DBG (DBG_INFO, "run_standalone: spawning Avahi process\n"); saned_avahi (fds, nfds); diff --git a/frontend/scanimage.c b/frontend/scanimage.c index ae65ebfd9..3902092f1 100644 --- a/frontend/scanimage.c +++ b/frontend/scanimage.c @@ -369,13 +369,13 @@ print_option (SANE_Device * device, int opt_num, const SANE_Option_Descriptor *o } /* if both of these are set, option is invalid */ - if(opt->cap & SANE_CAP_SOFT_SELECT && opt->cap & SANE_CAP_HARD_SELECT){ + if((opt->cap & SANE_CAP_SOFT_SELECT) && (opt->cap & SANE_CAP_HARD_SELECT)){ fprintf (stderr, "%s: invalid option caps, SS+HS\n", prog_name); return; } /* invalid to select but not detect */ - if(opt->cap & SANE_CAP_SOFT_SELECT && !(opt->cap & SANE_CAP_SOFT_DETECT)){ + if((opt->cap & SANE_CAP_SOFT_SELECT) && !(opt->cap & SANE_CAP_SOFT_DETECT)){ fprintf (stderr, "%s: invalid option caps, SS!SD\n", prog_name); return; } @@ -609,7 +609,7 @@ print_option (SANE_Device * device, int opt_num, const SANE_Option_Descriptor *o else if(opt->cap & SANE_CAP_HARD_SELECT) fputs (" [hardware]", stdout); - else if(!(opt->cap & SANE_CAP_SOFT_SELECT) && opt->cap & SANE_CAP_SOFT_DETECT) + else if(!(opt->cap & SANE_CAP_SOFT_SELECT) && (opt->cap & SANE_CAP_SOFT_DETECT)) fputs (" [read-only]", stdout); fputs ("\n ", stdout); @@ -987,7 +987,15 @@ set_option (SANE_Handle device, int optnum, void *valuep) SANE_Int info = 0; opt = sane_get_option_descriptor (device, optnum); - if (opt && (!SANE_OPTION_IS_ACTIVE (opt->cap))) + if (!opt) + { + if (verbose > 0) + fprintf (stderr, "%s: ignored request to set invalid option %d\n", + prog_name, optnum); + return; + } + + if (!SANE_OPTION_IS_ACTIVE (opt->cap)) { if (verbose > 0) fprintf (stderr, "%s: ignored request to set inactive option %s\n", @@ -1229,7 +1237,9 @@ write_png_header (SANE_Frame format, int width, int height, int depth, int dpi, #ifdef HAVE_LIBJPEG static void -write_jpeg_header (SANE_Frame format, int width, int height, int dpi, FILE *ofp, struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr) +write_jpeg_header (SANE_Frame format, int width, int height, int dpi, FILE *ofp, + struct jpeg_compress_struct *cinfo, + struct jpeg_error_mgr *jerr) { cinfo->err = jpeg_std_error(jerr); jpeg_create_compress(cinfo); @@ -2599,7 +2609,7 @@ List of available devices:", prog_name); ofp = fopen(output_file, "w"); if (ofp == NULL) { - fprintf(stderr, "%s: could not open input file '%s', " + fprintf(stderr, "%s: could not open output file '%s', " "exiting\n", prog_name, output_file); scanimage_exit(1); } diff --git a/include/sane/sanei.h b/include/sane/sanei.h index aa7003ad1..7513d2299 100644 --- a/include/sane/sanei.h +++ b/include/sane/sanei.h @@ -65,9 +65,7 @@ * This documentation is far from complete. Any help is appreciated. * * @section additional Additional documentation - * - The SANE standard can be found at the SANE webserver, - * though the PostScript version produced from the source may be more recent. + * - The SANE Standard. * - Information on how to write a backend: backend-writing.txt. * - General SANE documentation is on fd) && (NELEMS (port) <= fd)) + if ((0 > fd) || (NELEMS (port) <= fd)) return; if (!p->in_use) @@ -515,7 +515,7 @@ sanei_pio_close (int fd) int sanei_pio_read (int fd, u_char * buf, int n) { - if ((0 > fd) && (NELEMS (port) <= fd)) + if ((0 > fd) || (NELEMS (port) <= fd)) return -1; if (!port[fd].in_use) @@ -527,7 +527,7 @@ sanei_pio_read (int fd, u_char * buf, int n) int sanei_pio_write (int fd, const u_char * buf, int n) { - if ((0 > fd) && (NELEMS (port) <= fd)) + if ((0 > fd) || (NELEMS (port) <= fd)) return -1; if (!port[fd].in_use) diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c index db0f452ae..8e8185a8f 100644 --- a/sanei/sanei_usb.c +++ b/sanei/sanei_usb.c @@ -195,15 +195,16 @@ static sanei_usb_testing_mode testing_mode = sanei_usb_testing_mode_disabled; #if WITH_USB_RECORD_REPLAY static int testing_development_mode = 0; -int testing_known_commands_input_failed = 0; -unsigned testing_last_known_seq = 0; -SANE_String testing_record_backend = NULL; -xmlNode* testing_append_commands_node = NULL; +static int testing_already_opened = 0; +static int testing_known_commands_input_failed = 0; +static unsigned testing_last_known_seq = 0; +static SANE_String testing_record_backend = NULL; +static xmlNode* testing_append_commands_node = NULL; // XML file from which we read testing data -SANE_String testing_xml_path = NULL; -xmlDoc* testing_xml_doc = NULL; -xmlNode* testing_xml_next_tx_node = NULL; +static SANE_String testing_xml_path = NULL; +static xmlDoc* testing_xml_doc = NULL; +static xmlNode* testing_xml_next_tx_node = NULL; #endif // WITH_USB_RECORD_REPLAY #if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) @@ -587,9 +588,8 @@ static void sanei_xml_print_seq_if_any(xmlNode* node, const char* parent_fun) xmlFree(attr); } -// Checks whether transaction should be ignored. We ignore get_descriptor and -// set_configuration transactions. The latter is ignored because -// set_configuration is called in sanei_usb_open outside test path. +// Checks whether transaction should be ignored. We ignore set_configuration +// transactions, because set_configuration is called in sanei_usb_open outside test path. static int sanei_xml_is_transaction_ignored(xmlNode* node) { if (xmlStrcmp(node->name, (const xmlChar*)"control_tx") != 0) @@ -627,7 +627,8 @@ static int sanei_xml_is_transaction_ignored(xmlNode* node) static xmlNode* sanei_xml_skip_non_tx_nodes(xmlNode* node) { const char* known_node_names[] = { - "control_tx", "bulk_tx", "interrupt_tx", "debug", "known_commands_end" + "control_tx", "bulk_tx", "interrupt_tx", + "get_descriptor", "debug", "known_commands_end" }; while (node != NULL) @@ -660,13 +661,11 @@ static int sanei_xml_is_known_commands_end(xmlNode* node) return xmlStrcmp(node->name, (const xmlChar*)"known_commands_end") == 0; } -// returns next transaction node that is not get_descriptor static xmlNode* sanei_xml_peek_next_tx_node() { return testing_xml_next_tx_node; } -// returns next transaction node that is not get_descriptor static xmlNode* sanei_xml_get_next_tx_node() { xmlNode* next = testing_xml_next_tx_node; @@ -1200,7 +1199,7 @@ static SANE_Status sanei_usb_testing_init() memset(&device, 0, sizeof(device)); device.devname = strdup(testing_xml_path); - // other code shouldn't depend on methon because testing_mode is + // other code shouldn't depend on method because testing_mode is // sanei_usb_testing_mode_replay device.method = sanei_usb_method_libusb; device.vendor = device_id; @@ -1303,6 +1302,18 @@ static void sanei_usb_testing_exit() xmlFreeDoc(testing_xml_doc); free(testing_xml_path); xmlCleanupParser(); + + // reset testing-related all data to initial values + testing_development_mode = 0; + testing_already_opened = 0; + testing_known_commands_input_failed = 0; + testing_last_known_seq = 0; + testing_record_backend = NULL; + testing_append_commands_node = NULL; + + testing_xml_path = NULL; + testing_xml_doc = NULL; + testing_xml_next_tx_node = NULL; } #else // WITH_USB_RECORD_REPLAY SANE_Status sanei_usb_testing_enable_replay(SANE_String_Const path, @@ -1848,6 +1859,11 @@ static void libusb_scan_devices(void) continue; } +/* macOS won't configure several USB scanners (i.e. ScanSnap 300M) because their + * descriptors are vendor specific. As a result the device will get configured + * later during sanei_usb_open making it safe to ignore the configuration check + * here on these platforms. */ +#if !defined(__APPLE__) if (config == 0) { DBG (1, @@ -1855,6 +1871,7 @@ static void libusb_scan_devices(void) vid, pid, busno, address); continue; } +#endif ret = libusb_get_config_descriptor (dev, 0, &config0); if (ret < 0) @@ -2288,22 +2305,43 @@ sanei_usb_get_endpoint (SANE_Int dn, SANE_Int ep_type) } #if WITH_USB_RECORD_REPLAY +static void sanei_xml_indent_child(xmlNode* parent, unsigned indent_count) +{ + indent_count *= 4; + + xmlChar* indent_str = malloc(indent_count + 2); + indent_str[0] = '\n'; + memset(indent_str + 1, ' ', indent_count); + indent_str[indent_count + 1] = '\0'; + + xmlAddChild(parent, xmlNewText(indent_str)); + free(indent_str); +} + static void sanei_usb_record_open(SANE_Int dn) { + if (testing_already_opened) + return; + xmlNode* e_root = xmlNewNode(NULL, (const xmlChar*) "device_capture"); xmlDocSetRootElement(testing_xml_doc, e_root); xmlNewProp(e_root, (const xmlChar*)"backend", (const xmlChar*) testing_record_backend); + sanei_xml_indent_child(e_root, 1); xmlNode* e_description = xmlNewChild(e_root, NULL, (const xmlChar*) "description", NULL); sanei_xml_set_hex_attr(e_description, "id_vendor", devices[dn].vendor); sanei_xml_set_hex_attr(e_description, "id_product", devices[dn].product); + sanei_xml_indent_child(e_description, 2); xmlNode* e_configurations = xmlNewChild(e_description, NULL, (const xmlChar*) "configurations", NULL); + + sanei_xml_indent_child(e_configurations, 3); xmlNode* e_configuration = xmlNewChild(e_configurations, NULL, (const xmlChar*) "configuration", NULL); sanei_xml_set_uint_attr(e_configuration, "number", 1); + sanei_xml_indent_child(e_configuration, 4); xmlNode* e_interface = xmlNewChild(e_configuration, NULL, (const xmlChar*) "interface", NULL); sanei_xml_set_uint_attr(e_interface, "number", devices[dn].interface_nr); @@ -2329,6 +2367,7 @@ static void sanei_usb_record_open(SANE_Int dn) { if (endpoints[i].ep_address) { + sanei_xml_indent_child(e_interface, 5); xmlNode* e_endpoint = xmlNewChild(e_interface, NULL, (const xmlChar*)"endpoint", NULL); xmlNewProp(e_endpoint, (const xmlChar*)"transfer_type", (const xmlChar*) endpoints[i].transfer_type); @@ -2338,10 +2377,17 @@ static void sanei_usb_record_open(SANE_Int dn) sanei_xml_set_hex_attr(e_endpoint, "address", endpoints[i].ep_address); } } + sanei_xml_indent_child(e_interface, 4); + sanei_xml_indent_child(e_configuration, 3); + sanei_xml_indent_child(e_configurations, 2); + sanei_xml_indent_child(e_description, 1); + + sanei_xml_indent_child(e_root, 1); xmlNode* e_transactions = xmlNewChild(e_root, NULL, (const xmlChar*)"transactions", NULL); // add an empty node so that we have something to append to - testing_append_commands_node = xmlAddChild(e_transactions, xmlNewText((const xmlChar*)""));; + testing_append_commands_node = xmlAddChild(e_transactions, xmlNewText((const xmlChar*)"")); + testing_already_opened = 1; } #endif // WITH_USB_RECORD_REPLAY @@ -2574,11 +2620,17 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) return SANE_STATUS_INVAL; } +/* macOS won't configure several USB scanners (i.e. ScanSnap 300M) because their + * descriptors are vendor specific. As a result the device will get configured + * later during sanei_usb_open making it safe to ignore the configuration check + * here on these platforms. */ +#if !defined(__APPLE__) if (config == 0) { DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname); return SANE_STATUS_INVAL; } +#endif result = libusb_get_device_descriptor (dev, &desc); if (result < 0) @@ -3851,7 +3903,7 @@ sanei_usb_replay_control_msg(SANE_Int dn, SANE_Int rtype, SANE_Int req, (void) dn; if (testing_known_commands_input_failed) - return -1; + return SANE_STATUS_IO_ERROR; xmlNode* node = sanei_xml_get_next_tx_node(); if (node == NULL) @@ -4184,6 +4236,11 @@ static int sanei_usb_replay_read_int(SANE_Int dn, SANE_Byte* buffer, return -1; } + if (sanei_usb_check_attr(node, "error", "timeout", __func__)) + { + return -1; + } + size_t tx_data_size = 0; char* tx_data = sanei_xml_get_hex_data(node, &tx_data_size); @@ -4723,14 +4780,66 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate) } } +#if WITH_USB_RECORD_REPLAY + static SANE_Status sanei_usb_replay_get_descriptor(SANE_Int dn, struct sanei_usb_dev_descriptor *desc) { (void) dn; - (void) desc; - return SANE_STATUS_UNSUPPORTED; - // ZZTODO + + if (testing_known_commands_input_failed) + return SANE_STATUS_IO_ERROR; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return SANE_STATUS_IO_ERROR; + } + + if (sanei_xml_is_known_commands_end(node)) + { + testing_known_commands_input_failed = 1; + return SANE_STATUS_IO_ERROR; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"get_descriptor") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + testing_known_commands_input_failed = 1; + return SANE_STATUS_IO_ERROR; + } + + int desc_type = sanei_xml_get_prop_uint(node, "descriptor_type"); + int bcd_usb = sanei_xml_get_prop_uint(node, "bcd_usb"); + int bcd_dev = sanei_xml_get_prop_uint(node, "bcd_device"); + int dev_class = sanei_xml_get_prop_uint(node, "device_class"); + int dev_sub_class = sanei_xml_get_prop_uint(node, "device_sub_class"); + int dev_protocol = sanei_xml_get_prop_uint(node, "device_protocol"); + int max_packet_size = sanei_xml_get_prop_uint(node, "max_packet_size"); + + if (desc_type < 0 || bcd_usb < 0 || bcd_dev < 0 || dev_class < 0 || + dev_sub_class < 0 || dev_protocol < 0 || max_packet_size < 0) + { + FAIL_TEST_TX(__func__, node, "get_descriptor recorded block is missing attributes\n"); + testing_known_commands_input_failed = 1; + return SANE_STATUS_IO_ERROR; + } + + desc->desc_type = desc_type; + desc->bcd_usb = bcd_usb; + desc->bcd_dev = bcd_dev; + desc->dev_class = dev_class; + desc->dev_sub_class = dev_sub_class; + desc->dev_protocol = dev_protocol; + desc->max_packet_size = max_packet_size; + + return SANE_STATUS_GOOD; } static void @@ -4738,10 +4847,28 @@ sanei_usb_record_get_descriptor(SANE_Int dn, struct sanei_usb_dev_descriptor *desc) { (void) dn; - (void) desc; - // ZZTODO + + xmlNode* node = testing_append_commands_node; + + xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"get_descriptor"); + + xmlNewProp(e_tx, (const xmlChar*)"time_usec", (const xmlChar*)"0"); + sanei_xml_set_uint_attr(node, "seq", ++testing_last_known_seq); + + sanei_xml_set_hex_attr(e_tx, "descriptor_type", desc->desc_type); + sanei_xml_set_hex_attr(e_tx, "bcd_usb", desc->bcd_usb); + sanei_xml_set_hex_attr(e_tx, "bcd_device", desc->bcd_dev); + sanei_xml_set_hex_attr(e_tx, "device_class", desc->dev_class); + sanei_xml_set_hex_attr(e_tx, "device_sub_class", desc->dev_sub_class); + sanei_xml_set_hex_attr(e_tx, "device_protocol", desc->dev_protocol); + sanei_xml_set_hex_attr(e_tx, "max_packet_size", desc->max_packet_size); + + node = sanei_xml_append_command(node, 1, e_tx); + testing_append_commands_node = node; } +#endif // WITH_USB_RECORD_REPLAY + extern SANE_Status sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor __sane_unused__ @@ -4757,7 +4884,12 @@ sanei_usb_get_descriptor( SANE_Int dn, if (testing_mode == sanei_usb_testing_mode_replay) { +#if WITH_USB_RECORD_REPLAY return sanei_usb_replay_get_descriptor(dn, desc); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif } DBG (5, "sanei_usb_get_descriptor\n"); @@ -4808,7 +4940,12 @@ sanei_usb_get_descriptor( SANE_Int dn, if (testing_mode == sanei_usb_testing_mode_record) { +#if WITH_USB_RECORD_REPLAY sanei_usb_record_get_descriptor(dn, desc); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif } return SANE_STATUS_GOOD; diff --git a/testsuite/backend/genesys/session_config_test.cpp b/testsuite/backend/genesys/session_config_test.cpp index 72043bbf8..cc8abe267 100644 --- a/testsuite/backend/genesys/session_config_test.cpp +++ b/testsuite/backend/genesys/session_config_test.cpp @@ -47,6 +47,7 @@ struct TestConfig { std::uint16_t vendor_id = 0; std::uint16_t product_id = 0; + std::uint16_t bcd_device = 0; std::string model_name; genesys::ScanMethod method = genesys::ScanMethod::FLATBED; genesys::ScanColorMode color_mode = genesys::ScanColorMode::COLOR_SINGLE_PASS; @@ -143,7 +144,7 @@ public: auto i = find_option(name, SANE_TYPE_FIXED); int value = 0; TIE(sane_control_option(handle_, i, SANE_ACTION_GET_VALUE, &value, nullptr)); - return static_cast(SANE_UNFIX(value)); + return genesys::fixed_to_float(value); } void set_value_float(const std::string& name, float value) @@ -246,7 +247,8 @@ void run_single_test_scan(const TestConfig& config, std::stringstream& out) build_checkpoint(dev, iface, checkpoint_name, out); }; - genesys::enable_testing_mode(config.vendor_id, config.product_id, build_checkpoint_wrapper); + genesys::enable_testing_mode(config.vendor_id, config.product_id, config.bcd_device, + build_checkpoint_wrapper); SANE_Handle handle; @@ -389,35 +391,42 @@ TestResult perform_single_test(const TestConfig& config, const std::string& chec std::vector get_all_test_configs() { genesys::genesys_init_usb_device_tables(); + genesys::genesys_init_sensor_tables(); + genesys::verify_usb_device_tables(); + genesys::verify_sensor_tables(); std::vector configs; std::unordered_set model_names; for (const auto& usb_dev : *genesys::s_usb_devices) { - if (usb_dev.model.flags & GENESYS_FLAG_UNTESTED) { + + const auto& model = usb_dev.model(); + + if (genesys::has_flag(model.flags, genesys::ModelFlag::UNTESTED)) { continue; } - if (model_names.find(usb_dev.model.name) != model_names.end()) { + if (model_names.find(model.name) != model_names.end()) { continue; } - model_names.insert(usb_dev.model.name); + model_names.insert(model.name); for (auto scan_mode : { genesys::ScanColorMode::LINEART, genesys::ScanColorMode::GRAY, genesys::ScanColorMode::COLOR_SINGLE_PASS }) { - auto depth_values = usb_dev.model.bpp_gray_values; + auto depth_values = model.bpp_gray_values; if (scan_mode == genesys::ScanColorMode::COLOR_SINGLE_PASS) { - depth_values = usb_dev.model.bpp_color_values; + depth_values = model.bpp_color_values; } for (unsigned depth : depth_values) { - for (auto method_resolutions : usb_dev.model.resolutions) { + for (auto method_resolutions : model.resolutions) { for (auto method : method_resolutions.methods) { for (unsigned resolution : method_resolutions.get_resolutions()) { TestConfig config; - config.vendor_id = usb_dev.vendor; - config.product_id = usb_dev.product; - config.model_name = usb_dev.model.name; + config.vendor_id = usb_dev.vendor_id(); + config.product_id = usb_dev.product_id(); + config.bcd_device = usb_dev.bcd_device(); + config.model_name = model.name; config.method = method; config.depth = depth; config.resolution = resolution; diff --git a/testsuite/backend/genesys/tests_calibration.cpp b/testsuite/backend/genesys/tests_calibration.cpp index 559f8a894..8c9c8b59f 100644 --- a/testsuite/backend/genesys/tests_calibration.cpp +++ b/testsuite/backend/genesys/tests_calibration.cpp @@ -66,11 +66,9 @@ Genesys_Calibration_Cache create_fake_calibration_entry() Genesys_Sensor sensor; sensor.sensor_id = SensorId::CCD_UMAX; - sensor.optical_res = 1200; + 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 }; @@ -104,8 +102,6 @@ Genesys_Calibration_Cache create_fake_calibration_entry() sensor.gamma = {1.0, 1.0, 1.0}; calib.sensor = sensor; - calib.calib_pixels = 12345; - calib.calib_channels = 3; calib.average_size = 7; calib.white_average_data = { 8, 7, 6, 5, 4, 3, 2 }; calib.dark_average_data = { 6, 5, 4, 3, 2, 18, 12 }; diff --git a/testsuite/backend/genesys/tests_image_pipeline.cpp b/testsuite/backend/genesys/tests_image_pipeline.cpp index d4853d262..ef0ed62ea 100644 --- a/testsuite/backend/genesys/tests_image_pipeline.cpp +++ b/testsuite/backend/genesys/tests_image_pipeline.cpp @@ -42,29 +42,17 @@ void test_image_buffer_genesys_usb() requests.push_back(x); }; - FakeBufferModel model; - model.push_step(453120, 1); - model.push_step(56640, 3540); - ImageBufferGenesysUsb buffer{1086780, model, on_read_usb}; + ImageBufferGenesysUsb buffer{1086780, 453120, on_read_usb}; std::vector dummy; dummy.resize(1086780); ASSERT_TRUE(buffer.get_data(453120, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(453120, dummy.data())); + ASSERT_TRUE(buffer.get_data(180550, dummy.data())); std::vector expected = { - 453120, 56576, 56576, 56576, 56832, 56576, 56576, 56576, 56832, 56576, 56576, 56576, 11008 + 453120, 453120, 180736 }; ASSERT_EQ(requests, expected); } @@ -79,25 +67,19 @@ void test_image_buffer_genesys_usb_capped_remaining_bytes() requests.push_back(x); }; - FakeBufferModel model; - model.push_step(453120, 1); - model.push_step(56640, 3540); - ImageBufferGenesysUsb buffer{1086780, model, on_read_usb}; + ImageBufferGenesysUsb buffer{1086780, 453120, on_read_usb}; std::vector dummy; dummy.resize(1086780); ASSERT_TRUE(buffer.get_data(453120, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); - ASSERT_TRUE(buffer.get_data(56640, dummy.data())); + ASSERT_TRUE(buffer.get_data(453120, dummy.data())); buffer.set_remaining_size(10000); ASSERT_FALSE(buffer.get_data(56640, dummy.data())); std::vector expected = { // note that the sizes are rounded-up to 256 bytes - 453120, 56576, 56576, 56576, 56832, 10240 + 453120, 453120, 10240 }; ASSERT_EQ(requests, expected); } @@ -300,6 +282,109 @@ void test_node_swap_16bit_endian() ASSERT_EQ(out_data, expected_data); } +void test_node_invert_16_bits() +{ + using Data16 = std::vector; + using Data = std::vector; + + Data16 in_data = { + 0x1020, 0x3011, 0x2131, + 0x1222, 0x3213, 0x2333, + 0x1424, 0x3415, 0x2525, + 0x1626, 0x3617, 0x2737, + }; + + Data in_data_8bit; + in_data_8bit.resize(in_data.size() * 2); + std::memcpy(in_data_8bit.data(), in_data.data(), in_data_8bit.size()); + + ImagePipelineStack stack; + stack.push_first_node(4, 1, PixelFormat::RGB161616, + std::move(in_data_8bit)); + stack.push_node(); + + ASSERT_EQ(stack.get_output_width(), 4u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 24u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616); + + auto out_data_8bit = stack.get_all_data(); + Data16 out_data; + out_data.resize(out_data_8bit.size() / 2); + std::memcpy(out_data.data(), out_data_8bit.data(), out_data_8bit.size()); + + Data16 expected_data = { + 0xefdf, 0xcfee, 0xdece, + 0xeddd, 0xcdec, 0xdccc, + 0xebdb, 0xcbea, 0xdada, + 0xe9d9, 0xc9e8, 0xd8c8, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_invert_8_bits() +{ + using Data = std::vector; + + Data in_data = { + 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, + 0x12, 0x22, 0x32, 0x13, 0x23, 0x33, + 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, + 0x16, 0x26, 0x36, 0x17, 0x27, 0x37, + }; + + ImagePipelineStack stack; + stack.push_first_node(8, 1, PixelFormat::RGB888, + std::move(in_data)); + stack.push_node(); + + ASSERT_EQ(stack.get_output_width(), 8u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 24u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0xef, 0xdf, 0xcf, 0xee, 0xde, 0xce, + 0xed, 0xdd, 0xcd, 0xec, 0xdc, 0xcc, + 0xeb, 0xdb, 0xcb, 0xea, 0xda, 0xca, + 0xe9, 0xd9, 0xc9, 0xe8, 0xd8, 0xc8, + }; + + ASSERT_EQ(out_data, expected_data); +} + +void test_node_invert_1_bits() +{ + using Data = std::vector; + + Data in_data = { + 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, + 0x16, 0x26, 0x36, 0x17, 0x27, 0x37, + }; + + ImagePipelineStack stack; + stack.push_first_node(32, 1, PixelFormat::RGB111, + std::move(in_data)); + stack.push_node(); + + ASSERT_EQ(stack.get_output_width(), 32u); + ASSERT_EQ(stack.get_output_height(), 1u); + ASSERT_EQ(stack.get_output_row_bytes(), 12u); + ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB111); + + auto out_data = stack.get_all_data(); + + Data expected_data = { + 0xef, 0xdf, 0xcf, 0xee, 0xde, 0xce, + 0xe9, 0xd9, 0xc9, 0xe8, 0xd8, 0xc8, + }; + + ASSERT_EQ(out_data, expected_data); +} + void test_node_merge_mono_lines() { using Data = std::vector; @@ -445,7 +530,7 @@ void test_node_calibrate_8bit() ImagePipelineStack stack; stack.push_first_node(1, 1, PixelFormat::RGB888, std::move(in_data)); - stack.push_node(bottom, top); + stack.push_node(bottom, top, 0); ASSERT_EQ(stack.get_output_width(), 1u); ASSERT_EQ(stack.get_output_height(), 1u); @@ -481,7 +566,7 @@ void test_node_calibrate_16bit() ImagePipelineStack stack; stack.push_first_node(1, 1, PixelFormat::RGB161616, std::move(in_data)); - stack.push_node(bottom, top); + stack.push_node(bottom, top, 0); ASSERT_EQ(stack.get_output_width(), 1u); ASSERT_EQ(stack.get_output_height(), 1u); @@ -508,6 +593,9 @@ void test_image_pipeline() test_node_deinterleave_lines_i8(); test_node_deinterleave_lines_rgb888(); test_node_swap_16bit_endian(); + test_node_invert_16_bits(); + test_node_invert_8_bits(); + test_node_invert_1_bits(); test_node_merge_mono_lines(); test_node_split_mono_lines(); test_node_component_shift_lines(); diff --git a/testsuite/backend/genesys/tests_motor.cpp b/testsuite/backend/genesys/tests_motor.cpp index 07ca693a9..18a4d7ee9 100644 --- a/testsuite/backend/genesys/tests_motor.cpp +++ b/testsuite/backend/genesys/tests_motor.cpp @@ -31,100 +31,6 @@ namespace genesys { -void test_create_slope_table3() -{ - auto asic_type = AsicType::GL841; - auto max_table_size = get_slope_table_max_size(asic_type); - - Genesys_Motor motor; - motor.id = MotorId::CANON_LIDE_200; - motor.base_ydpi = 1200; - motor.optical_ydpi = 6400; - motor.slopes.push_back(MotorSlope::create_from_steps(10000, 1000, 20)); - motor.slopes.push_back(MotorSlope::create_from_steps(10000, 1000, 20)); - motor.slopes.push_back(MotorSlope::create_from_steps(10000, 1000, 16)); - - auto table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::FULL, 10000, - motor.base_ydpi); - - ASSERT_EQ(table.pixeltime_sum, 10000u); - ASSERT_EQ(table.steps_count, 1u); - - std::vector expected_steps = { - 10000, - }; - expected_steps.resize(max_table_size, 10000); - - ASSERT_EQ(table.table, expected_steps); - - table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::FULL, 2000, - motor.base_ydpi); - - ASSERT_EQ(table.pixeltime_sum, 33830u); - ASSERT_EQ(table.steps_count, 7u); - - expected_steps = { - 10000, 10000, 4099, 3028, 2511, 2192, 2000 - }; - expected_steps.resize(max_table_size, 2000); - - ASSERT_EQ(table.table, expected_steps); - - table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::HALF, 10000, - motor.base_ydpi); - - ASSERT_EQ(table.pixeltime_sum, 5000u); - ASSERT_EQ(table.steps_count, 1u); - - expected_steps = { - 5000, - }; - expected_steps.resize(max_table_size, 5000); - - - ASSERT_EQ(table.table, expected_steps); - - table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::HALF, 2000, - motor.base_ydpi); - - ASSERT_EQ(table.pixeltime_sum, 16914u); - ASSERT_EQ(table.steps_count, 7u); - - expected_steps = { - 5000, 5000, 2049, 1514, 1255, 1096, 1000 - }; - expected_steps.resize(max_table_size, 1000); - - ASSERT_EQ(table.table, expected_steps); - - table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::QUARTER, 10000, - motor.base_ydpi); - - ASSERT_EQ(table.pixeltime_sum, 2500u); - ASSERT_EQ(table.steps_count, 1u); - - expected_steps = { - 2500, - }; - expected_steps.resize(max_table_size, 2500); - - - ASSERT_EQ(table.table, expected_steps); - - table = sanei_genesys_create_slope_table3(asic_type, motor, StepType::QUARTER, 2000, - motor.base_ydpi); - - ASSERT_EQ(table.pixeltime_sum, 7680u); - ASSERT_EQ(table.steps_count, 6u); - - expected_steps = { - 2500, 2500, 932, 683, 565, 500 - }; - expected_steps.resize(max_table_size, 500); - - ASSERT_EQ(table.table, expected_steps); -} - void test_create_slope_table_small_full_step() { unsigned max_table_size = 1024; @@ -135,26 +41,24 @@ void test_create_slope_table_small_full_step() slope.max_speed_w = 2632; slope.acceleration = 1.2e-8; - auto table = create_slope_table(slope, 5000, StepType::FULL, 4, 8, max_table_size); + auto table = create_slope_table_for_speed(slope, 5000, StepType::FULL, 4, 8, max_table_size); std::vector expected_table = { - 62464, 62464, 6420, 5000 + 62464, 62464, 6420, 5000, 5000, 5000, 5000, 5000 }; - expected_table.resize(max_table_size, 5000); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 8u); - ASSERT_EQ(table.pixeltime_sum, 156348u); + ASSERT_EQ(table.table.size(), 8u); + ASSERT_EQ(table.pixeltime_sum(), 156348u); - table = create_slope_table(slope, 3000, StepType::FULL, 4, 8, max_table_size); + table = create_slope_table_for_speed(slope, 3000, StepType::FULL, 4, 8, max_table_size); expected_table = { - 62464, 62464, 6420, 4552, 3720, 3223, 3000 + 62464, 62464, 6420, 4552, 3720, 3223, 3000, 3000 }; - expected_table.resize(max_table_size, 3000); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 8u); - ASSERT_EQ(table.pixeltime_sum, 148843u); + ASSERT_EQ(table.table.size(), 8u); + ASSERT_EQ(table.pixeltime_sum(), 148843u); } void test_create_slope_table_small_full_step_target_speed_too_high() @@ -167,15 +71,14 @@ void test_create_slope_table_small_full_step_target_speed_too_high() slope.max_speed_w = 2632; slope.acceleration = 1.2e-8; - auto table = create_slope_table(slope, 2000, StepType::FULL, 4, 8, max_table_size); + auto table = create_slope_table_for_speed(slope, 2000, StepType::FULL, 4, 8, max_table_size); std::vector expected_table = { 62464, 62464, 6420, 4552, 3720, 3223, 2883, 2632 }; - expected_table.resize(max_table_size, 2632); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 8u); - ASSERT_EQ(table.pixeltime_sum, 148358u); + ASSERT_EQ(table.table.size(), 8u); + ASSERT_EQ(table.pixeltime_sum(), 148358u); } void test_create_slope_table_small_half_step() @@ -188,26 +91,24 @@ void test_create_slope_table_small_half_step() slope.max_speed_w = 2632; slope.acceleration = 1.2e-8; - auto table = create_slope_table(slope, 5000, StepType::HALF, 4, 8, max_table_size); + auto table = create_slope_table_for_speed(slope, 5000, StepType::HALF, 4, 8, max_table_size); std::vector expected_table = { - 31232, 31232, 3210, 2500 + 31232, 31232, 3210, 2500, 2500, 2500, 2500, 2500 }; - expected_table.resize(max_table_size, 2500); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 8u); - ASSERT_EQ(table.pixeltime_sum, 78174u); + ASSERT_EQ(table.table.size(), 8u); + ASSERT_EQ(table.pixeltime_sum(), 78174u); - table = create_slope_table(slope, 3000, StepType::HALF, 4, 8, max_table_size); + table = create_slope_table_for_speed(slope, 3000, StepType::HALF, 4, 8, max_table_size); expected_table = { - 31232, 31232, 3210, 2276, 1860, 1611, 1500 + 31232, 31232, 3210, 2276, 1860, 1611, 1500, 1500 }; - expected_table.resize(max_table_size, 1500); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 8u); - ASSERT_EQ(table.pixeltime_sum, 74421u); + ASSERT_EQ(table.table.size(), 8u); + ASSERT_EQ(table.pixeltime_sum(), 74421u); } void test_create_slope_table_large_full_step() @@ -243,7 +144,7 @@ void test_create_slope_table_large_full_step() slope.max_speed_w = 1500; slope.acceleration = 1.013948e-9; - auto table = create_slope_table(slope, 3000, StepType::FULL, 4, 8, max_table_size); + auto table = create_slope_table_for_speed(slope, 3000, StepType::FULL, 4, 8, max_table_size); std::vector expected_table = { 54612, 54612, 20570, 15090, 12481, 10880, 9770, 8943, 8295, 7771, @@ -251,15 +152,14 @@ void test_create_slope_table_large_full_step() 5072, 4945, 4826, 4716, 4613, 4517, 4426, 4341, 4260, 4184, 4111, 4043, 3977, 3915, 3855, 3799, 3744, 3692, 3642, 3594, 3548, 3503, 3461, 3419, 3379, 3341, 3304, 3268, 3233, 3199, - 3166, 3135, 3104, 3074, 3045, 3017, 3000, + 3166, 3135, 3104, 3074, 3045, 3017, 3000, 3000, 3000, 3000, }; - expected_table.resize(max_table_size, 3000); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 60u); - ASSERT_EQ(table.pixeltime_sum, 412616u); + ASSERT_EQ(table.table.size(), 60u); + ASSERT_EQ(table.pixeltime_sum(), 412616u); - table = create_slope_table(slope, 1500, StepType::FULL, 4, 8, max_table_size); + table = create_slope_table_for_speed(slope, 1500, StepType::FULL, 4, 8, max_table_size); expected_table = { 54612, 54612, 20570, 15090, 12481, 10880, 9770, 8943, 8295, 7771, @@ -284,12 +184,11 @@ void test_create_slope_table_large_full_step() 1614, 1610, 1606, 1601, 1597, 1593, 1589, 1585, 1581, 1577, 1573, 1569, 1565, 1561, 1557, 1554, 1550, 1546, 1542, 1539, 1535, 1531, 1528, 1524, 1520, 1517, 1513, 1510, 1506, 1503, - 1500, + 1500, 1500, 1500, 1500, }; - expected_table.resize(max_table_size, 1500); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 224u); - ASSERT_EQ(table.pixeltime_sum, 734910u); + ASSERT_EQ(table.table.size(), 224u); + ASSERT_EQ(table.pixeltime_sum(), 734910u); } void test_create_slope_table_large_half_step() @@ -303,7 +202,7 @@ void test_create_slope_table_large_half_step() slope.max_speed_w = 1500; slope.acceleration = 1.013948e-9; - auto table = create_slope_table(slope, 3000, StepType::HALF, 4, 8, max_table_size); + auto table = create_slope_table_for_speed(slope, 3000, StepType::HALF, 4, 8, max_table_size); std::vector expected_table = { 27306, 27306, 10285, 7545, 6240, 5440, 4885, 4471, 4147, 3885, @@ -311,15 +210,14 @@ void test_create_slope_table_large_half_step() 2536, 2472, 2413, 2358, 2306, 2258, 2213, 2170, 2130, 2092, 2055, 2021, 1988, 1957, 1927, 1899, 1872, 1846, 1821, 1797, 1774, 1751, 1730, 1709, 1689, 1670, 1652, 1634, 1616, 1599, - 1583, 1567, 1552, 1537, 1522, 1508, 1500, + 1583, 1567, 1552, 1537, 1522, 1508, 1500, 1500, 1500, 1500, }; - expected_table.resize(max_table_size, 1500); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 60u); - ASSERT_EQ(table.pixeltime_sum, 206294u); + ASSERT_EQ(table.table.size(), 60u); + ASSERT_EQ(table.pixeltime_sum(), 206294u); - table = create_slope_table(slope, 1500, StepType::HALF, 4, 8, max_table_size); + table = create_slope_table_for_speed(slope, 1500, StepType::HALF, 4, 8, max_table_size); expected_table = { 27306, 27306, 10285, 7545, 6240, 5440, 4885, 4471, 4147, 3885, @@ -344,17 +242,15 @@ void test_create_slope_table_large_half_step() 807, 805, 803, 800, 798, 796, 794, 792, 790, 788, 786, 784, 782, 780, 778, 777, 775, 773, 771, 769, 767, 765, 764, 762, 760, 758, 756, 755, 753, 751, - 750, + 750, 750, 750, 750, }; - expected_table.resize(max_table_size, 750); ASSERT_EQ(table.table, expected_table); - ASSERT_EQ(table.steps_count, 224u); - ASSERT_EQ(table.pixeltime_sum, 367399u); + ASSERT_EQ(table.table.size(), 224u); + ASSERT_EQ(table.pixeltime_sum(), 367399u); } void test_motor() { - test_create_slope_table3(); test_create_slope_table_small_full_step(); test_create_slope_table_small_full_step_target_speed_too_high(); test_create_slope_table_small_half_step(); diff --git a/testsuite/tools/Makefile.am b/testsuite/tools/Makefile.am index 532e90426..5fd97caad 100644 --- a/testsuite/tools/Makefile.am +++ b/testsuite/tools/Makefile.am @@ -28,19 +28,25 @@ check: check.local check.local: @echo "**** Testing $(SANEDESC) with $(TESTFILE)" - @for mode in ascii html-backends-split html-mfgs xml statistics usermap db udev udev+acl udev+hwdb hwdb plist hal hal-new; \ + @pass=true; \ + for mode in ascii html-backends-split html-mfgs xml statistics usermap db udev udev+acl udev+hwdb hwdb plist hal hal-new; \ do \ $(SANEDESC) -m $$mode -s $(srcdir)/data >$$mode.res ;\ - if diff -I "[ 012][0-9]:[0-5][0-9]:[0-6][0-9] 20[0-9][0-9]" \ - -I "sane-backends 1\.0\.[0-9]\+\([-0-9a-fgdirty]\+\)\?$$" \ + if diff -I "sane-backends 1\.0\.[0-9]\+\([-0-9a-fgdirty]\+\)\?$$" \ $(srcdir)/data/$$mode.ref $$mode.res ; \ then \ echo "PASS: sane-desc -m $$mode -s $(srcdir)/data"; \ else \ echo "FAIL: sane-desc -m $$mode -s $(srcdir)/data"; \ - exit 1 ;\ + pass=false ; \ fi; \ done ;\ - echo "================" ;\ - echo "All tests passed" ;\ - echo "================" + if `$$pass`; then \ + echo "================" ; \ + echo "All tests passed" ; \ + echo "================" ; \ + else \ + echo "========================" ; \ + echo "One or more tests failed" ; \ + echo "========================" ; \ + fi diff --git a/testsuite/tools/data/db.ref b/testsuite/tools/data/db.ref index 38286677f..3df3e95da 100644 --- a/testsuite/tools/data/db.ref +++ b/testsuite/tools/data/db.ref @@ -1,5 +1,5 @@ -# This file was automatically created based on description files (*.desc) -# by sane-desc 3.5 from sane-backends 1.0.24git on Wed Jul 31 07:52:48 2013 +# This file was generated from description files (*.desc) +# by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab # # The entries below are used to detect a USB device when it's plugged in # and then run a script to change the ownership and diff --git a/testsuite/tools/data/html-backends-split.ref b/testsuite/tools/data/html-backends-split.ref index bd8d1543d..1b2e8dd4b 100644 --- a/testsuite/tools/data/html-backends-split.ref +++ b/testsuite/tools/data/html-backends-split.ref @@ -16496,7 +16496,6 @@ Grandtek Scopecam >Contact -This page was last updated on Wed Jul 31 07:52:48 2013 - by sane-desc 3.5 from sane-backends 1.0.24git +This page was created by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab diff --git a/testsuite/tools/data/html-mfgs.ref b/testsuite/tools/data/html-mfgs.ref index 1b34bb461..3c7f2ae7e 100644 --- a/testsuite/tools/data/html-mfgs.ref +++ b/testsuite/tools/data/html-mfgs.ref @@ -23910,7 +23910,6 @@ qcam
(unmaintained) >Contact -This page was last updated on Wed Jul 31 07:52:48 2013 - by sane-desc 3.5 from sane-backends 1.0.24git +This page was created by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab diff --git a/testsuite/tools/data/hwdb.ref b/testsuite/tools/data/hwdb.ref index 9adc73f3f..1dd6b2a7d 100644 --- a/testsuite/tools/data/hwdb.ref +++ b/testsuite/tools/data/hwdb.ref @@ -1,5 +1,5 @@ -# This file was automatically created based on description files (*.desc) -# by sane-desc 3.5 from sane-backends 1.0.25git on Tue Dec 3 15:24:46 2013 +# This file was generated from description files (*.desc) +# by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab # # hwdb file for supported USB devices # diff --git a/testsuite/tools/data/udev+acl.ref b/testsuite/tools/data/udev+acl.ref index bcedd50f1..81a81d399 100644 --- a/testsuite/tools/data/udev+acl.ref +++ b/testsuite/tools/data/udev+acl.ref @@ -1,5 +1,5 @@ -# This file was automatically created based on description files (*.desc) -# by sane-desc 3.5 from sane-backends 1.0.24git on Wed Jul 31 07:52:49 2013 +# This file was generated from description files (*.desc) +# by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab # # udev rules file for supported USB and SCSI devices # diff --git a/testsuite/tools/data/udev+hwdb.ref b/testsuite/tools/data/udev+hwdb.ref index ead2939c4..2cb6ff780 100644 --- a/testsuite/tools/data/udev+hwdb.ref +++ b/testsuite/tools/data/udev+hwdb.ref @@ -1,5 +1,5 @@ -# This file was automatically created based on description files (*.desc) -# by sane-desc 3.5 from sane-backends 1.0.24git on Thu Aug 1 18:50:15 2013 +# This file was generated from description files (*.desc) +# by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab # # udev rules file for supported USB and SCSI devices # diff --git a/testsuite/tools/data/udev.ref b/testsuite/tools/data/udev.ref index bd448af9e..478e8d5f5 100644 --- a/testsuite/tools/data/udev.ref +++ b/testsuite/tools/data/udev.ref @@ -1,5 +1,5 @@ -# This file was automatically created based on description files (*.desc) -# by sane-desc 3.5 from sane-backends 1.0.24git on Wed Jul 31 07:52:48 2013 +# This file was generated from description files (*.desc) +# by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab # # udev rules file for supported USB and SCSI devices # diff --git a/testsuite/tools/data/usermap.ref b/testsuite/tools/data/usermap.ref index b1d5a9fc2..0b7281bab 100644 --- a/testsuite/tools/data/usermap.ref +++ b/testsuite/tools/data/usermap.ref @@ -1,5 +1,5 @@ -# This file was automatically created based on description files (*.desc) -# by sane-desc 3.5 from sane-backends 1.0.24git on Wed Jul 31 07:52:48 2013 +# This file was generated from description files (*.desc) +# by sane-desc 3.6 from sane-backends 1.0.29-241-g1f9590ab # # The entries below are used to detect a USB device and change owner # and permissions on the "device node" used by libusb. diff --git a/tools/openbsd/attach b/tools/openbsd/attach index b6c98c8c6..16ce31ff8 100755 --- a/tools/openbsd/attach +++ b/tools/openbsd/attach @@ -5,16 +5,15 @@ DEVNAME=$2 case $DEVCLASS in 0) - # generic devices case "$DEVNAME" in ugen*) - BUSNAME=`usbdevs -v -d | egrep "Controller|$DEVNAME\$" | grep -B 1 ugen0$ | head -n 1 | sed -e 's,Controller ,,' -e 's,:$,,' ` - echo $BUSNAME > /var/run/${DEVNAME}.bus - # probably our scanner - chgrp usb /dev/"$DEVNAME".* - chgrp usb /dev/"$BUSNAME" + BUSNAME=$(usbdevs -vv | egrep "Controller|$DEVNAME\$" | grep -B 1 "$DEVNAME\$" | awk -F'[ :]' '/^Controller/ { print $2 }') + echo $BUSNAME > /var/run/${DEVNAME}.bus + chown _cups:_saned /dev/${DEVNAME}.* && + chmod 660 /dev/${DEVNAME}.* + chown _cups:_saned $BUSNAME && + chmod 660 $BUSNAME ;; esac - - ;; + ;; esac diff --git a/tools/openbsd/detach b/tools/openbsd/detach index a5c209c56..8566e5152 100755 --- a/tools/openbsd/detach +++ b/tools/openbsd/detach @@ -5,18 +5,17 @@ DEVNAME=$2 case $DEVCLASS in 0) - # generic devices case "$DEVNAME" in ugen*) - BUSNAME=`cat /var/run/${DEVNAME}.bus` + BUSNAME=$(cat /var/run/${DEVNAME}.bus) rm -f /var/run/${DEVNAME}.bus - # probably our scanner - chgrp wheel /dev/"$DEVNAME".* - if [ x$BUSNAME != x ] ; then - chgrp wheel /dev/"$BUSNAME" - fi + chown root:wheel /dev/${DEVNAME}.* && + chmod 600 /dev/${DEVNAME}.* + test -n "$BUSNAME" && { + chown root:wheel $BUSNAME && + chmod 600 $BUSNAME + } ;; esac - - ;; + ;; esac diff --git a/tools/sane-desc.c b/tools/sane-desc.c index 890e754a7..191c6e5c3 100644 --- a/tools/sane-desc.c +++ b/tools/sane-desc.c @@ -45,7 +45,7 @@ #include "../include/sane/sanei.h" #include "../include/sane/sanei_config.h" -#define SANE_DESC_VERSION "3.5" +#define SANE_DESC_VERSION "3.6" #define MAN_PAGE_LINK "man/%s.5.html" #define COLOR_MINIMAL "\"#B00000\"" @@ -2808,16 +2808,14 @@ html_print_header (void) static void html_print_footer (void) { - time_t current_time = time (0); - printf ("
\n" "SANE homepage\n" "
\n" "Contact\n" "
\n" "\n"); - printf ("This page was last updated on %s by sane-desc %s from %s\n", - asctime (localtime (¤t_time)), SANE_DESC_VERSION, PACKAGE_STRING); + printf ("This page was created by sane-desc %s from %s\n", + SANE_DESC_VERSION, PACKAGE_STRING); printf ("\n"); printf (" \n"); } @@ -3318,21 +3316,22 @@ create_scsiids_table (void) return first_scsiid; } +static void +print_header_comment (void) +{ + printf ("# This file was generated from description files (*.desc)\n" + "# by sane-desc %s from %s\n", + SANE_DESC_VERSION, PACKAGE_STRING); +} + /* print USB usermap file to be used by the hotplug tools */ static void print_usermap_header (void) { - time_t current_time = time (0); - + print_header_comment (); printf - ("# This file was automatically created based on description files (*.desc)\n" - "# by sane-desc %s from %s on %s" - "#\n" - , - SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (¤t_time))); - - printf - ("# The entries below are used to detect a USB device and change owner\n" + ("#\n" + "# The entries below are used to detect a USB device and change owner\n" "# and permissions on the \"device node\" used by libusb.\n" "#\n" "# The 0x0003 match flag means the device is matched by its vendor and\n" @@ -3396,10 +3395,7 @@ print_usermap (void) static void print_db_header (void) { - time_t current_time = time (0); - printf ("# This file was automatically created based on description files (*.desc)\n" - "# by sane-desc %s from %s on %s", - SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (¤t_time))); + print_header_comment (); printf ("#\n" "# The entries below are used to detect a USB device when it's plugged in\n" @@ -3461,11 +3457,7 @@ print_db (void) static void print_udev_header (void) { - time_t current_time = time (0); - printf ("# This file was automatically created based on description files (*.desc)\n" - "# by sane-desc %s from %s on %s", - SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (¤t_time))); - + print_header_comment (); printf ("#\n" "# udev rules file for supported USB and SCSI devices\n" @@ -3654,11 +3646,7 @@ print_udev (void) static void print_udevhwdb_header (void) { - time_t current_time = time (0); - printf ("# This file was automatically created based on description files (*.desc)\n" - "# by sane-desc %s from %s on %s", - SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (¤t_time))); - + print_header_comment (); printf ("#\n" "# udev rules file for supported USB and SCSI devices\n" @@ -3764,11 +3752,7 @@ print_udevhwdb (void) static void print_hwdb_header (void) { - time_t current_time = time (0); - printf ("# This file was automatically created based on description files (*.desc)\n" - "# by sane-desc %s from %s on %s", - SANE_DESC_VERSION, PACKAGE_STRING, asctime (localtime (¤t_time))); - + print_header_comment (); printf ("#\n" "# hwdb file for supported USB devices\n"