aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2023-01-06 10:02:49 +0200
committerIgor Pashev <pashev.igor@gmail.com>2023-01-06 10:04:59 +0200
commit1145733c29db0a678537ce99ff60e21613f622a8 (patch)
tree63d5d6c324629d4eef1354db3c97f857d6016a34 /backend
downloadiscan-1145733c29db0a678537ce99ff60e21613f622a8.tar.gz
Import iscan 2.30.4-2
Diffstat (limited to 'backend')
-rw-r--r--backend/Makefile.am168
-rw-r--r--backend/backend.c600
-rw-r--r--backend/backend.h380
-rw-r--r--backend/cfg-obj.c1619
-rw-r--r--backend/cfg-obj.h151
-rw-r--r--backend/channel-net.c242
-rw-r--r--backend/channel-pio.c165
-rw-r--r--backend/channel-scsi.c170
-rw-r--r--backend/channel-usb.c285
-rw-r--r--backend/channel.c321
-rw-r--r--backend/channel.h143
-rw-r--r--backend/command.c814
-rw-r--r--backend/command.h223
-rw-r--r--backend/defines.h104
-rw-r--r--backend/device.c563
-rw-r--r--backend/device.h265
-rw-r--r--backend/dip-obj.c746
-rw-r--r--backend/dip-obj.h119
-rw-r--r--backend/epkowa.c6513
-rw-r--r--backend/epkowa.conf65
-rw-r--r--backend/epkowa.h291
-rw-r--r--backend/epkowa_ip.c596
-rw-r--r--backend/epkowa_ip.h124
-rw-r--r--backend/epkowa_ip_api.h92
-rw-r--r--backend/epkowa_scsi.c103
-rw-r--r--backend/epkowa_scsi.h28
-rw-r--r--backend/extension.h177
-rw-r--r--backend/get-infofile.c277
-rw-r--r--backend/get-infofile.h91
-rw-r--r--backend/hw-data.c372
-rw-r--r--backend/hw-data.h136
-rw-r--r--backend/ipc.c563
-rw-r--r--backend/ipc.h121
-rw-r--r--backend/list.c198
-rw-r--r--backend/list.h117
-rw-r--r--backend/message.c152
-rw-r--r--backend/message.h191
-rw-r--r--backend/model-info.c707
-rw-r--r--backend/model-info.h145
-rw-r--r--backend/net-obj.c123
-rw-r--r--backend/net-obj.h89
-rw-r--r--backend/profile.c540
-rw-r--r--backend/tests/45532d48333030.xml28
-rw-r--r--backend/tests/47542d58393730.xml38
-rw-r--r--backend/tests/50657266656374696f6e363130.xml27
-rw-r--r--backend/tests/Makefile.am79
-rw-r--r--backend/tests/network.c86
-rw-r--r--backend/tests/test-cfg-obj.cc60
-rw-r--r--backend/tests/test-cfg-obj.hh254
-rw-r--r--backend/tests/test-model-info.cc83
-rw-r--r--backend/tests/test-model-info.hh241
-rw-r--r--backend/tests/test-net-obj.cc35
-rw-r--r--backend/tests/test-net-obj.hh126
-rwxr-xr-xbackend/tests/xmltest-runner.sh87
-rw-r--r--backend/tests/xmltest.c247
-rw-r--r--backend/tests/xmltest.h190
-rw-r--r--backend/timing.c188
-rw-r--r--backend/timing.h119
-rw-r--r--backend/utils.c334
-rw-r--r--backend/utils.h199
-rw-r--r--backend/xmlreader.c378
-rw-r--r--backend/xmlreader.h79
62 files changed, 21767 insertions, 0 deletions
diff --git a/backend/Makefile.am b/backend/Makefile.am
new file mode 100644
index 0000000..4a36339
--- /dev/null
+++ b/backend/Makefile.am
@@ -0,0 +1,168 @@
+## Makefile.am -- an automake template for a Makefile.in file
+## Copyright (C) 2004, 2005, 2009 Olaf Meeuwissen
+##
+## This file is part of the "Image Scan!" build infra-structure.
+##
+## The "Image Scan!" build infra-structure 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 FITNESS
+## FOR A PARTICULAR PURPOSE or MERCHANTABILITY.
+## See the GNU General Public License for more details.
+##
+## You should have received a verbatim copy of the GNU General Public
+## License along with this program; if not, write to:
+##
+## Free Software Foundation, Inc.
+## 59 Temple Place, Suite 330
+## Boston, MA 02111-1307 USA
+
+
+SUBDIRS = . \
+ tests
+
+AM_CPPFLAGS = \
+ -DPIC \
+ -DPKGDATADIR=\"$(datadir)/$(DATA_PKG_NAME)\" \
+ -DMODELDATADIR=\"$(datadir)/$(DATA_PKG_NAME)/$(MODEL_DATA_DIR_NAME)\" \
+ -DPKGLIBDIR=\"$(pkglibdir)\" \
+ -DPKGLOCALSTATEDIR=\"$(localstatedir)/lib/$(PACKAGE_TARNAME)\" \
+ -DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DENABLE_DEBUG=1 \
+ -DMSG_MODULE=\"epkowa\" \
+ -I$(top_srcdir)
+AM_CFLAGS = \
+ -fPIC
+
+exec_sanelibdir = $(libdir)/sane
+exec_sanelib_LTLIBRARIES = \
+ libsane-epkowa.la
+
+libsane_epkowa_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -DBACKEND_NAME=epkowa
+libsane_epkowa_la_LDFLAGS = \
+ -export-symbols-regex ^sane_ \
+ -version-info $(SANE_MAJOR):$(SANE_REVISION):$(SANE_MINOR)
+libsane_epkowa_la_LIBADD = \
+ libepkowa.la
+libsane_epkowa_la_SOURCES = \
+ backend.c \
+ backend.h
+
+noinst_LTLIBRARIES = \
+ libepkowa.la
+
+libepkowa_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(LTDLINCL) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/non-free \
+ $(XML_CFLAGS) \
+ $(LIBUSB_1_0_CFLAGS) \
+ -DV_MAJOR=$(SANE_MAJOR) -DV_MINOR=$(SANE_MINOR)
+libepkowa_la_LDFLAGS = \
+ -static
+libepkowa_la_LIBADD = \
+ -lm \
+ $(XML_LIBS) \
+ $(LIBUSB_1_0_LIBS) \
+ $(LIBLTDL)
+libepkowa_la_SOURCES = \
+ $(sane_backends_files) \
+ ipc.c \
+ ipc.h \
+ cfg-obj.c \
+ cfg-obj.h \
+ command.c \
+ command.h \
+ defines.h \
+ hw-data.c \
+ hw-data.h \
+ message.c \
+ message.h \
+ net-obj.c \
+ net-obj.h \
+ dip-obj.c \
+ dip-obj.h \
+ device.c \
+ device.h \
+ timing.c \
+ timing.h \
+ utils.c \
+ utils.h \
+ epkowa_ip.c \
+ epkowa_ip.h \
+ epkowa_ip_api.h \
+ channel.c \
+ channel.h \
+ channel-net.c \
+ channel-pio.c \
+ channel-scsi.c \
+ channel-usb.c \
+ model-info.c \
+ model-info.h \
+ list.h \
+ list.c \
+ get-infofile.h \
+ get-infofile.c \
+ xmlreader.h \
+ xmlreader.c
+
+if ENABLE_TIMING
+libepkowa_la_CPPFLAGS += -DENABLE_TIMING=1
+libepkowa_la_LIBADD += -lrt
+endif
+
+EXTRA_DIST = \
+ extension.h \
+ profile.c \
+ epkowa.conf
+
+sane_backends_files = \
+ ../include/sane/sanei.h \
+ ../include/sane/sanei_config.h \
+ ../include/sane/sanei_debug.h \
+ ../include/sane/sanei_magic.h \
+ ../include/sane/sanei_pio.h \
+ ../include/sane/sanei_scsi.h \
+ ../include/sane/sanei_usb.h \
+ ../sanei/linux_sg3_err.h \
+ ../sanei/sanei_config.c \
+ ../sanei/sanei_constrain_value.c \
+ ../sanei/sanei_init_debug.c \
+ ../sanei/sanei_magic.c \
+ ../sanei/sanei_pio.c \
+ ../sanei/sanei_scsi.c \
+ ../sanei/sanei_usb.c \
+ epkowa.c \
+ epkowa.h \
+ epkowa_scsi.c \
+ epkowa_scsi.h
+
+
+## There should be NO libsane.so symlink in $(exec_sanelibdir), but
+## libtool insists on making one for us anyway.
+##
+install-exec-hook:
+ -rm -f $(DESTDIR)$(exec_sanelibdir)/libsane.so.$(SANE_MAJOR)
+
+
+## Minimal sanity checks on the backends we distribute.
+## WARNING: These checks may fail on non-Linux systems for a variety
+## of reasons. If you know why, we would like to know (and
+## patches are naturally welcome).
+check-local:
+ for d in .libs _libs; do \
+ test -d $$d || continue; \
+ for l in $$d/libsane-*.so; do \
+ soname=`objdump -p $$l | awk '/SONAME/ {print $$2}'`; \
+ if test "$$soname" != "libsane.so.$(SANE_MAJOR)"; then \
+ echo "$$l: libsane.so.$(SANE_MAJOR) != $$soname"; \
+ exit 1; \
+ fi; \
+ done; \
+ done
diff --git a/backend/backend.c b/backend/backend.c
new file mode 100644
index 0000000..c9e6cf0
--- /dev/null
+++ b/backend/backend.c
@@ -0,0 +1,600 @@
+/* backend.c -- implements the SANE epkowa backend
+ * Copyright (C) 2008--2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#define BACKEND_CREATE_FORWARDERS
+#include "backend.h"
+
+#define BACKEND_BUILD 213
+#define BACKEND_SOURCE PACKAGE_STRING
+
+#include <errno.h>
+#include <ltdl.h>
+#include <string.h>
+
+#include "cfg-obj.h"
+#include "dip-obj.h"
+#include "net-obj.h"
+#include "model-info.h"
+#include "utils.h"
+
+/* Deprecated includes */
+#include "include/sane/sanei_usb.h"
+#include "epkowa.h"
+
+/* Deprecated API calls */
+extern SANE_Status epkowa_open (const char *, SANE_Handle *, const void *);
+
+
+typedef struct
+{
+ void *cfg;
+ void *net;
+ void *dip;
+ list *sane_dev;
+ void **dev_list;
+ void *model_info_cache;
+
+} backend_type;
+
+static backend_type *be = NULL;
+
+
+static void be_sane_dev_dtor (void *);
+
+
+/*! \defgroup SANE_API SANE API Entry Points
+ *
+ * The SANE API entry points make up the \e full API available to the
+ * SANE frontend application programmer. Users of this API should be
+ * careful \e never to assume \e anything about a backend's behaviour
+ * beyond what is required by the SANE standard. The standard can be
+ * retrieved via http://www.sane-project.org/docs.html.
+ *
+ * Whatever documentation may be provided here serves to document the
+ * implementation, if anything. In case of discrepancy with the SANE
+ * specification, the SANE specification is correct.
+ *
+ * @{
+ */
+
+
+/*! \brief Prepares the backend for use by a SANE frontend.
+ *
+ * \remarks
+ * This function \e must be called before any other SANE API entry is
+ * called. It is the only SANE function that may be called after the
+ * sane_exit() function has been called.
+ *
+ * \todo
+ * Deal properly with the \c SANE_Auth_Callback.
+ * It is completely ignored in the current implementation.
+ */
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (be)
+ {
+ log_call ("(%p, %p)", version_code, authorize);
+ err_minor ("backend already initialised");
+ return status;
+ }
+
+ msg_init ();
+ log_call ("(%p, %p)", version_code, authorize);
+ log_info ("%s", BACKEND_SOURCE);
+ log_info ("version %d.%d.%d", SANE_MAJOR, SANE_MINOR, BACKEND_BUILD);
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_MAJOR, SANE_MINOR,
+ BACKEND_BUILD);
+ }
+
+ if (authorize)
+ {
+ err_minor ("authorisation not supported");
+ }
+
+ be = t_calloc (1, backend_type);
+ if (!be)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Needs to be done _before_ cfg_init() because that refers to the
+ * model_info_cache somewhere deep down in its bowels.
+ */
+ be->model_info_cache = model_info_cache_init (MODELDATADIR, &status);
+ if (!be->model_info_cache)
+ {
+ sane_exit ();
+ return status;
+ }
+
+ be->cfg = cfg_init (PKGDATADIR, &status);
+ if (!be->cfg)
+ {
+ sane_exit ();
+ return status;
+ }
+
+ if (cfg_has (be->cfg, CFG_KEY_NET))
+ {
+ be->net = net_init (PKGLIBDIR, &status);
+ if (!be->net)
+ {
+ if (SANE_STATUS_GOOD != status)
+ err_fatal ("%s", sane_strstatus (status));
+ err_major ("disabling network device support");
+ cfg_set (be->cfg, CFG_KEY_NET, false);
+ status = SANE_STATUS_GOOD;
+ }
+ }
+ if (cfg_has (be->cfg, CFG_KEY_PIO))
+ {
+ /* place holder */
+ }
+ if (cfg_has (be->cfg, CFG_KEY_SCSI))
+ {
+ /* place holder */
+ }
+ if (cfg_has (be->cfg, CFG_KEY_USB))
+ {
+ sanei_usb_init ();
+ }
+
+ if (cfg_has (be->cfg, CFG_KEY_INTERPRETER))
+ {
+ if (0 != lt_dlinit ())
+ {
+ err_fatal ("%s", lt_dlerror ());
+ err_major ("disabling interpreter support");
+ cfg_set (be->cfg, CFG_KEY_INTERPRETER, false);
+ }
+ }
+
+ be->dip = dip_init (PKGLIBDIR, &status);
+ if (!be->dip)
+ {
+ sane_exit ();
+ return status;
+ }
+
+ return status;
+}
+
+
+/*! \brief Releases all resources held by the backend.
+ *
+ * \remarks
+ * Applications \e must call this function to terminate use of the
+ * backend. After it has been called, sane_init() has to be called
+ * before other SANE API can be used. The function needs to close
+ * any open handles.
+ *
+ * \remarks
+ * The implementation must be able to deal properly with a partially
+ * initialised backend so that sane_init() can use this function for
+ * its error recovery.
+ */
+void
+sane_exit (void)
+{
+ log_call ("()");
+
+ if (!be)
+ {
+ msg_init ();
+ err_minor ("backend is not initialized");
+ return;
+ }
+
+ be->dip = dip_exit (be->dip);
+
+ if (cfg_has (be->cfg, CFG_KEY_INTERPRETER))
+ {
+ lt_dlexit ();
+ }
+
+ if (cfg_has (be->cfg, CFG_KEY_USB))
+ {
+ /* sanei_usb_exit, if only there was one! */
+ }
+ if (cfg_has (be->cfg, CFG_KEY_SCSI))
+ {
+ /* place holder */
+ }
+ if (cfg_has (be->cfg, CFG_KEY_PIO))
+ {
+ /* place holder */
+ }
+ if (be->net)
+ {
+ be->net = net_exit (be->net);
+ }
+
+ be->cfg = cfg_exit (be->cfg);
+ delete (be->dev_list);
+ list_destroy (be->sane_dev, be_sane_dev_dtor);
+
+ be->model_info_cache = model_info_cache_exit (be->model_info_cache);
+
+ delete (be);
+}
+
+
+/*! \brief Creates a list of devices available through the backend.
+ *
+ * \remarks
+ * The returned \a device_list \e must remain unchanged and valid
+ * until this function is called again or sane_exit() is called.
+ *
+ * \remarks
+ * Applications are \e not required to call this function before
+ * they call sane_open().
+ */
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ list *sane_dev = NULL;
+
+ log_call ("(%p, %d)", device_list, local_only);
+
+ if (!be)
+ {
+ msg_init ();
+ err_fatal ("backend is not initialized");
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ if (!device_list)
+ {
+ err_fatal ("%s", strerror (EINVAL));
+ return SANE_STATUS_INVAL;
+ }
+
+ sane_dev = list_create ();
+ if (sane_dev)
+ {
+ if (!local_only && cfg_has (be->cfg, CFG_KEY_NET))
+ {
+ cfg_find (be->cfg, CFG_KEY_NET, sane_dev);
+ }
+ if (cfg_has (be->cfg, CFG_KEY_PIO))
+ {
+ cfg_find (be->cfg, CFG_KEY_PIO, sane_dev);
+ }
+ if (cfg_has (be->cfg, CFG_KEY_SCSI))
+ {
+ cfg_find (be->cfg, CFG_KEY_SCSI, sane_dev);
+ }
+ if (cfg_has (be->cfg, CFG_KEY_USB))
+ {
+ cfg_find (be->cfg, CFG_KEY_USB, sane_dev);
+ }
+
+ if (cfg_has (be->cfg, CFG_KEY_INTERPRETER))
+ {
+ cfg_find (be->cfg, CFG_KEY_INTERPRETER, sane_dev);
+ }
+
+ if (be->sane_dev)
+ {
+ delete (be->dev_list);
+ list_destroy (be->sane_dev, be_sane_dev_dtor);
+ }
+ be->sane_dev = sane_dev;
+ }
+
+ be->dev_list = list_normalize (be->sane_dev);
+ *device_list = (const SANE_Device **) be->dev_list;
+ if (!*device_list)
+ {
+ status = SANE_STATUS_NO_MEM;
+ }
+
+ return status;
+}
+
+
+/*! \brief Establishes a connection to a named device.
+ *
+ * \remarks
+ * Applications are allowed to call this function directly, without a
+ * call to sane_get_devices() first. An empty string may be used for
+ * the \a device_name to request the first available device.
+ *
+ * \todo Register the handle with \c be before \c SANE_STATUS_GOOD is
+ * returned so we can check in the other API entries whether we
+ * got a known handle passed in as well as call sane_close() on
+ * all open handles in sane_exit().
+ */
+SANE_Status
+sane_open (SANE_String_Const device_name, SANE_Handle *handle)
+{
+ const SANE_Device *sane_dev = NULL;
+
+ log_call ("(%s, %p)", device_name, handle);
+
+ if (!be)
+ {
+ msg_init ();
+ err_fatal ("backend is not initialized");
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ if (!handle)
+ {
+ err_fatal ("%s", strerror (EINVAL));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!device_name)
+ {
+ /* The SANE API specification explicitly talks about a zero
+ * length string. There is no mention about what should be
+ * done in the case where there is NO string at all.
+ * We degrade gracefully.
+ */
+ err_minor ("assuming frontend meant to pass an empty string");
+ }
+
+ if (!be->sane_dev)
+ { /* FIXME: does more than necessary */
+ const SANE_Device **dev = NULL;
+ sane_get_devices (&dev, false);
+ }
+
+ if (0 == list_size (be->sane_dev))
+ {
+ err_major ("no supported devices available");
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ if (!device_name || 0 == strlen (device_name))
+ {
+ sane_dev = be->sane_dev->head->data;
+ }
+ else
+ {
+ list_reset (be->sane_dev);
+ while ((sane_dev = list_next (be->sane_dev))
+ && 0 != strcmp_c (sane_dev->name, device_name))
+ {
+ /* nothing to do, condition does all the processing */
+ }
+ }
+
+ if (!sane_dev)
+ {
+ err_major ("no such device");
+ return SANE_STATUS_INVAL;
+ }
+
+ return epkowa_open (sane_dev->name, handle, be->dip);
+}
+
+//! Obtains the current scan parameters for a device
+/*! \remarks
+ * The parameters are only guaranteed to be accurate between a call
+ * to sane_start() and the completion of that request. Outside of
+ * that scope the parameters are a best effort only and the backend
+ * is at liberty to change them.
+ */
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *parameters)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Epson_Scanner *s;
+
+ log_call ("(%p, %p)", handle, parameters);
+
+ if (!handle || !parameters)
+ {
+ err_fatal ("%s", strerror (EINVAL));
+ return SANE_STATUS_INVAL;
+ }
+
+ s = (Epson_Scanner *) handle;
+
+ if (s->src->transfer_started && !s->src->transfer_stopped)
+ {
+ static const char *const color_space[] = {
+ "GRAY", "RGB", "RED", "GREEN", "BLUE"
+ };
+
+ const SANE_Parameters *p = &s->src->ctx;
+
+ log_info
+ ("Scan area : %.2f x %.2f [mm^2]",
+ SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w),
+ SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w));
+ log_info
+ ("Offset : (%.2f, %.2f) [mm]",
+ SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_TL_Y].w));
+
+ log_info ("Color space : %s-%d", color_space[p->format], p->depth);
+ log_info ("Image size : %d x %d [pixels^2] (%.2f x %.2f [mm^2])",
+ p->pixels_per_line, p->lines,
+ p->pixels_per_line * MM_PER_INCH / s->val[OPT_X_RESOLUTION].w,
+ p->lines * MM_PER_INCH / s->val[OPT_Y_RESOLUTION].w);
+ log_info ("X Resolution: %d [dpi]", s->val[OPT_X_RESOLUTION].w);
+ log_info ("Y Resolution: %d [dpi]", s->val[OPT_Y_RESOLUTION].w);
+
+ memcpy (parameters, p, sizeof (*p));
+ }
+ else
+ {
+ status = estimate_parameters (s, parameters);
+ }
+
+ return status;
+}
+
+/*! \brief Acquires up to \a max_length bytes of new image data.
+ *
+ * \remarks
+ * The \a length is guaranteed to be zero in case of an unsuccessful
+ * request.
+ *
+ * \remarks
+ * The implementation allows for \c NULL as a \a buffer value. This
+ * caters to a frontends that do not prepare buffer space when they
+ * expect a \c SANE_STATUS_EOF return value.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *buffer,
+ SANE_Int max_length, SANE_Int *length)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Epson_Scanner *s;
+
+ log_call ("(%p, %p, %i, %p)", handle, buffer, max_length, length);
+
+ if (length) *length = 0;
+
+ if (!handle)
+ {
+ err_fatal ("%s", strerror (EINVAL));
+ return SANE_STATUS_INVAL;
+ }
+
+ s = (Epson_Scanner *) handle;
+
+ require (s->src == &s->raw || s->src == &s->img);
+
+ if (s->src == &s->raw)
+ {
+ status = fetch_image_data (s, buffer, max_length, length);
+ }
+ else if (s->src == &s->img)
+ {
+ /**/ if (!s->img.ptr)
+ {
+ err_major ("%s", strerror (ENOMEM));
+ status = SANE_STATUS_NO_MEM;
+ }
+ else if (s->img.ptr == s->img.end)
+ {
+ status = SANE_STATUS_EOF;
+ }
+ else if (s->img.cancel_requested)
+ {
+ s->img.transfer_stopped = true;
+ status = SANE_STATUS_CANCELLED;
+ }
+ else if (buffer && 0 < max_length)
+ {
+ SANE_Int len = s->img.end - s->img.ptr;
+
+ if (len > max_length) len = max_length;
+ memcpy (buffer, s->img.ptr, len);
+ s->img.ptr += len;
+ if (length) *length = len;
+ }
+ else
+ {
+ status = SANE_STATUS_NO_MEM;
+ }
+ }
+
+ if (SANE_STATUS_EOF == status)
+ {
+ s->src->transfer_stopped = true;
+ }
+
+ return status;
+}
+
+/*!
+ @}
+ */
+
+
+/*! Releases the resources held by a SANE_Device.
+
+ This function is primarily useful to maintain the \c sane_dev
+ member of a backend_type object.
+ */
+static void
+be_sane_dev_dtor (void *p)
+{
+ SANE_Device *sd = (SANE_Device *) p;
+ if (!sd) return;
+
+ const_delete (sd->name , SANE_String);
+ const_delete (sd->vendor, SANE_String);
+ const_delete (sd->model , SANE_String);
+ const_delete (sd->type , SANE_String);
+
+ delete (sd);
+}
diff --git a/backend/backend.h b/backend/backend.h
new file mode 100644
index 0000000..848f5a7
--- /dev/null
+++ b/backend/backend.h
@@ -0,0 +1,380 @@
+/* backend.h -- SANE backend implementation boiler plate
+ * Copyright (C) 2007 EPSON AVASYS CORPORATION
+ * Copyright (C) 2008 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef included_backend_h
+#define included_backend_h
+
+/*! \file
+ * \brief Provides backend implementation boiler plate.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sane/sane.h>
+#include <stdio.h>
+
+
+#ifndef BACKEND_NAME
+#error "Define the BACKEND_NAME macro to match your backend's name"
+#error "before including this file (backend.h)."
+#error "The value should not be quoted. Characters in your backend"
+#error "name that are not allowed in C variables should be replaced"
+#error "by underscores, one for each such character."
+#endif /* !defined (BACKEND_NAME) */
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* Set up the machinery used to create the "shadow" API entries.
+ * FIXME: Move these #define's into their own header file so that
+ * multi-file plug-ins can use them too.
+ * FIXME: Add an api_entry() function that mimicks the API_ENTRY()
+ * #define so that plug-ins can use dlsym() and friends more
+ * easily. You need a way to create names at run-time. The
+ * #define only works at compile-time!
+ */
+#define CONCAT_INTERNAL(s1,s2) s1##s2
+#define CONCAT(s1,s2) CONCAT_INTERNAL(s1,s2)
+
+#define API_ENTRY(backend,function) \
+ CONCAT(sane_,CONCAT(backend,CONCAT(_,function)))
+
+/* For each API entry, set up "shadow" API entries with a unique name.
+ */
+
+SANE_Status
+API_ENTRY (BACKEND_NAME, init) (SANE_Int *version,
+ SANE_Auth_Callback authoriser);
+void
+API_ENTRY (BACKEND_NAME, exit) (void);
+
+SANE_Status
+API_ENTRY (BACKEND_NAME, get_devices) (const SANE_Device ***device_list,
+ SANE_Bool local_only);
+
+SANE_Status
+API_ENTRY (BACKEND_NAME, open) (SANE_String_Const device_name,
+ SANE_Handle *handle);
+void
+API_ENTRY (BACKEND_NAME, close) (SANE_Handle handle);
+
+const SANE_Option_Descriptor *
+API_ENTRY (BACKEND_NAME, get_option_descriptor) (SANE_Handle handle,
+ SANE_Int index);
+SANE_Status
+API_ENTRY (BACKEND_NAME, control_option) (SANE_Handle handle, SANE_Int index,
+ SANE_Action action, void *value,
+ SANE_Word *info);
+SANE_Status
+API_ENTRY (BACKEND_NAME, get_parameters) (SANE_Handle handle,
+ SANE_Parameters *parameters);
+
+SANE_Status
+API_ENTRY (BACKEND_NAME, start) (SANE_Handle handle);
+SANE_Status
+API_ENTRY (BACKEND_NAME, read) (SANE_Handle handle, SANE_Byte *buffer,
+ SANE_Int max_length, SANE_Int *length);
+void
+API_ENTRY (BACKEND_NAME, cancel) (SANE_Handle handle);
+
+SANE_Status
+API_ENTRY (BACKEND_NAME, set_io_mode) (SANE_Handle handle,
+ SANE_Bool non_blocking);
+SANE_Status
+API_ENTRY (BACKEND_NAME, get_select_fd) (SANE_Handle handle,
+ SANE_Int *fdp);
+
+
+SANE_String_Const
+API_ENTRY (BACKEND_NAME, strstatus) (SANE_Status status);
+
+
+#ifdef BACKEND_CREATE_FORWARDERS
+
+/* For each API entry, forward the original call to the "shadow" API
+ * entry just declared.
+ * Most of the argument validation can be added here.
+ */
+
+SANE_Status
+sane_init (SANE_Int *version, SANE_Auth_Callback authoriser)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, init) (version, authoriser);
+ return status;
+}
+
+void
+sane_exit (void)
+{
+ API_ENTRY (BACKEND_NAME, exit) ();
+ return;
+}
+
+
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, get_devices) (device_list, local_only);
+ return status;
+}
+
+
+SANE_Status
+sane_open (SANE_String_Const device_name, SANE_Handle *handle)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, open) (device_name, handle);
+ return status;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ API_ENTRY (BACKEND_NAME, close) (handle);
+ return;
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int index)
+{
+ const SANE_Option_Descriptor *desc;
+
+ desc = API_ENTRY (BACKEND_NAME, get_option_descriptor) (handle, index);
+ return desc;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int index, SANE_Action action,
+ void *value, SANE_Word *info)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, control_option) (handle, index, action,
+ value, info);
+ return status;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters *parameters)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, get_parameters) (handle, parameters);
+ return status;
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, start) (handle);
+ return status;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *buffer, SANE_Int max_length,
+ SANE_Int *length)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, read) (handle, buffer, max_length,
+ length);
+ return status;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ API_ENTRY (BACKEND_NAME, cancel) (handle);
+ return;
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, set_io_mode) (handle, non_blocking);
+ return status;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fdp)
+{
+ SANE_Status status;
+
+ status = API_ENTRY (BACKEND_NAME, get_select_fd) (handle, fdp);
+ return status;
+}
+
+SANE_String_Const
+sane_strstatus (SANE_Status status)
+{
+ SANE_String_Const str;
+
+ str = API_ENTRY (BACKEND_NAME, strstatus) (status);
+ return str;
+}
+
+/*! \brief Marks a string for the translation tools.
+ */
+#define N(string) (string)
+
+/* Backends should not be bothered with implementing their own
+ * version of this bit of SANE API. At best, all will do the same
+ * thing, at worst every one has its own implementation returning
+ * different strings for the same status. This implementation will
+ * be used by every backend without them even noticing.
+ */
+SANE_String_Const
+API_ENTRY (BACKEND_NAME, strstatus) (SANE_Status status)
+{
+ switch (status)
+ {
+ case SANE_STATUS_GOOD:
+ return N("Success");
+ case SANE_STATUS_UNSUPPORTED:
+ return N("Operation not supported");
+ case SANE_STATUS_CANCELLED:
+ return N("Operation was cancelled");
+ case SANE_STATUS_DEVICE_BUSY:
+ return N("Device busy");
+ case SANE_STATUS_INVAL:
+ return N("Invalid argument");
+ case SANE_STATUS_EOF:
+ return N("End of file reached");
+ case SANE_STATUS_JAMMED:
+ return N("Document feeder jammed");
+ case SANE_STATUS_NO_DOCS:
+ return N("Document feeder out of documents");
+ case SANE_STATUS_COVER_OPEN:
+ return N("Scanner cover is open");
+ case SANE_STATUS_IO_ERROR:
+ return N("Error during device I/O");
+ case SANE_STATUS_NO_MEM:
+ return N("Out of memory");
+ case SANE_STATUS_ACCESS_DENIED:
+ return N("Access to resource has been denied");
+
+ default:
+ {
+ static char msg[80]; /* not re-entrant! */
+
+ snprintf (msg, 80, N("Unknown status code (%d)"), status);
+ return msg;
+ }
+ }
+}
+
+#undef N /* no longer needed, clean up */
+
+
+#endif /* !defined (BACKEND_CREATE_FORWARDERS) */
+
+
+/* Use the preprocessor to rename API entries in the backend to match
+ * the "shadow" API entries we have set up above.
+ * This way, the implementer can ignore all these name playing games.
+ */
+
+#define sane_init API_ENTRY (BACKEND_NAME, init)
+#define sane_exit API_ENTRY (BACKEND_NAME, exit)
+#define sane_get_devices \
+ API_ENTRY (BACKEND_NAME, get_devices)
+#define sane_open API_ENTRY (BACKEND_NAME, open)
+#define sane_close API_ENTRY (BACKEND_NAME, close)
+#define sane_get_option_descriptor \
+ API_ENTRY (BACKEND_NAME, get_option_descriptor)
+#define sane_control_option \
+ API_ENTRY (BACKEND_NAME, control_option)
+#define sane_get_parameters \
+ API_ENTRY (BACKEND_NAME, get_parameters)
+#define sane_start API_ENTRY (BACKEND_NAME, start)
+#define sane_read API_ENTRY (BACKEND_NAME, read)
+#define sane_cancel API_ENTRY (BACKEND_NAME, cancel)
+#define sane_set_io_mode \
+ API_ENTRY (BACKEND_NAME, set_io_mode)
+#define sane_get_select_fd \
+ API_ENTRY (BACKEND_NAME, get_select_fd)
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (included_backend_h) */
diff --git a/backend/cfg-obj.c b/backend/cfg-obj.c
new file mode 100644
index 0000000..e26901c
--- /dev/null
+++ b/backend/cfg-obj.c
@@ -0,0 +1,1619 @@
+/* cfg-obj.c -- configuration objects
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ * \todo Split the probing responsibility off into the respective
+ * channel classes.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "cfg-obj.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "command.h"
+#include "hw-data.h"
+#include "net-obj.h"
+#include "model-info.h"
+#include "ipc.h"
+#include "utils.h"
+
+/* Deprecated includes */
+#include "include/sane/sanei_usb.h"
+#include "include/sane/sanei_scsi.h"
+
+
+#ifndef SANE_CONFIG_DIR_NAME
+#define SANE_CONFIG_DIR_NAME "/etc/sane.d"
+#endif
+
+#define DEV_NAME_SEP_STR ":"
+#define DEV_NAME_SEP DEV_NAME_SEP_STR[0]
+
+
+const char *cfg_file_name = "epkowa.conf";
+
+
+typedef struct
+{
+ bool active[_CFG_KEY_ID_TERMINATOR_];
+ list * seen[_CFG_KEY_ID_TERMINATOR_];
+
+} cfg_type;
+
+static cfg_type *_cfg = NULL;
+
+static cfg_key_type _cfg_key[] =
+ {
+ "net",
+ "pio",
+ "scsi",
+ "usb",
+
+ "interpreter",
+ "fs-blacklist",
+
+ "option",
+ };
+
+static char* _opt_vals[] =
+ {
+ "prefer-adf",
+ };
+
+
+static FILE *_cfg_fopen_conf (const char *filename);
+static FILE *_cfg_fopen_data (const char *dirname, const char *key);
+
+
+static cfg_key_id_type _cfg_getline (char **line, size_t *size, FILE *fp);
+
+
+static bool _cfg_is_bare_key (const char *string);
+static bool _cfg_is_valid_net_entry (const char *string);
+static bool _cfg_is_valid_scsi_entry (const char *string);
+static bool _cfg_is_valid_usb_entry (const char *string);
+static bool _cfg_is_valid_interpreter_entry (const char *string);
+static bool _cfg_is_valid_fs_blacklist_entry (const char *string);
+static bool _cfg_is_valid_option_entry (const char *string);
+
+typedef bool (*validator) (const char *);
+
+static validator _cfg_validate[] =
+ {
+ _cfg_is_valid_net_entry,
+ _cfg_is_bare_key,
+ _cfg_is_valid_scsi_entry,
+ _cfg_is_valid_usb_entry,
+
+ _cfg_is_valid_interpreter_entry,
+ _cfg_is_valid_fs_blacklist_entry,
+
+ _cfg_is_valid_option_entry,
+ };
+
+
+static bool _cfg_register_no_op (const char *string);
+static bool _cfg_register_net_entry (const char *string);
+static bool _cfg_register_scsi_entry (const char *string);
+static bool _cfg_register_usb_entry (const char *string);
+static bool _cfg_register_interpreter_entry (const char *string);
+static bool _cfg_register_fs_blacklist_entry (const char *string);
+static bool _cfg_register_option_entry (const char *string);
+
+typedef bool (*registrar) (const char *);
+
+static registrar _cfg_register[] =
+ {
+ _cfg_register_net_entry,
+ _cfg_register_no_op,
+ _cfg_register_scsi_entry,
+ _cfg_register_usb_entry,
+
+ _cfg_register_interpreter_entry,
+ _cfg_register_fs_blacklist_entry,
+
+ _cfg_register_option_entry,
+ };
+
+
+static void _cfg_probe_no_op (list *dev_list);
+static void _cfg_probe_net (list *dev_list);
+static void _cfg_probe_scsi (list *dev_list);
+static void _cfg_probe_usb (list *dev_list);
+static void _cfg_probe_interpreter (list *dev_list);
+
+typedef void (*probe) (list *);
+
+static probe _cfg_probe[] =
+ {
+ _cfg_probe_net,
+ _cfg_probe_no_op,
+ _cfg_probe_scsi,
+ _cfg_probe_usb,
+
+ _cfg_probe_interpreter,
+ _cfg_probe_no_op,
+
+ _cfg_probe_no_op,
+ };
+
+
+static SANE_Status _cfg_attach (SANE_String_Const dev_name, list *dev_list);
+static SANE_String_Const _cfg_get_vendor (SANE_String_Const dev_name);
+static SANE_String_Const _cfg_get_model (SANE_String_Const dev_name);
+static SANE_String_Const _cfg_get_type (SANE_String_Const dev_name);
+
+static bool _cfg_have_interpreter (const char *library, const char *firmware);
+
+/* Helper stuff to hack around sanei_usb/sanei_scsi API requirements
+ used in the various _cfg_probe_*() implementations.
+ */
+static list *_cfg_dev_list = NULL;
+static const char *_cfg_dev_key = NULL;
+static SANE_Status _cfg_scsi_attach (SANE_String_Const dev_name);
+static SANE_Status _cfg_usb_attach (SANE_String_Const dev_name);
+
+
+static void _cfg_net_dtor (void *);
+static void _cfg_scsi_dtor (void *);
+static void _cfg_interpreter_dtor (void *);
+
+typedef void (*destructor) (void *);
+
+static destructor _cfg_dtor[] =
+ {
+ _cfg_net_dtor,
+ free,
+ _cfg_scsi_dtor,
+ free,
+
+ _cfg_interpreter_dtor,
+ free,
+
+ free,
+ };
+
+
+/*! Creates and initialises a configuration object.
+
+ The configuration object is initialised with per key data files in
+ the \a pkgdatadir and a configuration file, \c cfg_file_name. The
+ configuration file is searched for in list of directories with the
+ first file found being used.
+
+ The list of directories can be customised via a \c SANE_CONFIG_DIR
+ environment variable. This variable uses the exact same syntax as
+ the \c PATH environment variable with this difference that a final
+ colon, \c :, appends the default locations.
+
+ When no \c SANE_CONFIG_DIR variable is set, the default locations
+ will be used. The default locations are, in order:
+ - the process' current working directory
+ - the system's SANE configuration directory
+
+ On most system's the above is equivalent to setting:
+ \code
+ SANE_CONFIG_DIR=.:/etc/sane.d
+ \endcode
+ */
+void *
+cfg_init (const char *pkgdatadir, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ char *lc_ctype = NULL;
+ FILE *fp = NULL;
+ cfg_key_id_type id;
+
+ log_call ("(%s, %p)", pkgdatadir, status);
+ require (num_of (_cfg_key) == _CFG_KEY_ID_TERMINATOR_);
+
+ if (_cfg)
+ {
+ if (pkgdatadir) err_minor ("been here, done that");
+ if (status) *status = s;
+ return _cfg;
+ }
+
+ _cfg = t_calloc (1, cfg_type);
+ if (!_cfg)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return _cfg;
+ }
+
+ lc_ctype = setlocale (LC_CTYPE, "C");
+
+ if (pkgdatadir)
+ {
+ /* Look for data files containing information about the
+ * devices we "officially" support. Keywords without a
+ * corresponding data file are not an error.
+ */
+ id = 0;
+ while (num_of (_cfg_key) > id)
+ {
+ fp = _cfg_fopen_data (pkgdatadir, _cfg_key[id]);
+ if (fp)
+ {
+ size_t size = 0;
+ char *line = NULL;
+
+ while (_cfg_getline (&line, &size, fp) == id)
+ {
+ log_info ("line: '%s'", line);
+
+ if (0 != strcmp_c (line, _cfg_key[id]))
+ {
+ _cfg_register[id] (line);
+ }
+ }
+ delete (line);
+ if (fclose (fp))
+ {
+ err_minor ("%s%s%s: %s",
+ pkgdatadir, FILE_SEP_STR, _cfg_key[id],
+ strerror (errno));
+ }
+ }
+ ++id;
+ }
+
+ /* Allow for interpreter based packages to (un)register
+ * themselves upon (un)installation in a suitable place
+ * so we don't have to track them anymore.
+ */
+ id = CFG_KEY_INTERPRETER;
+ fp = _cfg_fopen_data (PKGLOCALSTATEDIR, _cfg_key[id]);
+ if (fp)
+ {
+ size_t size = 0;
+ char *line = NULL;
+
+ while (_cfg_getline (&line, &size, fp) == id)
+ {
+ log_info ("line: '%s'", line);
+
+ if (0 != strcmp_c (line, _cfg_key[id]))
+ {
+ _cfg_register[id] (line);
+ }
+ }
+ delete (line);
+ if (fclose (fp))
+ {
+ err_minor ("%s%s%s: %s",
+ PKGLOCALSTATEDIR, FILE_SEP_STR, _cfg_key[id],
+ strerror (errno));
+ }
+ }
+ }
+
+ fp = _cfg_fopen_conf (cfg_file_name);
+ if (fp)
+ {
+ size_t size = 0;
+ char *line = NULL;
+
+ while (num_of (_cfg_key) != (id = _cfg_getline (&line, &size, fp)))
+ {
+ log_info ("line: '%s'", line);
+
+ _cfg->active[id] = true;
+
+ if (0 != strcmp_c (line, _cfg_key[id]))
+ {
+ _cfg_register[id] (line);
+ }
+ }
+ delete (line);
+ if (fclose (fp))
+ {
+ err_minor ("%s: %s", cfg_file_name, strerror (errno));
+ }
+ }
+
+ setlocale (LC_CTYPE, lc_ctype);
+
+ /* For backwards compatibity with older configuration files.
+ */
+ if (cfg_has (_cfg, CFG_KEY_USB)
+ && 0 < list_size (_cfg->seen[CFG_KEY_INTERPRETER]))
+ {
+ log_info ("enabling interpreter support");
+ cfg_set (_cfg, CFG_KEY_INTERPRETER, true);
+ }
+
+ if (status) *status = s;
+ return _cfg;
+}
+
+/*! Releases resources acquired by a configuration object.
+
+ Always returns \c NULL.
+
+ \sa cfg_init
+ */
+void *
+cfg_exit (void *self)
+{
+ log_call ("(%p)", self);
+ require (_cfg == self);
+
+ if (_cfg)
+ {
+ int id;
+
+ for (id = 0; num_of (_cfg_key) > id; ++id)
+ {
+ if (_cfg->seen[id])
+ {
+ list_destroy (_cfg->seen[id], _cfg_dtor[id]);
+ _cfg->seen[id] = NULL;
+ }
+ }
+
+ delete (_cfg);
+ }
+ return _cfg;
+}
+
+
+/*! Searches for supported devices and adds them to a \a dev_list.
+ */
+void
+cfg_find (const void *self, cfg_key_id_type id, list *dev_list)
+{
+ log_call ("(%p, %u, %p)", self, id, dev_list);
+ require (_cfg && _cfg == self);
+ require (0 <= id && id < num_of (_cfg_key));
+
+ if (!cfg_has (self, id)) return;
+
+ _cfg_probe[id] (dev_list);
+}
+
+/*! Provides read-only access to the supported devices found.
+
+ \bug Access is not read-only.
+ */
+list *
+cfg_seen (const void *self, cfg_key_id_type id)
+{
+ log_call ("(%p, %u)", self, id);
+ require (_cfg && _cfg == self);
+ require (0 <= id && id < num_of (_cfg_key));
+
+ return _cfg->seen[id];
+}
+
+/*! Tells whether configuration for a key \a id is in effect.
+
+ \sa cfg_set
+ */
+bool
+cfg_has (const void *self, cfg_key_id_type id)
+{
+ log_call ("(%p, %u)", self, id);
+ require (_cfg && _cfg == self);
+ require (0 <= id && id < num_of (_cfg_key));
+
+ return _cfg->active[id];
+}
+
+/*! Enables or disabled support for a configuration key.
+
+ This can be used to disable configured functionality when the
+ required components are not available. Think network support
+ and interpreter based scanners.
+ */
+void
+cfg_set (void *self, cfg_key_id_type id, bool value)
+{
+ log_call ("(%p, %u, %u)", self, id, value);
+ require (_cfg && _cfg == self);
+ require (0 <= id && id < num_of (_cfg_key));
+
+ _cfg->active[id] = value;
+}
+
+
+bool
+cfg_has_value (const void *self, cfg_key_id_type id, const char* val)
+{
+ list *seen = cfg_seen (self, id);
+ const char *found = NULL;
+
+ if (val && seen)
+ {
+ list_entry *cur = seen->cur;
+ list_reset (seen);
+
+ while ((found = list_next (seen))
+ && 0 != strcmp_c (val, found))
+ {
+ /* condition does all the processing */
+ }
+ seen->cur = cur;
+ }
+ log_info ("check for %s in %s: %s", val, _cfg_key[id],
+ (NULL != found) ? "found" : "not found");
+ return (NULL != found);
+}
+
+
+/*! Returns the string that corresponds to the key \a id.
+ */
+cfg_key_type
+cfg_key (const void *self, cfg_key_id_type id)
+{
+ log_call ("(%p, %u)", self, id);
+ require (_cfg && _cfg == self);
+ require (0 <= id && id < num_of (_cfg_key));
+
+ return _cfg_key[id];
+}
+
+
+/*! Tries to find and open a configuration file.
+
+ Returns a valid file pointer if successfull, \c NULL otherwise.
+
+ \sa cfg_init
+ */
+static FILE *
+_cfg_fopen_conf (const char *name)
+{
+ const char *default_path = "." PATH_SEP_STR SANE_CONFIG_DIR_NAME;
+
+ char *path = getenv ("SANE_CONFIG_DIR");
+ char *next;
+ char *dir;
+ FILE *fp;
+
+ log_call ("(%s)", name);
+ require (name);
+
+ if (path)
+ {
+ size_t len = strlen (path);
+ char *p;
+
+ if (0 < len && PATH_SEP == path[len-1])
+ {
+ len += strlen (default_path);
+ }
+
+ p = t_malloc (len + 1, char);
+ if (p)
+ {
+ strcpy (p, path);
+ if (strlen (path) < len)
+ {
+ strcat (p, default_path);
+ }
+ path = strdup (p);
+ delete (p);
+ }
+ else
+ {
+ err_major ("SANE_CONFIG_DIR: %s", strerror (ENOMEM));
+ }
+ }
+ else
+ {
+ path = strdup (default_path);
+ }
+
+ if (!path)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ fp = NULL;
+ next = path;
+
+ while (!fp && (dir = strsep (&next, PATH_SEP_STR)))
+ {
+ fp = _cfg_fopen_data (dir, name);
+ }
+ delete (path);
+
+ return fp;
+}
+
+/*! Tries to open a configuration or data file.
+
+ Returns a valid file pointer if successfull, \c NULL otherwise.
+
+ \sa cfg_init
+ */
+static FILE *
+_cfg_fopen_data (const char *dir, const char *name)
+{
+ FILE *fp = NULL;
+ char file[PATH_MAX];
+ int n = snprintf (file, sizeof (file), "%s%c%s", dir, FILE_SEP, name);
+
+ log_call ("(%s, %s)", dir, name);
+ require (dir && name);
+
+ if (sizeof (file) > n)
+ {
+ fp = fopen (file, "rb");
+ if (!fp)
+ {
+ log_info ("%s: %s", file, strerror (errno));
+ }
+ }
+ else
+ {
+ err_minor ("%s%c%s: %s",
+ dir, FILE_SEP, name, strerror (ENAMETOOLONG));
+ }
+ if (fp) log_info ("using '%s'", file);
+
+ return fp;
+}
+
+/*! Returns a validated line of a configuration or data file.
+
+ Validated lines are guaranteed to start with one of the entries in
+ \c _cfg_key and adhere to the syntax for that particular key.
+
+ Comments and empty lines will be silently ignored, invalid entries
+ will be logged and skipped and leading and/or trailing white space
+ as well as trailing comments will be stripped.
+
+ Comments start with a sharp, \c #. An escape mechanism is \e not
+ supported.
+
+ Returns \c _CFG_KEY_ID_TERMINATOR_ if no validated line can be
+ found.
+ */
+static cfg_key_id_type
+_cfg_getline (char **line, size_t *size, FILE *fp)
+{
+ int id = num_of (_cfg_key);
+ bool valid = false;
+ ssize_t n;
+
+ char *lc_ctype;
+
+ require (line && size && fp);
+
+ lc_ctype = setlocale (LC_CTYPE, "C");
+
+ while (!valid && -1 != (n = getline (line, size, fp)))
+ {
+ char *s;
+
+ log_data ("looking at '%s'", *line);
+
+ s = strchr (*line, '#');
+ if (s) *s = '\0'; /* chomp trailing comments */
+
+ s = *line;
+ n = strlen (s);
+
+ while (0 < n && (isspace (*s))) /* whitespace removal */
+ --n, ++s;
+ while (0 < n && (isspace (s[n-1])))
+ --n, s[n] = '\0';
+
+ log_data ("payload is '%s'", s);
+
+ require (strlen (s) == n);
+
+ if (0 < n) /* content validation */
+ {
+ id = 0;
+ while (num_of (_cfg_key) > id
+ && 0 != strncmp_c (s, _cfg_key[id], strlen (_cfg_key[id])))
+ {
+ log_data ("%s !~ %s", _cfg_key[id], s);
+ ++id;
+ }
+
+ valid = (num_of (_cfg_key) > id) && _cfg_validate[id] (s);
+ if (valid && s != *line)
+ {
+ memmove (*line, s, strlen (s) + 1);
+ }
+ if (!valid)
+ {
+ err_major ("invalid: '%s'", s);
+ }
+ }
+ }
+
+ setlocale (LC_CTYPE, lc_ctype);
+
+ return (-1 == n ? num_of (_cfg_key) : id);
+}
+
+/*! Tells whether a \a string consists of a single key.
+ */
+static bool
+_cfg_is_bare_key (const char *string)
+{
+ int id = 0;
+
+ require (string);
+
+ while (num_of (_cfg_key) > id
+ && 0 != strcmp_c (string, _cfg_key[id]))
+ {
+ ++id;
+ }
+ return (num_of (_cfg_key) > id);
+}
+
+/*! Says whether a \a string makes up a valid network device entry.
+ *
+ * Valid network device entries have one of the following formats:
+ * - \c net IPv4-address[( |:)port]
+ * - \c net hostname[( |:)port]
+ *
+ * \todo Tighten up validation!
+ * \todo Consider support for IPv4-address ranges by use of netmasks
+ * or CIDR notation.
+ */
+static bool
+_cfg_is_valid_net_entry (const char *string)
+{
+ const char *p = string;
+
+ int port = 0;
+ char c = '\0';
+
+ require (string);
+
+ if (0 == strcmp_c (string, _cfg_key[CFG_KEY_NET]))
+ return false;
+
+ if (0 != strncmp_c (string, _cfg_key[CFG_KEY_NET],
+ strlen (_cfg_key[CFG_KEY_NET]))
+ || !isspace (string[strlen(_cfg_key[CFG_KEY_NET])]))
+ return false;
+
+ p += strlen (_cfg_key[CFG_KEY_NET]);
+ while (*p && isspace (*p))
+ {
+ ++p;
+ }
+
+ if (1 == sscanf (p, "%*s %d%1s", &port, &c)) return true;
+
+ // check for [ip/host]:[port] format
+ while (*p && !isspace (*p) && ':' != *p)
+ {
+ ++p;
+ }
+ if ('\0' == *p) return true; // case without a port number
+ if (isspace (*p)) return false;
+ if (1 == sscanf (p, ":%d%1s", &port, &c)) return true;
+
+ return false;
+}
+
+/*! Says whether a \a string is a valid SCSI device entry.
+ *
+ * Valid SCSI device entries have the following format:
+ * - \c scsi[ vendor[ model]]
+ *
+ * Vendor and model specifications are case-insensitive.
+ *
+ * \todo Consider support for absolute path names to a device.
+ */
+static bool
+_cfg_is_valid_scsi_entry (const char *string)
+{
+ const char *p = string;
+
+ require (string);
+
+ if (0 == strcmp_c (string, _cfg_key[CFG_KEY_SCSI]))
+ return true;
+
+ if (0 != strncmp_c (string, _cfg_key[CFG_KEY_SCSI],
+ strlen (_cfg_key[CFG_KEY_SCSI]))
+ || !isspace (string[strlen(_cfg_key[CFG_KEY_SCSI])]))
+ return false;
+
+ p += strlen (_cfg_key[CFG_KEY_SCSI]);
+ while (*p && isspace (*p))
+ {
+ ++p;
+ }
+ while (*p && !isspace (*p))
+ {
+ ++p;
+ }
+ if (!*p) return true; /* vendor only */
+
+ while (*p && isspace (*p))
+ {
+ ++p;
+ }
+ while (*p && !isspace (*p))
+ {
+ ++p;
+ }
+ if (!*p) return true; /* vendor model specification */
+
+ return false; /* we have trailing cruft */
+}
+
+/*! Decides whether a \a string constitutes a valid USB entry.
+ *
+ * Valid USB entries have the following format:
+ * - \c usb[ vendor_id prodoct_id]
+ *
+ * Both IDs, if present, shall be \c 0x or \c 0X prefixed sequences
+ * of one to four hexadecimal digits. Case of the digits \c A thru
+ * \c F is irrelevant and may be mixed, even within a single ID.
+ *
+ * \bug Does not catch IDs without intervening whitespace.
+ */
+static bool
+_cfg_is_valid_usb_entry (const char *string)
+{
+ unsigned int vendor;
+ unsigned int product;
+ char x[] = "x"; /* matches 'x' or 'X' */
+ char c = '\0'; /* matches trailing content */
+
+ require (string);
+
+ if (0 == strcmp_c (string, _cfg_key[CFG_KEY_USB]))
+ return true;
+
+ if (0 != strncmp_c (string, _cfg_key[CFG_KEY_USB],
+ strlen (_cfg_key[CFG_KEY_USB]))
+ || !isspace (string[strlen(_cfg_key[CFG_KEY_USB])]))
+ return false;
+
+ return (4 == sscanf (string, "%*s 0%1[xX]%4x 0%1[xX]%4x%1s",
+ x, &vendor, x, &product, &c));
+}
+
+/*! Decides whether a \a string constitutes a valid interpreter entry.
+ *
+ * Valid interpreter entries have the following format:
+ * - \c interpreter usb vendor_id prodoct_id library[ firmware-file]
+ *
+ * Both IDs, if present, shall be \c 0x or \c 0X prefixed sequences
+ * of one to four hexadecimal digits. Case of the digits \c A thru
+ * \c F is irrelevant and may be mixed, even within a single ID.
+ *
+ * \bug Does not catch IDs without intervening whitespace.
+ */
+static bool
+_cfg_is_valid_interpreter_entry (const char *string)
+{
+ const char *p1 = string;
+ const char *p2 = _cfg_key[CFG_KEY_INTERPRETER];
+
+ unsigned int vendor;
+ unsigned int product;
+ char x[] = "x"; /* matches 'x' or 'X' */
+ char c1 = '\0'; /* matches required whitespace */
+ char c2 = '\0'; /* matches start of library-name */
+
+ require (string);
+
+ if (0 != strncmp_c (p1, p2, strlen (p2)))
+ return false;
+
+ p1 += strlen (p2);
+ while (*p1 && isspace (*p1))
+ {
+ ++p1;
+ }
+
+ p2 = _cfg_key[CFG_KEY_USB];
+ if (0 != strncmp_c (p1, p2, strlen (p2))
+ || !isspace (p1[strlen(p2)]))
+ return false;
+
+ return (6 == sscanf (string, "%*s %*s 0%1[xX]%4x 0%1[xX]%4x%c %c",
+ x, &vendor, x, &product, &c1, &c2)
+ && isspace (c1));
+}
+
+/*! Says whether \a string is a valid generic key/value entry.
+ *
+ * Valid entries have the following format:
+ * - \c <key> <value>
+ */
+static bool
+_cfg_is_valid_key_value_entry (cfg_key_id_type key_id, const char *string)
+{
+ require (string);
+
+ if (0 != strncmp_c (string, _cfg_key[key_id],
+ strlen (_cfg_key[key_id]))
+ || !isspace (string[strlen(_cfg_key[key_id])]))
+ return false;
+
+ return true;
+}
+
+/*! Says whether \a string is a valid FS blacklist entry.
+ *
+ * Valid blacklist entries have the following format:
+ * - \c fs-blacklist fw_name
+ */
+static bool
+_cfg_is_valid_fs_blacklist_entry (const char *string)
+{
+ return _cfg_is_valid_key_value_entry (CFG_KEY_FS_BLACKLIST, string);
+}
+
+/*! Says whether \a string is a valid option entry.
+ *
+ * Valid option entries have the following format:
+ * - \c option option_name
+ */
+static bool
+_cfg_is_valid_option_entry (const char *string)
+{
+ return _cfg_is_valid_key_value_entry (CFG_KEY_OPTION, string);
+}
+
+/*! Does \e not register a \a string at all.
+
+ This function should never be called at run-time. It is solely
+ meant as a place holder for use in the \c _cfg_register array.
+
+ Always returns \c false.
+ */
+static bool
+_cfg_register_no_op (const char *string)
+{
+ require (string);
+
+ err_minor ("internal error: '%s'", string);
+ return false;
+}
+
+static bool
+_cfg_register_net_entry (const char *string)
+{
+ cfg_net_info *info = NULL;
+
+ require (string);
+
+ if (!_cfg->seen[CFG_KEY_NET])
+ _cfg->seen[CFG_KEY_NET] = list_create ();
+ if (!_cfg->seen[CFG_KEY_NET]) return false;
+
+ info = t_malloc (1, cfg_net_info);
+ if (info)
+ {
+ char *spec = NULL;
+ int port = 0;
+
+ string += strlen (_cfg_key[CFG_KEY_NET]);
+ while (++string && isspace (*string))
+ {
+ /* condition does all the processing */
+ }
+ spec = strdup (string);
+
+ if (1 == sscanf (spec, "%*s %d", &port))
+ {
+ char *colon;
+ char *p = spec;
+ while (*p && !isspace (*p))
+ {
+ ++p;
+ }
+ *p = ':';
+ colon = p;
+ ++p;
+ while (*p && isspace (*p))
+ {
+ ++p;
+ }
+ memmove (colon + 1, p, strlen (p) + 1);
+ }
+
+ if (list_append (_cfg->seen[CFG_KEY_NET], info))
+ {
+ info->spec = spec;
+ log_info ("registered '%s'", info->spec);
+ }
+ else
+ {
+ delete (spec);
+ delete (info);
+ }
+ }
+
+ return (NULL != info);
+}
+
+/*! Attemps to register supported SCSI device information.
+ */
+static bool
+_cfg_register_scsi_entry (const char *string)
+{
+ cfg_scsi_info *info = NULL;
+
+ require (string);
+
+ if (!_cfg->seen[CFG_KEY_SCSI])
+ _cfg->seen[CFG_KEY_SCSI] = list_create ();
+ if (!_cfg->seen[CFG_KEY_SCSI]) return false;
+
+ info = t_malloc (1, cfg_scsi_info);
+ if (info)
+ {
+ char *vendor = NULL;
+ char *model = NULL;
+
+ sscanf (string, "%*s %as %as", &vendor, &model);
+
+ if (list_append (_cfg->seen[CFG_KEY_SCSI], info))
+ {
+ info->vendor = vendor;
+ info->model = model;
+ log_info ("registered '%s'", string);
+ }
+ else
+ {
+ delete (vendor);
+ delete (model);
+ delete (info);
+ }
+ }
+
+ return (NULL != info);
+}
+
+/*! Attempts to register information for a USB entry.
+ *
+ * Relies on the assumption that the \a string has been validated and
+ * returns \c true if successfull. When not successful, this will be
+ * caused by lack of memory and the function returns \c false.
+ */
+static bool
+_cfg_register_usb_entry (const char *string)
+{
+ cfg_usb_info *info = NULL;
+
+ require (string);
+
+ if (!_cfg->seen[CFG_KEY_USB])
+ _cfg->seen[CFG_KEY_USB] = list_create ();
+ if (!_cfg->seen[CFG_KEY_USB]) return false;
+
+ info = t_malloc (1, cfg_usb_info);
+ if (info)
+ {
+ unsigned int vendor;
+ unsigned int product;
+
+ sscanf (string, "%*s %x %x", &vendor, &product);
+
+ if (list_append (_cfg->seen[CFG_KEY_USB], info))
+ {
+ info->vendor = vendor;
+ info->product = product;
+ log_info ("registered '%s'", string);
+ }
+ else
+ {
+ delete (info);
+ }
+ }
+
+ return (NULL != info);
+}
+
+/*! Tries to register information for an interpreter entry.
+ *
+ * \todo Check for existence of the \a library and \a firmware file.
+ */
+static bool
+_cfg_register_interpreter_entry (const char *string)
+{
+ cfg_interpreter_info *info = NULL;
+
+ require (string);
+
+ if (!_cfg->seen[CFG_KEY_INTERPRETER])
+ _cfg->seen[CFG_KEY_INTERPRETER] = list_create ();
+ if (!_cfg->seen[CFG_KEY_INTERPRETER]) return false;
+
+ info = t_malloc (1, cfg_interpreter_info);
+ if (info)
+ {
+ unsigned int vendor;
+ unsigned int product;
+ char *library = NULL;
+ char *firmware = NULL;
+
+ sscanf (string, "%*s %*s %x %x %as %as",
+ &vendor, &product, &library, &firmware);
+
+ if (library && _cfg_have_interpreter (library, firmware)
+ && list_append (_cfg->seen[CFG_KEY_INTERPRETER], info))
+ {
+ info->vendor = vendor;
+ info->product = product;
+ info->library = library;
+ info->firmware = firmware;
+ log_info ("registered '%s'", string);
+ }
+ else
+ {
+ delete (library);
+ delete (firmware);
+ delete (info);
+ }
+ }
+ return (NULL != info);
+}
+
+/*! Registers information for a generic key/value entry
+ */
+static bool
+_cfg_register_key_value_entry (cfg_key_id_type key_id, const char *string)
+{
+ char *value = NULL;
+
+ require (string);
+
+ if (!_cfg->seen[key_id])
+ _cfg->seen[key_id] = list_create ();
+ if (!_cfg->seen[key_id]) return false;
+
+ string += strlen (_cfg_key[key_id]);
+ while (++string && isspace (*string))
+ {
+ /* condition does all the processing */
+ }
+ value = strdup (string);
+
+ if (!list_append (_cfg->seen[key_id], value))
+ {
+ delete (value);
+ }
+ if (NULL != value)
+ {
+ log_info ("registered '%s %s'", _cfg_key[key_id], value);
+ }
+ return (NULL != value);
+}
+
+/*! Registers information for an FS command blacklist entry
+ */
+static bool
+_cfg_register_fs_blacklist_entry (const char *string)
+{
+ return _cfg_register_key_value_entry (CFG_KEY_FS_BLACKLIST, string);
+}
+
+/*! Registers information for an option entry
+ */
+static bool
+_cfg_register_option_entry (const char *string)
+{
+ int i = 0;
+ bool found = false;
+ const char* value = NULL;
+
+ value = string + strlen (_cfg_key[CFG_KEY_OPTION]);
+ while (++value && isspace (*value))
+ {
+ /* condition does all the processing */
+ }
+
+ for (i=0; i<num_of (_opt_vals); ++i)
+ {
+ if (0 == strcmp_c (value, _opt_vals[i]))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ log_info ("unknown option: '%s'", value);
+ return false;
+ }
+ return _cfg_register_key_value_entry (CFG_KEY_OPTION, string);
+}
+
+/*!
+ */
+static void
+_cfg_probe_no_op (list *dev_list)
+{
+ return;
+}
+
+/* Reuse the SCSI attach function because it does all we need to do,
+ at least for as long as it caters to the sanei_scsi API.
+ */
+#define _cfg_net_attach _cfg_scsi_attach
+
+/*!
+ */
+static void
+_cfg_probe_net (list *dev_list)
+{
+ char* buf = NULL;
+ char* rbuf = NULL;
+
+ size_t size = 0;
+
+ ssize_t n = 0;
+ uint16_t id = 0;
+ uint8_t status;
+
+ void* net = NULL;
+ int sock = -1;
+
+ int i = 0;
+ char* cp;
+
+ list *registry = _cfg->seen[CFG_KEY_NET];
+ cfg_net_info *info = NULL;
+ list_entry *cur;
+
+ require (dev_list);
+ if (!registry) return;
+
+ net = net_init (NULL, NULL);
+ if (net) sock = net_get_sock (net);
+
+ if (!net || 0 > sock)
+ {
+ cfg_set (_cfg, CFG_KEY_NET, false);
+ return;
+ }
+
+ /* construct string from list of scanners */
+
+ cur = registry->cur;
+ list_reset (registry);
+ while ((info = list_next (registry)))
+ {
+ /* +1 for separator */
+ size += strlen (info->spec) + 1;
+ }
+ registry->cur = cur;
+
+ buf = t_malloc (size+1, char); /* +1 for NULL termination character */
+ if (!buf)
+ {
+ cfg_set (_cfg, CFG_KEY_NET, false);
+ return;
+ }
+ memset (buf, 0, size+1);
+
+ cur = registry->cur;
+ list_reset (registry);
+ while ((info = list_next (registry)))
+ {
+ strcat (buf, info->spec);
+ strcat (buf, "\n");
+ }
+ registry->cur = cur;
+
+ log_info ("Probe network:\n%s", buf);
+
+ /* send string of NULL separated scanner ips to the network plugin */
+ for (i=0; i<size; ++i) if ('\n' == buf[i]) buf[i] = '\0';
+ n = ipc_send (sock, 0, TYPE_LIST, size, buf);
+ delete (buf);
+
+ if (n != size)
+ {
+ log_info ("Communication error occurred. Disabling network plugin.");
+ cfg_set (_cfg, CFG_KEY_NET, false);
+ return;
+ }
+
+ /* receive a string of available network scanners */
+ n = -1;
+ int tries = 3;
+ while (0 > n && 0 < tries)
+ {
+ n = ipc_recv (sock, &id, &status, (void **) &rbuf);
+ --tries;
+ }
+
+ /* bail if no network scanners were found or something bad happened */
+ if (0 >= n || 0 == strlen (rbuf) || status != STATUS_OK)
+ {
+ log_info ("No network scanners detected. Disabling network plugin.");
+ cfg_set (_cfg, CFG_KEY_NET, false);
+ delete (rbuf);
+ return;
+ }
+
+ /* process the scanner listing string */
+ _cfg_dev_list = dev_list;
+ _cfg_dev_key = _cfg_key[CFG_KEY_NET];
+
+ cp = rbuf;
+ for (i=0; i<n; ++i)
+ {
+ if ('\0' == rbuf[i])
+ {
+ log_info ("Detected network scanner: %s", cp);
+ _cfg_net_attach (cp);
+ cp = rbuf + i + 1;
+ }
+ }
+
+ _cfg_dev_key = NULL;
+ _cfg_dev_list = NULL;
+
+ delete (rbuf);
+
+ return;
+}
+
+/*!
+ */
+static void
+_cfg_probe_scsi (list *dev_list)
+{
+ list *registry = _cfg->seen[CFG_KEY_SCSI];
+ cfg_scsi_info *info = NULL;
+ list_entry *cur;
+
+ require (dev_list);
+ if (!registry) return;
+
+ cur = registry->cur;
+ list_reset (registry);
+ while ((info = list_next (registry)))
+ {
+ _cfg_dev_list = dev_list;
+ _cfg_dev_key = _cfg_key[CFG_KEY_SCSI];
+ sanei_scsi_find_devices (info->vendor, info->model, NULL,
+ -1, -1, -1, -1, _cfg_scsi_attach);
+ _cfg_dev_key = NULL;
+ _cfg_dev_list = NULL;
+ }
+ registry->cur = cur;
+
+ return;
+}
+
+static void
+_cfg_probe_usb (list *dev_list)
+{
+ list *registry = _cfg->seen[CFG_KEY_USB];
+ cfg_usb_info *info = NULL;
+ list_entry *cur;
+
+ require (dev_list);
+ if (!registry) return;
+
+ cur = registry->cur;
+ list_reset (registry);
+ while ((info = list_next (registry)))
+ {
+ _cfg_dev_list = dev_list;
+ _cfg_dev_key = _cfg_key[CFG_KEY_USB];
+ sanei_usb_find_devices (info->vendor, info->product, _cfg_usb_attach);
+ _cfg_dev_key = NULL;
+ _cfg_dev_list = NULL;
+ }
+ registry->cur = cur;
+
+ return;
+}
+
+/*!
+ */
+static void
+_cfg_probe_interpreter (list *dev_list)
+{
+ list *registry = _cfg->seen[CFG_KEY_INTERPRETER];
+ cfg_interpreter_info *info = NULL;
+ list_entry *cur;
+
+ require (dev_list);
+ if (!registry) return;
+
+ cur = registry->cur;
+ list_reset (registry);
+ while ((info = list_next (registry)))
+ {
+ _cfg_dev_list = dev_list;
+ _cfg_dev_key = _cfg_key[CFG_KEY_INTERPRETER];
+ sanei_usb_find_devices (info->vendor, info->product, _cfg_usb_attach);
+ _cfg_dev_key = NULL;
+ _cfg_dev_list = NULL;
+ }
+ registry->cur = cur;
+
+ return;
+}
+
+static SANE_Status
+_cfg_scsi_attach (SANE_String_Const dev_name)
+{
+ SANE_String name = NULL;
+ size_t len_name = (strlen (_cfg_dev_key) + strlen (DEV_NAME_SEP_STR)
+ + strlen (dev_name) + 1);
+
+ name = t_malloc (len_name, SANE_Char);
+ if (name)
+ {
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ strcpy (name, _cfg_dev_key);
+ strcat (name, DEV_NAME_SEP_STR);
+ strcat (name, dev_name);
+
+ s = _cfg_attach (name, _cfg_dev_list);
+ if (SANE_STATUS_NO_MEM == s)
+ {
+ delete (name);
+ }
+ return s;
+ }
+ return SANE_STATUS_NO_MEM;
+}
+
+static SANE_Status
+_cfg_usb_attach (SANE_String_Const dev_name)
+{
+ const char *sanei_usb_prefix = "libusb:";
+
+ SANE_String name = NULL;
+ size_t len_name = (strlen (_cfg_dev_key) + strlen (DEV_NAME_SEP_STR)
+ + strlen (dev_name) + 1);
+
+ if (0 == strncmp_c (dev_name, sanei_usb_prefix, strlen (sanei_usb_prefix)))
+ {
+ len_name -= strlen (sanei_usb_prefix);
+ dev_name += strlen (sanei_usb_prefix);
+ }
+
+ name = t_malloc (len_name, SANE_Char);
+ if (name)
+ {
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ strcpy (name, _cfg_dev_key);
+ strcat (name, DEV_NAME_SEP_STR);
+ strcat (name, dev_name);
+
+ s = _cfg_attach (name, _cfg_dev_list);
+ if (SANE_STATUS_NO_MEM == s)
+ {
+ delete (name);
+ }
+ return s;
+ }
+ return SANE_STATUS_NO_MEM;
+}
+
+/*! Creates a SANE_Device and attaches it to the \a dev_list.
+ *
+ * While perhaps not particularly user-friendly, it is perfectly okay
+ * to attach a SANE_Device with the vendor, model and type fields set
+ * to something that does not exactly correspond with the device that
+ * is dangling on the other end of the connection.
+ *
+ * We may not be able to get reliable information for several reasons,
+ * not in the least because of (intermittent?) failure to communicate
+ * with the device.
+ *
+ * Returns SANE_STATUS_NO_MEM if a SANE_Device could not be created
+ * and added to the \a dev_list, SANE_STATUS_GOOD otherwise. Note
+ * that in the latter case only the name field is guaranteed to be
+ * not \c NULL.
+ */
+static SANE_Status
+_cfg_attach (SANE_String_Const dev_name, list *dev_list)
+{
+ SANE_Device *dev = t_malloc (1, SANE_Device);
+
+ require (dev_name);
+
+ if (!dev || !list_append (dev_list, dev))
+ {
+ delete (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->name = dev_name;
+
+ dev->vendor = _cfg_get_vendor (dev_name);
+ dev->model = _cfg_get_model (dev_name);
+ dev->type = _cfg_get_type (dev_name);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*! Returns a best effort vendor name for a device.
+ *
+ * \todo Remove hard-coding (as soon as we support non-Epson
+ * devices).
+ */
+static SANE_String_Const
+_cfg_get_vendor (SANE_String_Const dev_name)
+{
+ return strdup ("Epson");
+}
+
+/*! Returns a best effort model name for a device.
+ */
+static SANE_String_Const
+_cfg_get_model (SANE_String_Const dev_name)
+{
+ SANE_String_Const model = NULL;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ char *fw_name = NULL;
+ channel *ch = NULL;
+
+ require (dev_name);
+
+ ch = channel_create (dev_name, &status);
+ if (!ch || SANE_STATUS_GOOD != status)
+ {
+ err_minor ("%s", sane_strstatus (status));
+ }
+ else
+ {
+ ch->open (ch, &status);
+ if (SANE_STATUS_GOOD == status)
+ {
+ fw_name = get_fw_name (ch);
+ }
+ ch->close (ch, NULL);
+ ch = ch->dtor (ch);
+ }
+
+ log_info ("F/W name: '%s'", fw_name);
+
+ model = model_info_cache_get_model (fw_name);
+ delete (fw_name);
+
+ return model;
+}
+
+/*! Returns a best effort type description for a device.
+ *
+ * \todo Figure out how to deal with the predefined strings in the
+ * face of devices that have a flatbed and ADF and/or TPU.
+ * Then there is also the various ideas people have about
+ * multi-function peripherals. Are these SPC's, all-in-ones,
+ * MFPPs or even plain scanners with both a flatbed and ADF?
+ */
+static SANE_String_Const
+_cfg_get_type (SANE_String_Const dev_name)
+{
+ return strdup ("flatbed scanner");
+}
+
+/*! Divines whether a certain interpreter is usable.
+ */
+static bool
+_cfg_have_interpreter (const char *library, const char *firmware)
+{
+ require (library);
+
+ return true;
+}
+
+
+static void
+_cfg_net_dtor (void *self)
+{
+ cfg_net_info *p = self;
+
+ if (!p) return;
+
+ const_delete (p->spec, char *);
+ delete (p);
+}
+
+static void
+_cfg_scsi_dtor (void *self)
+{
+ cfg_scsi_info *p = self;
+
+ if (!p) return;
+
+ const_delete (p->vendor, char *);
+ const_delete (p->model , char *);
+ delete (p);
+}
+
+static void
+_cfg_interpreter_dtor (void *self)
+{
+ cfg_interpreter_info *p = self;
+
+ if (!p) return;
+
+ const_delete (p->library , char *);
+ const_delete (p->firmware, char *);
+ delete (p);
+}
diff --git a/backend/cfg-obj.h b/backend/cfg-obj.h
new file mode 100644
index 0000000..1e414fa
--- /dev/null
+++ b/backend/cfg-obj.h
@@ -0,0 +1,151 @@
+/* cfg-obj.h -- configuration objects
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef included_cfg_obj_h
+#define included_cfg_obj_h
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <sane/sane.h>
+
+#include "list.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ extern const char *cfg_file_name;
+
+ typedef enum
+ {
+ CFG_KEY_NET = 0,
+ CFG_KEY_PIO,
+ CFG_KEY_SCSI,
+ CFG_KEY_USB,
+
+ CFG_KEY_INTERPRETER,
+ CFG_KEY_FS_BLACKLIST,
+
+ CFG_KEY_OPTION,
+
+ _CFG_KEY_ID_TERMINATOR_
+ }
+ cfg_key_id_type;
+
+ typedef const char * cfg_key_type;
+
+ typedef struct
+ {
+ const char *spec;
+
+ } cfg_net_info;
+
+ typedef struct
+ {
+ const char *vendor;
+ const char *model;
+
+ } cfg_scsi_info;
+
+ typedef struct
+ {
+ uint16_t vendor;
+ uint16_t product;
+
+ } cfg_usb_info;
+
+ typedef struct
+ {
+ uint16_t vendor;
+ uint16_t product;
+ const char *library;
+ const char *firmware;
+
+ } cfg_interpreter_info;
+
+
+ void * cfg_init (const char *pkgdatadir, SANE_Status *status);
+ void * cfg_exit (void *self);
+
+ void cfg_find (const void *self, cfg_key_id_type id, list *dev_list);
+ list * cfg_seen (const void *self, cfg_key_id_type id);
+
+ bool cfg_has (const void *self, cfg_key_id_type id);
+ void cfg_set (void *self, cfg_key_id_type id, bool value);
+
+ bool cfg_has_value (const void *self, cfg_key_id_type id, const char* val);
+
+ cfg_key_type cfg_key (const void *self, cfg_key_id_type id);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (included_cfg_obj_h) */
diff --git a/backend/channel-net.c b/backend/channel-net.c
new file mode 100644
index 0000000..38439fd
--- /dev/null
+++ b/backend/channel-net.c
@@ -0,0 +1,242 @@
+/* channel_net.c -- network device communication channel
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ * \brief Implements a network device communication channel.
+ * \todo Deal correctly with \c SIGPIPE.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "channel.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include "ipc.h"
+#include "net-obj.h"
+#include "utils.h"
+#include "hw-data.h"
+
+
+static void channel_net_open (channel *, SANE_Status *);
+static void channel_net_close (channel *, SANE_Status *);
+
+static ssize_t channel_net_send (channel *, const void *,
+ size_t, SANE_Status *);
+static ssize_t channel_net_recv (channel *, void *,
+ size_t, SANE_Status *);
+
+
+channel *
+channel_net_ctor (channel *self, const char *dev_name, SANE_Status *status)
+{
+ log_call ("(%p, '%s', %p)", self, dev_name, status);
+
+ if (status) *status = SANE_STATUS_GOOD;
+
+ require (self && dev_name);
+ require (0 == strncmp_c (dev_name, "net:", strlen ("net:")));
+
+ self->name = strdup (dev_name);
+ if (!self->name)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return self->dtor (self);
+ }
+
+ self->open = channel_net_open;
+ self->close = channel_net_close;
+
+ self->send = channel_net_send;
+ self->recv = channel_net_recv;
+
+ return self;
+}
+
+/*! Each call must consist of a complete handshake step.
+ * Cannot send in parts.
+ */
+static ssize_t
+channel_net_send (channel *self, const void *buffer,
+ size_t size, SANE_Status *status)
+{
+ ssize_t n = 0;
+ if (0 > self->fd)
+ {
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ return -1;
+ }
+ if (status) *status = SANE_STATUS_GOOD;
+
+ require (self && buffer);
+ require (0 < self->id);
+
+ n = ipc_send (self->fd, self->id, TYPE_ESC, size, buffer);
+ if (n != size)
+ {
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ }
+ return n;
+}
+
+/*! Each call must consist of a complete handshake step.
+ * Cannot receive in parts.
+ */
+static ssize_t
+channel_net_recv (channel *self, void *buffer,
+ size_t size, SANE_Status *status)
+{
+ char* rbuf = NULL;
+ uint16_t id = 0;
+ uint8_t ipc_status = STATUS_OK;
+ ssize_t n = 0;
+
+ if (0 > self->fd)
+ {
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ return -1;
+ }
+ if (status) *status = SANE_STATUS_GOOD;
+
+ require (self && buffer);
+ require (0 < self->id);
+
+ n = ipc_recv (self->fd, &id, &ipc_status, (void**)&rbuf);
+ if (n != size) err_major ("expected %zd bytes, received %zd bytes", size, n);
+ if (!rbuf || id != self->id || STATUS_OK != ipc_status || n != size)
+ {
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ delete (rbuf);
+ return -1;
+ }
+
+ memcpy (buffer, rbuf, n);
+ delete (rbuf);
+ return n;
+}
+
+static void
+channel_net_open (channel *self, SANE_Status *status)
+{
+ void* net = NULL;
+ ssize_t n = 0;
+
+ uint8_t ipc_status = STATUS_OK;
+
+ char* scanner = self->name + strlen ("net:");
+
+ if (status) *status = SANE_STATUS_GOOD;
+
+ net = net_init ("", NULL);
+ if (net) self->fd = net_get_sock (net);
+ if (!net || 0 > self->fd)
+ {
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ return;
+ }
+
+ n = ipc_send (self->fd, 0, TYPE_OPEN, strlen (scanner), scanner);
+ if (n != strlen (scanner))
+ {
+ self->fd = -1;
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ return;
+ }
+
+ n = ipc_recv (self->fd, &self->id, &ipc_status, NULL);
+
+ if (STATUS_OK != ipc_status || 0 != n)
+ {
+ self->id = 0;
+ self->fd = -1;
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ return;
+ }
+
+ log_info ("Opened network scanner at: %s", scanner);
+}
+
+static void
+channel_net_close (channel *self, SANE_Status *status)
+{
+ ssize_t n = 0;
+
+ if (status) *status = SANE_STATUS_GOOD;
+
+ n = ipc_send (self->fd, self->id, TYPE_CLOSE, 0, NULL);
+ self->id = 0;
+ self->fd = -1;
+ if (0 != n)
+ {
+ if (status) *status = SANE_STATUS_IO_ERROR;
+ log_info ("failed to close network scanner: %s",
+ self->name + strlen ("net:"));
+ return;
+ }
+ log_info ("closed network scanner: %s", self->name + strlen ("net:"));
+}
diff --git a/backend/channel-pio.c b/backend/channel-pio.c
new file mode 100644
index 0000000..c4f202c
--- /dev/null
+++ b/backend/channel-pio.c
@@ -0,0 +1,165 @@
+/* channel_pio.c -- parallel device communication channel
+ * Copyright (C) 2008, 2009, 2013 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ \brief Implements a parallel device communication channel.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "channel.h"
+
+#include <string.h>
+
+#include "utils.h"
+
+/* Deprecated includes */
+#include "sane/sanei_pio.h"
+
+
+static void channel_pio_open (channel *, SANE_Status *);
+static void channel_pio_close (channel *, SANE_Status *);
+
+static ssize_t channel_pio_send (channel *, const void *,
+ size_t, SANE_Status *);
+static ssize_t channel_pio_recv (channel *, void *,
+ size_t, SANE_Status *);
+
+static void channel_pio_set_max_request_size (channel *, size_t);
+
+channel *
+channel_pio_ctor (channel *self, const char *dev_name, SANE_Status *status)
+{
+ require (self && dev_name);
+ require (0 == strncmp_c (dev_name, "pio:", strlen ("pio:")));
+
+ self->open = channel_pio_open;
+ self->close = channel_pio_close;
+
+ self->send = channel_pio_send;
+ self->recv = channel_pio_recv;
+
+ self->set_max_request_size = channel_pio_set_max_request_size;
+
+ if (status) *status = SANE_STATUS_UNSUPPORTED;
+ return self->dtor (self);
+}
+
+static ssize_t
+channel_pio_send (channel *self, const void *buffer,
+ size_t size, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+ ssize_t n = sanei_pio_write (self->fd, buffer, size);
+
+ if (size != n)
+ s = SANE_STATUS_INVAL;
+ if (status) *status = s;
+
+ return n;
+}
+
+static ssize_t
+channel_pio_recv (channel *self, void *buffer,
+ size_t size, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+ ssize_t n = sanei_pio_read (self->fd, buffer, (size_t) size);
+
+ if (size != n)
+ s = SANE_STATUS_INVAL;
+ if (status) *status = s;
+
+ return n;
+}
+
+static void
+channel_pio_open (channel *self, SANE_Status *status)
+{
+ SANE_Status s = sanei_pio_open (self->name, &self->fd);
+
+ if (SANE_STATUS_GOOD != s)
+ {
+ err_fatal ("can not open %s (%s)", self->name, sane_strstatus (s));
+ }
+ if (status) *status = s;
+}
+
+static void
+channel_pio_close (channel *self, SANE_Status *status)
+{
+ sanei_pio_close (self->fd);
+ self->fd = -1;
+}
+
+static void
+channel_pio_set_max_request_size (channel *self, size_t size)
+{
+ require (self);
+
+ self->max_size = (size < (32 * 1024)) ? size : (32 * 1024);
+}
diff --git a/backend/channel-scsi.c b/backend/channel-scsi.c
new file mode 100644
index 0000000..20c9e93
--- /dev/null
+++ b/backend/channel-scsi.c
@@ -0,0 +1,170 @@
+/* channel_scsi.c -- SCSI device communication channel
+ * Copyright (C) 2008, 2009, 2013 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ \brief Implements a SCSI communication channel.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define USE_PROTECTED_CHANNEL_API
+#include "channel.h"
+
+#include <string.h>
+
+#include "utils.h"
+
+/* Deprecated includes */
+#include "epkowa_scsi.h"
+
+
+static void channel_scsi_open (channel *, SANE_Status *);
+static void channel_scsi_close (channel *, SANE_Status *);
+
+static ssize_t channel_scsi_send (channel *, const void *,
+ size_t, SANE_Status *);
+static ssize_t channel_scsi_recv (channel *, void *,
+ size_t, SANE_Status *);
+
+static void channel_scsi_set_max_request_size (channel *, size_t);
+
+
+channel *
+channel_scsi_ctor (channel *self, const char *dev_name, SANE_Status *status)
+{
+ size_t name_len = 0;
+
+ require (self && dev_name);
+ require (0 == strncmp_c (dev_name, "scsi:", strlen ("scsi:")));
+
+ dev_name += strlen ("scsi:");
+ name_len = strlen (dev_name) + 1;
+
+ self->name = t_malloc (name_len, char);
+ if (!self->name)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return self->dtor (self);
+ }
+ strcpy (self->name, dev_name);
+
+ self->open = channel_scsi_open;
+ self->close = channel_scsi_close;
+
+ self->send = channel_scsi_send;
+ self->recv = channel_scsi_recv;
+
+ self->set_max_request_size = channel_scsi_set_max_request_size;
+
+ self->max_size = sanei_scsi_max_request_size;
+
+ return self;
+}
+
+static ssize_t
+channel_scsi_send (channel *self, const void *buffer,
+ size_t size, SANE_Status *status)
+{
+ return sanei_epson_scsi_write (self->fd, buffer, size, status);
+}
+
+static ssize_t
+channel_scsi_recv (channel *self, void *buffer,
+ size_t size, SANE_Status *status)
+{
+ return sanei_epson_scsi_read (self->fd, buffer, size, status);
+}
+
+static void
+channel_scsi_open (channel *self, SANE_Status *status)
+{
+ SANE_Status s = sanei_scsi_open (self->name, &self->fd,
+ sanei_epson_scsi_sense_handler, NULL);
+ if (SANE_STATUS_GOOD != s)
+ {
+ err_fatal ("can not open %s (%s)", self->name, sane_strstatus (s));
+ }
+ if (status) *status = s;
+}
+
+static void
+channel_scsi_close (channel *self, SANE_Status *status)
+{
+ sanei_scsi_close (self->fd);
+ self->fd = -1;
+
+ if (status) *status = SANE_STATUS_GOOD;
+}
+
+static void
+channel_scsi_set_max_request_size (channel *self, size_t size)
+{
+ require (self);
+
+ self->max_size = (size < sanei_scsi_max_request_size)
+ ? size : sanei_scsi_max_request_size;
+}
diff --git a/backend/channel-usb.c b/backend/channel-usb.c
new file mode 100644
index 0000000..f83dcb1
--- /dev/null
+++ b/backend/channel-usb.c
@@ -0,0 +1,285 @@
+/* channel_usb.c -- USB device communication channel
+ * Copyright (C) 2008, 2009, 2013 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ \brief Implements a USB communication channel.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define USE_PROTECTED_CHANNEL_API
+#include "channel.h"
+
+#include <string.h>
+
+#include "utils.h"
+
+/* Deprecated includes */
+#include "include/sane/sanei_usb.h"
+#include "epkowa_ip.h"
+
+
+static void channel_usb_open (channel *, SANE_Status *);
+static void channel_usb_close (channel *, SANE_Status *);
+
+static ssize_t channel_usb_send (channel *, const void *,
+ size_t, SANE_Status *);
+static ssize_t channel_usb_recv (channel *, void *,
+ size_t, SANE_Status *);
+
+
+channel *
+channel_usb_ctor (channel *self, const char *dev_name, SANE_Status *status)
+{
+ size_t name_len = 0;
+
+ require (self && dev_name);
+ require (0 == strncmp_c (dev_name, "usb:", strlen ("usb:")));
+
+ dev_name += strlen ("usb:");
+ name_len = strlen ("libusb:") + strlen (dev_name) + 1;
+
+ self->name = t_malloc (name_len, char);
+ if (!self->name)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return self->dtor (self);
+ }
+ self->name[0] = '\0';
+ strcat (self->name, "libusb:");
+ strcat (self->name, dev_name);
+
+ self->open = channel_usb_open;
+ self->close = channel_usb_close;
+
+ self->send = channel_usb_send;
+ self->recv = channel_usb_recv;
+
+ self->max_size = 128 * 1024;
+
+ return self;
+}
+
+static ssize_t
+channel_usb_send (channel *self, const void *buffer,
+ size_t size, SANE_Status *status)
+{
+ ssize_t n = size;
+
+ if (self->interpreter)
+ {
+ n = self->interpreter->send (self, buffer, size, status);
+ }
+ else
+ {
+ SANE_Status s;
+ s = sanei_usb_write_bulk (self->fd, buffer, (size_t *)&n);
+ if (status) *status = s;
+ }
+
+ return n;
+}
+
+static ssize_t
+channel_usb_recv (channel *self, void *buffer,
+ size_t size, SANE_Status *status)
+{
+ ssize_t n = size;
+
+ if (self->interpreter)
+ {
+ n = self->interpreter->recv (self, buffer, size, status);
+ }
+ else
+ {
+ SANE_Status s = SANE_STATUS_GOOD;
+ s = sanei_usb_read_bulk (self->fd, (SANE_Byte *) buffer, (size_t *)&n);
+ if (status) *status = s;
+ }
+
+ return n;
+}
+
+static void
+channel_usb_open (channel *self, SANE_Status *status)
+{
+ SANE_Status s;
+
+ s = sanei_usb_open (self->name, &self->fd);
+
+ if (SANE_STATUS_GOOD == s)
+ {
+ SANE_Word product_id = -1;
+
+ sanei_usb_get_vendor_product (self->fd, NULL, &product_id);
+
+ if (-1 != product_id)
+ {
+ self->id = product_id;
+ }
+ }
+
+ if (self->interpreter && SANE_STATUS_GOOD == s)
+ {
+ if (0 > self->interpreter->open (self))
+ {
+ s = SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (status) *status = s;
+}
+
+static void
+channel_usb_close (channel *self, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ if (self->interpreter)
+ {
+ self->interpreter->close (self);
+ }
+
+ sanei_usb_close (self->fd);
+ self->fd = -1;
+ if (status) *status = s;
+}
+
+
+static channel * channel_interpreter_dtor (channel *self);
+
+channel *
+channel_interpreter_ctor (channel *self, const char *dev_name,
+ SANE_Status *status)
+{
+ char *name = NULL;
+ size_t name_len = 0;
+
+ require (self && dev_name);
+ require (0 == strncmp_c (dev_name, "interpreter:", strlen ("interpreter:")));
+
+ dev_name += strlen ("interpreter:");
+ name_len = strlen ("usb:") + strlen (dev_name) + 1;
+
+ name = t_malloc (name_len, char);
+ if (!name)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return self->dtor (self);
+ }
+ strcpy (name, "usb:");
+ strcat (name, dev_name);
+
+ self = channel_usb_ctor (self, name, status);
+ delete (name);
+
+ if (self)
+ {
+ SANE_Status s = SANE_STATUS_GOOD;
+ SANE_Word vendor;
+ SANE_Word product;
+
+ self->open (self, &s);
+ if (SANE_STATUS_GOOD == s)
+ {
+ s = sanei_usb_get_vendor_product (self->fd,
+ &vendor, &product);
+ }
+ self->close (self, NULL);
+ if (SANE_STATUS_GOOD == s)
+ {
+ s = create_interpreter (self, product);
+ }
+
+ if (!self->interpreter)
+ {
+ if (status) *status = s;
+ return self->dtor (self);
+ }
+ else
+ {
+ self->dtor = channel_interpreter_dtor;
+ }
+ }
+
+ self->max_size = 32 * 1024;
+
+ return self;
+}
+
+static channel *
+channel_interpreter_dtor (channel *self)
+{
+ require (self);
+
+ if (self->interpreter)
+ {
+ self->interpreter->dtor (self);
+ }
+ self->dtor = channel_dtor;
+ return self->dtor (self);
+}
diff --git a/backend/channel.c b/backend/channel.c
new file mode 100644
index 0000000..754f640
--- /dev/null
+++ b/backend/channel.c
@@ -0,0 +1,321 @@
+/* channel.c -- device communication channel
+ * Copyright (C) 2008, 2009, 2013 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ \brief Implements a hardware communication channel.
+
+ Hardware channels supported are usb, scsi and parallel.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define USE_PROTECTED_CHANNEL_API
+#include "channel.h"
+
+#include "utils.h"
+
+extern channel *
+channel_net_ctor (channel *self, const char *dev_name, SANE_Status *status);
+
+extern channel *
+channel_pio_ctor (channel *self, const char *dev_name, SANE_Status *status);
+
+extern channel *
+channel_scsi_ctor (channel *self, const char *dev_name, SANE_Status *status);
+
+extern channel *
+channel_usb_ctor (channel *self, const char *dev_name, SANE_Status *status);
+
+extern channel *
+channel_interpreter_ctor (channel *self,
+ const char *dev_name, SANE_Status *status);
+
+
+#include <string.h>
+
+#include "epkowa_ip.h"
+
+#include "hw-data.h"
+
+
+/*! A ::channel factory method.
+ *
+ * Creates and initializes a channel object to match a \a dev_name.
+ *
+ * \todo Validate \a dev_name?
+ */
+channel *
+channel_create (const char *dev_name, SANE_Status *status)
+{
+ channel *ch = NULL;
+
+ require (dev_name);
+
+ if (status) *status = SANE_STATUS_GOOD;
+
+ ch = t_calloc (1, channel);
+ if (!ch)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+
+ ch->dtor = channel_dtor;
+ ch->is_open = channel_is_open;
+ ch->max_request_size = channel_max_request_size;
+ ch->set_max_request_size = channel_set_max_request_size;
+
+ ch->fd = -1;
+ ch->id = 0;
+ ch->max_size = 32 * 1024;
+
+ if (0 == strncmp_c (dev_name, "net:", strlen ("net:")))
+ {
+ ch->ctor = channel_net_ctor;
+ ch->type = CHAN_NET;
+ }
+ if (0 == strncmp_c (dev_name, "pio:", strlen ("pio:")))
+ {
+ ch->ctor = channel_pio_ctor;
+ ch->type = CHAN_PIO;
+ }
+ if (0 == strncmp_c (dev_name, "scsi:", strlen ("scsi:")))
+ {
+ ch->ctor = channel_scsi_ctor;
+ ch->type = CHAN_SCSI;
+ }
+ if (0 == strncmp_c (dev_name, "usb:", strlen ("usb:")))
+ {
+ ch->ctor = channel_usb_ctor;
+ ch->type = CHAN_USB;
+ }
+ if (0 == strncmp_c (dev_name, "interpreter:", strlen ("interpreter:")))
+ {
+ ch->ctor = channel_interpreter_ctor;
+ ch->type = CHAN_INTERP;
+ }
+
+ if (!ch->ctor)
+ {
+ err_major ("unsupported channel for '%s'", dev_name);
+ if (status) *status = SANE_STATUS_UNSUPPORTED;
+ delete (ch);
+ return NULL;
+ }
+
+ return ch->ctor (ch, dev_name, status);
+}
+
+/*! Logging wrapper around a channel's send() method.
+ */
+ssize_t
+channel_send (channel* ch, const void *buffer, size_t size,
+ SANE_Status *status)
+{
+ ssize_t n = 0;
+
+ log_call ("(%zd)", size);
+ dbg_hex (buffer, size);
+
+ n = ch->send (ch, buffer, size, status);
+
+ log_call ("transferred %zd bytes", n);
+ return n;
+}
+
+/*! Logging wrapper around a channel's recv() method.
+ */
+ssize_t
+channel_recv (channel *ch, void *buffer, size_t size, SANE_Status *status)
+{
+ ssize_t n = 0;
+
+ log_call ("(%zd)", size);
+
+ if (size < 256)
+ memset (buffer, 0x00, size);
+
+ n = ch->recv (ch, buffer, size, status);
+
+ if (0 < n)
+ {
+ if (size < 256)
+ { dbg_hex (buffer, n); }
+ else
+ { dbg_img (buffer, n); }
+ }
+
+ log_call ("transferred %zd bytes", n);
+ return n;
+}
+
+/*! Throttle the number of bytes read in a single go
+ */
+static ssize_t
+channel_recv_throttle (channel *ch, void *buffer, size_t size,
+ SANE_Status *status)
+{
+ size_t max = ch->max_request_size (ch);
+ return ch->recv (ch, buffer, size < max ? size : max, status);
+}
+
+ssize_t channel_recv_all (channel *ch, void *buffer,
+ size_t size, SANE_Status *status)
+{
+ return channel_recv_all_retry (ch, buffer, size, 1, status);
+}
+
+ssize_t
+channel_recv_all_retry (channel *ch, void *buffer, size_t size,
+ size_t max_attempts, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ ssize_t n = 0;
+ ssize_t t = 0;
+ size_t attempts = 0;
+
+ log_call ("(%zd)", size);
+
+ while (n < size && attempts < max_attempts)
+ {
+ t = channel_recv_throttle (ch, buffer + n, size - n, &s);
+ if (SANE_STATUS_GOOD != s || 0 >= t)
+ {
+ ++attempts;
+ log_info ("attempts: %zd/%zd", attempts, max_attempts);
+ }
+ if (0 < t) n += t;
+ log_call ("transferred %zd bytes, total %zd/%zd", t, n, size);
+ }
+
+ if (0 < n)
+ {
+ if (size < 256)
+ { dbg_hex (buffer, n); }
+ else
+ { dbg_img (buffer, n); }
+ }
+
+ if (status) *status = s;
+
+ return n;
+}
+
+/*! Tells whether a channel is ready to send() and recv() data.
+ */
+bool
+channel_is_open (const struct channel *self)
+{
+ return (self && 0 <= self->fd);
+}
+
+/*! Indicates the maximum number of bytes the channel should read
+ * in a singe request.
+ */
+size_t
+channel_max_request_size (const struct channel *self)
+{
+ require (self);
+
+ return self->max_size;
+}
+
+/*! Change the maximum number of bytes a channel should read in a
+ * single request.
+ */
+void
+channel_set_max_request_size (struct channel *self, size_t size)
+{
+ require (self);
+
+ self->max_size = size;
+}
+
+/*! "Base class" destructor.
+ */
+channel *
+channel_dtor (struct channel *self)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ("(fd = %d)", self->fd);
+
+ if (!self) return NULL;
+
+ if (self->interpreter) self->interpreter->dtor (self);
+
+ if (self->is_open (self))
+ {
+ self->close (self, &status);
+ }
+
+ delete (self->name);
+ delete (self);
+
+ return NULL;
+}
diff --git a/backend/channel.h b/backend/channel.h
new file mode 100644
index 0000000..b89732d
--- /dev/null
+++ b/backend/channel.h
@@ -0,0 +1,143 @@
+/* channel.h -- device communication channels
+ * Copyright (C) 2008, 2009, 2013 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef channel_h_included
+#define channel_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sane/sane.h>
+#include <sys/types.h>
+#include <stdint.h>
+
+#include "defines.h"
+
+typedef enum
+{
+ CHAN_NET = 0,
+ CHAN_PIO,
+ CHAN_SCSI,
+ CHAN_USB,
+ CHAN_INTERP
+}
+channel_type;
+
+typedef struct channel
+{
+ struct channel * (*ctor) (struct channel *self,
+ const char *dev_name, SANE_Status *status);
+ struct channel * (*dtor) (struct channel *self);
+
+ void (*open) (struct channel *self, SANE_Status *status);
+ void (*close) (struct channel *self, SANE_Status *status);
+
+ bool (*is_open) (const struct channel *self);
+
+ ssize_t (*send) (struct channel *self, const void *buffer,
+ size_t buf_size, SANE_Status *status);
+ ssize_t (*recv) (struct channel *self, void *buffer,
+ size_t buf_size, SANE_Status *status);
+ size_t (*max_request_size) (const struct channel *self);
+ void (*set_max_request_size) (struct channel *self, size_t size);
+
+ char *name;
+ channel_type type;
+ int fd;
+ uint16_t id; /* target scanner ID when used with the network plugin
+ * or USB product ID
+ */
+ size_t max_size;
+
+ struct interpreter_type *interpreter;
+
+} channel;
+
+
+channel * channel_create (const char *dev_name, SANE_Status *status);
+
+
+/* Convenience API */
+ssize_t channel_send (channel *ch, const void *buffer,
+ size_t size, SANE_Status *status);
+ssize_t channel_recv (channel *ch, void *buffer,
+ size_t size, SANE_Status *status);
+ssize_t channel_recv_all (channel *ch, void *buffer,
+ size_t size, SANE_Status *status);
+ssize_t channel_recv_all_retry (channel *ch, void *buffer, size_t size,
+ size_t max_attempts, SANE_Status *status);
+
+
+#ifdef USE_PROTECTED_CHANNEL_API
+
+channel *channel_dtor (channel *self);
+bool channel_is_open (const channel *self);
+size_t channel_max_request_size (const channel *self);
+void channel_set_max_request_size (struct channel *self, size_t size);
+
+#endif
+
+
+#endif /* !defined (channel_h_included) */
diff --git a/backend/command.c b/backend/command.c
new file mode 100644
index 0000000..7c35aa6
--- /dev/null
+++ b/backend/command.c
@@ -0,0 +1,814 @@
+/* command.c -- assorted ESC/I protocol commands
+ * Copyright (C) 2008--2014 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ \brief Implements 'bare' ESC/I commands.
+
+ This file contains functions that implement of a number of ESC/I
+ commands. These functions only handle the sending and receiving
+ of data, data interpretation and I/O or memory related errors.
+
+ Interpretation of received data is limited to taking bytes apart
+ into bit flags and combining bytes into larger entities, such as
+ 2- or 4-byte integers and strings. The hardware device object's
+ state is updated to correspond with the interpreted data.
+
+ Under \e no circumstances shall the functions implemented in this
+ file draw any conclusions whatsoever as to the device's resulting
+ state. This is the reponsibility of the hardware device object.
+ Also, it is the hardware device object's responsibility to check
+ the timing/validity of calling any of the functions implemented
+ here.
+
+ All functions shall return a status that is one of:
+
+ - \c SANE_STATUS_GOOD
+ - \c SANE_STATUS_NO_MEM
+ - \c SANE_STATUS_IO_ERROR
+ - \c SANE_STATUS_INVAL
+
+ Function names follow the description in the ESC/I specification
+ and shall start with a \c cmd_ prefix.
+
+ \note I want to replace the device::cmd crap with function
+ pointers and use a no-op for commands that are not supported by
+ the device. Functions may return SANE_STATUS_UNSUPPORTED in the
+ interim.
+
+ \note I also want to introduce a function pointer based callback
+ mechanism to fix up broken firmware replies. That way, we do not
+ have to pass the firmware name everytime. We can set appropriate
+ callbacks for a device once and be done with it.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "command.h"
+
+#include "utils.h"
+
+#include <string.h>
+
+#define SANE_INT_MAX 2147483647
+
+
+static void
+fixme_request_identity (const char *fw_name, byte *buf, size_t size)
+{
+ if (!fw_name) return;
+ if (!buf) return;
+
+ if (0 == strcmp_c ("NX100", fw_name) && 16 < size)
+ {
+ buf[12] = 'A';
+ buf[13] = 0xEC;
+ buf[14] = 0x13;
+ buf[15] = 0x6C;
+ buf[16] = 0x1B;
+ }
+}
+
+/*! \brief Establishes basic device capabilities.
+
+ \note This command is assumed to be supported by \e all ESC/I
+ devices.
+
+ \todo Implement error checking.
+ */
+SANE_Status
+cmd_request_identity (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, 'I' };
+ byte info[4];
+
+ byte *data = NULL;
+ size_t size;
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, info, num_of (info), &status);
+
+ hw->status = info[1];
+ size = info[3] << 8 | info[2];
+
+ if (0 < size)
+ {
+ data = t_calloc (size, byte);
+ if (!data)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ channel_recv (hw->channel, data, size, &status);
+ if (SANE_STATUS_GOOD == status)
+ {
+ byte *p = data + 2;
+
+ fixme_request_identity (hw->fw_name, data, size);
+
+ hw->cmd_lvl[0] = data[0];
+ hw->cmd_lvl[1] = data[1];
+
+ free_resolution_info (&hw->res);
+ init_resolution_info (&hw->res, p);
+ init_resolution_info (&hw->resolution, NULL);
+ copy_resolution_info (&hw->resolution, &hw->res, SANE_TRUE);
+
+ hw->max_x = data[size-3] << 8 | data[size-4];
+ hw->max_y = data[size-1] << 8 | data[size-2];
+ }
+ delete (data);
+ }
+
+ return status;
+}
+
+static void
+fixme_request_hardware_property (const char *fw_name, byte *buf, size_t size)
+{
+ if (!fw_name) return;
+ if (!buf) return;
+
+ if (0 == strcmp_c ("NX100", fw_name) && 33 < size)
+ {
+ buf[32] = 0xB0;
+ buf[33] = 0x04;
+ }
+}
+
+/*! \brief Query additional device capabilities.
+
+ \note This command is not supported for B level devices. It is
+ supported for D level devices.
+
+ \todo Implement error checking.
+ */
+SANE_Status
+cmd_request_hardware_property (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, 'i' };
+ byte info[4];
+
+ byte *data = NULL;
+ size_t size;
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, info, num_of (info), &status);
+
+ hw->status = info[1];
+ size = info[3] << 8 | info[2];
+
+ if (0 < size)
+ {
+ data = t_calloc (size, byte);
+ if (!data)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ channel_recv (hw->channel, data, size, &status);
+ if (SANE_STATUS_GOOD == status)
+ {
+ byte *p = data + 14;
+
+ fixme_request_hardware_property (hw->fw_name, data, size);
+
+ hw->optical_res = data[1] << 8 | data[0];
+ hw->sensor_info = data[2];
+ hw->scan_order = data[3];
+ hw->line_dist_x = data[4];
+ hw->line_dist_y = data[5];
+
+ free_resolution_info (&hw->res_x);
+ init_resolution_info (&hw->res_x, p);
+
+ while (resolution_info_ESC_i_cond (p))
+ p += 2;
+
+ p += 2; /* start of sub resolution info */
+ free_resolution_info (&hw->res_y);
+ init_resolution_info (&hw->res_y, p);
+ }
+ delete (data);
+ }
+
+ return status;
+}
+
+SANE_Status
+cmd_set_scanning_parameter (device* hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte FS_W[] = {FS, 'W'};
+
+ byte ack_buf;
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, FS_W, 2, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ channel_recv (hw->channel, &ack_buf, 1, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ if (ACK != ack_buf) return SANE_STATUS_UNSUPPORTED;
+
+ channel_send (hw->channel, hw->param_buf, 64, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ channel_recv (hw->channel, &ack_buf, 1, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ if (ACK != ack_buf) return SANE_STATUS_INVAL;
+
+ return status;
+}
+
+SANE_Status
+cmd_request_scanning_parameter (device* hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ const byte FS_S[] = {FS, 'S'};
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, FS_S, 2, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ channel_recv (hw->channel, hw->param_buf, 64, &status);
+
+ return status;
+}
+
+SANE_Status
+cmd_request_scanner_status (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { FS, 'F' };
+ byte buf[16];
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, buf, num_of (buf), &status);
+
+ hw->fsf_status = buf[0];
+ { /* transfer corresponding flags */
+ byte mask = FSF_STATUS_FER | FSF_STATUS_WU;
+ hw->ext_status &= ~mask;
+ hw->ext_status |= (mask & hw->fsf_status);
+ }
+
+ if ((ADF_STATUS_IST & buf[1]) && !hw->adf)
+ {
+ hw->adf = t_calloc (1, adf_extension);
+ if (!hw->adf) return SANE_STATUS_NO_MEM;
+ }
+ if ((TPU_STATUS_IST & buf[2]) && !hw->tpu)
+ {
+ hw->tpu = t_calloc (1, tpu_extension);
+ if (!hw->tpu) return SANE_STATUS_NO_MEM;
+ }
+
+ if (hw->fbf)
+ {
+ hw->fbf->status = buf[3];
+ update_doc_size (hw->fbf, buf[8] << 8 | buf[7]);
+ }
+ if (hw->adf)
+ {
+ hw->adf->status = buf[1];
+ hw->adf->ext_status = buf[10];
+ update_doc_size (hw->adf, buf[6] << 8 | buf[5]);
+ }
+ if (hw->tpu)
+ {
+ hw->tpu->status = buf[2];
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Int
+buf_to_sane_int (const byte *p, const char *var)
+{
+ SANE_Int result = 0;
+
+ require (p);
+ result = buf_to_uint32 (p);
+
+ if (SANE_INT_MAX < result)
+ {
+ err_major ("overflow: %s", var);
+ result = SANE_INT_MAX;
+ }
+ return result;
+}
+
+SANE_Status
+cmd_request_extended_identity (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { FS, 'I' };
+ byte buf[80];
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, buf, num_of (buf), &status);
+
+ hw->cmd_lvl[0] = buf[0];
+ hw->cmd_lvl[1] = buf[1];
+
+ hw->version[0] = buf[62];
+ hw->version[1] = buf[63];
+ hw->version[2] = buf[64];
+ hw->version[3] = buf[65];
+
+ hw->fsi_cap_1 = buf[44];
+ hw->fsi_cap_2 = buf[45];
+ hw->fsi_cap_3 = buf[76];
+ { /* transfer corresponding flags */
+ byte mask = (EXT_STATUS_NO_FBF | EXT_STATUS_ADFT | EXT_STATUS_ADFS
+ | EXT_STATUS_ADFO | EXT_STATUS_LID | EXT_STATUS_PB);
+ hw->ext_status &= ~mask;
+ hw->ext_status |= (mask & hw->fsi_cap_1);
+ }
+
+ if (!(EXT_STATUS_NO_FBF & hw->fsi_cap_1) && !hw->fbf)
+ {
+ hw->fbf = t_calloc (1, fbf_extension);
+ if (!hw->fbf) return SANE_STATUS_NO_MEM;
+ }
+
+ hw->cmd->request_push_button_status
+ = ((EXT_STATUS_PB & hw->fsi_cap_1) ? '!' : 0);
+
+ hw->base_res = buf_to_sane_int (buf + 4, "base resolution");
+
+ hw->dpi_range.min = buf_to_sane_int (buf + 8, "minimum resolution");
+ hw->dpi_range.max = buf_to_sane_int (buf + 12, "maixmum resolution");
+ hw->dpi_range.quant = 1;
+
+ hw->scan_width_limit = buf_to_sane_int (buf + 16, "scan width");
+
+ if (hw->fbf)
+ {
+ hw->fbf->max_x = buf_to_sane_int (buf + 20, "fbf max_x");
+ hw->fbf->max_y = buf_to_sane_int (buf + 24, "fbf max_y");
+ update_ranges (hw, hw->fbf);
+ }
+ if (hw->adf)
+ {
+ hw->adf->max_x = buf_to_sane_int (buf + 28, "adf max_x");
+ hw->adf->max_y = buf_to_sane_int (buf + 32, "adf max_y");
+ update_ranges (hw, hw->adf);
+ }
+ if (hw->tpu)
+ {
+ hw->tpu->max_x = buf_to_sane_int (buf + 36, "tpu max_x");
+ hw->tpu->max_y = buf_to_sane_int (buf + 40, "tpu max_y");
+ update_ranges (hw, hw->tpu);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+fixme_request_extended_status (const char *fw_name, byte *buf, size_t size)
+{
+ if (!fw_name) return;
+ if (!buf) return;
+
+ if (0 == strcmp_c ("GT-8200", fw_name) && 15 < size)
+ {
+ uint16_t max_x;
+ uint16_t max_y;
+
+ max_x = buf[13] << 8 | buf[12];
+ max_y = buf[15] << 8 | buf[14];
+ if (max_y < max_x)
+ {
+ err_minor ("Fixing up buggy FBF max scan dimensions.");
+ max_y *= 2;
+ buf[14] = 0xFF & max_y;
+ buf[15] = 0xFF & (max_y >> 8);
+ }
+
+ max_x = buf[ 8] << 8 | buf[7];
+ max_y = buf[10] << 8 | buf[9];
+ if (max_y < max_x)
+ {
+ err_minor ("Fixing up buggy TPU max scan dimensions.");
+ max_y *= 2;
+ buf[ 9] = 0xFF & max_y;
+ buf[10] = 0xFF & (max_y >> 8);
+ }
+ }
+
+ if ((0 == strcmp_c ("ES-9000H", fw_name) ||
+ 0 == strcmp_c ("GT-30000", fw_name))
+ && 5 < size)
+ {
+ err_minor ("Fixing up buggy ADF max scan dimensions.");
+ buf[2] = 0xB0;
+ buf[3] = 0x6D;
+ buf[4] = 0x60;
+ buf[5] = 0x9F;
+ }
+}
+
+/*! Updates the extended status of a hardware device object.
+ */
+SANE_Status
+cmd_request_extended_status (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, 'f' };
+ byte info[4];
+
+ byte *data = NULL;
+ size_t size;
+
+ const size_t DEVNAME_OFFSET = 26;
+
+ const byte DEVT_MASK = 0xC0;
+ const byte DEVTYPE_3 = 0xC0;
+
+ log_call ();
+ require (hw);
+
+ if (!hw->cmd->request_extended_status)
+ return SANE_STATUS_UNSUPPORTED;
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, info, num_of (info), &status);
+
+ hw->status = info[1];
+ size = info[3] << 8 | info[2];
+
+ require (DEVNAME_OFFSET + DEVNAME_LENGTH <= size);
+
+ if (0 < size)
+ {
+ data = t_calloc (size, byte);
+ if (!data)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ channel_recv (hw->channel, data, size, &status);
+ if (SANE_STATUS_GOOD == status)
+ {
+ fixme_request_extended_status (hw->fw_name, data, size);
+
+ hw->ext_status = data[0];
+
+ hw->cmd->request_push_button_status
+ = ((EXT_STATUS_PB & data[0]) ? '!' : 0);
+
+ if (!(EXT_STATUS_NO_FBF & data[0]) && !hw->fbf)
+ {
+ hw->fbf = t_calloc (1, fbf_extension);
+ if (!hw->fbf) status = SANE_STATUS_NO_MEM;
+ }
+ if ((ADF_STATUS_IST & data[1]) && !hw->adf)
+ {
+ hw->adf = t_calloc (1, adf_extension);
+ if (!hw->adf) status = SANE_STATUS_NO_MEM;
+ }
+ if ((TPU_STATUS_IST & data[6]) && !hw->tpu)
+ {
+ hw->tpu = t_calloc (1, tpu_extension);
+ if (!hw->tpu) status = SANE_STATUS_NO_MEM;
+ }
+
+ if (hw->fbf)
+ {
+ hw->fbf->status = 0x00;
+ if (DEVTYPE_3 == (DEVT_MASK & data[11]))
+ {
+ hw->fbf->status = data[11];
+ hw->fbf->max_x = data[13] << 8 | data[12];
+ hw->fbf->max_y = data[15] << 8 | data[14];
+ }
+ else
+ {
+ hw->fbf->max_x = hw->max_x;
+ hw->fbf->max_y = hw->max_y;
+ }
+ update_ranges (hw, hw->fbf);
+ update_doc_size (hw->fbf, data[19] << 8 | data[18]);
+ }
+ if (hw->adf)
+ {
+ hw->adf->status = data[1];
+ hw->adf->max_x = data[3] << 8 | data[2];
+ hw->adf->max_y = data[5] << 8 | data[4];
+ update_ranges (hw, hw->adf);
+ update_doc_size (hw->adf, data[17] << 8 | data[16]);
+ }
+ if (hw->tpu)
+ {
+ hw->tpu->status = data[6];
+ hw->tpu->max_x = data[ 8] << 8 | data[7];
+ hw->tpu->max_y = data[10] << 8 | data[9];
+ update_ranges (hw, hw->tpu);
+ }
+ }
+ delete (data);
+ }
+
+ return status;
+}
+
+
+/*! Sets the option unit to use as well as the unit's behaviour.
+
+ \todo Implement error checking.
+ */
+SANE_Status
+cmd_control_option_unit (device *hw, byte value)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, 'e' };
+ byte reply = NUL;
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, &reply, 1, &status);
+ channel_send (hw->channel, &value, 1, &status);
+ channel_recv (hw->channel, &reply, 1, &status);
+
+ return status;
+}
+
+
+/*! Resets the device to a well known state.
+
+ \todo Implement error checking.
+ */
+SANE_Status
+cmd_initialize (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, '@' };
+ byte reply = NUL;
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, &reply, 1, &status);
+
+ return status;
+}
+
+/*! Loads a sheet on a page type ADF extension.
+
+ \todo Implement error checking.
+ */
+SANE_Status
+cmd_load_paper (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { PF };
+ byte reply = NUL;
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, &reply, 1, &status);
+
+ return status;
+}
+
+/*! Ejects sheets from the ADF extension.
+
+ \todo Implement error checking.
+ */
+SANE_Status
+cmd_eject_paper (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { FF };
+ byte reply = NUL;
+
+ log_call ();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ channel_recv (hw->channel, &reply, 1, &status);
+
+ return status;
+}
+
+SANE_Status
+cmd_lock (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, '(' };
+ byte reply = NUL;
+
+ log_call();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ channel_recv (hw->channel, &reply, 1, &status);
+
+ if (SANE_STATUS_GOOD == status)
+ {
+ if (0x80 == reply)
+ {
+ hw->is_locked = true;
+ }
+ else if (0x40 == reply)
+ {
+ err_minor ("failed to acquire lock");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ else if (NAK == reply)
+ {
+ err_minor ("locking not supported by device, disabling");
+ hw->uses_locking = false;
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ err_major ("unexpected reply to lock command (%02x)", reply);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ }
+ return status;
+}
+
+SANE_Status
+cmd_unlock (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, ')' };
+ byte reply = NUL;
+
+ log_call();
+ require (hw);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ channel_recv (hw->channel, &reply, 1, &status);
+
+ if (SANE_STATUS_GOOD == status)
+ {
+ if (0x80 == reply)
+ {
+ hw->is_locked = false;
+ }
+ else if (NAK == reply)
+ {
+ err_minor ("locking not supported by device, disabling");
+ hw->uses_locking = false;
+ status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ err_major ("unexpected reply to unlock command (%02x)", reply);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ return status;
+}
+
+SANE_Status
+cmd_request_scanner_maintenance (device *hw, uint16_t mode)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const byte cmd[] = { ESC, '1' };
+ byte param[8];
+ byte reply = NUL;
+
+ log_call ("(%04x)", mode);
+ require (hw);
+
+ memset (param, 0, sizeof (param));
+ uint16_to_buf (mode, param);
+
+ channel_send (hw->channel, cmd, num_of (cmd), &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ channel_recv (hw->channel, &reply, sizeof (reply), &status);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ if (ACK != reply)
+ {
+ err_major ("unexpected reply to maintenance command (%02x)", reply);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ channel_send (hw->channel, (const byte *)param, num_of (param), &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ channel_recv (hw->channel, &reply, sizeof (reply), &status);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ if (BUSY == reply)
+ {
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ else if (NAK == reply)
+ {
+ err_minor ("invalid maintenance command (%04x)", mode);
+ status = SANE_STATUS_INVAL;
+ }
+ else if (ACK != reply)
+ {
+ err_major ("unexpected reply to maintenance command (mode=%04x, %02x)",
+ mode, reply);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ return status;
+}
diff --git a/backend/command.h b/backend/command.h
new file mode 100644
index 0000000..774984c
--- /dev/null
+++ b/backend/command.h
@@ -0,0 +1,223 @@
+/* command.h -- assorted ESC/I protocol commands
+ * Copyright (C) 2008--2014 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef command_h_included
+#define command_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+/* ESC/I protocol constants.
+ */
+
+/* Readability definitions for selected ASCII codes used by the ESC/I
+ protocol.
+ */
+#define NUL 0x00 /* '\0' */
+#define STX 0x02
+#define ACK 0x06
+#define BUSY 0x07 /* BEL in ascii(7) */
+#define FF 0x0c /* '\f' */
+#define NAK 0x15
+#define CAN 0x18
+#define PF 0x19 /* EM in ascii(7) */
+#define ESC 0x1b
+#define FS 0x1c
+
+
+/* Bit flags for the status byte in the information block.
+ */
+#define STATUS_FATAL_ERROR 0x80
+#define STATUS_NOT_READY 0x40 /* in use via other interface */
+#define STATUS_AREA_END 0x20 /* scan finished */
+#define STATUS_OPTION 0x10 /* option detected */
+#define STATUS_COLOR_ATTR_MASK 0x0c
+#define STATUS_EXT_COMMANDS 0x02 /* FS commands supported */
+
+/* Bit flags for the extended image acquisition (FS G) error byte
+ */
+#define FSG_FATAL_ERROR 0x80
+#define FSG_NOT_READY 0x40 /* in use via other interface */
+#define FSG_PAGE_END 0x20 /* notify of the paper end */
+#define FSG_CANCEL_REQUEST 0x10 /* cancel request from scanner */
+
+/* Bit flags for selected bytes in the extended status and scanner
+ status replies. Synchronised status flags are defined in terms
+ of the corresponding flag. All other values are supposed to be
+ unlinked. The various *_ERR flags indicate that at least one of
+ the other error bit flags in the _same_ block is set.
+
+ The EXT_STATUS_* flags are specific to the ESC f command's reply,
+ The FSF_STATUS_*, ADF_EXT_STATUS_* and AFL_STATUS_* flags to that
+ of the FS F command.
+ */
+#define EXT_STATUS_FER 0x80 /* fatal error */
+#define EXT_STATUS_NO_FBF 0x40 /* no flat bed */
+#define EXT_STATUS_ADFT 0x20 /* ADF unit type */
+#define EXT_STATUS_ADFS 0x10 /* simplex/duplex */
+#define EXT_STATUS_ADFO 0x08 /* feed from first/last sheet */
+#define EXT_STATUS_LID 0x04 /* lid type option */
+#define EXT_STATUS_WU 0x02 /* warming up */
+#define EXT_STATUS_PB 0x01 /* scanner has a push button */
+
+#define FSF_STATUS_FER 0x80 /* fatal error */
+#define FSF_STATUS_NR 0x40 /* in use via other interface */
+#define FSF_STATUS_WU 0x02 /* warming up */
+#define FSF_STATUS_CWU 0x01 /* can cancel warming up */
+
+#define ADF_STATUS_IST 0x80 /* option detected */
+#define ADF_STATUS_EN 0x40 /* option enabled */
+#define ADF_STATUS_ERR 0x20 /* option error detected */
+#define ADF_STATUS_ATYP 0x10 /* photo ADF type detected */
+#define ADF_STATUS_PE 0x08 /* no paper */
+#define ADF_STATUS_PJ 0x04 /* paper jam */
+#define ADF_STATUS_OPN 0x02 /* cover open */
+#define ADF_STATUS_PAG 0x01 /* duplex selected */
+
+#define ADF_EXT_STATUS_IST ADF_STATUS_IST
+#define ADF_EXT_STATUS_EN ADF_STATUS_EN
+#define ADF_EXT_STATUS_ERR 0x20 /* option error detected */
+#define ADF_EXT_STATUS_DFE 0x10 /* double feed */
+#define ADF_EXT_STATUS_TR_OPN 0x02 /* tray open */
+
+#define TPU_STATUS_IST 0x80 /* option detected */
+#define TPU_STATUS_EN 0x40 /* option enabled */
+#define TPU_STATUS_ERR 0x20 /* option error detected */
+#define TPU_STATUS_OPN 0x02 /* cover open */
+#define TPU_STATUS_LTF 0x01 /* lamp luminescence too low */
+
+#define AFL_STATUS_IST 0x80 /* option detected */
+#define AFL_STATUS_EN 0x40 /* option enabled */
+#define AFL_STATUS_ERR 0x20 /* option error detected */
+#define AFL_STATUS_OPN 0x02 /* cover open */
+#define AFL_STATUS_LTF 0x01 /* lamp luminescence too low */
+
+#define DV3_STATUS_ERR 0x20
+#define DV3_STATUS_PE 0x08 /* no paper */
+#define DV3_STATUS_PJ 0x04 /* paper jam */
+#define DV3_STATUS_OPN 0x02 /* cover open */
+
+/* Bit flags
+ */
+#define FSI_CAP_DLF 0x80 /* ??? */
+#define FSI_CAP_NO_FBF 0x40 /* no flat bed */
+#define FSI_CAP_ADFT 0x20 /* ADF unit type */
+#define FSI_CAP_ADFS 0x10 /* simplex/duplex */
+#define FSI_CAP_ADFO 0x08 /* feed from first/last sheet */
+#define FSI_CAP_LID 0x04 /* lid type option */
+#define FSI_CAP_TPIR 0x02 /* TPU with IR support */
+#define FSI_CAP_PB 0x01 /* scanner has a push button */
+
+#define FSI_CAP_ADFAS 0x10 /* ADF with auto scan support */
+#define FSI_CAP_DFD 0x08 /* double feed detection */
+#define FSI_CAP_AFF 0x04 /* auto form feed */
+#define FSI_CAP_ESST 0x02 /* ??? */
+#define FSI_CAP_PED 0x01 /* paper end detection support */
+
+#define FSI_CAP_DPOS_MASK 0x03 /* the document position of the ADF */
+
+/* DPOS status */
+#define FSI_CAP_DPOS_UNKN 0x00 /* no information */
+#define FSI_CAP_DPOS_LEFT 0x01 /* left side position */
+#define FSI_CAP_DPOS_CNTR 0x02 /* center position */
+#define FSI_CAP_DPOS_RIGT 0x03 /* right position */
+
+/* Maintenance requests.
+ */
+#define ESC1_REQ_CLEANING 0x0001
+#define ESC1_REQ_CALIBRATION 0x0002
+#define ESC1_REQ_STATUS 0xffff
+
+#include "device.h"
+
+
+/* Getter commands.
+ */
+SANE_Status cmd_request_identity (device *hw);
+SANE_Status cmd_request_hardware_property (device *hw);
+SANE_Status cmd_request_scanner_status (device *hw);
+SANE_Status cmd_request_extended_identity (device *hw);
+SANE_Status cmd_request_extended_status (device *hw);
+SANE_Status cmd_request_scanning_parameter (device* hw);
+
+
+/* Setter commands.
+ */
+SANE_Status cmd_control_option_unit (device *hw, byte value);
+SANE_Status cmd_set_scanning_parameter (device* hw);
+
+
+/* Action commands.
+ */
+SANE_Status cmd_initialize (device *hw);
+SANE_Status cmd_load_paper (device *hw);
+SANE_Status cmd_eject_paper (device *hw);
+SANE_Status cmd_lock (device *hw);
+SANE_Status cmd_unlock (device *hw);
+SANE_Status cmd_request_scanner_maintenance (device *hw, uint16_t mode);
+
+
+#endif /* !defined (command_h_included) */
diff --git a/backend/defines.h b/backend/defines.h
new file mode 100644
index 0000000..9940b7b
--- /dev/null
+++ b/backend/defines.h
@@ -0,0 +1,104 @@
+/* defines.h -- a grab bag of preprocessor utilities
+ * Copyright (C) 2019 SEIKO EPSON Corporation
+ *
+ * License: EPSON END USER SOFTWARE LICENSE
+ * Author : SEIKO EPSON Corporation
+ *
+ * This file is part of Image Scan! for Linux.
+ * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE.
+ *
+ * You should have received a verbatim copy of the EPSON END USER SOFTWARE
+ * LICENSE along with the software.
+ */
+
+
+#ifndef defines_h
+#define defines_h
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Assorted constants.
+ */
+
+#define MM_PER_INCH 25.4 /* exactly */
+
+
+#ifndef __cplusplus
+/*! A C++ Boolean type and corresponding keywords for our C code.
+ */
+typedef enum {
+ false = 0,
+ true
+} bool;
+#endif
+
+
+/* Run-time contract validation.
+ */
+#include <stdlib.h>
+#include "message.h"
+
+#define _assert(type,condition) \
+ if (!(condition)) \
+ { \
+ err_fatal ("failed: %s (%s)", type, #condition); \
+ exit (EXIT_FAILURE); \
+ }
+#define require(condition) _assert ("require", condition)
+#define promise(condition) _assert ("promise", condition)
+
+
+/* "Typed" memory allocation convenience wrappers.
+ * These are meant to make the invocations consistent and take care of
+ * the casting for you.
+ */
+#include <alloca.h>
+#include <stdlib.h>
+
+#define t_alloca(sz,t) ((t *) alloca ((sz) * sizeof (t)))
+#define t_calloc(sz,t) ((t *) calloc ((sz) , sizeof (t)))
+#define t_malloc(sz,t) ((t *) malloc ((sz) * sizeof (t)))
+#define t_realloc(p,sz,t) ((t *) realloc ((p), (sz) * sizeof (t)))
+
+/* Compute sizes of _statically_ allocated arrays easily.
+ */
+#define num_of(p) (sizeof (p) / sizeof (*p))
+
+/* Safely release acquired resources.
+ * The const_delete() is meant for those rare cases where you need to
+ * clean up const t* typed memory areas.
+ */
+#define delete(p) do { if (p) free (p); p = 0; } while (0)
+#define const_delete(p,t) do { if (p) free ((t) p); p = 0; } while (0)
+
+
+/* Portable path and file name component separators.
+ */
+#ifdef __unix
+#define PATH_SEP_STR ":"
+#define PATH_SEP PATH_SEP_STR[0]
+#define FILE_SEP_STR "/"
+#define FILE_SEP FILE_SEP_STR[0]
+#else
+#define PATH_SEP_STR ";"
+#define PATH_SEP PATH_SEP_STR[0]
+#define FILE_SEP_STR "\\"
+#define FILE_SEP FILE_SEP_STR[0]
+#endif
+
+
+ typedef unsigned char byte;
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (defines_h) */
diff --git a/backend/device.c b/backend/device.c
new file mode 100644
index 0000000..13fdc65
--- /dev/null
+++ b/backend/device.c
@@ -0,0 +1,563 @@
+/* device.c -- physical device representation
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ \brief Implements a hardware device object.
+
+ The hardware device object is built on top of the ESC/I commands.
+ Whereas the commands are only responsible for the I/O details and
+ the encoding and decoding of parameters, hardware device objects
+ handle the protocol logic and keep state.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include "command.h"
+#include "hw-data.h"
+#include "utils.h"
+
+
+/*! Releases all resources associated with a scanner device.
+ *
+ * The channel will be closed if open and acquired memory is returned
+ * to the operating system.
+ */
+device *
+dev_dtor (device *hw)
+{
+ if (!hw) return hw;
+
+ hw->channel = hw->channel->dtor (hw->channel);
+
+ delete (hw->fbf);
+ delete (hw->adf);
+ delete (hw->tpu);
+ delete (hw->fw_name);
+ if (hw->res_y.list != hw->res.list
+ && hw->res_y.list != hw->res_x.list)
+ {
+ delete (hw->res_y.list);
+ }
+ if (hw->res_x.list != hw->res.list)
+ {
+ delete (hw->res_x.list);
+ }
+ delete (hw->res.list);
+ delete (hw->resolution.list);
+ delete (hw);
+
+ return hw;
+}
+
+/*! Updates the extended status of a hardware device object.
+ */
+SANE_Status
+dev_request_extended_status (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (hw->using_fs)
+ {
+ status = cmd_request_scanner_status (hw);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ status = cmd_request_extended_identity (hw);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ status = cmd_request_scanner_status (hw);
+ }
+ else
+ status = cmd_request_extended_status (hw);
+
+ return status;
+}
+
+#include <string.h>
+/*! Loads a sheet on a page type ADF extension.
+ */
+SANE_Status
+dev_load_paper (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (!hw->adf) return status; /* guard clauses */
+ if (!(ADF_STATUS_IST & hw->adf->status)) return status;
+ if (!(ADF_STATUS_EN & hw->adf->status)) return status;
+ hw->adf->sheet_count++;
+ if (!(EXT_STATUS_ADFT & hw->ext_status)) return status;
+
+ log_call ();
+
+ status = cmd_load_paper (hw);
+ if (SANE_STATUS_GOOD != status)
+ {
+ hw->adf->sheet_count--;
+ return status;
+ }
+ log_info ("loaded sheet #%d", hw->adf->sheet_count);
+
+ status = dev_request_extended_status (hw);
+
+ if (ADF_STATUS_PE & hw->adf->status
+ && adf_early_paper_end_kills_scan (hw))
+ { /* so we can scan the last sheet */
+ cmd_control_option_unit (hw, 0x00);
+ hw->adf->status &= ~ ADF_STATUS_EN;
+ }
+
+ /* Clear the ADF_STATUS_PE bit. We just successfully loaded a
+ sheet. Also update the ADF_STATUS_ERR bit.
+ */
+ hw->adf->status &= ~ADF_STATUS_PE;
+ if ( (ADF_STATUS_PE & hw->adf->status)
+ || (ADF_STATUS_PJ & hw->adf->status)
+ || (ADF_STATUS_OPN & hw->adf->status))
+ hw->adf->status |= ADF_STATUS_ERR;
+ else
+ hw->adf->status &= ~ADF_STATUS_ERR;
+
+ return status;
+}
+
+/*! Ejects sheets from the ADF extension.
+ */
+SANE_Status
+dev_eject_paper (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (!hw->adf) return status; /* guard clauses */
+ if (!(ADF_STATUS_IST & hw->adf->status)) return status;
+ if (!(ADF_STATUS_EN & hw->adf->status)) return status;
+
+ log_call ();
+
+ status = cmd_eject_paper (hw);
+ hw->adf->sheet_count = 0;
+
+ return status;
+}
+
+/*! Open the scanner device. Depending on the connection method,
+ * different open functions are called.
+ */
+SANE_Status
+dev_open (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+ require (hw->channel);
+
+ if (hw->channel->is_open (hw->channel))
+ {
+ log_info ("scanner is already open: fd = %d", hw->channel->fd);
+ return SANE_STATUS_GOOD; /* no need to open the scanner */
+ }
+
+ hw->channel->open (hw->channel, &status);
+
+ return status;
+}
+
+/*! Log extended (FS W) parameter settings
+ */
+SANE_Status
+dev_log_scanning_parameter (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+
+ byte *buf = hw->param_buf; /* for convenience */
+
+ log_info ("SANE_START: Color: %d", (int) buf[24]);
+ log_info ("SANE_START: Resolution (x, y): (%u, %u)",
+ buf_to_uint32 (buf+0), buf_to_uint32 (buf+4));
+ log_info ("SANE_START: Scan offset (x, y): (%d, %d)",
+ buf_to_uint32 (buf+8), buf_to_uint32 (buf+12));
+ log_info ("SANE_START: Scan size (w, h): (%d, %d)",
+ buf_to_uint32 (buf+16), buf_to_uint32 (buf+20));
+ log_info ("SANE_START: Data format: %d", (int) buf[25]);
+ log_info ("SANE_START: Halftone: %d", (int) buf[32]);
+ log_info ("SANE_START: Brightness: %d", (int) buf[30]);
+ log_info ("SANE_START: Gamma: %d", (int) buf[29]);
+ log_info ("SANE_START: Color correction: %d", (int) buf[31]);
+ log_info ("SANE_START: Sharpness control: %d", (int) buf[35]);
+ log_info ("SANE_START: Scanning mode: %d", (int) buf[27]);
+ log_info ("SANE_START: Mirroring: %d", (int) buf[36]);
+ log_info ("SANE_START: Auto area segmentation: %d", (int) buf[34]);
+ log_info ("SANE_START: Threshold: %d", (int) buf[33]);
+ log_info ("SANE_START: Line counter: %d", (int) buf[28]);
+ log_info ("SANE_START: Option unit control: %d", (int) buf[26]);
+ log_info ("SANE_START: Film type: %d", (int) buf[37]);
+
+ return status;
+}
+
+/*! Obtain the offset and size of the parameter corresponding
+ * to the given ESC command in the FS W packet structure.
+ * Return true if the given command is in the FS W packet
+ * structure and obtaining the parameter info succeeded,
+ * otherwise return false.
+ */
+static bool
+get_extended_param_info (byte cmd, short *offset, short *size)
+{
+ if (NULL == offset) return false;
+ if (NULL == size) return true;
+
+ *offset = 0;
+ *size = 1; /* default size of most parameters */
+
+ if ('R' == cmd) { *offset = 0; *size = 8; }
+ else if ('A' == cmd) { *offset = 8; *size = 16; }
+ else if ('C' == cmd) *offset = 24;
+ else if ('D' == cmd) *offset = 25;
+ else if ('e' == cmd) *offset = 26;
+ else if ('g' == cmd) *offset = 27;
+ else if ('d' == cmd) *offset = 28;
+ else if ('Z' == cmd) *offset = 29;
+ else if ('L' == cmd) *offset = 30;
+ else if ('M' == cmd) *offset = 31;
+ else if ('B' == cmd) *offset = 32;
+ else if ('t' == cmd) *offset = 33;
+ else if ('s' == cmd) *offset = 34;
+ else if ('Q' == cmd) *offset = 35;
+ else if ('K' == cmd) *offset = 36;
+ else if ('N' == cmd) *offset = 37;
+ else *size = 0;
+
+ if (0 == *size) return false;
+ return true;
+}
+
+/*! Copies a number of bytes from \a param into the FS W packet buffer.
+ * The size and offset are looked up based on the \a cmd passed.
+ */
+SANE_Status
+dev_set_scanning_parameter (device *hw, byte cmd, const byte* param)
+{
+ short offset = 0;
+ short size = 0;
+
+ log_call ();
+ require (hw);
+
+ if (NULL == param) return SANE_STATUS_INVAL;
+ if (!get_extended_param_info (cmd, &offset, &size)) return SANE_STATUS_INVAL;
+
+ memcpy (hw->param_buf + offset, param, size);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+dev_set_scanning_resolution (device *hw, SANE_Int x_dpi, SANE_Int y_dpi)
+{
+ byte buf[8];
+ uint32_t x;
+ uint32_t y;
+
+ log_call ();
+ require (hw);
+
+ if (x_dpi < 0 || x_dpi > UINT32_MAX) return SANE_STATUS_INVAL;
+ if (y_dpi < 0 || y_dpi > UINT32_MAX) return SANE_STATUS_INVAL;
+
+ x = x_dpi;
+ y = y_dpi;
+
+ uint32_to_buf (x, buf + 0);
+ uint32_to_buf (y, buf + 4);
+
+ return dev_set_scanning_parameter (hw, 'R', buf);
+}
+
+SANE_Status
+dev_set_scanning_area (device *hw, SANE_Int left, SANE_Int top,
+ SANE_Int width, SANE_Int height)
+{
+ byte buf[16];
+ uint32_t nx;
+ uint32_t ny;
+ uint32_t nw;
+ uint32_t nh;
+
+ log_call ();
+ require (hw);
+
+ if (left > UINT32_MAX) return SANE_STATUS_INVAL;
+ if (top > UINT32_MAX) return SANE_STATUS_INVAL;
+ if (width > UINT32_MAX) return SANE_STATUS_INVAL;
+ if (height > UINT32_MAX) return SANE_STATUS_INVAL;
+
+ nx = left;
+ ny = top;
+ nw = width;
+ nh = height;
+
+ uint32_to_buf (nx, buf + 0);
+ uint32_to_buf (ny, buf + 4);
+ uint32_to_buf (nw, buf + 8);
+ uint32_to_buf (nh, buf + 12);
+
+ return dev_set_scanning_parameter (hw, 'A', buf);
+}
+
+SANE_Status
+dev_set_option_unit (device *hw, byte adf_mode)
+{
+ byte val = 0;
+
+ log_call ();
+ require (hw);
+
+ val = (using (hw, fbf) ? 0 : 1);
+ if (hw->adf && 1 == val)
+ val += adf_mode; /* set simplex/duplex */
+
+ return dev_set_scanning_parameter (hw, 'e', &val);
+}
+
+static void
+limit_res_list (resolution_info *res, int limit)
+{
+ int i = 0;
+ int new_size = 0;
+
+ for(i=1; i<res->size; i++) // first entry is the list size, skip it
+ {
+ if (res->list[i] > limit) break;
+ ++new_size;
+ }
+
+ res->list[0] = new_size;
+ res->size = new_size;
+ res->last = 0;
+
+ log_info ("Limit resolution to %ddpi", res->list[res->size]);
+}
+
+void
+dev_limit_res (device *self, SANE_Constraint_Type type, int limit)
+{
+ if (SANE_CONSTRAINT_RANGE == type)
+ {
+ self->old_max = self->dpi_range.max;
+ self->dpi_range.max = limit;
+ }
+ else
+ {
+ self->old_max = self->res.size;
+ limit_res_list (&self->res, limit);
+ limit_res_list (&self->res_x, limit);
+ limit_res_list (&self->res_y, limit);
+ }
+}
+
+void
+dev_restore_res (device *self, SANE_Constraint_Type type)
+{
+ if (0 == self->old_max) return;
+
+ if (SANE_CONSTRAINT_RANGE == type)
+ {
+ self->dpi_range.max = self->old_max;
+ }
+ else
+ {
+ // res_x and res_y are recreated, only restore res
+ self->res.size = self->old_max;
+ self->res.list[0] = self->old_max;
+ self->res.last = 0;
+ }
+}
+
+/*! Sends a cancel to the device when not in the middle of retrieving scan data.
+ Return true if a cancel command was successfully sent, false, if not.
+ */
+bool
+dev_force_cancel (device *self)
+{
+ u_char buf[14];
+ u_char params[2];
+ u_char *dummy = NULL;
+ uint32_t block_size = 0;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (!self->using_fs) return false;
+
+ params[0] = FS;
+ params[1] = self->cmd->start_scanning;
+
+ channel_send (self->channel, params, 2, &status);
+ if (SANE_STATUS_GOOD != status) return false;
+
+ channel_recv (self->channel, buf, num_of (buf), &status);
+ if (SANE_STATUS_GOOD != status) return false;
+ if (STX != buf[0]) return false;
+
+ block_size = buf_to_uint32 (buf + 2);
+
+ dummy = t_malloc (block_size, u_char);
+ if (dummy == NULL)
+ {
+ err_fatal ("%s", strerror (errno));
+ return false;
+ }
+
+ channel_recv_all (self->channel, dummy,
+ block_size, &status);
+ delete (dummy);
+
+ if (SANE_STATUS_GOOD != status) return false;
+
+ buf[0] = CAN;
+ channel_send (self->channel, buf, 1, &status);
+ if (SANE_STATUS_GOOD != status) return false;
+
+ channel_recv (self->channel, buf, 1, &status);
+ if (SANE_STATUS_GOOD != status) return false;
+ if (ACK != buf[0]) return false;
+
+ return true;
+}
+
+SANE_Status
+dev_lock (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+ require (hw);
+
+ if (!hw->uses_locking) return status;
+ if ( hw->is_locked) return status;
+
+ return cmd_lock (hw);
+}
+
+SANE_Status
+dev_unlock (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+ require (hw);
+
+ if(!hw->uses_locking) return status;
+ if(!hw->is_locked) return status;
+
+ return cmd_unlock (hw);
+}
+
+static SANE_Status
+dev_maintenance (device *hw, uint16_t mode)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ("(%04x)", mode);
+ require (hw);
+ if (!maintenance_is_supported (hw))
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ if ( ESC1_REQ_CLEANING != mode
+ && ESC1_REQ_CALIBRATION != mode)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ status = cmd_request_scanner_maintenance (hw, mode);
+ if (SANE_STATUS_GOOD == status)
+ {
+ do
+ {
+ microsleep (hw->polling_time);
+ status = cmd_request_scanner_maintenance (hw, ESC1_REQ_STATUS);
+ } while (SANE_STATUS_DEVICE_BUSY == status);
+ }
+
+ return status;
+}
+
+/* convenience function */
+SANE_Status
+dev_clean (device *hw)
+{
+ return dev_maintenance (hw, ESC1_REQ_CLEANING);
+}
+
+/* convenience function */
+SANE_Status
+dev_calibrate (device *hw)
+{
+ return dev_maintenance (hw, ESC1_REQ_CALIBRATION);
+}
diff --git a/backend/device.h b/backend/device.h
new file mode 100644
index 0000000..739429e
--- /dev/null
+++ b/backend/device.h
@@ -0,0 +1,265 @@
+/* device.h -- physical device representation
+ * Copyright (C) 2008, 2009, 2014 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef device_h_included
+#define device_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include "channel.h"
+#include "extension.h"
+
+#define DEVNAME_LENGTH 16
+
+
+typedef struct
+{
+ char *level;
+
+ unsigned char request_identity;
+ unsigned char request_identity2; /* new request identity command for Dx command level */
+ unsigned char request_status;
+ unsigned char request_condition;
+ unsigned char set_color_mode;
+ unsigned char start_scanning;
+ unsigned char set_data_format;
+ unsigned char set_resolution;
+ unsigned char set_zoom;
+ unsigned char set_scan_area;
+ unsigned char set_bright;
+ SANE_Range bright_range;
+ unsigned char set_gamma;
+ unsigned char set_halftoning;
+ unsigned char set_color_correction;
+ unsigned char initialize_scanner;
+ unsigned char set_speed; /* B4 and later */
+ unsigned char set_lcount;
+ unsigned char mirror_image; /* B5 and later */
+ unsigned char set_gamma_table; /* B4 and later */
+ unsigned char set_outline_emphasis; /* B4 and later */
+ unsigned char set_dither; /* B4 and later */
+ unsigned char set_color_correction_coefficients; /* B3 and later */
+ unsigned char request_extended_status; /* get extended status from scanner */
+ unsigned char control_an_extension; /* for extension control */
+ unsigned char eject; /* for extension control */
+ unsigned char feed;
+ unsigned char request_push_button_status;
+ unsigned char control_auto_area_segmentation;
+ unsigned char set_film_type; /* for extension control */
+ unsigned char set_exposure_time; /* F5 only */
+ unsigned char set_bay; /* F5 only */
+ unsigned char set_threshold;
+ unsigned char set_focus_position; /* B8 only */
+ unsigned char request_focus_position; /* B8 only */
+} EpsonCmdRec, *EpsonCmd;
+
+typedef struct
+{
+ SANE_Int last;
+ SANE_Int size;
+ SANE_Word *list;
+ SANE_Bool deep;
+} resolution_info;
+
+typedef struct
+{
+ int modelID;
+
+ double color_profile[4][9];
+} EpsonScanHardRec, *EpsonScanHard;
+
+
+/*! Software abstraction of the \e physical device.
+ */
+struct device
+{
+ struct channel *channel; /*<! for all device I/O */
+
+ char cmd_lvl[2+1]; /* +1 for string termination */
+ char version[4+1]; /* +1 for string termination */
+ char *fw_name; /*!< model name reported by firmware */
+
+ SANE_Byte status; /*!< info block status byte */
+ SANE_Byte ext_status;
+ SANE_Byte fsf_status;
+ SANE_Byte fsi_cap_1;
+ SANE_Byte fsi_cap_2;
+ SANE_Byte fsi_cap_3;
+
+ const extension *src; /*!< current document source */
+ fbf_extension *fbf; /*!< \c NULL if not available */
+ adf_extension *adf; /*!< \c NULL if not available */
+ tpu_extension *tpu; /*!< \c NULL if not available */
+
+ /*! \brief Selectable scan sources.
+ * A \c NULL terminated list of up to three scan sources. Which
+ * ones are listed depends on device capabilities.
+ */
+ SANE_String_Const sources[4];
+
+ resolution_info resolution; /* full-blown list */
+ resolution_info res; /* advertised list */
+ SANE_Int max_x; /* in pixels */
+ SANE_Int max_y;
+
+ SANE_Int optical_res; /* resolution in pixels per inch */
+ SANE_Byte sensor_info;
+ SANE_Byte scan_order;
+ SANE_Byte line_dist_x;
+ SANE_Byte line_dist_y;
+ resolution_info res_x;
+ resolution_info res_y;
+
+
+ SANE_Int level;
+ SANE_Range dpi_range;
+
+ SANE_Range matrix_range;
+
+ const int *gamma_type;
+ const SANE_Bool *gamma_user_defined;
+
+ const int *color_type;
+ const SANE_Bool *color_user_defined;
+
+ SANE_Bool color_shuffle; /* does this scanner need color shuffling */
+ SANE_Int maxDepth; /* max. color depth */
+
+ SANE_Int max_line_distance;
+
+ SANE_Bool need_color_reorder;
+ SANE_Bool need_reset_on_source_change;
+
+ SANE_Bool wait_for_button; /* do we have to wait until the scanner button is pressed? */
+
+ SANE_Int doctype;
+
+ unsigned int productID;
+
+ EpsonCmd cmd;
+ const EpsonScanHardRec *scan_hard;
+
+ SANE_Bool using_fs; /*! determines whether to use fs commands */
+
+ SANE_Bool block_mode;
+ uint32_t image_block_size;
+ uint32_t final_block_size;
+ uint32_t block_total; /* does NOT include final block! */
+ uint32_t block_count;
+
+ unsigned char param_buf[64]; /*! holds the FS W parameter buffer */
+
+ SANE_Int scan_width_limit;
+
+ SANE_Int base_res; /*! resolution at which the max scan area is defined */
+
+ SANE_Int old_max; /*! used to restore after limiting resolutions */
+
+ SANE_Int polling_time;
+
+ bool uses_locking;
+ bool is_locked;
+};
+
+typedef struct device device;
+
+device *dev_dtor (device *hw);
+
+SANE_Status dev_request_extended_status (device *hw);
+SANE_Status dev_load_paper (device *hw);
+SANE_Status dev_eject_paper (device *hw);
+SANE_Status dev_open (device *hw);
+
+/* FS W/S related functions */
+SANE_Status dev_log_scanning_parameter (device *hw);
+SANE_Status dev_set_scanning_parameter (device *hw,
+ byte cmd,
+ const byte* param);
+SANE_Status dev_set_scanning_resolution (device *hw, SANE_Int x_dpi,
+ SANE_Int y_dpi);
+SANE_Status dev_set_scanning_area (device *hw,
+ SANE_Int left, SANE_Int top,
+ SANE_Int width, SANE_Int height);
+SANE_Status dev_set_option_unit (device *hw, byte adf_mode);
+
+/* other */
+
+/*! supports workarounds to temporarily limit resolution lists */
+void dev_limit_res (device *self, SANE_Constraint_Type type, int limit);
+
+/*! restore original resolution list after it has been limited */
+void dev_restore_res (device *self, SANE_Constraint_Type type);
+
+bool dev_force_cancel (device *self);
+
+SANE_Status dev_lock (device *hw);
+SANE_Status dev_unlock (device *hw);
+
+SANE_Status dev_clean (device *hw);
+SANE_Status dev_calibrate (device *hw);
+
+#endif /* !defined (device_h_included) */
diff --git a/backend/dip-obj.c b/backend/dip-obj.c
new file mode 100644
index 0000000..021cb01
--- /dev/null
+++ b/backend/dip-obj.c
@@ -0,0 +1,746 @@
+/* dip-obj.c -- digital image processing functionality singleton
+ * Copyright (C) 2011 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "dip-obj.h"
+#include "defines.h"
+#include "hw-data.h"
+#include "include/sane/sanei_magic.h"
+#include "ipc.h"
+
+#ifndef ENABLE_SANEI_MAGIC
+#define ENABLE_SANEI_MAGIC 0
+#endif
+
+/*! \brief Constrain a value \a v in the interval \c [lo,hi]
+ */
+#define clamp(v,lo,hi) \
+ ((v) < (lo) \
+ ? (lo) \
+ : ((v) > (hi) \
+ ? (hi) \
+ : (v))) \
+ /**/
+
+typedef struct
+{
+ process *plugin;
+
+ void (*autocrop) ();
+ void (*deskew) (buffer *, int, int);
+
+} dip_type;
+
+static dip_type *dip = NULL;
+
+static void esdip_crop (buffer *buf, const device *hw, unsigned int count,
+ const Option_Value *val);
+static void esdip_turn (buffer *buf, int res_x, int res_y);
+
+static void magic_crop (buffer *buf, int res_x, int res_y);
+static void magic_turn (buffer *buf, int res_x, int res_y);
+
+void *
+dip_init (const char *pkglibdir, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ log_call ("(%s, %p)", pkglibdir, status);
+
+ if (dip)
+ {
+ err_minor ("been here, done that");
+ if (status) *status = s;
+ return dip;
+ }
+
+ dip = t_calloc (1, dip_type);
+ if (!dip)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return dip;
+ }
+
+ dip->plugin = ipc_exec ("esdip", pkglibdir, status);
+
+ if (dip->plugin)
+ {
+ dip->autocrop = esdip_crop;
+ dip->deskew = esdip_turn;
+ }
+ else if (ENABLE_SANEI_MAGIC) /* use free alternative API */
+ {
+ sanei_magic_init ();
+ dip->autocrop = magic_crop;
+ dip->deskew = magic_turn;
+ }
+
+ if (status) *status = s;
+ return dip;
+}
+
+void *
+dip_exit (void *self)
+{
+ log_call ("(%p)", self);
+ require (dip == self);
+
+ if (dip)
+ {
+ if (dip->plugin)
+ {
+ dip->plugin = ipc_kill (dip->plugin);
+ }
+ else
+ {
+ /* sanei_magic_exit, if there was one */
+ }
+ delete (dip);
+ }
+
+ return dip;
+}
+
+bool
+dip_needs_whole_image (const void *self, const Option_Value *val,
+ const SANE_Option_Descriptor *opt)
+{
+ require (dip == self && val);
+
+ /* esdip_deskew and esdip_autocrop do not support this
+ */
+ if (val[OPT_X_RESOLUTION].w != val[OPT_Y_RESOLUTION].w)
+ return false;
+
+ return ((SANE_OPTION_IS_ACTIVE (opt[OPT_DESKEW].cap)
+ && val[OPT_DESKEW].b)
+ || (SANE_OPTION_IS_ACTIVE (opt[OPT_AUTOCROP].cap)
+ && val[OPT_AUTOCROP].b));
+}
+
+void
+dip_apply_LUT (const void *self, const buffer *buf,
+ const LUT *m)
+{
+ require (dip == self && buf && m);
+ require (m->depth == buf->ctx.depth);
+
+ /**/ if (16 == buf->ctx.depth)
+ {
+ uint16_t *p = (uint16_t *) buf->ptr;
+ uint16_t *e = (uint16_t *) buf->end;
+
+ while (p < e)
+ {
+ *p = * (uint16_t *) (m->lut + 2 * *p);
+ ++p;
+ }
+ }
+ else if ( 8 == buf->ctx.depth)
+ {
+ SANE_Byte *p = buf->ptr;
+ SANE_Byte *e = buf->end;
+
+ while (p < e)
+ {
+ *p = m->lut[*p];
+ ++p;
+ }
+ }
+ else
+ err_major ("noop: unsupported bit depth %d", buf->ctx.depth);
+}
+
+void
+dip_apply_LUT_RGB (const void *self, const buffer *buf,
+ const LUT *r, const LUT *g, const LUT *b)
+{
+ require (dip == self && buf && r && g && b);
+ require (r->depth == buf->ctx.depth);
+ require (g->depth == buf->ctx.depth);
+ require (b->depth == buf->ctx.depth);
+
+ if (SANE_FRAME_RGB != buf->ctx.format)
+ {
+ err_minor ("noop: image data not in RGB format");
+ return;
+ }
+
+ /**/ if (16 == buf->ctx.depth)
+ {
+ uint16_t *p = (uint16_t *) buf->ptr;
+ uint16_t *e = (uint16_t *) buf->end;
+
+ while (p < e)
+ {
+ p[0] = * (uint16_t *) (r->lut + 2 * p[0]);
+ p[1] = * (uint16_t *) (g->lut + 2 * p[1]);
+ p[2] = * (uint16_t *) (b->lut + 2 * p[2]);
+ p += 3;
+ }
+ }
+ else if ( 8 == buf->ctx.depth)
+ {
+ SANE_Byte *p = buf->ptr;
+ SANE_Byte *e = buf->end;
+
+ while (p < e)
+ {
+ p[0] = r->lut[p[0]];
+ p[1] = g->lut[p[1]];
+ p[2] = b->lut[p[2]];
+ p += 3;
+ }
+ }
+ else
+ err_major ("noop: unsupported bit depth %d", buf->ctx.depth);
+}
+
+/*! \brief Destroys a LUT object
+ */
+LUT *
+dip_destroy_LUT (const void *self, LUT *m)
+{
+ require (dip == self);
+
+ if (m) delete (m->lut);
+ delete (m);
+
+ return m;
+}
+
+/*! \brief Creates a LUT with an encoding \a gamma
+ *
+ * \sa http://en.wikipedia.org/wiki/Gamma_correction
+ */
+LUT *
+dip_gamma_LUT (const void *self, int depth,
+ double gamma)
+{
+ SANE_Byte *lut;
+ LUT *m;
+
+ size_t i;
+ double max;
+
+ require (dip == self);
+ require (8 == depth || 16 == depth);
+
+ lut = t_malloc ((1 << depth) * (depth / 8), SANE_Byte);
+ m = t_malloc (1, LUT);
+
+ if (!lut || !m)
+ {
+ delete (lut);
+ delete (m);
+ return m;
+ }
+
+ m->lut = lut;
+ m->depth = depth;
+
+ max = (1 << depth) - 1;
+
+ for (i = 0; i < (1 << depth); ++i)
+ {
+ double value = max * pow (i / max, 1 / gamma);
+
+ if (16 == depth)
+ {
+ uint16_t *p = (uint16_t *) lut;
+
+ p[i] = clamp (value, 0, max);
+ }
+ else
+ {
+ lut[i] = clamp (value, 0, max);
+ }
+ }
+
+ return m;
+}
+
+LUT *
+dip_iscan_BCHS_LUT (const void *self, int depth,
+ double brightness, double contrast,
+ double highlight, double shadow)
+{
+ SANE_Byte *lut;
+ LUT *m;
+
+ size_t i;
+ size_t max;
+
+ int32_t b, c, h, s, f;
+
+ require (dip == self);
+ require (-1 <= brightness && brightness <= 1);
+ require (-1 <= contrast && contrast <= 1);
+ require ( 0 <= highlight && highlight <= 1);
+ require ( 0 <= shadow && shadow <= 1);
+ require (8 == depth || 16 == depth);
+
+ lut = t_malloc ((1 << depth) * (depth / 8), SANE_Byte);
+ m = t_malloc (1, LUT);
+ if (!lut || !m)
+ {
+ delete (lut);
+ delete (m);
+ return m;
+ }
+
+ m->lut = lut;
+ m->depth = depth;
+
+ /* Compute algorithm parameters by scaling the double values into
+ * integral values that correspond to the bit depth. Computation
+ * of loop variable independent parts is done by absorption into
+ * existing variables.
+ */
+ f = (1 << (depth - 1)) - 1;
+ s = f * shadow;
+ h = -f * highlight; h += 2 * f + 1;
+ b = f * brightness;
+ c = ((0 > contrast)
+ ? f * contrast
+ : (h - s) / 2 * contrast);
+
+ log_data ("b = %d", b);
+ log_data ("c = %d", c);
+ log_data ("h = %d", h);
+ log_data ("s = %d", s);
+
+ if (2 * c == (h - s)) --c; /* avoid zero division */
+
+ s += c; /* absorb scaled contrast */
+ h -= c; /* absorb scaled contrast */
+ h -= s; /* absorb shifted shadow */
+
+ log_data ("h' = %d", h);
+ log_data ("s' = %d", s);
+
+ max = (1 << depth) - 1;
+ log_data ("max = %zd", max);
+
+ for (i = 0; i < (1 << depth); ++i)
+ {
+ int32_t value = i;
+
+ value -= s;
+ value *= max;
+ value /= h;
+ value += b;
+
+ if (16 == depth)
+ {
+ uint16_t *p = (uint16_t *) lut;
+
+ p[i] = clamp (value, 0, max);
+ }
+ else
+ {
+ lut[i] = clamp (value, 0, max);
+ }
+ }
+ return m;
+}
+
+LUT *
+dip_iscan_BC_LUT (const void *self, int depth,
+ double brightness, double contrast)
+{
+ return dip_iscan_BCHS_LUT (self, depth, brightness, contrast, 0, 0);
+}
+
+/*! \brief Creates a LUT for brightness/contrast manipulations
+ *
+ * Algorithm indirectly taken from the GIMP.
+ *
+ * \sa http://en.wikipedia.org/wiki/Image_editing#Contrast_change_and_brightening
+ */
+LUT *
+dip_gimp_BC_LUT (const void *self, int depth,
+ double brightness, double contrast)
+{
+ SANE_Byte *lut;
+ LUT *m;
+
+ size_t i;
+ double max;
+
+ require (dip == self);
+ require (-1 <= brightness && brightness <= 1);
+ require (-1 <= contrast && contrast <= 1);
+ require (8 == depth || 16 == depth);
+
+ lut = t_malloc ((1 << depth) * (depth / 8), SANE_Byte);
+ m = t_malloc (1, LUT);
+
+ if (!lut || !m)
+ {
+ delete (lut);
+ delete (m);
+ return m;
+ }
+
+ m->lut = lut;
+ m->depth = depth;
+
+ max = (1 << depth) - 1;
+
+ for (i = 0; i < (1 << depth); ++i)
+ {
+ double value = i / max;
+
+ if (brightness < 0.0)
+ value *= 1.0 + brightness;
+ else
+ value += (1 - value) * brightness;
+
+ value = (value - 0.5) * tan ((contrast + 1) * M_PI / 4) + 0.5;
+ value *= max;
+
+ if (16 == depth)
+ {
+ uint16_t *p = (uint16_t *) lut;
+
+ p[i] = clamp (value, 0, max);
+ }
+ else
+ {
+ lut[i] = clamp (value, 0, max);
+ }
+ }
+
+ return m;
+}
+
+void
+dip_flip_bits (const void *self, const buffer *buf)
+{
+ SANE_Byte *p;
+
+ require (dip == self && buf);
+
+ p = buf->ptr;
+ while (p != buf->end)
+ {
+ *p = ~*p;
+ ++p;
+ }
+}
+
+static
+void
+dip_change_GRB_to_RGB_16 (const void *self, const buffer *buf)
+{
+ SANE_Byte *p, tmp;
+
+ require (dip == self && buf && 16 == buf->ctx.depth);
+
+ p = buf->ptr;
+ while (p < buf->end)
+ {
+ tmp = p[0]; /* most significant byte */
+ p[0] = p[2];
+ p[2] = p[0];
+ tmp = p[1]; /* least significant byte */
+ p[1] = p[3];
+ p[3] = tmp;
+
+ p += 6;
+ }
+}
+
+static
+void
+dip_change_GRB_to_RGB_8 (const void *self, const buffer *buf)
+{
+ SANE_Byte *p, tmp;
+
+ require (dip == self && buf && 8 == buf->ctx.depth);
+
+ p = buf->ptr;
+ while (p < buf->end)
+ {
+ tmp = p[0];
+ p[0] = p[1];
+ p[1] = tmp;
+
+ p += 3;
+ }
+}
+
+void
+dip_change_GRB_to_RGB (const void *self, const buffer *buf)
+{
+ require (dip == self && buf);
+
+ if (SANE_FRAME_RGB != buf->ctx.format)
+ return;
+
+ /**/ if (16 == buf->ctx.depth)
+ return dip_change_GRB_to_RGB_16 (self, buf);
+ else if ( 8 == buf->ctx.depth)
+ return dip_change_GRB_to_RGB_8 (self, buf);
+
+ err_major ("unsupported bit depth");
+ return;
+}
+
+/*! \todo Add support for 16 bit color values (#816).
+ */
+void
+dip_apply_color_profile (const void *self, const buffer *buf,
+ const double profile[9])
+{
+ SANE_Int i;
+ SANE_Byte *r_buf, *g_buf, *b_buf;
+ double red, grn, blu;
+
+ SANE_Byte *data;
+ SANE_Int size;
+
+ require (dip == self && buf && profile);
+ require (8 == buf->ctx.depth);
+
+ if (SANE_FRAME_RGB != buf->ctx.format)
+ return;
+
+ data = buf->ptr;
+ size = buf->end - buf->ptr;
+
+ for (i = 0; i < size / 3; i++)
+ {
+ r_buf = data;
+ g_buf = data + 1;
+ b_buf = data + 2;
+
+ red =
+ profile[0] * (*r_buf) + profile[1] * (*g_buf) + profile[2] * (*b_buf);
+ grn =
+ profile[3] * (*r_buf) + profile[4] * (*g_buf) + profile[5] * (*b_buf);
+ blu =
+ profile[6] * (*r_buf) + profile[7] * (*g_buf) + profile[8] * (*b_buf);
+
+ *data++ = clamp (red, 0, 255);
+ *data++ = clamp (grn, 0, 255);
+ *data++ = clamp (blu, 0, 255);
+ }
+}
+
+bool
+dip_has_deskew (const void *self, const device *hw)
+{
+ require (dip == self);
+
+ return (magic_turn == dip->deskew
+ || (esdip_turn == dip->deskew
+ && enable_dip_deskew (hw)));
+}
+
+static
+void
+esdip_turn (buffer *buf, int res_x, int res_y)
+{
+ ipc_dip_parms p;
+
+ require (dip->plugin);
+
+ memset (&p, 0, sizeof (p));
+ memcpy (&p.parms, &buf->ctx, sizeof (p.parms));
+ p.res_x = res_x;
+ p.res_y = res_y;
+
+ ipc_dip_proc (dip->plugin, TYPE_DIP_SKEW_FLAG, &p,
+ &buf->ctx, (void **) &buf->buf);
+
+ buf->cap = buf->ctx.bytes_per_line * buf->ctx.lines;
+ buf->ptr = buf->buf;
+ buf->end = buf->ptr;
+ buf->end += buf->ctx.bytes_per_line * buf->ctx.lines;
+}
+
+static
+void
+magic_turn (buffer *buf, int res_x, int res_y)
+{
+ SANE_Status status;
+ int center_x, center_y;
+ double angle;
+ const int bg_sample = 0xff; /* white */
+
+ require (buf);
+
+ status = sanei_magic_findSkew (&buf->ctx, buf->buf, res_x, res_y,
+ &center_x, &center_y, &angle);
+ if (SANE_STATUS_GOOD == status)
+ {
+ status = sanei_magic_rotate (&buf->ctx, buf->buf,
+ center_x, center_y, -angle, bg_sample);
+ }
+
+ buf->ptr = buf->buf;
+ buf->end = buf->ptr;
+ buf->end += buf->ctx.bytes_per_line * buf->ctx.lines;
+}
+
+void
+dip_deskew (const void *self, const device *hw, unsigned int count,
+ buffer *buf, const Option_Value *val)
+{
+ require (dip == self && buf && val);
+
+ dip->deskew (buf, val[OPT_X_RESOLUTION].w, val[OPT_Y_RESOLUTION].w);
+}
+
+/*! \bug autocrop_max_y() is the \e wrong criterion to limit autocrop
+ * support to selected devices.
+ */
+bool
+dip_has_autocrop (const void *self, const device *hw)
+{
+ require (dip == self);
+
+ return (magic_crop == dip->autocrop
+ || (esdip_crop == dip->autocrop
+ && 0 != autocrop_max_y (hw)));
+}
+
+static
+void
+esdip_crop (buffer *buf, const device *hw, unsigned int count,
+ const Option_Value *val)
+{
+ ipc_dip_parms p;
+
+ require (dip->plugin && hw && hw->fw_name && val);
+
+ memset (&p, 0, sizeof (p));
+ memcpy (&p.parms, &buf->ctx, sizeof (p.parms));
+ p.res_x = val[OPT_X_RESOLUTION].w;
+ p.res_y = val[OPT_Y_RESOLUTION].w;
+ p.gamma = hw->gamma_type[ val[OPT_GAMMA_CORRECTION].w ];
+ p.bside = SANE_FALSE;
+ if (using (hw, adf) && val[OPT_ADF_MODE].w)
+ {
+ p.bside = (0 == count % 2);
+ }
+ strncpy (p.fw_name, hw->fw_name, num_of (p.fw_name));
+
+ ipc_dip_proc (dip->plugin, TYPE_DIP_CROP_FLAG, &p,
+ &buf->ctx, (void **) &buf->buf);
+
+ buf->cap = buf->ctx.bytes_per_line * buf->ctx.lines;
+ buf->ptr = buf->buf;
+ buf->end = buf->ptr;
+ buf->end += buf->ctx.bytes_per_line * buf->ctx.lines;
+}
+
+static
+void
+magic_crop (buffer *buf, int res_x, int res_y)
+{
+ SANE_Status status;
+ int top, left, bottom, right;
+
+ require (buf);
+
+ status = sanei_magic_findEdges (&buf->ctx, buf->buf, res_x, res_y,
+ &top, &bottom, &left, &right);
+ if (SANE_STATUS_GOOD == status)
+ {
+ status = sanei_magic_crop (&buf->ctx, buf->buf,
+ top, bottom, left, right);
+ }
+
+ buf->ptr = buf->buf;
+ buf->end = buf->ptr;
+ buf->end += buf->ctx.bytes_per_line * buf->ctx.lines;
+}
+
+void
+dip_autocrop (const void *self, const device *hw, unsigned int count,
+ buffer *buf, const Option_Value *val)
+{
+ require (dip == self && buf && val);
+
+ /**/ if (esdip_crop == dip->autocrop)
+ {
+ esdip_crop (buf, hw, count, val);
+ }
+ else if (magic_crop == dip->autocrop)
+ {
+ int res_x = val[OPT_X_RESOLUTION].w;
+ int res_y = val[OPT_Y_RESOLUTION].w;
+
+ magic_crop (buf, res_x, res_y);
+ }
+}
diff --git a/backend/dip-obj.h b/backend/dip-obj.h
new file mode 100644
index 0000000..6f010c8
--- /dev/null
+++ b/backend/dip-obj.h
@@ -0,0 +1,119 @@
+/* dip-obj.h -- digital image processing functionality singleton
+ * Copyright (C) 2011 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef dip_obj_h_included
+#define dip_obj_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sane/sane.h>
+
+#include "device.h"
+#include "epkowa.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void * dip_init (const char *pkglibdir, SANE_Status *status);
+ void * dip_exit (void *self);
+
+ bool dip_needs_whole_image (const void *self, const Option_Value *val,
+ const SANE_Option_Descriptor *opt);
+
+ void dip_apply_LUT (const void *self, const buffer *buf, const LUT *m);
+ void dip_apply_LUT_RGB (const void *self, const buffer *buf,
+ const LUT *r, const LUT *g, const LUT *b);
+ LUT * dip_destroy_LUT (const void *self, LUT *m);
+ LUT * dip_gamma_LUT (const void *self, int depth,
+ double gamma);
+ LUT * dip_iscan_BCHS_LUT (const void *self, int depth,
+ double brightness, double contrast,
+ double highlight, double shadow);
+ LUT * dip_iscan_BC_LUT (const void *self, int depth,
+ double brightness, double contrast);
+ LUT * dip_gimp_BC_LUT (const void *self, int depth,
+ double brightness, double contrast);
+
+ void dip_flip_bits (const void *self, const buffer *buf);
+ void dip_change_GRB_to_RGB (const void *self, const buffer *buf);
+ void dip_apply_color_profile (const void *self, const buffer *buf,
+ const double profile[9]);
+
+ bool dip_has_deskew (const void *self, const device *hw);
+ bool dip_has_autocrop (const void *self, const device *hw);
+
+ void dip_deskew (const void *self, const device *hw, unsigned int count,
+ buffer *buf, const Option_Value *val);
+ void dip_autocrop (const void *self, const device *hw, unsigned int count,
+ buffer *buf, const Option_Value *val);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (dip_obj_h_included) */
diff --git a/backend/epkowa.c b/backend/epkowa.c
new file mode 100644
index 0000000..14887bb
--- /dev/null
+++ b/backend/epkowa.c
@@ -0,0 +1,6513 @@
+/* epkowa.c - SANE backend for EPSON flatbed scanners
+ (Image Scan! version)
+
+ Based on the SANE Epson backend (originally from sane-1.0.3)
+ - updated to sane-backends-1.0.6
+ - renamed from epson to epkowa to avoid confusion
+ - updated to sane-backends-1.0.12
+ - updated to sane-backends-1.0.15
+
+ Based on Kazuhiro Sasayama previous
+ Work on epson.[ch] file from the SANE package.
+
+ Original code taken from sane-0.71
+ Copyright (C) 1997 Hypercore Software Design, Ltd.
+
+ modifications
+ Copyright (C) 1998-1999 Christian Bucher <bucher@vernetzt.at>
+ Copyright (C) 1998-1999 Kling & Hautzinger GmbH
+ Copyright (C) 1999 Norihiko Sawa <sawa@yb3.so-net.ne.jp>
+ Copyright (C) 2000 Mike Porter <mike@udel.edu> (mjp)
+ Copyright (C) 1999-2004 Karl Heinz Kremer <khk@khk.net>
+ Copyright (C) 2001-2016 SEIKO EPSON CORPORATION
+
+ This file is part of the EPKOWA SANE backend.
+
+ 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 SANE_I18N
+#define SANE_I18N(text) (text)
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+
+#include <sane/saneopts.h>
+
+#ifndef BACKEND_NAME
+#define BACKEND_NAME epkowa
+#endif
+
+#include "backend.h"
+#include "command.h"
+#include "hw-data.h"
+#include "utils.h"
+#include "timing.h"
+#include "cfg-obj.h"
+#include "dip-obj.h"
+#include "model-info.h"
+#include "utils.h"
+
+#include "epkowa_scsi.h"
+#include "sane/sanei_pio.h"
+#include "epkowa_ip.h" /* interpreter-based scanner support */
+
+#include "sane/sanei.h"
+
+#define DEFAULT_RESOLUTION 300 /* dpi */
+#define DEFAULT_X_RESOLUTION DEFAULT_RESOLUTION
+#define DEFAULT_Y_RESOLUTION DEFAULT_RESOLUTION
+
+#define S_ACK "\006"
+#define S_CAN "\030"
+
+/* Usable values when defining EPSON_LEVEL_DEFAULT
+ * There is also a function level "A5", used for the GT-300, a
+ * monochrome only scanner. This level is not supported.
+ */
+#define EPSON_LEVEL_A1 0
+#define EPSON_LEVEL_A2 1
+#define EPSON_LEVEL_B1 2
+#define EPSON_LEVEL_B2 3
+#define EPSON_LEVEL_B3 4
+#define EPSON_LEVEL_B4 5
+#define EPSON_LEVEL_B5 6
+#define EPSON_LEVEL_B6 7
+#define EPSON_LEVEL_B7 8
+#define EPSON_LEVEL_B8 9
+#define EPSON_LEVEL_F5 10
+#define EPSON_LEVEL_D1 11
+#define EPSON_LEVEL_D2 12
+#define EPSON_LEVEL_D7 13
+#define EPSON_LEVEL_D8 14
+
+#define EPSON_LEVEL_DEFAULT EPSON_LEVEL_B3
+
+static EpsonCmdRec epson_cmd[] = {
+/*
+ * request identity
+ * | request identity2
+ * | | request status
+ * | | | request condition
+ * | | | | set color mode
+ * | | | | | start scanning
+ * | | | | | | set data format
+ * | | | | | | | set resolution
+ * | | | | | | | | set zoom
+ * | | | | | | | | | set scan area
+ * | | | | | | | | | | set brightness
+ * | | | | | | | | | | | set gamma
+ * | | | | | | | | | | | | set halftoning
+ * | | | | | | | | | | | | | set color correction
+ * | | | | | | | | | | | | | | initialize scanner
+ * | | | | | | | | | | | | | | | set speed
+ * | | | | | | | | | | | | | | | | set lcount
+ * | | | | | | | | | | | | | | | | | mirror image
+ * | | | | | | | | | | | | | | | | | | set gamma table
+ * | | | | | | | | | | | | | | | | | | | set outline emphasis
+ * | | | | | | | | | | | | | | | | | | | | set dither
+ * | | | | | | | | | | | | | | | | | | | | | set color correction coefficients
+ * | | | | | | | | | | | | | | | | | | | | | | request extension status
+ * | | | | | | | | | | | | | | | | | | | | | | | control an extension
+ * | | | | | | | | | | | | | | | | | | | | | | | | forward feed / eject
+ * | | | | | | | | | | | | | | | | | | | | | | | | | feed
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | request push button status
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | control auto area segmentation
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | set film type
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set exposure time
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set bay
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set threshold
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set focus position
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request focus position
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ */
+ {"A1",'I', 0 ,'F','S', 0 ,'G', 0 ,'R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"A2",'I', 0 ,'F','S', 0 ,'G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B1",'I', 0 ,'F','S','C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 ,'B', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B2",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B3",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@', 0 , 0 , 0 , 0 , 0 , 0 ,'m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B4",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d', 0 ,'z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B5",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B6",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
+ {"B7",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0 ,'!','s','N', 0 , 0 ,'t', 0 , 0 },
+ {"B8",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0x19,'!','s','N', 0 , 0 ,'t','p','q'},
+ {"F5",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z', 0 ,'M','@','g','d','K','z','Q', 0 ,'m','f','e','\f', 0 , 0 , 0 ,'N','T','P', 0 , 0 , 0 },
+ {"D1",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f', 0 , 0 , 0 ,'!', 0 , 0 , 0 , 0 ,'t', 0 , 0 },
+ {"D2",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e', 0 , 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 },
+ {"D7",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 },
+ {"D8",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 },
+};
+
+
+/* Definition of the mode_param struct, that is used to
+ * specify the valid parameters for the different scan modes.
+ *
+ * The depth variable gets updated when the bit depth is modified.
+ */
+
+struct mode_param
+{
+ int color;
+ int mode_flags;
+ int dropout_mask;
+ int depth;
+};
+
+static struct mode_param mode_params[] = {
+ {0, 0x00, 0x30, 1},
+ {0, 0x00, 0x30, 8},
+ {1, 0x02, 0x00, 8}
+};
+
+static const SANE_String_Const mode_list[] = {
+ SANE_I18N ("Binary"),
+ SANE_I18N ("Gray"),
+ SANE_I18N ("Color"),
+ NULL
+};
+
+static const SANE_String_Const adf_mode_list[] = {
+ SANE_I18N ("Simplex"),
+ SANE_I18N ("Duplex"),
+ NULL
+};
+
+static const SANE_String_Const dfd_sensitivity[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Low"),
+ SANE_I18N ("High"),
+ NULL
+};
+
+/* Define the different scan sources:
+ */
+
+#define FBF_STR SANE_I18N("Flatbed")
+#define TPU_STR SANE_I18N("Transparency Unit")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+#define FILM_TYPE_POSITIVE (0)
+#define FILM_TYPE_NEGATIVE (1)
+
+static const SANE_String_Const film_list[] = {
+ SANE_I18N ("Positive Film"),
+ SANE_I18N ("Negative Film"),
+ NULL
+};
+
+static const SANE_String_Const focus_list[] = {
+ SANE_I18N ("Focus on glass"),
+ SANE_I18N ("Focus 2.5mm above glass"),
+ NULL
+};
+
+#define HALFTONE_NONE 0x01
+#define HALFTONE_TET 0x03
+
+static int halftone_params[] = {
+ HALFTONE_NONE,
+ 0x00,
+ 0x10,
+ 0x20,
+ 0x80,
+ 0x90,
+ 0xa0,
+ 0xb0,
+ HALFTONE_TET,
+ 0xc0,
+ 0xd0
+};
+
+static const SANE_String_Const halftone_list[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Halftone A (Hard Tone)"),
+ SANE_I18N ("Halftone B (Soft Tone)"),
+ SANE_I18N ("Halftone C (Net Screen)"),
+ NULL
+};
+
+static const SANE_String_Const halftone_list_4[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Halftone A (Hard Tone)"),
+ SANE_I18N ("Halftone B (Soft Tone)"),
+ SANE_I18N ("Halftone C (Net Screen)"),
+ SANE_I18N ("Dither A (4x4 Bayer)"),
+ SANE_I18N ("Dither B (4x4 Spiral)"),
+ SANE_I18N ("Dither C (4x4 Net Screen)"),
+ SANE_I18N ("Dither D (8x4 Net Screen)"),
+ NULL
+};
+
+static const SANE_String_Const halftone_list_7[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Halftone A (Hard Tone)"),
+ SANE_I18N ("Halftone B (Soft Tone)"),
+ SANE_I18N ("Halftone C (Net Screen)"),
+ SANE_I18N ("Dither A (4x4 Bayer)"),
+ SANE_I18N ("Dither B (4x4 Spiral)"),
+ SANE_I18N ("Dither C (4x4 Net Screen)"),
+ SANE_I18N ("Dither D (8x4 Net Screen)"),
+ SANE_I18N ("Text Enhanced Technology"),
+ SANE_I18N ("Download pattern A"),
+ SANE_I18N ("Download pattern B"),
+ NULL
+};
+
+static int dropout_params[] = {
+ 0x00, /* none */
+ 0x10, /* red */
+ 0x20, /* green */
+ 0x30 /* blue */
+};
+
+static const SANE_String_Const dropout_list[] = {
+ SANE_I18N ("None"),
+ SANE_I18N ("Red"),
+ SANE_I18N ("Green"),
+ SANE_I18N ("Blue"),
+ NULL
+};
+
+static const SANE_String_Const brightness_method_list[] = {
+ SANE_I18N ("hardware"), /* needs to be first */
+ SANE_I18N ("iscan"),
+ SANE_I18N ("gimp"),
+ NULL
+};
+
+#define max_val 100 /* any integer value will do */
+static const SANE_Range brightness_range = { -max_val, max_val, 1};
+static const SANE_Range contrast_range = { -max_val, max_val, 1};
+#if 0
+/* We don't provide highlight and shadow options yet because we
+ * haven't worked out the GIMP part of that and how that should
+ * interact with the brightness-method option. For the "iscan"
+ * method, the values below are OK.
+ */
+static const SANE_Range highlight_range = { 0, max_val, 0 };
+static const SANE_Range shadow_range = { 0, max_val, 0 };
+#endif
+#undef max_val
+
+/* Color correction:
+ * One array for the actual parameters that get sent to the scanner (color_params[]),
+ * one array for the strings that get displayed in the user interface (color_list[])
+ * and one array to mark the user defined color correction (dolor_userdefined[]).
+ */
+
+static int color_params_ab[] = {
+ 0x00,
+ 0x01,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80
+};
+
+static SANE_Bool color_userdefined_ab[] = {
+ SANE_FALSE,
+ SANE_TRUE,
+ SANE_FALSE,
+ SANE_FALSE,
+ SANE_FALSE,
+ SANE_FALSE
+};
+
+static const SANE_String_Const color_list_ab[] = {
+ SANE_I18N ("No Correction"),
+ SANE_I18N ("User defined"),
+ SANE_I18N ("Impact-dot printers"),
+ SANE_I18N ("Thermal printers"),
+ SANE_I18N ("Ink-jet printers"),
+ SANE_I18N ("CRT monitors"),
+ NULL
+};
+
+static int color_params_d[] = {
+ 0x00
+};
+
+static SANE_Bool color_userdefined_d[] = {
+ SANE_TRUE
+};
+
+static const SANE_String_Const color_list_d[] = {
+ "User defined",
+ NULL
+};
+
+
+/* Gamma correction:
+ * The A and B level scanners work differently than the D level scanners, therefore
+ * I define two different sets of arrays, plus one set of variables that get set to
+ * the actally used params and list arrays at runtime.
+ */
+
+static int gamma_params_ab[] = {
+ 0x01,
+ 0x03,
+ 0x04,
+ 0x00,
+ 0x10,
+ 0x20
+};
+
+static const SANE_String_Const gamma_list_ab[] = {
+ SANE_I18N ("Default"),
+ SANE_I18N ("User defined (Gamma=1.0)"),
+ SANE_I18N ("User defined (Gamma=1.8)"),
+ SANE_I18N ("High density printing"),
+ SANE_I18N ("Low density printing"),
+ SANE_I18N ("High contrast printing"),
+ NULL
+};
+
+static SANE_Bool gamma_userdefined_ab[] = {
+ SANE_FALSE,
+ SANE_TRUE,
+ SANE_TRUE,
+ SANE_FALSE,
+ SANE_FALSE,
+ SANE_FALSE,
+};
+
+static int gamma_params_d[] = {
+ 0x03,
+ 0x04
+};
+
+static const SANE_String_Const gamma_list_d[] = {
+ SANE_I18N ("User defined (Gamma=1.0)"),
+ SANE_I18N ("User defined (Gamma=1.8)"),
+ NULL
+};
+
+static SANE_Bool gamma_userdefined_d[] = {
+ SANE_TRUE,
+ SANE_TRUE
+};
+
+
+/* Bay list:
+ * this is used for the FilmScan
+ */
+
+static const SANE_String_Const bay_list[] = {
+ " 1 ",
+ " 2 ",
+ " 3 ",
+ " 4 ",
+ " 5 ",
+ " 6 ",
+ NULL
+};
+
+static const SANE_Range u8_range = { 0, 255, 0 };
+static const SANE_Range s8_range = { -127, 127, 0 };
+static const SANE_Range zoom_range = { 50, 200, 0 };
+
+/* The "switch_params" are used for several boolean choices
+ */
+static int switch_params[] = {
+ 0,
+ 1
+};
+
+#define mirror_params switch_params
+#define speed_params switch_params
+#define film_params switch_params
+
+static const SANE_Range outline_emphasis_range = { -2, 2, 0 };
+
+enum
+ {
+ EXT_SANE_STATUS_NONE,
+ EXT_SANE_STATUS_MULTI_FEED,
+ EXT_SANE_STATUS_TRAY_CLOSED,
+ EXT_SANE_STATUS_MAX,
+ };
+static const SANE_Range ext_sane_status = {
+ EXT_SANE_STATUS_NONE,
+ EXT_SANE_STATUS_MAX - 1,
+ 0
+};
+
+typedef struct {
+ double width; /* in mm */
+ double height; /* in mm */
+ SANE_String_Const name;
+} media_type;
+
+static SANE_String_Const media_maximum = SANE_I18N ("Maximum");
+static SANE_String_Const media_automatic = SANE_I18N ("Automatic");
+
+/*! \brief List of preset media sizes
+ * This list is used to populate the constraint list for the
+ * scan-area option.
+ *
+ * \remark When adding Landscape and Portrait variants of a medium
+ * size, make sure to add three(!) entries using the same
+ * pattern as used for those already listed. The algorithm
+ * used to populate the constraint list depends on this
+ * convention to create the most user-friendly list.
+ */
+static const media_type media_list[] = {
+ /* ISO A Series */
+ { 297, 420, SANE_I18N ("A3") },
+ { 297, 210, SANE_I18N ("A4 Landscape") },
+ { 210, 297, SANE_I18N ("A4 Portrait") },
+ { 210, 297, SANE_I18N ("A4") },
+ { 210, 148, SANE_I18N ("A5 Landscape") },
+ { 148, 210, SANE_I18N ("A5 Portrait") },
+ { 148, 210, SANE_I18N ("A5") },
+ /* JIS B Series */
+ { 257, 364, SANE_I18N ("B4") },
+ { 257, 184, SANE_I18N ("B5 Landscape") },
+ { 184, 257, SANE_I18N ("B5 Portrait") },
+ { 184, 257, SANE_I18N ("B5") },
+ /* North American sizes */
+ { 11.00 * MM_PER_INCH, 17.00 * MM_PER_INCH, SANE_I18N ("Ledger") },
+ { 8.50 * MM_PER_INCH, 14.00 * MM_PER_INCH, SANE_I18N ("Legal") },
+ { 11.00 * MM_PER_INCH, 8.50 * MM_PER_INCH, SANE_I18N ("Letter Landscape") },
+ { 8.50 * MM_PER_INCH, 11.00 * MM_PER_INCH, SANE_I18N ("Letter Portrait") },
+ { 8.50 * MM_PER_INCH, 11.00 * MM_PER_INCH, SANE_I18N ("Letter") },
+ { 10.50 * MM_PER_INCH, 7.25 * MM_PER_INCH, SANE_I18N ("Executive Landscape") },
+ { 7.25 * MM_PER_INCH, 10.50 * MM_PER_INCH, SANE_I18N ("Executive Portrait") },
+ { 7.25 * MM_PER_INCH, 10.50 * MM_PER_INCH, SANE_I18N ("Executive") },
+ /* Miscellaneous */
+ { 120, 120, SANE_I18N ("CD")},
+};
+
+
+static SANE_Word *bitDepthList = NULL;
+
+
+/* Some utility macros
+ */
+
+/*! Returns the larger of the arguments \a a and \a b.
+ */
+#define max(a, b) ((a) < (b) ? (b) : (a))
+
+/*! Returns the smaller of the arguments \a a and \a b.
+ */
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; i++)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+static inline
+bool
+need_autocrop_override (const Epson_Scanner *s)
+{
+ return (SANE_OPTION_IS_ACTIVE (s->opt[OPT_AUTOCROP].cap)
+ && s->val[OPT_AUTOCROP].b
+ && 0 != autocrop_max_y (s->hw));
+}
+
+typedef struct
+{
+ u_char code;
+ u_char status;
+ u_char count1;
+ u_char count2;
+
+ u_char type;
+ u_char level;
+
+ u_char buf[1];
+
+} EpsonIdentRec, *EpsonIdent;
+
+typedef struct
+{
+ u_char code;
+ u_char status;
+ u_char count1;
+ u_char count2;
+ u_char buf[1];
+
+} EpsonHdrRec, *EpsonHdr;
+
+
+static SANE_Status
+read_image_info_block (device *hw);
+
+static SANE_Status
+get_identity_information (device *hw);
+
+static SANE_Status
+get_hardware_property (device *hw);
+
+static SANE_Status
+get_identity2_information (device *hw, Epson_Scanner *s);
+
+static SANE_Status
+check_warmup (device *hw);
+
+static SANE_Status
+check_ext_status (device *hw);
+
+SANE_Status
+control_option_unit (device *hw, SANE_Bool use_duplex);
+
+static SANE_Status
+initialize (device *hw);
+
+static SANE_Status
+get_resolution_constraints (device *hw, Epson_Scanner *s);
+
+static SANE_Status
+get_push_button_status (device *hw, SANE_Bool *button_pushed);
+
+static SANE_Status color_shuffle (Epson_Scanner *s, int *new_length);
+static void activateOption (Epson_Scanner * s, SANE_Int option,
+ SANE_Bool * change);
+static void deactivateOption (Epson_Scanner * s, SANE_Int option,
+ SANE_Bool * change);
+static void setOptionState (Epson_Scanner * s, SANE_Bool state,
+ SANE_Int option, SANE_Bool * change);
+static void filter_resolution_list (Epson_Scanner * s);
+static void scan_finish (Epson_Scanner * s);
+
+static void get_colorcoeff_from_profile (double *profile,
+ unsigned char *color_coeff);
+static void round_cct (double org_cct[], int rnd_cct[]);
+static int get_roundup_index (double frac[], int n);
+static int get_rounddown_index (double frac[], int n);
+
+static void handle_mode (Epson_Scanner * s, SANE_Int optindex,
+ SANE_Bool * reload);
+static void handle_resolution (Epson_Scanner * s, SANE_Int option,
+ SANE_Word value);
+static void change_profile_matrix (Epson_Scanner * s);
+static void handle_depth_halftone (Epson_Scanner * s, SANE_Int optindex,
+ SANE_Bool * reload);
+
+static SANE_Status create_epson_device (device **dev, channel* ch);
+static SANE_Status create_epson_scanner (device *dev, Epson_Scanner **scanner);
+static SANE_Status create_sane_handle (Epson_Scanner **scanner, const char *name, const void *dip);
+static SANE_Status init_options (Epson_Scanner * s);
+
+static scan_area_t get_model_info_max_scan_area (device *hw, int adf_mode);
+static SANE_Status handle_scan_area(Epson_Scanner *s, int adf_mode);
+
+static SANE_Status handle_source (Epson_Scanner * s,
+ SANE_Int optindex,
+ char *value);
+
+static void adf_handle_out_of_paper (Epson_Scanner * s);
+static void adf_handle_adjust_alignment (Epson_Scanner *s, SANE_Bool finalize);
+
+static void handle_autocrop (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload);
+static void handle_deskew (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload);
+static void handle_detect_doc_size (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload);
+static void handle_preview (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload);
+
+static SANE_Status
+expect_ack (device *hw)
+{
+ u_char result[1];
+ size_t len;
+ SANE_Status status;
+
+ log_call ();
+
+ len = sizeof (result);
+
+ channel_recv (hw->channel, result, len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ if (ACK != result[0])
+ return SANE_STATUS_INVAL;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+set_cmd (device *hw, u_char cmd, u_char val)
+{
+ SANE_Status status;
+ u_char params[2];
+
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ log_call ("(%c)", cmd);
+
+ /* Override with extended parameter command (FS W).
+ * Do not override A, R and e, they are handled separately.
+ */
+ if (hw->using_fs && strchr ("CDgdZLMBtsQKN", cmd))
+ return dev_set_scanning_parameter (hw, cmd, &val);
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+ channel_send (hw->channel, params, 2, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (hw)))
+ return status;
+
+ params[0] = val;
+ channel_send (hw->channel, params, 1, &status);
+ status = expect_ack (hw);
+
+ return status;
+}
+
+static void
+print_params (const SANE_Parameters params)
+{
+ log_data ("params.format = %d", params.format);
+ log_data ("params.last_frame = %d", params.last_frame);
+ log_data ("params.bytes_per_line = %d", params.bytes_per_line);
+ log_data ("params.pixels_per_line = %d", params.pixels_per_line);
+ log_data ("params.lines = %d", params.lines);
+ log_data ("params.depth = %d", params.depth);
+}
+
+
+#define set_focus_position(h,v) set_cmd (h,(h)->cmd->set_focus_position,v)
+#define set_color_mode(h,v) set_cmd (h,(h)->cmd->set_color_mode,v)
+#define set_data_format(h,v) set_cmd (h,(h)->cmd->set_data_format, v)
+#define set_halftoning(h,v) set_cmd (h,(h)->cmd->set_halftoning, v)
+#define set_gamma(h,v) set_cmd (h,(h)->cmd->set_gamma, v)
+#define set_color_correction(h,v) set_cmd (h,(h)->cmd->set_color_correction, v)
+#define set_lcount(h,v) set_cmd (h,(h)->cmd->set_lcount, v)
+#define set_bright(h,v) set_cmd (h,(h)->cmd->set_bright, v)
+#define mirror_image(h,v) set_cmd (h,(h)->cmd->mirror_image, v)
+#define set_speed(h,v) set_cmd (h,(h)->cmd->set_speed, v)
+#define set_outline_emphasis(h,v) set_cmd (h,(h)->cmd->set_outline_emphasis, v)
+#define control_auto_area_segmentation(h,v) set_cmd (h,(h)->cmd->control_auto_area_segmentation, v)
+#define set_film_type(h,v) set_cmd (h,(h)->cmd->set_film_type, v)
+#define set_exposure_time(h,v) set_cmd (h,(h)->cmd->set_exposure_time, v)
+#define set_bay(h,v) set_cmd (h,(h)->cmd->set_bay, v)
+#define set_threshold(h,v) set_cmd (h,(h)->cmd->set_threshold, v)
+
+
+static SANE_Status
+set_zoom (device *hw, int x_zoom, int y_zoom)
+{
+ SANE_Status status;
+ u_char cmd[2];
+ u_char params[2];
+
+ if (!hw->cmd->set_zoom)
+ return SANE_STATUS_GOOD;
+
+ log_call ();
+
+ cmd[0] = ESC;
+ cmd[1] = hw->cmd->set_zoom;
+
+ channel_send (hw->channel, cmd, 2, &status);
+ status = expect_ack (hw);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = x_zoom;
+ params[1] = y_zoom;
+
+ channel_send (hw->channel, params, 2, &status);
+ status = expect_ack (hw);
+
+ return status;
+}
+
+
+static SANE_Status
+set_resolution (device *hw, int xres, int yres)
+{
+ SANE_Status status;
+ u_char params[4];
+
+ if (!hw->cmd->set_resolution)
+ return SANE_STATUS_GOOD;
+
+ log_call ();
+
+ params[0] = ESC;
+ params[1] = hw->cmd->set_resolution;
+
+ channel_send (hw->channel, params, 2, &status);
+ status = expect_ack (hw);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = xres;
+ params[1] = xres >> 8;
+ params[2] = yres;
+ params[3] = yres >> 8;
+
+ channel_send (hw->channel, params, 4, &status);
+ status = expect_ack (hw);
+
+ return status;
+}
+
+/* set_scan_area()
+ *
+ * Sends the "set scan area" command to the scanner with the currently selected
+ * scan area. This scan area is already corrected for "color shuffling" if
+ * necessary.
+ */
+static SANE_Status
+set_scan_area (device *hw, int x, int y, int width, int height)
+{
+ SANE_Status status;
+ u_char params[8];
+
+ log_call ("(%d, %d, %d, %d)", x, y, width, height);
+
+ if (!hw->cmd->set_scan_area)
+ {
+ err_major ("set_scan_area not supported");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* verify the scan area */
+ if (x < 0 || y < 0 || width <= 0 || height <= 0)
+ return SANE_STATUS_INVAL;
+
+ params[0] = ESC;
+ params[1] = hw->cmd->set_scan_area;
+
+ channel_send (hw->channel, params, 2, &status);
+ status = expect_ack (hw);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = x;
+ params[1] = x >> 8;
+ params[2] = y;
+ params[3] = y >> 8;
+ params[4] = width;
+ params[5] = width >> 8;
+ params[6] = height;
+ params[7] = height >> 8;
+
+ channel_send (hw->channel, params, 8, &status);
+ status = expect_ack (hw);
+
+ return status;
+}
+
+/* set_color_correction_coefficients()
+ *
+ * Sends the "set color correction coefficients" command with the currently selected
+ * parameters to the scanner.
+ */
+
+static SANE_Status
+set_color_correction_coefficients (device *hw, Epson_Scanner *s)
+{
+ SANE_Status status;
+ u_char cmd = hw->cmd->set_color_correction_coefficients;
+ u_char params[2];
+ const int length = 9;
+ unsigned char cccoeff[9];
+
+ log_call ();
+
+ s->cct[0] = SANE_UNFIX (s->val[OPT_CCT_1].w);
+ s->cct[1] = SANE_UNFIX (s->val[OPT_CCT_2].w);
+ s->cct[2] = SANE_UNFIX (s->val[OPT_CCT_3].w);
+ s->cct[3] = SANE_UNFIX (s->val[OPT_CCT_4].w);
+ s->cct[4] = SANE_UNFIX (s->val[OPT_CCT_5].w);
+ s->cct[5] = SANE_UNFIX (s->val[OPT_CCT_6].w);
+ s->cct[6] = SANE_UNFIX (s->val[OPT_CCT_7].w);
+ s->cct[7] = SANE_UNFIX (s->val[OPT_CCT_8].w);
+ s->cct[8] = SANE_UNFIX (s->val[OPT_CCT_9].w);
+
+ if (!cmd) /* effect will be emulated */
+ return SANE_STATUS_GOOD;
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+ channel_send (hw->channel, params, 2, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (hw)))
+ return status;
+
+ get_colorcoeff_from_profile (s->cct, cccoeff);
+
+ log_data ("[ %d %d %d ][ %d %d %d ][ %d %d %d]",
+ cccoeff[0], cccoeff[1], cccoeff[2],
+ cccoeff[3], cccoeff[4], cccoeff[5],
+ cccoeff[6], cccoeff[7], cccoeff[8]);
+
+ channel_send (hw->channel, cccoeff, length, &status);
+ status = expect_ack (hw);
+
+ log_call ("exit");
+
+ return status;
+}
+
+
+static SANE_Status
+set_gamma_table (device *hw, const Epson_Scanner *s)
+{
+ SANE_Status status;
+ u_char cmd = hw->cmd->set_gamma_table;
+ u_char params[2];
+ const int length = 257;
+ u_char gamma[257];
+ int n;
+ int table;
+ static char gamma_cmds[] = { 'R', 'G', 'B' };
+
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ log_call ();
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+/* When handling inverted images, we must also invert the user
+ * supplied gamma function. This is *not* just 255-gamma -
+ * this gives a negative image.
+ */
+
+ if (strcmp_c ("GT-6600", hw->fw_name) == 0 ||
+ strcmp_c ("Perfection 610", hw->fw_name) == 0)
+ {
+ gamma_cmds[0] = 'R';
+ gamma_cmds[1] = 'B';
+ gamma_cmds[2] = 'G';
+ }
+
+ for (table = 0; table < 3; table++)
+ {
+ gamma[0] = gamma_cmds[table];
+ if (s->invert_image)
+ {
+ for (n = 0; n < 256; ++n)
+ {
+ gamma[n + 1] = 255 - s->gamma_table[table][255 - n];
+ }
+ }
+ else
+ {
+ for (n = 0; n < 256; ++n)
+ {
+ gamma[n + 1] = s->gamma_table[table][n];
+ }
+ }
+
+ channel_send (hw->channel, params, 2, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (hw)))
+ return status;
+
+ channel_send (hw->channel, gamma, length, &status);
+ if (SANE_STATUS_GOOD != (status = expect_ack (hw)))
+ return status;
+ }
+
+ log_call ("exit");
+
+ return status;
+}
+
+static SANE_Status
+check_warmup (device *hw)
+{
+ SANE_Status status = check_ext_status (hw);
+
+ log_call ();
+
+ if (status == SANE_STATUS_DEVICE_BUSY)
+ {
+ int timeout;
+
+ for (timeout = 0; timeout < 60; timeout++)
+ {
+ status = check_ext_status (hw);
+
+ if (status == SANE_STATUS_DEVICE_BUSY)
+ sleep (1);
+ else
+ {
+ return status;
+ }
+ }
+ }
+ else
+ return status;
+
+ return status;
+}
+
+
+static SANE_Status
+check_ext_status (device *hw)
+{
+ SANE_Status status;
+
+ log_call ();
+ require (hw);
+
+ status = dev_request_extended_status (hw);
+
+ if (EXT_STATUS_WU & hw->ext_status)
+ {
+ log_info ("option: warming up");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (EXT_STATUS_FER & hw->ext_status)
+ {
+ log_info ("option: fatal error");
+ status = SANE_STATUS_INVAL;
+ }
+
+ if (hw->adf)
+ {
+ if (ADF_STATUS_ERR & hw->adf->status
+ || ADF_EXT_STATUS_ERR & hw->adf->ext_status)
+ {
+ log_info ("ADF: other error");
+ status = SANE_STATUS_INVAL;
+ }
+
+ if (ADF_STATUS_PE & hw->adf->status)
+ {
+ log_info ("ADF: no paper");
+ status = SANE_STATUS_NO_DOCS;
+ }
+
+ if (ADF_STATUS_PJ & hw->adf->status)
+ {
+ log_info ("ADF: paper jam");
+ status = SANE_STATUS_JAMMED;
+ }
+
+ if (ADF_STATUS_OPN & hw->adf->status)
+ {
+ log_info ("ADF: cover open");
+ status = SANE_STATUS_COVER_OPEN;
+ }
+
+ if (ADF_EXT_STATUS_DFE & hw->adf->ext_status)
+ {
+ log_info ("ADF: multi sheet feed");
+ status = SANE_STATUS_JAMMED;
+ }
+
+ if (ADF_EXT_STATUS_TR_OPN & hw->adf->ext_status)
+ {
+ log_info ("ADF: tray open");
+ status = SANE_STATUS_COVER_OPEN;
+ }
+ }
+
+ if (hw->tpu)
+ {
+ if (TPU_STATUS_ERR & hw->tpu->status)
+ {
+ log_info ("TPU: other error");
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (hw->fbf)
+ {
+ /* ??? is this for MFDs? */
+ if (DV3_STATUS_OPN & hw->fbf->status)
+ {
+ log_info ("UNIT: Scanner Unit open");
+ status = SANE_STATUS_COVER_OPEN;
+ }
+ }
+
+ return status;
+}
+
+
+static SANE_Status
+initialize (device *hw)
+{
+ SANE_Status status;
+ u_char param[2];
+
+ log_call ();
+
+ if (!hw->cmd->initialize_scanner)
+ return SANE_STATUS_GOOD;
+
+ param[0] = ESC;
+ param[1] = hw->cmd->initialize_scanner;
+
+ channel_send (hw->channel, param, 2, &status);
+ status = expect_ack (hw);
+
+ return status;
+}
+
+
+static Epson_Scanner *first_handle = NULL;
+
+
+static EpsonHdr
+command (device *hw, const u_char * cmd, size_t cmd_size,
+ SANE_Status * status)
+{
+ EpsonHdr head;
+ u_char *buf;
+ int count;
+
+ log_call ();
+
+ if (NULL == (head = t_malloc (1,EpsonHdrRec)))
+ {
+ err_fatal ("%s", strerror (errno));
+ *status = SANE_STATUS_NO_MEM;
+ return (EpsonHdr) 0;
+ }
+
+ channel_send (hw->channel, cmd, cmd_size, status);
+
+ if (SANE_STATUS_GOOD != *status)
+ {
+ /* this is necessary for the GT-8000. I don't know why, but
+ it seems to fix the problem. It should not have any
+ ill effects on other scanners. */
+ *status = SANE_STATUS_GOOD;
+ channel_send (hw->channel, cmd, cmd_size, status);
+ if (SANE_STATUS_GOOD != *status)
+ return (EpsonHdr) 0;
+ }
+
+ buf = (u_char *) head;
+ buf += channel_recv (hw->channel, buf, 4, status);
+
+ if (SANE_STATUS_GOOD != *status)
+ {
+ delete (head);
+ return (EpsonHdr) 0;
+ }
+
+ switch (head->code)
+ {
+
+ case NAK:
+ /* fall through */
+ /* !!! is this really sufficient to report an error ? */
+ case ACK:
+ break; /* no need to read any more data after ACK or NAK */
+
+ case STX:
+ hw->status = head->status;
+
+ if (SANE_STATUS_GOOD != *status)
+ {
+ delete (head);
+ return (EpsonHdr) 0;
+ }
+
+ count = head->count2 * 255 + head->count1;
+ log_info ("need to read %d data bytes", count);
+
+ /* Grow head so it has enough space to hold an additional count
+ * bytes of payload. We can _not_ use t_realloc here.
+ */
+ if (NULL == (head = realloc (head, sizeof (EpsonHdrRec) + count)))
+ {
+ err_fatal ("%s", strerror (errno));
+ *status = SANE_STATUS_NO_MEM;
+ return (EpsonHdr) 0;
+ }
+
+ buf = head->buf;
+ channel_recv (hw->channel, buf, count, status);
+
+ if (SANE_STATUS_GOOD != *status)
+ {
+ delete (head);
+ return (EpsonHdr) 0;
+ }
+
+ break;
+
+ default:
+ if (0 == head->code)
+ { err_major ("Incompatible printer port (probably bi/directional)"); }
+ else if (cmd[cmd_size - 1] == head->code)
+ { err_major ("Incompatible printer port (probably not bi/directional)"); }
+
+ err_major ("Illegal response of scanner for command: %02x", head->code);
+ break;
+ }
+
+ return head;
+}
+
+
+static SANE_Status
+create_epson_device (device **devp, channel* ch)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ device *dev = NULL;
+
+ require (devp && ch);
+
+ dev = t_calloc (1, device);
+ if (!dev)
+ {
+ err_fatal ("%s", strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->channel = ch;
+
+ init_resolution_info (&dev->res , NULL);
+ init_resolution_info (&dev->res_x, NULL);
+ init_resolution_info (&dev->res_y, NULL);
+
+ dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT];
+
+ dev->fw_name = get_fw_name (dev->channel);
+
+ {
+ void *cfg = cfg_init (NULL, NULL);
+ dev->using_fs = !cfg_has_value (cfg, CFG_KEY_FS_BLACKLIST, dev->fw_name);
+ }
+
+ if (!dev->using_fs && CHAN_NET == ch->type)
+ {
+ err_fatal ("Network channel does not support scanning via ESC commands");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ {
+ size_t protocol_max = (dev->using_fs ? UINT32_MAX : UINT16_MAX);
+
+ ch->set_max_request_size (ch, min (protocol_max, 512 * 1024));
+ }
+
+ initialize (dev);
+
+ if (dev->cmd->request_identity != 0)
+ {
+ status = get_identity_information (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+
+ if (dev->cmd->request_identity2 != 0)
+ {
+ get_hardware_property (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+
+ /* Check for the max. supported color depth and assign the values to
+ * the bitDepthList. Note that bitDepthList is a SANE word list and
+ * therefore has an initial element that indicates the number of its
+ * elements. We store up to 2 bit depths (8 and only one of 16, 14,
+ * and 12).
+ */
+
+ bitDepthList = t_malloc (1 + 2, SANE_Word);
+ if (bitDepthList == NULL)
+ {
+ err_fatal ("%s", strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ bitDepthList[0] = 1; /* we start with one element in the list */
+ bitDepthList[1] = 8; /* 8bit is the default */
+
+ if (set_data_format (dev, 16) == SANE_STATUS_GOOD)
+ {
+ dev->maxDepth = 16;
+
+ bitDepthList[0]++;
+ bitDepthList[bitDepthList[0]] = 16;
+ }
+ else if (set_data_format (dev, 14) == SANE_STATUS_GOOD)
+ {
+ dev->maxDepth = 14;
+
+ bitDepthList[0]++;
+ bitDepthList[bitDepthList[0]] = 14;
+ }
+ else if (set_data_format (dev, 12) == SANE_STATUS_GOOD)
+ {
+ dev->maxDepth = 12;
+
+ bitDepthList[0]++;
+ bitDepthList[bitDepthList[0]] = 12;
+ }
+ else
+ {
+ dev->maxDepth = 8;
+ /* the default depth is already in the list */
+ }
+
+ log_info ("maximum color depth = %d", dev->maxDepth);
+
+ status = dev_request_extended_status (dev);
+ if (SANE_STATUS_GOOD != status)
+ {
+ if (SANE_STATUS_UNSUPPORTED != status)
+ {
+ err_minor ("failure processing extended status request");
+ }
+ }
+ else
+ {
+ const void *info = model_info_cache_get_info (dev->fw_name, &status);
+ if (SANE_STATUS_GOOD == status && info)
+ {
+ dev->scan_hard = model_info_get_profile (info);
+ model_info_customise_commands (info, dev->cmd);
+ dev->uses_locking = model_info_has_lock_commands (info);
+ }
+ else
+ {
+ err_minor ("failure getting model info (%s)", sane_strstatus (status));
+ }
+ }
+
+ if (0 == strcmp_c ("GT-8200", dev->fw_name))
+ {
+ /* Version 1.08 of the firmware is said to only report half of the
+ vertical size of the scan area. We should double that if it is
+ smaller than the horizontal scan area. Although we already try
+ doing this by fixing up the extended status reply, that doesn't
+ do the whole trick because we are dealing with a device type 0
+ scanner. In that case we get the FBF scan area via the 'ESC I'
+ command which does not include model info so we can not safely
+ fix up that reply.
+ */
+ if (dev->fbf && (dev->fbf->max_y < dev->fbf->max_x))
+ {
+ err_minor ("Fixing up buggy FBF max scan dimensions.");
+ dev->fbf->max_y *= 2;
+ update_ranges (dev, dev->fbf);
+ dev->need_color_reorder = SANE_TRUE;
+ }
+ }
+
+ if (dev->fbf)
+ {
+ log_info (" FBF: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]",
+ SANE_UNFIX (dev->fbf->x_range.min),
+ SANE_UNFIX (dev->fbf->y_range.min),
+ SANE_UNFIX (dev->fbf->x_range.max),
+ SANE_UNFIX (dev->fbf->y_range.max));
+
+ // check for auto size detection support and cache the results
+ dev->fbf->has_size_check = ((0 != dev->fbf->doc_x)
+ && (0 != dev->fbf->doc_y));
+ }
+ if (dev->adf)
+ {
+ log_info (" ADF: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]",
+ SANE_UNFIX (dev->adf->x_range.min),
+ SANE_UNFIX (dev->adf->y_range.min),
+ SANE_UNFIX (dev->adf->x_range.max),
+ SANE_UNFIX (dev->adf->y_range.max));
+ log_info (" ADF: %s, %s feed type",
+ (EXT_STATUS_ADFS & dev->ext_status ? "duplex" : "simplex"),
+ (EXT_STATUS_ADFT & dev->ext_status ? "page" : "sheet"));
+
+ // check for auto size detection support and cache the results
+ dev->adf->has_size_check = ((0 != dev->adf->doc_x)
+ && (0 != dev->adf->doc_y));
+
+ // handle the special case where some scanners claim to have auto
+ // document size detection support for flatbed but not for adf
+ if (dev->fbf && (dev->fbf->has_size_check && !dev->adf->has_size_check))
+ {
+ cmd_control_option_unit (dev, 0x01); // turn on ADF
+ dev_request_extended_status (dev); // now re-query scanner
+ // re-check for document size detection support
+ dev->adf->has_size_check = ((0 != dev->adf->doc_x)
+ && (0 != dev->adf->doc_y));
+ cmd_control_option_unit (dev, 0x00); // turn off ADF
+ }
+ }
+ if (dev->tpu)
+ {
+ log_info (" TPU: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]",
+ SANE_UNFIX (dev->tpu->x_range.min),
+ SANE_UNFIX (dev->tpu->y_range.min),
+ SANE_UNFIX (dev->tpu->x_range.max),
+ SANE_UNFIX (dev->tpu->y_range.max));
+
+ // check for auto size detection support and cache the results
+ dev->tpu->has_size_check = ((0 != dev->tpu->doc_x)
+ && (0 != dev->tpu->doc_y));
+ }
+
+
+ /* establish defaults */
+ dev->need_reset_on_source_change = SANE_FALSE;
+
+ if (strcmp_c ("ES-9000H", dev->fw_name) == 0 ||
+ strcmp_c ("GT-30000", dev->fw_name) == 0)
+ {
+ dev->cmd->set_focus_position = 0;
+ dev->cmd->feed = 0x19;
+ }
+ else if (strcmp_c ("GT-8200", dev->fw_name) == 0 ||
+ strcmp_c ("Perfection1640", dev->fw_name) == 0 ||
+ strcmp_c ("GT-8700", dev->fw_name) == 0)
+ {
+ dev->cmd->feed = 0;
+ dev->cmd->set_focus_position = 0;
+ dev->need_reset_on_source_change = SANE_TRUE;
+ }
+
+ { /* set up supported scan sources */
+ int i = 0;
+ if (dev->fbf) dev->sources[i++] = FBF_STR;
+ if (dev->adf) dev->sources[i++] = ADF_STR;
+ if (dev->tpu) dev->sources[i++] = TPU_STR;
+ dev->sources[i] = NULL; /* terminator */
+ }
+
+ if (!dev->src) dev->src = (extension *) dev->fbf;
+ if (!dev->src) dev->src = (extension *) dev->adf;
+ if (!dev->src) dev->src = (extension *) dev->tpu;
+
+ require (dev->src);
+
+ if (!using (dev, fbf))
+ dev_set_option_unit (dev, false);
+
+ dev->polling_time = DEFAULT_POLLING_TIME;
+ if (push_button_needs_polling (dev)
+ || maintenance_is_supported (dev))
+ {
+ dev->polling_time = (250 * 1000);
+ }
+
+ *devp = dev;
+
+ return SANE_STATUS_GOOD;
+} /* create epson device */
+
+
+static SANE_Status
+create_epson_scanner (device *dev, Epson_Scanner **scanner)
+{
+ Epson_Scanner *s = t_calloc (1, Epson_Scanner);
+
+ if (!s)
+ {
+ err_fatal ("%s", strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->hw = dev;
+ s->src = &s->raw;
+
+ if (s->hw->cmd->request_identity2 != 0)
+ {
+ /* reset constraints because we are pointing to different lists now
+ FIXME: Only needs to be done the first time we (successfully)
+ send the ESC i command. This should be done when constructing
+ the device and is therefore done by the time we construct a
+ scanner. While the content and size of the lists may vary
+ depending on the selected option, the list nature is constant.
+ Hmm, we may actually be zapping the lists ...
+ */
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_X_RESOLUTION].constraint.word_list = s->hw->res_x.list;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->hw->res_y.list;
+ }
+
+ s->frame_count = 0;
+
+ promise (s->hw);
+ promise (s->src);
+
+ *scanner = s;
+ return SANE_STATUS_GOOD;
+}
+
+
+/*! \todo Release resources in error recovery.
+ */
+static SANE_Status
+create_sane_handle (Epson_Scanner **handle, const char *name, const void *dip)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ device *dev = NULL;
+ Epson_Scanner *s = NULL;
+ channel *ch = NULL;
+
+ dev = NULL;
+
+ ch = channel_create (name, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ ch->open (ch, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ status = create_epson_device (&dev, ch);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ status = create_epson_scanner (dev, &s);
+ if (!s || SANE_STATUS_GOOD != status)
+ return status;
+
+ s->dip = dip;
+ init_options (s);
+
+ *handle = s;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (Epson_Scanner * s)
+{
+ int i;
+ SANE_Bool dummy;
+ SANE_Bool reload;
+
+ log_call ();
+
+ 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[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Scan Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].w = 2; /* Color */
+
+ /* bit depth */
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitDepthList;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w = bitDepthList[1]; /* the first "real" element is the default */
+
+ if (bitDepthList[0] == 1) /* only one element in the list -> hide the option */
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+ /* halftone */
+ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
+ s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
+ s->opt[OPT_HALFTONE].desc = SANE_I18N ("Selects the halftone.");
+
+ s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE].size = max_string_size (halftone_list_7);
+ s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+
+ if (s->hw->level >= 7)
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7;
+ else if (s->hw->level >= 4)
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4;
+ else
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
+
+ s->val[OPT_HALFTONE].w = 1; /* Halftone A */
+
+ if (!s->hw->cmd->set_halftoning)
+ {
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* dropout */
+ s->opt[OPT_DROPOUT].name = "dropout";
+ s->opt[OPT_DROPOUT].title = SANE_I18N ("Dropout");
+ s->opt[OPT_DROPOUT].desc = SANE_I18N ("Selects the dropout.");
+
+ s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING;
+ s->opt[OPT_DROPOUT].size = max_string_size (dropout_list);
+ s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_DROPOUT].constraint.string_list = dropout_list;
+ s->val[OPT_DROPOUT].w = 0; /* None */
+
+ /* brightness algorithm selection */
+ s->opt[OPT_BRIGHTNESS_METHOD].name = "brightness-method";
+ s->opt[OPT_BRIGHTNESS_METHOD].title = SANE_I18N ("Brightness Method");
+ s->opt[OPT_BRIGHTNESS_METHOD].desc = SANE_I18N ("Selects a method to change the brightness of the acquired image.");
+ s->opt[OPT_BRIGHTNESS_METHOD].type = SANE_TYPE_STRING;
+ s->opt[OPT_BRIGHTNESS_METHOD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS_METHOD].size = max_string_size (brightness_method_list);
+ s->opt[OPT_BRIGHTNESS_METHOD].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_BRIGHTNESS_METHOD].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_BRIGHTNESS_METHOD].constraint.string_list = brightness_method_list; s->val[OPT_BRIGHTNESS_METHOD].w = 0; /* first supported */
+
+ if (!s->hw->cmd->set_bright) /* skip "hardware" */
+ ++s->opt[OPT_BRIGHTNESS_METHOD].constraint.string_list;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].size = sizeof (SANE_Int);
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->val[OPT_BRIGHTNESS].w = 0; /* neutral */
+
+ if (s->hw->cmd->set_bright)
+ {
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range;
+ }
+ else
+ {
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ }
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].size = sizeof (SANE_Int);
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &contrast_range;
+ s->val[OPT_CONTRAST].w = 0; /* neutral */
+
+ /* sharpness */
+ s->opt[OPT_SHARPNESS].name = "sharpness";
+ s->opt[OPT_SHARPNESS].title = SANE_I18N ("Sharpness");
+ s->opt[OPT_SHARPNESS].desc = "";
+
+ s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range;
+ s->val[OPT_SHARPNESS].w = 0; /* Normal */
+
+ if (!s->hw->cmd->set_outline_emphasis)
+ {
+ s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* gamma */
+ s->opt[OPT_GAMMA_CORRECTION].name = SANE_NAME_GAMMA_CORRECTION;
+ s->opt[OPT_GAMMA_CORRECTION].title = SANE_TITLE_GAMMA_CORRECTION;
+ s->opt[OPT_GAMMA_CORRECTION].desc = SANE_DESC_GAMMA_CORRECTION;
+
+ s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ /*
+ * special handling for D1 function level - at this time I'm not
+ * testing for D1, I'm just assuming that all D level scanners will
+ * behave the same way. This has to be confirmed with the next D-level
+ * scanner
+ */
+ if (s->hw->cmd->level[0] == 'D')
+ {
+ s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_d);
+ s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_d;
+ s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */
+ s->hw->gamma_user_defined = gamma_userdefined_d;
+ s->hw->gamma_type = gamma_params_d;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_ab);
+ s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_ab;
+ s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */
+ s->hw->gamma_user_defined = gamma_userdefined_ab;
+ s->hw->gamma_type = gamma_params_ab;
+ }
+
+ if (!s->hw->cmd->set_gamma)
+ {
+ s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0];
+
+ if (s->hw->cmd->set_gamma_table &&
+ s->hw->gamma_user_defined[s->val[OPT_GAMMA_CORRECTION].w])
+ {
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* initialize the Gamma tables */
+ memset (&s->gamma_table[0], 0, 256 * sizeof (SANE_Word));
+ memset (&s->gamma_table[1], 0, 256 * sizeof (SANE_Word));
+ memset (&s->gamma_table[2], 0, 256 * sizeof (SANE_Word));
+ for (i = 0; i < 256; i++)
+ {
+ s->gamma_table[0][i] = i;
+ s->gamma_table[1][i] = i;
+ s->gamma_table[2][i] = i;
+ }
+
+ /* color correction */
+ s->opt[OPT_COLOR_CORRECTION].name = "color-correction";
+ s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N ("Color correction");
+ s->opt[OPT_COLOR_CORRECTION].desc =
+ SANE_I18N
+ ("Sets the color correction table for the selected output device.");
+
+ s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING;
+ s->opt[OPT_COLOR_CORRECTION].size = 32;
+ s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_COLOR_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ if (s->hw->cmd->level[0] == 'D')
+ {
+ s->opt[OPT_COLOR_CORRECTION].size = max_string_size (color_list_d);
+ s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list_d;
+ s->val[OPT_COLOR_CORRECTION].w = 0; /* Default */
+ s->hw->color_user_defined = color_userdefined_d;
+ s->hw->color_type = color_params_d;
+ }
+ else
+ {
+ s->opt[OPT_COLOR_CORRECTION].size = max_string_size (color_list_ab);
+ s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list_ab;
+ s->val[OPT_COLOR_CORRECTION].w = 5; /* scanner default: CRT monitors */
+ s->hw->color_user_defined = color_userdefined_ab;
+ s->hw->color_type = color_params_ab;
+ }
+
+ if (!s->hw->cmd->set_color_correction)
+ {
+ s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+
+ if (s->hw->cmd->level[0] != 'D')
+ {
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ }
+ else
+ {
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->res.list;
+ }
+ handle_resolution (s, OPT_RESOLUTION, DEFAULT_RESOLUTION);
+
+#undef SANE_NAME_SCAN_X_RESOLUTION
+#define SANE_NAME_SCAN_X_RESOLUTION "x-resolution"
+
+ /* resolution in main scan direction */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+
+ if (s->hw->cmd->level[0] != 'D')
+ {
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ }
+ else
+ {
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_X_RESOLUTION].constraint.word_list = s->hw->res_x.list;
+ s->opt[OPT_X_RESOLUTION].cap |= SANE_CAP_ADVANCED;
+ }
+ handle_resolution (s, OPT_X_RESOLUTION, DEFAULT_X_RESOLUTION);
+
+ /* resolution in sub scan direction */
+ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+
+ if (s->hw->cmd->level[0] != 'D')
+ {
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &s->hw->dpi_range;
+ }
+ else
+ {
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->hw->res_y.list;
+ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_ADVANCED;
+ }
+ handle_resolution (s, OPT_Y_RESOLUTION, DEFAULT_Y_RESOLUTION);
+
+ switch (s->hw->productID)
+ {
+ /* We already use kludges for a number of models and don't want to
+ be burdened with rechecking their functionality. Really fixing
+ their support requires more changes than merely adding main/sub
+ resolution lists.
+ */
+ case 0x0116:
+ case 0x0118:
+ case 0x0119:
+ case 0x012b:
+ s->opt[OPT_X_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ break;
+ default:
+ /* use the default capability set at the top of this function */
+ ;
+ }
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
+ s->val[OPT_THRESHOLD].w = 0x80;
+
+ if (!s->hw->cmd->set_threshold)
+ {
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_CCT_GROUP].title = SANE_I18N ("Color correction coefficients");
+ s->opt[OPT_CCT_GROUP].desc = SANE_I18N ("Matrix multiplication of RGB");
+ s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* color correction coefficients */
+ s->opt[OPT_CCT_1].name = "cct-1";
+ s->opt[OPT_CCT_2].name = "cct-2";
+ s->opt[OPT_CCT_3].name = "cct-3";
+ s->opt[OPT_CCT_4].name = "cct-4";
+ s->opt[OPT_CCT_5].name = "cct-5";
+ s->opt[OPT_CCT_6].name = "cct-6";
+ s->opt[OPT_CCT_7].name = "cct-7";
+ s->opt[OPT_CCT_8].name = "cct-8";
+ s->opt[OPT_CCT_9].name = "cct-9";
+
+ s->opt[OPT_CCT_1].title = SANE_I18N ("Red");
+ s->opt[OPT_CCT_2].title = SANE_I18N ("Shift green to red");
+ s->opt[OPT_CCT_3].title = SANE_I18N ("Shift blue to red");
+ s->opt[OPT_CCT_4].title = SANE_I18N ("Shift red to green");
+ s->opt[OPT_CCT_5].title = SANE_I18N ("Green");
+ s->opt[OPT_CCT_6].title = SANE_I18N ("Shift blue to green");
+ s->opt[OPT_CCT_7].title = SANE_I18N ("Shift red to blue");
+ s->opt[OPT_CCT_8].title = SANE_I18N ("Shift green to blue");
+ s->opt[OPT_CCT_9].title = SANE_I18N ("Blue");
+
+ s->opt[OPT_CCT_1].desc = SANE_I18N ("Controls red level");
+ s->opt[OPT_CCT_2].desc = SANE_I18N ("Adds to red based on green level");
+ s->opt[OPT_CCT_3].desc = SANE_I18N ("Adds to red based on blue level");
+ s->opt[OPT_CCT_4].desc = SANE_I18N ("Adds to green based on red level");
+ s->opt[OPT_CCT_5].desc = SANE_I18N ("Controls green level");
+ s->opt[OPT_CCT_6].desc = SANE_I18N ("Adds to green based on blue level");
+ s->opt[OPT_CCT_7].desc = SANE_I18N ("Adds to blue based on red level");
+ s->opt[OPT_CCT_8].desc = SANE_I18N ("Adds to blue based on green level");
+ s->opt[OPT_CCT_9].desc = SANE_I18N ("Control blue level");
+
+ s->opt[OPT_CCT_1].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_2].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_3].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_4].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_5].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_6].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_7].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_8].type = SANE_TYPE_FIXED;
+ s->opt[OPT_CCT_9].type = SANE_TYPE_FIXED;
+
+ s->opt[OPT_CCT_1].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_2].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_3].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_4].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_5].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_6].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_7].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_8].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CCT_9].cap |= SANE_CAP_ADVANCED;
+
+ s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE;
+
+ if (!s->hw->cmd->set_color_correction_coefficients)
+ {
+ s->opt[OPT_CCT_1].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_2].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_3].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_4].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_5].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_6].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_7].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_8].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_CCT_9].cap |= SANE_CAP_EMULATED;
+ }
+
+ s->opt[OPT_CCT_1].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_2].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_3].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_4].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_5].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_6].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_7].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_8].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CCT_9].unit = SANE_UNIT_NONE;
+
+ s->opt[OPT_CCT_1].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_2].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_3].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_4].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_5].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_6].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_7].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_8].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CCT_9].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ s->hw->matrix_range.min = SANE_FIX (-2.0);
+ s->hw->matrix_range.max = SANE_FIX (2.0);
+ s->hw->matrix_range.quant = 0;
+
+ s->opt[OPT_CCT_1].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_2].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_3].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_4].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_5].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_6].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_7].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_8].constraint.range = &s->hw->matrix_range;
+ s->opt[OPT_CCT_9].constraint.range = &s->hw->matrix_range;
+
+ s->val[OPT_CCT_1].w = SANE_FIX (1.0);
+ s->val[OPT_CCT_2].w = 0;
+ s->val[OPT_CCT_3].w = 0;
+ s->val[OPT_CCT_4].w = 0;
+ s->val[OPT_CCT_5].w = SANE_FIX (1.0);
+ s->val[OPT_CCT_6].w = 0;
+ s->val[OPT_CCT_7].w = 0;
+ s->val[OPT_CCT_8].w = 0;
+ s->val[OPT_CCT_9].w = SANE_FIX (1.0);
+
+ /* "Advanced" group: */
+ s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N ("Advanced");
+ s->opt[OPT_ADVANCED_GROUP].desc = "";
+ s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* mirror */
+ s->opt[OPT_MIRROR].name = "mirror";
+ s->opt[OPT_MIRROR].title = SANE_I18N ("Mirror image");
+ s->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror the image.");
+
+ s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
+ s->val[OPT_MIRROR].w = SANE_FALSE;
+
+ if (!s->hw->cmd->mirror_image)
+ {
+ s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* speed */
+ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+
+ s->opt[OPT_SPEED].type = SANE_TYPE_BOOL;
+ s->val[OPT_SPEED].w = SANE_FALSE;
+
+ if (!s->hw->cmd->set_speed)
+ {
+ s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* preview speed */
+ s->opt[OPT_PREVIEW_SPEED].name = "preview-speed";
+ s->opt[OPT_PREVIEW_SPEED].title = SANE_I18N ("Speed");
+ s->opt[OPT_PREVIEW_SPEED].desc = "";
+
+ s->opt[OPT_PREVIEW_SPEED].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW_SPEED].w = SANE_FALSE;
+
+ if (!s->hw->cmd->set_speed)
+ {
+ s->opt[OPT_PREVIEW_SPEED].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* auto area segmentation */
+ s->opt[OPT_AAS].name = "auto-area-segmentation";
+ s->opt[OPT_AAS].title = SANE_I18N ("Auto area segmentation");
+ s->opt[OPT_AAS].desc = "";
+
+ s->opt[OPT_AAS].type = SANE_TYPE_BOOL;
+ s->val[OPT_AAS].w = SANE_TRUE;
+
+ if (!s->hw->cmd->control_auto_area_segmentation)
+ {
+ s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* limit resolution list */
+ s->opt[OPT_LIMIT_RESOLUTION].name = "short-resolution";
+ s->opt[OPT_LIMIT_RESOLUTION].title = SANE_I18N ("Short resolution list");
+ s->opt[OPT_LIMIT_RESOLUTION].desc =
+ SANE_I18N ("Display short resolution list");
+ s->opt[OPT_LIMIT_RESOLUTION].type = SANE_TYPE_BOOL;
+ s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE;
+
+ if (SANE_CONSTRAINT_WORD_LIST != s->opt[OPT_RESOLUTION].constraint_type)
+ {
+ s->opt[OPT_LIMIT_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* zoom */
+ s->opt[OPT_ZOOM].name = "zoom";
+ s->opt[OPT_ZOOM].title = SANE_I18N ("Zoom");
+ s->opt[OPT_ZOOM].desc =
+ SANE_I18N ("Defines the zoom factor the scanner will use");
+
+ s->opt[OPT_ZOOM].type = SANE_TYPE_INT;
+ s->opt[OPT_ZOOM].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ZOOM].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_ZOOM].constraint.range = &zoom_range;
+ s->val[OPT_ZOOM].w = 100;
+
+ if (s->hw->using_fs) s->hw->cmd->set_zoom = 0;
+
+ if (!s->hw->cmd->set_zoom)
+ {
+ s->opt[OPT_ZOOM].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* "Preview settings" group: */
+ s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW_GROUP].desc = "";
+ s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* preview */
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* media size oriented scan area setting */
+ s->opt[OPT_SCAN_AREA].name = "scan-area";
+ s->opt[OPT_SCAN_AREA].title = SANE_I18N ("Scan area");
+ s->opt[OPT_SCAN_AREA].desc =
+ SANE_I18N ("Select an area to scan based on well-known media sizes.");
+ s->opt[OPT_SCAN_AREA].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_AREA].size = 0;
+ s->opt[OPT_SCAN_AREA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_SCAN_AREA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_AREA].constraint.string_list = NULL;
+ s->val[OPT_SCAN_AREA].w = 0;
+
+ /* Quick format */
+ s->opt[OPT_QUICK_FORMAT].name = "quick-format";
+ s->opt[OPT_QUICK_FORMAT].title = SANE_I18N ("Quick format");
+ s->opt[OPT_QUICK_FORMAT].desc =
+ SANE_I18N ("Select an area to scan based on well-known media sizes. (DEPRECATED)");
+ s->opt[OPT_QUICK_FORMAT].type = SANE_TYPE_STRING;
+ s->opt[OPT_QUICK_FORMAT].size = 0;
+ s->opt[OPT_QUICK_FORMAT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_QUICK_FORMAT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_QUICK_FORMAT].constraint.string_list = NULL;
+ s->val[OPT_QUICK_FORMAT].w = 0;
+
+ handle_scan_area (s, 0); /* divines device/setting dependent bits */
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &(s->hw->src->x_range);
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &(s->hw->src->y_range);
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &(s->hw->src->x_range);
+ s->val[OPT_BR_X].w = s->hw->src->x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &(s->hw->src->y_range);
+ s->val[OPT_BR_Y].w = s->hw->src->y_range.max;
+
+ /* "Optional equipment" group: */
+ s->opt[OPT_EQU_GROUP].title = SANE_I18N ("Optional equipment");
+ s->opt[OPT_EQU_GROUP].desc = "";
+ s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED;
+
+ /* source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].size = max_string_size (s->hw->sources);
+
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].constraint.string_list = s->hw->sources;
+
+ if (!s->hw->sources[1]) /* two or more scan sources */
+ {
+ s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ }
+ s->val[OPT_SOURCE].w = 0;
+
+
+ /* film type */
+ s->opt[OPT_FILM_TYPE].name = "film-type";
+ s->opt[OPT_FILM_TYPE].title = SANE_I18N ("Film type");
+ s->opt[OPT_FILM_TYPE].desc = "";
+
+ s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_FILM_TYPE].size = max_string_size (film_list);
+
+ s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_FILM_TYPE].constraint.string_list = film_list;
+
+ s->val[OPT_FILM_TYPE].w = 0;
+
+ deactivateOption (s, OPT_FILM_TYPE, &dummy); /* default is inactive */
+
+ /* focus position */
+ s->opt[OPT_FOCUS].name = SANE_EPSON_FOCUS_NAME;
+ s->opt[OPT_FOCUS].title = SANE_EPSON_FOCUS_TITLE;
+ s->opt[OPT_FOCUS].desc = SANE_EPSON_FOCUS_DESC;
+ s->opt[OPT_FOCUS].type = SANE_TYPE_STRING;
+ s->opt[OPT_FOCUS].size = max_string_size (focus_list);
+ s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_FOCUS].constraint.string_list = focus_list;
+ s->val[OPT_FOCUS].w = 0;
+
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_ADVANCED;
+ if (s->hw->tpu && s->hw->tpu->has_focus)
+ {
+ s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* forward feed / eject */
+ s->opt[OPT_EJECT].name = "eject";
+ s->opt[OPT_EJECT].title = SANE_I18N ("Eject");
+ s->opt[OPT_EJECT].desc = SANE_I18N ("Eject the sheet in the ADF");
+
+ s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON;
+
+ if ((!s->hw->adf) && (!s->hw->cmd->set_bay))
+ { /* Hack: Using set_bay to indicate. */
+ s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* auto forward feed / eject */
+ s->opt[OPT_AUTO_EJECT].name = "auto-eject";
+ s->opt[OPT_AUTO_EJECT].title = SANE_I18N ("Auto eject");
+ s->opt[OPT_AUTO_EJECT].desc = SANE_I18N ("Eject document after scanning");
+
+ s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL;
+ s->val[OPT_AUTO_EJECT].w = SANE_TRUE;
+
+ if (s->hw->adf) s->hw->adf->auto_eject = SANE_TRUE;
+
+ if (!s->hw->adf)
+ {
+ s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_ADF_MODE].name = "adf-mode";
+ s->opt[OPT_ADF_MODE].title = SANE_I18N ("ADF Mode");
+ s->opt[OPT_ADF_MODE].desc =
+ SANE_I18N ("Selects the ADF mode (simplex/duplex)");
+ s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_ADF_MODE].size = max_string_size (adf_mode_list);
+ s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
+ s->val[OPT_ADF_MODE].w = 0; /* simplex */
+
+ if (!(using (s->hw, adf) && (EXT_STATUS_ADFS & s->hw->ext_status)))
+ {
+ s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* select bay */
+ s->opt[OPT_BAY].name = "bay";
+ s->opt[OPT_BAY].title = SANE_I18N ("Bay");
+ s->opt[OPT_BAY].desc = SANE_I18N ("Select bay to scan");
+
+ s->opt[OPT_BAY].type = SANE_TYPE_STRING;
+ s->opt[OPT_BAY].size = max_string_size (bay_list);
+ s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_BAY].constraint.string_list = bay_list;
+ s->val[OPT_BAY].w = 0; /* Bay 1 */
+
+ if (!s->hw->cmd->set_bay)
+ {
+ s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_WAIT_FOR_BUTTON].name = SANE_EPSON_WAIT_FOR_BUTTON_NAME;
+ s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_EPSON_WAIT_FOR_BUTTON_TITLE;
+ s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_EPSON_WAIT_FOR_BUTTON_DESC;
+
+ s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL;
+ s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL;
+ s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED;
+ s->val[OPT_WAIT_FOR_BUTTON].w = SANE_FALSE;
+
+ if (!s->hw->cmd->request_push_button_status
+ || push_button_is_black_listed (s->hw))
+ {
+ s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (push_button_needs_polling (s->hw))
+ {
+ s->val[OPT_WAIT_FOR_BUTTON].w = SANE_TRUE;
+ }
+
+ s->opt[OPT_MONITOR_BUTTON].name = SANE_EPSON_MONITOR_BUTTON_NAME;
+ s->opt[OPT_MONITOR_BUTTON].title = SANE_EPSON_MONITOR_BUTTON_TITLE;
+ s->opt[OPT_MONITOR_BUTTON].desc = SANE_EPSON_MONITOR_BUTTON_DESC;
+
+ s->opt[OPT_MONITOR_BUTTON].type = SANE_TYPE_BOOL;
+ s->opt[OPT_MONITOR_BUTTON].unit = SANE_UNIT_NONE;
+ s->opt[OPT_MONITOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_MONITOR_BUTTON].constraint.range = NULL;
+ s->opt[OPT_MONITOR_BUTTON].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_MONITOR_BUTTON].cap &= ~SANE_CAP_SOFT_SELECT;
+ s->val[OPT_MONITOR_BUTTON].w = SANE_FALSE;
+
+ if (!s->hw->cmd->request_push_button_status
+ || push_button_is_black_listed (s->hw))
+ {
+ s->opt[OPT_MONITOR_BUTTON].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_POLLING_TIME].name = SANE_EPSON_POLLING_TIME_NAME;
+ s->opt[OPT_POLLING_TIME].title = SANE_EPSON_POLLING_TIME_TITLE;
+ s->opt[OPT_POLLING_TIME].desc = SANE_EPSON_POLLING_TIME_DESC;
+
+ s->opt[OPT_POLLING_TIME].type = SANE_TYPE_INT;
+ s->opt[OPT_POLLING_TIME].unit = SANE_UNIT_MICROSECOND;
+ s->opt[OPT_POLLING_TIME].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_POLLING_TIME].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_POLLING_TIME].cap &= ~SANE_CAP_SOFT_SELECT;
+ s->val[OPT_POLLING_TIME].w = s->hw->polling_time;
+
+ if (!s->hw->cmd->request_push_button_status
+ || push_button_is_black_listed (s->hw))
+ {
+ s->opt[OPT_POLLING_TIME].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_NEEDS_POLLING].name = SANE_EPSON_NEEDS_POLLING_NAME;
+ s->opt[OPT_NEEDS_POLLING].title = SANE_EPSON_NEEDS_POLLING_TITLE;
+ s->opt[OPT_NEEDS_POLLING].desc = SANE_EPSON_NEEDS_POLLING_DESC;
+
+ s->opt[OPT_NEEDS_POLLING].type = SANE_TYPE_BOOL;
+ s->opt[OPT_NEEDS_POLLING].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NEEDS_POLLING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_NEEDS_POLLING].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_NEEDS_POLLING].cap &= ~SANE_CAP_SOFT_SELECT;
+ s->val[OPT_NEEDS_POLLING].w = SANE_FALSE;
+
+ if (!s->hw->cmd->request_push_button_status
+ || push_button_is_black_listed (s->hw))
+ {
+ s->opt[OPT_NEEDS_POLLING].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (push_button_needs_polling (s->hw))
+ {
+ s->val[OPT_NEEDS_POLLING].w = SANE_TRUE;
+ }
+
+ s->opt[OPT_DETECT_DOC_SIZE].name = SANE_EPSON_DETECT_DOC_SIZE_NAME;
+ s->opt[OPT_DETECT_DOC_SIZE].title = SANE_EPSON_DETECT_DOC_SIZE_TITLE;
+ s->opt[OPT_DETECT_DOC_SIZE].desc = SANE_EPSON_DETECT_DOC_SIZE_DESC;
+
+ s->opt[OPT_DETECT_DOC_SIZE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DETECT_DOC_SIZE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DETECT_DOC_SIZE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_DETECT_DOC_SIZE].constraint.range = NULL;
+ s->opt[OPT_DETECT_DOC_SIZE].cap |= SANE_CAP_ADVANCED;
+ s->val[OPT_DETECT_DOC_SIZE].w = SANE_FALSE;
+
+ if (!has_size_check_support (s->hw->src))
+ {
+ s->opt[OPT_DETECT_DOC_SIZE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_SCAN_AREA_IS_VALID].name = SANE_EPSON_SCAN_AREA_IS_VALID_NAME;
+ s->opt[OPT_SCAN_AREA_IS_VALID].title = SANE_EPSON_SCAN_AREA_IS_VALID_TITLE;
+ s->opt[OPT_SCAN_AREA_IS_VALID].desc = SANE_EPSON_SCAN_AREA_IS_VALID_DESC;
+
+ s->opt[OPT_SCAN_AREA_IS_VALID].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SCAN_AREA_IS_VALID].unit = SANE_UNIT_NONE;
+ s->opt[OPT_SCAN_AREA_IS_VALID].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_SCAN_AREA_IS_VALID].constraint.range = NULL;
+ s->opt[OPT_SCAN_AREA_IS_VALID].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->val[OPT_SCAN_AREA_IS_VALID].w = SANE_FALSE;
+
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].name =
+ SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_NAME;
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].title =
+ SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_TITLE;
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].desc =
+ SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_DESC;
+
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].type = SANE_TYPE_BOOL;
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].constraint_type =
+ SANE_CONSTRAINT_NONE;
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].constraint.range = NULL;
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].cap =
+ SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->val[OPT_ADF_DUPLEX_DIRECTION_MATCHES].w = SANE_FALSE;
+
+ if (!(using (s->hw, adf) && (EXT_STATUS_ADFS & s->hw->ext_status)))
+ {
+ s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_ADF_AUTO_SCAN].name = SANE_EPSON_ADF_AUTO_SCAN_NAME;
+ s->opt[OPT_ADF_AUTO_SCAN].title = SANE_EPSON_ADF_AUTO_SCAN_TITLE;
+ s->opt[OPT_ADF_AUTO_SCAN].desc = SANE_EPSON_ADF_AUTO_SCAN_DESC;
+
+ s->opt[OPT_ADF_AUTO_SCAN].type = SANE_TYPE_BOOL;
+ s->opt[OPT_ADF_AUTO_SCAN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ADF_AUTO_SCAN].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_ADF_AUTO_SCAN].constraint.range = NULL;
+ s->opt[OPT_ADF_AUTO_SCAN].cap |= SANE_CAP_ADVANCED;
+ s->val[OPT_ADF_AUTO_SCAN].w = SANE_FALSE;
+
+ if (!using (s->hw, adf) || !(FSI_CAP_ADFAS & s->hw->fsi_cap_2))
+ {
+ s->opt[OPT_ADF_AUTO_SCAN].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_ADF_DFD_SENSITIVITY].name = SANE_EPSON_ADF_DFD_SENSITIVITY_NAME;
+ s->opt[OPT_ADF_DFD_SENSITIVITY].title = SANE_EPSON_ADF_DFD_SENSITIVITY_TITLE;
+ s->opt[OPT_ADF_DFD_SENSITIVITY].desc = SANE_EPSON_ADF_DFD_SENSITIVITY_DESC;
+
+ s->opt[OPT_ADF_DFD_SENSITIVITY].type = SANE_TYPE_STRING;
+ s->opt[OPT_ADF_DFD_SENSITIVITY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_ADF_DFD_SENSITIVITY].size = max_string_size (dfd_sensitivity);
+ s->opt[OPT_ADF_DFD_SENSITIVITY].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_ADF_DFD_SENSITIVITY].constraint.string_list = dfd_sensitivity;
+ s->opt[OPT_ADF_DFD_SENSITIVITY].cap |= SANE_CAP_ADVANCED;
+ s->val[OPT_ADF_DFD_SENSITIVITY].w = 0;
+
+ if (!using (s->hw, adf) || !(FSI_CAP_DFD & s->hw->fsi_cap_2))
+ {
+ s->opt[OPT_ADF_DFD_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_EXT_SANE_STATUS].name = "ext-sane-status";
+ s->opt[OPT_EXT_SANE_STATUS].title = "Extended SANE Status";
+ s->opt[OPT_EXT_SANE_STATUS].desc = "Ugly kludge to provide additional status message strings to a frontend.";
+
+ s->opt[OPT_EXT_SANE_STATUS].type = SANE_TYPE_INT;
+ s->opt[OPT_EXT_SANE_STATUS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_EXT_SANE_STATUS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_EXT_SANE_STATUS].constraint.range = &ext_sane_status;
+ s->opt[OPT_EXT_SANE_STATUS].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+ s->val[OPT_EXT_SANE_STATUS].w = 0;
+
+ /* deskew */
+ s->opt[OPT_DESKEW].name = "deskew";
+ s->opt[OPT_DESKEW].title = SANE_I18N ("Deskew");
+ s->opt[OPT_DESKEW].desc = SANE_I18N ("Rotate image so it appears upright.");
+ s->opt[OPT_DESKEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DESKEW].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DESKEW].size = sizeof (SANE_Bool);
+ s->opt[OPT_DESKEW].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_DESKEW].cap |= SANE_CAP_ADVANCED;
+ if ( dip_has_deskew (s->dip, s->hw) )
+ {
+ s->opt[OPT_DESKEW].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_DESKEW].cap |= SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_DESKEW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DESKEW].w = SANE_FALSE;
+
+ /* auto-crop */
+ s->opt[OPT_AUTOCROP].name = "autocrop";
+ s->opt[OPT_AUTOCROP].title = SANE_I18N ("Trim image to paper size");
+ s->opt[OPT_AUTOCROP].desc = SANE_I18N ("Determines empty margins in the scanned image and removes them. This normally reduces the image to the size of the original document but may remove more.");
+ s->opt[OPT_AUTOCROP].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AUTOCROP].unit = SANE_UNIT_NONE;
+ s->opt[OPT_AUTOCROP].size = sizeof (SANE_Bool);
+ s->opt[OPT_AUTOCROP].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_AUTOCROP].cap |= SANE_CAP_ADVANCED;
+ if ( dip_has_autocrop (s->dip, s->hw) )
+ {
+ s->opt[OPT_AUTOCROP].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_AUTOCROP].cap |= SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_AUTOCROP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_AUTOCROP].w = SANE_FALSE;
+
+ s->opt[OPT_CALIBRATE].name = SANE_EPSON_CALIBRATE_NAME;
+ s->opt[OPT_CALIBRATE].title = SANE_EPSON_CALIBRATE_TITLE;
+ s->opt[OPT_CALIBRATE].desc = SANE_EPSON_CALIBRATE_DESC;
+ s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATE].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE;
+ if (!maintenance_is_supported (s->hw))
+ {
+ s->opt[OPT_CALIBRATE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ s->opt[OPT_CLEAN].name = SANE_EPSON_CLEAN_NAME;
+ s->opt[OPT_CLEAN].title = SANE_EPSON_CLEAN_TITLE;
+ s->opt[OPT_CLEAN].desc = SANE_EPSON_CLEAN_DESC;
+ s->opt[OPT_CLEAN].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_CLEAN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CLEAN].cap |= SANE_CAP_ADVANCED;
+ s->opt[OPT_CLEAN].constraint_type = SANE_CONSTRAINT_NONE;
+ if (!maintenance_is_supported (s->hw))
+ {
+ s->opt[OPT_CLEAN].cap |= SANE_CAP_INACTIVE;
+ }
+
+ handle_mode (s, s->val[OPT_MODE].w, &reload);
+ change_profile_matrix (s);
+
+ /* adjust default settings based on configuration options */
+ {
+ void *cfg = cfg_init (NULL, NULL);
+ if (cfg_has_value (cfg, CFG_KEY_OPTION, "prefer-adf"))
+ {
+ const char *found = NULL;
+ int i = 0;
+ while ((found = s->hw->sources[i])
+ && 0 != strcmp_c (ADF_STR, found))
+ {
+ ++i;
+ }
+ if (found) handle_source (s, i, ADF_STR);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+epkowa_open (const char *name, SANE_Handle *handle, const void *dip)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Epson_Scanner *s = NULL;
+
+ status = create_sane_handle (&s, name, dip);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ /* insert newly opened handle into list of open handles */
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = (SANE_Handle) s;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Epson_Scanner *s, *prev;
+ size_t i;
+
+ /* Test if there is still data pending from the scanner. If so, then
+ * do a cancel.
+ */
+
+ log_call ();
+
+ s = (Epson_Scanner *) handle;
+
+ /* remove handle from list of open handles */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+
+ if (!s)
+ {
+ err_fatal ("invalid handle (0x%p)", handle);
+ return;
+ }
+
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s->next;
+
+ s->hw = dev_dtor (s->hw);
+
+ const_delete (s->opt[OPT_BIT_DEPTH].constraint.word_list, SANE_Word *);
+ const_delete (s->opt[OPT_SCAN_AREA].constraint.string_list,
+ SANE_String_Const *);
+
+ /* image data acquisition related resources */
+ delete (s->raw.buf);
+ delete (s->img.buf);
+ for (i = 0; i < LINES_SHUFFLE_MAX; ++i)
+ delete (s->line_buffer[i]);
+
+ dip_destroy_LUT (s->dip, s->lut);
+
+ delete (s);
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ {
+ log_call ("(%d)", option);
+ return NULL;
+ }
+
+ log_call ("(%s)", s->opt[option].name);
+ return (s->opt + option);
+}
+
+
+static const SANE_String_Const *
+search_string_list (const SANE_String_Const * list, SANE_String value)
+{
+ log_call ("(%s)", value);
+
+ while (*list != NULL && strcmp_c (value, *list) != 0)
+ {
+ ++list;
+ }
+
+ return ((*list == NULL) ? NULL : list);
+}
+
+
+/* Activate, deactivate an option. Subroutines so we can add
+ debugging info if we want. The change flag is set to TRUE
+ if we changed an option. If we did not change an option,
+ then the value of the changed flag is not modified.
+ */
+static void
+activateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change)
+{
+ log_call ("(%s)", s->opt[option].name);
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ {
+ s->opt[option].cap &= ~SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+deactivateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change)
+{
+ log_call ("(%s)", s->opt[option].name);
+ if (SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ {
+ s->opt[option].cap |= SANE_CAP_INACTIVE;
+ *change = SANE_TRUE;
+ }
+}
+
+static void
+setOptionState (Epson_Scanner * s, SANE_Bool state,
+ SANE_Int option, SANE_Bool * change)
+{
+ if (state)
+ {
+ activateOption (s, option, change);
+ }
+ else
+ {
+ deactivateOption (s, option, change);
+ }
+}
+
+static void
+calculate_scan_area_offset (const Option_Value *v, int *left, int *top)
+{
+ *left =
+ SANE_UNFIX (v[OPT_TL_X].w) / MM_PER_INCH *
+ v[OPT_X_RESOLUTION].w * v[OPT_ZOOM].w / 100 + 0.5;
+
+ *top =
+ SANE_UNFIX (v[OPT_TL_Y].w) / MM_PER_INCH *
+ v[OPT_Y_RESOLUTION].w * v[OPT_ZOOM].w / 100 + 0.5;
+}
+
+static void
+calculate_scan_area_max (const Epson_Scanner *s, int *x, int *y)
+{
+ /* Cast first value to double to force the whole computation to be
+ done in double. Works around integer overflows.
+ */
+ *x = ((double) s->hw->src->max_x * s->val[OPT_X_RESOLUTION].w *
+ s->val[OPT_ZOOM].w / (s->hw->base_res * 100));
+ *y = ((double) s->hw->src->max_y * s->val[OPT_Y_RESOLUTION].w *
+ s->val[OPT_ZOOM].w / (s->hw->base_res * 100));
+}
+
+static bool
+scan_area_is_valid (Epson_Scanner *s)
+{
+ int left = 0;
+ int top = 0;
+ int max_x = 0;
+ int max_y = 0;
+
+ bool rv = true;
+
+ /* finalize parameters before we validate*/
+ estimate_parameters (s, NULL);
+
+ calculate_scan_area_max (s, &max_x, &max_y);
+ calculate_scan_area_offset (s->val, &left, &top);
+
+ if ( s->raw.ctx.pixels_per_line > max_x) rv = false;
+ if ((left + s->raw.ctx.pixels_per_line) > max_x) rv = false;
+
+ if (!need_autocrop_override (s))
+ {
+ if ( s->raw.ctx.lines > max_y ) rv = false;
+ if ((top + s->raw.ctx.lines) > max_y) rv = false;
+ }
+
+ /* check physical channel limitations */
+ {
+ size_t max_req = s->hw->channel->max_request_size (s->hw->channel);
+ if (s->raw.ctx.bytes_per_line > max_req) rv = false;
+ }
+
+ if (s->hw->using_fs)
+ {
+ if (s->raw.ctx.pixels_per_line > s->hw->scan_width_limit) rv = false;
+ return rv;
+ }
+
+ if (SANE_FRAME_RGB == s->raw.ctx.format) /* max x according to to spec */
+ if (s->raw.ctx.pixels_per_line > 21840) rv = false;
+ if (top > 65530) rv = false;
+ if (left > 65530) rv = false;
+
+ return rv;
+}
+
+static SANE_Status
+getvalue (Epson_Scanner *s, SANE_Int option, void *value)
+{
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ log_call ("(%s)", sopt->name);
+
+ switch (option)
+ {
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (value, sval->wa, sopt->size);
+ break;
+
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_MIRROR:
+ case OPT_SPEED:
+ case OPT_PREVIEW_SPEED:
+ case OPT_AAS:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_SHARPNESS:
+ case OPT_AUTO_EJECT:
+ case OPT_CCT_1:
+ case OPT_CCT_2:
+ case OPT_CCT_3:
+ case OPT_CCT_4:
+ case OPT_CCT_5:
+ case OPT_CCT_6:
+ case OPT_CCT_7:
+ case OPT_CCT_8:
+ case OPT_CCT_9:
+ case OPT_THRESHOLD:
+ case OPT_ZOOM:
+ case OPT_BIT_DEPTH:
+ case OPT_WAIT_FOR_BUTTON:
+ case OPT_DETECT_DOC_SIZE:
+ case OPT_LIMIT_RESOLUTION:
+ case OPT_ADF_AUTO_SCAN:
+ case OPT_DESKEW:
+ case OPT_AUTOCROP:
+ case OPT_NEEDS_POLLING:
+ *((SANE_Word *) value) = sval->w;
+ break;
+ case OPT_MODE:
+ case OPT_ADF_MODE:
+ case OPT_HALFTONE:
+ case OPT_DROPOUT:
+ case OPT_BRIGHTNESS_METHOD:
+ case OPT_SCAN_AREA:
+ case OPT_SOURCE:
+ case OPT_FILM_TYPE:
+ case OPT_GAMMA_CORRECTION:
+ case OPT_COLOR_CORRECTION:
+ case OPT_BAY:
+ case OPT_FOCUS:
+ case OPT_ADF_DFD_SENSITIVITY:
+ strcpy ((char *) value, sopt->constraint.string_list[sval->w]);
+ break;
+
+ case OPT_QUICK_FORMAT:
+ getvalue (s, OPT_SCAN_AREA, value);
+ break;
+
+ case OPT_EXT_SANE_STATUS:
+ if (using (s->hw, adf)
+ && (ADF_EXT_STATUS_DFE & s->hw->adf->ext_status))
+ sval->w = EXT_SANE_STATUS_MULTI_FEED;
+ if (using (s->hw, adf)
+ && (ADF_EXT_STATUS_TR_OPN & s->hw->adf->ext_status))
+ sval->w = EXT_SANE_STATUS_TRAY_CLOSED;
+ *((SANE_Word *) value) = sval->w;
+ sval->w = 0;
+ break;
+
+ case OPT_MONITOR_BUTTON:
+ if (SANE_OPTION_IS_ACTIVE (option))
+ {
+ SANE_Bool pressed;
+ SANE_Status status = SANE_STATUS_GOOD;
+ if (SANE_STATUS_GOOD == status)
+ {
+ status = get_push_button_status (s->hw, &pressed);
+ if (SANE_STATUS_GOOD == status)
+ {
+ *((SANE_Bool *) value) = pressed;
+ }
+ }
+ return status;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ break;
+ case OPT_POLLING_TIME:
+ *((SANE_Word *) value) = sval->w;
+ break;
+ case OPT_SCAN_AREA_IS_VALID:
+ {
+ sval->w = scan_area_is_valid (s);
+ *((SANE_Word *) value) = sval->w;
+ }
+ break;
+ case OPT_ADF_DUPLEX_DIRECTION_MATCHES:
+ {
+ sval->w = adf_duplex_direction_matches (s->hw);
+ *((SANE_Word *) value) = sval->w;
+ }
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static void
+handle_mode (Epson_Scanner * s, SANE_Int optindex, SANE_Bool * reload)
+{
+ SANE_Bool dropout, aas, halftone, threshold, cct;
+ SANE_Bool brightness, contrast;
+
+ log_call ();
+
+ *reload = SANE_FALSE;
+
+ switch (optindex)
+ {
+ case 0: /* b & w */
+ dropout = SANE_TRUE;
+ aas = SANE_TRUE;
+ halftone = SANE_TRUE;
+ threshold = SANE_TRUE;
+ cct = SANE_FALSE;
+ brightness = SANE_FALSE;
+ contrast = SANE_FALSE;
+ break;
+ case 1: /* gray */
+ dropout = SANE_TRUE;
+ aas = SANE_FALSE;
+ halftone = SANE_FALSE;
+ threshold = SANE_FALSE;
+ cct = SANE_FALSE;
+ brightness = SANE_TRUE;
+ contrast = SANE_TRUE;
+ break;
+ case 2: /* color */
+ dropout = SANE_FALSE;
+ aas = SANE_FALSE;
+ halftone = SANE_FALSE;
+ threshold = SANE_FALSE;
+ cct = SANE_TRUE;
+ brightness = SANE_TRUE;
+ contrast = SANE_TRUE;
+ break;
+ default:
+ return;
+ }
+
+ if (s->hw->cmd->level[0] == 'D')
+ {
+ dropout = SANE_FALSE;
+ aas = SANE_FALSE;
+ halftone = SANE_FALSE;
+ }
+
+ setOptionState (s, dropout, OPT_DROPOUT, reload);
+ s->val[OPT_DROPOUT].w = 0;
+ setOptionState (s, halftone, OPT_HALFTONE, reload);
+ s->val[OPT_HALFTONE].w = 0;
+ setOptionState (s, aas, OPT_AAS, reload);
+ s->val[OPT_AAS].w = SANE_FALSE;
+
+ setOptionState (s, threshold, OPT_THRESHOLD, reload);
+
+ setOptionState (s, brightness, OPT_BRIGHTNESS, reload);
+ setOptionState (s, contrast, OPT_CONTRAST, reload);
+
+ setOptionState (s, cct, OPT_CCT_1, reload);
+ setOptionState (s, cct, OPT_CCT_2, reload);
+ setOptionState (s, cct, OPT_CCT_3, reload);
+ setOptionState (s, cct, OPT_CCT_4, reload);
+ setOptionState (s, cct, OPT_CCT_5, reload);
+ setOptionState (s, cct, OPT_CCT_6, reload);
+ setOptionState (s, cct, OPT_CCT_7, reload);
+ setOptionState (s, cct, OPT_CCT_8, reload);
+ setOptionState (s, cct, OPT_CCT_9, reload);
+
+ /* if binary, then disable the bit depth selection */
+ if (optindex == 0)
+ {
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ if (bitDepthList[0] == 1)
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ else
+ {
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth;
+ }
+ }
+
+ if (optindex == 0) /* b & w */
+ handle_depth_halftone (s, 0, reload);
+
+ *reload = SANE_TRUE;
+}
+
+
+static void
+change_profile_matrix (Epson_Scanner * s)
+{
+ int index = 0;
+
+ log_call ();
+
+ require (s->hw->scan_hard);
+
+ if (using (s->hw, tpu)) /* TPU */
+ {
+ if (s->val[OPT_FILM_TYPE].w == 0) /* posi */
+ index = 3;
+ else
+ index = 1;
+ }
+ else /* Flatbed or ADF */
+ {
+ index = 0;
+ }
+
+ s->val[OPT_CCT_1].w = SANE_FIX (s->hw->scan_hard->color_profile[index][0]);
+ s->val[OPT_CCT_2].w = SANE_FIX (s->hw->scan_hard->color_profile[index][1]);
+ s->val[OPT_CCT_3].w = SANE_FIX (s->hw->scan_hard->color_profile[index][2]);
+ s->val[OPT_CCT_4].w = SANE_FIX (s->hw->scan_hard->color_profile[index][3]);
+ s->val[OPT_CCT_5].w = SANE_FIX (s->hw->scan_hard->color_profile[index][4]);
+ s->val[OPT_CCT_6].w = SANE_FIX (s->hw->scan_hard->color_profile[index][5]);
+ s->val[OPT_CCT_7].w = SANE_FIX (s->hw->scan_hard->color_profile[index][6]);
+ s->val[OPT_CCT_8].w = SANE_FIX (s->hw->scan_hard->color_profile[index][7]);
+ s->val[OPT_CCT_9].w = SANE_FIX (s->hw->scan_hard->color_profile[index][8]);
+}
+
+
+static unsigned char
+int2cpt (int val)
+{
+ if (val >= 0)
+ {
+ if (val > 127)
+ val = 127;
+ return (unsigned char) val;
+ }
+ else
+ {
+ val = -val;
+ if (val > 127)
+ val = 127;
+ return (unsigned char) (0x80 | val);
+ }
+}
+
+
+static void
+get_colorcoeff_from_profile (double *profile, unsigned char *color_coeff)
+{
+ int cc_idx[] = { 4, 1, 7, 3, 0, 6, 5, 2, 8 };
+ int color_table[9];
+ int i;
+
+ round_cct (profile, color_table);
+
+ for (i = 0; i < 9; i++)
+ color_coeff[i] = int2cpt (color_table[cc_idx[i]]);
+}
+
+
+static void
+round_cct (double org_cct[], int rnd_cct[])
+{
+ int i, j, index;
+ double mult_cct[9], frac[9];
+ int sum[3];
+ int loop;
+
+ for (i = 0; i < 9; i++)
+ mult_cct[i] = org_cct[i] * 32;
+
+ for (i = 0; i < 9; i++)
+ rnd_cct[i] = (int) floor (org_cct[i] * 32 + 0.5);
+
+ loop = 0;
+
+ do
+ {
+ for (i = 0; i < 3; i++)
+ {
+ if ((rnd_cct[i * 3] == 11) &&
+ (rnd_cct[i * 3] == rnd_cct[i * 3 + 1]) &&
+ (rnd_cct[i * 3] == rnd_cct[i * 3 + 2]))
+ {
+ rnd_cct[i * 3 + i]--;
+ mult_cct[i * 3 + i] = rnd_cct[i * 3 + i];
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ sum[i] = 0;
+ for (j = 0; j < 3; j++)
+ sum[i] += rnd_cct[i * 3 + j];
+ }
+
+ for (i = 0; i < 9; i++)
+ frac[i] = mult_cct[i] - rnd_cct[i];
+
+ for (i = 0; i < 3; i++)
+ {
+ if (sum[i] < 32)
+ {
+ index = get_roundup_index (&frac[i * 3], 3);
+ if (index != -1)
+ {
+ rnd_cct[i * 3 + index]++;
+ mult_cct[i * 3 + index] = rnd_cct[i * 3 + index];
+ sum[i]++;
+ }
+ }
+ else if (sum[i] > 32)
+ {
+ index = get_rounddown_index (&frac[i * 3], 3);
+ if (index != -1)
+ {
+ rnd_cct[i * 3 + index]--;
+ mult_cct[i * 3 + index] = rnd_cct[i * 3 + index];
+ sum[i]--;
+ }
+ }
+ }
+ }
+ while ((++loop < 2)
+ && ((sum[0] != 32) || (sum[1] != 32) || (sum[2] != 32)));
+}
+
+
+static int
+get_roundup_index (double frac[], int n)
+{
+ int i, index = -1;
+ double max_val = 0.0;
+
+ for (i = 0; i < n; i++)
+ {
+ if (frac[i] < 0)
+ continue;
+ if (max_val < frac[i])
+ {
+ index = i;
+ max_val = frac[i];
+ }
+ }
+
+ return index;
+}
+
+
+static int
+get_rounddown_index (double frac[], int n)
+{
+ int i, index = -1;
+ double min_val = 1.0;
+
+ for (i = 0; i < n; i++)
+ {
+ if (frac[i] > 0)
+ continue;
+ if (min_val > frac[i])
+ {
+ index = i;
+ min_val = frac[i];
+ }
+ }
+
+ return index;
+}
+
+
+/* This routine handles common options between OPT_MODE and
+ OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS
+ - auto area segmentation, and threshold. Apparently AAS
+ is some method to differentiate between text and photos.
+ Or something like that.
+
+ AAS is available when the scan color depth is 1 and the
+ halftone method is not TET.
+
+ Threshold is available when halftone is NONE, and depth is 1.
+*/
+static void
+handle_depth_halftone (Epson_Scanner * s, SANE_Int optindex,
+ SANE_Bool * reload)
+{
+ SANE_Bool threshold, aas, dropout;
+
+ log_call ();
+
+ *reload = SANE_FALSE;
+
+ switch (halftone_params[optindex])
+ {
+ case HALFTONE_NONE:
+ threshold = SANE_TRUE;
+ aas = SANE_TRUE;
+ dropout = SANE_TRUE;
+ break;
+ case HALFTONE_TET:
+ threshold = SANE_FALSE;
+ aas = SANE_FALSE;
+ dropout = SANE_FALSE;
+ break;
+ default:
+ threshold = SANE_FALSE;
+ aas = SANE_TRUE;
+ dropout = SANE_TRUE;
+ }
+
+ setOptionState (s, threshold, OPT_THRESHOLD, reload);
+ setOptionState (s, aas, OPT_AAS, reload);
+ setOptionState (s, dropout, OPT_DROPOUT, reload);
+
+ *reload = SANE_TRUE;
+}
+
+
+static void
+handle_resolution (Epson_Scanner * s, SANE_Int option, SANE_Word value)
+{
+ SANE_Int *last = NULL;
+
+ SANE_Int size = 0;
+ SANE_Word *list = NULL;
+
+ int f = 0;
+ int k = 0;
+ int n = 0;
+
+ log_call ("(%s, %d)", s->opt[option].name, value);
+
+ switch (option)
+ {
+ case OPT_RESOLUTION:
+ last = &s->hw->res.last;
+ size = s->hw->res.size;
+ list = s->hw->res.list;
+ break;
+ case OPT_X_RESOLUTION:
+ last = &s->hw->res_x.last;
+ size = s->hw->res_x.size;
+ list = s->hw->res_x.list;
+ break;
+ case OPT_Y_RESOLUTION:
+ last = &s->hw->res_y.last;
+ size = s->hw->res_y.size;
+ list = s->hw->res_y.list;
+ break;
+ default:
+ err_fatal ("%s", strerror (EINVAL));
+ exit (EXIT_FAILURE);
+ }
+
+ if (SANE_CONSTRAINT_RANGE == s->opt[option].constraint_type)
+ {
+ sanei_constrain_value (&(s->opt[option]), &value, NULL);
+ s->val[option].w = value;
+ }
+ else
+ {
+ SANE_Int best = list[size];
+ int min_d = INT_MAX;
+
+ /* find supported resolution closest to that requested */
+ for (n = 1; n <= size; n++)
+ {
+ int d = abs (value - list[n]);
+
+ if (d < min_d)
+ {
+ min_d = d;
+ k = n;
+ best = list[n];
+ }
+ }
+
+ /* FIXME? what's this trying to do? Use a resolution close to the
+ last one used if best is far away? Why??? */
+ if ((value != best) && *last)
+ {
+ for (f = 1; f <= size; f++)
+ if (*last == list[f])
+ break;
+
+ if (f != k && f != k - 1 && f != k + 1)
+ {
+ if (k > f)
+ best = list[f + 1];
+ else if (k < f)
+ best = list[f - 1];
+ }
+ }
+
+ *last = best;
+ s->val[option].w = (SANE_Word) best;
+ }
+
+ if (OPT_RESOLUTION == option)
+ {
+ s->val[OPT_X_RESOLUTION].w = s->val[option].w;
+ s->val[OPT_Y_RESOLUTION].w = s->val[option].w;
+
+ s->hw->res_x.last = s->hw->res.last;
+ s->hw->res_y.last = s->hw->res.last;
+ }
+ {
+ SANE_Bool dummy;
+ handle_deskew (s, NULL, &dummy);
+ }
+}
+
+static void
+limit_adf_res (Epson_Scanner * s)
+{
+ SANE_Constraint_Type type = s->opt[OPT_RESOLUTION].constraint_type;
+ int limit = large_res_kills_adf_scan (s->hw);
+
+ if (using (s->hw, adf))
+ {
+ dev_limit_res (s->hw, type, limit);
+
+ /* constrain the current values to the new limit */
+ handle_resolution (s, OPT_RESOLUTION, s->val[OPT_RESOLUTION].w);
+ handle_resolution (s, OPT_X_RESOLUTION, s->val[OPT_X_RESOLUTION].w);
+ handle_resolution (s, OPT_Y_RESOLUTION, s->val[OPT_Y_RESOLUTION].w);
+ }
+ else
+ {
+ dev_restore_res (s->hw, type);
+ }
+}
+
+
+/*
+ Handles setting the source (flatbed, transparency adapter (TPU),
+ or auto document feeder (ADF)).
+
+ For newer scanners it also sets the focus according to the
+ glass / TPU settings.
+*/
+static SANE_Status
+handle_source (Epson_Scanner * s, SANE_Int optindex, char *value)
+{
+ SANE_Bool dummy;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ("(%s)", value);
+
+ if (s->val[OPT_SOURCE].w == optindex)
+ return SANE_STATUS_GOOD;
+
+ if (s->hw->adf && strcmp_c (ADF_STR, value) == 0)
+ {
+ s->val[OPT_SOURCE].w = optindex;
+ s->hw->src = (const extension *) s->hw->adf;
+ deactivateOption (s, OPT_FILM_TYPE, &dummy);
+ s->val[OPT_FOCUS].w = 0;
+ if (EXT_STATUS_ADFS & s->hw->ext_status)
+ {
+ activateOption (s, OPT_ADF_MODE, &dummy);
+ activateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy);
+ }
+ else
+ {
+ deactivateOption (s, OPT_ADF_MODE, &dummy);
+ s->val[OPT_ADF_MODE].w = 0;
+ deactivateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy);
+ }
+ if (FSI_CAP_ADFAS & s->hw->fsi_cap_2)
+ {
+ activateOption (s, OPT_ADF_AUTO_SCAN, &dummy);
+ }
+ if (FSI_CAP_DFD & s->hw->fsi_cap_2)
+ {
+ activateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy);
+ }
+ else
+ {
+ deactivateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy);
+ s->val[OPT_ADF_DFD_SENSITIVITY].w = 0;
+ }
+ }
+ else if (s->hw->tpu && strcmp_c (TPU_STR, value) == 0)
+ {
+ s->val[OPT_SOURCE].w = optindex;
+ s->hw->src = (const extension *) s->hw->tpu;
+ deactivateOption (s, OPT_ADF_MODE, &dummy);
+ deactivateOption (s, OPT_ADF_AUTO_SCAN, &dummy);
+ deactivateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy);
+ deactivateOption (s, OPT_EJECT, &dummy);
+ deactivateOption (s, OPT_AUTO_EJECT, &dummy);
+ deactivateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy);
+ }
+ else if (s->hw->fbf)
+ {
+ s->val[OPT_SOURCE].w = optindex;
+ s->hw->src = (const extension *) s->hw->fbf;
+ s->val[OPT_FOCUS].w = 0;
+ deactivateOption (s, OPT_ADF_MODE, &dummy);
+ deactivateOption (s, OPT_ADF_AUTO_SCAN, &dummy);
+ deactivateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy);
+ deactivateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy);
+ }
+ else
+ {
+ err_fatal ("internal inconsistency");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* reset the scanner when we are changing the source setting -
+ this is necessary for the Perfection 1650 */
+ if (s->hw->need_reset_on_source_change)
+ initialize (s->hw);
+
+ handle_detect_doc_size (s, NULL, &dummy);
+
+ /*change*/
+ status = handle_scan_area(s, s->val[OPT_ADF_MODE].w);
+
+ change_profile_matrix (s);
+
+ setOptionState (s, using (s->hw, tpu), OPT_FILM_TYPE, &dummy);
+ setOptionState (s, using (s->hw, adf), OPT_AUTO_EJECT, &dummy);
+ setOptionState (s, using (s->hw, adf), OPT_EJECT, &dummy);
+
+ if (s->hw->cmd->set_focus_position)
+ {
+ if (using (s->hw, tpu))
+ {
+ s->val[OPT_FOCUS].w = 1;
+ setOptionState (s, SANE_TRUE, OPT_FOCUS, &dummy);
+ }
+ else if (using (s->hw, adf))
+ {
+ s->val[OPT_FOCUS].w = 0;
+ setOptionState (s, SANE_FALSE, OPT_FOCUS, &dummy);
+ }
+ else
+ {
+ s->val[OPT_FOCUS].w = 0;
+ setOptionState (s, SANE_TRUE, OPT_FOCUS, &dummy);
+ }
+ }
+
+ status = get_resolution_constraints (s->hw, s);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ if (s->hw->adf)
+ {
+ if (large_res_kills_adf_scan (s->hw)) limit_adf_res (s);
+
+ if (zoom_kills_adf_scan (s->hw))
+ {
+ if (using (s->hw, adf))
+ {
+ s->val[OPT_ZOOM].w = 100;
+ deactivateOption (s, OPT_ZOOM, &dummy);
+ }
+ else
+ {
+ if (s->hw->cmd->set_zoom)
+ activateOption (s, OPT_ZOOM, &dummy);
+ }
+ }
+ }
+
+ return status;
+}
+
+
+static void
+handle_filmtype (Epson_Scanner * s, SANE_Int optindex, char *value)
+{
+ log_call ();
+
+ value = value;
+
+ if (!s->hw->tpu || s->val[OPT_FILM_TYPE].w == optindex)
+ return;
+
+ s->val[OPT_FILM_TYPE].w = optindex;
+
+ require (s->hw->src == (extension *) s->hw->tpu);
+
+ s->val[OPT_TL_X].w = 0;
+ s->val[OPT_TL_Y].w = 0;
+ s->val[OPT_BR_X].w = s->hw->src->x_range.max;
+ s->val[OPT_BR_Y].w = s->hw->src->y_range.max;
+
+ s->opt[OPT_TL_X].constraint.range = &(s->hw->src->x_range);
+ s->opt[OPT_TL_Y].constraint.range = &(s->hw->src->y_range);
+ s->opt[OPT_BR_X].constraint.range = &(s->hw->src->x_range);
+ s->opt[OPT_BR_Y].constraint.range = &(s->hw->src->y_range);
+
+ change_profile_matrix (s);
+}
+
+static void
+handle_autocrop (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload)
+{
+ *reload = SANE_FALSE;
+
+ if ( dip_has_autocrop (s->dip, s->hw)
+ && ( ! SANE_OPTION_IS_ACTIVE (s->opt[OPT_DESKEW].cap)
+ || ! s->val[OPT_DESKEW].b )
+ && !s->val[OPT_PREVIEW].b )
+ {
+ activateOption (s, OPT_AUTOCROP, reload);
+ if (value)
+ {
+ SANE_Bool dummy;
+ s->val[OPT_AUTOCROP].b = *value;
+ handle_deskew (s, NULL, &dummy);
+ *reload = SANE_TRUE;
+ }
+ }
+ else
+ {
+ deactivateOption (s, OPT_AUTOCROP, reload);
+ }
+}
+
+static void
+handle_deskew (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload)
+{
+ *reload = SANE_FALSE;
+
+ if ( dip_has_deskew (s->dip, s->hw)
+ && ( ! SANE_OPTION_IS_ACTIVE (s->opt[OPT_AUTOCROP].cap)
+ || ! s->val[OPT_AUTOCROP].b )
+ && ( ! SANE_OPTION_IS_ACTIVE (s->opt[OPT_BIT_DEPTH].cap)
+ || 8 == s->val[OPT_BIT_DEPTH].w)
+ && 600 >= s->val[OPT_RESOLUTION].w
+ && 600 >= s->val[OPT_X_RESOLUTION].w
+ && 600 >= s->val[OPT_Y_RESOLUTION].w
+ && !s->val[OPT_PREVIEW].b )
+ {
+ activateOption (s, OPT_DESKEW , reload);
+ if (value)
+ {
+ SANE_Bool dummy;
+ s->val[OPT_DESKEW].b = *value;
+ handle_autocrop (s, NULL, &dummy);
+ *reload = SANE_TRUE;
+ }
+ }
+ else
+ {
+ deactivateOption (s, OPT_DESKEW, reload);
+ }
+}
+
+static void
+handle_detect_doc_size (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload)
+{
+ *reload = SANE_FALSE;
+
+ if (has_size_check_support (s->hw->src)
+ && !s->val[OPT_PREVIEW].b)
+ {
+ activateOption (s, OPT_DETECT_DOC_SIZE, reload);
+ if (value)
+ {
+ s->val[OPT_DETECT_DOC_SIZE].b = *value;
+ *reload = SANE_TRUE;
+ }
+ }
+ else
+ {
+ deactivateOption (s, OPT_DETECT_DOC_SIZE, reload);
+ }
+}
+
+static void
+handle_preview (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload)
+{
+ SANE_Bool dummy;
+
+ if (value)
+ {
+ s->val[OPT_PREVIEW].b = *value;
+ handle_detect_doc_size (s, &s->val[OPT_DETECT_DOC_SIZE].b, &dummy);
+ handle_autocrop (s, &s->val[OPT_AUTOCROP].b, &dummy);
+ handle_deskew (s, &s->val[OPT_DESKEW].b, &dummy);
+ *reload = SANE_TRUE;
+ }
+}
+
+static SANE_Status
+setvalue (Epson_Scanner *s, SANE_Int option, void *value, SANE_Int * info)
+{
+ SANE_Option_Descriptor *sopt = &(s->opt[option]);
+ Option_Value *sval = &(s->val[option]);
+
+ SANE_Status status;
+ const SANE_String_Const *optval;
+ int optindex;
+ SANE_Bool reload = SANE_FALSE;
+
+ log_call ("(%s, value @%p)", sopt->name, value);
+
+ status = sanei_constrain_value (sopt, value, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ optval = NULL;
+ optindex = 0;
+
+ if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST)
+ {
+ optval = search_string_list (sopt->constraint.string_list,
+ (char *) value);
+
+ if (optval == NULL)
+ return SANE_STATUS_INVAL;
+ optindex = optval - sopt->constraint.string_list;
+ }
+
+ switch (option)
+ {
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (sval->wa, value, sopt->size); /* Word arrays */
+ break;
+
+ case OPT_CCT_1:
+ case OPT_CCT_2:
+ case OPT_CCT_3:
+ case OPT_CCT_4:
+ case OPT_CCT_5:
+ case OPT_CCT_6:
+ case OPT_CCT_7:
+ case OPT_CCT_8:
+ case OPT_CCT_9:
+ sval->w = *((SANE_Word *) value); /* Simple values */
+ break;
+
+ case OPT_FILM_TYPE:
+ handle_filmtype (s, optindex, (char *) value);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_DROPOUT:
+ case OPT_BAY:
+ case OPT_FOCUS:
+ sval->w = optindex; /* Simple lists */
+ break;
+
+ case OPT_EJECT:
+ dev_eject_paper (s->hw);
+ break;
+
+ case OPT_RESOLUTION:
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ handle_resolution (s, option, *((SANE_Word *) value));
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ sval->w = *((SANE_Word *) value);
+ log_info ("set = %f", SANE_UNFIX (sval->w));
+ if (NULL != info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+
+ case OPT_SOURCE:
+ status = handle_source (s, optindex, (char *) value);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_MODE:
+ {
+ if (sval->w == optindex)
+ break;
+
+ sval->w = optindex;
+
+ handle_mode (s, optindex, &reload);
+
+ break;
+ }
+
+ case OPT_ADF_MODE:
+ status = handle_scan_area(s, optindex);
+ reload = SANE_TRUE;
+ /* through intentionally */
+ case OPT_ADF_DFD_SENSITIVITY:
+ sval->w = optindex;
+ break;
+
+ case OPT_BIT_DEPTH:
+ {
+ SANE_Bool dummy;
+ sval->w = *((SANE_Word *) value);
+ mode_params[s->val[OPT_MODE].w].depth = sval->w;
+ handle_deskew (s, NULL, &dummy);
+ reload = SANE_TRUE;
+ break;
+ }
+ case OPT_HALFTONE:
+ if (sval->w == optindex)
+ break;
+ sval->w = optindex;
+ handle_depth_halftone (s, optindex, &reload);
+ break;
+
+ case OPT_BRIGHTNESS_METHOD:
+ {
+ const SANE_Range *r;
+ SANE_Bool f
+ = s->hw->gamma_user_defined[s->val[OPT_GAMMA_CORRECTION].w];
+
+ if (sval->w == optindex) break;
+
+ r = s->opt[OPT_BRIGHTNESS].constraint.range;
+
+ if (0 == strcmp_c (brightness_method_list[0], /* "hardware" */
+ sopt->constraint.string_list[optindex]))
+ {
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_EMULATED;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range;
+ setOptionState (s, !f, OPT_BRIGHTNESS, &reload);
+ }
+ else
+ {
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_EMULATED;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ setOptionState (s, SANE_TRUE, OPT_BRIGHTNESS, &reload);
+ }
+
+ if (r != s->opt[OPT_BRIGHTNESS].constraint.range)
+ {
+ double v = s->val[OPT_BRIGHTNESS].w;
+
+ /**/ if (0 < v)
+ {
+ require (0 < r->max);
+
+ v /= r->max;
+ r = s->opt[OPT_BRIGHTNESS].constraint.range;
+ v *= r->max;
+ v += 0.5;
+
+ reload = SANE_TRUE;
+ }
+ else if (0 > v)
+ {
+ require (0 > r->min);
+
+ v /= r->min;
+ r = s->opt[OPT_BRIGHTNESS].constraint.range;
+ v *= r->min;
+ v -= 0.5;
+
+ reload = SANE_TRUE;
+ }
+ else /* 0 == v */
+ {}
+
+ s->val[OPT_BRIGHTNESS].w = (SANE_Int) v;
+ }
+
+ sval->w = optindex;
+
+ break;
+ }
+
+ case OPT_COLOR_CORRECTION:
+ {
+ SANE_Bool f = s->hw->color_user_defined[optindex];
+
+ sval->w = optindex;
+ setOptionState (s, f, OPT_CCT_1, &reload);
+ setOptionState (s, f, OPT_CCT_2, &reload);
+ setOptionState (s, f, OPT_CCT_3, &reload);
+ setOptionState (s, f, OPT_CCT_4, &reload);
+ setOptionState (s, f, OPT_CCT_5, &reload);
+ setOptionState (s, f, OPT_CCT_6, &reload);
+ setOptionState (s, f, OPT_CCT_7, &reload);
+ setOptionState (s, f, OPT_CCT_8, &reload);
+ setOptionState (s, f, OPT_CCT_9, &reload);
+
+ break;
+ }
+
+ case OPT_GAMMA_CORRECTION:
+ {
+ SANE_Bool f = s->hw->gamma_user_defined[optindex];
+
+ sval->w = optindex;
+ setOptionState (s, f, OPT_GAMMA_VECTOR_R, &reload);
+ setOptionState (s, f, OPT_GAMMA_VECTOR_G, &reload);
+ setOptionState (s, f, OPT_GAMMA_VECTOR_B, &reload);
+
+ if (0 == strcmp_c (brightness_method_list[0], /* "hardware" */
+ s->opt[OPT_BRIGHTNESS_METHOD].constraint
+ .string_list[s->val[OPT_BRIGHTNESS_METHOD].w]))
+ {
+ setOptionState (s, !f, OPT_BRIGHTNESS, &reload);
+ }
+
+ break;
+ }
+
+ case OPT_AUTO_EJECT:
+ sval->w = *((SANE_Word *) value);
+ if (s->hw && s->hw->adf) s->hw->adf->auto_eject = sval->w;
+ break;
+ case OPT_MIRROR:
+ case OPT_SPEED:
+ case OPT_PREVIEW_SPEED:
+ case OPT_AAS:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_SHARPNESS:
+ case OPT_THRESHOLD:
+ case OPT_ZOOM:
+ case OPT_WAIT_FOR_BUTTON:
+ case OPT_ADF_AUTO_SCAN:
+ sval->w = *((SANE_Word *) value);
+ break;
+
+ case OPT_DETECT_DOC_SIZE:
+ handle_detect_doc_size (s, (SANE_Word *) value, &reload);
+ break;
+
+ case OPT_PREVIEW:
+ handle_preview (s, (SANE_Word *) value, &reload);
+ break;
+
+ case OPT_DESKEW:
+ handle_deskew (s, (SANE_Word *) value, &reload);
+ break;
+
+ case OPT_AUTOCROP:
+ handle_autocrop (s, (SANE_Word *) value, &reload);
+ break;
+
+ case OPT_LIMIT_RESOLUTION:
+ sval->w = *((SANE_Word *) value);
+ filter_resolution_list (s);
+ reload = SANE_TRUE;
+ break;
+
+ case OPT_SCAN_AREA:
+ {
+ sval->w = optindex;
+
+ /**/ if (0 == strcmp_c (sopt->constraint.string_list[sval->w],
+ media_maximum))
+ {
+ s->val[OPT_TL_X].w = SANE_FIX (0.0);
+ s->val[OPT_TL_Y].w = SANE_FIX (0.0);
+ s->val[OPT_BR_X].w = s->hw->src->x_range.max;
+ s->val[OPT_BR_Y].w = s->hw->src->y_range.max;
+ }
+ else if (0 == strcmp_c (sopt->constraint.string_list[sval->w],
+ media_automatic))
+ {
+ SANE_Bool yes = SANE_TRUE;
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_DETECT_DOC_SIZE].cap))
+ {
+ setvalue (s, OPT_DETECT_DOC_SIZE, &yes, NULL);
+ }
+ }
+ else
+ {
+ size_t i = 0;
+
+ while (i < num_of (media_list)
+ && 0 != strcmp_c (sopt->constraint.string_list[sval->w],
+ media_list[i].name))
+ ++i;
+
+ require (i < num_of (media_list));
+
+ s->val[OPT_TL_X].w = SANE_FIX (0.0);
+ s->val[OPT_TL_Y].w = SANE_FIX (0.0);
+ s->val[OPT_BR_X].w = SANE_FIX (media_list[i].width);
+ s->val[OPT_BR_Y].w = SANE_FIX (media_list[i].height);
+
+ adf_handle_adjust_alignment (s, SANE_FALSE);
+ }
+
+ reload = SANE_TRUE;
+ break;
+ }
+ case OPT_QUICK_FORMAT:
+ setvalue (s, OPT_SCAN_AREA, value, info);
+ sval->w = s->val[OPT_SCAN_AREA].w;
+ break;
+
+ case OPT_CALIBRATE:
+ status = dev_calibrate (s->hw);
+ break;
+
+ case OPT_CLEAN:
+ status = dev_clean (s->hw);
+ break;
+
+ case OPT_MONITOR_BUTTON:
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ if (reload && info != NULL)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+
+ return status;
+}
+
+
+SANE_Status
+sane_control_option (SANE_Handle handle,
+ SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ log_call ();
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ if (info != NULL)
+ *info = 0;
+
+ switch (action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ return (getvalue (handle, option, value));
+
+ case SANE_ACTION_SET_VALUE:
+ return (setvalue (handle, option, value, info));
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* This function is part of the SANE API and gets called when the front end
+ * requests information aobut the scan configuration (e.g. color depth, mode,
+ * bytes and pixels per line, number of lines. This information is returned
+ * in the SANE_Parameters structure.
+ *
+ * Once a scan was started, this routine has to report the correct values, if
+ * it is called before the scan is actually started, the values are based on
+ * the current settings.
+ *
+ */
+SANE_Status
+estimate_parameters (Epson_Scanner *s, SANE_Parameters * params)
+{
+ int zoom, max_x, max_y;
+ int bytes_per_pixel;
+
+ int x_dpi = 0;
+ int y_dpi = 0;
+
+ SANE_Int max_y_orig = -1;
+
+ log_call ();
+
+ memset (&s->raw.ctx, 0, sizeof (SANE_Parameters));
+
+ x_dpi = s->val[OPT_X_RESOLUTION].w;
+ y_dpi = s->val[OPT_Y_RESOLUTION].w;
+
+ zoom = s->val[OPT_ZOOM].w;
+
+ if (need_autocrop_override (s))
+ { /* yucky changes to be reverted below */
+ max_y_orig = s->hw->src->max_y;
+ ((extension *) s->hw->src)->max_y = autocrop_max_y (s->hw);
+ update_ranges (s->hw, s->hw->src);
+ s->val[OPT_BR_Y].w = s->hw->src->y_range.max;
+ }
+
+ calculate_scan_area_max (s, &max_x, &max_y);
+
+ s->raw.ctx.pixels_per_line =
+ SANE_UNFIX (s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / MM_PER_INCH * x_dpi * zoom / 100;
+ s->raw.ctx.lines =
+ SANE_UNFIX (s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / MM_PER_INCH * y_dpi * zoom / 100;
+
+ log_data ("max x:%d y:%d [in pixels]", max_x, max_y);
+
+ if (max_x != 0 && max_y != 0)
+ {
+ if (max_x < s->raw.ctx.pixels_per_line)
+ s->raw.ctx.pixels_per_line = max_x;
+ if (max_y < s->raw.ctx.lines)
+ s->raw.ctx.lines = max_y;
+ }
+
+ if (s->raw.ctx.pixels_per_line < 8)
+ s->raw.ctx.pixels_per_line = 8;
+ if (s->raw.ctx.lines < 1)
+ s->raw.ctx.lines = 1;
+
+ log_data ("Preview = %d", s->val[OPT_PREVIEW].w);
+ log_data ("X Resolution = %d", s->val[OPT_X_RESOLUTION].w);
+ log_data ("Y Resolution = %d", s->val[OPT_Y_RESOLUTION].w);
+
+ log_data ("Scan area: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]",
+ SANE_UNFIX (s->val[OPT_TL_X].w),
+ SANE_UNFIX (s->val[OPT_TL_Y].w),
+ SANE_UNFIX (s->val[OPT_BR_X].w),
+ SANE_UNFIX (s->val[OPT_BR_Y].w));
+
+ /* Calculate bytes_per_pixel and bytes_per_line for any color
+ * depths. The default color depth is stored in mode_params.depth.
+ */
+ if (mode_params[s->val[OPT_MODE].w].depth == 1)
+ {
+ s->raw.ctx.depth = 1;
+ }
+ else
+ {
+ s->raw.ctx.depth = s->val[OPT_BIT_DEPTH].w;
+ }
+
+ if (s->raw.ctx.depth > 8)
+ {
+ /* The frontends can only handle 8 or 16 bits for gray or color -
+ * so if it's more than 8, it gets automatically set to 16. This
+ * works as long as EPSON does not come out with a scanner that
+ * can handle more than 16 bits per color channel.
+ */
+ s->raw.ctx.depth = 16;
+ }
+
+ bytes_per_pixel = s->raw.ctx.depth / 8; /* this works because it can only be set to 1, 8 or 16 */
+ if (s->raw.ctx.depth % 8) /* just in case ... */
+ {
+ bytes_per_pixel++;
+ }
+
+ /* All models require alignment on a multiple of 8 pixels per line.
+ However, some models require multiples of 32 pixels instead of 8
+ when scanning in monochrome mode.
+ We use the largest multiple that is not larger than the original
+ value.
+ */
+ s->raw.ctx.pixels_per_line &= ~7;
+ if (1 == s->raw.ctx.depth)
+ {
+ s->raw.ctx.pixels_per_line &= ~31;
+ }
+
+ s->raw.ctx.last_frame = SANE_TRUE;
+
+ if (mode_params[s->val[OPT_MODE].w].color)
+ {
+ s->raw.ctx.format = SANE_FRAME_RGB;
+ s->raw.ctx.bytes_per_line =
+ 3 * s->raw.ctx.pixels_per_line * bytes_per_pixel;
+ }
+ else
+ {
+ s->raw.ctx.format = SANE_FRAME_GRAY;
+ s->raw.ctx.bytes_per_line =
+ s->raw.ctx.pixels_per_line * s->raw.ctx.depth / 8;
+ }
+
+ if (NULL != params)
+ memcpy (params, &s->raw.ctx, sizeof (SANE_Parameters));
+
+ print_params (s->raw.ctx);
+
+ if (need_autocrop_override (s))
+ { /* revert yucky changes made above */
+ ((extension *) s->hw->src)->max_y = max_y_orig;
+ update_ranges (s->hw, s->hw->src);
+ s->val[OPT_BR_Y].w = s->hw->src->y_range.max;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+device_init (Epson_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+
+ status = initialize (s->hw);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+/* There is some undocumented special behavior with the TPU enable/disable.
+ * TPU power ESC e status
+ * on 0 NAK
+ * on 1 ACK
+ * off 0 ACK
+ * off 1 NAK
+ *
+ * It makes no sense to scan with TPU powered on and source flatbed, because
+ * light will come from both sides.
+ */
+
+ if (s->hw->adf || s->hw->tpu)
+ {
+ /* if it is previewing now, disable duplex */
+ SANE_Bool adf_duplex = ((1 == s->val[OPT_ADF_MODE].w)
+ && !s->val[OPT_PREVIEW].b);
+ status = control_option_unit (s->hw, adf_duplex);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ if (s->hw->tpu)
+ err_major ("You may have to power %s your TPU",
+ (using (s->hw, tpu) ? "on" : "off"));
+
+ err_major ("You may have to restart the SANE frontend.");
+ return status;
+ }
+
+ if (s->hw->cmd->request_extended_status != 0)
+ {
+ status = check_ext_status (s->hw);
+
+ if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
+ {
+ return status;
+ }
+ }
+ }
+
+ if (s->hw->using_fs)
+ {
+ cmd_request_scanning_parameter (s->hw);
+ s->hw->param_buf[39] = s->val[OPT_ADF_DFD_SENSITIVITY].w;
+ s->hw->param_buf[40] = (s->val[OPT_ADF_AUTO_SCAN].w ? 0xFF : 0x00);
+ }
+
+ return status;
+}
+
+static SANE_Status
+device_set_focus (Epson_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+
+ if (!s->hw->tpu || !using (s->hw, tpu)) return status;
+
+ /* set the focus position according to the extension used:
+ * if the TPU is selected, then focus 2.5mm above the glass,
+ * otherwise focus on the glass. Scanners that don't support
+ * this feature, will just ignore these calls.
+ */
+
+ if (s->hw->adf || s->hw->tpu)
+ {
+ if (s->hw->tpu && s->hw->tpu->has_focus)
+ {
+ if (s->val[OPT_FOCUS].w == 0)
+ {
+ log_info ("Setting focus to glass surface");
+ status = set_focus_position (s->hw, 0x40);
+ }
+ else
+ {
+ log_info ("Setting focus to 2.5mm above glass");
+ status = set_focus_position (s->hw, 0x59);
+ }
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+ }
+ }
+ return status;
+}
+
+static SANE_Status
+set_scan_parameters (Epson_Scanner *s, int *x_res, int *y_res)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ const struct mode_param *mparam;
+ int x_dpi, y_dpi;
+
+ log_call ();
+
+ mparam = mode_params + s->val[OPT_MODE].w;
+ log_data ("setting data format to %d bits", mparam->depth);
+ status = set_data_format (s->hw, mparam->depth);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_data_format failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* The byte sequence mode was introduced in B5, for B[34] we need
+ line sequence mode
+ */
+ if ((s->hw->cmd->level[0] == 'D' ||
+ (s->hw->cmd->level[0] == 'B' && s->hw->level >= 5)) &&
+ mparam->mode_flags == 0x02)
+ {
+ status = set_color_mode (s->hw, 0x13);
+ }
+ else
+ {
+ status = set_color_mode (s->hw, (mparam->mode_flags
+ | (mparam->dropout_mask
+ & dropout_params[s->val[OPT_DROPOUT].w])));
+ }
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_color_mode failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (s->hw->cmd->set_halftoning &&
+ SANE_OPTION_IS_ACTIVE (s->opt[OPT_HALFTONE].cap))
+ {
+ status = set_halftoning (s->hw, halftone_params[s->val[OPT_HALFTONE].w]);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_halftoning failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ s->brightness = 0;
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BRIGHTNESS].cap))
+ {
+ SANE_Option_Descriptor *sopt = &(s->opt[OPT_BRIGHTNESS_METHOD]);
+ Option_Value *sval = &(s->val[OPT_BRIGHTNESS_METHOD]);
+
+ if (0 == strcmp_c (brightness_method_list[0], /* "hardware" */
+ sopt->constraint.string_list[sval->w]))
+ {
+ status = set_bright (s->hw, s->val[OPT_BRIGHTNESS].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_bright failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+ else /* software emulation */
+ {
+ s->brightness = s->val[OPT_BRIGHTNESS].w;
+ s->brightness /= s->opt[OPT_BRIGHTNESS].constraint.range->max;
+ }
+ }
+
+ s->contrast = 0;
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_CONTRAST].cap))
+ {
+ s->contrast = s->val[OPT_CONTRAST].w;
+ s->contrast /= s->opt[OPT_CONTRAST].constraint.range->max;
+ }
+
+ s->lut = dip_destroy_LUT (s->dip, s->lut);
+ if (0 != s->brightness || 0 != s->contrast) /* non-linear LUT */
+ {
+ SANE_Option_Descriptor *sopt = &(s->opt[OPT_BRIGHTNESS_METHOD]);
+ Option_Value *sval = &(s->val[OPT_BRIGHTNESS_METHOD]);
+
+ if (0 == strcmp_c (brightness_method_list[2], /* "gimp" */
+ sopt->constraint.string_list[sval->w]))
+ s->lut = dip_gimp_BC_LUT (s->dip, s->raw.ctx.depth,
+ s->brightness, s->contrast);
+ else
+ /* We use "iscan" for contrast by default. If "hardware" was
+ * given then s->brightness is 0 and doesn't produce knock-on
+ * effects with this method.
+ */
+ s->lut = dip_iscan_BC_LUT (s->dip, s->raw.ctx.depth,
+ s->brightness, s->contrast);
+
+ if (!s->lut)
+ status = SANE_STATUS_NO_MEM;
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_contrast failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_MIRROR].cap))
+ {
+ status = mirror_image (s->hw, mirror_params[s->val[OPT_MIRROR].w]);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("mirror_image failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SPEED].cap))
+ {
+
+ if (s->val[OPT_PREVIEW].w)
+ status = set_speed (s->hw, speed_params[s->val[OPT_PREVIEW_SPEED].w]);
+ else
+ status = set_speed (s->hw, speed_params[s->val[OPT_SPEED].w]);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_speed failed (%s)", sane_strstatus (status));
+ return status;
+ }
+ }
+
+ /* use of speed_params is ok here since they are false and true.
+ * NOTE: I think I should throw that "params" stuff as long w is
+ * already the value.
+ */
+
+ s->invert_image = SANE_FALSE; /* default: to not inverting the image */
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_FILM_TYPE].cap))
+ {
+ status = set_film_type (s->hw, film_params[s->val[OPT_FILM_TYPE].w]);
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_film_type failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BAY].cap))
+ {
+ status = set_bay (s->hw, s->val[OPT_BAY].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_bay failed (%s)", sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SHARPNESS].cap))
+ {
+
+ status = set_outline_emphasis (s->hw, s->val[OPT_SHARPNESS].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_outline_emphasis failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (s->hw->cmd->set_gamma &&
+ SANE_OPTION_IS_ACTIVE (s->opt[OPT_GAMMA_CORRECTION].cap))
+ {
+ int val;
+ if (s->hw->cmd->level[0] == 'D')
+ {
+ /* The D1 level has only the two user defined gamma settings.
+ */
+ val = s->hw->gamma_type[s->val[OPT_GAMMA_CORRECTION].w];
+ }
+ else
+ {
+ val = s->hw->gamma_type[s->val[OPT_GAMMA_CORRECTION].w];
+
+ /* If "Default" is selected then determine the actual value to
+ * send to the scanner: If bilevel mode, just send the value
+ * from the table (0x01), for grayscale or color mode add one
+ * and send 0x02.
+ */
+ if (s->val[OPT_GAMMA_CORRECTION].w == 0)
+ {
+ val += mparam->depth == 1 ? 0 : 1;
+ }
+ }
+
+ log_info ("set_gamma (s, 0x%x)", val);
+ status = set_gamma (s->hw, val);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_gamma failed (%s)", sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (s->hw->cmd->set_gamma_table &&
+ s->hw->gamma_user_defined[s->val[OPT_GAMMA_CORRECTION].w])
+ { /* user defined. */
+ status = set_gamma_table (s->hw, s);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_gamma_table failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (s->hw->cmd->set_color_correction)
+ {
+ int val = s->hw->color_type[s->val[OPT_COLOR_CORRECTION].w];
+
+ log_data ("set_color_correction (s, 0x%x)", val);
+ status = set_color_correction (s->hw, val);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_color_correction failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ if (s->hw->color_user_defined[s->val[OPT_COLOR_CORRECTION].w])
+ {
+ size_t i;
+
+ status = set_color_correction_coefficients (s->hw, s);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_color_correction_coefficients failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+
+ log_info ("Color correction coefficients");
+ for (i = 0; i < 9; ++i)
+ log_info ("cct[%zd] = %f", i, s->cct[i]);
+ }
+
+ if (s->hw->cmd->set_threshold != 0
+ && SANE_OPTION_IS_ACTIVE (s->opt[OPT_THRESHOLD].cap))
+ {
+ status = set_threshold (s->hw, s->val[OPT_THRESHOLD].w);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_threshold failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ x_dpi = s->val[OPT_X_RESOLUTION].w;
+ y_dpi = s->val[OPT_Y_RESOLUTION].w;
+
+ if (s->hw->using_fs)
+ status = dev_set_scanning_resolution (s->hw, x_dpi, y_dpi);
+ else
+ status = set_resolution (s->hw, x_dpi, y_dpi);
+
+ *x_res = x_dpi;
+ *y_res = y_dpi;
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_resolution(%d, %d) failed (%s)",
+ x_dpi, y_dpi, sane_strstatus (status));
+ return status;
+ }
+
+ if (s->hw->cmd->set_zoom != 0)
+ {
+ status = set_zoom (s->hw, s->val[OPT_ZOOM].w, s->val[OPT_ZOOM].w);
+ if (status != SANE_STATUS_GOOD)
+ {
+ err_fatal ("set_zoom(%d) failed (%s)",
+ s->val[OPT_ZOOM].w, sane_strstatus (status));
+ return status;
+ }
+ }
+ return status;
+}
+
+static void
+wait_for_button (Epson_Scanner *s)
+{
+ SANE_Bool button_status;
+
+ log_call ();
+
+ /* If WAIT_FOR_BUTTON is active, then do just that: Wait until the
+ * button is pressed. If the button was already pressed, then we
+ * will get the button Pressed event right away.
+ */
+ if (s->val[OPT_WAIT_FOR_BUTTON].w == SANE_TRUE)
+ {
+ s->hw->wait_for_button = SANE_TRUE;
+
+ while (s->hw->wait_for_button == SANE_TRUE)
+ {
+ if (s->raw.cancel_requested)
+ {
+ s->hw->wait_for_button = SANE_FALSE;
+ }
+ /* get the button status from the scanner */
+ else if (get_push_button_status (s->hw, &button_status) ==
+ SANE_STATUS_GOOD)
+ {
+ if (button_status == SANE_TRUE)
+ {
+ s->hw->wait_for_button = SANE_FALSE;
+ }
+ else
+ {
+ microsleep (s->hw->polling_time);
+ }
+ }
+ else
+ {
+ /* we run into an eror condition, just continue */
+ s->hw->wait_for_button = SANE_FALSE;
+ }
+ }
+ }
+}
+
+static SANE_Status
+set_line_count (Epson_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ int lcount = 1;
+ s->hw->block_mode = SANE_FALSE;
+
+ log_call ();
+
+ /* The set line count commands needs to be sent for certain scanners
+ * in color mode. The D1 level requires it, we are however only
+ * testing for 'D' and not for the actual numeric level.
+ */
+ if (((s->hw->cmd->level[0] == 'B') &&
+ ((s->hw->level >= 5) || ((s->hw->level >= 4) &&
+ (!mode_params[s->val[OPT_MODE].w].color))))
+ || (s->hw->cmd->level[0] == 'D'))
+ {
+ channel *ch = s->hw->channel; /* for the sake of brevity */
+ s->hw->block_mode = SANE_TRUE;
+ lcount = ch->max_request_size (ch) / s->raw.ctx.bytes_per_line;
+
+ if (0 >= lcount) lcount = 1;
+
+ if (lcount > 255)
+ {
+ lcount = 255;
+ }
+
+ if (using (s->hw, tpu) && lcount > 32)
+ {
+ lcount = 32;
+ }
+
+ /* The D1 series of scanners only allow an even line number
+ * for bi-level scanning. If a bit depth of 1 is selected, then
+ * make sure the next lower even number is selected.
+ */
+ {
+ if (3 < lcount && lcount % 2)
+ {
+ lcount -= 1;
+ }
+ }
+ s->line_count = lcount;
+
+ if (lcount == 0)
+ {
+ err_fatal ("can not set zero line count");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ status = set_lcount (s->hw, lcount);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_lcount(%d) failed (%s)",
+ lcount, sane_strstatus (status));
+ return status;
+ }
+ }
+ return status;
+}
+
+static SANE_Status
+request_command_parameters (Epson_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+
+ if (s->hw->cmd->request_condition != 0)
+ {
+ u_char params[2];
+ u_char result[4];
+ u_char *buf;
+ size_t len;
+
+ params[0] = ESC;
+ params[1] = s->hw->cmd->request_condition;
+
+ /* send request condition */
+ channel_send (s->hw->channel, params, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ len = 4;
+ channel_recv (s->hw->channel, result, len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+ s->hw->status = result[1];
+
+ len = result[3] << 8 | result[2];
+ buf = t_alloca (len, u_char);
+ channel_recv (s->hw->channel, buf, len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ log_info ("SANE_START: Color: %d", (int) buf[1]);
+ log_info ("SANE_START: Resolution (x, y): (%d, %d)",
+ (int) (buf[4] << 8 | buf[3]), (int) (buf[6] << 8 | buf[5]));
+ log_info ("SANE_START: Scan offset (x, y): (%d, %d)",
+ (int) (buf[9] << 8 | buf[8]), (int) (buf[11] << 8 | buf[10]));
+ log_info ("SANE_START: Scan size (w, h): (%d, %d)",
+ (int) (buf[13] << 8 | buf[12]), (int) (buf[15] << 8 | buf[14]));
+ log_info ("SANE_START: Data format: %d", (int) buf[17]);
+ log_info ("SANE_START: Halftone: %d", (int) buf[19]);
+ log_info ("SANE_START: Brightness: %d", (int) buf[21]);
+ log_info ("SANE_START: Gamma: %d", (int) buf[23]);
+ log_info ("SANE_START: Zoom (x, y): (%d, %d)", (int) buf[26],
+ (int) buf[25]);
+ log_info ("SANE_START: Color correction: %d", (int) buf[28]);
+ log_info ("SANE_START: Sharpness control: %d", (int) buf[30]);
+ log_info ("SANE_START: Scanning mode: %d", (int) buf[32]);
+ log_info ("SANE_START: Mirroring: %d", (int) buf[34]);
+ log_info ("SANE_START: Auto area segmentation: %d", (int) buf[36]);
+ log_info ("SANE_START: Threshold: %d", (int) buf[38]);
+ log_info ("SANE_START: Line counter: %d", (int) buf[40]);
+ log_info ("SANE_START: Option unit control: %d", (int) buf[42]);
+ log_info ("SANE_START: Film type: %d", (int) buf[44]);
+ }
+ return status;
+}
+
+static SANE_Status
+device_sheet_setup (Epson_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ int left, top;
+
+ int x_dpi = 0;
+ int y_dpi = 0;
+
+ const struct mode_param *mparam = NULL;
+
+ status = dev_load_paper (s->hw);
+ if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
+ {
+ return status;
+ }
+ status = device_set_focus (s);
+ if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
+ {
+ return status;
+ }
+ status = dev_request_extended_status (s->hw);
+ if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
+ {
+ return status;
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_AUTOCROP].cap)
+ && s->val[OPT_AUTOCROP].b)
+ {
+ s->val[OPT_TL_X].w = SANE_FIX (0);
+ s->val[OPT_TL_Y].w = SANE_FIX (0);
+ s->val[OPT_BR_X].w = s->hw->src->x_range.max;
+ s->val[OPT_BR_Y].w = s->hw->src->y_range.max;
+ }
+ else if (has_size_check_support (s->hw->src)
+ && SANE_OPTION_IS_ACTIVE (s->opt[OPT_DETECT_DOC_SIZE].cap)
+ && s->val[OPT_DETECT_DOC_SIZE].w)
+ {
+ s->val[OPT_TL_X].w = SANE_FIX (0);
+ s->val[OPT_TL_Y].w = SANE_FIX (0);
+ s->val[OPT_BR_X].w = SANE_FIX (s->hw->src->doc_x);
+ s->val[OPT_BR_Y].w = SANE_FIX (s->hw->src->doc_y);
+ }
+
+ adf_handle_adjust_alignment (s, SANE_TRUE);
+
+ status = set_scan_parameters (s, &x_dpi, &y_dpi);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ calculate_scan_area_offset (s->val, &left, &top);
+
+ /* Calculate correction for line_distance in D1 scanner: Start
+ * line_distance lines earlier and add line_distance lines at the
+ * end.
+ *
+ * Because the actual line_distance is not yet calculated we have to
+ * do this first.
+ */
+
+ s->hw->color_shuffle = SANE_FALSE;
+ s->current_output_line = 0;
+ s->color_shuffle_line = 0;
+
+ mparam = mode_params + s->val[OPT_MODE].w;
+ if ((s->hw->optical_res != 0) && (mparam->depth == 8)
+ && (mparam->mode_flags != 0))
+ {
+ s->line_distance = s->hw->max_line_distance * x_dpi / s->hw->optical_res;
+ if (s->line_distance != 0)
+ {
+ s->hw->color_shuffle = SANE_TRUE;
+ }
+ else
+ s->hw->color_shuffle = SANE_FALSE;
+ }
+
+ estimate_parameters (s, NULL);
+ { /* finalise the number of scanlines */
+ int lines = s->raw.ctx.lines;
+ int max_lines = lines;
+
+ if (!need_autocrop_override (s))
+ {
+ max_lines = (SANE_UNFIX(s->hw->src->y_range.max)
+ * s->val[OPT_Y_RESOLUTION].w * s->val[OPT_ZOOM].w / 100
+ / MM_PER_INCH) + 0.5;
+ }
+ /* add extra lines at the top and bottom to minimise the loss of
+ scanlines due to colour shuffling */
+ if (SANE_TRUE == s->hw->color_shuffle)
+ {
+ top -= 1 * s->line_distance;
+ lines += 2 * s->line_distance;
+ }
+
+ /* make sure values are within range
+ In the worst case, this chomps 2 * s->line_distance lines from
+ the area the user wanted to scan. C'est la vie. */
+ top = max (0, top);
+ lines = min (lines, max_lines - top);
+
+ if (s->hw->using_fs)
+ {
+ status = dev_set_scanning_area (s->hw, left, top,
+ s->raw.ctx.pixels_per_line, lines);
+ }
+ else
+ {
+ status = set_scan_area (s->hw, left, top,
+ s->raw.ctx.pixels_per_line, lines);
+ }
+
+ /* substract the additional lines needed for colour shuffling so
+ the frontend can know how many lines to expect */
+ if (SANE_TRUE == s->hw->color_shuffle)
+ {
+ lines -= 2 * s->line_distance;
+ }
+ s->raw.ctx.lines = lines;
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("set_scan_area failed (%s)",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ status = set_line_count (s);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ if (s->hw->using_fs)
+ {
+ /* if it is previewing now, disable duplex */
+ byte adf_duplex = ((1 == s->val[OPT_ADF_MODE].w)
+ && !s->val[OPT_PREVIEW].b) ? 0x01 : 0x00;
+ dev_set_option_unit (s->hw, adf_duplex);
+ status = cmd_set_scanning_parameter (s->hw);
+ }
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ if (s->hw->using_fs)
+ {
+ status = cmd_request_scanning_parameter (s->hw);
+ dev_log_scanning_parameter (s->hw);
+ }
+ else
+ status = request_command_parameters (s);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ if (s->hw->channel->interpreter)
+ {
+ status = s->hw->channel->interpreter->ftor1 (s->hw->channel, &s->raw.ctx,
+ mparam->depth, left, x_dpi,
+ s->hw->optical_res);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+ }
+
+ wait_for_button (s);
+
+ return status;
+}
+
+/*! \brief Implements image data buffer resizing policy.
+ *
+ * The implementation basically reuses previously acquired image data
+ * acquisition buffers as much as possible. This mitigates the risk
+ * of a (consecutive) scan aborting someway halfway through because
+ * memory allocation failed.
+ */
+static
+bool
+resize_warranted (size_t needed, size_t capacity)
+{
+ return needed > capacity;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ SANE_Status status;
+ u_char params[4];
+ size_t len_raw;
+
+ log_call ();
+
+ s->raw.cancel_requested = false;
+ s->raw.all_data_fetched = false;
+ s->raw.transfer_started = false;
+ s->raw.transfer_stopped = false;
+
+ s->img.cancel_requested = false;
+ s->img.all_data_fetched = false;
+ s->img.transfer_started = false;
+ s->img.transfer_stopped = false;
+
+ s->src = &s->raw;
+
+ if (0 == s->frame_count)
+ {
+ /* Back up original SANE options.
+ * \todo string and word array have to copy individually.
+ */
+ memcpy (s->val_bak, s->val, (sizeof (s->val[0]) * NUM_OPTIONS));
+ }
+ else if (!s->val[OPT_ADF_AUTO_SCAN].w)
+ {
+ /* Restore original SANE options.
+ * \todo string and word array have to copy individually.
+ */
+ memcpy (s->val, s->val_bak, (sizeof (s->val[0]) * NUM_OPTIONS));
+ }
+
+ if (!scan_area_is_valid (s))
+ {
+ err_fatal ("The image data resulting from the combination of the "
+ "specified scan area and resolution is too large.");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* for AFF devices, check paper status here instead of in scan_finish
+ * to avoid blocking there in dev_request_extended_status()
+ */
+ if (0 < s->frame_count
+ && using (s->hw, adf)
+ && adf_has_auto_form_feed (s->hw))
+ adf_handle_out_of_paper (s);
+
+ if (0 == s->frame_count)
+ {
+ status = device_init (s);
+ if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
+ {
+ return status;
+ }
+ }
+
+ if (ENABLE_TIMING && 0 == time_pass_count)
+ {
+ time_clear ();
+ time_stamp (time_scan, start);
+ }
+
+ if (!s->val[OPT_ADF_AUTO_SCAN].w || 0 == s->frame_count)
+ {
+ status = device_sheet_setup (s);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+ }
+
+ if (SANE_STATUS_GOOD != (status = check_warmup (s->hw))
+ && !(SANE_STATUS_NO_DOCS == status
+ && using (s->hw, adf)))
+ {
+ return status;
+ }
+
+ status = dev_lock (s->hw);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ params[0] = ESC;
+ if (s->hw->using_fs) params[0] = FS;
+ params[1] = s->hw->cmd->start_scanning;
+
+ if (ENABLE_TIMING && TIME_PASS_MAX > time_pass_count)
+ time_stamp (time_pass[time_pass_count], start);
+
+ channel_send (s->hw->channel, params, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_fatal ("start failed (%s)", sane_strstatus (status));
+ return status;
+ }
+
+ if (s->hw->color_shuffle == SANE_TRUE)
+ {
+ size_t len_line_buffer = s->raw.ctx.bytes_per_line;
+
+ if (resize_warranted (len_line_buffer, s->cap_line_buffer))
+ {
+ size_t i;
+
+ for (i = 0; i < 2 * s->line_distance + 1; ++i)
+ delete (s->line_buffer[i]);
+ s->cap_line_buffer = 0;
+
+ for (i = 0; i < 2 * s->line_distance + 1; ++i)
+ {
+ SANE_Byte *p = t_malloc (len_line_buffer, SANE_Byte);
+
+ if (p)
+ {
+ s->line_buffer[i] = p;
+ }
+ else /* clean up and bail */
+ {
+ size_t j;
+
+ for (j = 0; j < i; ++i)
+ delete (s->line_buffer[i]);
+
+ s->cap_line_buffer = 0;
+
+ return SANE_STATUS_NO_MEM;
+ }
+ s->cap_line_buffer = len_line_buffer;
+ }
+ }
+ }
+
+ if (s->hw->using_fs)
+ {
+ s->hw->block_mode = SANE_TRUE;
+ status = read_image_info_block (s->hw);
+ s->raw.transfer_started = (s->hw->block_total
+ * s->hw->image_block_size
+ + s->hw->final_block_size
+ > 0);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ len_raw = s->hw->image_block_size + 1; /* for error code */
+ }
+ else
+ {
+ len_raw = s->line_count * s->raw.ctx.bytes_per_line;
+ }
+
+ if (resize_warranted (len_raw, s->raw.cap))
+ {
+ delete (s->raw.buf);
+ s->raw.cap = 0;
+
+ if (!(s->raw.buf = t_malloc (len_raw, SANE_Byte)))
+ return SANE_STATUS_NO_MEM;
+
+ s->raw.cap = len_raw;
+ }
+ s->raw.ptr = s->raw.end = s->raw.buf;
+ s->raw.transfer_started = true;
+
+ /* This here will block sane_start() until the whole image has been
+ * scanned and pre-processed. The assumption made here is that the
+ * pre-processing can not be done in place and that the resulting
+ * image is no larger than the image acquired.
+ */
+ if (dip_needs_whole_image (s->dip, s->val, s->opt))
+ {
+ SANE_Int len_img = s->raw.ctx.bytes_per_line * s->raw.ctx.lines;
+ SANE_Int max = len_img;
+ SANE_Int len = 0;
+
+ log_info ("buffering image before returning from sane_start()");
+
+ if (resize_warranted (len_img, s->img.cap))
+ {
+ delete (s->img.buf);
+ s->img.cap = 0;
+
+ if (!(s->img.buf = t_malloc (len_img, SANE_Byte)))
+ return SANE_STATUS_NO_MEM;
+
+ s->img.cap = len_img;
+ }
+
+ s->img.ptr = s->img.buf;
+ do /* note: non-blocking I/O not supported */
+ {
+ s->img.ptr += len;
+ max -= len;
+ status = fetch_image_data (s, s->img.ptr, max, &len);
+ }
+ while (SANE_STATUS_GOOD == status);
+
+ if (SANE_STATUS_EOF != status)
+ return status;
+
+ if (0 != max)
+ return SANE_STATUS_IO_ERROR;
+
+ s->img.ptr = s->img.buf;
+ s->img.end = s->img.buf + len_img;
+ memcpy (&s->img.ctx, &s->raw.ctx, sizeof (s->raw.ctx));
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_DESKEW].cap)
+ && s->val[OPT_DESKEW].b)
+ {
+ dip_deskew (s->dip, s->hw, s->frame_count, &s->img, s->val);
+ }
+
+ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_AUTOCROP].cap)
+ && s->val[OPT_AUTOCROP].b)
+ {
+ dip_autocrop (s->dip, s->hw, s->frame_count, &s->img, s->val);
+ }
+
+ s->img.all_data_fetched = true;
+ s->img.transfer_started = true;
+ s->src = &s->img;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*! Retrieves an image info block and computes image data block info.
+ */
+static SANE_Status
+read_image_info_block (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ u_char buf[14]; /* largest image handshake "info" block */
+ int buf_size = num_of (buf);
+
+ const int limit = 30;
+ int ticks = 0;
+
+ log_call ();
+
+ if (!hw->using_fs)
+ buf_size = (hw->block_mode ? 6 : 4);
+
+ channel_recv_all_retry (hw->channel, buf, buf_size,
+ MAX_READ_ATTEMPTS, &status);
+
+ /* Prevent reporting of stale values if we bail early.
+ */
+ hw->image_block_size = 0;
+ hw->final_block_size = 0;
+ if (hw->using_fs)
+ {
+ hw->block_total = 0;
+ hw->block_count = 0;
+ }
+
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ if (STX != buf[0])
+ {
+ log_data ("code %02x", buf[0]);
+ log_data ("error, expected STX");
+
+ return SANE_STATUS_INVAL;
+ }
+ hw->status = buf[1];
+
+ /* Update values here so they are also available in case we bail in
+ the while loop below.
+ */
+ if (hw->using_fs)
+ {
+ hw->image_block_size = buf_to_uint32 (buf + 2);
+ hw->final_block_size = buf_to_uint32 (buf + 10);
+ hw->block_total = buf_to_uint32 (buf + 6);
+ hw->block_count = 0;
+ }
+ else
+ {
+ hw->image_block_size = buf[3] << 8 | buf[2];
+ if (hw->block_mode)
+ hw->image_block_size *= buf[5] << 8 | buf[4];
+ }
+
+ /* Although spec compliant scanners have their warming up bit set
+ when they are getting ready for a scan, the world is less than
+ perfect :-(
+
+ We hack around non-compliant behaviour by looping until 'ESC G'
+ succeeds or our patience runs out. In the latter case we just
+ claim that the device is busy.
+
+ However, when the ADF function is used, and the last paper is
+ scanned, we do not need looping any more.
+ */
+ while (hw->status & (STATUS_FATAL_ERROR | STATUS_NOT_READY)
+ && !(using (hw, adf)
+ && (hw->status & STATUS_AREA_END))
+ && ticks++ < limit)
+ {
+ u_char cmd[2];
+
+ err_fatal ("fatal error - Status = %02x", hw->status);
+
+ if (SANE_STATUS_GOOD != (status = check_warmup (hw)))
+ return status;
+
+ cmd[0] = (hw->using_fs ? FS : ESC);
+ cmd[1] = 'G';
+
+ channel_send (hw->channel, cmd, 2, &status);
+ sleep (1);
+ channel_recv_all_retry (hw->channel, buf, buf_size,
+ MAX_READ_ATTEMPTS, &status);
+ hw->status = buf[1];
+
+ if (hw->using_fs)
+ {
+ hw->image_block_size = buf_to_uint32 (buf + 2);
+ hw->final_block_size = buf_to_uint32 (buf + 10);
+ hw->block_total = buf_to_uint32 (buf + 6);
+ hw->block_count = 0;
+ }
+ else
+ {
+ hw->image_block_size = buf[3] << 8 | buf[2];
+ if (hw->block_mode)
+ hw->image_block_size *= buf[5] << 8 | buf[4];
+ }
+ }
+ return (ticks < limit
+ ? status
+ : SANE_STATUS_DEVICE_BUSY);
+}
+
+
+static void
+scan_finish (Epson_Scanner * s)
+{
+ log_call ();
+
+ s->raw.transfer_stopped = true;
+
+ if (s->hw->channel->interpreter) /* FIXME: do we really need this? */
+ {
+ s->hw->channel->interpreter->free (s->hw->channel);
+ }
+
+ s->frame_count++;
+
+ if (!using (s->hw, adf)) return; /* we're done */
+
+ if (!(EXT_STATUS_ADFT & s->hw->ext_status) /* sheet feed */
+ && !adf_has_auto_form_feed (s->hw))
+ {
+ dev_eject_paper (s->hw);
+ }
+
+ /* avoid blocking here on dev_request_extended_status()
+ * with AFF devices, unless canceling
+ */
+ if (!adf_has_auto_form_feed (s->hw) || s->raw.cancel_requested)
+ adf_handle_out_of_paper (s);
+}
+
+/* part of the scan sequence logic and should not be used generally */
+static void
+adf_handle_out_of_paper (Epson_Scanner * s)
+{
+ if (!using (s->hw, adf)) return;
+
+ /* if we're finishing an ADF scan and the ADF unit has been disabled,
+ * enable it again in order to get accurate status information
+ */
+ if (using (s->hw, adf) && !(ADF_STATUS_EN & s->hw->adf->status))
+ {
+ /* if it is previewing now, disable duplex */
+ byte value = ((s->hw->adf->using_duplex && !s->val[OPT_PREVIEW].b)
+ ? 0x02 : 0x01);
+ cmd_control_option_unit (s->hw, value);
+ }
+
+ dev_request_extended_status (s->hw);
+ if (!(ADF_STATUS_PE & s->hw->adf->status)) return;
+
+ log_info ("ADF: out of paper, %s mode, %d sheets",
+ ((ADF_STATUS_PAG & s->hw->adf->status) ? "duplex" : "simplex"),
+ s->hw->adf->sheet_count);
+ if (!(ADF_STATUS_PAG & s->hw->adf->status)
+ || 0 == (s->hw->adf->sheet_count % 2))
+ {
+ dev_eject_paper (s->hw);
+ s->frame_count = 0;
+ /* Restore original SANE options.
+ * \todo string and word array have to copy individually.
+ */
+ memcpy (s->val, s->val_bak, (sizeof (s->val[0]) * NUM_OPTIONS));
+ }
+ else
+ log_info ("ADF: scanning reverse side");
+}
+
+static SANE_Bool
+adf_needs_realignment (const device *hw)
+{
+ SANE_Byte dpos;
+
+ require (using (hw, adf));
+
+ dpos = hw->fsi_cap_3 & FSI_CAP_DPOS_MASK;
+
+ return (adf_needs_manual_centering (hw)
+ || FSI_CAP_DPOS_CNTR == dpos
+ || FSI_CAP_DPOS_RIGT == dpos);
+}
+
+static void
+adf_handle_adjust_alignment (Epson_Scanner *s, SANE_Bool finalize)
+{
+ scan_area_t adf_scan_area;
+ double shift = 0.0;
+ double sides = 2; /* distribute shift on both sides
+ --> we center by default */
+ log_call();
+
+ if (!using (s->hw, adf)) return;
+ if (!adf_needs_realignment (s->hw)) return;
+
+ log_info ("before alignment: tl-x = %.2f, br-x = %.2f",
+ SANE_UNFIX (s->val[OPT_TL_X].w),
+ SANE_UNFIX (s->val[OPT_BR_X].w));
+
+ adf_scan_area =
+ get_model_info_max_scan_area (s->hw, s->val[OPT_ADF_MODE].w);
+
+ if(SANE_UNFIX (adf_scan_area.width) < 0
+ && SANE_UNFIX (adf_scan_area.height) < 0)
+ {
+ adf_scan_area.width = s->hw->src->x_range.max;
+ adf_scan_area.height = s->hw->src->y_range.max;
+ }
+
+ if (FSI_CAP_DPOS_RIGT == s->hw->fsi_cap_3 & FSI_CAP_DPOS_MASK)
+ {
+ sides = 1; /* put whole shift on one side */
+ }
+
+ /* scan area setting or no marquee */
+ if (!finalize ||
+ (adf_scan_area.width == (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)
+ && adf_scan_area.height == (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)))
+ {
+ double scan_width = (SANE_UNFIX (s->val[OPT_BR_X].w)
+ - SANE_UNFIX (s->val[OPT_TL_X].w));
+ shift = SANE_UNFIX (s->hw->src->x_range.max) - scan_width;
+ shift /= sides;
+
+ s->val[OPT_TL_X].w = SANE_FIX (shift + 0);
+ s->val[OPT_BR_X].w = SANE_FIX (shift + scan_width);
+ }
+ /* uses auto-detect document size */
+ else if (has_size_check_support (s->hw->src)
+ && SANE_OPTION_IS_ACTIVE (s->opt[OPT_DETECT_DOC_SIZE].cap)
+ && s->val[OPT_DETECT_DOC_SIZE].w)
+ {
+ shift = SANE_UNFIX (s->hw->src->x_range.max) - s->hw->src->doc_x;
+ shift /= sides;
+
+ s->val[OPT_TL_X].w = SANE_FIX (shift + 0);
+ s->val[OPT_BR_X].w = SANE_FIX (shift + s->hw->src->doc_x);
+ }
+ /* has marquee and adf-duplex scan area differ from adf-simplex scan area */
+ else if (!(adf_scan_area.width == s->hw->src->x_range.max
+ && adf_scan_area.height == s->hw->src->y_range.max))
+ {
+ shift = SANE_UNFIX (s->hw->src->x_range.max - adf_scan_area.width);
+ shift /= sides;
+
+ s->val[OPT_TL_X].w += SANE_FIX (shift);
+ s->val[OPT_BR_X].w += SANE_FIX (shift);
+ }
+
+ log_info ("after alignment : tl-x = %.2f, br-x = %.2f",
+ SANE_UNFIX (s->val[OPT_TL_X].w),
+ SANE_UNFIX (s->val[OPT_BR_X].w));
+ log_info ("shifted scan area offset by %.2f mm", shift);
+}
+
+#define GET_COLOR(x) (((x) >> 2) & 0x03)
+
+SANE_Status
+fetch_image_data (Epson_Scanner *s, SANE_Byte * data, SANE_Int max_length,
+ SANE_Int * length)
+{
+ SANE_Status status;
+ int index = 0;
+ SANE_Bool reorder = SANE_FALSE;
+ SANE_Bool needStrangeReorder = SANE_FALSE;
+
+ log_call ();
+
+ if (s->raw.transfer_stopped && s->raw.cancel_requested)
+ return SANE_STATUS_CANCELLED;
+
+START_READ:
+ if (s->raw.ptr == s->raw.end)
+ {
+ size_t buf_len;
+
+ if (s->raw.all_data_fetched)
+ {
+ *length = 0;
+ return SANE_STATUS_EOF;
+ }
+
+ if (!s->hw->using_fs)
+ {
+ status = read_image_info_block (s->hw);
+ if (SANE_STATUS_GOOD != status)
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+ buf_len = s->hw->image_block_size;
+ }
+ else
+ {
+ buf_len = s->hw->image_block_size;
+ if (s->hw->block_count >= s->hw->block_total)
+ buf_len = s->hw->final_block_size;
+ ++buf_len; /* include error byte */
+ }
+
+ if (!s->hw->block_mode && SANE_FRAME_RGB == s->raw.ctx.format)
+ {
+ /* Read color data in line mode */
+
+ /* read the first color line - the number of bytes to read
+ * is already known (from last call to read_image_info_block()
+ * We determine where to write the line from the color information
+ * in the data block. At the end we want the order RGB, but the
+ * way the data is delivered does not guarantee this - actually it's
+ * most likely that the order is GRB if it's not RGB!
+ */
+ switch (GET_COLOR (s->hw->status))
+ {
+ case 1:
+ index = 1;
+ break;
+ case 2:
+ index = 0;
+ break;
+ case 3:
+ index = 2;
+ break;
+ }
+
+ channel_recv (s->hw->channel, s->raw.buf + index * s->raw.ctx.pixels_per_line,
+ buf_len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+
+ /* send the ACK signal to the scanner in order to make
+ * it ready for the next image info block.
+ */
+ channel_send (s->hw->channel, S_ACK, 1, &status);
+
+ /* ... and request the next image info block
+ */
+ if (SANE_STATUS_GOOD != (status = read_image_info_block (s->hw)))
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+
+ buf_len = s->hw->image_block_size;
+
+ switch (GET_COLOR (s->hw->status))
+ {
+ case 1:
+ index = 1;
+ break;
+ case 2:
+ index = 0;
+ break;
+ case 3:
+ index = 2;
+ break;
+ }
+
+ channel_recv (s->hw->channel, s->raw.buf + index * s->raw.ctx.pixels_per_line,
+ buf_len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+
+ channel_send (s->hw->channel, S_ACK, 1, &status);
+
+ /* ... and the last image info block
+ */
+ if (SANE_STATUS_GOOD != (status = read_image_info_block (s->hw)))
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+
+ buf_len = s->hw->image_block_size;
+
+ switch (GET_COLOR (s->hw->status))
+ {
+ case 1:
+ index = 1;
+ break;
+ case 2:
+ index = 0;
+ break;
+ case 3:
+ index = 2;
+ break;
+ }
+
+ channel_recv (s->hw->channel, s->raw.buf + index * s->raw.ctx.pixels_per_line,
+ buf_len, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+ }
+ else
+ {
+ /* Read image data in block mode */
+
+ /* do we have to reorder the image data ? */
+ if (GET_COLOR (s->hw->status) == 0x01)
+ {
+ reorder = SANE_TRUE;
+ }
+
+ channel_recv_all_retry (s->hw->channel, s->raw.buf, buf_len,
+ MAX_READ_ATTEMPTS, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ *length = 0;
+ scan_finish (s);
+ return status;
+ }
+ }
+
+ if (s->hw->using_fs)
+ {
+ u_char err = 0;
+
+ if (s->hw->block_count >= s->hw->block_total)
+ s->raw.all_data_fetched = true;
+ ++(s->hw->block_count);
+ log_info ("read image block %u/%u",
+ s->hw->block_count, s->hw->block_total + 1);
+
+ err = s->raw.buf[--buf_len]; /* drop error byte */
+ log_info ("image block error byte: %x", err);
+
+ if ((FSG_FATAL_ERROR | FSG_NOT_READY) & err)
+ {
+ *length = 0;
+ scan_finish (s);
+ return check_ext_status (s->hw);
+ }
+ else if (FSG_CANCEL_REQUEST & err)
+ {
+ s->raw.cancel_requested = true;
+ }
+ else if (FSG_PAGE_END & err)
+ {
+ if (FSI_CAP_PED & s->hw->fsi_cap_2)
+ {
+ log_info ("paper end flag raised");
+ }
+ else
+ {
+ err_minor ("invalid paper end flag raised");
+ }
+ }
+ else if (0 != err)
+ {
+ log_info ("unknown error flag(s) raised");
+ *length = 0;
+ scan_finish (s);
+ return check_ext_status (s->hw);
+ }
+ }
+ else
+ {
+ s->raw.all_data_fetched = (STATUS_AREA_END & s->hw->status);
+ }
+
+ if (s->raw.all_data_fetched
+ && ENABLE_TIMING && TIME_PASS_MAX > time_pass_count)
+ {
+ time_stamp (time_pass[time_pass_count], stop);
+ ++time_pass_count;
+ }
+
+ if (!s->raw.all_data_fetched)
+ {
+ if (s->raw.cancel_requested)
+ {
+ channel_send (s->hw->channel, S_CAN, 1, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ status = expect_ack (s->hw);
+ if (SANE_STATUS_GOOD != status) return status;
+
+ *length = 0;
+
+ scan_finish (s);
+
+ return SANE_STATUS_CANCELLED;
+ }
+ else
+ {
+ channel_send (s->hw->channel, S_ACK, 1, &status);
+ if (SANE_STATUS_GOOD != status) return status;
+ }
+ }
+
+ s->raw.end = s->raw.buf + buf_len;
+ s->raw.ptr = s->raw.buf;
+
+ /* if we have to re-order the color components (GRB->RGB) we
+ * are doing this here:
+ */
+
+ /* Some scanners (e.g. the Perfection 1640 and GT-2200) seem
+ * to have the R and G channels swapped.
+ * The GT-8700 is the Asian version of the Perfection1640.
+ * If the scanner name is one of these, and the scan mode is
+ * RGB then swap the colors.
+ */
+
+ needStrangeReorder = ((0 == strcmp_c (s->hw->fw_name, "GT-2200")
+ || 0 == strcmp_c (s->hw->fw_name, "Perfection1640")
+ || 0 == strcmp_c (s->hw->fw_name, "GT-8700"))
+ && s->raw.ctx.format == SANE_FRAME_RGB);
+
+ /* Certain Perfection 1650 also need this re-ordering of the two
+ * color channels. These scanners are identified by the problem
+ * with the half vertical scanning area. When we corrected this,
+ * we also set the variable s->hw->need_color_reorder
+ */
+ if (s->hw->need_color_reorder)
+ {
+ needStrangeReorder = SANE_TRUE;
+ }
+
+ if (needStrangeReorder)
+ reorder = SANE_FALSE; /* reordering once is enough */
+
+ if (s->raw.ctx.format != SANE_FRAME_RGB)
+ reorder = SANE_FALSE; /* don't reorder for BW or gray */
+
+ if (reorder)
+ {
+ s->raw.ptr = s->raw.buf;
+ dip_change_GRB_to_RGB (s->dip, &s->raw);
+ }
+
+ /* Do the color_shuffle if everything else is correct - at this
+ * time most of the stuff is hardcoded for the Perfection 610
+ */
+ if (s->hw->color_shuffle)
+ {
+ int new_length = 0;
+
+ status = color_shuffle (s, &new_length);
+
+ /* If no bytes are returned, check if the scanner is already
+ * done, if so, we'll probably just return, but if there is more
+ * data to process get the next batch.
+ */
+
+ if (new_length == 0 && s->raw.end != s->raw.ptr)
+ {
+ goto START_READ;
+ }
+
+ s->raw.end = s->raw.buf + new_length;
+ s->raw.ptr = s->raw.buf;
+ }
+
+ if ((SANE_CAP_EMULATED & s->opt[OPT_CCT_1].cap)
+ && s->hw->color_user_defined[s->val[OPT_COLOR_CORRECTION].w]
+ && SANE_FRAME_RGB == s->raw.ctx.format)
+ {
+ dip_apply_color_profile (s->dip, &s->raw, s->cct);
+ }
+
+ if (s->hw->channel->interpreter)
+ {
+ s->hw->channel->interpreter->ftor0 (s->hw->channel,
+ &s->raw.ctx,
+ s->raw.ptr, s->raw.end);
+ }
+
+ if (s->lut)
+ {
+ dip_apply_LUT (s->dip, &s->raw, s->lut);
+ }
+
+ /* WARNING: The SANE specification normally uses zero to indicate
+ * minimum intensity. However, SANE_FRAME_GRAY images with a bit
+ * depth of one use zero to indicate *maximum* intensity.
+ * The device always uses zero for minimum intensity, irrespective
+ * of the color mode and bit depth.
+ */
+ if (1 == s->raw.ctx.depth && SANE_FRAME_GRAY == s->raw.ctx.format)
+ {
+ if (!s->invert_image)
+ dip_flip_bits (s->dip, &s->raw);
+ }
+ else
+ {
+ if (s->invert_image)
+ dip_flip_bits (s->dip, &s->raw);
+ }
+ }
+
+ /* copy the image data to the data memory area
+ */
+ if (!s->hw->block_mode && SANE_FRAME_RGB == s->raw.ctx.format)
+ {
+ max_length /= 3;
+
+ if (max_length > s->raw.end - s->raw.ptr)
+ max_length = s->raw.end - s->raw.ptr;
+
+ *length = 3 * max_length;
+
+ if (s->raw.cancel_requested)
+ s->raw.ptr += max_length;
+ else
+ {
+ while (max_length-- != 0)
+ {
+ *data++ = s->raw.ptr[0];
+ *data++ = s->raw.ptr[s->raw.ctx.pixels_per_line];
+ *data++ = s->raw.ptr[2 * s->raw.ctx.pixels_per_line];
+ ++s->raw.ptr;
+ }
+ }
+ }
+ else
+ {
+ if (max_length > s->raw.end - s->raw.ptr)
+ max_length = s->raw.end - s->raw.ptr;
+
+ *length = max_length;
+
+ if (s->raw.cancel_requested)
+ s->raw.ptr += max_length;
+ else
+ {
+ memcpy (data, s->raw.ptr, max_length);
+ s->raw.ptr += max_length;
+ }
+ }
+
+ if (s->raw.ptr == s->raw.end && s->raw.all_data_fetched)
+ {
+ scan_finish (s);
+ if (0 == strcmp_c (s->hw->fw_name, "DS-30"))
+ {
+ status = check_ext_status (s->hw);
+ /* Ignore ADF paper empty */
+ s->hw->adf->status &= ~ADF_STATUS_PE;
+ if ( SANE_STATUS_GOOD != status
+ && SANE_STATUS_NO_DOCS != status)
+ {
+ return status;
+ }
+ }
+ }
+ log_call ("exit");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*! Puts raw scan data into the correct scan line.
+
+ When scanning with a non-zero line distance, the RGB data is \e
+ not on a single line in the raw scan data. The RGB channels are
+ separated by a number of lines that depends on the line distance
+ as reported by the ESC i command and the resolution of the scan.
+
+ This function reorganises raw scan data so that the RGB channels
+ are no longer separated and all data is on the same scan line.
+
+ \note
+ It seems that the Perfection 610 and 640U both report a maximum
+ scan area with the two line distances included (based on 11.7"
+ at 600dpi = 7020, whereas the scan area is reportedly 7036 with
+ both line distances equal to 8).
+ */
+static SANE_Status
+color_shuffle (Epson_Scanner *s, int *new_length)
+{
+ SANE_Byte *buf = s->raw.buf;
+ int length = s->raw.end - s->raw.buf;
+
+ log_call ();
+
+ if (s->hw->color_shuffle == SANE_TRUE)
+ {
+ SANE_Byte *data_ptr; /* ptr to data to process */
+ SANE_Byte *data_end; /* ptr to end of processed data */
+ SANE_Byte *out_data_ptr; /* ptr to memory when writing data */
+ int i; /* loop counter */
+
+ log_call ();
+
+ /* Initialize the variables we are going to use for the
+ * copying of the data. data_ptr is the pointer to
+ * the currently worked on scan line. data_end is the
+ * end of the data area as calculated from adding *length
+ * to the start of data.
+ * out_data_ptr is used when writing out the processed data
+ * and always points to the beginning of the next line to
+ * write.
+ */
+
+ data_ptr = out_data_ptr = buf;
+ data_end = data_ptr + length;
+
+ /* The image data is in *raw.buf, we know that the buffer contains
+ * s->raw.end - s->raw.buf ( = length) bytes of data. The width of
+ * one line is in s->raw.ctx.bytes_per_line
+ */
+
+ /* The buffer area is supposed to have a number of full scan
+ * lines, let's test if this is the case.
+ */
+
+ if (length % s->raw.ctx.bytes_per_line != 0)
+ {
+ err_major ("ERROR in size of buffer: %d / %d",
+ length, s->raw.ctx.bytes_per_line);
+ return SANE_STATUS_INVAL;
+ }
+
+ while (data_ptr < data_end)
+ {
+ SANE_Byte *source_ptr, *dest_ptr;
+ int loop;
+
+ /* copy the green information into the current line */
+
+ source_ptr = data_ptr + 1;
+ dest_ptr = s->line_buffer[s->color_shuffle_line] + 1;
+
+ for (i = 0; i < s->raw.ctx.bytes_per_line / 3; i++)
+ {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+
+ /* copy the red information n lines back */
+
+ if (s->color_shuffle_line >= s->line_distance)
+ {
+ source_ptr = data_ptr + 2;
+ dest_ptr =
+ s->line_buffer[s->color_shuffle_line - s->line_distance] + 2;
+
+ for (loop = 0; loop < s->raw.ctx.bytes_per_line / 3; loop++)
+ {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+ }
+
+ /* copy the blue information n lines forward */
+
+ source_ptr = data_ptr;
+ dest_ptr = s->line_buffer[s->color_shuffle_line + s->line_distance];
+
+ for (loop = 0; loop < s->raw.ctx.bytes_per_line / 3; loop++)
+ {
+ *dest_ptr = *source_ptr;
+ dest_ptr += 3;
+ source_ptr += 3;
+ }
+
+ data_ptr += s->raw.ctx.bytes_per_line;
+ s->raw.ptr += s->raw.ctx.bytes_per_line;
+
+ if (s->color_shuffle_line == s->line_distance)
+ {
+ /* We just finished shuffling the line in line_buffer[0] -
+ * write the RGB image data in it to the output buffer and
+ * cyclically shift the line_buffers up by one.
+ */
+
+ SANE_Byte *first;
+
+ if ((s->current_output_line >= s->line_distance) &&
+ (s->current_output_line < s->raw.ctx.lines + s->line_distance))
+ {
+ memcpy (out_data_ptr, s->line_buffer[0], s->raw.ctx.bytes_per_line);
+ out_data_ptr += s->raw.ctx.bytes_per_line;
+ }
+
+ s->current_output_line++;
+
+ first = s->line_buffer[0];
+ for (i = 0; i < 2 * s->line_distance; ++i)
+ {
+ s->line_buffer[i] = s->line_buffer[i + 1];
+ }
+ s->line_buffer[2 * s->line_distance] = first;
+ }
+ else
+ {
+ s->color_shuffle_line++; /* increase the buffer number */
+ }
+ }
+
+ /* At this time we've used up all the new data from the scanner,
+ * some of it is still in the line_buffers, but we are ready to
+ * return some of it to the front end software. To do so we have
+ * to adjust the size of the data area and the *new_length
+ * variable.
+ */
+
+ *new_length = out_data_ptr - buf;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+get_identity_information (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+
+ if (!hw->cmd->request_identity)
+ return SANE_STATUS_INVAL;
+
+ status = cmd_request_identity (hw);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ log_data ("detected command level %s", hw->cmd_lvl);
+
+ {
+ char *force = getenv ("SANE_EPSON_CMD_LVL");
+
+ if (force)
+ {
+ hw->cmd_lvl[0] = force[0];
+ hw->cmd_lvl[1] = force[1];
+ log_info ("forced command level %s", hw->cmd_lvl);
+ }
+ }
+
+ /* check if option equipment is installed */
+
+ if (hw->status & STATUS_OPTION)
+ {
+ log_info ("option equipment is installed");
+ }
+
+ delete (hw->tpu); /* FIXME: is this necessary?? */
+ delete (hw->adf);
+
+ /* set command type and level */
+ {
+ int n = 0;
+
+ while (num_of (epson_cmd) > n
+ && (0 != strcmp_c (hw->cmd_lvl, epson_cmd[n].level)))
+ ++n;
+
+ if (num_of (epson_cmd) > n)
+ hw->cmd = &epson_cmd[n];
+ else
+ err_minor ("unknown command level %s, using %s instead",
+ hw->cmd_lvl, hw->cmd->level);
+
+ hw->level = hw->cmd->level[1] - '0';
+ } /* set comand type and level */
+
+ if (hw->using_fs && 'D' != hw->cmd->level[0]) return SANE_STATUS_GOOD;
+
+ /* Setting available resolutions and xy ranges for sane frontend. */
+ {
+ hw->dpi_range.min = hw->res.list[1];
+ hw->dpi_range.max = hw->res.list[hw->res.size];
+ if (!hw->using_fs) hw->base_res = hw->res.list[hw->res.size];
+ hw->dpi_range.quant = 0;
+
+ if (hw->fbf)
+ {
+ update_ranges (hw, hw->fbf);
+
+ log_info ("FBF: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]",
+ SANE_UNFIX (hw->fbf->x_range.min),
+ SANE_UNFIX (hw->fbf->y_range.min),
+ SANE_UNFIX (hw->fbf->x_range.max),
+ SANE_UNFIX (hw->fbf->y_range.max));
+ }
+ }
+
+ copy_resolution_info (&hw->res_x, &hw->res, SANE_FALSE);
+ copy_resolution_info (&hw->res_y, &hw->res, SANE_FALSE);
+
+ return SANE_STATUS_GOOD;
+} /* request identity */
+
+
+static SANE_Status
+get_hardware_property (device *hw)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ log_call ();
+
+ if (hw->cmd->request_identity2 == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+ status = cmd_request_hardware_property (hw);
+ if (SANE_STATUS_GOOD != status)
+ {
+ return status;
+ }
+
+ log_info ("optical resolution: %ddpi", hw->optical_res);
+
+ if (hw->line_dist_x != hw->line_dist_y)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ hw->max_line_distance = hw->line_dist_y;
+ if (hw->fbf)
+ {
+ hw->fbf->y_range.max = SANE_FIX ((hw->fbf->max_y -
+ 2 * (hw->max_line_distance))
+ * MM_PER_INCH / hw->base_res);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+get_identity2_information (device *hw, Epson_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ require (s && (hw == s->hw));
+
+ status = get_hardware_property (hw);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ /* reset constraints because we are pointing to different lists now
+ FIXME: Only needs to be done the first time we (successfully)
+ send the ESC i command. This should be done when constructing
+ the device and is therefore done by the time we construct a
+ scanner. While the content and size of the lists may vary
+ depending on the selected option, the list nature is constant.
+ */
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_X_RESOLUTION].constraint.word_list = hw->res_x.list;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_Y_RESOLUTION].constraint.word_list = hw->res_y.list;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+
+ log_call ();
+
+ s->img.cancel_requested = true;
+
+ if (s->src->transfer_started && !s->src->transfer_stopped)
+ {
+ SANE_Byte dumpster[1024];
+ int len;
+
+ if (s->src == &s->raw)
+ {
+ s->raw.cancel_requested = true;
+ do
+ {
+ fetch_image_data (s, dumpster, num_of (dumpster), &len);
+ }
+ while (!s->raw.transfer_stopped);
+ }
+
+ if (using (s->hw, adf) && 0 != s->hw->adf->sheet_count
+ && ((EXT_STATUS_ADFT & s->hw->ext_status) /* page type */
+ || adf_has_auto_form_feed (s->hw)))
+ dev_eject_paper (s->hw);
+ }
+ if (!s->raw.cancel_requested && s->raw.all_data_fetched
+ && s->hw->using_fs && s->val[OPT_ADF_AUTO_SCAN].w
+ && SANE_STATUS_NO_DOCS != check_ext_status (s->hw))
+ {
+ s->raw.cancel_requested = dev_force_cancel (s->hw);
+ }
+ dev_unlock (s->hw);
+
+ if (ENABLE_TIMING)
+ {
+ time_stamp (time_scan, stop);
+ time_stats (time_pass_count);
+ time_pass_count = 0;
+ }
+
+ s->frame_count = 0;
+
+ /* Restore original SANE options.
+ * \todo string and word array have to copy individually.
+ */
+ if (s->src->transfer_started)
+ {
+ memcpy (s->val, s->val_bak, (sizeof (s->val[0]) * NUM_OPTIONS));
+ }
+
+ /* release resource hogs between scan sequences */
+ delete (s->img.buf);
+ s->img.cap = 0;
+}
+
+/* Request the push button status returns SANE_TRUE if the button was
+ * pressed and SANE_FALSE if the button was not pressed. It also
+ * returns SANE_TRUE in case of an error. This is necessary so that a
+ * process that waits for the button does not block indefinitely.
+ */
+static SANE_Status
+get_push_button_status (device *hw, SANE_Bool *button_pushed)
+{
+ SANE_Status status;
+ int len;
+ u_char param[3];
+ u_char result[4];
+ u_char *buf;
+
+ log_call ();
+
+ if (hw->cmd->request_push_button_status == 0)
+ {
+ log_info ("push button status unsupported");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ param[0] = ESC;
+ param[1] = hw->cmd->request_push_button_status;
+ param[2] = '\0';
+
+ channel_send (hw->channel, param, 2, &status);
+
+ if (SANE_STATUS_GOOD != status)
+ {
+ err_minor ("error sending command");
+ return status;
+ }
+
+ len = 4; /* get info block */
+ channel_recv (hw->channel, result, len, &status);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+
+ /* get button status */
+ hw->status = result[1];
+ len = result[3] << 8 | result[2];
+ buf = t_alloca (len, u_char);
+ channel_recv (hw->channel, buf, len, &status);
+
+ log_info ("Push button status = %d", buf[0] & 0x01);
+ *button_pushed = ((buf[0] & 0x01) != 0);
+
+ return (SANE_STATUS_GOOD);
+}
+
+
+static void
+filter_resolution_list (Epson_Scanner * s)
+{
+ log_call ();
+
+ if (SANE_TRUE == s->val[OPT_LIMIT_RESOLUTION].w)
+ { /* trim list */
+ SANE_Int i;
+
+ s->hw->res.size = 0;
+ for (i = 1; i <= s->hw->resolution.size; i++)
+ {
+ SANE_Word res = s->hw->resolution.list[i];
+
+ if ((res < 100) || (0 == (res % 300)) || (0 == (res % 400)))
+ {
+ s->hw->res.size++;
+ s->hw->res.list[s->hw->res.size] = res;
+ }
+ }
+ }
+ else
+ { /* restore full list */
+ SANE_Int i;
+
+ for (i = 1; i <= s->hw->resolution.size; i++)
+ {
+ s->hw->res.list[i] = s->hw->resolution.list[i];
+ }
+ s->hw->res.size = s->hw->resolution.size;
+ }
+ s->hw->res.list[0] = s->hw->res.size;
+
+ /* We have just created a filtered resolution list on the *full*
+ * list of resolutions. Now apply ADF capping if necessary. No
+ * need to call dev_restore_res as we've already (sorta) done so
+ * above.
+ */
+ if (using (s->hw, adf)
+ && 0 < large_res_kills_adf_scan (s->hw))
+ {
+ dev_limit_res (s->hw, s->opt[OPT_RESOLUTION].constraint_type,
+ large_res_kills_adf_scan (s->hw));
+ }
+ handle_resolution (s, OPT_RESOLUTION, s->val[OPT_RESOLUTION].w);
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ log_call ("(%s-blocking)", (non_blocking ? "non" : ""));
+
+ /* get rid of compiler warning */
+ handle = handle;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ log_call ();
+
+ /* get rid of compiler warnings */
+ handle = handle;
+ fd = fd;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+
+SANE_Status
+control_option_unit (device *hw, SANE_Bool use_duplex)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ u_char value = 0;
+
+ log_call ();
+
+ if (!hw) return SANE_STATUS_INVAL;
+ if (!(hw->adf || hw->tpu)) return SANE_STATUS_GOOD;
+
+ value = using (hw, adf) || using (hw, tpu);
+ if (value && use_duplex)
+ {
+ value = 2;
+ hw->adf->using_duplex = SANE_TRUE;
+ }
+
+ status = set_cmd (hw, hw->cmd->control_an_extension, value);
+
+ if (using (hw, adf)
+ && (0 == strcmp_c ("ES-10000G", hw->fw_name)
+ || 0 == strcmp_c ("Expression10000", hw->fw_name)))
+ {
+ u_char *buf;
+ u_char params[2];
+ EpsonHdr head;
+ int failures_allowed = 5;
+
+ params[0] = ESC;
+ params[1] = hw->cmd->request_extended_status;
+
+ head = (EpsonHdr) command (hw, params, 2, &status);
+ buf = &head->buf[0];
+
+ while (!(buf[1] & ADF_STATUS_EN))
+ {
+ sleep (1);
+ status = set_cmd (hw, hw->cmd->control_an_extension, value);
+
+ head = (EpsonHdr) command (hw, params, 2, &status);
+ if (SANE_STATUS_GOOD != status)
+ {
+ --failures_allowed;
+ if (!failures_allowed)
+ return status;
+ }
+ buf = &head->buf[0];
+ }
+ }
+ return status;
+}
+
+/* Fetches the main/sub resolution lists for the device.
+ */
+static SANE_Status
+get_resolution_constraints (device *hw, Epson_Scanner *s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Bool adf_duplex;
+
+ log_call ();
+
+ if (!hw->cmd->request_identity2)
+ return SANE_STATUS_GOOD;
+
+ /* if it is previewing now, disable duplex */
+ adf_duplex = ((1 == s->val[OPT_ADF_MODE].w)
+ && !s->val[OPT_PREVIEW].b);
+ status = control_option_unit (hw, adf_duplex);
+ status = get_identity2_information (hw, s);
+
+ handle_resolution (s, OPT_X_RESOLUTION, DEFAULT_X_RESOLUTION);
+ handle_resolution (s, OPT_Y_RESOLUTION, DEFAULT_Y_RESOLUTION);
+
+ return status;
+}
+
+
+static scan_area_t get_model_info_max_scan_area (device *hw, int adf_mode)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ scan_area_t scan_area;
+
+ scan_area.width = SANE_FIX(-1);
+ scan_area.height = SANE_FIX(-1);
+
+ if(hw->adf){
+ const void *info = model_info_cache_get_info (hw->fw_name, &status);
+ if (SANE_STATUS_GOOD == status && info){
+ const char *mode = (1 == adf_mode ? "duplex": "simplex");
+
+ scan_area = model_info_max_scan_area(info, "adf", mode);
+ }
+ }
+ return scan_area;
+}
+
+
+static SANE_Status handle_scan_area(Epson_Scanner *s, int adf_mode)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ scan_area_t scan_area;
+
+ if(s->hw->adf){
+ scan_area = get_model_info_max_scan_area (s->hw, adf_mode);
+
+ if(SANE_UNFIX (scan_area.width) >= 0
+ && SANE_UNFIX (scan_area.height) >= 0){
+ s->hw->adf->x_range.max = scan_area.width;
+ s->hw->adf->y_range.max = scan_area.height;
+ }else{
+ err_minor ("failure getting model info (%s)", sane_strstatus (status));
+ }
+ }
+
+ {
+ SANE_Option_Descriptor *sopt = &(s->opt[OPT_SCAN_AREA]);
+ Option_Value *sval = &(s->val[OPT_SCAN_AREA]);
+
+ SANE_String_Const area = NULL;
+ SANE_String_Const *p
+ = (SANE_String_Const *) sopt->constraint.string_list;
+
+ if (!p)
+ {
+ s->opt[OPT_SCAN_AREA].cap |= SANE_CAP_INACTIVE;
+
+ /* media_list + media_maximum, media_automatic, and NULL terminator */
+ p = t_calloc (num_of (media_list) + 3, SANE_String_Const);
+
+ if (!p) /* do without the option */
+ {
+ s->opt[OPT_QUICK_FORMAT].cap = s->opt[OPT_SCAN_AREA].cap;
+ return status;
+ }
+
+ sopt->constraint.string_list = p;
+ }
+ area = p[sval->w];
+
+ memset (p, 0, (num_of (media_list) + 3) * sizeof (SANE_String_Const));
+ sopt->size = 0;
+
+ *p = media_maximum;
+ sopt->size = max (strlen (*p), sopt->size);
+ ++p;
+
+ if (using (s->hw, tpu))
+ {
+ s->opt[OPT_SCAN_AREA].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ size_t i;
+
+ if (has_size_check_support (s->hw->src))
+ {
+ *p = media_automatic;
+ sopt->size = max (strlen (*p) + 1, sopt->size);
+ ++p;
+ s->opt[OPT_SCAN_AREA].cap |= SANE_CAP_AUTOMATIC;
+ }
+
+ for (i = 0; i < num_of (media_list); ++i)
+ {
+ SANE_Fixed w = SANE_FIX (media_list[i].width);
+ SANE_Fixed h = SANE_FIX (media_list[i].height);
+
+ if ( w <= s->hw->src->x_range.max
+ && h <= s->hw->src->y_range.max)
+ {
+ *p = media_list[i].name;
+ sopt->size = max (strlen (*p) + 1, sopt->size);
+ ++p;
+ if (strstr (media_list[i].name, "Portrait"))
+ {
+ ++i;
+ }
+ }
+ else if (strstr (media_list[i].name, "Landscape"))
+ { /* no need to list "Portrait" */
+ ++i;
+ }
+ }
+
+ s->opt[OPT_SCAN_AREA].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ { // recompute index of selected area in the new list
+ size_t i = 0;
+ p = (SANE_String_Const *) sopt->constraint.string_list;
+ while (*p && 0 != strcmp_c (*p, area))
+ ++i, ++p;
+ sval->w = (*p ? i : 0);
+ }
+ }
+
+ s->val[OPT_BR_X].w = s->hw->src->x_range.max;
+ s->val[OPT_BR_Y].w = s->hw->src->y_range.max;
+ s->val[OPT_TL_X].w = 0;
+ s->val[OPT_TL_Y].w = 0;
+
+ s->opt[OPT_TL_X].constraint.range = &(s->hw->src->x_range);
+ s->opt[OPT_TL_Y].constraint.range = &(s->hw->src->y_range);
+ s->opt[OPT_BR_X].constraint.range = &(s->hw->src->x_range);
+ s->opt[OPT_BR_Y].constraint.range = &(s->hw->src->y_range);
+
+ /* copy OPT_SCAN_AREA to OPT_QUICK_FORMAT */
+ s->opt[OPT_QUICK_FORMAT].size = s->opt[OPT_SCAN_AREA].size;
+ s->opt[OPT_QUICK_FORMAT].cap = s->opt[OPT_SCAN_AREA].cap;
+ s->opt[OPT_QUICK_FORMAT].constraint.string_list
+ = s->opt[OPT_SCAN_AREA].constraint.string_list;
+ s->val[OPT_QUICK_FORMAT].w = s->val[OPT_SCAN_AREA].w;
+
+ return status;
+}
diff --git a/backend/epkowa.conf b/backend/epkowa.conf
new file mode 100644
index 0000000..fd387b1
--- /dev/null
+++ b/backend/epkowa.conf
@@ -0,0 +1,65 @@
+# epkowa.conf -- sample configuration for the EPKOWA SANE backend
+# Copyright (C) 2004, 2008, 2009 Olaf Meeuwissen
+#
+# See sane-epkowa(5), sane-usb(5) and sane-scsi(5) for details.
+
+# Detect all devices supported by the backend.
+# If you don't have a SCSI device, you can comment out the "scsi"
+# keyword. Similarly for the other keywords.
+#
+usb
+scsi
+
+
+# For any USB scanner not known to the backend (yet), you may, at your
+# own peril(!!), force the backend to recognise and use it via libusb.
+# You can do so by the following configuration command:
+#
+# usb <USB vendor ID> <USB product ID>
+#
+# SEIKO EPSON's USB vendor ID is '0x04b8' (without quotes). In order
+# to find the USB product ID, use lsusb(1).
+# A sample configuration for the Epson Perfection 1650 (Epson GT-8200),
+# which has a product ID of 0x0110, would look as follows:
+#
+#usb 0x04b8 0x0110
+
+
+# For SCSI devices not detected, you can add an entry like:
+#
+# scsi EPSON GT-20000
+#
+# where the GT-20000 bit corresponds to the SCSI model information as
+# shown in the output of dmesg(1) or in the /var/log/kern.log file.
+
+
+# Network attached devices may be made to work by first installing the
+# (non-free) iscan-network-nt package and then adding configuration lines
+# as per information below.
+#
+# For each network attached device, you must add an entry as follows:
+#
+# net <IP-address|hostname> [port-number]
+#
+# Ask your network administrator for the device's IP address or check
+# for yourself on the panel (if it has one). The port-number is very
+# optional and defaults to 1865.
+# Note that network attached devices are not queried unless configured
+# in this file.
+#
+# Examples:
+#
+#net 192.16.136.2 1865
+#net 10.0.0.1
+#net scanner.mydomain.com
+
+
+# Some backend behaviour can be customized by using the option keyword
+# followed by an option name, as shown below.
+#
+# option <option-name>
+#
+# Currently available options:
+#
+# Makes the automatic document feeder the default document source
+#option prefer-adf
diff --git a/backend/epkowa.h b/backend/epkowa.h
new file mode 100644
index 0000000..72ab909
--- /dev/null
+++ b/backend/epkowa.h
@@ -0,0 +1,291 @@
+/* epkowa.h - SANE backend for EPSON flatbed scanners
+ (Image Scan! version)
+
+ Based on the SANE Epson backend (originally from sane-1.0.3)
+ - updated to sane-backends-1.0.6
+ - renamed from epson to epkowa to avoid confusion
+ - updated to sane-backends-1.0.10
+ - updated to sane-backends-1.0.15
+
+ Based on Kazuhiro Sasayama previous
+ Work on epson.[ch] file from the SANE package.
+
+ Original code taken from sane-0.71
+ Copyright (C) 1997 Hypercore Software Design, Ltd.
+
+ modifications
+ Copyright (C) 1998-1999 Christian Bucher <bucher@vernetzt.at>
+ Copyright (C) 1998-1999 Kling & Hautzinger GmbH
+ Copyright (C) 1999 Norihiko Sawa <sawa@yb3.so-net.ne.jp>
+ Copyright (C) 2000 Karl Heinz Kremer <khk@khk.net>
+ Copyright (C) 2001-2009 SEIKO EPSON CORPORATION
+
+ This file is part of the EPKOWA SANE backend.
+
+ 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 epkowa_h
+#define epkowa_h
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "device.h"
+
+typedef struct
+{
+ SANE_Byte *lut;
+ int depth;
+
+} LUT;
+
+
+/* convenience union to access option values given to the backend
+ */
+typedef union
+{
+ SANE_Bool b;
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+}
+Option_Value;
+
+/* string constants for GUI elements that are not defined SANE-wide */
+
+#define SANE_NAME_GAMMA_CORRECTION "gamma-correction"
+#define SANE_TITLE_GAMMA_CORRECTION SANE_I18N("Gamma Correction")
+#define SANE_DESC_GAMMA_CORRECTION SANE_I18N("Selects the gamma correction value from a list of pre-defined devices or the user defined table, which can be downloaded to the scanner")
+
+#define SANE_EPSON_FOCUS_NAME "focus-position"
+#define SANE_EPSON_FOCUS_TITLE SANE_I18N("Focus Position")
+#define SANE_EPSON_FOCUS_DESC SANE_I18N("Sets the focus position to either the glass or 2.5mm above the glass")
+#define SANE_EPSON_WAIT_FOR_BUTTON_NAME "wait-for-button"
+#define SANE_EPSON_WAIT_FOR_BUTTON_TITLE SANE_I18N("Wait for Button")
+#define SANE_EPSON_WAIT_FOR_BUTTON_DESC SANE_I18N("After sending the scan command, wait until the button on the scanner is pressed to actually start the scan process.");
+#define SANE_EPSON_MONITOR_BUTTON_NAME "monitor-button"
+#define SANE_EPSON_MONITOR_BUTTON_TITLE SANE_I18N("Monitor Button")
+#define SANE_EPSON_MONITOR_BUTTON_DESC SANE_I18N("Indicates whether a button on the scanner has been pressed.");
+#define SANE_EPSON_DETECT_DOC_SIZE_NAME "detect-doc-size"
+#define SANE_EPSON_DETECT_DOC_SIZE_TITLE SANE_I18N("Auto-detect document size")
+#define SANE_EPSON_DETECT_DOC_SIZE_DESC SANE_I18N("Activates document size auto-detection. The scan area will be set to match the detected document size.")
+#define SANE_EPSON_SCAN_AREA_IS_VALID_NAME "scan-area-is-valid"
+#define SANE_EPSON_SCAN_AREA_IS_VALID_TITLE SANE_I18N("Scan Area Is Valid")
+#define SANE_EPSON_SCAN_AREA_IS_VALID_DESC SANE_I18N("Indicates whether the current scan area settings are valid.")
+
+#define SANE_EPSON_ADF_AUTO_SCAN_NAME "adf-auto-scan"
+#define SANE_EPSON_ADF_AUTO_SCAN_TITLE SANE_I18N("ADF Auto Scan")
+#define SANE_EPSON_ADF_AUTO_SCAN_DESC SANE_I18N("Skips per sheet device setup for faster throughput.")
+
+#define SANE_EPSON_ADF_DFD_SENSITIVITY_NAME "double-feed-detection-sensitivity"
+#define SANE_EPSON_ADF_DFD_SENSITIVITY_TITLE SANE_I18N("Double Feed Detection Sensitivity")
+#define SANE_EPSON_ADF_DFD_SENSITIVITY_DESC SANE_I18N("Sets the sensitivity with which multi-sheet page feeds are detected and reported as errors.")
+
+#define SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_NAME "adf-duplex-direction-matches"
+#define SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_TITLE SANE_I18N("ADF Duplex Direction Matches")
+#define SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_DESC SANE_I18N("Indicates whether the device's ADF duplex mode, if available, scans in the same direction for the front and back.")
+
+#define SANE_EPSON_POLLING_TIME_NAME "polling-time"
+#define SANE_EPSON_POLLING_TIME_TITLE SANE_I18N("Polling Time")
+#define SANE_EPSON_POLLING_TIME_DESC SANE_I18N("Time between queries when waiting for device state changes.")
+
+#define SANE_EPSON_NEEDS_POLLING_NAME "needs-polling"
+#define SANE_EPSON_NEEDS_POLLING_TITLE SANE_I18N("Needs Polling")
+#define SANE_EPSON_NEEDS_POLLING_DESC SANE_I18N("Indicates whether the scanner needs to poll.")
+
+#define SANE_EPSON_CALIBRATE_NAME "calibrate"
+#define SANE_EPSON_CALIBRATE_TITLE SANE_I18N("Calibrate")
+#define SANE_EPSON_CALIBRATE_DESC SANE_I18N("Performs color matching to make sure that the document's color tones are scanned correctly.")
+
+#define SANE_EPSON_CLEAN_NAME "clean"
+#define SANE_EPSON_CLEAN_TITLE SANE_I18N("Clean")
+#define SANE_EPSON_CLEAN_DESC SANE_I18N("Cleans the scanners reading section.")
+
+#define LINES_SHUFFLE_MAX (17) /* 2 x 8 lines plus 1 */
+
+#define SANE_EPSON_MAX_RETRIES (120) /* how often do we retry during warmup ? */
+
+#define MAX_READ_ATTEMPTS 10 /* maximum number of attempts at
+ reading scan data */
+#define DEFAULT_POLLING_TIME (1000 * 1000) /* usec */
+
+enum {
+ OPT_NUM_OPTS = 0,
+ /* */
+ OPT_MODE_GROUP,
+ OPT_MODE,
+ OPT_BIT_DEPTH,
+ OPT_HALFTONE,
+ OPT_DROPOUT,
+ OPT_BRIGHTNESS_METHOD,
+ OPT_BRIGHTNESS,
+ OPT_CONTRAST,
+ OPT_SHARPNESS,
+ OPT_GAMMA_CORRECTION,
+ OPT_COLOR_CORRECTION,
+ OPT_RESOLUTION,
+ OPT_X_RESOLUTION,
+ OPT_Y_RESOLUTION,
+ OPT_THRESHOLD,
+ /* */
+ OPT_ADVANCED_GROUP,
+ OPT_MIRROR,
+ OPT_SPEED,
+ OPT_AAS,
+ OPT_LIMIT_RESOLUTION,
+ OPT_ZOOM,
+ OPT_GAMMA_VECTOR_R,
+ OPT_GAMMA_VECTOR_G,
+ OPT_GAMMA_VECTOR_B,
+ OPT_WAIT_FOR_BUTTON,
+ OPT_MONITOR_BUTTON,
+ OPT_POLLING_TIME,
+ OPT_NEEDS_POLLING,
+ /* */
+ OPT_CCT_GROUP,
+ OPT_CCT_1,
+ OPT_CCT_2,
+ OPT_CCT_3,
+ OPT_CCT_4,
+ OPT_CCT_5,
+ OPT_CCT_6,
+ OPT_CCT_7,
+ OPT_CCT_8,
+ OPT_CCT_9,
+ /* */
+ OPT_PREVIEW_GROUP,
+ OPT_PREVIEW,
+ OPT_PREVIEW_SPEED,
+ /* */
+ OPT_GEOMETRY_GROUP,
+ OPT_SCAN_AREA,
+ OPT_TL_X,
+ OPT_TL_Y,
+ OPT_BR_X,
+ OPT_BR_Y,
+ OPT_QUICK_FORMAT,
+ /* */
+ OPT_EQU_GROUP,
+ OPT_SOURCE,
+ OPT_AUTO_EJECT,
+ OPT_FILM_TYPE,
+ OPT_FOCUS,
+ OPT_BAY,
+ OPT_EJECT,
+ OPT_ADF_MODE,
+ OPT_DETECT_DOC_SIZE,
+ OPT_SCAN_AREA_IS_VALID,
+ OPT_ADF_AUTO_SCAN,
+ OPT_ADF_DFD_SENSITIVITY,
+ OPT_EXT_SANE_STATUS,
+ OPT_ADF_DUPLEX_DIRECTION_MATCHES,
+ OPT_DESKEW,
+ OPT_AUTOCROP,
+ OPT_CALIBRATE,
+ OPT_CLEAN,
+ /* */
+ NUM_OPTIONS
+};
+
+/*! Container for image data.
+ */
+typedef struct
+{
+ size_t cap; /*!< buffer capacity */
+ SANE_Byte *buf; /*!< start of image data */
+ SANE_Byte *end; /*!< end of image data */
+ SANE_Byte *ptr; /*!< current position */
+ SANE_Parameters ctx; /*!< buffer context */
+
+ bool cancel_requested;
+ bool all_data_fetched;
+ bool transfer_started;
+ bool transfer_stopped;
+
+} buffer;
+
+/*! Software representation of the \e logical device.
+ */
+struct Epson_Scanner
+{
+ struct Epson_Scanner *next;
+
+ const void *dip;
+ device *hw;
+ SANE_Option_Descriptor opt[NUM_OPTIONS];
+ Option_Value val[NUM_OPTIONS];
+ Option_Value val_bak[NUM_OPTIONS];
+
+ /* image data acquisition buffers and parameters */
+ buffer *src; /*!< buffer to provide data to frontend */
+ buffer raw; /*!< device image data blocks */
+ buffer img; /*!< complete in-memory image */
+
+ SANE_Byte *line_buffer[LINES_SHUFFLE_MAX];
+ size_t cap_line_buffer;
+
+ SANE_Int color_shuffle_line; /* current line number for color shuffling */
+ SANE_Int line_distance; /* current line distance */
+ SANE_Int current_output_line; /* line counter when color shuffling */
+
+ SANE_Bool invert_image;
+ SANE_Word gamma_table[3][256];
+ double cct[9];
+ LUT *lut;
+ double brightness;
+ double contrast;
+
+ /*! Number of frames acquired so far.
+ * This includes partial frames if scan_finish() is called before a
+ * frame is completed.
+ */
+ unsigned int frame_count;
+
+ /*! Number of raw scan lines data gotten from scanner
+ * This corresponds to the \c ESC_d parameter.
+ */
+ unsigned int line_count;
+};
+
+typedef struct Epson_Scanner Epson_Scanner;
+
+SANE_Status estimate_parameters (Epson_Scanner *, SANE_Parameters *);
+SANE_Status fetch_image_data (Epson_Scanner *, SANE_Byte *, SANE_Int,
+ SANE_Int *);
+
+#endif /* not epkowa_h */
diff --git a/backend/epkowa_ip.c b/backend/epkowa_ip.c
new file mode 100644
index 0000000..763a34a
--- /dev/null
+++ b/backend/epkowa_ip.c
@@ -0,0 +1,596 @@
+/* epkowa_ip.c -- for scanners that don't speak EPSON ESC/I natively
+ Copyright (C) 2005--2008 Olaf Meeuwissen
+ Copyright (C) 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the EPKOWA SANE backend.
+ It defines a wrapper around the backend's "Interpreter" interface.
+
+ The EPKOWA SANE backend 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 FITNESS
+ FOR A PARTICULAR PURPOSE or MERCHANTABILITY.
+ See the GNU General Public License for more details.
+
+ You should have received a verbatim copy of the GNU General Public
+ License along with this program; if not, write to:
+
+ Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330
+ Boston, MA 02111-1307 USA
+
+ Linking the EPKOWA SANE backend statically or dynamically with
+ other modules is making a combined work based on the EPKOWA SANE
+ backend. Thus, the terms and conditions of the GNU General Public
+ License cover the whole combination.
+
+ As a special exception, the copyright holders of the EPKOWA SANE
+ backend give you permission to link the EPKOWA SANE backend with
+ independent modules that communicate with the EPKOWA SANE backend
+ solely through the "Interpreter" interface, regardless of the
+ license terms of these independent modules, and to copy and
+ distribute the resulting combined work under terms of your choice,
+ provided that every copy of the combined work is accompanied by a
+ complete copy of the source code of the EPKOWA SANE backend (the
+ version of the EPKOWA SANE backend used to produce the combined
+ work), being distributed under the terms of the GNU General Public
+ License plus this exception. An independent module is a module
+ which is not derived from or based on the EPKOWA SANE backend.
+
+ Note that people who make modified versions of the EPKOWA SANE
+ backend are not obligated to grant this special exception for their
+ modified versions; it is their choice whether to do so. The GNU
+ General Public License gives permission to release a modified
+ version without this exception; this exception also makes it
+ possible to release a modified version which carries forward this
+ exception.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "epkowa_ip.h"
+#include "cfg-obj.h"
+
+#include "sane/sanei_usb.h"
+
+#define TICK_INTERVAL 60 /* seconds */
+#define TICK_TIME_OUT 15 /* intervals */
+
+// FIXME: this ought to be part of the interpreter_type struct so that
+// every instance can have its own.
+device_type *g_epson = NULL;
+
+
+/* default member function implementors */
+
+static SANE_Status _dtor (device_type *);
+static int _open (device_type *);
+static int _close (device_type *);
+static ssize_t _recv (device_type *, void *, size_t, SANE_Status *);
+static ssize_t _send (device_type *, const void *, size_t, SANE_Status *);
+
+static SANE_Status _free (device_type *);
+static SANE_Status _ftor0 (device_type *, SANE_Parameters *,
+ SANE_Byte *, SANE_Byte *);
+static SANE_Status _ftor1 (device_type *, SANE_Parameters *,
+ int, int, int, int);
+
+/* private functions */
+
+static void *_load (const char *, device_type *);
+
+
+/* callback functions */
+
+static ssize_t usb_read (void *buffer, size_t size);
+static ssize_t usb_write (void *buffer, size_t size);
+static ssize_t usb_ctrl (size_t request_type, size_t request,
+ size_t value, size_t index,
+ size_t size, void *buffer);
+
+
+/*! Creates an interpreter object for a \a device if both necessary
+ and available. The object is accessible through the \a device's
+ \c interpreter field once successfully created.
+ */
+SANE_Status
+create_interpreter (device_type *device, unsigned int usb_product_id)
+{
+ void *cfg = cfg_init (NULL, NULL);
+ list *seen = cfg_seen (cfg, CFG_KEY_INTERPRETER);
+ const cfg_interpreter_info *next = NULL;
+
+ if (!device) /* sanity check */
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (device->interpreter) /* we've been here before */
+ {
+ if (device != device->interpreter->_device)
+ { /* internal inconsistency! */
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ if (seen)
+ {
+ list_entry *cur = seen->cur;
+ list_reset (seen);
+ while ((next = list_next (seen))
+ && usb_product_id != next->product)
+ {
+ /* condition does all the processing */
+ }
+ seen->cur = cur;
+ }
+ if (!next) return SANE_STATUS_GOOD;
+
+ /* If we are still here, we need to set up an interpreter and load
+ the corresponding module. */
+
+ device->interpreter = t_malloc (1, struct interpreter_type);
+
+ if (!device->interpreter)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ device->interpreter->_device = device;
+ device->interpreter->_module = _load (next->library, device);
+
+ if (!device->interpreter->_module)
+ {
+ delete (device->interpreter);
+ return SANE_STATUS_INVAL;
+ }
+
+ device->interpreter->_tick_count = -1;
+ device->interpreter->_table = NULL;
+ device->interpreter->_buffer = NULL;
+
+ device->interpreter->dtor = _dtor;
+ device->interpreter->open = _open;
+ device->interpreter->close = _close;
+ device->interpreter->recv = _recv;
+ device->interpreter->send = _send;
+
+ device->interpreter->free = _free;
+ device->interpreter->ftor0 = _ftor0;
+ device->interpreter->ftor1 = _ftor1;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*! Destroys a \a device's interpreter object.
+ */
+SANE_Status
+_dtor (device_type *device)
+{
+ if (!device || !device->interpreter)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ device->interpreter->close (device);
+ device->interpreter->free (device);
+
+#ifdef USE_ALARM
+ alarm (0);
+#endif
+
+ lt_dlclose (device->interpreter->_module);
+ device->interpreter->_module = 0;
+
+ delete (device->interpreter);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*! Frees several temporary buffers.
+ */
+SANE_Status
+_free (device_type *device)
+{
+ if (!device || !device->interpreter)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ delete (device->interpreter->_table);
+ delete (device->interpreter->_buffer);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*! Performs whatever actions are needed when opening an interpreter
+ "controlled" device. One of these actions is disarming an alarm
+ used to control whether the device can go into power saving mode
+ or not. */
+int
+_open (device_type *device)
+{
+ struct interpreter_type *di;
+
+ if (!device || !device->interpreter)
+ {
+ return -1;
+ }
+ di = device->interpreter;
+
+#ifdef USE_ALARM
+ alarm (0);
+ log_info ("alarm (%d)", 0);
+#endif
+ di->_tick_count = -1;
+
+ g_epson = device;
+
+ if (device->fd < 0
+ || !(di->_init
+ ? di->_init (device->fd, usb_read, usb_write)
+ : di->_init_with_ctrl (device->fd, usb_read,
+ usb_write, usb_ctrl)))
+ {
+ err_fatal ("failed to initialize interpreter");
+ g_epson = NULL;
+ return -1;
+ }
+
+ return device->fd;
+}
+
+int
+_close (device_type *device)
+{
+ if (!device || !device->interpreter)
+ {
+ return -1;
+ }
+
+ device->interpreter->_fini ();
+
+ device->interpreter->_tick_count = 0;
+#ifdef USE_ALARM
+ alarm (TICK_INTERVAL);
+ log_info ("alarm (%d)", TICK_INTERVAL);
+#endif
+
+ g_epson = NULL;
+
+ return device->fd;
+}
+
+ssize_t
+_send (device_type *device, const void *buffer, size_t size,
+ SANE_Status *status)
+{
+ if (!status)
+ {
+ return -1;
+ }
+
+ if (!device || !device->interpreter)
+ {
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+ /* ASSUMPTION: Interpreter does NOT change buffer's content.
+ */
+ if (device->interpreter->_write ((void *)buffer, size))
+ {
+ *status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ *status = SANE_STATUS_INVAL;
+ }
+
+ return size;
+}
+
+ssize_t
+_recv (device_type *device, void *buffer, size_t size,
+ SANE_Status *status)
+{
+ if (!status)
+ {
+ return -1;
+ }
+
+ if (!device || !device->interpreter)
+ {
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+ if (device->interpreter->_read (buffer, size))
+ {
+ *status = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ *status = SANE_STATUS_INVAL;
+ }
+
+ return size;
+}
+
+/*! Callback for use by the interpreter. */
+ssize_t
+usb_read (void *buffer, size_t size)
+{
+ size_t n = size;
+
+ if (!g_epson || g_epson->fd < 0)
+ {
+ return 0;
+ }
+
+ if (SANE_STATUS_GOOD == sanei_usb_read_bulk (g_epson->fd, buffer, &n))
+ {
+ if (size != n)
+ err_minor ("Did not read number of bytes requested");
+ return n;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/*! Callback for use by the interpreter. */
+ssize_t
+usb_write (void *buffer, size_t size)
+{
+ size_t n = size;
+
+ if (!g_epson || g_epson->fd < 0)
+ {
+ return 0;
+ }
+
+ if (SANE_STATUS_GOOD == sanei_usb_write_bulk (g_epson->fd, buffer, &n))
+ {
+ if (size != n)
+ err_minor ("Did not read number of bytes requested");
+ return n;
+ }
+ else
+ return 0;
+}
+
+/*! Callback for use by the interpreter. */
+ssize_t
+usb_ctrl (size_t request_type, size_t request, size_t value,
+ size_t index, size_t size, void *buffer)
+{
+ size_t n = size;
+
+ if (!g_epson || g_epson->fd < 0)
+ {
+ return 0;
+ }
+
+ if (SANE_STATUS_GOOD == sanei_usb_control_msg (g_epson->fd, request_type,
+ request, value, index,
+ &n, buffer))
+ {
+ if (size != n)
+ err_minor ("Did not read number of bytes requested");
+ return n;
+ }
+ else
+ return 0;
+}
+
+#ifdef USE_ALARM
+void
+timer_event (int sig)
+{
+ sig = sig;
+
+ log_call ();
+
+ if (!g_epson || !g_epson->interpreter)
+ return;
+
+ if ((g_epson && 0 < g_epson->fd) || g_epson->interpreter->_tick_count == -1)
+ {
+ return;
+ }
+
+ g_epson->interpreter->_tick_count++;
+
+ if (TICK_TIME_OUT - 1 <= g_epson->interpreter->_tick_count)
+ {
+ g_epson->interpreter->_power ();
+ g_epson->interpreter->_tick_count = -1;
+ return;
+ }
+
+ alarm (TICK_INTERVAL);
+ log_info ("alarm (%d)", TICK_INTERVAL);
+}
+#endif
+
+/*! Loads the interpreter module. */
+/*!
+ */
+void *
+_load (const char *name, device_type *device)
+{
+ void *handle = NULL;
+ struct interpreter_type *di = device->interpreter;
+
+ /* FIXME: should set the search path to a list of directories based
+ on the regular load path with the PACKAGE attached to each of the
+ individual directories. Should _not_ look in the regular load
+ path's directories. */
+ {
+ const char *path = lt_dlgetsearchpath ();
+ if (!(path && strstr (path, PKGLIBDIR)))
+ {
+ lt_dladdsearchdir (PKGLIBDIR);
+ }
+ handle = lt_dlopenext (name);
+ }
+ if (!handle)
+ {
+ err_fatal ("%s", lt_dlerror());
+ return NULL;
+ }
+
+ di->_init_with_ctrl = lt_dlsym (handle, "int_init_with_ctrl");
+ if (di->_init_with_ctrl)
+ {
+ di->_init = NULL;
+ }
+ else
+ {
+ di->_init = lt_dlsym (handle, "int_init");
+ }
+ di->_fini = lt_dlsym (handle, "int_fini");
+ di->_read = lt_dlsym (handle, "int_read");
+ di->_write = lt_dlsym (handle, "int_write");
+ di->_power = lt_dlsym (handle, "int_power_saving_mode");
+ di->_s_0 = lt_dlsym (handle, "function_s_0");
+ di->_s_1 = lt_dlsym (handle, "function_s_1");
+
+ if (!
+ ( (di->_init || di->_init_with_ctrl)
+ && di->_fini
+ && di->_read
+ && di->_write
+ && di->_s_0
+ && di->_s_1)
+ )
+ {
+ err_fatal ("failed to find all required interpreter API");
+ di->_init_with_ctrl = NULL;
+ di->_init = NULL;
+ di->_fini = NULL;
+ di->_read = NULL;
+ di->_write = NULL;
+ di->_power = NULL;
+ di->_s_0 = NULL;
+ di->_s_1 = NULL;
+ lt_dlclose (handle);
+ return NULL;
+ }
+
+ /* FIXME: if we are still here, we should set up the alarm stuff
+ here because _load is really a sort of ctor. Note that open
+ should disarm the alarm and close should re-arm it. Our dtor
+ should take care of disarming the alarm permanently. */
+ /* FIXME: add error handling */
+ { /* set up alarm for power saving */
+#ifdef USE_ALARM
+ struct sigaction act;
+
+ /* FIXME: set act.sa_sigaction instead because that can hold a
+ void (*) (int, siginfo_t *, void *) so we can pass arguments
+ such as device->interpreter->_device(!) to it, perhaps. */
+ act.sa_handler = timer_event;
+ sigemptyset (&act.sa_mask);
+ act.sa_flags = 0;
+
+ sigaction (SIGALRM, &act, 0);
+
+ alarm (TICK_INTERVAL);
+ log_info ("alarm (%d)", TICK_INTERVAL);
+#endif
+ di->_tick_count = 0;
+ }
+ return handle;
+}
+
+/*
+ This functor is called by sane_read only.
+ */
+SANE_Status
+_ftor0 (device_type *device, SANE_Parameters *params,
+ SANE_Byte *ptr, SANE_Byte *end)
+{
+ if (!device || !device->interpreter || !params)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (params->depth != 1
+ && device->interpreter->_table && device->interpreter->_buffer)
+ {
+ int i, row;
+
+ row = (end - ptr) / params->bytes_per_line;
+
+ for (i = 0; i < row; ++i)
+ {
+ memcpy (device->interpreter->_buffer, ptr + i * params->bytes_per_line,
+ params->bytes_per_line);
+ device->interpreter->_s_1 (device->interpreter->_buffer,
+ ptr + i * params->bytes_per_line,
+ params->pixels_per_line,
+ params->format == SANE_FRAME_RGB,
+ device->interpreter->_table);
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ This functor is called by sane_start only.
+ */
+SANE_Status
+_ftor1 (device_type *device, SANE_Parameters *params,
+ int depth, int left, int x_dpi, int optical_res)
+{
+ if (!device || !device->interpreter || !params)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ device->interpreter->free (device);
+
+ if (depth != 1)
+ {
+ device->interpreter->_table =
+ t_malloc (params->pixels_per_line, double);
+
+ if (!device->interpreter->_table)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (device->interpreter->_s_0 (left, params->pixels_per_line,
+ x_dpi, optical_res,
+ device->interpreter->_table))
+ {
+ device->interpreter->_buffer
+ = t_malloc (params->bytes_per_line, SANE_Byte);
+
+ if (!device->interpreter->_buffer)
+ {
+ delete (device->interpreter->_table);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ else
+ {
+ delete (device->interpreter->_table);
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
diff --git a/backend/epkowa_ip.h b/backend/epkowa_ip.h
new file mode 100644
index 0000000..cc0fe84
--- /dev/null
+++ b/backend/epkowa_ip.h
@@ -0,0 +1,124 @@
+/* epkowa_ip.h -- for scanners that don't speak EPSON ESC/I natively
+ Copyright (C) 2005 Olaf Meeuwissen
+ Copyright (C) 2009 SEIKO EPSON CORPORATION
+
+ This file is part of the EPKOWA SANE backend.
+ It declares a wrapper around the backend's "Interpreter" interface.
+
+ The EPKOWA SANE backend 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 FITNESS
+ FOR A PARTICULAR PURPOSE or MERCHANTABILITY.
+ See the GNU General Public License for more details.
+
+ You should have received a verbatim copy of the GNU General Public
+ License along with this program; if not, write to:
+
+ Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330
+ Boston, MA 02111-1307 USA
+
+ Linking the EPKOWA SANE backend statically or dynamically with
+ other modules is making a combined work based on the EPKOWA SANE
+ backend. Thus, the terms and conditions of the GNU General Public
+ License cover the whole combination.
+
+ As a special exception, the copyright holders of the EPKOWA SANE
+ backend give you permission to link the EPKOWA SANE backend with
+ independent modules that communicate with the EPKOWA SANE backend
+ solely through the "Interpreter" interface, regardless of the
+ license terms of these independent modules, and to copy and
+ distribute the resulting combined work under terms of your choice,
+ provided that every copy of the combined work is accompanied by a
+ complete copy of the source code of the EPKOWA SANE backend (the
+ version of the EPKOWA SANE backend used to produce the combined
+ work), being distributed under the terms of the GNU General Public
+ License plus this exception. An independent module is a module
+ which is not derived from or based on the EPKOWA SANE backend.
+
+ Note that people who make modified versions of the EPKOWA SANE
+ backend are not obligated to grant this special exception for their
+ modified versions; it is their choice whether to do so. The GNU
+ General Public License gives permission to release a modified
+ version without this exception; this exception also makes it
+ possible to release a modified version which carries forward this
+ exception.
+ */
+
+#ifndef epkowa_ip_h_included
+#define epkowa_ip_h_included
+
+#include <ltdl.h>
+
+#include "epkowa.h"
+#include "epkowa_ip_api.h"
+
+/* Insulate the interpreter_type declaration against changes.
+ */
+typedef channel device_type;
+
+
+/*! An object-like type for ESC/I interpreters.
+ */
+struct interpreter_type
+{
+ /* public members */
+
+ SANE_Status (*dtor) (device_type *device);
+
+ int (*open) (device_type *device);
+ int (*close) (device_type *device);
+
+ ssize_t (*recv) (device_type *device, void *buffer, size_t size,
+ SANE_Status *status);
+ ssize_t (*send) (device_type *device, const void *buffer, size_t size,
+ SANE_Status *status);
+
+ /* FIXME: very questionable public API for an _interpreter_ as this
+ seems to do some kind of image processing -> suggest to refactor
+ this out into a separate object */
+
+ SANE_Status (*free) (device_type *device);
+
+ SANE_Status (*ftor0) (device_type *device, SANE_Parameters *params,
+ SANE_Byte *ptr, SANE_Byte *end);
+ SANE_Status (*ftor1) (device_type *device, SANE_Parameters *params,
+ int depth, int left, int x_dpi, int optical_res);
+
+
+ /* private members */
+
+ device_type *_device;
+ void *_module;
+
+ int _tick_count;
+
+ double *_table;
+ SANE_Byte *_buffer;
+
+ /* hooks for all API declared in epkowa_ip_api.h */
+
+ bool (*_init) (int fd, io_callback *read, io_callback *write);
+ bool (*_init_with_ctrl) (int fd, io_callback *read, io_callback *write,
+ ctrl_callback *ctrl);
+ void (*_fini) (void);
+ int (*_read) (void *buf, size_t size);
+ int (*_write) (void *buf, size_t size);
+ void (*_power) (void);
+ int (*_s_0) (unsigned int offset, unsigned int width,
+ unsigned int resolution, unsigned int opt_resolution,
+ double *table);
+ void (*_s_1) (uint8_t *in_buf, uint8_t *out_buf,
+ unsigned int width, bool color, double *table);
+};
+
+/*! An abstract, factory-like ctor for interpreters.
+ */
+SANE_Status create_interpreter (device_type *device,
+ unsigned int usb_product_id);
+
+#endif /* !epkowa_ip_h_included */
diff --git a/backend/epkowa_ip_api.h b/backend/epkowa_ip_api.h
new file mode 100644
index 0000000..0e3f840
--- /dev/null
+++ b/backend/epkowa_ip_api.h
@@ -0,0 +1,92 @@
+/* epkowa_ip_api.h -- for scanners that don't speak EPSON ESC/I natively
+ Copyright (C) 2005 SEIKO EPSON CORPORATION
+
+ This file is part of the EPKOWA SANE backend.
+ It declares the backend's "Interpreter" interface.
+
+ The EPKOWA SANE backend 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 FITNESS
+ FOR A PARTICULAR PURPOSE or MERCHANTABILITY.
+ See the GNU General Public License for more details.
+
+ You should have received a verbatim copy of the GNU General Public
+ License along with this program; if not, write to:
+
+ Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330
+ Boston, MA 02111-1307 USA
+
+ Linking the EPKOWA SANE backend statically or dynamically with
+ other modules is making a combined work based on the EPKOWA SANE
+ backend. Thus, the terms and conditions of the GNU General Public
+ License cover the whole combination.
+
+ As a special exception, the copyright holders of the EPKOWA SANE
+ backend give you permission to link the EPKOWA SANE backend with
+ independent modules that communicate with the EPKOWA SANE backend
+ solely through the "Interpreter" interface, regardless of the
+ license terms of these independent modules, and to copy and
+ distribute the resulting combined work under terms of your choice,
+ provided that every copy of the combined work is accompanied by a
+ complete copy of the source code of the EPKOWA SANE backend (the
+ version of the EPKOWA SANE backend used to produce the combined
+ work), being distributed under the terms of the GNU General Public
+ License plus this exception. An independent module is a module
+ which is not derived from or based on the EPKOWA SANE backend.
+
+ Note that people who make modified versions of the EPKOWA SANE
+ backend are not obligated to grant this special exception for their
+ modified versions; it is their choice whether to do so. The GNU
+ General Public License gives permission to release a modified
+ version without this exception; this exception also makes it
+ possible to release a modified version which carries forward this
+ exception.
+ */
+
+#ifndef epkowa_ip_api_h_included
+#define epkowa_ip_api_h_included
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "defines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef ssize_t io_callback (void *buffer, size_t length);
+typedef ssize_t ctrl_callback (size_t request_type, size_t request,
+ size_t value, size_t index,
+ size_t size, void *buffer);
+
+bool int_init (int fd, io_callback *read, io_callback *write);
+bool int_init_with_ctrl (int fd, io_callback *read, io_callback *write,
+ ctrl_callback *ctrl);
+void int_fini (void);
+
+int int_read (void *buffer, size_t length);
+int int_write (void *buffer, size_t length);
+
+void int_power_saving_mode (void);
+
+int function_s_0 (unsigned int offset,
+ unsigned int width,
+ unsigned int resolution,
+ unsigned int opt_resolution,
+ double * table);
+
+void function_s_1 (uint8_t *in_buf, uint8_t *out_buf,
+ unsigned int width, bool color, double *table);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !epkowa_ip_api_h_included */
diff --git a/backend/epkowa_scsi.c b/backend/epkowa_scsi.c
new file mode 100644
index 0000000..9132507
--- /dev/null
+++ b/backend/epkowa_scsi.c
@@ -0,0 +1,103 @@
+#include "epkowa_scsi.h"
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h> /* for memset and memcpy */
+
+/*
+ * sense handler for the sanei_scsi_XXX comands
+ */
+SANE_Status
+sanei_epson_scsi_sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ /* to get rid of warnings */
+ scsi_fd = scsi_fd;
+ arg = arg;
+
+ if (result[0] && result[0] != 0x70)
+ {
+ log_data ("SCSI sense code = 0x%02x", result[0]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ return SANE_STATUS_GOOD;
+ }
+}
+
+/*
+ *
+ *
+ */
+SANE_Status
+sanei_epson_scsi_inquiry (int fd, int page_code, void *buf, size_t * buf_size)
+{
+ u_char cmd[6];
+ int status;
+
+ memset (cmd, 0, 6);
+ cmd[0] = INQUIRY_COMMAND;
+ cmd[2] = page_code;
+ cmd[4] = *buf_size > 255 ? 255 : *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof cmd, buf, buf_size);
+
+ return status;
+}
+
+/*
+ *
+ *
+ */
+int
+sanei_epson_scsi_read (int fd, void *buf, size_t buf_size,
+ SANE_Status * status)
+{
+ u_char cmd[6];
+
+ memset (cmd, 0, 6);
+ cmd[0] = READ_6_COMMAND;
+ cmd[2] = buf_size >> 16;
+ cmd[3] = buf_size >> 8;
+ cmd[4] = buf_size;
+
+ if (SANE_STATUS_GOOD ==
+ (*status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, &buf_size)))
+ return buf_size;
+
+ return 0;
+}
+
+/*
+ *
+ *
+ */
+int
+sanei_epson_scsi_write (int fd, const void *buf, size_t buf_size,
+ SANE_Status * status)
+{
+ u_char *cmd;
+
+ cmd = t_alloca (8 + buf_size, u_char);
+ memset (cmd, 0, 8);
+ cmd[0] = WRITE_6_COMMAND;
+ cmd[2] = buf_size >> 16;
+ cmd[3] = buf_size >> 8;
+ cmd[4] = buf_size;
+ memcpy (cmd + 8, buf, buf_size);
+
+ if (SANE_STATUS_GOOD ==
+ (*status = sanei_scsi_cmd2 (fd, cmd, 6, cmd + 8, buf_size, NULL, NULL)))
+ return buf_size;
+
+ return 0;
+}
diff --git a/backend/epkowa_scsi.h b/backend/epkowa_scsi.h
new file mode 100644
index 0000000..8328c76
--- /dev/null
+++ b/backend/epkowa_scsi.h
@@ -0,0 +1,28 @@
+#ifndef _EPSON_SCSI_H_
+#define _EPSON_SCSI_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "epkowa.h"
+#include "sane/sanei_scsi.h"
+
+#define TEST_UNIT_READY_COMMAND (0x00)
+#define READ_6_COMMAND (0x08)
+#define WRITE_6_COMMAND (0x0a)
+#define INQUIRY_COMMAND (0x12)
+#define TYPE_PROCESSOR (0x03)
+
+#define INQUIRY_BUF_SIZE (36)
+
+SANE_Status sanei_epson_scsi_sense_handler (int scsi_fd, u_char * result,
+ void *arg);
+SANE_Status sanei_epson_scsi_inquiry (int fd, int page_code, void *buf,
+ size_t * buf_size);
+int sanei_epson_scsi_read (int fd, void *buf, size_t buf_size,
+ SANE_Status * status);
+int sanei_epson_scsi_write (int fd, const void *buf, size_t buf_size,
+ SANE_Status * status);
+
+#endif
diff --git a/backend/extension.h b/backend/extension.h
new file mode 100644
index 0000000..b8daad2
--- /dev/null
+++ b/backend/extension.h
@@ -0,0 +1,177 @@
+/* extension.h -- types to handle the various scanner document sources
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef included_source_h
+#define included_source_h
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+/*! Base "class" for hardware extensions.
+
+ This "class" provides the common part of the other extension types.
+
+ \sa struct _fbf_extension, fbf_extension
+ \sa struct _adf_extension, adf_extension
+ \sa struct _tpu_extension, tpu_extension
+ */
+struct _extension
+{
+ SANE_Byte status;
+
+ SANE_Range x_range; /* in mm */
+ SANE_Range y_range;
+
+ SANE_Int max_x; /* in pixels */
+ SANE_Int max_y;
+
+ double doc_x; /* in mm */
+ double doc_y;
+ SANE_Bool has_size_check;
+};
+typedef struct _extension extension;
+
+
+/*! Flatbed hardware "extension".
+
+ \sa struct _extension, extension
+ */
+struct _fbf_extension
+{
+ SANE_Byte status;
+
+ SANE_Range x_range; /* in mm */
+ SANE_Range y_range;
+
+ SANE_Int max_x; /* in pixels */
+ SANE_Int max_y;
+
+ double doc_x; /* in mm */
+ double doc_y;
+ SANE_Bool has_size_check;
+};
+typedef struct _fbf_extension fbf_extension;
+
+
+/*! Auto Document Feeder (ADF) hardware extension.
+
+ \sa struct _extension, extension
+ */
+struct _adf_extension
+{
+ SANE_Byte status;
+
+ SANE_Range x_range; /* in mm */
+ SANE_Range y_range;
+
+ SANE_Int max_x; /* in pixels */
+ SANE_Int max_y;
+
+ double doc_x; /* in mm */
+ double doc_y;
+ SANE_Bool has_size_check;
+
+ SANE_Byte ext_status;
+
+ unsigned int sheet_count;
+ SANE_Bool using_duplex;
+
+ SANE_Bool auto_eject;
+};
+typedef struct _adf_extension adf_extension;
+
+
+/*! TransParency Unit (TPU) hardware extension.
+
+ \sa struct _extension, extension
+ */
+struct _tpu_extension
+{
+ SANE_Byte status;
+
+ SANE_Range x_range; /* in mm */
+ SANE_Range y_range;
+
+ SANE_Int max_x; /* in pixels */
+ SANE_Int max_y;
+
+ double doc_x; /* in mm */
+ double doc_y;
+ SANE_Bool has_size_check;
+
+ SANE_Bool has_focus;
+ SANE_Bool use_focus;
+};
+typedef struct _tpu_extension tpu_extension;
+
+
+/*! Convenience macro to test whether \a ext is currently being used.
+ */
+#define using(hw,ext) \
+ ((hw) && ((hw)->src == (const extension *) (hw)->ext))
+
+
+#endif /* !defined (included_source_h) */
diff --git a/backend/get-infofile.c b/backend/get-infofile.c
new file mode 100644
index 0000000..79d8c98
--- /dev/null
+++ b/backend/get-infofile.c
@@ -0,0 +1,277 @@
+/* get-infofile.c --
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: SEIKO EPSON CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#include <errno.h>
+#include <ltdl.h>
+#include <string.h>
+
+#include "get-infofile.h"
+#include "profile.c"
+
+const struct ScannerData scanner_data[] = {
+ {"GT-10000", 0x05, 0x05, {NULL, NULL}},
+ {"ES-6000", 0x05, 0x05, {NULL, NULL}},
+ {"Perfection610", 0x06, 0x01, {"Perfection 610", NULL}},
+ {"GT-6600", 0x06, 0x01, {NULL, NULL}},
+ {"Perfection1200", 0x07, 0x01, {"Perfection 1200", NULL}},
+ {"GT-7600", 0x07, 0x01, {NULL, NULL}},
+ {"Expression1600", 0x0D, 0x02, {"Expression 1600", NULL}},
+ {"ES-2000", 0x0D, 0x02, {NULL, NULL}},
+ {"Expression1640XL", 0x0F, 0x04, {"Expression 1640XL", NULL}},
+ {"ES-8500", 0x0F, 0x04, {NULL, NULL}},
+ {"Perfection640", 0x15, 0x01, {"Perfection 640", NULL}},
+ {"GT-6700", 0x15, 0x01, {NULL, NULL}},
+ {"Perfection1640", 0x16, 0x01, {"Perfection 1640", NULL}},
+ {"GT-8700", 0x16, 0x01, {NULL, NULL}},
+ {"Perfection1240", 0x18, 0x01, {"Perfection 1240", NULL}},
+ {"GT-7700", 0x18, 0x01, {NULL, NULL}},
+ {"GT-30000", 0x1A, 0x05, {NULL, NULL}},
+ {"ES-9000H", 0x1A, 0x05, {NULL, NULL}},
+ {"Expression1680", 0x1B, 0x02, {"Expression 1680", NULL}},
+ {"ES-2200", 0x1B, 0x02, {NULL, NULL}},
+ {"GT-7200", 0x1D, 0x01, {"Perfection 1250", "GT-7200"}},
+ {"GT-8200", 0x1F, 0x01, {"Perfection 1650", "GT-8200"}},
+ {"GT-9700", 0x21, 0x01, {"Perfection 2450", "GT-9700"}},
+ {"GT-7300", 0x23, 0x01, {"Perfection 1260", "GT-7300"}},
+ {"GT-8300", 0x25, 0x01, {"Perfection 1660", "GT-8300"}},
+ {"GT-9300", 0x27, 0x01, {"Perfection 2400", "GT-9300"}},
+ {"GT-9800", 0x29, 0x01, {"Perfection 3200", "GT-9800"}},
+ {"ES-7000H", 0x2B, 0x05, {"GT-15000", "ES-7000H"}},
+ {"LP-A500", 0x51, 0x01, {NULL, NULL}},
+ {"AL-CX11", 0x51, 0x01, {"AcuLaser CX11", NULL}},
+ {"GT-9400", 0x32, 0x06, {"Perfection 3170", "GT-9400"}},
+ {"CC-600PX", 0x2D, 0x01, {"Stylus CX5100/CX5200", "CC-600PX"}},
+ {"PM-A850", 0x3A, 0x01, {"Stylus Photo RX600", "PM-A850"}},
+ {"CX5400", 0x36, 0x01, {"Stylus CX5300/CX5400", NULL}},
+ {"GT-X700", 0x34, 0x01, {"Perfection 4870", "GT-X700"}},
+ {"RX500", 0x38, 0x01, {"Stylus Photo RX500/RX510", NULL}},
+ {"PX-A650", 0x37, 0x01, {"Stylus CX6300/CX6400", NULL}},
+ {"ES-10000G", 0x3F, 0x05, {NULL, NULL}},
+ {"Expression10000", 0x3F, 0x05, {"Expression 10000XL", NULL}},
+ {"CX4600", 0x46, 0x01, {"Stylus CX4500/CX4600", NULL}},
+ {"CX6600", 0x49, 0x01, {"Stylus CX6500/CX6600", NULL}},
+ {"CX3600", 0x46, 0x01, {"Stylus CX3500/CX3600/CX3650", "PX-A550"}},
+ {"RX420", 0x48, 0x01, {"Stylus Photo RX420/RX425/RX430", NULL}},
+ {"PM-A700", 0x48, 0x01, {NULL, NULL}},
+ {"PM-A870", 0x4B, 0x01, {"Stylus Photo RX620/RX630", "PM-A870"}},
+ {"GT-F500", 0x41, 0x06, {"Perfection 2480/2580", "GT-F500/F550"}},
+ {"GT-F600", 0x43, 0x06, {"Perfection 4180", "GT-F600"}},
+ {"PM-A900", 0x4D, 0x01, {"Stylus Photo RX700", "PM-A900"}},
+ {"GT-X800", 0x4F, 0x01, {"Perfection 4990", "GT-X800"}},
+ {"GT-X750", 0x54, 0x01, {"Perfection 4490", "GT-X750"}},
+ {"LP-M5500", 0x56, 0x05, {NULL, NULL}},
+ {"LP-M5600", 0x78, 0x05, {NULL, "LP-M5600"}},
+ {"GT-F520", 0x52, 0x01, {"Perfection 3490/3590", "GT-F520/F570"}},
+ {"CX3800", 0x57, 0x01, {"Stylus CX3700/CX3800/DX3800", NULL}},
+ {"CX7800", 0x5B, 0x01, {"Stylus CX7700/CX7800", NULL}},
+ {"PM-A750", 0x5D, 0x01, {"Stylus Photo RX520/RX530", "PM-A750"}},
+ {"CX4800", 0x58, 0x01, {"Stylus CX4700/CX4800/DX4800", "PX-A650"}},
+ {"CX4200", 0x59, 0x01, {"Stylus CX4100/CX4200/DX4200", NULL}},
+ {"PM-A950", 0x61, 0x01, {NULL, "PM-A950"}},
+ {"PM-A890", 0x5F, 0x01, {"Stylus Photo RX640/RX650", "PM-A890"}},
+ {"GT-X900", 0x63, 0x01, {"Perfection V700/V750", "GT-X900"}},
+ {"CX4000", 0x6B, 0x01, {"Stylus CX3900/DX4000", "PX-A620"}},
+ {"CX3000v", 0x6A, 0x01, {"Stylus CX2800/CX2900/ME 200", NULL}},
+ {"ES-H300", 0x65, 0x01, {"GT-2500", "ES-H300"}},
+ {"CX6000", 0x6C, 0x01, {"Stylus CX5900/CX6000/DX6000", "PX-A720"}},
+ {"PM-A820", 0x70, 0x01, {"Stylus Photo RX560/RX580/RX590", "PM-A820"}},
+ {"PM-A920", 0x71, 0x01, {NULL, "PM-A920"}},
+ {"PM-A970", 0x73, 0x01, {NULL, "PM-A970"}},
+ {"PM-T990", 0x75, 0x01, {NULL, "PM-T990"}},
+ {"CX5000", 0x77, 0x01, {"Stylus CX4900/CX5000/DX5000", NULL}},
+ {"GT-S600", 0x66, 0x01, {"Perfection V10/V100", "GT-S600/GT-F650"}},
+ {"GT-F700", 0x68, 0x01, {"Perfection V350", "GT-F700"}},
+ {"AL-CX21", 0x79, 0x01, {"AcuLaser CX21", NULL}},
+ {"GT-F670", 0x7A, 0x01, {"Perfection V200", "GT-F670"}},
+ {"GT-X770", 0x7C, 0x06, {"Perfection V500", "GT-X770"}},
+ {"CX4400", 0x7E, 0x01, {"Stylus CX4300/CX4400/CX5500/CX5600/DX4400", NULL}},
+ {"CX7400", 0x7F, 0x01, {"Stylus CX7300/CX7400/DX7400", "PX-A640"}},
+ {"CX8400", 0x80, 0x01, {"Stylus CX8300/CX8400/DX8400", "PX-A740"}},
+ {"CX9400Fax", 0x81, 0x01, {"Stylus CX9300F/CX9400Fax/DX9400F", "PX-FA700"}},
+ {"PM-T960", 0x82, 0x01, {NULL, "PM-T960"}},
+ {"PM-A940", 0x84, 0x01, {"Stylus Photo RX680/RX685/RX690", "PM-A940"}},
+ {"PM-A840", 0x85, 0x01, {"Stylus Photo RX585/RX595/RX610", "PM-A840/PM-A840S"}},
+ {"GT-D1000", 0x86, 0x01, {"GT-1500", "GT-D1000"}},
+ {"GT-X970", 0x87, 0x01, {NULL, NULL}},
+ {"LP-M5000", 0x97, 0x01, {NULL, NULL}},
+ {"LP-M6000", 0x89, 0x01, {NULL, NULL}},
+ {"ES-H7200", 0x8A, 0x05, {NULL, NULL}},
+ {"GT-20000", 0x8A, 0x05, {NULL, NULL}},
+ {"NX200", 0x8D, 0x01, {"Stylus NX200/SX200/TX200", NULL}},
+ {"NX400", 0x8E, 0x01, {"Stylus NX400/SX400/TX400", "PX-501A"}},
+ {"NX100", 0x93, 0x01, {"Stylus NX100/SX100/TX100/ME 300", "PX-401A"}},
+ {"NX300", 0x8F, 0x01, {"Stylus BX300F/TX300F/NX300/ME Office 600F", NULL}},
+ {"WorkForce 600", 0x90, 0x01, {"Stylus BX600FW/SX600FW/TX600FW/ME Office 700FW/WorkForce 600", "PX-601F"}},
+ {"Artisan 800", 0x91, 0x01, {"Stylus Photo PX800FW/TX800FW/Artisan 800", "EP-901A/EP-901F"}},
+ {"Artisan 700", 0x92, 0x01, {"Stylus Photo PX700W/TX700W/Artisan 700", "EP-801A"}},
+ {"WorkForce 500", 0x96, 0x01, {NULL, NULL}},
+ {"GT-F720", 0x8B, 0x01, {"Perfection V300", "GT-F720"}},
+ {"GT-S620", 0x8B, 0x01, {"Perfection V30", "GT-S620"}},
+ {"GT-S50", 0x00, 0x01, {"GT-S50", "ES-D200"}},
+ {"GT-S80", 0x00, 0x01, {"GT-S80", "ES-D400"}},
+ {"PID 0851", 0x98, 0x01, {"Stylus NX410/SX410/TX410 Series", NULL}},
+ {"PID 084D", 0x99, 0x01, {"Stylus NX110/SX110/TX110 Series", "PX-402A"}},
+ {"PID 084F", 0x9A, 0x01, {"Stylus NX210/SX210/TX210/ME OFFICE 510 Series", NULL}},
+ {"PID 0854", 0x9B, 0x01, {"Stylus Office TX510FN/BX310FN/WorkForce 310/ME OFFICE 650FN Series", NULL}},
+ {"PID 0856", 0x9C, 0x01, {"Stylus NX510/SX510W/TX550W Series", "PX-502A"}},
+ {"PID 0855", 0x9D, 0x01, {"Stylus Office TX610FW/BX610FW/SX610FW/WorkForce 610 Series", "PX-602F"}},
+ {"PID 0850", 0x9E, 0x01, {"Stylus Photo PX650/TX650 Series", "EP-702A"}},
+ {"PID 0852", 0xA0, 0x01, {"Stylus Photo TX710W/PX710W/Artisan 710 Series", "EP-802A"}},
+ {"PID 0853", 0x9F, 0x01, {"Stylus Photo PX810FW/Artisan 810 Series", "EP-902A"}},
+ {"GT-X820", 0xA1, 0x01, {"Perfection V600 Photo", "GT-X820"}},
+ {"GT-S55", 0x00, 0x01, {"GT-S55", NULL}},
+ {"GT-S85", 0x00, 0x01, {"GT-S85", "ES-D350"}},
+
+ /* array terminator */
+ {NULL, 0x00, 0x00, {NULL, NULL}},
+};
+
+
+static struct EpsonScanCommand scan_command[] = {
+ {0x01, 0, 0, ILLEGAL_CMD},
+ {0x02, ILLEGAL_CMD, 0, ILLEGAL_CMD},
+ {0x03, 0, ILLEGAL_CMD, ILLEGAL_CMD},
+ {0x04, ILLEGAL_CMD, ILLEGAL_CMD, ILLEGAL_CMD},
+ {0x05, 0, 0x19, ILLEGAL_CMD},
+ {0x06, 0, 0, '\f'},
+};
+
+static
+const scanner_data_t *
+get_scanner (const char *fw_name)
+{
+ const scanner_data_t *data = scanner_data;
+ require (data);
+ { /* input validation*/
+ if (!fw_name || 0 == strlen (fw_name))
+ return NULL;
+ }
+
+ while (data->fw_name != NULL /* end of array */
+ && (0 != strcmp (data->fw_name, fw_name)))
+ {
+ ++data;
+ }
+
+ if(!data->fw_name){
+ err_major("Unknown model name.");
+ return NULL;
+ }
+
+ return data;
+}
+
+char *
+get_scanner_data(const char* fw_name, const char *name)
+{
+ const scanner_data_t *data = get_scanner (fw_name);
+
+ if (!data) return NULL;
+
+ if(strcmp(name, FIRMWARE) == 0) return data->fw_name;
+ else if(strcmp(name, MODEL_OVERSEAS) == 0) return data->name.overseas;
+ else if(strcmp(name, MODEL_JAPAN) == 0) return data->name.japan;
+
+ return NULL;
+}
+
+struct EpsonScanCommand *get_scan_command(const char *fw_name)
+{
+ const scanner_data_t *data = get_scanner (fw_name);
+ scan_command_t *custom_command = NULL;
+
+ if (data && data->command_ID)
+ {
+ int id = data->command_ID - 1; /* adjust for offset */
+ require (id >= 0);
+ require ((unsigned) id < num_of (scan_command));
+
+ custom_command = &scan_command[id];
+ }
+ else
+ {
+ custom_command = &scan_command[0];
+ }
+
+ return custom_command;
+}
+
+EpsonScanHard get_epson_scan_hard (const char *fw_name)
+{
+ const scanner_data_t *data = get_scanner (fw_name);
+ EpsonScanHard profile = NULL;
+
+ int i = 0;
+
+ if (data && data->profile_ID)
+ {
+ i = num_of (_epson_scan_hard);
+
+ while (--i && data->profile_ID != epson_scan_hard[i].modelID)
+ ;
+ }
+ profile = (EpsonScanHard) &epson_scan_hard[i];
+
+ return profile;
+}
diff --git a/backend/get-infofile.h b/backend/get-infofile.h
new file mode 100644
index 0000000..d3daf80
--- /dev/null
+++ b/backend/get-infofile.h
@@ -0,0 +1,91 @@
+/* get-infofile.h --
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: SEIKO EPSON CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef included_get_infofile_h
+#define included_get_infofile_h
+
+#include "hw-data.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*----------ã‚¿ã‚°----------------*/
+#define FIRMWARE "firmware name"
+#define MODEL_OVERSEAS "model overseas"
+#define MODEL_JAPAN "model japan"
+/*-----------------------------*/
+
+char *get_scanner_data(const char* fw_name, const char* name);
+
+struct EpsonScanCommand *get_scan_command(const char *fw_name);
+
+EpsonScanHard get_epson_scan_hard (const char *fw_name);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/backend/hw-data.c b/backend/hw-data.c
new file mode 100644
index 0000000..2f8aa39
--- /dev/null
+++ b/backend/hw-data.c
@@ -0,0 +1,372 @@
+/* hw-data.c -- selected hardware specific data
+ * Copyright (C) 2004--2016 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "hw-data.h"
+
+#include <ctype.h>
+#include <locale.h>
+#include <stddef.h>
+#include <string.h>
+#include <time.h>
+
+
+#include "command.h"
+
+#include "utils.h"
+
+/* Not all scanners report the model name that is printed on the case.
+ Here we try to adjust for that by second guessing the name received
+ from the hardware with the aid of some environmental data.
+ WARNING: this is not and can not be foolproof.
+
+ The caller gets to manage the memory occupied by the string that is
+ returned.
+ */
+
+/*! Returns a copy of the F/W name of the device on the other end of a channel.
+ */
+char *
+get_fw_name (channel *ch)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ const byte cmd[] = { ESC, 'f' };
+ char *fw_name = NULL;
+
+ if (!ch) return NULL;
+
+ channel_send (ch, cmd, num_of (cmd), &status);
+ if (SANE_STATUS_GOOD == status)
+ {
+ byte info[4];
+
+ channel_recv (ch, info, num_of (info), &status);
+ if (SANE_STATUS_GOOD == status)
+ {
+ char reply[42+1]; /* extra byte to enable string
+ termination */
+ channel_recv (ch, reply, num_of (reply) - 1, &status);
+ if (SANE_STATUS_GOOD == status)
+ {
+ char *lc_ctype = setlocale (LC_CTYPE, "C");
+ size_t n = num_of (reply);
+
+ do
+ {
+ --n, reply[n] = '\0';
+ }
+ while (26 < n && (isspace (reply[n-1]) || '\0' == reply[n-1]));
+ /* workaround for non-compliant
+ interpreter firmware names that
+ are padded with NULs and spaces
+ */
+ fw_name = strdup (reply + 26);
+ setlocale (LC_CTYPE, lc_ctype);
+ }
+ }
+ }
+ if (SANE_STATUS_GOOD != status)
+ err_minor ("%s", sane_strstatus (status));
+
+ /* Devices with USB product IDs 0x085C and 0x0883 may report the
+ * same firmware name. That breaks our data file finding scheme
+ * so we fix up the firmware name here to be spec compliant.
+ */
+ if (0 == strcmp_c ("PID 085C", fw_name)
+ && (CHAN_USB == ch->type && 0x0883 == ch->id))
+ {
+ strcpy (fw_name, "PID 0883");
+ }
+
+ return fw_name;
+}
+
+static SANE_Bool
+_is_listed (const char *needle, const char **haystack)
+{
+ if (!needle || !haystack) return SANE_FALSE;
+
+ while (*haystack)
+ {
+ if (0 == strcmp_c (needle, *haystack))
+ return SANE_TRUE;
+ ++haystack;
+ }
+ return SANE_FALSE;
+}
+
+
+SANE_Bool
+adf_needs_manual_centering (const device *hw)
+{
+ const char *fw_names[] = {
+ "LP-M6000",
+ "LP-M5000",
+ "LP-M5300",
+ "LP-M8040",
+ "LP-M8170",
+ "ES-H300",
+ "CX9400Fax",
+ "PID 087C",
+ "GT-S80",
+ "GT-S50",
+ "GT-S85",
+ "GT-S55",
+ NULL
+ };
+
+ require (using (hw, adf));
+ return _is_listed (hw->fw_name, fw_names);
+}
+
+
+SANE_Bool
+adf_has_auto_form_feed (const device *hw)
+{
+ const char *fw_names[] = {
+ "LP-M6000",
+ "LP-M5000",
+ "LP-M5300",
+ NULL
+ };
+
+ require (using (hw, adf));
+ return _is_listed (hw->fw_name, fw_names)
+ || (FSI_CAP_AFF & hw->fsi_cap_2);
+}
+
+SANE_Bool
+adf_early_paper_end_kills_scan (const device *hw)
+{
+ const char *fw_names[] = {
+ "ES-10000G",
+ "ES-7000H",
+ "ES-H7200",
+ "Expression10000",
+ "GT-20000",
+ NULL,
+ };
+
+ require (using (hw, adf));
+ return _is_listed (hw->fw_name, fw_names);
+}
+
+SANE_Bool
+push_button_is_black_listed (const device *hw)
+{
+ const char *fw_names[] = {
+ "LP-M6000",
+ "LP-M5000",
+ "LP-M5300",
+ NULL,
+ };
+
+ // whitelist of scanners that support push button via the network
+ const char *fw_names_net[] = {
+ "ES-H7200",
+ "GT-20000",
+ NULL,
+ };
+
+
+ return _is_listed (hw->fw_name, fw_names)
+ || hw->uses_locking
+ || (CHAN_NET == hw->channel->type &&
+ !_is_listed (hw->fw_name, fw_names_net));
+}
+
+SANE_Int
+large_res_kills_adf_scan (const device *hw)
+{
+ const char *fw_names1[] = {
+ "ES-H300",
+ "LP-M6000",
+ "LP-M5000",
+ "LP-M5300",
+ "LP-M8040",
+ "LP-M8170",
+ NULL,
+ };
+
+ const char *fw_names2[] = {
+ "NX300",
+ "WorkForce 600",
+ "Artisan 800",
+ NULL,
+ };
+
+ require (hw->adf);
+ if (_is_listed (hw->fw_name, fw_names1)) return 600;
+ if (_is_listed (hw->fw_name, fw_names2)) return 1200;
+
+ return 0;
+}
+SANE_Bool
+zoom_kills_adf_scan (const device *hw)
+{
+ const char *fw_names[] = {
+ "LP-M6000",
+ "LP-M5000",
+ "LP-M5300",
+ NULL,
+ };
+
+ require (hw->adf);
+ return _is_listed (hw->fw_name, fw_names);
+}
+
+/* Say whether duplex scans scan front and back sides in the same
+ * direction. This is definitely the case for single pass duplex
+ * devices (which scan front and back simultaneously) but is only
+ * seldomly encountered with double pass duplexers (which scan the
+ * front and back one after another).
+ */
+SANE_Bool
+adf_duplex_direction_matches (const device *hw)
+{
+ const char *fw_names[] = {
+ "GT-S80",
+ "GT-S50",
+ "GT-S85",
+ "GT-S55",
+ NULL,
+ };
+
+ return hw->adf && _is_listed (hw->fw_name, fw_names);
+}
+
+/*! Return an override for the current source's max_y value (in pixels)
+ * to be used with autocropping. In case no override is needed, zero
+ * is returned.
+ *
+ * A number of models support scanning a slightly taller document than
+ * the firmware would have you believe. This "feature" is referred to
+ * as overscanning and used to minimize the risks of chopping off bits
+ * from skewed originals when performing autocropping.
+ *
+ * \todo Allow for model specific max_y values.
+ */
+SANE_Int
+autocrop_max_y (const device *hw)
+{
+ SANE_Int rv = 0;
+
+ const char *fw_names[] = {
+ "GT-S80",
+ "GT-S50",
+ "GT-S85",
+ "GT-S55",
+ NULL,
+ };
+
+ if (_is_listed (hw->fw_name, fw_names))
+ {
+ if (using (hw, adf))
+ {
+ rv = 15 * hw->base_res;
+ }
+ }
+ return rv;
+}
+
+/* Restrict functionality to tested devices only.
+ */
+SANE_Bool
+enable_dip_deskew (const device *hw)
+{
+ const char *fw_names[] = {
+ "GT-S80",
+ "GT-S50",
+ "GT-S85",
+ "GT-S55",
+ NULL,
+ };
+
+ return _is_listed (hw->fw_name, fw_names);
+}
+
+SANE_Bool
+push_button_needs_polling (const device *hw)
+{
+ const char *fw_names[] = {
+ "DS-30",
+ NULL,
+ };
+
+ return _is_listed (hw->fw_name, fw_names);
+}
+
+SANE_Bool
+maintenance_is_supported (const device *hw)
+{
+ const char *fw_names[] = {
+ "DS-30",
+ NULL,
+ };
+
+ return _is_listed (hw->fw_name, fw_names);
+}
diff --git a/backend/hw-data.h b/backend/hw-data.h
new file mode 100644
index 0000000..9ead25a
--- /dev/null
+++ b/backend/hw-data.h
@@ -0,0 +1,136 @@
+/* hw-data.h -- selected hardware specific data
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef hw_data_h_included
+#define hw_data_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "device.h"
+
+struct ScannerName
+{
+ char *overseas;
+ char *japan;
+};
+typedef struct ScannerName scanner_name_t;
+
+struct ScannerData
+{
+ char *fw_name;
+ int profile_ID;
+ int command_ID;
+ const scanner_name_t name;
+};
+typedef struct ScannerData scanner_data_t;
+
+#define ILLEGAL_CMD 0xFF
+
+struct EpsonScanCommand
+{
+ int command_ID;
+ unsigned char set_focus_position;
+ unsigned char feed;
+ unsigned char eject;
+
+ bool lock;
+ bool unlock;
+};
+typedef struct EpsonScanCommand scan_command_t;
+
+struct CapabilityData
+{
+ char *option;
+ char *mode;
+
+ long width;
+ long height;
+ long base;
+};
+typedef struct CapabilityData capability_data_t;
+
+char * get_fw_name (channel *ch);
+
+SANE_Bool adf_early_paper_end_kills_scan (const device *hw);
+SANE_Bool adf_has_auto_form_feed (const device *hw);
+SANE_Bool adf_needs_manual_centering (const device *hw);
+SANE_Bool push_button_is_black_listed (const device *hw);
+SANE_Int large_res_kills_adf_scan (const device *hw);
+SANE_Bool zoom_kills_adf_scan (const device *hw);
+SANE_Bool adf_duplex_direction_matches (const device *hw);
+SANE_Int autocrop_max_y (const device *hw);
+SANE_Bool enable_dip_deskew (const device *hw);
+SANE_Bool push_button_needs_polling (const device *hw);
+SANE_Bool maintenance_is_supported (const device *hw);
+
+/*! Array with colour correction profiles.
+
+ \todo Replace this with a data member in the scanner that gets
+ initialized with data read from file.
+ */
+extern const EpsonScanHardRec *epson_scan_hard;
+
+#endif /* !defined (hw_data_h_included) */
diff --git a/backend/ipc.c b/backend/ipc.c
new file mode 100644
index 0000000..e1d6437
--- /dev/null
+++ b/backend/ipc.c
@@ -0,0 +1,563 @@
+/* ipc.c -- inter-process communication (IPC) support
+ * Copyright (C) 2019 SEIKO EPSON Corporation
+ *
+ * License: EPSON END USER SOFTWARE LICENSE
+ * Author : SEIKO EPSON Corporation
+ *
+ * This file is part of Image Scan! for Linux.
+ * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE.
+ *
+ * You should have received a verbatim copy of the EPSON END USER SOFTWARE
+ * LICENSE along with the software.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ipc.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "defines.h"
+#include "message.h"
+
+
+/*! Attempts to read all the data up to \a size bytes.
+ * MERR is returned if an error, such as a timeout, occurs.
+ * MEOF is returned if EOF is reached.
+ * Otherwise, returns the number of bytes read, which must be greater than 0.
+ */
+static ssize_t
+recv_all (int fd, void *buf, size_t size)
+{
+ ssize_t n = 0;
+ ssize_t t = 1;
+
+ if (0 == size) return MERR;
+
+ while (n < size && t > 0)
+ {
+ errno = 0;
+ t = read (fd, buf + n, size - n);
+ if (0 > t)
+ {
+ err_major ("read failed: %s", strerror (errno));
+ return MERR;
+ }
+ else
+ {
+ n += t;
+ log_call ("transferred %zd bytes, total %zd/%zd", t, n, size);
+ }
+ if (0 == t) return MEOF;
+ }
+
+ return n;
+}
+
+/*! Attempts to write all the data up to \a size bytes.
+ * MERR is returned if an error, such as a timeout, occurs.
+ * Otherwise, returns the number of bytes written.
+ * A return value of zero indicates that nothing was written.
+ */
+static ssize_t
+send_all (int fd, const void *buf, size_t size)
+{
+ ssize_t n = 0;
+ ssize_t t = 1;
+
+ if (0 == size) return MERR;
+
+ while (n < size && t > 0)
+ {
+ errno = 0;
+ t = write (fd, buf + n, size - n);
+ if (0 > t)
+ {
+ err_major ("write failed: %s", strerror (errno));
+ return MERR;
+ }
+ else
+ {
+ n += t;
+ log_call ("transferred %zd bytes, total %zd/%zd", t, n, size);
+ }
+ }
+
+ return n;
+}
+
+/*! MERR is returned if an error, such as a timeout, occurs.
+ * MERR is also returned if writing the ipc header failed.
+ * Otherwise, the number of bytes of the payload that were successfully
+ * written is returned.
+ * A return value of zero indicates that nothing was written, this should
+ * only occur when the payload is of zero size.
+ */
+ssize_t
+ipc_send (int sock, uint16_t id, uint8_t type_status,
+ size_t size, const void* payload)
+{
+ ssize_t n = 0;
+
+ n = send_all (sock, &id, sizeof (id));
+ if (0 >= n) return MERR;
+
+ n = send_all (sock, &type_status, sizeof (type_status));
+ if (0 >= n) return MERR;
+
+ n = send_all (sock, &size, sizeof (size));
+ if (0 >= n) return MERR;
+
+ if (0 == size) return 0;
+ if (!payload) return MERR;
+
+ n = send_all (sock, payload, size);
+
+ log_info ("send packet {key: %d, msg: 0x%02x, size: %zd}",
+ id, type_status, size);
+
+ if (ENABLE_DEBUG && 0 < n)
+ {
+ if (MSG_DBG_IMG_THRESHOLD < n)
+ dbg_img (payload, n);
+ else
+ dbg_hex (payload, n);
+ }
+
+ return n;
+}
+
+/*! Caller must have allocated space for \a id, and \a type_status.
+ * \a payload is automatically allocated based on the size field of the
+ * ipcling header, and it is the responsibility of the caller to
+ * deallocate it.
+ *
+ * MERR is returned if an error, such as a timeout, occurs.
+ * MEOF is returned if EOF is reached.
+ * Otherwise, returns the number of bytes read.
+ * A return value of zero is valid, as ipc packets do not necessarily
+ * have to contain a payload.
+ */
+ssize_t
+ipc_recv (int sock, uint16_t *id, uint8_t *type_status,
+ void** payload)
+{
+ size_t size = 0;
+ char* buf = NULL;
+ ssize_t n = 0;
+
+ n = recv_all (sock, id, sizeof (*id));
+ if (0 > n) return n;
+
+ n = recv_all (sock, type_status, sizeof (*type_status));
+ if (0 > n) return n;
+
+ n = recv_all (sock, &size, sizeof (size));
+ if (0 > n) return n;
+
+ if (0 == size) return 0;
+ if (!payload) return MERR;
+
+ buf = t_malloc (size, char);
+ if (!buf) return MERR;
+
+ n = recv_all (sock, buf, size);
+
+ *payload = buf;
+
+ log_info ("recv packet {key: %d, msg: 0x%02x, size: %zd}",
+ *id, *type_status, size);
+
+ if (ENABLE_DEBUG && 0 < n)
+ {
+ if (MSG_DBG_IMG_THRESHOLD < n)
+ dbg_img (*payload, n);
+ else
+ dbg_hex (*payload, n);
+ }
+
+ return n;
+}
+
+/*! \brief Does the real work of starting the \a child process
+ */
+static
+SANE_Status
+ipc_fork (process *child)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ int pipe_fd[2];
+
+ require (child);
+
+ if (-1 == pipe (pipe_fd))
+ {
+ err_fatal ("pipe: %s", strerror (errno));
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ child->pid = fork ();
+ if (0 == child->pid)
+ {
+ /* replace child process with a plugin program
+ */
+ close (pipe_fd[0]); /* unused read end */
+ if (0 <= dup2 (pipe_fd[1], STDOUT_FILENO))
+ {
+ log_info ("%s[%d]: starting", child->name, getpid ());
+ if (-1 == execl (child->name, child->name, NULL))
+ {
+ err_fatal ("%s[%d]: %s", child->name, getpid (),
+ strerror (errno));
+ }
+ }
+ else
+ {
+ err_major ("%s[%d]: %s", child->name, getpid (),
+ strerror (errno));
+ }
+
+ /* notify the parent process that we're done here */
+ write (pipe_fd[1], "-1\n", strlen ("-1\n"));
+ fsync (pipe_fd[1]);
+
+ close (pipe_fd[1]);
+ exit (EXIT_FAILURE);
+ }
+
+ if (0 > child->pid)
+ {
+ err_fatal ("fork: %s", strerror (errno));
+ s = SANE_STATUS_CANCELLED;
+ }
+ else
+ {
+ /* Check whether child process has (unexpectedly) exited. We
+ don't want to have zombies in our closet ;-)
+ */
+ pid_t w = waitpid (child->pid, NULL, WNOHANG);
+ if (-1 == w)
+ {
+ err_minor ("waitpid: %s", strerror (errno));
+ }
+ if (0 != w)
+ {
+ log_info ("%s[%d]: exited prematurely", child->name, child->pid);
+ s = SANE_STATUS_CANCELLED;
+ }
+ else
+ {
+ FILE *fp = fdopen (pipe_fd[0], "rb");
+ if (fp)
+ {
+ if (1 != fscanf (fp, "%d", &(child->port)))
+ {
+ err_major ("fscanf: %s", strerror (errno));
+ }
+ fclose (fp);
+ }
+ else
+ {
+ err_fatal ("%s", strerror (errno));
+ }
+ }
+ }
+ close (pipe_fd[0]);
+ close (pipe_fd[1]);
+
+ if (0 > child->port)
+ s = SANE_STATUS_CANCELLED;
+
+ return s;
+}
+
+/*! \brief Requests a connection to a \a child
+ */
+static
+SANE_Status
+ipc_connect (process *child)
+{
+ struct sockaddr_in addr;
+ struct timeval t;
+ int rv;
+
+ require (child);
+
+ log_call ("(%s, %d)", child->name, child->port);
+
+ errno = 0;
+ child->socket = socket (AF_INET, SOCK_STREAM, 0);
+ if (0 > child->socket)
+ {
+ err_major ("socket: %s", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ t.tv_sec = 30;
+ t.tv_usec = 0;
+ errno = 0;
+ rv = setsockopt (child->socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof (t));
+ if (0 > rv)
+ {
+ err_minor ("socket option: %s", strerror (errno));
+ }
+
+ errno = 0;
+ rv = setsockopt (child->socket, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof (t));
+ if (0 > rv)
+ {
+ err_minor ("socket option: %s", strerror (errno));
+ }
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons (child->port);
+ addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ if (0 != connect (child->socket, (struct sockaddr *) &addr, sizeof (addr)))
+ {
+ err_major ("connect: %s", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+process *
+ipc_exec (const char *program, const char *pkglibdir, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+ process *child = NULL;
+
+ log_call ("(%s, %s, %p)", program, pkglibdir, status);
+
+ child = t_malloc (1, process);
+ if (!child)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+
+ child->pid = -1;
+ child->port = -1;
+ child->socket = -1;
+ child->name = NULL;
+
+ if (!pkglibdir)
+ {
+ child->name = strdup (program);
+ }
+ else
+ {
+ int n = (strlen (pkglibdir) + strlen (FILE_SEP_STR)
+ + strlen (program) + 1);
+ char *name = t_malloc (n, char);
+
+ if (name)
+ {
+ sprintf (name, "%s%s%s", pkglibdir, FILE_SEP_STR, program);
+ child->name = name;
+ }
+ }
+
+ if (!child->name)
+ {
+ s = SANE_STATUS_NO_MEM;
+ }
+ else if (access (child->name, X_OK))
+ {
+ s = SANE_STATUS_ACCESS_DENIED;
+ }
+
+ if (SANE_STATUS_GOOD != s)
+ {
+ if (status) *status = s;
+ delete (child);
+ return NULL;
+ }
+
+ s = ipc_fork (child);
+ if (SANE_STATUS_GOOD == s)
+ {
+ int tries = 5;
+
+ do
+ {
+ if (SANE_STATUS_GOOD != s)
+ sleep (1);
+ s = ipc_connect (child);
+ }
+ while (0 < --tries && SANE_STATUS_GOOD != s);
+ }
+
+ if (SANE_STATUS_GOOD != s)
+ {
+ child = ipc_kill (child);
+ promise (!child);
+ }
+ else
+ {
+ promise (child);
+ promise (0 < child->pid);
+ promise (0 < child->port);
+ promise (0 < child->socket);
+ promise (child->name);
+ }
+
+ if (status) *status = s;
+
+ return child;
+}
+
+process *
+ipc_kill (process *child)
+{
+ log_call ("(%p)", child);
+
+ if (child)
+ {
+ int status = 0;
+
+ log_info ("terminating %s (port %d)", child->name, child->port);
+
+ if (0 <= child->socket)
+ {
+ if (0 != close (child->socket))
+ {
+ err_minor ("%s", strerror (errno));
+ }
+ }
+ if (1 < child->pid)
+ {
+ if (0 != kill (child->pid, SIGHUP))
+ {
+ err_minor ("%s", strerror (errno));
+ }
+ if (child->pid != waitpid (child->pid, &status, 0))
+ {
+ err_major ("%s", strerror (errno));
+ }
+
+ if (!WIFSIGNALED (status))
+ {
+ err_major ("%s[%d]: went off the deep end!",
+ child->name, child->pid);
+ }
+ else
+ {
+ if (SIGHUP != WTERMSIG (status))
+ {
+ err_major ("%s[%d]: %s", child->name, child->pid,
+ strsignal (WTERMSIG (status)));
+ }
+ }
+ }
+
+ const_delete (child->name, char *);
+ delete (child);
+ }
+
+ return child;
+}
+
+void
+ipc_dip_proc (process *child, int flag, const ipc_dip_parms *p,
+ SANE_Parameters *ctx, void **buffer)
+{
+ int socket;
+ uint8_t status = STATUS_NG;
+ uint16_t id = 0;
+ ssize_t n;
+
+ require (child);
+ socket = child->socket;
+
+ require (TYPE_DIP_SKEW_FLAG == flag || TYPE_DIP_CROP_FLAG == flag);
+ require (0 < socket && p && ctx && buffer && *buffer);
+
+ /* inter-process procedure call, status will be STATUS_NG in case
+ * anything goes wrong during IPC call sequence
+ */
+ {
+ n = ipc_send (socket, id, flag | TYPE_DIP_CTOR,
+ strlen (p->fw_name), p->fw_name);
+ if (strlen (p->fw_name) == n)
+ {
+ n = ipc_recv (socket, &id, &status, NULL);
+ if (STATUS_OK == status)
+ {
+ if (sizeof (*p) != ipc_send (socket, id, flag | TYPE_DIP_PARM,
+ sizeof (*p), p))
+ {
+ status = STATUS_NG;
+ }
+ else
+ {
+ ipc_recv (socket, &id, &status, NULL);
+ if (STATUS_OK == status)
+ {
+ ssize_t size = ctx->bytes_per_line * ctx->lines;
+
+ if (size != ipc_send (socket, id, flag | TYPE_DIP_DATA,
+ size, *buffer))
+ {
+ err_minor ("image truncated");
+ status = STATUS_NG;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (STATUS_NG == status) /* abort further processing */
+ {
+ ipc_send (socket, id, flag | TYPE_DIP_DTOR, 0, NULL);
+ ipc_recv (socket, &id, &status, NULL);
+ return;
+ }
+
+ /* acquire DIP results */
+ {
+ uint8_t req = flag | TYPE_DIP_PARM;
+ void *buf = NULL;
+ ipc_dip_parms par;
+
+ if (sizeof (par) == ipc_recv (socket, &id, &req, &buf))
+ {
+ ssize_t size;
+
+ memcpy (&par, buf, sizeof (par));
+ size = par.parms.bytes_per_line * par.parms.lines;
+
+ req = flag | TYPE_DIP_DATA;
+ delete (buf);
+
+ if (size == ipc_recv (socket, &id, &req, &buf))
+ {
+ memcpy (ctx, &par.parms, sizeof (*ctx));
+ delete (*buffer);
+ *buffer = buf;
+ }
+ else
+ {
+ err_minor ("image truncated");
+ delete (buf);
+ }
+ }
+ }
+ ipc_send (socket, id, flag | TYPE_DIP_DTOR, 0, NULL);
+ ipc_recv (socket, &id, &status, NULL);
+}
diff --git a/backend/ipc.h b/backend/ipc.h
new file mode 100644
index 0000000..46d2047
--- /dev/null
+++ b/backend/ipc.h
@@ -0,0 +1,121 @@
+/* ipc.h -- inter-process communication (IPC) support
+ * Copyright (C) 2019 SEIKO EPSON Corporation
+ *
+ * License: EPSON END USER SOFTWARE LICENSE
+ * Author : SEIKO EPSON Corporation
+ *
+ * This file is part of Image Scan! for Linux.
+ * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE.
+ *
+ * You should have received a verbatim copy of the EPSON END USER SOFTWARE
+ * LICENSE along with the software.
+ */
+
+
+#ifndef ipc_h
+#define ipc_h
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sane/sane.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*! \brief Exceptional return values from ipc_recv() and ipc_send()
+ * In particular, a return value of zero from read() conflicts with
+ * our ipc_recv() API where a return value of zero does not indicate
+ * EOF, but a successful read of zero bytes.
+ */
+#define MERR -1 /*!< read/write error */
+#define MEOF -2 /*!< eof; only returned by ipc_recv() */
+
+enum {
+ TYPE_ESC = 1,
+ TYPE_INIT,
+ TYPE_DIE,
+ TYPE_OPEN,
+ TYPE_CLOSE,
+ TYPE_LIST,
+ TYPE_STATUS,
+
+ TYPE_DIP_CTOR = 0x01,
+ TYPE_DIP_DTOR = 0x02,
+ TYPE_DIP_PARM = 0x03,
+ TYPE_DIP_DATA = 0x04,
+ TYPE_DIP_MASK = 0x0f,
+ TYPE_DIP_FLAG = 0xf0,
+
+ TYPE_DIP_SKEW_FLAG = 0x10,
+ TYPE_DIP_SKEW_CTOR = TYPE_DIP_SKEW_FLAG | TYPE_DIP_CTOR,
+ TYPE_DIP_SKEW_DTOR = TYPE_DIP_SKEW_FLAG | TYPE_DIP_DTOR,
+ TYPE_DIP_SKEW_PARM = TYPE_DIP_SKEW_FLAG | TYPE_DIP_PARM,
+ TYPE_DIP_SKEW_DATA = TYPE_DIP_SKEW_FLAG | TYPE_DIP_DATA,
+ TYPE_DIP_SKEW_MASK = TYPE_DIP_SKEW_FLAG | TYPE_DIP_MASK,
+
+ TYPE_DIP_CROP_FLAG = 0x20,
+ TYPE_DIP_CROP_CTOR = TYPE_DIP_CROP_FLAG | TYPE_DIP_CTOR,
+ TYPE_DIP_CROP_DTOR = TYPE_DIP_CROP_FLAG | TYPE_DIP_DTOR,
+ TYPE_DIP_CROP_PARM = TYPE_DIP_CROP_FLAG | TYPE_DIP_PARM,
+ TYPE_DIP_CROP_DATA = TYPE_DIP_CROP_FLAG | TYPE_DIP_DATA,
+ TYPE_DIP_CROP_MASK = TYPE_DIP_CROP_FLAG | TYPE_DIP_MASK,
+};
+
+enum {
+ STATUS_OK = 0,
+ STATUS_NG
+};
+
+ssize_t ipc_send (int sock, uint16_t id, uint8_t type_status,
+ size_t size, const void* payload);
+ssize_t ipc_recv (int sock, uint16_t *id, uint8_t *type_status,
+ void** payload);
+
+ typedef struct
+ {
+ pid_t pid;
+ int port;
+ int socket;
+
+ const char *name;
+
+ } process;
+
+ /*! \brief Attempts to start \a program as a child process
+ */
+ process * ipc_exec (const char *program, const char *pkglibdir,
+ SANE_Status *status);
+
+ /*! \brief Terminates a child process
+ */
+ process * ipc_kill (process *child);
+
+ typedef struct
+ {
+ SANE_Parameters parms;
+ SANE_Int res_x;
+ SANE_Int res_y;
+ SANE_Int gamma;
+ SANE_Bool bside;
+ char fw_name[16 + 1];
+
+ } ipc_dip_parms;
+
+ /*! \brief Performs an image processing action
+ *
+ * If any of the IPC messaging signals an error, the original image
+ * data will not be modified at all. That is, \a ctx and \a buffer
+ * remain unchanged in such a case.
+ */
+ void ipc_dip_proc (process *child, int flag, const ipc_dip_parms *p,
+ SANE_Parameters *ctx, void **buffer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (ipc_h) */
diff --git a/backend/list.c b/backend/list.c
new file mode 100644
index 0000000..7e0810c
--- /dev/null
+++ b/backend/list.c
@@ -0,0 +1,198 @@
+/* list.c -- A poor implementation of a linked list
+ * Copyright (C) 2008 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! \file
+ \brief Implements a linked list.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "list.h"
+
+#include <string.h>
+
+/*! Create a new list.
+ */
+list* list_create ()
+{
+ list* lst = 0;
+
+ lst = t_calloc (1, list);
+ return lst;
+}
+
+/*! Destroy a list.
+ * Deletes the list related structures, and the contained data with the
+ * given destructor function. If a destructor is not provided the data is
+ * left as is.
+ */
+void list_destroy (list* lst, void (*dtor)(void*))
+{
+ if (!lst) return;
+
+ list_entry* entry = lst->head;
+ list_entry* tmp;
+
+ while (entry != NULL)
+ {
+ if (dtor && entry->data)
+ (*dtor) (entry->data);
+
+ tmp = entry->next;
+ delete (entry);
+ entry = tmp;
+ }
+
+ delete (lst);
+}
+
+/*! Adds a new element to the end of the list.
+ * Does *not* make a copy.
+ */
+bool list_append (list* lst, void* new_data)
+{
+ if (!lst) return false;
+
+ list_entry* entry = 0;
+
+ entry = t_calloc (1, list_entry);
+ if (!entry) return false;
+
+ entry->data = new_data;
+
+ if (0 == lst->num_entries)
+ {
+ lst->head = entry;
+ lst->tail = entry;
+ lst->cur = entry;
+ }
+ else
+ {
+ lst->tail->next = entry;
+ lst->tail = entry;
+ }
+
+ lst->num_entries += 1;
+ return true;
+}
+
+/*! Creates a NULL terminated array of pointers to list entries.
+ */
+void** list_normalize (const list *lst)
+{
+ void **nlst;
+
+ if (!lst) return NULL;
+
+ nlst = t_malloc (lst->num_entries + 1, void *);
+ if (nlst)
+ {
+ list *p = (list *) lst;
+ list_entry *cur = p->cur;
+
+ void *entry;
+ int i = 0;
+
+ list_reset (p);
+ while ((entry = list_next (p)))
+ {
+ nlst[i++] = entry;
+ }
+ nlst[i++] = NULL;
+ p->cur = cur;
+ }
+ return nlst;
+}
+
+/*! Obtain the number of elements in the list
+ */
+size_t list_size (list* lst)
+{
+ if (!lst) return 0;
+ return lst->num_entries;
+}
+
+/*! Reset iteration to start from the beginning of the list
+ */
+void list_reset (list* lst)
+{
+ if (!lst) return;
+
+ lst->cur = lst->head;
+}
+
+/*! Proceed to the next element in the list.
+ * Used to iterate over the list items.
+ */
+void* list_next (list* lst)
+{
+ if (!lst || !lst->cur) return 0;
+
+ void* rv = lst->cur->data;
+ lst->cur = lst->cur->next;
+
+ return rv;
+}
diff --git a/backend/list.h b/backend/list.h
new file mode 100644
index 0000000..f7547f6
--- /dev/null
+++ b/backend/list.h
@@ -0,0 +1,117 @@
+/* list.h -- A poor implementation of a linked list
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+#ifndef list_h_included
+#define list_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "defines.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct list_entry
+{
+ void* data;
+ struct list_entry* next;
+} list_entry;
+
+typedef struct list
+{
+ list_entry* head; /* first element of the list */
+ list_entry* tail; /* last element of the list */
+ list_entry* cur; /* current element of the list */
+ size_t num_entries; /* number of elements in the list */
+} list;
+
+/* create a new empty list */
+list* list_create ();
+
+/* destroy a list */
+void list_destroy (list* lst, void (*dtor)(void*));
+
+/* add one element to the end of the list */
+bool list_append (list* lst, void* new_data);
+
+/* convert a list to a flat array */
+void** list_normalize (const list* lst);
+
+/* number of elements in the list */
+size_t list_size (list* lst);
+
+/* reset iteration to start from the beginning of the list*/
+void list_reset (list* lst);
+
+/* proceed to the next element in the list */
+void* list_next (list* lst);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (list_h_included) */
diff --git a/backend/message.c b/backend/message.c
new file mode 100644
index 0000000..67853d4
--- /dev/null
+++ b/backend/message.c
@@ -0,0 +1,152 @@
+/* message.c -- consistent error, progress and debugging feedback
+ * Copyright (C) 2019 SEIKO EPSON Corporation
+ *
+ * License: EPSON END USER SOFTWARE LICENSE
+ * Author : SEIKO EPSON Corporation
+ *
+ * This file is part of Image Scan! for Linux.
+ * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE.
+ *
+ * You should have received a verbatim copy of the EPSON END USER SOFTWARE
+ * LICENSE along with the software.
+ */
+
+/*! \file
+ \brief Infra-structure to provide consistent backend feedback.
+
+ \todo Describe purpose of the three message categories and their
+ levels. Also document usage policy and message formatting
+ conventions.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "message.h"
+
+#if ENABLE_DEBUG
+
+unsigned long msg_level = 0;
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <strings.h>
+
+/*! Initialises the message infra-structure.
+
+ This function sets level at which the backend should produce
+ feedback. The value is gotten from the \c SANE_DEBUG_EPKOWA
+ environment variable.
+
+ The following, case insensitive string literals are supported
+ (in increasing level of feedback):
+
+ - \c FATAL
+ - \c MAJOR
+ - \c MINOR
+ - \c INFO
+ - \c CALL
+ - \c DATA
+ - \c CMD
+ - \c HEX
+ - \c IMG
+
+ \todo Add support for decimal literal level specification.
+ */
+void
+msg_init (void)
+{
+ struct level_def
+ {
+ const char *key;
+ msg_level_type val;
+ };
+
+ const struct level_def def[] =
+ {
+ {"FATAL", ERR_FATAL},
+ {"MAJOR", ERR_MAJOR},
+ {"MINOR", ERR_MINOR},
+
+ {"INFO" , LOG_INFO},
+ {"CALL" , LOG_CALL},
+ {"DATA" , LOG_DATA},
+
+ {"CMD" , DBG_CMD},
+ {"HEX" , DBG_HEX},
+ {"IMG" , DBG_IMG},
+
+ {NULL} /* array terminator */
+ };
+
+ const char *level = getenv ("SANE_DEBUG_EPKOWA");
+ const struct level_def *p = def;
+
+ msg_level = 0;
+
+ if (!level) return;
+
+ while (p && p->key)
+ {
+ if (0 == strcasecmp (level, p->key))
+ {
+ msg_level = p->val;
+ log_info ("setting message level to '%s' (%d)",
+ p->key, p->val);
+ return;
+ }
+ ++p;
+ }
+}
+
+
+/*! Dumps the contents of a \a buffer in hexadecimal format.
+ */
+void
+msg_dump (const char *fmt, const void *buffer, size_t sz)
+{
+ const size_t quad_length = 4;
+ const size_t quad_count = 4;
+ const size_t line_length = quad_length * quad_count;
+
+ const unsigned char *buf = buffer;
+
+ char ascii[line_length + 1];
+ size_t i = 0;
+
+ ascii[line_length] = '\0';
+
+ while (i < sz)
+ {
+ if (0 == i % line_length) /* header */
+ fprintf (stderr, "%s%08zx: ", fmt, i);
+
+ ascii[i % line_length] = (isprint (buf[i]) ? buf[i] : '.');
+
+ fprintf (stderr, " %02x", buf[i]);
+ ++i;
+ if (0 == i % quad_length) /* spacer */
+ fprintf (stderr, " ");
+ if (0 == i % line_length) /* trailer */
+ fprintf (stderr, " |%s|\n", ascii);
+ }
+
+ if (0 != i % line_length) /* last line */
+ {
+ do
+ { /* align trailer */
+ ascii[i % line_length] = ' ';
+ fprintf (stderr, " ");
+ ++i;
+ if (0 == i % quad_length)
+ fprintf (stderr, " ");
+ }
+ while (0 != i % line_length);
+ fprintf (stderr, " |%s|\n", ascii);
+ }
+}
+
+#endif /* ENABLE_DEBUG */
diff --git a/backend/message.h b/backend/message.h
new file mode 100644
index 0000000..622f2a1
--- /dev/null
+++ b/backend/message.h
@@ -0,0 +1,191 @@
+/* message.h -- consistent error, progress and debugging feedback
+ * Copyright (C) 2019 SEIKO EPSON Corporation
+ *
+ * License: EPSON END USER SOFTWARE LICENSE
+ * Author : SEIKO EPSON Corporation
+ *
+ * This file is part of Image Scan! for Linux.
+ * It is distributed under the terms of the EPSON END USER SOFTWARE LICENSE.
+ *
+ * You should have received a verbatim copy of the EPSON END USER SOFTWARE
+ * LICENSE along with the software.
+ */
+
+
+#ifndef message_h
+#define message_h
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#ifndef ENABLE_DEBUG
+#define ENABLE_DEBUG 0
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+ typedef enum
+ {
+ ERR_FATAL = (1 << 0),
+ ERR_MAJOR = (1 << 1),
+ ERR_MINOR = (1 << 2),
+
+ LOG_INFO = (1 << 3),
+ LOG_CALL = (1 << 4),
+ LOG_DATA = (1 << 5),
+
+ DBG_CMD = (1 << 6),
+ DBG_HEX = (1 << 7),
+ DBG_IMG = (1 << 8),
+ }
+ msg_level_type;
+
+
+ extern unsigned long msg_level;
+
+
+ /*! \brief Maximum buffer size not considered to be image data
+ *
+ * This value can be used by low-level I/O functions that want to
+ * log the interesting data they handle but lack the knowledge to
+ * distinguish between that data and boring old image bytes.
+ */
+#define MSG_DBG_IMG_THRESHOLD 512
+
+
+#define FMT_FILE __FILE__ ":%d: "
+#define FMT_LINE , __LINE__
+#define FMT_MODULE FMT_FILE "[%s]"
+
+#define FMT_FATAL FMT_MODULE "[F] "
+#define FMT_MAJOR FMT_MODULE "[M] "
+#define FMT_MINOR FMT_MODULE "[m] "
+#define FMT_INFO FMT_MODULE "{I} "
+#define FMT_CALL FMT_MODULE "{C} " "%s "
+#define FMT_DATA FMT_MODULE "{D} "
+#define FMT_CMD FMT_MODULE "(e) "
+#define FMT_HEX "[" MSG_MODULE "]" "(x) "
+#define FMT_IMG "[" MSG_MODULE "]" "(i) "
+
+#if ENABLE_DEBUG
+
+#define err_fatal(fmt,arg...) \
+ do \
+ { \
+ if (ERR_FATAL <= msg_level) \
+ fprintf (stderr, FMT_FATAL fmt "\n" FMT_LINE, MSG_MODULE, \
+ ## arg); \
+ } \
+ while (0) \
+ /**/
+
+#define err_major(fmt,arg...) \
+ do \
+ { \
+ if (ERR_MAJOR <= msg_level) \
+ fprintf (stderr, FMT_MAJOR fmt "\n" FMT_LINE, MSG_MODULE, \
+ ## arg); \
+ } \
+ while (0) \
+ /**/
+
+#define err_minor(fmt,arg...) \
+ do \
+ { \
+ if (ERR_MINOR <= msg_level) \
+ fprintf (stderr, FMT_MINOR fmt "\n" FMT_LINE, MSG_MODULE, \
+ ## arg); \
+ } \
+ while (0) \
+ /**/
+
+#define log_info(fmt,arg...) \
+ do \
+ { \
+ if (LOG_INFO <= msg_level) \
+ fprintf (stderr, FMT_INFO fmt "\n" FMT_LINE, MSG_MODULE, \
+ ## arg); \
+ } \
+ while (0) \
+ /**/
+
+#define log_call(fmt,arg...) \
+ do \
+ { \
+ if (LOG_CALL <= msg_level) \
+ fprintf (stderr, FMT_CALL fmt "\n" FMT_LINE, MSG_MODULE, \
+ __func__, ## arg); \
+ } \
+ while (0) \
+ /**/
+
+#define log_data(fmt,arg...) \
+ do \
+ { \
+ if (LOG_DATA <= msg_level) \
+ fprintf (stderr, FMT_DATA fmt "\n" FMT_LINE, MSG_MODULE, \
+ ## arg); \
+ } \
+ while (0) \
+ /**/
+
+#define dbg_cmd(buf,sz) \
+ do \
+ { \
+ if (DBG_CMD <= msg_level) \
+ fprintf (stderr, FMT_CMD fmt "\n" FMT_LINE, MSG_MODULE, \
+ ## arg); \
+ } \
+ while (0) \
+ /**/
+
+#define dbg_hex(buf,sz) \
+ do \
+ { \
+ if (DBG_HEX <= msg_level) \
+ msg_dump (FMT_HEX, buf, sz); \
+ } \
+ while (0) \
+ /**/
+
+#define dbg_img(buf,sz) \
+ do \
+ { \
+ if (DBG_IMG <= msg_level) \
+ msg_dump (FMT_IMG, buf, sz); \
+ } \
+ while (0) \
+ /**/
+
+ void msg_init (void);
+ void msg_dump (const char *, const void *, size_t);
+
+#else /* !ENABLE_DEBUG */
+
+#define err_fatal(fmt,arg...)
+#define err_major(fmt,arg...)
+#define err_minor(fmt,arg...)
+#define log_info(fmt,arg...)
+#define log_call(fmt,arg...)
+#define log_data(fmt,arg...)
+#define dbg_cmd(buf,sz)
+#define dbg_hex(buf,sz)
+#define dbg_img(buf,sz)
+
+#define msg_init()
+#define msg_dump(fmt,buf,sz)
+
+#endif /* !ENABLE_DEBUG */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (message_h) */
diff --git a/backend/model-info.c b/backend/model-info.c
new file mode 100644
index 0000000..5423483
--- /dev/null
+++ b/backend/model-info.c
@@ -0,0 +1,707 @@
+/* model-info.c -- per model information objects and cache
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "model-info.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "list.h"
+#include "message.h"
+#include "utils.h"
+
+#include "get-infofile.h"
+#include "xmlreader.h"
+
+
+/*! \brief Points to our model info cache.
+ *
+ * Its value is guaranteed to be \c NULL \e outside the scope of a
+ * model_info_cache_init()/model_info_cache_exit() pair.
+ */
+static list *_cache = NULL;
+
+
+/*! \brief Directory to search for resource files
+ *
+ * The _model_info_ctor() needs this.
+ */
+static const char *_datadir = NULL;
+
+
+/*! \brief Collects all (supported) model information
+ */
+typedef struct
+{
+ char *fw_name; /* key for _model_info_cache_get_info */
+ char *overseas; /* model name */
+ char *japan; /* model name */
+ char *name; /* points to one of overseas, japan or
+ * fw_name, never NULL */
+ scan_command_t *command; /* command customisation info */
+ EpsonScanHard profile; /* colour profiles */
+
+ bool from_file; /* origin of our data, used to control
+ * which resources need to be released
+ * at destruction time */
+
+ capability_data_t *dfault;
+ capability_data_t *adf_duplex;
+} _model_info_t;
+
+typedef enum
+{
+ FEED,
+ FOCUS,
+ EJECT,
+
+} _command_id_t;
+
+/* Model info cache implementation details */
+static _model_info_t * _model_info_cache_get_info (const char *fw_name,
+ SANE_Status *status);
+
+/* Model info implementation details */
+static _model_info_t * _model_info_ctor (const char *fw_name,
+ SANE_Status *status);
+static void _model_info_dtor (void *p);
+static char * _model_info_guess_name (const _model_info_t *self);
+static SANE_Status _model_info_merge_file (_model_info_t *self);
+static void _model_info_merge_data (_model_info_t *self,
+ xmlNodePtr node);
+
+static bool _model_info_set_cmd (const _model_info_t *self,
+ unsigned char *cmd, _command_id_t id);
+
+
+/*! \brief Sets up model info cache support.
+ *
+ * Model specific information will be looked for in \a pkgdatadir.
+ *
+ * \returns An opaque pointer to the cache. An additional \a status
+ * will be returned as well if the argument is not \c NULL.
+ */
+void *
+model_info_cache_init (const char *pkgdatadir, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+
+ log_call ("(%s, %p)", pkgdatadir, status);
+ require (pkgdatadir);
+
+ if (_cache)
+ {
+ err_minor ("been here, done that");
+ if (0 != strcmp_c (_datadir, pkgdatadir))
+ {
+ err_major ("already using %s", _datadir);
+ }
+ if (status) *status = s;
+ return _cache;
+ }
+
+ _datadir = strdup (pkgdatadir);
+ _cache = list_create ();
+ if (!_datadir || !_cache)
+ {
+ _cache = model_info_cache_exit (_cache);
+
+ s = SANE_STATUS_NO_MEM;
+ }
+
+ if (0 != atexit (xmlCleanupParser))
+ {
+ err_minor ("could not register XML parser cleanup function");
+ }
+
+ /* ?FIXME?
+ * Check for existence/readability of _pkgdatadir and log its
+ * absence/presence. It is _not_ fatal if the directory does
+ * not exist or is not readable. We just want to note it via
+ * an err_minor(). If not readable, we may want to return a
+ * SANE_STATUS_ACCESS_DENIED.
+ */
+
+ if (status) *status = s;
+ return _cache;
+}
+
+
+/*! \brief Tears down model info cache support.
+ *
+ * Releases all resources associated with the model info cache.
+ * The \a self argument should be an opaque pointer obtained via a
+ * call to model_info_cache_init().
+ *
+ * Note that for error recovery purposes, model_info_cache_init() may
+ * call this function with an empty _cache or no cache at all.
+ *
+ * \returns \c NULL, always
+ */
+void *
+model_info_cache_exit (void *self)
+{
+ log_call ("(%p)", self);
+ require (_cache == self);
+
+ const_delete (_datadir, char *);
+ list_destroy (_cache, _model_info_dtor);
+ _datadir = NULL;
+ _cache = NULL;
+
+ promise (!_cache);
+
+ return _cache;
+}
+
+
+/*! \brief Attempts to find model information for \a fw_name.
+ *
+ * \return A pointer to the model information, \c NULL if not found.
+ */
+const void *
+model_info_cache_get_info (const char *fw_name, SANE_Status *status)
+{
+ /* Just forward to the internal implementation. The return
+ * statement will handle the pointer type conversion.
+ */
+ return _model_info_cache_get_info (fw_name, status);
+}
+
+
+/*! \brief Returns a best-effort model name based on \a fw_name.
+ *
+ * The caller gets to manage the memory occupied by the string that
+ * is returned. Note that \c NULL may be returned.
+ */
+char *
+model_info_cache_get_model (const char *fw_name)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+ _model_info_t *m = NULL;
+
+ log_call ("(%s)", fw_name);
+ require (_cache && _datadir);
+
+ if (!fw_name || 0 == strlen (fw_name))
+ {
+ err_minor ("%s", sane_strstatus (SANE_STATUS_INVAL));
+ return strdup ("(unknown model)");
+ }
+
+ m = _model_info_cache_get_info (fw_name, &s);
+ if (!m)
+ {
+ err_minor ("%s", sane_strstatus (s));
+ return strdup (fw_name); /* best we can do */
+ }
+
+ return strdup (m->name);
+}
+
+
+/*! \brief Returns a reference to the model name.
+ *
+ * Resources associated with the reference are owned by \a self. The
+ * caller should \e not attempt to release them.
+ */
+const char *
+model_info_get_name (const void *self)
+{
+ require (self); /* ?FIXME? check if in _cache? */
+
+ return ((const _model_info_t *) self)->name;
+}
+
+
+/*! \brief Returns a reference to the model's colour profiles.
+ *
+ * Resources associated with the reference are owned by \a self. The
+ * caller should \e not attempt to release them.
+ */
+const EpsonScanHard
+model_info_get_profile (const void *self)
+{
+ require (self); /* ?FIXME? check if in _cache? */
+
+ return ((const _model_info_t *) self)->profile;
+}
+
+
+/*! \brief Modify selected commands in the \a cmd specification
+ *
+ * This function caters to quirks in the command level specification
+ * reported by the device. Especially commands for hardware options
+ * may be affected.
+ *
+ * \return \c true if commands have been modified, \c false otherwise
+ */
+bool
+model_info_customise_commands (const void *self, EpsonCmd cmd)
+{
+ bool customised = false;
+ _model_info_t *self_ = NULL;
+
+ require (self); /* ?FIXME? check if in _cache? */
+
+ if (!cmd)
+ {
+ err_minor ("%s", sane_strstatus (SANE_STATUS_INVAL));
+ return customised;
+ }
+
+ self_ = (_model_info_t *) self;
+ customised |= _model_info_set_cmd (self_, &cmd->set_focus_position, FOCUS);
+ customised |= _model_info_set_cmd (self_, &cmd->feed, FEED);
+ customised |= _model_info_set_cmd (self_, &cmd->eject, EJECT);
+
+ return customised;
+}
+
+
+/*! \brief Attempts to find model information for \a fw_name.
+ *
+ * Checks for existing information in the cache before it attempts to
+ * add new model information. Takes care to preserve the cache's cur
+ * member so as not to invalidate existing "iterators".
+ *
+ * \return A pointer to the model information. \c NULL if not found,
+ * in case anything went wrong trying to add the info to the
+ * cache or the caller passed in garbage.
+ */
+static _model_info_t *
+_model_info_cache_get_info (const char *fw_name, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+ _model_info_t *info = NULL;
+ list_entry *cur = NULL;
+ bool found = false;
+
+ log_call ("(%s)", fw_name);
+ require (_cache && _datadir);
+
+ if (!fw_name || 0 == strlen (fw_name))
+ {
+ if (status) *status = SANE_STATUS_INVAL;
+ return NULL;
+ }
+
+ cur = _cache->cur; /* check whether cached */
+ list_reset (_cache);
+ while (!found && (info = list_next (_cache)))
+ {
+ found = (0 == strcmp_c (info->fw_name, fw_name));
+ }
+ _cache->cur = cur;
+
+ if (!found) /* try to add info to cache */
+ {
+ info = _model_info_ctor (fw_name, &s);
+ if (!info || !list_append (_cache, info))
+ {
+ _model_info_dtor (info);
+ info = NULL;
+ }
+ }
+
+ if (status) *status = s;
+ return info;
+}
+
+
+/*! \brief Creates and initialises a model info object.
+ *
+ * \return A valid model info object or \c NULL if unable to acquire
+ * the necessary memory resources.
+ */
+static _model_info_t *
+_model_info_ctor (const char *fw_name, SANE_Status *status)
+{
+ SANE_Status s = SANE_STATUS_GOOD;
+ _model_info_t *self = NULL;
+
+ log_call ("(%s)", fw_name);
+ require (fw_name);
+
+ self = t_calloc (1, _model_info_t);
+ if (!self)
+ {
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+
+ self->fw_name = strdup (fw_name);
+ if (!self->fw_name)
+ {
+ _model_info_dtor (self);
+ if (status) *status = SANE_STATUS_NO_MEM;
+ return NULL;
+ }
+
+ /* Set defaults using data defined in the source code. The various
+ * getters decide a decent default in case self->fw_name is not one
+ * of the names for which we have data in our sources.
+ */
+ self->overseas = get_scanner_data (self->fw_name, MODEL_OVERSEAS);
+ self->japan = get_scanner_data (self->fw_name, MODEL_JAPAN);
+ self->profile = get_epson_scan_hard (self->fw_name);
+ self->command = get_scan_command (self->fw_name);
+
+ self->from_file = false;
+
+ s = _model_info_merge_file (self);
+
+ self->name = _model_info_guess_name (self);
+
+ if (self) /* make sure things are compliant */
+ {
+ promise (self->fw_name && self->name);
+ promise ( self->name == self->fw_name
+ || self->name == self->overseas
+ || self->name == self->japan);
+ promise (self->profile);
+ promise (self->command);
+ }
+ if (status) *status = s;
+ return self;
+}
+
+
+/*! \brief Destroys a model object.
+ *
+ * Releases any resources acquired throughout the object's life time.
+ */
+static void
+_model_info_dtor (void *self)
+{
+ _model_info_t *p = (_model_info_t *) self;
+
+ if (!p) return;
+
+ if (p->from_file)
+ {
+ /* :FIXME: p->profile may have been acquired as several
+ * individual arrays at construction. Check!
+ */
+ if (p->profile != get_epson_scan_hard (p->fw_name))
+ delete (p->profile);
+ if (p->command != get_scan_command (p->fw_name))
+ delete (p->command);
+ delete (p->overseas);
+ delete (p->japan);
+ if(p->dfault)
+ {
+ delete (p->dfault->option);
+ delete (p->dfault->mode);
+ }
+ delete (p->dfault);
+ if (p->adf_duplex)
+ {
+ delete (p->adf_duplex->option);
+ delete (p->adf_duplex->mode);
+ }
+ delete (p->adf_duplex);
+ }
+
+ delete (p->fw_name);
+ delete (p);
+}
+
+/*! \brief Returns a best effort guess for model name on the device
+ *
+ * This functions implements the policy used to decide which of the
+ * various model names \c self->name should point to. The function
+ * is intended for use at construction of \a self.
+ */
+static char *
+_model_info_guess_name (const _model_info_t *self)
+{
+ require (self);
+
+ if (self->japan && self->overseas)
+ {
+ time_t lt = time (NULL);
+ struct tm *ptr = localtime (&lt);
+
+ if (ptr && 0 == strncmp_c ("JST", ptr->tm_zone, 3))
+ {
+ return self->japan;
+ }
+ else
+ {
+ return self->overseas;
+ }
+ }
+
+ if (self->japan) return self->japan;
+ if (self->overseas) return self->overseas;
+ return self->fw_name;
+}
+
+static char *
+get_path_name (const char *hex_name, char *path_name, size_t path_size)
+{
+ char *path;
+ size_t size = snprintf (path_name, path_size, "%s%s%s%s",
+ _datadir, FILE_SEP_STR, hex_name,
+ ".xml");
+
+ if (size > -1 || size < path_size)
+ return path_name;
+
+ if (size > -1)
+ path_size = size + 1;
+ else
+ path_size *= 2;
+
+ if (0 == path_size)
+ path_size = 512;
+
+ path = t_realloc (path_name, path_size, char);
+ if (!path)
+ {
+ delete (path_name);
+ return NULL;
+ }
+ path_name = path;
+
+ return get_path_name (hex_name, path_name, path_size);
+}
+
+/*! \brief Attempts to merge model information from a data file.
+ */
+static SANE_Status
+_model_info_merge_file (_model_info_t *self)
+{
+ xmlDocPtr doc = NULL;
+
+ char *path_name = NULL;
+ char *hex_name = NULL;
+
+ require (self);
+
+ hex_name = fw_name_to_hex (self->fw_name);
+
+ if (!hex_name) return SANE_STATUS_NO_MEM;
+
+ path_name = get_path_name (hex_name, NULL, 0);
+ delete (hex_name); /* no longer needed */
+
+ if (!path_name) return SANE_STATUS_NO_MEM;
+
+ log_data ("%s", path_name);
+
+ doc = xmlReadFile (path_name, NULL,
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+ delete (path_name); /* no longer needed */
+
+ if (doc)
+ {
+ _model_info_merge_data (self, xmlDocGetRootElement (doc));
+ xmlFreeDoc (doc);
+ }
+ else
+ {
+ xmlErrorPtr p = xmlGetLastError ();
+ if (p) err_minor ("%s", p->message);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+_model_info_merge_data (_model_info_t *self, xmlNodePtr node)
+{
+ /* Define the XML element tags that we are able to merge.
+ */
+ const xmlChar *device = (xmlChar *) "device";
+ const xmlChar *profiles = (xmlChar *) "profile-set";
+ const xmlChar *commands = (xmlChar *) "command-set";
+ const xmlChar *capabilities = (xmlChar *) "capabilities";
+ const xmlChar *cap_option = (xmlChar *) "option";
+ const xmlChar *cap_mode = (xmlChar *) "mode";
+ char *tmp;
+
+ require (self);
+
+ if (!node) return;
+
+ node = node->xmlChildrenNode;
+ while (node)
+ {
+ if (!xmlIsBlankNode (node))
+ {
+ if (0 == xmlStrcmp (node->name, device))
+ {
+ char *tmp = NULL;
+
+ tmp = parseDevices (node, MODEL_OVERSEAS);
+ if (tmp) self->overseas = tmp;
+
+ tmp = parseDevices (node, MODEL_JAPAN);
+ if (tmp) self->japan = tmp;
+ }
+ else if (0 == xmlStrcmp (node->name, profiles))
+ {
+ EpsonScanHard profile = parseProfiles (node);
+ if (profile) self->profile = profile;
+ }
+ else if (0 == xmlStrcmp (node->name, commands))
+ {
+ scan_command_t *command = parseCommands_set (node);
+ if (command) self->command = command;
+ }
+ else if (0 == xmlStrcmp (node->name, capabilities))
+ {
+ tmp = (char *)xmlGetProp(node, (const xmlChar *) cap_mode);
+ if(strcmp_c(tmp, "duplex") == 0){
+ capability_data_t *capability = parseCapabilities (node);
+ if (capability) {
+ self->adf_duplex = capability;
+ self->adf_duplex->option = (char *)xmlGetProp(node, (const xmlChar *) cap_option);
+ self->adf_duplex->mode = tmp;
+ }
+ }else {
+ capability_data_t *capability = parseCapabilities (node);
+ if (capability) {
+ self->dfault = capability;
+ self->dfault->option = (char *)xmlGetProp(node, (const xmlChar *) cap_option);
+ self->dfault->mode = tmp;
+ }
+ }
+ }
+ }
+ node = node->next;
+ }
+ self->from_file = true;
+}
+
+/*! \brief Customises a single command
+ *
+ * \return \c true if \a cmd was modified, \c false otherwise
+ */
+static bool
+_model_info_set_cmd (const _model_info_t *self, unsigned char *cmd,
+ _command_id_t id)
+{
+ unsigned char cmd_ = ILLEGAL_CMD;
+
+ require (self && cmd);
+
+ if (FEED == id) cmd_ = self->command->feed;
+ if (FOCUS == id) cmd_ = self->command->set_focus_position;
+ if (EJECT == id) cmd_ = self->command->eject;
+
+ if (ILLEGAL_CMD != cmd_)
+ {
+ *cmd = cmd_;
+ return true;
+ }
+ return false;
+}
+
+bool
+model_info_has_lock_commands (const void *self)
+{
+ _model_info_t *self_ = NULL;
+
+ require (self);
+
+ self_ = (_model_info_t *) self;
+
+ return (self_->command->lock && self_->command->unlock);
+}
+
+scan_area_t
+model_info_max_scan_area(const void *self, const char *option, const char *mode)
+{
+ _model_info_t *self_ = NULL;
+ scan_area_t scan_area;
+
+ require (self);
+ require (option);
+
+ scan_area.width = SANE_FIX(-1);
+ scan_area.height = SANE_FIX(-1);
+
+ self_ = (_model_info_t *) self;
+
+ if(strcmp(option, "adf") == 0 && strcmp_c(mode, "duplex") == 0){
+ if(self_->adf_duplex){
+ scan_area.width = SANE_FIX (self_->adf_duplex->width * MM_PER_INCH / self_->adf_duplex->base);
+ scan_area.height = SANE_FIX (self_->adf_duplex->height * MM_PER_INCH / self_->adf_duplex->base);
+ }
+ }else {
+ if(self_->dfault){
+ scan_area.width = SANE_FIX (self_->dfault->width * MM_PER_INCH / self_->dfault->base);
+ scan_area.height = SANE_FIX (self_->dfault->height * MM_PER_INCH / self_->dfault->base);
+ }
+ }
+
+ return scan_area;
+}
diff --git a/backend/model-info.h b/backend/model-info.h
new file mode 100644
index 0000000..b92defb
--- /dev/null
+++ b/backend/model-info.h
@@ -0,0 +1,145 @@
+/* model-info.h -- per model information objects and cache
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef model_info_h_included
+#define model_info_h_included
+
+/*! \file
+ * \brief Model specific information.
+ *
+ * There is a fair bit of device information that does not change
+ * during the life-time of the device. The ESC/I scanner protocol
+ * provides support to query the device to get a significant part of
+ * this information. However, some of the information we would like
+ * to use is not available from the device. In addition, the device
+ * sometimes returns incorrect information.
+ *
+ * For a long time we have been working around this by putting the
+ * additional information and the corrections directly in our code.
+ * The model info support provided here implements a unified API to
+ * the device information allowing us to move it out of the code and
+ * into per model resource files.
+ *
+ * The API aims for on-demand, cached data retrieval. In order to
+ * support that, the sane_init() and sane_exit() functions need to
+ * handle cache initialisation and clean-up. They can do so through
+ * the model_info_cache_init() and model_cache_exit() functions.
+ *
+ * The sane_get_devices() and sane_open() functions can get access to
+ * per model information through the model_info_cache_get_info() API
+ * and use its return value in calls to the model info accessors. Of
+ * course, other SANE API entries may do so as well, though we cannot
+ * think of a good need for them to do so (as the information can be
+ * stored in the SANE_Handle during sane_open()).
+ *
+ * The number of model info accessors is still quite limited but we
+ * expect that to change when the implementation proceeds.
+ *
+ * There is also convenience API meant to make initialisation of the
+ * SANE_Device structure marginally less verbose.
+ */
+
+#include <sane/sane.h>
+
+#include "defines.h"
+#include "device.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ struct ScanArea
+ {
+ SANE_Fixed width;
+ SANE_Fixed height;
+ };
+ typedef struct ScanArea scan_area_t;
+
+ /* Model info cache creation and destruction */
+ void * model_info_cache_init (const char *pkgdatadir, SANE_Status *status);
+ void * model_info_cache_exit (void *self);
+
+ /* Model info cache accessors */
+ const void * model_info_cache_get_info (const char *fw_name,
+ SANE_Status *status);
+
+ /* Model info cache convenience methods */
+ char * model_info_cache_get_model (const char *fw_name);
+ /* ?FIXME? add convenience methods for vendor and type? */
+
+ /* Model info accessors */
+ const char * model_info_get_name (const void *self);
+ const EpsonScanHard model_info_get_profile (const void *self);
+ bool model_info_customise_commands (const void *self, EpsonCmd cmd);
+ bool model_info_has_lock_commands (const void *self);
+
+ scan_area_t model_info_max_scan_area(const void *self, const char *option, const char *mode);
+ /* :FIXME: add more accessors */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (model_info_h_included) */
diff --git a/backend/net-obj.c b/backend/net-obj.c
new file mode 100644
index 0000000..d5ee958
--- /dev/null
+++ b/backend/net-obj.c
@@ -0,0 +1,123 @@
+/* net-obj.c --
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "net-obj.h"
+
+#include "defines.h"
+#include "ipc.h"
+#include "message.h"
+
+const char *net_prog_name = "network";
+
+static process *net = NULL;
+
+void *
+net_init (const char *pkglibdir, SANE_Status *status)
+{
+ log_call ("(%s, %p)", pkglibdir, status);
+
+ if (net)
+ {
+ err_minor ("been here, done that");
+ if (status) *status = SANE_STATUS_GOOD;
+ return net;
+ }
+
+ if (!pkglibdir) return NULL;
+
+ net = ipc_exec (net_prog_name, pkglibdir, status);
+
+ return net;
+}
+
+void *
+net_exit (void *self)
+{
+ log_call ("(%p)", self);
+ require (net == self);
+
+ if (net)
+ {
+ net = ipc_kill (net);
+ promise (!net);
+ }
+
+ return net;
+}
+
+/*! Obtain the socket connected to the network plugin
+ */
+int
+net_get_sock (void *self)
+{
+ log_call ("(%p)", self);
+ require (net == self);
+
+ if (net) return net->socket;
+ return -1;
+}
diff --git a/backend/net-obj.h b/backend/net-obj.h
new file mode 100644
index 0000000..e336626
--- /dev/null
+++ b/backend/net-obj.h
@@ -0,0 +1,89 @@
+/* net-obj.h --
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef net_obj_h_included
+#define net_obj_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sane/sane.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+ void * net_init (const char *pkglibdir, SANE_Status *status);
+ void * net_exit (void *self);
+ int net_get_sock (void *self);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (net_obj_h_included) */
diff --git a/backend/profile.c b/backend/profile.c
new file mode 100644
index 0000000..57c0401
--- /dev/null
+++ b/backend/profile.c
@@ -0,0 +1,540 @@
+/* profile.c -- hardware colour correction coefficients
+ * Copyright (C) 2001--2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+/*! Hardware colour correction coefficients (CCC).
+
+ Each entry starts with a unique identifier, followed by four CCC
+ profiles; the first is for reflective materials, the second for
+ colour negatives, the third for monochrome negatives, and the
+ fourth and last one is for colour positives.
+*/
+const EpsonScanHardRec _epson_scan_hard[] = {
+ {0x00, /* default */
+ {{1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x05,
+ {{1.1419,-0.0596,-0.0825,-0.1234, 1.2812,-0.1413, 0.0703,-0.5720, 1.5016},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1419,-0.0596,-0.0825,-0.1234, 1.2812,-0.1413, 0.0703,-0.5720, 1.5016}}},
+ {0x06,
+ {{1.1442,-0.0705,-0.0737,-0.0702, 1.1013,-0.0311,-0.0080,-0.3588, 1.3668},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x07,
+ {{1.1967,-0.1379,-0.0588,-0.0538, 1.0385, 0.0153, 0.0348,-0.4070, 1.3721},
+ {1.0010,-0.0010, 0.0000,-0.1120, 1.1710,-0.0590, 0.0000,-0.0910, 1.0920},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1967,-0.1379,-0.0588,-0.0538, 1.0385, 0.0153, 0.0348,-0.4070, 1.3721}}},
+ {0x0D,
+ {{1.1980,-0.1365,-0.0616,-0.1530, 1.1729,-0.0198,-0.0025,-0.2776, 1.2801},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1980,-0.1365,-0.0616,-0.1530, 1.1729,-0.0198,-0.0025,-0.2776, 1.2801}}},
+ {0x0F,
+ {{1.0961,-0.0181,-0.0779,-0.1279, 1.1957,-0.0678, 0.0315,-0.3891, 1.3576},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0961,-0.0181,-0.0779,-0.1279, 1.1957,-0.0678, 0.0315,-0.3891, 1.3576}}},
+ {0x15,
+ {{1.0999,-0.0425,-0.0574,-0.0806, 1.0835,-0.0028, 0.0057,-0.2924, 1.2866},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x16,
+ {{1.2020,-0.1518,-0.0502,-0.0847, 1.1385,-0.0538, 0.0059,-0.3255, 1.3196},
+ {1.0030,-0.0030, 0.0000,-0.0980, 1.1500,-0.0520,-0.0030,-0.0840, 1.0880},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2456,-0.1617,-0.0839,-0.1160, 1.1862,-0.0702,-0.0036,-0.3438, 1.3473}}},
+ {0x18,
+ {{1.1339,-0.0526,-0.0813,-0.1177, 1.1661,-0.0485,-0.0030,-0.3298, 1.3328},
+ {1.0010,-0.0010, 0.0000,-0.1120, 1.1710,-0.0590, 0.0000,-0.0910, 1.0920},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2066,-0.0360,-0.1706,-0.1313, 1.2523,-0.1210,-0.0299,-0.3377, 1.3676}}},
+ {0x1A,
+ {{1.0986, 0.0235,-0.1221,-0.1294, 1.0896, 0.0399, 0.0928,-0.6043, 1.5115},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x1B,
+ {{1.1855,-0.1372,-0.0483,-0.2060, 1.2468,-0.0407, 0.0358,-0.3059, 1.2701},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1976,-0.1182,-0.0794,-0.1578, 1.2720,-0.1142, 0.0122,-0.3467, 1.3345}}},
+ {0x1D,
+ {{1.0675,-0.0586,-0.0088,-0.0332, 0.9716, 0.0616, 0.0175,-0.4054, 1.3879},
+ {1.0090,-0.0090, 0.0000,-0.0390, 1.0750,-0.0360,-0.0070,-0.1060, 1.1130},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1394,-0.0829,-0.0564,-0.0003, 1.0008,-0.0004,-0.0059,-0.3674, 1.3733}}},
+ {0x1F,
+ {{1.0800,-0.0607,-0.0193,-0.0787, 1.0846,-0.0059, 0.0135,-0.3334, 1.3199},
+ {1.0040,-0.0040, 0.0000,-0.0780, 1.1360,-0.0570,-0.0020,-0.0810, 1.0830},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1334,-0.0929,-0.0405,-0.0418, 1.0689,-0.0271,-0.0521,-0.3262, 1.3783}}},
+ {0x21,
+ {{1.0919,-0.0739,-0.0180,-0.0941, 1.1150,-0.0209, 0.0220,-0.3744, 1.3524},
+ {1.0090,-0.0100, 0.0010,-0.0720, 1.1310,-0.0600, 0.0000,-0.1000, 1.1000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1374,-0.1396, 0.0021,-0.0489, 1.0655,-0.0166, 0.0081,-0.3492, 1.3411}}},
+ {0x23,
+ {{1.0339,-0.0166,-0.0173,-0.0117, 0.9797, 0.0319, 0.0010,-0.3609, 1.3599},
+ {1.0090,-0.0090, 0.0000,-0.0390, 1.0750,-0.0360,-0.0070,-0.1060, 1.1130},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1666,-0.0898,-0.0768,-0.0076, 1.0157,-0.0081, 0.0012,-0.3048, 1.3036}}},
+ {0x25,
+ {{1.0800,-0.0607,-0.0193,-0.0787, 1.0846,-0.0059, 0.0135,-0.3334, 1.3199},
+ {1.0040,-0.0040, 0.0000,-0.0780, 1.1360,-0.0570,-0.0020,-0.0810, 1.0830},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1334,-0.0929,-0.0405,-0.0418, 1.0689,-0.0271,-0.0521,-0.3262, 1.3783}}},
+ {0x27,
+ {{1.0919,-0.0739,-0.0180,-0.0941, 1.1150,-0.0209, 0.0220,-0.3744, 1.3524},
+ {1.0083,-0.0094, 0.0011,-0.0760, 1.1379,-0.0619,-0.0002,-0.0945, 1.0947},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1952,-0.1519,-0.0433,-0.0932, 1.1613,-0.0681,-0.0418,-0.3140, 1.3558}}},
+ {0x29,
+ {{1.0369,-0.0210,-0.0160,-0.0820, 1.1160,-0.0341, 0.0150,-0.5035, 1.4885},
+ {1.0122,-0.0151, 0.0029,-0.0861, 1.1402,-0.0542,-0.0061,-0.1607, 1.1669},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1764,-0.1749,-0.0014,-0.0590, 1.0983,-0.0393, 0.0208,-0.5194, 1.4986}}},
+ {0x2B,
+ {{1.0305,-0.0116,-0.0189,-0.0936, 1.1245,-0.0309,-0.0072,-0.1413, 1.1485},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x32,
+ {{1.0932,-0.0529,-0.0403,-0.1077, 1.1416,-0.0338, 0.0079,-0.5525, 1.5446},
+ {1.0259,-0.0356, 0.0097,-0.1085, 1.2225,-0.1140,-0.0046,-0.1848, 1.1894},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2720,-0.2665,-0.0054,-0.0672, 1.1301,-0.0629,-0.0048,-0.3917, 1.3965}}},
+ {0x2D,
+ {{1.0436,-0.0078,-0.0359,-0.0169, 1.0114, 0.0056, 0.0308,-0.4425, 1.4117},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x3A,
+ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806},
+ {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}},
+ {0x36,
+ {{1.0848,-0.0153,-0.0695,-0.0902, 1.0611, 0.0291, 0.0344,-0.5002, 1.4658},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x34,
+ {{1.1032,-0.0590,-0.0442,-0.1915, 1.3371,-0.1456, 0.0387,-0.5804, 1.5417},
+ {1.0232,-0.0258, 0.0026,-0.1296, 1.2882,-0.1587,-0.0011,-0.1928, 1.1940},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2662,-0.2664, 0.0002,-0.1050, 1.3168,-0.2118,-0.0058,-0.4370, 1.4428}}},
+ {0x38,
+ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806},
+ {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}},
+ {0x37,
+ {{0.9640, 0.1455,-0.1095, 0.0108, 1.1933,-0.2041, 0.0071,-0.3487, 1.3416},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x3F,
+ {{1.1223,-0.0985,-0.0238,-0.0847, 1.1502,-0.0655, 0.0118,-0.5022, 1.4904},
+ {1.0077,-0.0129, 0.0052,-0.0904, 1.1785,-0.0881, 0.0000,-0.1528, 1.1528},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1927,-0.1646,-0.0280,-0.0655, 1.1033,-0.0378, 0.0034,-0.4173, 1.4139}}},
+ {0x41,
+ {{1.0732,-0.0581,-0.0150,-0.0897, 1.1553,-0.0657,-0.0179,-0.6500, 1.6679},
+ {1.0163,-0.0203, 0.0040,-0.1125, 1.1797,-0.0672,-0.0091,-0.2343, 1.2434},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2437,-0.2022,-0.0415,-0.0352, 1.0735,-0.0383,-0.0188,-0.5020, 1.5209}}},
+ {0x43,
+ {{1.0782,-0.0697,-0.0085,-0.1605, 1.2862,-0.1257, 0.0148,-0.5854, 1.5706},
+ {1.0136,-0.0151, 0.0016,-0.1836, 1.3422,-0.1586,-0.0014,-0.1851, 1.1865},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1491,-0.1456,-0.0035,-0.0990, 1.2657,-0.1666, 0.0015,-0.3868, 1.3853}}},
+ {0x46,
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x48,
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x49,
+ {{0.9640, 0.1455,-0.1095, 0.0108, 1.1933,-0.2041, 0.0071,-0.3487, 1.3416},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x4B,
+ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806},
+ {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}},
+ {0x4D,
+ {{1.1011,-0.0824,-0.0186,-0.0970, 1.1991,-0.1021,-0.0161,-0.6247, 1.6408},
+ {1.0259,-0.0356, 0.0097,-0.1085, 1.2225,-0.1140,-0.0046,-0.1848, 1.1894},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2150,-0.2074,-0.0076,-0.0521, 1.1430,-0.0909,-0.0204,-0.4156, 1.4360}}},
+ {0x4F,
+ {{1.1052,-0.0850,-0.0202,-0.1050, 1.2294,-0.1245,-0.0486,-0.4160, 1.4646},
+ {1.0255,-0.0272, 0.0017,-0.0919, 1.2098,-0.1180,-0.0021,-0.1296, 1.1317},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2950,-0.2619,-0.0332,-0.0562, 1.1587,-0.1025,-0.0397,-0.3100, 1.3497}}},
+ {0x51,
+ {{1.0614,-0.0361,-0.0253,-0.1081, 1.1320,-0.0240,-0.0536,-0.2045, 1.2580},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x52,
+ {{1.0978,-0.0806,-0.0173,-0.0802, 1.1515,-0.0713,-0.0476,-0.4656, 1.5132},
+ {1.0192,-0.0192, 0.0000,-0.0974, 1.1846,-0.0872,-0.0031,-0.1797, 1.1828},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2490,-0.2030,-0.0460,-0.0469, 1.1046,-0.0577,-0.0361,-0.3857, 1.4217}}},
+ {0x54,
+ {{1.0905,-0.0654,-0.0251,-0.1030, 1.1801,-0.0771,-0.0685,-0.4238, 1.4923},
+ {1.0206,-0.0207, 0.0000,-0.0890, 1.1770,-0.0880,-0.0014,-0.1450, 1.1464},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.3041,-0.2907,-0.0134,-0.0383, 1.0908,-0.0525,-0.0327,-0.2947, 1.3275}}},
+ {0x56,
+ {{1.0784,-0.0560,-0.0224,-0.1793, 1.2234,-0.0441,-0.0041,-0.2636, 1.2677},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x57,
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x58,
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x59,
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x5B,
+ {{0.9764, 0.1095,-0.0859, 0.0149, 1.1154,-0.1303, 0.0051,-0.2851, 1.2800},
+ {1.0024,-0.0149, 0.0124,-0.2569, 1.3432,-0.0864,-0.0043,-0.1306, 1.1349},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1003,-0.0493,-0.0510,-0.1607, 1.2748,-0.1142,-0.0059,-0.3161, 1.3220}}},
+ {0x5D,
+ {{0.9764, 0.1095,-0.0859, 0.0149, 1.1154,-0.1303, 0.0051,-0.2851, 1.2800},
+ {1.0024,-0.0149, 0.0124,-0.2569, 1.3432,-0.0864,-0.0043,-0.1306, 1.1349},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.1003,-0.0493,-0.0510,-0.1607, 1.2748,-0.1142,-0.0059,-0.3161, 1.3220}}},
+ {0x5F,
+ {{1.0697,-0.0561,-0.0137,-0.0824, 1.1291,-0.0467,-0.0390,-0.5218, 1.5608},
+ {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2606,-0.2125,-0.0482,-0.0567, 1.1441,-0.0874,-0.0431,-0.3490, 1.3921}}},
+ {0x61,
+ {{1.0921,-0.0722,-0.0199,-0.0831, 1.1550,-0.0718,-0.0452,-0.3721, 1.4173},
+ {1.0168,-0.0168, 0.0000,-0.0953, 1.1928,-0.0975,-0.0012,-0.1235, 1.1247},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2603,-0.2763, 0.0155,-0.0398, 1.1033,-0.0635,-0.0249,-0.2675, 1.2924}}},
+ {0x63,
+ {{1.0976,-0.0789,-0.0187,-0.0958, 1.1821,-0.0863,-0.0565,-0.4179, 1.4744},
+ {1.0250,-0.0267, 0.0016,-0.0930, 1.2108,-0.1178,-0.0022,-0.1296, 1.1317},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.3111,-0.2979,-0.0132,-0.0441, 1.1148,-0.0707,-0.0348,-0.2971, 1.3319}}},
+ {0x65,
+ {{1.0359,-0.0146,-0.0213,-0.0752, 1.0963,-0.0211,-0.0456,-0.3238, 1.3693},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x66,
+ {{1.0878,-0.0667,-0.0211,-0.0892, 1.1513,-0.0622,-0.0654,-0.5175, 1.5829},
+ {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2688,-0.2522,-0.0166,-0.0559, 1.1291,-0.0733,-0.0377,-0.3519, 1.3896}}},
+ {0x68,
+ {{1.0950,-0.0646,-0.0305,-0.0792, 1.1398,-0.0606,-0.0123,-0.5175, 1.5298},
+ {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2697,-0.2501,-0.0195,-0.0351, 1.1236,-0.0885,-0.0131,-0.3268, 1.3400}}},
+ {0x6A,
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x6B,
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x6C,
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x70,
+ {{0.9533, 0.0885,-0.0418, 0.0033, 1.0627,-0.0660,-0.0137,-0.1904, 1.2041},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x71,
+ {{1.0697,-0.0561,-0.0137,-0.0824, 1.1291,-0.0467,-0.0390,-0.5218, 1.5608},
+ {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2606,-0.2125,-0.0482,-0.0567, 1.1441,-0.0874,-0.0431,-0.3490, 1.3921}}},
+ {0x73,
+ {{1.0828,-0.0739,-0.0089,-0.0895, 1.1597,-0.0702,-0.0531,-0.4291, 1.4822},
+ {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2579,-0.2384,-0.0195,-0.0569, 1.1454,-0.0884,-0.0411,-0.3072, 1.3483}}},
+ {0x75,
+ {{1.0828,-0.0739,-0.0089,-0.0895, 1.1597,-0.0702,-0.0531,-0.4291, 1.4822},
+ {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.2579,-0.2384,-0.0195,-0.0569, 1.1454,-0.0884,-0.0411,-0.3072, 1.3483}}},
+ {0x77,
+ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x78,
+ {{1.0784,-0.0560,-0.0224,-0.1793, 1.2234,-0.0441,-0.0041,-0.2636, 1.2677},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x79,
+ {{1.0614,-0.0361,-0.0253,-0.1081, 1.1320,-0.0240,-0.0536,-0.2045, 1.2580},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x7A,
+ {{1.1754,-0.1173,-0.0580,-0.0687, 1.1307,-0.0620,-0.0255,-0.4699, 1.4954},
+ {1.0150,-0.0173, 0.0022,-0.0853, 1.2238,-0.1384,-0.0073,-0.1490, 1.1562},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4283,-0.4335, 0.0052,-0.0170, 1.1308,-0.1138,-0.0147,-0.2230, 1.2377}}},
+ {0x7C,
+ {{1.2470,-0.2041,-0.0429,-0.1920, 1.2918,-0.0998,-0.0100,-0.2503, 1.2603},
+ {1.0050,-0.0076, 0.0026,-0.2532, 1.1289, 0.1243,-0.0733,-0.0960, 1.1693},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4724,-0.4599,-0.0125,-0.0876, 1.1562,-0.0686,-0.0097,-0.2278, 1.2375}}},
+ {0x7E,
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x7F,
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x80,
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x81,
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x82,
+ {{1.1622,-0.1102,-0.0519,-0.0717, 1.1060,-0.0343,-0.0248,-0.4138, 1.4385},
+ {0.9913, 0.0082, 0.0005,-0.1259, 1.0452, 0.0807,-0.0072,-0.0767, 1.0839},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.3900,-0.3008,-0.0892,-0.0254, 1.0890,-0.0636,-0.0300,-0.2501, 1.2801}}},
+ {0x84,
+ {{1.0934,-0.0042,-0.0892, 0.0052, 1.1019,-0.1071, 0.0259,-0.2651, 1.2392},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x85,
+ {{1.0534, 0.0399,-0.0934, 0.0098, 1.0589,-0.0687, 0.0016,-0.1131, 1.1115},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x86,
+ {{1.1945,-0.1413,-0.0532,-0.1929, 1.2525,-0.0596,-0.0235,-0.2761, 1.2996},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x87,
+ {{1.1978,-0.1417,-0.0561,-0.0852, 1.1610,-0.0758,-0.0395,-0.3212, 1.3607},
+ {1.0000, 0.0009,-0.0009,-0.1268, 1.0523, 0.0745,-0.0075,-0.0873, 1.0948},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4475,-0.3957,-0.0518,-0.0138, 1.0644,-0.0506,-0.0199,-0.2050, 1.2249}}},
+ {0x97,
+ {{1.1115,-0.0377,-0.0738,-0.0658, 1.0624, 0.0034, 0.0042,-0.2883, 1.2841},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x89,
+ {{1.1115,-0.0377,-0.0738,-0.0658, 1.0624, 0.0034, 0.0042,-0.2883, 1.2841},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8A,
+ {{1.1221,-0.0396,-0.0825,-0.0718, 1.0822,-0.0104, 0.0112,-0.2995, 1.2883},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8B,
+ {{1.2402,-0.1891,-0.0511,-0.1535, 1.2008,-0.0473,-0.0316,-0.3293, 1.3609},
+ {1.0027,-0.0048, 0.0021,-0.2067, 1.0878, 0.1189,-0.0408,-0.0767, 1.1175},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4524,-0.4346,-0.0178,-0.0601, 1.1273,-0.0672,-0.0173,-0.1823, 1.1996}}},
+ {0x8D,
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8E,
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x8F,
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x90,
+ {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x91,
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x92,
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x93,
+ {{1.0934,-0.0042,-0.0892, 0.0052, 1.1019,-0.1071, 0.0259,-0.2651, 1.2392},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x96,
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x98,
+ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x99,
+ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9A,
+ {{1.0779, 0.0132,-0.0911, 0.0214, 1.1003,-0.1217, 0.0109,-0.1487, 1.1378},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9B,
+ {{1.0779, 0.0132,-0.0911, 0.0214, 1.1003,-0.1217, 0.0109,-0.1487, 1.1378},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9C,
+ {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9D,
+ {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9E,
+ {{1.0534, 0.0399,-0.0934, 0.0098, 1.0589,-0.0687, 0.0016,-0.1131, 1.1115},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0x9F,
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0xA0,
+ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}},
+ {0xA1,
+ {{1.2578,-0.2140,-0.0438,-0.1939, 1.2856,-0.0917,-0.0258,-0.2642, 1.2900},
+ {0.9989,-0.0018, 0.0029,-0.2608, 1.1305, 0.1303,-0.0802,-0.0807, 1.1609},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4431,-0.4193,-0.0238,-0.0915, 1.1507,-0.0592,-0.0226,-0.1978, 1.2204}}},
+};
+
+const EpsonScanHardRec *epson_scan_hard = _epson_scan_hard;
diff --git a/backend/tests/45532d48333030.xml b/backend/tests/45532d48333030.xml
new file mode 100644
index 0000000..e7d6a04
--- /dev/null
+++ b/backend/tests/45532d48333030.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<scanner_entry>
+
+ <interfaces>
+ <usb vendor="04b8" product="012b"/>
+ <net status="unsupported" note="option"/>
+ </interfaces>
+
+ <device>
+ <firmware name="ES-H300"/>
+ <model name="GT-2500"/>
+ <model name="ES-H300" region="japan"/>
+ </device>
+
+ <profile-set>
+ <profile type="reflective">
+ <rr value=" 1.0359"/><rg value="-0.0146"/><rb value="-0.0213"/>
+ <gr value="-0.0752"/><gg value=" 1.0963"/><gb value="-0.0211"/>
+ <br value="-0.0456"/><bg value="-0.3238"/><bb value=" 1.3693"/>
+ </profile>
+ </profile-set>
+
+ <command-set type="extended" level="B8">
+ <command name="set_focus_position" code="1B70" status="disabled"/>
+ <command name="feed" code="19" status="disabled"/>
+ </command-set>
+
+</scanner_entry>
diff --git a/backend/tests/47542d58393730.xml b/backend/tests/47542d58393730.xml
new file mode 100644
index 0000000..26cb7e7
--- /dev/null
+++ b/backend/tests/47542d58393730.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<scanner_entry>
+
+ <interfaces>
+ <usb vendor="04b8" product="012b"/>
+ <net status="unsupported" note="option"/>
+ </interfaces>
+
+ <profile-set>
+ <profile type="reflective">
+ <rr value=" 1.1978"/><rg value="-0.1417"/><rb value="-0.0561"/>
+ <gr value="-0.0852"/><gg value=" 1.161"/><gb value="-0.0758"/>
+ <br value="-0.0395"/><bg value="-0.3212"/><bb value=" 1.3607"/>
+ </profile>
+ <profile type="color negative">
+ <rr value=" 1.000"/><rg value="0.0009"/><rb value="-0.0009"/>
+ <gr value="-0.1268"/><gg value=" 1.0523"/><gb value="0.0745"/>
+ <br value="-0.0075"/><bg value="-0.0873"/><bb value=" 1.0948"/>
+ </profile>
+ <profile type="positive">
+ <rr value=" 1.4475"/><rg value="-0.3957"/><rb value="-0.0518"/>
+ <gr value="-0.0138"/><gg value=" 1.0644"/><gb value="-0.0506"/>
+ <br value="-0.0199"/><bg value="-0.205"/><bb value=" 1.2249"/>
+ </profile>
+ </profile-set>
+
+ <device>
+ <firmware name="GT-X970"/>
+ </device>
+
+ <command-set type="extended" level="B8">
+ <command name="set_focus_position" code="1B70" status="disabled"/>
+ <command name="feed" code="19" status="disabled"/>
+ <command name="lock"/>
+ <command name="unlock"/>
+ </command-set>
+
+</scanner_entry>
diff --git a/backend/tests/50657266656374696f6e363130.xml b/backend/tests/50657266656374696f6e363130.xml
new file mode 100644
index 0000000..2ac6c46
--- /dev/null
+++ b/backend/tests/50657266656374696f6e363130.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<scanner_entry>
+
+ <interfaces>
+ <usb vendor="04b8" product="012b"/>
+ <net status="unsupported" note="option"/>
+ </interfaces>
+
+ <device>
+ <firmware name="Perfection610"/>
+ <model name="Perfection 610"/>
+ </device>
+
+ <profile-set>
+ <profile type="reflective">
+ <rr value=" 1.1442"/><rg value="-0.0705"/><rb value="-0.0737"/>
+ <gr value="-0.0702"/><gg value=" 1.1013"/><gb value="-0.0311"/>
+ <br value="-0.0080"/><bg value="-0.3588"/><bb value=" 1.3668"/>
+ </profile>
+ </profile-set>
+
+ <command-set type="extended" level="B8">
+ <command name="set_focus_position" code="1B70" status="disabled"/>
+ <command name="feed" code="19" status="disabled"/>
+ </command-set>
+
+</scanner_entry>
diff --git a/backend/tests/Makefile.am b/backend/tests/Makefile.am
new file mode 100644
index 0000000..ef275d1
--- /dev/null
+++ b/backend/tests/Makefile.am
@@ -0,0 +1,79 @@
+## Makefile.am -- an -*- automake -*- template for Makefile.in
+## Copyright (C) 2008 SEIKO EPSON CORPORATION
+##
+## License: GPLv2+
+## Authors: AVASYS CORPORATION
+##
+## This file is part of the "Image Scan!" build infra-structure.
+##
+## The "Image Scan!" build infra-structure 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 ought to have received a copy of the GNU General Public License
+## along with this package. If not, see <http://www.gnu.org/licenses/>.
+
+
+AM_CPPFLAGS = $(XML_CFLAGS)
+
+check_PROGRAMS = \
+ xmltest
+
+TESTS =
+
+xmltest_LDADD = ../libepkowa.la
+xmltest_SOURCES = xmltest.c xmltest.h
+
+EXTRA_DIST = \
+ 47542d58393730.xml \
+ 45532d48333030.xml \
+ 50657266656374696f6e363130.xml \
+ xmltest-runner.sh
+
+if HAVE_CXXTESTGEN
+
+check_PROGRAMS += \
+ cfg-obj \
+ net-obj \
+ network \
+ model-info
+
+TESTS += \
+ cfg-obj \
+ net-obj \
+ model-info
+
+cfg_obj_LDADD = ../libepkowa.la
+cfg_obj_SOURCES = \
+ test-cfg-obj.cc \
+ test-cfg-obj.hh
+
+net_obj_LDADD = ../libepkowa.la
+net_obj_SOURCES = \
+ test-net-obj.cc \
+ test-net-obj.hh
+
+model_info_LDADD = ../libepkowa.la
+model_info_SOURCES = \
+ test-model-info.cc \
+ test-model-info.hh
+
+# Use the CxxTest code generator on all files matching test-*.hh to
+# create the corresponding test-*.cc file. This convention is used
+# so that we can still use regular source code without this getting
+# clobbered as soon as we change the header file.
+
+.hh.cc:
+ @if test xtest- = "x`echo $^ | sed -n 's|^\(test-\).*|\1|p'`"; \
+ then \
+ echo "$(CXXTESTGEN) $(CXXTESTGEN_OPTS) -o $@ $^"; \
+ $(CXXTESTGEN) $(CXXTESTGEN_OPTS) -o $@ $^; \
+ fi
+
+endif ## HAVE_CXXTESTGEN
diff --git a/backend/tests/network.c b/backend/tests/network.c
new file mode 100644
index 0000000..8dd6dec
--- /dev/null
+++ b/backend/tests/network.c
@@ -0,0 +1,86 @@
+/* network.c -- a network mock program for use by unit tests
+ * Copyright (C) 2008 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of Image Scan!'s SANE backend test suite.
+ *
+ * Image Scan!'s SANE backend test suite 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+int
+main (int argc, char *argv[])
+{
+ socklen_t n;
+ int r;
+ int s;
+ int as;
+ struct sockaddr_in addr;
+
+ s = socket (AF_INET, SOCK_STREAM, 0);
+ if (0 > s)
+ {
+ perror ("socket");
+ return EXIT_FAILURE;
+ }
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_family = PF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ r = bind (s, (struct sockaddr *) &addr, sizeof (addr));
+ if (0 > r)
+ {
+ perror ("bind");
+ return EXIT_FAILURE;
+ }
+
+ n = sizeof (addr);
+ r = getsockname (s, (struct sockaddr *) &addr, &n);
+ if (0 > r)
+ {
+ perror ("getsockname");
+ return EXIT_FAILURE;
+ }
+
+ setvbuf (stdout, NULL, _IONBF, BUFSIZ);
+ fprintf (stdout, "%d\n", ntohs (addr.sin_port));
+ fclose (stdout); /* not strictly needed */
+
+ listen (s, 0);
+ as = accept (s, (struct sockaddr*) &addr, (socklen_t*) &n);
+
+ pause ();
+
+ close (s);
+ close (as);
+
+ return EXIT_SUCCESS;
+}
diff --git a/backend/tests/test-cfg-obj.cc b/backend/tests/test-cfg-obj.cc
new file mode 100644
index 0000000..9fe213f
--- /dev/null
+++ b/backend/tests/test-cfg-obj.cc
@@ -0,0 +1,60 @@
+/* Generated file, do not edit */
+
+#ifndef CXXTEST_RUNNING
+#define CXXTEST_RUNNING
+#endif
+
+#define _CXXTEST_HAVE_STD
+#include <cxxtest/TestListener.h>
+#include <cxxtest/TestTracker.h>
+#include <cxxtest/TestRunner.h>
+#include <cxxtest/RealDescriptions.h>
+#include <cxxtest/ErrorPrinter.h>
+
+int main() {
+ return CxxTest::ErrorPrinter().run();
+}
+#include "test-cfg-obj.hh"
+
+static test_cfg_obj suite_test_cfg_obj;
+
+static CxxTest::List Tests_test_cfg_obj = { 0, 0 };
+CxxTest::StaticSuiteDescription suiteDescription_test_cfg_obj( "test-cfg-obj.hh", 89, "test_cfg_obj", suite_test_cfg_obj, Tests_test_cfg_obj );
+
+static class TestDescription_test_cfg_obj_test_life_cycle : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_cfg_obj_test_life_cycle() : CxxTest::RealTestDescription( Tests_test_cfg_obj, suiteDescription_test_cfg_obj, 93, "test_life_cycle" ) {}
+ void runTest() { suite_test_cfg_obj.test_life_cycle(); }
+} testDescription_test_cfg_obj_test_life_cycle;
+
+static class TestDescription_test_cfg_obj_test_life_cycle_status : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_cfg_obj_test_life_cycle_status() : CxxTest::RealTestDescription( Tests_test_cfg_obj, suiteDescription_test_cfg_obj, 102, "test_life_cycle_status" ) {}
+ void runTest() { suite_test_cfg_obj.test_life_cycle_status(); }
+} testDescription_test_cfg_obj_test_life_cycle_status;
+
+static class TestDescription_test_cfg_obj_test_key_query : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_cfg_obj_test_key_query() : CxxTest::RealTestDescription( Tests_test_cfg_obj, suiteDescription_test_cfg_obj, 115, "test_key_query" ) {}
+ void runTest() { suite_test_cfg_obj.test_key_query(); }
+} testDescription_test_cfg_obj_test_key_query;
+
+static class TestDescription_test_cfg_obj_test_option_value_query : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_cfg_obj_test_option_value_query() : CxxTest::RealTestDescription( Tests_test_cfg_obj, suiteDescription_test_cfg_obj, 123, "test_option_value_query" ) {}
+ void runTest() { suite_test_cfg_obj.test_option_value_query(); }
+} testDescription_test_cfg_obj_test_option_value_query;
+
+static class TestDescription_test_cfg_obj_test_key_mutator : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_cfg_obj_test_key_mutator() : CxxTest::RealTestDescription( Tests_test_cfg_obj, suiteDescription_test_cfg_obj, 134, "test_key_mutator" ) {}
+ void runTest() { suite_test_cfg_obj.test_key_mutator(); }
+} testDescription_test_cfg_obj_test_key_mutator;
+
+static class TestDescription_test_cfg_obj_test_net_registration : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_cfg_obj_test_net_registration() : CxxTest::RealTestDescription( Tests_test_cfg_obj, suiteDescription_test_cfg_obj, 146, "test_net_registration" ) {}
+ void runTest() { suite_test_cfg_obj.test_net_registration(); }
+} testDescription_test_cfg_obj_test_net_registration;
+
+#include <cxxtest/Root.cpp>
diff --git a/backend/tests/test-cfg-obj.hh b/backend/tests/test-cfg-obj.hh
new file mode 100644
index 0000000..ab8b851
--- /dev/null
+++ b/backend/tests/test-cfg-obj.hh
@@ -0,0 +1,254 @@
+// test-cfg-obj.hh -- test suite for configuration objects
+// Copyright (C) 2008 SEIKO EPSON CORPORATION
+//
+// License: GPLv2+|iscan
+// Authors: AVASYS CORPORATION
+//
+// This file is part of Image Scan!'s SANE backend test suite.
+//
+// Image Scan!'s SANE backend test suite 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 ought to have received a copy of the GNU General Public License
+// along with this package. If not, see <http://www.gnu.org/licenses/>.
+//
+//
+// Linking Image Scan!'s SANE backend statically or dynamically with
+// other modules is making a combined work based on this SANE backend.
+// Thus, the terms and conditions of the GNU General Public License
+// cover the whole combination.
+//
+// As a special exception, the copyright holders of Image Scan!'s SANE
+// backend give you permission to link Image Scan!'s SANE backend with
+// SANE frontends that communicate with Image Scan!'s SANE backend
+// solely through the SANE Application Programming Interface,
+// regardless of the license terms of these SANE frontends, and to
+// copy and distribute the resulting combined work under terms of your
+// choice, provided that every copy of the combined work is
+// accompanied by a complete copy of the source code of Image Scan!'s
+// SANE backend (the version of Image Scan!'s SANE backend used to
+// produce the combined work), being distributed under the terms of
+// the GNU General Public License plus this exception. An independent
+// module is a module which is not derived from or based on Image
+// Scan!'s SANE backend.
+//
+// As a special exception, the copyright holders of Image Scan!'s SANE
+// backend give you permission to link Image Scan!'s SANE backend with
+// independent modules that communicate with Image Scan!'s SANE
+// backend solely through the "Interpreter" interface, regardless of
+// the license terms of these independent modules, and to copy and
+// distribute the resulting combined work under terms of your choice,
+// provided that every copy of the combined work is accompanied by a
+// complete copy of the source code of Image Scan!'s SANE backend (the
+// version of Image Scan!'s SANE backend used to produce the combined
+// work), being distributed under the terms of the GNU General Public
+// License plus this exception. An independent module is a module
+// which is not derived from or based on Image Scan!'s SANE backend.
+//
+// Note that people who make modified versions of Image Scan!'s SANE
+// backend are not obligated to grant special exceptions for their
+// modified versions; it is their choice whether to do so. The GNU
+// General Public License gives permission to release a modified
+// version without this exception; this exception also makes it
+// possible to release a modified version which carries forward this
+// exception.
+
+
+#ifndef included_test_cfg_obj_hh
+#define included_test_cfg_obj_hh
+
+#ifndef __cplusplus
+#error "This is a C++ include file. Use a C++ compiler to compile"
+#error "code that includes this file."
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "../cfg-obj.h"
+#include <cxxtest/TestSuite.h>
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <string>
+#include <unistd.h>
+
+#include "../message.h"
+
+
+class test_cfg_obj : public CxxTest::TestSuite
+{
+public:
+
+ void test_life_cycle (void)
+ {
+ cfg = cfg_init (dir.c_str (), NULL);
+ TS_ASSERT (cfg);
+
+ cfg = cfg_exit (cfg);
+ TS_ASSERT (!cfg);
+ }
+
+ void test_life_cycle_status (void)
+ {
+ SANE_Status status = SANE_STATUS_NO_MEM;
+
+ cfg = cfg_init (dir.c_str (), &status);
+ TS_ASSERT (cfg);
+
+ TS_ASSERT (SANE_STATUS_NO_MEM != status);
+
+ cfg = cfg_exit (cfg);
+ TS_ASSERT (!cfg);
+ }
+
+ void test_key_query (void)
+ {
+ cfg = cfg_init (dir.c_str (), NULL);
+ TS_ASSERT (cfg);
+
+ TS_ASSERT (cfg_has (cfg, CFG_KEY_USB));
+ }
+
+ void test_option_value_query (void)
+ {
+ cfg = cfg_init (dir.c_str (), NULL);
+ TS_ASSERT (cfg);
+
+ TS_ASSERT (cfg_has (cfg, CFG_KEY_OPTION));
+ TS_ASSERT (cfg_has_value (cfg, CFG_KEY_OPTION, "prefer-adf"));
+ TS_ASSERT (!cfg_has_value (cfg, CFG_KEY_OPTION, "not-a-valid-option"));
+ TS_ASSERT (!cfg_has_value (cfg, CFG_KEY_OPTION, "not-in-config"));
+ }
+
+ void test_key_mutator (void)
+ {
+ cfg = cfg_init (dir.c_str (), NULL);
+ TS_ASSERT (cfg);
+
+ TS_ASSERT (cfg_has (cfg, CFG_KEY_USB));
+ cfg_set (cfg, CFG_KEY_USB, false);
+ TS_ASSERT (!cfg_has (cfg, CFG_KEY_USB));
+ cfg_set (cfg, CFG_KEY_USB, true);
+ TS_ASSERT (cfg_has (cfg, CFG_KEY_USB));
+ }
+
+ void test_net_registration (void)
+ {
+ add_cfg_entry ("net\n");
+
+ cfg = cfg_init (dir.c_str (), NULL);
+ TS_ASSERT (!cfg_has (cfg, CFG_KEY_NET));
+ }
+
+private:
+
+ void *cfg;
+ std::string dir;
+
+ /*! Create a temporary, minimal configuration directory.
+
+ The configuration directory is made available to the unit test
+ environment via the \c SANE_CONFIG_DIR environment variable so
+ configuration objects (should) take note. In addition, a very
+ minimal configuration file, containing the \c usb keyword and
+ a few options is created in this directory as well.
+ */
+ void setUp (void)
+ {
+ int result;
+ msg_init ();
+
+ char dirname_template[] = ".cfg-obj-XXXXXX";
+ char *dirname = mkdtemp (dirname_template);
+ if (!dirname)
+ {
+ err_fatal ("mkdtemp: %s", strerror (errno));
+ }
+ require (dirname);
+
+ result = chdir (dirname);
+ require (0 == result);
+
+ std::ofstream ofs (cfg_file_name);
+ ofs << "usb\n";
+ ofs << "option prefer-adf \n";
+ ofs << "option not-a-valid-option\n";
+
+ result = chdir ("..");
+ require (0 == result);
+
+ result = setenv ("SANE_CONFIG_DIR", dirname, true);
+ require (0 == result);
+
+ cfg = NULL;
+ dir = dirname;
+ }
+
+ /*! Attempts to undo the effects of setUp().
+
+ The environment's \c SANE_CONFIG_DIR is unset and the temporary
+ configuration directory is recursively removed. Failures to
+ undo any of the necessary actions are logged but will not cause
+ a test to fail.
+ */
+ void tearDown (void)
+ {
+ int result;
+
+ if (0 != unsetenv ("SANE_CONFIG_DIR"))
+ {
+ err_minor ("unsetenv: %s", strerror (errno));
+ }
+
+ if (0 != chdir (dir.c_str ()))
+ {
+ err_minor ("%s: %s", dir.c_str (), strerror (errno));
+ }
+ else
+ {
+ if (0 != unlink (cfg_file_name))
+ {
+ err_minor ("%s: %s", cfg_file_name, strerror (errno));
+ }
+ if (0 != unlink ("usb"))
+ {
+ err_minor ("%s: %s", "usb", strerror (errno));
+ }
+ result = chdir ("..");
+ require (0 == result);
+ }
+
+ if (0 != rmdir (dir.c_str ()))
+ {
+ err_minor ("%s: %s", dir.c_str (), strerror (errno));
+ }
+ dir = "";
+
+ cfg = cfg_exit (cfg);
+ promise (!cfg);
+ }
+
+ void add_cfg_entry (const char *str)
+ {
+ chdir (dir.c_str ());
+ std::ofstream ofs (cfg_file_name,
+ std::ios_base::out | std::ios_base::app);
+
+ ofs << str;
+ chdir ("..");
+ }
+};
+
+
+#endif /* !defined (included_test_cfg_obj_hh) */
diff --git a/backend/tests/test-model-info.cc b/backend/tests/test-model-info.cc
new file mode 100644
index 0000000..bb5eb7e
--- /dev/null
+++ b/backend/tests/test-model-info.cc
@@ -0,0 +1,83 @@
+/* Generated file, do not edit */
+
+#ifndef CXXTEST_RUNNING
+#define CXXTEST_RUNNING
+#endif
+
+#define _CXXTEST_HAVE_STD
+#include <cxxtest/TestListener.h>
+#include <cxxtest/TestTracker.h>
+#include <cxxtest/TestRunner.h>
+#include <cxxtest/RealDescriptions.h>
+#include <cxxtest/ErrorPrinter.h>
+
+int main() {
+ return CxxTest::ErrorPrinter().run();
+}
+#include "test-model-info.hh"
+
+static test_model_cache_info suite_test_model_cache_info;
+
+static CxxTest::List Tests_test_model_cache_info = { 0, 0 };
+CxxTest::StaticSuiteDescription suiteDescription_test_model_cache_info( "test-model-info.hh", 111, "test_model_cache_info", suite_test_model_cache_info, Tests_test_model_cache_info );
+
+static class TestDescription_test_model_cache_info_test_cache_life_cycle : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_cache_info_test_cache_life_cycle() : CxxTest::RealTestDescription( Tests_test_model_cache_info, suiteDescription_test_model_cache_info, 115, "test_cache_life_cycle" ) {}
+ void runTest() { suite_test_model_cache_info.test_cache_life_cycle(); }
+} testDescription_test_model_cache_info_test_cache_life_cycle;
+
+static class TestDescription_test_model_cache_info_test_cache_life_cycle_status : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_cache_info_test_cache_life_cycle_status() : CxxTest::RealTestDescription( Tests_test_model_cache_info, suiteDescription_test_model_cache_info, 124, "test_cache_life_cycle_status" ) {}
+ void runTest() { suite_test_model_cache_info.test_cache_life_cycle_status(); }
+} testDescription_test_model_cache_info_test_cache_life_cycle_status;
+
+static class TestDescription_test_model_cache_info_test_cache_unique_entries : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_cache_info_test_cache_unique_entries() : CxxTest::RealTestDescription( Tests_test_model_cache_info, suiteDescription_test_model_cache_info, 137, "test_cache_unique_entries" ) {}
+ void runTest() { suite_test_model_cache_info.test_cache_unique_entries(); }
+} testDescription_test_model_cache_info_test_cache_unique_entries;
+
+static test_model_info suite_test_model_info;
+
+static CxxTest::List Tests_test_model_info = { 0, 0 };
+CxxTest::StaticSuiteDescription suiteDescription_test_model_info( "test-model-info.hh", 154, "test_model_info", suite_test_model_info, Tests_test_model_info );
+
+static class TestDescription_test_model_info_test_get_non_existent_model : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_info_test_get_non_existent_model() : CxxTest::RealTestDescription( Tests_test_model_info, suiteDescription_test_model_info, 158, "test_get_non_existent_model" ) {}
+ void runTest() { suite_test_model_info.test_get_non_existent_model(); }
+} testDescription_test_model_info_test_get_non_existent_model;
+
+static class TestDescription_test_model_info_test_get_non_existent_info : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_info_test_get_non_existent_info() : CxxTest::RealTestDescription( Tests_test_model_info, suiteDescription_test_model_info, 165, "test_get_non_existent_info" ) {}
+ void runTest() { suite_test_model_info.test_get_non_existent_info(); }
+} testDescription_test_model_info_test_get_non_existent_info;
+
+static class TestDescription_test_model_info_test_get_info_from_loaded_cache : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_info_test_get_info_from_loaded_cache() : CxxTest::RealTestDescription( Tests_test_model_info, suiteDescription_test_model_info, 171, "test_get_info_from_loaded_cache" ) {}
+ void runTest() { suite_test_model_info.test_get_info_from_loaded_cache(); }
+} testDescription_test_model_info_test_get_info_from_loaded_cache;
+
+static class TestDescription_test_model_info_test_get_existing_model : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_info_test_get_existing_model() : CxxTest::RealTestDescription( Tests_test_model_info, suiteDescription_test_model_info, 186, "test_get_existing_model" ) {}
+ void runTest() { suite_test_model_info.test_get_existing_model(); }
+} testDescription_test_model_info_test_get_existing_model;
+
+static class TestDescription_test_model_info_test_default_values : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_info_test_default_values() : CxxTest::RealTestDescription( Tests_test_model_info, suiteDescription_test_model_info, 195, "test_default_values" ) {}
+ void runTest() { suite_test_model_info.test_default_values(); }
+} testDescription_test_model_info_test_default_values;
+
+static class TestDescription_test_model_info_test_profile_equality_values : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_model_info_test_profile_equality_values() : CxxTest::RealTestDescription( Tests_test_model_info, suiteDescription_test_model_info, 207, "test_profile_equality_values" ) {}
+ void runTest() { suite_test_model_info.test_profile_equality_values(); }
+} testDescription_test_model_info_test_profile_equality_values;
+
+#include <cxxtest/Root.cpp>
diff --git a/backend/tests/test-model-info.hh b/backend/tests/test-model-info.hh
new file mode 100644
index 0000000..1796b03
--- /dev/null
+++ b/backend/tests/test-model-info.hh
@@ -0,0 +1,241 @@
+// test-model-info.hh -- test suite for model info objects
+// Copyright (C) 2010 SEIKO EPSON CORPORATION
+//
+// License: GPLv2+|iscan
+// Authors: AVASYS CORPORATION
+//
+// This file is part of Image Scan!'s SANE backend test suite.
+//
+// Image Scan!'s SANE backend test suite 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 ought to have received a copy of the GNU General Public License
+// along with this package. If not, see <http://www.gnu.org/licenses/>.
+//
+//
+// Linking Image Scan!'s SANE backend statically or dynamically with
+// other modules is making a combined work based on this SANE backend.
+// Thus, the terms and conditions of the GNU General Public License
+// cover the whole combination.
+//
+// As a special exception, the copyright holders of Image Scan!'s SANE
+// backend give you permission to link Image Scan!'s SANE backend with
+// SANE frontends that communicate with Image Scan!'s SANE backend
+// solely through the SANE Application Programming Interface,
+// regardless of the license terms of these SANE frontends, and to
+// copy and distribute the resulting combined work under terms of your
+// choice, provided that every copy of the combined work is
+// accompanied by a complete copy of the source code of Image Scan!'s
+// SANE backend (the version of Image Scan!'s SANE backend used to
+// produce the combined work), being distributed under the terms of
+// the GNU General Public License plus this exception. An independent
+// module is a module which is not derived from or based on Image
+// Scan!'s SANE backend.
+//
+// As a special exception, the copyright holders of Image Scan!'s SANE
+// backend give you permission to link Image Scan!'s SANE backend with
+// independent modules that communicate with Image Scan!'s SANE
+// backend solely through the "Interpreter" interface, regardless of
+// the license terms of these independent modules, and to copy and
+// distribute the resulting combined work under terms of your choice,
+// provided that every copy of the combined work is accompanied by a
+// complete copy of the source code of Image Scan!'s SANE backend (the
+// version of Image Scan!'s SANE backend used to produce the combined
+// work), being distributed under the terms of the GNU General Public
+// License plus this exception. An independent module is a module
+// which is not derived from or based on Image Scan!'s SANE backend.
+//
+// Note that people who make modified versions of Image Scan!'s SANE
+// backend are not obligated to grant special exceptions for their
+// modified versions; it is their choice whether to do so. The GNU
+// General Public License gives permission to release a modified
+// version without this exception; this exception also makes it
+// possible to release a modified version which carries forward this
+// exception.
+
+
+#ifndef included_test_model_info_hh
+#define included_test_model_info_hh
+
+#ifndef __cplusplus
+#error "This is a C++ include file. Use a C++ compiler to compile"
+#error "code that includes this file."
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "../model-info.h"
+#include <cxxtest/TestSuite.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+#include "../list.h"
+#include "../message.h"
+#include "../get-infofile.h"
+
+struct base
+{
+ std::string dir;
+ void *cache;
+
+ void setUp (void)
+ {
+ // Log at least broken promises and unmet requirements.
+ setenv ("SANE_DEBUG_EPKOWA", "FATAL", false);
+ msg_init ();
+
+ char *srcdir = getenv ("srcdir");
+ dir = (srcdir ? srcdir : ".");
+
+ cache = NULL;
+ }
+
+ void tearDown (void)
+ {
+ cache = model_info_cache_exit (cache);
+ promise (!cache);
+ }
+};
+
+class test_model_cache_info : public CxxTest::TestSuite, public base
+{
+public:
+
+ void test_cache_life_cycle (void)
+ {
+ cache = model_info_cache_init (dir.c_str (), NULL);
+ TS_ASSERT (cache);
+
+ cache = model_info_cache_exit (cache);
+ TS_ASSERT (!cache);
+ }
+
+ void test_cache_life_cycle_status (void)
+ {
+ SANE_Status status = SANE_STATUS_NO_MEM;
+
+ cache = model_info_cache_init (dir.c_str (), &status);
+ TS_ASSERT (cache);
+
+ TS_ASSERT (SANE_STATUS_NO_MEM != status);
+
+ cache = model_info_cache_exit (cache);
+ TS_ASSERT (!cache);
+ }
+
+ void test_cache_unique_entries (void)
+ {
+ cache = model_info_cache_init (dir.c_str (), NULL);
+ TS_ASSERT (cache);
+
+ for (int i = 0; i < 10; ++i) {
+ model_info_cache_get_info ("GT-X970", NULL);
+ model_info_cache_get_info ("ES-H300", NULL);
+ }
+ TS_ASSERT_EQUALS (2, list_size (static_cast<list *> (cache)));
+ }
+
+private:
+ void setUp (void) { base::setUp (); }
+ void tearDown (void) { base::setUp (); }
+};
+
+class test_model_info : public CxxTest::TestSuite, public base
+{
+public:
+
+ void test_get_non_existent_model (void)
+ {
+ char *model = model_info_cache_get_model (bad_fw_name);
+ TS_ASSERT_EQUALS (std::string (model), std::string (bad_fw_name));
+ free (model);
+ }
+
+ void test_get_non_existent_info (void)
+ {
+ const void *info = model_info_cache_get_info (bad_fw_name, NULL);
+ TS_ASSERT (info);
+ }
+
+ void test_get_info_from_loaded_cache (void)
+ {
+ // load cache
+ model_info_cache_get_info (bad_fw_name, NULL);
+ model_info_cache_get_info ("GT-X970", NULL); // XML data
+ model_info_cache_get_info ("ES-H300", NULL); // XML data
+
+ // get info
+ const void *info = model_info_cache_get_info ("GT-X970", NULL);
+ TS_ASSERT (info);
+ TS_ASSERT (model_info_cache_get_info ("ES-H300", NULL));
+ TS_ASSERT (model_info_cache_get_info (bad_fw_name, NULL));
+ TS_ASSERT_EQUALS (info, model_info_cache_get_info ("GT-X970", NULL));
+ }
+
+ void test_get_existing_model (void)
+ {
+ setenv ("TZ", "", true); // force UTC, i.e. overseas model name
+
+ char *model = model_info_cache_get_model ("ES-H300");
+ TS_ASSERT_EQUALS (std::string ("GT-2500"), std::string (model));
+ free (model);
+ }
+
+ void test_default_values (void)
+ {
+ const void *p = model_info_cache_get_info (bad_fw_name, NULL);
+
+ const char *name = model_info_get_name (p);
+ TS_ASSERT_EQUALS (std::string (bad_fw_name), std::string (name));
+
+ const EpsonScanHard profile = model_info_get_profile (p);
+ TS_ASSERT_SAME_DATA (profile, &epson_scan_hard[0],
+ sizeof (profile));
+ }
+
+ void test_profile_equality_values (void)
+ {
+ // Identical models but for their fw_names.
+ const void *p1 = model_info_cache_get_info ("GT-10000", NULL);
+ const void *p2 = model_info_cache_get_info ("ES-6000", NULL);
+
+ TS_ASSERT_DIFFERS (p1, p2);
+ TS_ASSERT_SAME_DATA (model_info_get_profile (p1),
+ model_info_get_profile (p2),
+ sizeof (const EpsonScanHard));
+ }
+
+private:
+
+ // A firmware name that is guaranteed not to be used.
+ static const char *bad_fw_name;
+
+ void setUp (void)
+ {
+ base::setUp ();
+ cache = model_info_cache_init (dir.c_str (), NULL);
+ TS_ASSERT (cache);
+ // The ESC/I spec has a 16 byte limit on the F/W name.
+ TS_ASSERT (16 < strlen (bad_fw_name));
+ }
+
+ void tearDown (void)
+ {
+ base::tearDown ();
+ }
+};
+
+const char * test_model_info::bad_fw_name = " __ BAD F/W NAME __ ";
+
+#endif /* !defined (included_test_model_info_hh) */
diff --git a/backend/tests/test-net-obj.cc b/backend/tests/test-net-obj.cc
new file mode 100644
index 0000000..c43610d
--- /dev/null
+++ b/backend/tests/test-net-obj.cc
@@ -0,0 +1,35 @@
+/* Generated file, do not edit */
+
+#ifndef CXXTEST_RUNNING
+#define CXXTEST_RUNNING
+#endif
+
+#include <cxxtest/TestListener.h>
+#include <cxxtest/TestTracker.h>
+#include <cxxtest/TestRunner.h>
+#include <cxxtest/RealDescriptions.h>
+#include <cxxtest/ErrorPrinter.h>
+
+int main() {
+ return CxxTest::ErrorPrinter().run();
+}
+#include "test-net-obj.hh"
+
+static test_net_obj suite_test_net_obj;
+
+static CxxTest::List Tests_test_net_obj = { 0, 0 };
+CxxTest::StaticSuiteDescription suiteDescription_test_net_obj( "test-net-obj.hh", 88, "test_net_obj", suite_test_net_obj, Tests_test_net_obj );
+
+static class TestDescription_test_net_obj_test_lifecycle : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_net_obj_test_lifecycle() : CxxTest::RealTestDescription( Tests_test_net_obj, suiteDescription_test_net_obj, 92, "test_lifecycle" ) {}
+ void runTest() { suite_test_net_obj.test_lifecycle(); }
+} testDescription_test_net_obj_test_lifecycle;
+
+static class TestDescription_test_net_obj_test_missing_program : public CxxTest::RealTestDescription {
+public:
+ TestDescription_test_net_obj_test_missing_program() : CxxTest::RealTestDescription( Tests_test_net_obj, suiteDescription_test_net_obj, 100, "test_missing_program" ) {}
+ void runTest() { suite_test_net_obj.test_missing_program(); }
+} testDescription_test_net_obj_test_missing_program;
+
+#include <cxxtest/Root.cpp>
diff --git a/backend/tests/test-net-obj.hh b/backend/tests/test-net-obj.hh
new file mode 100644
index 0000000..ef0c557
--- /dev/null
+++ b/backend/tests/test-net-obj.hh
@@ -0,0 +1,126 @@
+// test-net-obj.hh -- test suite for network objects
+// Copyright (C) 2008 SEIKO EPSON CORPORATION
+//
+// License: GPLv2+|iscan
+// Authors: AVASYS CORPORATION
+//
+// This file is part of Image Scan!'s SANE backend test suite.
+//
+// Image Scan!'s SANE backend test suite 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 ought to have received a copy of the GNU General Public License
+// along with this package. If not, see <http://www.gnu.org/licenses/>.
+//
+//
+// Linking Image Scan!'s SANE backend statically or dynamically with
+// other modules is making a combined work based on this SANE backend.
+// Thus, the terms and conditions of the GNU General Public License
+// cover the whole combination.
+//
+// As a special exception, the copyright holders of Image Scan!'s SANE
+// backend give you permission to link Image Scan!'s SANE backend with
+// SANE frontends that communicate with Image Scan!'s SANE backend
+// solely through the SANE Application Programming Interface,
+// regardless of the license terms of these SANE frontends, and to
+// copy and distribute the resulting combined work under terms of your
+// choice, provided that every copy of the combined work is
+// accompanied by a complete copy of the source code of Image Scan!'s
+// SANE backend (the version of Image Scan!'s SANE backend used to
+// produce the combined work), being distributed under the terms of
+// the GNU General Public License plus this exception. An independent
+// module is a module which is not derived from or based on Image
+// Scan!'s SANE backend.
+//
+// As a special exception, the copyright holders of Image Scan!'s SANE
+// backend give you permission to link Image Scan!'s SANE backend with
+// independent modules that communicate with Image Scan!'s SANE
+// backend solely through the "Interpreter" interface, regardless of
+// the license terms of these independent modules, and to copy and
+// distribute the resulting combined work under terms of your choice,
+// provided that every copy of the combined work is accompanied by a
+// complete copy of the source code of Image Scan!'s SANE backend (the
+// version of Image Scan!'s SANE backend used to produce the combined
+// work), being distributed under the terms of the GNU General Public
+// License plus this exception. An independent module is a module
+// which is not derived from or based on Image Scan!'s SANE backend.
+//
+// Note that people who make modified versions of Image Scan!'s SANE
+// backend are not obligated to grant special exceptions for their
+// modified versions; it is their choice whether to do so. The GNU
+// General Public License gives permission to release a modified
+// version without this exception; this exception also makes it
+// possible to release a modified version which carries forward this
+// exception.
+
+
+#ifndef included_test_net_obj_hh
+#define included_test_net_obj_hh
+
+#ifndef __cplusplus
+#error "This is a C++ include file. Use a C++ compiler to compile"
+#error "code that includes this file."
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "../net-obj.h"
+
+#include <cxxtest/TestSuite.h>
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <unistd.h>
+
+#include "../message.h"
+
+
+class test_net_obj : public CxxTest::TestSuite
+{
+public:
+
+ void test_lifecycle (void)
+ {
+ void *net = net_init (get_current_dir_name (), NULL);
+ TS_ASSERT (net);
+ net = net_exit (net);
+ TS_ASSERT (!net);
+ }
+
+ void test_missing_program (void)
+ {
+ char dirname_template[] = "network-XXXXXX";
+ char *dirname = mkdtemp (dirname_template);
+ TS_ASSERT (dirname);
+
+ void *net = net_init (dirname, NULL);
+ TS_ASSERT (!net);
+ net = net_exit (net);
+ TS_ASSERT (!net);
+
+ if (0 != rmdir (dirname))
+ {
+ err_minor ("%s: %s", dirname, strerror (errno));
+ }
+ }
+
+private:
+
+ void setUp (void)
+ {
+ msg_init ();
+ }
+};
+
+
+#endif /* !defined (included_test_net_obj_hh) */
diff --git a/backend/tests/xmltest-runner.sh b/backend/tests/xmltest-runner.sh
new file mode 100755
index 0000000..8bc3383
--- /dev/null
+++ b/backend/tests/xmltest-runner.sh
@@ -0,0 +1,87 @@
+#! /bin/sh
+
+test_result=PASS
+
+run_test () {
+ ./xmltest "$@"
+ if test 0 = $?; then
+ echo "PASS: xmltest $@"
+ else
+ echo "FAIL: xmltest $@"
+ test_result=FAIL
+ fi
+}
+
+run_test GT-X970
+run_test PM-A820
+run_test EP-210F
+run_test GT-X970 ES-H300
+run_test GT-X970 LP-M5600
+run_test LP-M5600 PM-A820
+run_test PM-A820 GT-X970
+run_test GT-X970 GT-X970
+run_test LP-M5600 LP-M5600
+run_test EP-210F hoge
+run_test GT-X970 hoge
+run_test PM-A820 MAN
+run_test EP-210F ES-H300
+run_test EP-210F LP-M5600
+run_test GT-X970 ES-H300 Perfection610
+run_test GT-X970 ES-H300 PM-A820
+run_test GT-X970 ES-H300 MAN
+run_test GT-X970 LP-M5600 Perfection610
+run_test GT-X970 LP-M5600 CX4600
+run_test GT-X970 LP-M5600 EP-210F
+run_test ES-H300 hoge GT-X970
+run_test ES-H300 hoge PM-A820
+run_test ES-H300 hoge EP-210F
+run_test PM-A820 LP-M5600 CX4600
+run_test PM-A820 LP-M5600 GT-X970
+run_test PM-A820 LP-M5600 MAN
+run_test LP-M5600 Perfection610 GT-X970
+run_test LP-M5600 Perfection610 PM-A820
+run_test LP-M5600 Perfection610 EP-210F
+run_test LP-M5600 EP-210F GT-X970
+run_test LP-M5600 EP-210F CX4600
+run_test LP-M5600 EP-210F MAN
+run_test EP-210F MAN hoge
+run_test EP-210F MAN ES-H300
+run_test EP-210F MAN LP-M5600
+run_test EP-210F GT-X970 ES-H300
+run_test EP-210F GT-X970 PM-A820
+run_test EP-210F GT-X970 hoge
+run_test EP-210F CX4600 Perfection610
+run_test EP-210F CX4600 PM-A820
+run_test EP-210F CX4600 hoge
+run_test ES-H300 ES-H300 ES-H300
+run_test PM-A820 PM-A820 PM-A820
+run_test GT-X970 GT-X970 CX4600
+run_test Perfection610 Perfection610 MAN
+run_test Perfection610 Perfection610 GT-X970
+run_test LP-M5600 LP-M5600 GT-X970
+run_test LP-M5600 LP-M5600 PM-A820
+run_test CX4600 CX4600 hoge
+run_test EP-210F EP-210F ES-H300
+run_test EP-210F EP-210F LP-M5600
+run_test EP-210F EP-210F MAN
+run_test ES-H300 GT-X970 GT-X970
+run_test ES-H300 PM-A820 PM-A820
+run_test GT-X970 hoge hoge
+run_test CX4600 Perfection610 Perfection610
+run_test PM-A820 LP-M5600 LP-M5600
+run_test LP-M5600 MAN MAN
+run_test hoge GT-X970 GT-X970
+run_test MAN PM-A820 PM-A820
+run_test hoge EP-210F EP-210F
+run_test GT-X970 CX4600 GT-X970
+run_test GT-X970 ES-H300 GT-X970
+run_test GT-X970 EP-210F GT-X970
+run_test PM-A820 ES-H300 PM-A820
+run_test LP-M5600 CX4600 LP-M5600
+run_test PM-A820 MAN PM-A820
+run_test hoge GT-X970 hoge
+run_test EP-210F CX4600 EP-210F
+run_test MAN hoge MAN
+
+test PASS = "$test_result"
+exit $?
diff --git a/backend/tests/xmltest.c b/backend/tests/xmltest.c
new file mode 100644
index 0000000..0f3a277
--- /dev/null
+++ b/backend/tests/xmltest.c
@@ -0,0 +1,247 @@
+/* xmltest.c -- simple model cache info checker
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: SEIKO EPSON CORPORATION
+ * AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xmltest.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "../list.h"
+#include "../message.h"
+#include "../model-info.h"
+#include "../utils.h"
+
+static bool check_cache_content (list *info);
+static bool check_info_content (const _model_info_t *info);
+static bool model_info_test_cmp (const _model_info_t *info,
+ const _model_info_t_test *reference);
+
+int
+main (int argc, char** argv)
+{
+ bool pass = false;
+ list *cache = NULL;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ /* Log at least broken promises and unmet requirements.
+ */
+ setenv ("SANE_DEBUG_EPKOWA", "FATAL", false);
+ msg_init ();
+
+ cache = (list *) model_info_cache_init (getenv ("srcdir"), &status);
+ if (!cache)
+ {
+ err_fatal ("cannot initialise model info cache (%s)",
+ sane_strstatus (status));
+ return EXIT_FAILURE;
+ }
+
+ while (--argc && ++argv)
+ {
+ const void *info = model_info_cache_get_info (*argv, &status);
+ if (!info)
+ {
+ err_major ("cannot get info for '%s' (%s)", *argv,
+ sane_strstatus (status));
+ }
+ }
+
+ pass = check_cache_content (cache);
+ cache = model_info_cache_exit (cache);
+
+ return (pass ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+/*! \brief Loops over all cache entries and checks each one of them.
+ */
+static bool
+check_cache_content (list *cache)
+{
+ bool pass = true;
+ _model_info_t *info;
+
+ list_reset (cache);
+ while ((info = list_next (cache)))
+ {
+ pass &= check_info_content (info);
+ }
+ return pass;
+}
+
+/*! \brief Checks a cache entry against known good data.
+ *
+ * \note Unknown entries are skipped.
+ */
+static bool
+check_info_content (const _model_info_t *info)
+{
+ if (0 == strcmp_c (info->fw_name, gt_x970.fw_name))
+ return model_info_test_cmp (info, &gt_x970);
+
+ if (0 == strcmp_c (info->fw_name, es_h300.fw_name))
+ return model_info_test_cmp (info, &es_h300);
+
+ if (0 == strcmp_c (info->fw_name, perfection_610.fw_name))
+ return model_info_test_cmp (info, &perfection_610);
+
+ if (0 == strcmp_c (info->fw_name, lp_m5600.fw_name))
+ return model_info_test_cmp (info, &lp_m5600);
+
+ if (0 == strcmp_c (info->fw_name, pm_a820.fw_name))
+ return model_info_test_cmp (info, &pm_a820);
+
+ if (0 == strcmp_c (info->fw_name, cx_4600.fw_name))
+ return model_info_test_cmp (info, &cx_4600);
+
+ /* cannot do remaining tests */
+ printf (" SKIP: unexpected fw_name (%s)\n", info->fw_name);
+ return true;
+}
+
+/*! \brief Compares \a info against a \a reference.
+ */
+static bool
+model_info_test_cmp (const _model_info_t *info,
+ const _model_info_t_test *reference)
+{
+ bool pass = true;
+ int i, j;
+
+ require (info && reference);
+
+ /* Compare model names */
+ if (0 != strcmp_c (info->overseas, reference->overseas))
+ {
+ pass = false;
+ printf ("FAIL: overseas -> %s != %s\n", info->overseas,
+ reference->overseas);
+ }
+
+ if (0 != strcmp_c (info->japan, reference->japan))
+ {
+ pass = false;
+ printf ("FAIL: japan -> %s != %s\n", info->japan, reference->japan);
+ }
+
+ /* Compare color profiles */
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 9; j++)
+ {
+ if (info->profile->color_profile[i][j]
+ != reference->profile.color_profile[i][j])
+ {
+ pass = false;
+ printf ("FAIL: profile[%i][%i] -> %f != %f\n", i, j,
+ info->profile->color_profile[i][j],
+ reference->profile.color_profile[i][j]);
+ }
+ }
+ }
+
+ /* Compare custom command entries */
+ if (info->command->set_focus_position
+ != reference->command.set_focus_position)
+ {
+ pass = false;
+ printf ("FAIL: focus -> %d != %d\n", info->command->set_focus_position,
+ reference->command.set_focus_position);
+ }
+
+ if (info->command->feed != reference->command.feed)
+ {
+ pass = false;
+ printf ("FAIL: feed -> %d != %d\n", info->command->feed,
+ reference->command.feed);
+ }
+
+ if (info->command->eject != reference->command.eject)
+ {
+ pass = false;
+ printf ("FAIL: eject -> %d != %d\n", info->command->eject,
+ reference->command.eject);
+ }
+
+ if (info->command->lock != reference->command.lock)
+ {
+ pass = false;
+ printf ("FAIL: lock -> %d != %d\n", info->command->lock,
+ reference->command.lock);
+ }
+
+ if (info->command->unlock != reference->command.unlock)
+ {
+ pass = false;
+ printf ("FAIL: unlock -> %d != %d\n", info->command->unlock,
+ reference->command.lock);
+ }
+
+ return pass;
+}
diff --git a/backend/tests/xmltest.h b/backend/tests/xmltest.h
new file mode 100644
index 0000000..02404e1
--- /dev/null
+++ b/backend/tests/xmltest.h
@@ -0,0 +1,190 @@
+/* xmltest.c -- simple model cache info checker
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: SEIKO EPSON CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef included_xmltest_h
+#define included_xmltest_h
+
+#include "../get-infofile.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct
+{
+ char *fw_name; /* key for _model_info_cache_get_info */
+ char *overseas; /* model name */
+ char *japan; /* model name */
+ char *name; /* points to one of overseas, japan or
+ * fw_name, never NULL */
+ scan_command_t *command; /* command customisation info */
+ EpsonScanHard profile; /* colour profiles */
+
+ bool from_file; /* origin of our data, used to control
+ * which members need to be free()d at
+ * destruction time */
+
+} _model_info_t;
+
+typedef struct
+{
+ char *fw_name; /* key for _model_info_cache_get_info */
+ char *overseas; /* model name */
+ char *japan; /* model name */
+
+ scan_command_t command; /* command customisation info */
+ EpsonScanHardRec profile; /* colour profiles */
+
+} _model_info_t_test;
+
+const _model_info_t_test gt_x970 = {
+ "GT-X970",
+ NULL,
+ NULL,
+
+ {0x01, 0, 0, 0xFF, true, true},
+ {0x87,
+ {{1.1978,-0.1417,-0.0561,-0.0852, 1.1610,-0.0758,-0.0395,-0.3212, 1.3607},
+ {1.0000, 0.0009,-0.0009,-0.1268, 1.0523, 0.0745,-0.0075,-0.0873, 1.0948},
+ {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000},
+ {1.4475,-0.3957,-0.0518,-0.0138, 1.0644,-0.0506,-0.0199,-0.2050, 1.2249}}},
+
+};
+
+const _model_info_t_test es_h300 = {
+ "ES-H300",
+ "GT-2500",
+ "ES-H300",
+
+ {0x01, 0, 0, 0xFF, false, false},
+ {0x87,
+ {{1.0359,-0.0146,-0.0213,-0.0752, 1.0963,-0.0211,-0.0456,-0.3238, 1.3693},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1}}},
+
+};
+
+const _model_info_t_test perfection_610 = {
+ "Perfection610",
+ "Perfection 610",
+ NULL,
+
+ {0x01, 0, 0, 0xFF, false, false},
+ {0x87,
+ {{1.1442,-0.0705,-0.0737,-0.0702, 1.1013,-0.0311,-0.0080,-0.3588, 1.3668},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1}}},
+
+};
+
+const _model_info_t_test lp_m5600 = {
+ "LP-M5600",
+ NULL,
+ "LP-M5600",
+
+ {0x01, 0, 0x19, 0xFF, false, false},
+ {0x87,
+ {{1.0784,-0.0560,-0.0224,-0.1793, 1.2234,-0.0441,-0.0041,-0.2636, 1.2677},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1}}},
+
+};
+
+const _model_info_t_test pm_a820 = {
+ "PM-A820",
+ "Stylus Photo RX560/RX580/RX590",
+ "PM-A820",
+
+ {0x01, 0, 0, 0xFF, false, false},
+ {0x87,
+ {{0.9533, 0.0885,-0.0418, 0.0033, 1.0627,-0.0660,-0.0137,-0.1904, 1.2041},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1}}},
+
+};
+
+const _model_info_t_test cx_4600 = {
+ "CX4600",
+ "Stylus CX4500/CX4600",
+ NULL,
+
+ {0x01, 0, 0, 0xFF, false, false},
+ {0x87,
+ {{0.9828, 0.0924,-0.0752, 0.0255, 1.151,-0.1765, 0.0049,-0.325, 1.3201},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1},
+ {1, 0, 0, 0, 1, 0, 0, 0, 1}}},
+
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !defined (included_xmltest_h) */
diff --git a/backend/timing.c b/backend/timing.c
new file mode 100644
index 0000000..1b370ce
--- /dev/null
+++ b/backend/timing.c
@@ -0,0 +1,188 @@
+/* timing.c -- optional support for run-time time stamp collection
+ * Copyright (C) 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "timing.h"
+
+#if ENABLE_TIMING
+
+#include <math.h>
+#include <string.h>
+
+size_t time_pass_count = 0;
+
+struct time_interval_ time_scan;
+struct time_interval_ time_pass[TIME_PASS_MAX];
+
+static const long NANOS_PER_SEC = 1000000000;
+
+static void
+time_compute_span (struct time_interval_ *m)
+{
+ m->span.t.tv_nsec = m->stop.t.tv_nsec - m->start.t.tv_nsec;
+ if (0 > m->span.t.tv_nsec)
+ {
+ m->span.t.tv_nsec += NANOS_PER_SEC;
+ m->stop.t.tv_sec -= 1;
+ }
+ m->span.t.tv_sec = m->stop.t.tv_sec - m->start.t.tv_sec;
+
+ m->span.is_valid = m->start.is_valid && m->stop.is_valid;
+}
+
+void
+time_clear (void)
+{
+ memset (&time_scan, 0, sizeof (time_scan));
+ memset ( time_pass, 0, sizeof (*time_pass) * TIME_PASS_MAX);
+}
+
+static double
+time_stamp_to_double (const struct time_stamp_ *ts)
+{
+ return ((double) ts->t.tv_nsec) / NANOS_PER_SEC + ts->t.tv_sec;
+}
+
+static void
+time_fprintf_interval (FILE *stream, const struct time_interval_ *ti,
+ const char *prefix)
+{
+ char str[512];
+
+ if (ti->span.is_valid)
+ snprintf (str, num_of (str), "%s: %f (start: %f, stop: %f)", prefix,
+ time_stamp_to_double (&ti->span),
+ time_stamp_to_double (&ti->start),
+ time_stamp_to_double (&ti->stop));
+ else if (ti->start.is_valid)
+ snprintf (str, num_of (str), "%s: --- (start: %f, stop: ---)", prefix,
+ time_stamp_to_double (&ti->start));
+ else if (ti->stop.is_valid)
+ snprintf (str, num_of (str), "%s: --- (start: ---, stop: %f)", prefix,
+ time_stamp_to_double (&ti->stop));
+ else
+ snprintf (str, num_of (str), "%s: --- (start: ---, stop: ---)", prefix);
+
+ fprintf (stream, "%s\n", str);
+}
+
+/* There is a script in utils/ that can combine the results of
+ multiple scans (in one or more SANE frontend sessions) into
+ a single CSV file.
+ */
+void
+time_stats (size_t count)
+{
+ size_t i, n = 0;
+ double t, sum = 0, sum_sq = 0;
+
+ fprintf (stderr, "\f\n"); /* start a new form */
+ fprintf (stderr, "Per pass timing data (in seconds)\n");
+ for (i = 0; i < count && i < TIME_PASS_MAX; ++i)
+ {
+ time_compute_span (&time_pass[i]);
+ time_fprintf_interval (stderr, &time_pass[i], "pass");
+ }
+ fprintf (stderr, "\n");
+
+ fprintf (stderr, "Scan timing data (in seconds)\n");
+ time_compute_span (&time_scan);
+ time_fprintf_interval (stderr, &time_scan, "scan");
+ fprintf (stderr, "\n");
+
+ if (time_scan.span.is_valid)
+ {
+ if (0 < count)
+ fprintf (stderr, "Scan avg: %f s/pass\n",
+ time_stamp_to_double (&time_scan.span) / count);
+ }
+
+ for (i = 0; i < count && i < TIME_PASS_MAX; ++i)
+ {
+ if (time_pass[i].span.is_valid)
+ {
+ ++n;
+ t = time_stamp_to_double (&time_pass[i].span);
+ sum += t;
+ sum_sq += (t * t);
+ }
+ }
+
+ if (0 != n)
+ {
+ fprintf (stderr, "Pass sum: %f s, %zd passes\n", sum, n);
+ fprintf (stderr, "Pass avg: %f", sum / n);
+ if (1 < n) /* add standard deviation */
+ {
+ fprintf (stderr, " (+/- %f)",
+ sqrt ((n * sum_sq - sum * sum) / n / (n-1)));
+ }
+ fprintf (stderr, " s/pass\n");
+ }
+}
+
+#endif /* ENABLE_TIMING */
diff --git a/backend/timing.h b/backend/timing.h
new file mode 100644
index 0000000..8181cda
--- /dev/null
+++ b/backend/timing.h
@@ -0,0 +1,119 @@
+/* timing.h -- optional support for run-time time stamp collection
+ * Copyright (C) 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef timing_h_included
+#define timing_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#ifndef ENABLE_TIMING
+#define ENABLE_TIMING 0
+#endif
+
+#define TIME_PASS_MAX 100
+extern size_t time_pass_count;
+
+#if !ENABLE_TIMING /* turn API calls into no-ops */
+
+#define time_clear() do {} while (0)
+#define time_stamp(interval,end) do {} while (0)
+#define time_stats(count) do {} while (0)
+
+#else /* do something useful */
+
+#include <time.h>
+#include "defines.h"
+
+struct time_stamp_
+{
+ bool is_valid;
+ struct timespec t;
+};
+
+struct time_interval_
+{
+ struct time_stamp_ start;
+ struct time_stamp_ stop;
+ struct time_stamp_ span;
+};
+
+extern struct time_interval_ time_scan;
+extern struct time_interval_ time_pass[TIME_PASS_MAX];
+
+#define time_stamp(interval,end) \
+ do { \
+ interval.end.is_valid = (0 == clock_gettime (CLOCK_MONOTONIC, \
+ &(interval.end.t))); \
+ } while (0)
+
+void time_clear (void);
+void time_stats (size_t count);
+
+#endif /* ENABLE_TIMING */
+
+#endif /* !defined (timing_h_included) */
diff --git a/backend/utils.c b/backend/utils.c
new file mode 100644
index 0000000..e43afc1
--- /dev/null
+++ b/backend/utils.c
@@ -0,0 +1,334 @@
+/* utils.c -- assorted utility functions and macros
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "utils.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h> /* nanosleep */
+
+
+/*! Creates a string of 2-character hexadecimal values from a NUL
+ terminated character string.
+
+ The implementation acquires the memory needed for the result and
+ the caller is responsible for releasing it.
+
+ \return a pointer to the encoded string or \c NULL if the memory
+ required could not be acquired
+ */
+char *
+fw_name_to_hex (const char *fw_name) /* '\0' terminated */
+{
+ char *rv, *p;
+
+ log_call();
+
+ if (!fw_name) return NULL; /* guard clause */
+
+ p = rv = t_malloc (2 * strlen (fw_name) + 1, char);
+ if (!rv) return NULL;
+
+ while ('\0' != *fw_name)
+ {
+ sprintf (p, "%02x", *fw_name);
+ p += 2;
+ ++fw_name;
+ }
+ *p = '\0';
+ return rv;
+}
+
+/*! Sets up a properly initialised resolution info object.
+ */
+void
+init_resolution_info (resolution_info *self, u_char *data)
+{
+ SANE_Bool (*cond) (const u_char *) = NULL;
+ size_t step = 0;
+
+ if (!self) return;
+
+ self->last = 0; /* set defaults */
+ self->size = -1;
+ self->list = NULL;
+ self->deep = SANE_TRUE;
+
+ if (!data) return; /* act like a default constructor */
+
+ self->size = 0;
+ self->list = t_realloc (NULL, self->size + 1, SANE_Word);
+
+ if (!self->list)
+ {
+ err_major ("%s", strerror (ENOMEM));
+ self->size = -1;
+ return;
+ }
+
+ if ('R' == data[0]) /* ESC I data block */
+ {
+ cond = resolution_info_ESC_I_cond;
+ step = 3;
+ }
+ else /* ESC i data block, hopefully */
+ {
+ cond = resolution_info_ESC_i_cond;
+ step = 2;
+ }
+
+ while (cond (data))
+ {
+ void *p = self->list;
+
+ self->size++;
+ self->list = t_realloc (p, self->size + 1, SANE_Word);
+ if (!self->list)
+ {
+ delete (p);
+
+ err_major ("%s", strerror (ENOMEM));
+ self->size = -1;
+ return;
+ }
+ self->list[self->size] = data[step - 1] << 8 | data[step - 2];
+ data += step;
+ log_info ("resolution: %d dpi", self->list[self->size]);
+ }
+ self->list[0] = self->size;
+}
+
+/*! Releases resources held by \a self and resets it to default state.
+ */
+void
+free_resolution_info (resolution_info *self)
+{
+ if (!self) return;
+
+ if (self->deep)
+ delete (self->list);
+
+ init_resolution_info (self, NULL);
+}
+
+/*! Makes an optionally deep copy of \a src to \a dest.
+
+ Any resources held by \a dest will be returned to the system.
+ */
+SANE_Status
+copy_resolution_info (resolution_info *dest, const resolution_info *src,
+ SANE_Bool deep)
+{
+ if (!dest || !src) return SANE_STATUS_INVAL;
+
+ require (!src->list || src->size == src->list[0]);
+
+ if (deep && src->list) /* copy resolution list */
+ {
+ size_t size = (src->size + 1) * sizeof (SANE_Word);
+ SANE_Word *list = t_malloc (size, SANE_Word);
+
+ if (!list) return SANE_STATUS_NO_MEM;
+
+ memcpy (list, src->list, size);
+
+ if (dest->deep)
+ delete (dest->list);
+ dest->list = list;
+ }
+ else /* just refer to it */
+ {
+ if (dest->deep)
+ delete (dest->list);
+ dest->list = src->list;
+ }
+
+ dest->last = src->last;
+ dest->size = src->size;
+ dest->deep = deep;
+
+ promise (!dest->list || dest->size == dest->list[0]);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+_update_ranges (const device *hw, extension *src)
+{
+ require (hw);
+ require (src);
+
+ src->x_range.min = 0;
+ src->x_range.max = SANE_FIX (src->max_x * MM_PER_INCH / hw->base_res);
+ src->x_range.quant = 0;
+
+ src->y_range.min = 0;
+ src->y_range.max = SANE_FIX (src->max_y * MM_PER_INCH / hw->base_res);
+ src->y_range.quant = 0;
+
+ if (!hw->cmd->request_identity2) return;
+
+ /* correct for color shuffle offsets */
+ src->y_range.max = SANE_FIX ((src->max_y - 2 * hw->max_line_distance)
+ * MM_PER_INCH / hw->base_res);
+}
+
+
+/*! Convenience type to hold document size information.
+ */
+struct _doc_size_info
+{
+ const double width;
+ const double height;
+ const char *label;
+};
+
+/*! Document size information for known sizes.
+ */
+static const struct _doc_size_info
+doc_size[] = {
+ /* second byte bit flag values */
+ { 182.00, 257.00, "B5V"},
+ { 257.00, 182.00, "B5H"},
+ { 148.00, 210.00, "A5V"},
+ { 210.00, 148.00, "A5H"},
+ { 184.15, 266.70, "EXV"},
+ { 266.70, 184.15, "EXH"},
+ { 0 , 0 , "RSV"}, /* reserved */
+ { 0 , 0 , "UNK"}, /* unknown */
+ /* first byte bit flag values */
+ { 297.00, 420.00, "A3V"},
+ { 279.40, 431.80, "WLT"},
+ { 257.00, 364.00, "B4V"},
+ { 215.90, 355.60, "LGV"},
+ { 210.00, 297.00, "A4V"},
+ { 297.00, 210.00, "A4H"},
+ { 215.90, 279.40, "LTV"},
+ { 279.40, 215.90, "LTH"},
+};
+
+
+void
+_update_doc_size (extension *src, uint16_t value)
+{
+ const uint16_t DOC_MASK = ~0x0200;
+
+ size_t i = 0;
+
+ require (src);
+
+ if ((DOC_MASK & value) != value)
+ {
+ err_minor ("clearing reserved bit flags to match spec");
+ value &= DOC_MASK;
+ }
+
+ if (0 == value) /* size detection not supported */
+ {
+ src->doc_x = 0;
+ src->doc_y = 0;
+ return;
+ }
+
+ while (!(0x8000 & value) && (num_of (doc_size) > i))
+ {
+ value = value << 1;
+ ++i;
+ }
+
+ if (0 != strcmp_c ("UNK", doc_size[i].label))
+ {
+ src->doc_x = doc_size[i].width;
+ src->doc_y = doc_size[i].height;
+ }
+ else
+ {
+ src->doc_x = SANE_UNFIX (src->x_range.max);
+ src->doc_y = SANE_UNFIX (src->y_range.max);
+ }
+
+ value = value << 1;
+ if (0 != value)
+ {
+ err_minor ("device detected multiple document sizes!\n");
+ }
+
+ log_info ("detected document size: %s (%.2fmm x %.2fmm)",
+ doc_size[i].label, src->doc_x, src->doc_y);
+}
+
+int
+microsleep (size_t usec)
+{
+ struct timespec ts;
+ ts.tv_sec = usec / 1000000;
+ ts.tv_nsec = (usec % 1000000) * 1000;
+
+ return nanosleep (&ts, NULL);
+}
diff --git a/backend/utils.h b/backend/utils.h
new file mode 100644
index 0000000..7b1e18f
--- /dev/null
+++ b/backend/utils.h
@@ -0,0 +1,199 @@
+/* utils.h -- assorted utility functions and macros
+ * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: AVASYS CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef utils_h_included
+#define utils_h_included
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "device.h"
+
+
+/*! \brief Encodes a fw_name as a string of hexadecimals.
+ */
+char * fw_name_to_hex (const char *fw_name);
+
+/*! \brief Converts a buffer to an unsigned 32-bit integer.
+ */
+inline static uint32_t
+buf_to_uint32 (const byte *buf)
+{
+ return buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
+}
+
+/*! \brief Converts a buffer to an unsigned 16-bit integer.
+ */
+inline static uint16_t
+buf_to_uint16 (const byte *buf)
+{
+ return buf[1] << 8 | buf[0];
+}
+
+/*! \brief Converts an unsigned 32-bit integer to a buffer.
+ */
+inline static void
+uint32_to_buf (uint32_t val, byte *buf)
+{
+ if (!buf) return;
+
+ buf[0] = val;
+ buf[1] = val >> 8;
+ buf[2] = val >> 16;
+ buf[3] = val >> 24;
+}
+
+/*! \brief Converts an unsigned 16-bit integer to a buffer.
+ */
+inline static void
+uint16_to_buf (uint16_t val, byte *buf)
+{
+ if (!buf) return;
+
+ buf[0] = val;
+ buf[1] = val >> 8;
+}
+
+
+/* Scan area related queries and computations.
+ */
+
+/*! \brief Tells whether an extension supports document size detection.
+ */
+inline static SANE_Bool
+has_size_check_support (const extension *src)
+{
+ return src->has_size_check;
+}
+
+/*! \brief Recomputes an extension's scan area dimensions (in mm).
+ \hideinitializer
+ */
+#define update_ranges(hw,src) _update_ranges (hw, (extension *) src)
+
+void _update_ranges (const device *hw, extension *src);
+
+/*! \brief Re-establishes the detected document size.
+ \hideinitializer
+ */
+#define update_doc_size(src,value) _update_doc_size ((extension *) src, value)
+
+void _update_doc_size (extension *src, uint16_t value);
+
+
+/* Resolution information handlers.
+ */
+
+void init_resolution_info (resolution_info *self, byte *data);
+void free_resolution_info (resolution_info *self);
+SANE_Status copy_resolution_info (resolution_info *dest,
+ const resolution_info *src, SANE_Bool deep);
+
+/*! \brief Tells whether we are looking at a resolution in an ESC I reply.
+ */
+inline static SANE_Bool
+resolution_info_ESC_I_cond (const u_char *data)
+{
+ return ('R' == data[0]);
+}
+
+/*! \brief Tells whether we are looking at a resolution in an ESC i reply.
+ */
+inline static SANE_Bool
+resolution_info_ESC_i_cond (const u_char *data)
+{
+ return (0 != data[0] || 0 != data[1]);
+}
+
+/*! \brief A wrapper around strcmp that checks for NULL strings
+ */
+inline static int
+strcmp_c (const char *s1, const char *s2)
+{
+ if (!s1 && !s2) return 0;
+ if (s1 && !s2) return 1;
+ if (!s1 && s2) return -1;
+
+ return strcmp (s1, s2);
+}
+
+/*! \brief A wrapper around strncmp that checks for NULL strings
+ */
+inline static int
+strncmp_c (const char *s1, const char *s2, size_t n)
+{
+ if (!s1 && !s2) return 0;
+ if (s1 && !s2) return 1;
+ if (!s1 && s2) return -1;
+
+ return strncmp (s1, s2, n);
+}
+
+int microsleep (size_t usec);
+
+
+#endif /* !defined (utils_h_included) */
diff --git a/backend/xmlreader.c b/backend/xmlreader.c
new file mode 100644
index 0000000..56e23e9
--- /dev/null
+++ b/backend/xmlreader.c
@@ -0,0 +1,378 @@
+/* xmlreader.c --
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: SEIKO EPSON CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "utils.h"
+#include "get-infofile.h"
+#include "xmlreader.h"
+
+typedef struct devices{
+ char *firmware;
+ char *overseas;
+ char *japan;
+}devices, *devicePtr;
+
+typedef struct commands{
+ char *command_type;
+ char *command_level;
+}commands, *commandPtr;
+
+static void default_profile_set(double color_profile[9]);
+
+
+static void parseCommand(xmlNodePtr cur, scan_command_t* command);
+static unsigned char parseStatus(char *status, char *name);
+
+char *
+parseDevices(xmlNodePtr cur, char *name){
+ devicePtr ret;
+
+ char *region = NULL;
+ char *result = NULL;
+
+ log_call();
+
+ ret = t_calloc (1, devices);
+ if(ret == NULL){
+ err_major("out of memory");
+ return NULL;
+ }
+
+ cur = cur->xmlChildrenNode;
+
+ while (cur != NULL) {
+ if (!xmlStrcmp(cur->name, (const xmlChar *) "firmware")) {
+ if(ret->firmware == NULL && strcmp(name, FIRMWARE) == 0){
+ ret->firmware = (char *)xmlGetProp(cur, (const xmlChar *) "name");
+ result = strdup(ret->firmware);
+ if(!ret->firmware){
+ delete (ret);
+ return NULL;
+ }
+ break;
+ }
+ }else if (!xmlStrcmp(cur->name, (const xmlChar *) "model")) {
+ region = (char *)xmlGetProp(cur, (const xmlChar *) "region");
+ if(region && strcmp(name, MODEL_JAPAN) == 0){
+ if(strcmp(name, MODEL_JAPAN) == 0 && strcasecmp(region, "Japan") == 0){
+ ret->japan = (char *)xmlGetProp(cur, (const xmlChar *) "name");
+ result = strdup(ret->japan);
+ delete (ret->japan);
+ if(!result || isspace(result[0]) != 0 || strlen(result) == 0){
+ delete (result);
+ delete (ret);
+ delete (region);
+ result = NULL;
+ err_minor("Model has no Name.");
+ }
+ }
+ break;
+ }else if(!region && strcmp(name, MODEL_OVERSEAS) == 0){
+ ret->overseas = (char *)xmlGetProp(cur, (const xmlChar *) "name");
+ result = strdup(ret->overseas);
+ delete(ret->overseas);
+ if(!result || isspace(result[0]) != 0 || strlen(result) == 0){
+ delete (result);
+ delete (ret);
+ delete (region);
+ result = NULL;
+ err_minor("Model has no Name.");
+ }
+ break;
+ }
+ delete (region);
+ }
+ cur = cur->next;
+ }
+
+ delete (region);
+ delete (ret);
+
+ return result;
+}
+
+EpsonScanHard
+parseProfiles(xmlNodePtr cur){
+ EpsonScanHard ret;
+ int i, j;
+ char *tmp;
+ char pmat[9][3] = {"rr", "rg", "rb",
+ "gr", "gg", "gb",
+ "br", "bg", "bb"};
+ char *profile_type;
+ xmlNodePtr curtmp;
+
+ log_call();
+
+ ret = t_calloc (1, EpsonScanHardRec);
+ if(ret == NULL){
+ err_major("out of memory");
+ return NULL;
+ }
+
+ /*add default value*/
+ for(i = 0; i < 4; i++){
+ default_profile_set(ret->color_profile[i]);
+ }
+
+ cur = curtmp = cur->xmlChildrenNode;
+ while(cur != NULL){
+ if ((!xmlStrcmp(cur->name, (const xmlChar *) "profile"))){
+ curtmp = cur;
+ profile_type = (char *)xmlGetProp(cur, (const xmlChar *) "type");
+
+ if(strcmp(profile_type, "reflective") == 0){
+ i = 0;
+ }else if(strcmp(profile_type, "color negative") == 0){
+ i = 1;
+ }else if(strcmp(profile_type, "monochrome negative") == 0){
+ i = 2;
+ }else if(strcmp(profile_type, "positive") == 0){
+ i = 3;
+ }else {
+ err_minor("profile of the wrong type.");
+ delete (profile_type);
+ delete (ret);
+ return NULL;
+ }
+ delete (profile_type);
+
+ j = 0;
+ cur = cur->xmlChildrenNode;
+ while(cur != NULL){
+ if(!xmlStrcmp(cur->name, (const xmlChar *)pmat[j])){
+ tmp = (char*)xmlGetProp(cur, (const xmlChar *) "value");
+ ret->color_profile[i][j] = atof(tmp);
+ delete (tmp);
+ j++;
+ }
+ cur = cur->next;
+ }
+ if(j != 9){
+ err_minor("Value that is not sufficient exists.");
+ default_profile_set(ret->color_profile[i]);
+ }
+ }
+ cur = curtmp;
+ cur = curtmp = cur->next;
+ }
+
+ return ret;
+}
+
+static
+void default_profile_set(double color_profile[9]){
+ /*default value is assigned*/
+ const double default_profile[9] =
+ {1.0000, 0.0000, 0.0000,
+ 0.0000, 1.0000, 0.0000,
+ 0.0000, 0.0000, 1.0000} ;
+ int j;
+
+ for(j = 0; j < 9; j++){
+ color_profile[j] = default_profile[j];
+ }
+}
+
+scan_command_t *
+parseCommands_set(xmlNodePtr cur){
+ commandPtr ret;
+ scan_command_t* command;
+
+ log_call();
+
+ command = t_calloc (1, scan_command_t);
+ if(command == NULL){
+ err_major("out of memory");
+ return NULL;
+ }
+
+ /*default value*/
+ command->set_focus_position = 0xFF;
+ command->feed = 0xFF;
+ command->eject = 0xFF;
+ command->lock = false;
+ command->unlock = false;
+
+ ret = t_calloc (1, commands);
+ if(ret == NULL){
+ err_major("out of memory");
+ delete (command);
+ return NULL;
+ }
+
+ ret->command_type = (char *)xmlGetProp(cur, (const xmlChar *) "type");
+
+ ret->command_level = (char *)xmlGetProp(cur, (const xmlChar *) "level");
+
+ cur = cur->xmlChildrenNode;
+ while(cur != NULL){
+ if ((!xmlStrcmp(cur->name, (const xmlChar *) "command"))){
+ parseCommand(cur, command);
+ }
+ cur = cur->next;
+ }
+
+ delete (ret->command_type);
+ delete (ret->command_level);
+ delete (ret);
+
+ return command;
+}
+
+static void
+parseCommand(xmlNodePtr cur, scan_command_t* command)
+{
+ char *tmp;
+ char *status = NULL;
+
+ status = (char *)xmlGetProp(cur, (const xmlChar *) "status");
+
+ if((tmp = (char *)xmlGetProp(cur, (const xmlChar *) "name")) != NULL){
+ if(strcmp(tmp, "set_focus_position") == 0){
+ command->set_focus_position = parseStatus(status, "set_focus_position");
+ }else if(strcmp(tmp, "feed") == 0){
+ command->feed = parseStatus(status, "feed");
+ }else if(strcmp(tmp, "eject") == 0){
+ command->eject = parseStatus(status, "eject");
+ }else if(strcmp(tmp, "lock") == 0){
+ command->lock = true;
+ if(status && strcmp(status, "disable") == 0) command->lock = false;
+ }else if(strcmp(tmp, "unlock") == 0){
+ command->unlock = true;
+ if(status && strcmp(status, "disable") == 0) command->unlock = false;
+ }
+ delete (tmp);
+ delete (status);
+ }
+}
+
+static unsigned char
+parseStatus(char *status, char *name)
+{
+ unsigned char value;
+
+ if(!status || strcmp(status, "enabled") == 0){/*enabled*/
+ if(strcmp(name, "set_focus_position") == 0) value = 0x70;
+ else if(strcmp(name, "feed") == 0) value = 0x19;
+ else if(strcmp(name, "eject") == 0) value = 0x0C;
+ else value = 0;
+ }else if(strcmp(status, "disabled") == 0){
+ value = 0;
+ }else{
+ value = 0;
+ }
+
+ return value;
+}
+
+capability_data_t*
+parseCapabilities(xmlNodePtr cur)
+{
+ capability_data_t *capabilities;
+ char *tmp;
+ char *endp;
+
+ log_call();
+
+ capabilities = t_calloc (1, capability_data_t);
+ if(capabilities == NULL){
+ err_major("out of memory");
+ return NULL;
+ }
+
+ cur = cur->xmlChildrenNode;
+ while(cur != NULL){
+ if ((!xmlStrcmp(cur->name, (const xmlChar *) "scan-area"))){
+ endp = tmp = (char *)xmlGetProp(cur, (const xmlChar *) "width");
+ capabilities->width = strtol(tmp, &endp, 10);
+ if(endp == tmp) capabilities->width = -1;
+ if(*endp != '\0')
+ err_minor("ignoring trailing garbage (%s)", endp);
+ delete (tmp);
+
+ endp = tmp = (char *)xmlGetProp(cur, (const xmlChar *) "height");
+ capabilities->height = strtol(tmp, &endp, 10);
+ if(endp == tmp) capabilities->height = -1;
+ if(*endp != '\0')
+ err_minor("ignoring trailing garbage (%s)", endp);
+ delete (tmp);
+
+ endp = tmp = (char *)xmlGetProp(cur, (const xmlChar *) "base");
+ capabilities->base = strtol(tmp, &endp, 10);
+ if(endp == tmp) capabilities->base = 1;
+ if(*endp != '\0')
+ err_minor("ignoring trailing garbage (%s)", endp);
+ delete (tmp);
+ }
+ cur = cur->next;
+ }
+
+ return capabilities;
+}
diff --git a/backend/xmlreader.h b/backend/xmlreader.h
new file mode 100644
index 0000000..cabafdf
--- /dev/null
+++ b/backend/xmlreader.h
@@ -0,0 +1,79 @@
+/* xmlreader.h --
+ * Copyright (C) 2010 SEIKO EPSON CORPORATION
+ *
+ * License: GPLv2+|iscan
+ * Authors: SEIKO EPSON CORPORATION
+ *
+ * This file is part of the SANE backend distributed with Image Scan!
+ *
+ * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License
+ * along with this package. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Linking Image Scan!'s SANE backend statically or dynamically with
+ * other modules is making a combined work based on this SANE backend.
+ * Thus, the terms and conditions of the GNU General Public License
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * SANE frontends that communicate with Image Scan!'s SANE backend
+ * solely through the SANE Application Programming Interface,
+ * regardless of the license terms of these SANE frontends, and to
+ * copy and distribute the resulting combined work under terms of your
+ * choice, provided that every copy of the combined work is
+ * accompanied by a complete copy of the source code of Image Scan!'s
+ * SANE backend (the version of Image Scan!'s SANE backend used to
+ * produce the combined work), being distributed under the terms of
+ * the GNU General Public License plus this exception. An independent
+ * module is a module which is not derived from or based on Image
+ * Scan!'s SANE backend.
+ *
+ * As a special exception, the copyright holders of Image Scan!'s SANE
+ * backend give you permission to link Image Scan!'s SANE backend with
+ * independent modules that communicate with Image Scan!'s SANE
+ * backend solely through the "Interpreter" interface, regardless of
+ * the license terms of these independent modules, and to copy and
+ * distribute the resulting combined work under terms of your choice,
+ * provided that every copy of the combined work is accompanied by a
+ * complete copy of the source code of Image Scan!'s SANE backend (the
+ * version of Image Scan!'s SANE backend used to produce the combined
+ * work), being distributed under the terms of the GNU General Public
+ * License plus this exception. An independent module is a module
+ * which is not derived from or based on Image Scan!'s SANE backend.
+ *
+ * Note that people who make modified versions of Image Scan!'s SANE
+ * backend are not obligated to grant special exceptions for their
+ * modified versions; it is their choice whether to do so. The GNU
+ * General Public License gives permission to release a modified
+ * version without this exception; this exception also makes it
+ * possible to release a modified version which carries forward this
+ * exception.
+ */
+
+
+#ifndef included_xmlreader_h
+#define included_xmlreader_h
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+char *parseDevices(xmlNodePtr cur, char *name);
+
+EpsonScanHard parseProfiles(xmlNodePtr cur);
+
+scan_command_t *parseCommands_set(xmlNodePtr cur);
+
+capability_data_t *parseCapabilities(xmlNodePtr cur);
+
+#endif