summaryrefslogtreecommitdiff
path: root/backend/hs2p.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/hs2p.c')
-rw-r--r--backend/hs2p.c3332
1 files changed, 3332 insertions, 0 deletions
diff --git a/backend/hs2p.c b/backend/hs2p.c
new file mode 100644
index 0000000..de3be42
--- /dev/null
+++ b/backend/hs2p.c
@@ -0,0 +1,3332 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Jeremy Johnson
+ This file is part of a SANE backend for Ricoh IS450
+ and IS420 family of HS2P Scanners using the SCSI controller.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+*/
+/* SANE-FLOW-DIAGRAM
+
+ - sane_init() : initialize backend, attach scanners
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device
+ . . - attach : to the device
+ . . . init_options : initialize SANE_OPTIONS array
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option informations
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner-device
+ - sane_exit() : terminate use of backend
+*/
+#define BUILD 1
+
+/* Begin includes */
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+#define BACKEND_NAME hs2p
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#include "hs2p-scsi.c"
+
+/* Begin macros */
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+
+/* Begin static constants */
+static int num_devices = 0;
+static HS2P_Device *first_dev = NULL;
+static HS2P_Scanner *first_handle = NULL;
+
+static SANE_Char inquiry_data[255] = "HS2P scanner";
+/*
+static SANE_Int disable_optional_frames = 0;
+static SANE_Int fake_inquiry = 0;
+*/
+
+static HS2P_HWEntry HS2P_Device_List[] = {
+ {"RICOH", "IS450"},
+ {"RICOH", "IS430"}, /*untested */
+ {"RICOH", "IS420"}, /*untested */
+ {"RICOH", "IS01"}, /*untested */
+ {"RICOH", "IS02"}, /*untested */
+ {NULL, NULL} /*sentinel */
+};
+
+#if 0
+static int
+allblank (const char *s)
+{
+ while (s && *s)
+ if (!isspace (*s++))
+ return 0;
+
+ return 1;
+}
+#endif
+
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (DBG_proc, ">> max_string_size\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (DBG_proc, "<< max_string_size\n");
+ return max_size;
+}
+
+static void
+trim_spaces (char *s, size_t n)
+{
+ for (s += (n - 1); n > 0; n--, s--)
+ {
+ if (*s && !isspace (*s))
+ break;
+ *s = '\0';
+ }
+}
+static SANE_Bool
+is_device_supported (char *device)
+{
+ HS2P_HWEntry *hw;
+
+ for (hw = &HS2P_Device_List[0]; hw->mfg != NULL; hw++)
+ if (strncmp (device, hw->model, strlen (hw->model)) == 0)
+ break; /* found a match */
+
+ return (hw == NULL) ? SANE_FALSE : SANE_TRUE;
+}
+
+static SANE_Int
+get_list_index (const char *list[], char *s) /* sequential search */
+{
+ SANE_Int i;
+
+ for (i = 0; list[i]; i++)
+ if (strcmp (s, list[i]) == 0)
+ return i; /* FOUND */
+
+ /* unknown paper_list strings are treated as 'custom' */
+ /* unknown compression_list strings are treated as 'none' */
+ /* unknown scan_source_list strings are treated as 'ADF' */
+ return 0;
+}
+
+static SANE_Int
+get_val_id_strndx (struct val_id *vi, int len, SANE_Int val)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ if (vi[i].val == val)
+ return vi[i].id; /* FOUND */
+ return vi[0].id; /* NOT FOUND so let's default to first */
+}
+
+static SANE_Status
+init_options (HS2P_Scanner * s)
+{
+ SANE_Int i;
+ DBG (DBG_proc, ">> init_options\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->opt[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->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+
+ /*
+ * "Scan Mode" GROUP:
+ */
+ s->opt[OPT_MODE_GROUP].name = "";
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE_GROUP;
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* 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->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Inquiry */
+ s->opt[OPT_INQUIRY].name = SANE_NAME_INQUIRY;
+ s->opt[OPT_INQUIRY].title = SANE_TITLE_INQUIRY;
+ s->opt[OPT_INQUIRY].desc = SANE_DESC_INQUIRY;
+ s->opt[OPT_INQUIRY].type = SANE_TYPE_STRING;
+ s->opt[OPT_INQUIRY].size = sizeof (inquiry_data);
+ s->opt[OPT_INQUIRY].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_INQUIRY].s = strdup (inquiry_data);
+ s->opt[OPT_INQUIRY].cap = SANE_CAP_SOFT_DETECT; /* Display Only */
+
+ /* Scan mode */
+ s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_SCAN_MODE].size =
+ max_string_size ((SANE_String_Const *) scan_mode_list);
+ s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_MODE].constraint.string_list =
+ (SANE_String_Const *) & scan_mode_list[0];
+ s->val[OPT_SCAN_MODE].s = strdup (scan_mode_list[0]);
+ s->image_composition = LINEART;
+
+ /* Standard resolutions */
+ s->opt[OPT_RESOLUTION].name = "std-" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = "Std-" SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = "Std " SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->info.resStdList;
+ s->val[OPT_RESOLUTION].w = s->hw->info.default_res;
+
+ /* X Resolution */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = "X " SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &(s->hw->info.xres_range);
+ s->val[OPT_X_RESOLUTION].w = s->hw->info.resBasicX;
+
+ /* Y Resolution */
+ 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 = "Y " SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &(s->hw->info.yres_range);
+ s->val[OPT_Y_RESOLUTION].w = s->hw->info.resBasicY;
+
+ /* Compression */
+ s->opt[OPT_COMPRESSION].name = SANE_NAME_COMPRESSION;
+ s->opt[OPT_COMPRESSION].title = SANE_TITLE_COMPRESSION;
+ s->opt[OPT_COMPRESSION].desc = SANE_DESC_COMPRESSION;
+ s->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING;
+ s->opt[OPT_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_COMPRESSION].size =
+ max_string_size ((SANE_String_Const *) compression_list);
+ s->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_COMPRESSION].constraint.string_list =
+ (SANE_String_Const *) & compression_list[0];
+ s->val[OPT_COMPRESSION].s = strdup (compression_list[0]);
+ if (s->hw->info.supports_MH == SANE_FALSE || /* MH G3 1-D */
+ s->hw->info.supports_MR == SANE_FALSE || /* MR G3 2-D */
+ s->hw->info.supports_MMR == SANE_FALSE || /* MMR G4 2-D */
+ s->hw->info.supports_MHB == SANE_FALSE) /* MH byte boundary */
+ {
+ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+
+ /*
+ * "Geometry" GROUP:
+ */
+ s->opt[OPT_GEOMETRY_GROUP].name = "";
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Auto Size Recognition available if IPU installed */
+ s->opt[OPT_AUTO_SIZE].name = SANE_NAME_AUTO_SIZE;
+ s->opt[OPT_AUTO_SIZE].title = SANE_TITLE_AUTO_SIZE;
+ s->opt[OPT_AUTO_SIZE].desc = SANE_DESC_AUTO_SIZE;
+ s->opt[OPT_AUTO_SIZE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AUTO_SIZE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_AUTO_SIZE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_AUTO_SIZE].w = SANE_FALSE;
+ if (!s->hw->info.supports_sizerecognition)
+ s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE;
+
+ /* Pad short documents to requested length with white space */
+ s->opt[OPT_PADDING].name = SANE_NAME_PADDING;
+ s->opt[OPT_PADDING].title = SANE_TITLE_PADDING;
+ s->opt[OPT_PADDING].desc = SANE_DESC_PADDING;
+ s->opt[OPT_PADDING].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PADDING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_PADDING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PADDING].w = SANE_TRUE;
+ /*if (!s->hw->info.hasADF)
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE;
+ FIXME: compare to user setting, not the existence of FB?
+ if (!strcmp (scan_source_list, "FB"))
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; */
+ /* Permanently disable OPT_PADDING */
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE;
+
+ /* Paper Orientation */
+ s->opt[OPT_PAGE_ORIENTATION].name = SANE_NAME_ORIENTATION;
+ s->opt[OPT_PAGE_ORIENTATION].title = SANE_TITLE_ORIENTATION;
+ s->opt[OPT_PAGE_ORIENTATION].desc = SANE_DESC_ORIENTATION;
+ s->opt[OPT_PAGE_ORIENTATION].type = SANE_TYPE_STRING;
+ s->opt[OPT_PAGE_ORIENTATION].size = max_string_size (orientation_list);
+ s->opt[OPT_PAGE_ORIENTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAGE_ORIENTATION].constraint.string_list = &orientation_list[0];
+ s->val[OPT_PAGE_ORIENTATION].s = strdup (orientation_list[0]);
+
+ /* Paper Size */
+ s->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
+ s->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
+ s->opt[OPT_PAPER_SIZE].size = max_string_size (paper_list);
+ s->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER_SIZE].constraint.string_list = &paper_list[0];
+ s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]);
+
+ /* 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->info.x_range);
+ s->val[OPT_TL_X].w = SANE_FIX (0.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->info.y_range);
+ s->val[OPT_TL_Y].w = SANE_FIX (0.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->info.x_range);
+ s->val[OPT_BR_X].w = s->hw->info.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->info.y_range);
+ s->val[OPT_BR_Y].w = s->hw->info.y_range.max;
+
+ DBG (DBG_info, "INIT_OPTIONS: ul(x,y) = (%d,%d) br(x,y) = (%d,%d)\n",
+ (unsigned) SANE_UNFIX (s->val[OPT_TL_X].w),
+ (unsigned) SANE_UNFIX (s->val[OPT_TL_Y].w),
+ (unsigned) SANE_UNFIX (s->val[OPT_BR_X].w),
+ (unsigned) SANE_UNFIX (s->val[OPT_BR_Y].w));
+ /* Autoborder */
+ /* Rotation */
+ /* Deskew */
+
+
+
+ /*
+ * "Feeder" GROUP:
+ */
+ s->opt[OPT_FEEDER_GROUP].name = "";
+ s->opt[OPT_FEEDER_GROUP].title = SANE_TITLE_FEEDER_GROUP;
+ s->opt[OPT_FEEDER_GROUP].desc = "";
+ s->opt[OPT_FEEDER_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_FEEDER_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_FEEDER_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scan Source */
+ s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_SCAN_SOURCE].size = max_string_size (scan_source_list);
+ s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_SOURCE].constraint.string_list =
+ (SANE_String_Const *) & scan_source_list[0];
+ s->val[OPT_SCAN_SOURCE].s = strdup (scan_source_list[0]);
+ if (!s->hw->info.hasADF)
+ s->opt[OPT_SCAN_SOURCE].cap |= SANE_CAP_INACTIVE;
+
+ /* Duplex: */
+ s->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
+ s->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
+ s->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
+ s->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
+ s->opt[OPT_DUPLEX].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_DUPLEX].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_DUPLEX].w = s->hw->info.default_duplex;
+ if (!s->hw->info.hasDuplex)
+ s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
+
+ /* Prefeed: */
+ s->opt[OPT_PREFEED].name = SANE_NAME_PREFEED;
+ s->opt[OPT_PREFEED].title = SANE_TITLE_PREFEED;
+ s->opt[OPT_PREFEED].desc = SANE_DESC_PREFEED;
+ s->opt[OPT_PREFEED].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREFEED].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_PREFEED].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_PREFEED].w = SANE_FALSE;
+ s->opt[OPT_PREFEED].cap |= SANE_CAP_INACTIVE;
+
+ /* Endorser: */
+ s->opt[OPT_ENDORSER].name = SANE_NAME_ENDORSER;
+ s->opt[OPT_ENDORSER].title = SANE_TITLE_ENDORSER;
+ s->opt[OPT_ENDORSER].desc = SANE_DESC_ENDORSER;
+ s->opt[OPT_ENDORSER].type = SANE_TYPE_BOOL;
+ s->opt[OPT_ENDORSER].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_ENDORSER].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_ENDORSER].w = s->hw->info.endorser_control;
+ if (!s->hw->info.hasEndorser)
+ s->opt[OPT_ENDORSER].cap |= SANE_CAP_INACTIVE;
+
+ /* Endorser String: */
+ s->opt[OPT_ENDORSER_STRING].name = SANE_NAME_ENDORSER_STRING;
+ s->opt[OPT_ENDORSER_STRING].title = SANE_TITLE_ENDORSER_STRING;
+ s->opt[OPT_ENDORSER_STRING].desc = SANE_DESC_ENDORSER_STRING;
+ s->opt[OPT_ENDORSER_STRING].type = SANE_TYPE_STRING;
+ s->opt[OPT_ENDORSER_STRING].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_ENDORSER_STRING].size = sizeof (s->hw->info.endorser_string);
+ s->opt[OPT_ENDORSER_STRING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_ENDORSER_STRING].s = strdup (s->hw->info.endorser_string);
+ if (!s->hw->info.hasEndorser)
+ s->opt[OPT_ENDORSER_STRING].cap |= SANE_CAP_INACTIVE;
+
+ /* Batch */
+ /* Check ADF */
+ /* timeout ADF */
+ /* timeout Manual */
+
+ /*
+ * "Enhancement" GROUP:
+ */
+ s->opt[OPT_ENHANCEMENT_GROUP].name = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Halftone Type */
+ s->opt[OPT_HALFTONE_CODE].name = SANE_NAME_HALFTONE_CODE;
+ s->opt[OPT_HALFTONE_CODE].title = SANE_TITLE_HALFTONE_CODE;
+ s->opt[OPT_HALFTONE_CODE].desc = SANE_DESC_HALFTONE_CODE;
+ s->opt[OPT_HALFTONE_CODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_CODE].size = max_string_size (halftone_code);
+ s->opt[OPT_HALFTONE_CODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_HALFTONE_CODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_CODE].constraint.string_list =
+ (SANE_String_Const *) & halftone_code[0];
+ s->val[OPT_HALFTONE_CODE].s = strdup (halftone_code[0]);
+ if (s->image_composition == LINEART)
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE;
+
+ /* Halftone patterns */
+ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE_PATTERN].size =
+ max_string_size ((SANE_String_Const *) halftone_pattern_list);
+ s->opt[OPT_HALFTONE_PATTERN].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE_PATTERN].constraint.string_list =
+ (SANE_String_Const *) & halftone_pattern_list[0];
+ s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]);
+ if (s->image_composition == LINEART)
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE;
+
+ /* Gray Filter */
+ s->opt[OPT_GRAYFILTER].name = SANE_NAME_GRAYFILTER;
+ s->opt[OPT_GRAYFILTER].title = SANE_TITLE_GRAYFILTER;
+ s->opt[OPT_GRAYFILTER].desc = SANE_DESC_GRAYFILTER;
+ s->opt[OPT_GRAYFILTER].type = SANE_TYPE_STRING;
+ s->opt[OPT_GRAYFILTER].size = max_string_size (grayfilter_list);
+ s->opt[OPT_GRAYFILTER].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GRAYFILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_GRAYFILTER].constraint.string_list =
+ (SANE_String_Const *) & grayfilter_list[0];
+ s->val[OPT_GRAYFILTER].s = strdup (grayfilter_list[0]);
+
+ /* Scan Wait Mode */
+ s->opt[OPT_SCAN_WAIT_MODE].name = SANE_NAME_SCAN_WAIT_MODE;
+ s->opt[OPT_SCAN_WAIT_MODE].title = SANE_TITLE_SCAN_WAIT_MODE;
+ s->opt[OPT_SCAN_WAIT_MODE].desc = SANE_DESC_SCAN_WAIT_MODE;
+ s->opt[OPT_SCAN_WAIT_MODE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SCAN_WAIT_MODE].unit = SANE_UNIT_NONE;
+ s->val[OPT_SCAN_WAIT_MODE].w =
+ (s->hw->info.scan_wait_mode) ? SANE_TRUE : SANE_FALSE;
+
+ /* 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].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->info.brightness_range;
+ s->val[OPT_BRIGHTNESS].w = 128;
+
+ /* 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 = &s->hw->info.threshold_range;
+ s->val[OPT_THRESHOLD].w = 128;
+
+ /* 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].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &s->hw->info.contrast_range;
+ s->val[OPT_CONTRAST].w = 128;
+
+ /* Gamma */
+ s->opt[OPT_GAMMA].name = SANE_NAME_GAMMA;
+ s->opt[OPT_GAMMA].title = SANE_TITLE_GAMMA;
+ s->opt[OPT_GAMMA].desc = SANE_DESC_GAMMA;
+ s->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GAMMA].size = max_string_size ((SANE_String_Const *) gamma_list);
+ /*
+ s->opt[OPT_GAMMA].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA].constraint.range = &u8_range;
+ s->val[OPT_GAMMA].w = 0;
+ */
+ s->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_GAMMA].constraint.string_list =
+ (SANE_String_Const *) & gamma_list[0];
+ s->val[OPT_GAMMA].s = strdup (gamma_list[0]);
+
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CUSTOM_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |=
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_GRAY].wa = s->gamma_table;
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+
+
+ /* Control Panel */
+ /* ACE Function */
+ /* ACE Sensitivity */
+
+ /* Binary Smoothing Filter */
+ s->opt[OPT_SMOOTHING].name = SANE_NAME_SMOOTHING;
+ s->opt[OPT_SMOOTHING].title = SANE_TITLE_SMOOTHING;
+ s->opt[OPT_SMOOTHING].desc = SANE_DESC_SMOOTHING;
+ s->opt[OPT_SMOOTHING].type = SANE_TYPE_BOOL;
+ s->opt[OPT_SMOOTHING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_SMOOTHING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_SMOOTHING].w = SANE_FALSE;
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_SMOOTHING].cap |= SANE_CAP_INACTIVE;
+
+ /* Binary Noise Removal Filter */
+ s->opt[OPT_NOISEREMOVAL].name = SANE_NAME_NOISEREMOVAL;
+ s->opt[OPT_NOISEREMOVAL].title = SANE_TITLE_NOISEREMOVAL;
+ s->opt[OPT_NOISEREMOVAL].desc = SANE_DESC_NOISEREMOVAL;
+ s->opt[OPT_NOISEREMOVAL].type = SANE_TYPE_STRING;
+ s->opt[OPT_NOISEREMOVAL].size = max_string_size (noisematrix_list);
+ s->opt[OPT_NOISEREMOVAL].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NOISEREMOVAL].constraint.string_list =
+ (SANE_String_Const *) & noisematrix_list[0];
+ s->val[OPT_NOISEREMOVAL].s = strdup (noisematrix_list[0]);
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_NOISEREMOVAL].cap |= SANE_CAP_INACTIVE;
+
+ /* Automatic Separation */
+ s->opt[OPT_AUTOSEP].name = SANE_NAME_AUTOSEP;
+ s->opt[OPT_AUTOSEP].title = SANE_TITLE_AUTOSEP;
+ s->opt[OPT_AUTOSEP].desc = SANE_DESC_AUTOSEP;
+ s->opt[OPT_AUTOSEP].type = SANE_TYPE_STRING;
+ s->opt[OPT_AUTOSEP].size = max_string_size (auto_separation_list);
+ s->opt[OPT_AUTOSEP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_AUTOSEP].constraint.string_list =
+ (SANE_String_Const *) & auto_separation_list[0];
+ s->val[OPT_AUTOSEP].s = strdup (auto_separation_list[0]);
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_AUTOSEP].cap |= SANE_CAP_INACTIVE;
+
+ /* Automatic Binarization */
+ s->opt[OPT_AUTOBIN].name = SANE_NAME_AUTOBIN;
+ s->opt[OPT_AUTOBIN].title = SANE_TITLE_AUTOBIN;
+ s->opt[OPT_AUTOBIN].desc = SANE_DESC_AUTOBIN;
+ s->opt[OPT_AUTOBIN].type = SANE_TYPE_STRING;
+ s->opt[OPT_AUTOBIN].size = max_string_size (auto_binarization_list);
+ s->opt[OPT_AUTOBIN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_AUTOBIN].constraint.string_list =
+ (SANE_String_Const *) & auto_binarization_list[0];
+ s->val[OPT_AUTOBIN].s = strdup (auto_binarization_list[0]);
+ if (!s->hw->info.hasIPU)
+ s->opt[OPT_AUTOBIN].cap |= SANE_CAP_INACTIVE;
+
+ /* SECTION
+ * The IS450 supports up to 4 Section; The IS420 supports up to 6 Sections
+ * For each struct window_section[i] we need to fill in ulx,uly,width,height,etc
+ * NOT YET IMPLEMENTED
+ */
+
+ /* Negative */
+ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_NEGATIVE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ /* White Balance */
+ s->opt[OPT_WHITE_BALANCE].name = SANE_NAME_WHITE_BALANCE;
+ s->opt[OPT_WHITE_BALANCE].title = SANE_TITLE_WHITE_BALANCE;
+ s->opt[OPT_WHITE_BALANCE].desc = SANE_DESC_WHITE_BALANCE;
+ s->opt[OPT_WHITE_BALANCE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_WHITE_BALANCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_BALANCE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_WHITE_BALANCE].w = SANE_FALSE; /* F/T = Relative/Absolute White */
+
+ /*
+ * "Miscellaneous" GROUP:
+ */
+ s->opt[OPT_MISCELLANEOUS_GROUP].name = "";
+ s->opt[OPT_MISCELLANEOUS_GROUP].title = SANE_TITLE_MISCELLANEOUS_GROUP;
+ s->opt[OPT_MISCELLANEOUS_GROUP].desc = "";
+ s->opt[OPT_MISCELLANEOUS_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MISCELLANEOUS_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_MISCELLANEOUS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Padding Type: */
+ s->opt[OPT_PADDING_TYPE].name = SANE_NAME_PADDING_TYPE;
+ s->opt[OPT_PADDING_TYPE].title = SANE_TITLE_PADDING_TYPE;
+ s->opt[OPT_PADDING_TYPE].desc = SANE_DESC_PADDING_TYPE;
+ s->opt[OPT_PADDING_TYPE].type = SANE_TYPE_STRING;
+ s->opt[OPT_PADDING_TYPE].cap = SANE_CAP_SOFT_DETECT; /* Display only */
+ s->opt[OPT_PADDING_TYPE].size = max_string_size (paddingtype_list);
+ /*
+ s->opt[OPT_PADDING_TYPE].size = sizeof((paddingtype_list[ get_paddingtype_strndx(TRUNCATE) ]));
+ s->opt[OPT_PADDING_TYPE].constraint_type = SANE_CONSTRAINT_NONE;
+ */
+ s->opt[OPT_PADDING_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PADDING_TYPE].constraint.string_list =
+ (SANE_String_Const *) & paddingtype_list[0];
+ s->val[OPT_PADDING_TYPE].s =
+ strdup (paddingtype_list[get_paddingtype_strndx (TRUNCATE)]);
+ DBG (DBG_info, "PADDINGTYPE =%s size=%d\n", s->val[OPT_PADDING_TYPE].s,
+ s->opt[OPT_PADDING_TYPE].size);
+
+ /* Bit Order
+ s->opt[OPT_BITORDER].name = SANE_NAME_BITORDER;
+ s->opt[OPT_BITORDER].title = SANE_TITLE_BITORDER;
+ s->opt[OPT_BITORDER].desc = SANE_DESC_BITORDER;
+ s->opt[OPT_BITORDER].type = SANE_TYPE_WORD;
+ s->opt[OPT_BITORDER].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BITORDER].constraint_type = SANE_CONSTRAINT_NONE
+ s->val[OPT_BITORDER].w = 0x7;
+ */
+
+ /* Self Diagnostics */
+ s->opt[OPT_SELF_DIAGNOSTICS].name = SANE_NAME_SELF_DIAGNOSTICS;
+ s->opt[OPT_SELF_DIAGNOSTICS].title = SANE_TITLE_SELF_DIAGNOSTICS;
+ s->opt[OPT_SELF_DIAGNOSTICS].desc = SANE_DESC_SELF_DIAGNOSTICS;
+ s->opt[OPT_SELF_DIAGNOSTICS].type = SANE_TYPE_BUTTON;
+
+ /* Optical Diagnostics */
+ s->opt[OPT_OPTICAL_ADJUSTMENT].name = SANE_NAME_OPTICAL_ADJUSTMENT;
+ s->opt[OPT_OPTICAL_ADJUSTMENT].title = SANE_TITLE_OPTICAL_ADJUSTMENT;
+ s->opt[OPT_OPTICAL_ADJUSTMENT].desc = SANE_DESC_OPTICAL_ADJUSTMENT;
+ s->opt[OPT_OPTICAL_ADJUSTMENT].type = SANE_TYPE_BUTTON;
+
+ /* MAINTENANCE DATA */
+ s->opt[OPT_DATA_GROUP].name = "";
+ s->opt[OPT_DATA_GROUP].title = "Maintenance Data";
+ s->opt[OPT_DATA_GROUP].desc = "";
+ s->opt[OPT_DATA_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_DATA_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_DATA_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_UPDATE].name = "Update";
+ s->opt[OPT_UPDATE].title = "Update";
+ s->opt[OPT_UPDATE].desc = "Update scanner data";
+ s->opt[OPT_UPDATE].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_NREGX_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGX_ADF].name = "# registers in main-scanning in ADF mode";
+ s->opt[OPT_NREGX_ADF].title = "# registers in main-scanning in ADF mode";
+ s->opt[OPT_NREGX_ADF].desc = "# registers in main-scanning in ADF mode";
+ s->opt[OPT_NREGX_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGX_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGX_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGY_ADF].name = "# registers in sub-scanning in ADF mode";
+ s->opt[OPT_NREGY_ADF].title = "# registers in sub-scanning in ADF mode";
+ s->opt[OPT_NREGY_ADF].desc = "# registers in sub-scanning in ADF mode";
+ s->opt[OPT_NREGY_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGY_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGY_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGY_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGX_BOOK].name = "# registers in main-scanning in book mode";
+ s->opt[OPT_NREGX_BOOK].title = "# registers in main-scanning in book mode";
+ s->opt[OPT_NREGX_BOOK].desc = "# registers in main-scanning in book mode";
+ s->opt[OPT_NREGX_BOOK].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGX_BOOK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGX_BOOK].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_BOOK].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGY_BOOK].name = "# registers in sub-scanning in book mode";
+ s->opt[OPT_NREGY_BOOK].title = "# registers in sub-scanning in book mode";
+ s->opt[OPT_NREGY_BOOK].desc = "# registers in sub-scanning in book mode";
+ s->opt[OPT_NREGY_BOOK].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGY_BOOK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGY_BOOK].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGY_BOOK].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NSCANS_ADF].name = "# ADF Scans";
+ s->opt[OPT_NSCANS_ADF].title = "# ADF Scans";
+ s->opt[OPT_NSCANS_ADF].desc = "# ADF Scans";
+ s->opt[OPT_NSCANS_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NSCANS_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NSCANS_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NSCANS_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NSCANS_BOOK].name = "# BOOK Scans";
+ s->opt[OPT_NSCANS_BOOK].title = "# BOOK Scans";
+ s->opt[OPT_NSCANS_BOOK].desc = "# BOOK Scans";
+ s->opt[OPT_NSCANS_BOOK].type = SANE_TYPE_INT;
+ s->opt[OPT_NSCANS_BOOK].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NSCANS_BOOK].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NSCANS_BOOK].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_LAMP_TIME].name = "LAMP TIME";
+ s->opt[OPT_LAMP_TIME].title = "LAMP TIME";
+ s->opt[OPT_LAMP_TIME].desc = "LAMP TIME";
+ s->opt[OPT_LAMP_TIME].type = SANE_TYPE_INT;
+ s->opt[OPT_LAMP_TIME].unit = SANE_UNIT_NONE;
+ s->opt[OPT_LAMP_TIME].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_LAMP_TIME].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_EO_ODD].name = "E/O Balance ODD";
+ s->opt[OPT_EO_ODD].title = "E/O Balance ODD";
+ s->opt[OPT_EO_ODD].desc = "Adj. of E/O Balance in black level ODD";
+ s->opt[OPT_EO_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_EO_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_EO_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_EO_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_EO_EVEN].name = "E/O Balance EVEN";
+ s->opt[OPT_EO_EVEN].title = "E/O Balance EVEN";
+ s->opt[OPT_EO_EVEN].desc = "Adj. of E/O Balance in black level EVEN";
+ s->opt[OPT_EO_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_EO_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_EO_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_EO_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_BLACK_LEVEL_ODD].name = "Black Level ODD";
+ s->opt[OPT_BLACK_LEVEL_ODD].title = "Black Level ODD";
+ s->opt[OPT_BLACK_LEVEL_ODD].desc = "Adj. data in black level (ODD)";
+ s->opt[OPT_BLACK_LEVEL_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_BLACK_LEVEL_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BLACK_LEVEL_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BLACK_LEVEL_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_BLACK_LEVEL_EVEN].name = "Black Level EVEN";
+ s->opt[OPT_BLACK_LEVEL_EVEN].title = "Black Level EVEN";
+ s->opt[OPT_BLACK_LEVEL_EVEN].desc = "Adj. data in black level (EVEN)";
+ s->opt[OPT_BLACK_LEVEL_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_BLACK_LEVEL_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BLACK_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BLACK_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_WHITE_LEVEL_ODD].name = "White Level ODD";
+ s->opt[OPT_WHITE_LEVEL_ODD].title = "White Level ODD";
+ s->opt[OPT_WHITE_LEVEL_ODD].desc = "Adj. data in White level (ODD)";
+ s->opt[OPT_WHITE_LEVEL_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WHITE_LEVEL_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_LEVEL_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_WHITE_LEVEL_EVEN].name = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].title = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].desc = "Adj. data in White level (EVEN)";
+ s->opt[OPT_WHITE_LEVEL_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WHITE_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_WHITE_LEVEL_EVEN].name = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].title = "White Level EVEN";
+ s->opt[OPT_WHITE_LEVEL_EVEN].desc = "Adj. data in White level (EVEN)";
+ s->opt[OPT_WHITE_LEVEL_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_WHITE_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_WHITE_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_DENSITY].name = "Density Adjustment";
+ s->opt[OPT_DENSITY].title = "Density Adjustment";
+ s->opt[OPT_DENSITY].desc = "Density adjustment of std. white board";
+ s->opt[OPT_DENSITY].type = SANE_TYPE_INT;
+ s->opt[OPT_DENSITY].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DENSITY].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_DENSITY].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].name = "1st adj. in white level (ODD)";
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].title = "1st adj. in white level (ODD)";
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].desc = "1st adj. in white level (ODD)";
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].type = SANE_TYPE_INT;
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_FIRST_ADJ_WHITE_ODD].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].name = "1st adj. in white level (EVEN)";
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].title = "1st adj. in white level (EVEN)";
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].desc = "1st adj. in white level (EVEN)";
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].type = SANE_TYPE_INT;
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].unit = SANE_UNIT_NONE;
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_FIRST_ADJ_WHITE_EVEN].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGX_REVERSE].name = "# registers of main-scanning of backside";
+ s->opt[OPT_NREGX_REVERSE].title =
+ "# registers of main-scanning of backside";
+ s->opt[OPT_NREGX_REVERSE].desc =
+ "# registers of main-scanning of ADF backside";
+ s->opt[OPT_NREGX_REVERSE].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGX_REVERSE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGX_REVERSE].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGX_REVERSE].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NREGY_REVERSE].name = "# registers of sub-scanning of backside";
+ s->opt[OPT_NREGY_REVERSE].title = "# registers of sub-scanning of backside";
+ s->opt[OPT_NREGY_REVERSE].desc =
+ "# registers of sub-scanning of ADF backside";
+ s->opt[OPT_NREGY_REVERSE].type = SANE_TYPE_INT;
+ s->opt[OPT_NREGY_REVERSE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NREGY_REVERSE].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NREGY_REVERSE].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NSCANS_REVERSE_ADF].name = "# of scans of reverse side in ADF";
+ s->opt[OPT_NSCANS_REVERSE_ADF].title = "# of scans of reverse side in ADF";
+ s->opt[OPT_NSCANS_REVERSE_ADF].desc = "# of scans of reverse side in ADF";
+ s->opt[OPT_NSCANS_REVERSE_ADF].type = SANE_TYPE_INT;
+ s->opt[OPT_NSCANS_REVERSE_ADF].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NSCANS_REVERSE_ADF].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NSCANS_REVERSE_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_REVERSE_TIME].name = "LAMP TIME (reverse)";
+ s->opt[OPT_REVERSE_TIME].title = "LAMP TIME (reverse)";
+ s->opt[OPT_REVERSE_TIME].desc = "LAMP TIME (reverse)";
+ s->opt[OPT_REVERSE_TIME].type = SANE_TYPE_INT;
+ s->opt[OPT_REVERSE_TIME].unit = SANE_UNIT_NONE;
+ s->opt[OPT_REVERSE_TIME].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_REVERSE_TIME].constraint_type = SANE_CONSTRAINT_NONE;
+
+ s->opt[OPT_NCHARS].name = "# of endorser characters";
+ s->opt[OPT_NCHARS].title = "# of endorser characters";
+ s->opt[OPT_NCHARS].desc = "# of endorser characters";
+ s->opt[OPT_NCHARS].type = SANE_TYPE_INT;
+ s->opt[OPT_NCHARS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NCHARS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NCHARS].constraint_type = SANE_CONSTRAINT_NONE;
+
+ DBG (DBG_proc, "<< init_options\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (SANE_String_Const devname, int __sane_unused__ connType,
+ HS2P_Device ** devp)
+{
+ SANE_Status status;
+ HS2P_Device *dev;
+ struct inquiry_standard_data ibuf;
+ struct inquiry_vpd_data vbuf;
+ struct inquiry_jis_data jbuf;
+ size_t buf_size;
+ int fd = -1;
+ double mm;
+
+ char device_string[60];
+
+ unsigned int i;
+ SANE_String *str;
+
+
+ DBG (DBG_sane_proc, ">>> attach:\n");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG (DBG_sane_proc, ">>> attach: opening \"%s\"\n", devname);
+
+ /* sanei_scsi_open takes an option bufsize argument */
+ status = sanei_scsi_open (devname, &fd, &sense_handler, &(dev->sense_data));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: open failed: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (DBG_sane_proc, ">>> attach: opened %s fd=%d\n", devname, fd);
+
+ DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (standard data)\n");
+ memset (&ibuf, 0, sizeof (ibuf));
+ buf_size = sizeof (ibuf);
+ status = inquiry (fd, &ibuf, &buf_size, 0, HS2P_INQUIRY_STANDARD_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: inquiry failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ DBG (DBG_info,
+ ">>> attach: reported devtype='%d', vendor='%.8s', product='%.16s', revision='%.4s'\n",
+ ibuf.devtype, ibuf.vendor, ibuf.product, ibuf.revision);
+ DBG (DBG_info,
+ ">>> attach: reported RMB=%#x Ver=%#x ResponseDataFormat=%#x Length=%#x Byte7=%#x\n",
+ ibuf.rmb_evpd, ibuf.version, ibuf.response_data_format, ibuf.length,
+ ibuf.byte7);
+
+ if (ibuf.devtype != 6 || strncmp ((char *) ibuf.vendor, "RICOH ", 8) != 0)
+ {
+ DBG (DBG_warning, ">>> attach: device is not a RICOH scanner\n");
+ sanei_scsi_close (fd);
+ return SANE_STATUS_INVAL;
+ }
+ else if (!is_device_supported ((char *) ibuf.product))
+ {
+ DBG (DBG_warning,
+ ">>> attach: device %s is not yet a supported RICOH scanner\n",
+ ibuf.product);
+ sanei_scsi_close (fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* We should now have an open file descriptor to a supported hs2p scanner */
+ DBG (DBG_sane_proc, ">>> attach: sending TEST_UNIT_READY\n");
+ do
+ {
+ status = test_unit_ready (fd);
+ }
+ while (status == HS2P_SK_UNIT_ATTENTION);
+ if (status != HS2P_SCSI_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (vpd data)\n");
+ memset (&vbuf, 0, sizeof (vbuf));
+ buf_size = sizeof (vbuf);
+ status = inquiry (fd, &vbuf, &buf_size, 1, HS2P_INQUIRY_VPD_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: inquiry (vpd data) failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+ print_vpd_info (&vbuf);
+
+ DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (jis data)\n");
+ memset (&jbuf, 0, sizeof (jbuf));
+ buf_size = sizeof (jbuf);
+ status = inquiry (fd, &jbuf, &buf_size, 1, HS2P_INQUIRY_JIS_PAGE_CODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: inquiry (jis data) failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return status;
+ }
+ print_jis_info (&jbuf);
+
+
+ /* Fill in HS2P_Device {sane;info} */
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ memset (dev, 0, sizeof (*dev));
+
+ /* Maximum Number of Sub-Sections of Main Scanning Window */
+ if (strncmp ((char *) ibuf.product, "IS450", 5) == 0)
+ {
+ dev->info.max_win_sections = 4;
+ }
+ else if (strncmp ((char *) ibuf.product, "IS420", 5) == 0)
+ {
+ dev->info.max_win_sections = 6;
+ }
+
+ /* Some MODE SELECT scanner options */
+ DBG (DBG_proc, ">>> attach: get_basic_measurement_unit\n");
+ status =
+ get_basic_measurement_unit (fd, &(dev->info.bmu), &(dev->info.mud));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: get_basic_measurement_unit failed (%s)\n",
+ sane_strstatus (status));
+ DBG (DBG_error, ">>> attach: setting to defaults\n");
+ status = set_basic_measurement_unit (fd, MILLIMETERS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ ">>> attach: set_basic_measurement_unit failed (%s)\n",
+ sane_strstatus (status));
+ }
+ }
+ if ((status =
+ get_connection_parameters (fd, &(dev->info.cxn))) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, ">>> attach: get_connection_parameters failed\n");
+ }
+ status = get_endorser_control (fd, &dev->info.endorser_control);
+ if (status != SANE_STATUS_GOOD || dev->info.endorser_control != 0x01)
+ {
+ DBG (DBG_error,
+ ">>> attach: get_endorser_control failed: return value=%#02x\n",
+ dev->info.endorser_control);
+ dev->info.endorser_control = 0x00;
+ }
+ if ((dev->info.service_mode = get_service_mode (fd)) != 0x00
+ && dev->info.service_mode != 0x01)
+ {
+ DBG (DBG_error, ">>> attach: get_service_mode failed %#02x\n",
+ dev->info.service_mode);
+ dev->info.service_mode = 0x00;
+ }
+ if ((dev->info.scan_wait_mode = get_scan_wait_mode (fd)) != 0x00
+ && dev->info.scan_wait_mode != 0x01)
+ {
+ DBG (DBG_error,
+ ">>> attach: get_scan_wait_mode failed: return value=%#02x\n",
+ dev->info.scan_wait_mode);
+ dev->info.scan_wait_mode = 0x00;
+ }
+ status = get_white_balance (fd, &dev->info.white_balance);
+ if (status != SANE_STATUS_GOOD && dev->info.white_balance != 0x01)
+ {
+ DBG (DBG_error,
+ ">>> attach: get_white_balance failed: return value=%#02x\n",
+ dev->info.white_balance);
+ dev->info.white_balance = RELATIVE_WHITE;
+ }
+
+ DBG (DBG_info, ">>> attach: flushing and closing fd=%d\n", fd);
+ sanei_scsi_req_flush_all ();
+ sanei_scsi_close (fd);
+
+ dev->info.devtype = ibuf.devtype;
+ snprintf (dev->info.vendor, 9, "%-.5s", ibuf.vendor); /* RICOH */
+ trim_spaces (dev->info.vendor, sizeof (dev->info.vendor));
+ snprintf (dev->info.product, 16, "%-.16s", ibuf.product); /* IS450 */
+ trim_spaces (dev->info.product, sizeof (dev->info.product));
+ snprintf (dev->info.revision, 5, "%-.4s", ibuf.revision); /* 1R04 */
+ trim_spaces (dev->info.revision, sizeof (dev->info.revision));
+
+ /* SANE_Device sane information */
+ dev->sane.name = strdup (devname);
+ dev->sane.vendor =
+ (strcmp (dev->info.vendor, "RICOH") ==
+ 0) ? strdup ("Ricoh") : strdup (dev->info.vendor);
+ dev->sane.model = strdup (dev->info.product);
+ dev->sane.type = strdup ("sheetfed scanner");
+ /*
+ dev->sane.email_backend_author = strdup("<Jeremy Johnson> jeremy@acjlaw.net");
+ dev->sane.backend_website = strdup("http://www.acjlaw.net:8080/~jeremy/Ricoh");
+ */
+ /* read these values from backend configuration file using parse_configuration
+ dev->sane.location = strdup();
+ dev->sane.comment = strdup();
+ dev->sane.backend_version_code = strdup();
+ */
+ /* NOT YET USED */
+ /* dev->sane.backend_capablity_flags = 0x00; */
+
+ /* set capabilities from vpd */
+ /* adf_id: 0=none,1=simplex,2=duplex,3=ARDF,4=reserved; should be 1 or 2 for IS450 family */
+ dev->info.hasADF = vbuf.adf_id == 0 ? SANE_FALSE : SANE_TRUE;
+ dev->info.hasSimplex = vbuf.adf_id == 1 ? SANE_TRUE : SANE_FALSE;
+ dev->info.hasDuplex = vbuf.adf_id == 2 ? SANE_TRUE : SANE_FALSE;
+ dev->info.hasARDF = vbuf.adf_id == 3 ? SANE_TRUE : SANE_FALSE;
+
+ /* end_id 0=none,1=Yes,2=reserved; should always be 0 or 1 */
+ dev->info.hasEndorser = vbuf.end_id == 1 ? SANE_TRUE : SANE_FALSE;
+ for (i = 0; i < 20; i++)
+ dev->info.endorser_string[i] = '\0';
+
+ /* ipu_id: Bit0: '0'-no IPU, '1'-has IPU
+ * Bit1: '0'-no extended board, '1'-has extended board;
+ * should always be 0
+ */
+ dev->info.hasIPU = (vbuf.ipu_id & 0x01) == 0x01 ? SANE_TRUE : SANE_FALSE;
+ dev->info.hasXBD = (vbuf.ipu_id & 0x02) == 0x02 ? SANE_TRUE : SANE_FALSE;
+
+
+ /* Image Composition Byte is set to 0x37 (0011 0111) */
+ dev->info.supports_lineart = (vbuf.imagecomposition & 0x01) == 0x01 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_dithering = (vbuf.imagecomposition & 0x02) == 0x02 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_errordiffusion = (vbuf.imagecomposition & 0x04) == 0x04 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_color = (vbuf.imagecomposition & 0x08) == 0x08 ? SANE_TRUE : SANE_FALSE; /* FALSE */
+ dev->info.supports_4bitgray = (vbuf.imagecomposition & 0x10) == 0x10 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ dev->info.supports_8bitgray = (vbuf.imagecomposition & 0x20) == 0x20 ? SANE_TRUE : SANE_FALSE; /* TRUE */
+ /* vbuf.imagecomposition & 0x40; FALSE */
+ /* vbuf.imagecomposition & 0x80 FALSE reserved */
+ str = &scan_mode_list[0]; /* array of string pointers */
+ if (dev->info.supports_lineart)
+ *str++ = strdup (SM_LINEART);
+ if (dev->info.supports_dithering || dev->info.supports_errordiffusion)
+ *str++ = strdup (SM_HALFTONE);
+ if (dev->info.supports_color)
+ *str++ = strdup (SM_COLOR);
+ if (dev->info.supports_4bitgray)
+ *str++ = strdup (SM_4BITGRAY);
+ if (dev->info.supports_8bitgray)
+ *str++ = strdup (SM_8BITGRAY);
+ *str = NULL;
+
+ snprintf (device_string, 60, "Flatbed%s%s%s%s%s%s",
+ dev->info.hasADF ? "/ADF" : "",
+ dev->info.hasDuplex ? "/Duplex" : "",
+ dev->info.hasEndorser ? "/Endorser" : "",
+ dev->info.hasIPU ? "/IPU" : "",
+ dev->info.supports_color ? " Color" : " B&W", " Scanner");
+ dev->sane.type = strdup (device_string);
+
+ /* ACE Image Data Processing Binary Filters
+ * For IS450 this is set to 0x18 (0001 1000) if IPU installed, else 0x00
+ * For IS420 this is set to 0x3C (0011 1100) if IPU installed, else 0x00
+ */
+ dev->info.supports_whiteframing =
+ ((vbuf.imagedataprocessing[0] & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_blackframing =
+ ((vbuf.imagedataprocessing[0] & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_edgeextraction =
+ ((vbuf.imagedataprocessing[0] & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_noiseremoval =
+ ((vbuf.imagedataprocessing[0] & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_smoothing =
+ ((vbuf.imagedataprocessing[0] & 0x10) == 0x10) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_linebolding =
+ ((vbuf.imagedataprocessing[0] & 0x20) == 0x20) ? SANE_TRUE : SANE_FALSE;
+
+ /* Compression Method is not supported for IS450
+ * is supported for IS420 */
+ dev->info.supports_MH =
+ ((vbuf.compression & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_MR =
+ ((vbuf.compression & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_MMR =
+ ((vbuf.compression & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE;
+ dev->info.supports_MHB = ((vbuf.compression & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE; /* MH Byte Boundary */
+
+ /* compression_list[] will have variable number of elements, but the order will be fixed as follows: */
+ str = &compression_list[0];
+ *str++ = strdup ("none");
+ if (dev->info.supports_MH)
+ *str++ = strdup ("G3 1-D MH");
+ if (dev->info.supports_MR)
+ *str++ = strdup ("G3 2-D MR");
+ if (dev->info.supports_MMR)
+ *str++ = strdup ("G4 2-D MMR");
+ if (dev->info.supports_MHB)
+ *str++ = strdup ("MH Byte Boundary");
+ *str = NULL;
+
+ /* Marker Recognition is set to 0x00 */
+ dev->info.supports_markerrecognition =
+ ((vbuf.markerrecognition & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+
+ /* Size Recognition
+ * For IS450 this is set to 0x01 when IPU installed; else 0x00
+ * For IS420 this is set to 0x01
+ */
+ dev->info.supports_sizerecognition =
+ ((vbuf.sizerecognition & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+
+ /* X Maximum Output Pixel in main scanning direction
+ * For IS450 this is set to 0x1360 (4960)
+ * For IS420 this is set to (4880)
+ * [MostSignificantByte LeastSignificantByte]
+ */
+ dev->info.xmaxoutputpixels =
+ (vbuf.xmaxoutputpixels[0] << 8) | vbuf.xmaxoutputpixels[1];
+
+ /* Set capabilities from jis VPD IDENTIFIER Page Code F0H */
+ dev->info.resBasicX = _2btol (&jbuf.BasicRes.x[0]); /* set to 400 */
+ dev->info.resBasicY = _2btol (&jbuf.BasicRes.y[0]); /* set to 400 */
+
+ dev->info.resXstep = (jbuf.resolutionstep >> 4) & 0x0F; /* set to 1 */
+ dev->info.resYstep = jbuf.resolutionstep & 0x0F; /* set to 1 */
+ dev->info.resMaxX = _2btol (&jbuf.MaxRes.x[0]); /* set to 800 */
+ dev->info.resMaxY = _2btol (&jbuf.MaxRes.y[0]); /* set to 800 */
+ dev->info.resMinX = _2btol (&jbuf.MinRes.x[0]); /* set to 100 for IS450 and 60 for IS420 */
+ dev->info.resMinY = _2btol (&jbuf.MinRes.y[0]); /* set to 100 for IS450 and 60 for IS420 */
+
+ dev->info.xres_range.min = _2btol (&jbuf.MinRes.x[0]); /* set to 100 for IS450 and 60 for IS420 */
+ dev->info.xres_range.max = _2btol (&jbuf.MaxRes.x[0]); /* set to 800 */
+ dev->info.resXstep = (jbuf.resolutionstep >> 4) & 0x0F; /* set to 1 */
+ dev->info.xres_range.quant = dev->info.resXstep;
+
+ dev->info.yres_range.min = _2btol (&jbuf.MinRes.y[0]); /* set to 100 for IS450 and 60 for IS420 */
+ dev->info.yres_range.max = _2btol (&jbuf.MaxRes.y[0]); /* set to 800 */
+ dev->info.resYstep = jbuf.resolutionstep & 0x0F; /* set to 1 */
+ dev->info.yres_range.quant = dev->info.resYstep;
+
+ /* set the length of the list to zero first, then append standard resolutions */
+ i = 0;
+ if ((jbuf.standardres[0] & 0x80) == 0x80)
+ dev->info.resStdList[++i] = 60;
+ if ((jbuf.standardres[0] & 0x40) == 0x40)
+ dev->info.resStdList[++i] = 75;
+ if ((jbuf.standardres[0] & 0x20) == 0x20)
+ dev->info.resStdList[++i] = 100;
+ if ((jbuf.standardres[0] & 0x10) == 0x10)
+ dev->info.resStdList[++i] = 120;
+ if ((jbuf.standardres[0] & 0x08) == 0x08)
+ dev->info.resStdList[++i] = 150;
+ if ((jbuf.standardres[0] & 0x04) == 0x04)
+ dev->info.resStdList[++i] = 160;
+ if ((jbuf.standardres[0] & 0x02) == 0x02)
+ dev->info.resStdList[++i] = 180;
+ if ((jbuf.standardres[0] & 0x01) == 0x01)
+ dev->info.resStdList[++i] = 200;
+ if ((jbuf.standardres[1] & 0x80) == 0x80)
+ dev->info.resStdList[++i] = 240;
+ if ((jbuf.standardres[1] & 0x40) == 0x40)
+ dev->info.resStdList[++i] = 300;
+ if ((jbuf.standardres[1] & 0x20) == 0x20)
+ dev->info.resStdList[++i] = 320;
+ if ((jbuf.standardres[1] & 0x10) == 0x10)
+ dev->info.resStdList[++i] = 400;
+ if ((jbuf.standardres[1] & 0x08) == 0x08)
+ dev->info.resStdList[++i] = 480;
+ if ((jbuf.standardres[1] & 0x04) == 0x04)
+ dev->info.resStdList[++i] = 600;
+ if ((jbuf.standardres[1] & 0x02) == 0x02)
+ dev->info.resStdList[++i] = 800;
+ if ((jbuf.standardres[1] & 0x01) == 0x01)
+ dev->info.resStdList[++i] = 1200;
+ dev->info.resStdList[0] = i; /* number of resolutions */
+ if (dev->info.resStdList[0] == 0)
+ { /* make a default standard resolutions for 200 and 300dpi */
+ DBG (DBG_warning, "attach: no standard resolutions reported\n");
+ dev->info.resStdList[0] = 2;
+ dev->info.resStdList[1] = 200;
+ dev->info.resStdList[2] = 300;
+ dev->info.resBasicX = dev->info.resBasicY = 300;
+ }
+ DBG (DBG_info, "attach: Window(W/L) = (%lu/%lu)\n",
+ _4btol (&jbuf.Window.width[0]), _4btol (&jbuf.Window.length[0]));
+ dev->info.winWidth = _4btol (&jbuf.Window.width[0]);
+ dev->info.winHeight = _4btol (&jbuf.Window.length[0]);
+ if (dev->info.winWidth <= 0)
+ {
+ dev->info.winWidth = (SANE_Int) (dev->info.resBasicX * 8.5);
+ DBG (DBG_warning, "attach: invalid window width reported, using %d\n",
+ dev->info.winWidth);
+ }
+ if (dev->info.winHeight <= 0)
+ {
+ dev->info.winHeight = dev->info.resBasicY * 14;
+ DBG (DBG_warning, "attach: invalid window height reported, using %d\n",
+ dev->info.winHeight);
+ }
+ /* 4692 / 400 * 25.4 = 297 */
+ mm = (dev->info.resBasicX > 0) ?
+ ((double) dev->info.winWidth / (double) dev->info.resBasicX *
+ MM_PER_INCH) : 0.0;
+ dev->info.x_range.min = SANE_FIX (0.0);
+ dev->info.x_range.max = SANE_FIX (mm);
+ dev->info.x_range.quant = SANE_FIX (0.0);
+ DBG (DBG_info, "attach: winWidth=%d resBasicX=%d mm/in=%f mm=%f\n",
+ dev->info.winWidth, dev->info.resBasicX, MM_PER_INCH, mm);
+
+ mm = (dev->info.resBasicY > 0) ?
+ ((double) dev->info.winHeight / (double) dev->info.resBasicY *
+ MM_PER_INCH) : 0.0;
+ dev->info.y_range.min = SANE_FIX (0.0);
+ dev->info.y_range.max = SANE_FIX (mm);
+ dev->info.y_range.quant = SANE_FIX (0.0);
+
+ DBG (DBG_info, "attach: RANGE x_range.max=%f, y_range.max=%f\n",
+ SANE_UNFIX (dev->info.x_range.max),
+ SANE_UNFIX (dev->info.y_range.max));
+
+ /* min, max, quantization light-dark 1-255, 0 means default 128 */
+ dev->info.brightness_range.min = 1;
+ dev->info.brightness_range.max = 255;
+ dev->info.brightness_range.quant = 1;
+ /* min, max, quantization white-black 1-255, 0 means default 128 */
+ dev->info.contrast_range.min = 1;
+ dev->info.contrast_range.max = 255;
+ dev->info.contrast_range.quant = 1;
+ /* min, max, quantization low-high 1-255, 0 means default 128 */
+ dev->info.threshold_range.min = 1;
+ dev->info.threshold_range.max = 255;
+ dev->info.threshold_range.quant = 1;
+
+ /* jbuf.functions */
+ dev->info.overflow_support =
+ ((jbuf.functions & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE;
+ dev->info.lineart_support =
+ ((jbuf.functions & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE;
+ dev->info.dither_support =
+ ((jbuf.functions & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE;
+ dev->info.grayscale_support =
+ ((jbuf.functions & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE;
+
+ /* set option defaults */
+ dev->info.default_res = dev->info.resBasicX;
+ dev->info.default_xres = dev->info.resBasicX;
+ dev->info.default_yres = dev->info.resBasicY;
+ dev->info.default_imagecomposition = LINEART;
+ dev->info.default_media = FLATBED;
+ dev->info.default_duplex = SANE_FALSE;
+
+ /* dev->info.autoborder_default = dev->info.canBorderRecog; */
+ /*
+ dev->info.batch_default = SANE_FALSE;
+ dev->info.deskew_default = SANE_FALSE;
+ dev->info.check_adf_default = SANE_FALSE;
+ dev->info.timeout_adf_default = 0;
+ dev->info.timeout_manual_default = 0;
+ */
+ /* dev->info.control_panel_default = dev->info.canACE; Image Data Processing */
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (DBG_sane_proc, "<<< attach:\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/* SANE callback to attach a SCSI device */
+static SANE_Status
+attach_one_scsi (const char *devname)
+{
+ return attach (devname, CONNECTION_SCSI, NULL);
+ /* return SANE_STATUS_GOOD; */
+}
+
+static void
+parse_configuration_file (FILE * fp)
+{
+ char line[PATH_MAX], *s, *t;
+ int linenumber;
+
+ DBG (DBG_proc, ">> parse_configuration_file\n");
+
+ if (fp == NULL)
+ {
+ DBG (DBG_proc,
+ ">> parse_configuration_file: No config file present!\n");
+ }
+ else
+ { /*parse configuration file */
+ for (linenumber = 0; sanei_config_read (line, sizeof (line), fp);
+ linenumber++)
+ {
+ DBG (DBG_proc,
+ ">> parse_configuration_file: parsing config line \"%s\"\n",
+ line);
+ if (line[0] == '#')
+ continue; /* ignore line comments */
+ for (s = line; isspace (*s); ++s); /* skip white space: */
+ for (t = s; *t != '\0'; t++);
+ for (--t; t > s && isspace (*t); t--);
+ *(++t) = '\0'; /*trim trailing space */
+ if (!strlen (s))
+ continue; /* ignore empty lines */
+ if ((t = strstr (s, "scsi ")) != NULL)
+ {
+ /* scsi VENDOR MODEL TYPE BUS CHANNEL ID LUN */
+ DBG (DBG_proc,
+ ">> parse_configuration_file: config file line %d: trying to attach SCSI: %s'\n",
+ linenumber, line);
+ sanei_config_attach_matching_devices (t, attach_one_scsi);
+ }
+ else if ((t = strstr (s, "/dev/")) != NULL)
+ {
+ /* /dev/scanner /dev/sg0 */
+ DBG (DBG_proc,
+ ">> parse_configuration_file: config file line %d: trying to attach SCSI: %s'\n",
+ linenumber, line);
+ sanei_config_attach_matching_devices (t, attach_one_scsi);
+ }
+ else if ((t = strstr (s, "option")) != NULL)
+ {
+ for (t += 6; isspace (*t); t++); /* skip to flag */
+ /* if(strstr(t,"FLAG_VALUE")!=NULL) FLAG_VALUE=SANE_TRUE; */
+ }
+ else
+ {
+ DBG (DBG_proc,
+ ">> parse_configuration_file: config file line %d: OBSOLETE !! use the scsi keyword!\n",
+ linenumber);
+ DBG (DBG_proc,
+ ">> parse_configuration_file: (see man sane-avision for details): trying to attach SCSI: %s'\n",
+ line);
+ }
+ }
+ fclose (fp);
+ }
+ DBG (DBG_proc, "<< parse_configuration_file\n");
+ return;
+}
+
+static SANE_Status
+do_cancel (HS2P_Scanner * s)
+{
+ SANE_Status status;
+ DBG (DBG_sane_proc, ">> do_cancel\n");
+
+ DBG (DBG_proc, "cancel: sending OBJECT POSITION\n");
+
+ s->scanning = SANE_FALSE;
+ s->cancelled = SANE_TRUE;
+ s->EOM = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ if ((status =
+ object_position (s->fd,
+ OBJECT_POSITION_UNLOAD)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "cancel: OBJECT POSTITION failed\n");
+ }
+ sanei_scsi_req_flush_all ();
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+ /*
+ if (s->reader_pid > 0){
+ int exit_status;
+ sanei_thread_kill (s->reader_pid);
+ sanei_thread_waitpid (s->reader_pid, &exit_status);
+ s->reader_pid = 0;
+ }
+ */
+
+ DBG (DBG_sane_proc, "<< do_cancel\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+ FILE *fp;
+
+ DBG_INIT (); /* initialize SANE DEBUG */
+
+ /*DBG (DBG_sane_init, "> sane_init (authorize = %p)\n", (void *) authorize); */
+#if defined PACKAGE && defined VERSION
+ DBG (DBG_sane_init, "> sane_init: hs2p backend version %d.%d-%d ("
+ PACKAGE " " VERSION ")\n", SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+#endif
+ /*
+ sanei_thread_init ();
+ */
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+
+ if ((fp = sanei_config_open (HS2P_CONFIG_FILE)) != NULL)
+ {
+ parse_configuration_file (fp);
+ }
+ else
+ {
+ DBG (DBG_sane_init, "> sane_init: No config file \"%s\" present!\n",
+ HS2P_CONFIG_FILE);
+ }
+
+#if 0
+ /* avision.c: search for all supported scanners on all scsi busses & channels */
+ for (hw = &HS2P_Device_List[0]; hw->mfg != NULL; hw++)
+ {
+ sanei_scsi_find_devices (hw->mfg, /*vendor */
+ hw->model, /*model */
+ NULL, /*all types */
+ -1, /*all bus */
+ -1, /*all channel */
+ -1, /*all id */
+ -1, /*all lun */
+ attach_one_scsi); /*callback */
+ DBG (2, "sane_init: %s %s\n", hw->mfg, hw->model);
+ }
+#endif
+
+ DBG (DBG_sane_init, "< sane_init\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ HS2P_Device *dev, *next;
+ DBG (DBG_proc, ">> sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) (SANE_String_Const *) dev->sane.name);
+ free ((SANE_String_Const *) dev->sane.model);
+ free (dev);
+ }
+
+ DBG (DBG_proc, "<< sane_exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ HS2P_Device *dev;
+ int i;
+ DBG (DBG_proc, ">> sane_get_devices (local_only = %d)\n", local_only);
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "<< sane_get_devices\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ HS2P_Device *dev;
+ HS2P_Scanner *s;
+ DBG (DBG_proc, "> sane_open\n");
+
+ if (devnam[0] == '\0')
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+ if (!dev)
+ {
+ status = attach (devnam, CONNECTION_SCSI, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s)); /* initialize */
+
+ s->fd = -1;
+ s->hw = dev;
+ s->hw->info.bmu = s->bmu = MILLIMETERS; /* 01H */
+ s->hw->info.mud = s->mud = 1; /* If the scale is MM or POINT, mud is fixed to 1 */
+ s->bpp = 1; /* supports 1,4,6,8 so we set to LINEART 1bpp */
+ /*
+ s->scanning = SANE_FALSE;
+ s->cancelled = SANE_FALSE;
+ */
+ /*
+ */
+
+ ScannerDump (s);
+ init_options (s);
+
+ s->next = first_handle; /* insert newly opened handle into list of open handles: */
+ first_handle = s;
+
+ /* initialize our parameters here AND in sane_start?
+ get_parameters(s, 0);
+ */
+
+ *handle = s;
+
+ DBG (DBG_proc, "< sane_open\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ HS2P_Scanner *s = (HS2P_Scanner *) handle;
+ char **str;
+ DBG (DBG_proc, ">> sane_close\n");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+ free (s);
+
+ for (str = &compression_list[0]; *str; str++);
+ free (*str);
+ for (str = &scan_mode_list[0]; *str; str++);
+ free (*str);
+
+ DBG (DBG_proc, "<< sane_close\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ HS2P_Scanner *s = handle;
+ DBG (DBG_proc, ">> sane_get_option_descriptor: %d name=%s\n", option,
+ s->opt[option].name);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (DBG_info, "<< sane_get_option_descriptor: name=%s\n",
+ s->opt[option].name);
+ return (s->opt + option);
+}
+
+#if 0
+static SANE_Int
+get_scan_mode_id (char *s) /* sequential search */
+{
+ SANE_Int i;
+
+ for (i = 0; scan_mode_list[i]; i++)
+ if (strcmp (s, scan_mode_list[i]) == 0)
+ break;
+
+ /* unknown strings are treated as 'lineart' */
+ return scan_mode_list[i] ? i : 0;
+}
+#endif
+static SANE_Status
+update_hs2p_data (HS2P_Scanner * s)
+{
+
+ DBG (DBG_proc, ">> update_hs2p_data\n");
+ /* OPT_NREGX_ADF: */
+ DBG (DBG_sane_option, "OPT_NREGX_ADF\n");
+ s->val[OPT_NREGX_ADF].w = (SANE_Word) s->data.maintenance.nregx_adf;
+
+ /* OPT_NREGY_ADF: */
+ DBG (DBG_sane_option, "OPT_NREGY_ADF\n");
+ s->val[OPT_NREGY_ADF].w = (SANE_Word) s->data.maintenance.nregx_book;
+
+ /* OPT_NREGX_BOOK: */
+ DBG (DBG_sane_option, "OPT_NREGX_BOOK\n");
+ s->val[OPT_NREGX_BOOK].w = (SANE_Word) s->data.maintenance.nregx_book;
+
+ /* OPT_NREGY_BOOK: */
+ DBG (DBG_sane_option, "OPT_NREGY_BOOK\n");
+ s->val[OPT_NREGY_BOOK].w = (SANE_Word) s->data.maintenance.nregy_book;
+
+ /* OPT_NSCANS_ADF: */
+ DBG (DBG_sane_option, "OPT_NSCANS_ADF\n");
+ s->val[OPT_NSCANS_ADF].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_adf[0]));
+
+ /* OPT_NSCANS_BOOK: */
+ DBG (DBG_sane_option, "OPT_NSCANS_BOOK\n");
+ s->val[OPT_NSCANS_BOOK].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_book[0]));
+
+ /* OPT_LAMP_TIME: */
+ DBG (DBG_sane_option, "OPT_LAMP_TIME\n");
+ s->val[OPT_LAMP_TIME].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.lamp_time[0]));
+
+ /* OPT_EO_ODD: */
+ DBG (DBG_sane_option, "OPT_EO_ODD\n");
+ s->val[OPT_EO_ODD].w = (SANE_Word) s->data.maintenance.eo_odd;
+
+ /* OPT_EO_EVEN: */
+ DBG (DBG_sane_option, "OPT_EO_EVEN\n");
+ s->val[OPT_EO_EVEN].w = (SANE_Word) s->data.maintenance.eo_even;
+
+ /* OPT_BLACK_LEVEL_ODD: */
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_ODD\n");
+ s->val[OPT_BLACK_LEVEL_ODD].w =
+ (SANE_Word) s->data.maintenance.black_level_odd;
+
+ /* OPT_BLACK_LEVEL_EVEN: */
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_EVEN\n");
+ s->val[OPT_BLACK_LEVEL_EVEN].w =
+ (SANE_Word) s->data.maintenance.black_level_even;
+
+ /* OPT_WHITE_LEVEL_ODD: */
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_ODD\n");
+ s->val[OPT_WHITE_LEVEL_ODD].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_odd[0]));
+
+ /* OPT_WHITE_LEVEL_EVEN: */
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_EVEN\n");
+ s->val[OPT_WHITE_LEVEL_EVEN].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_even[0]));
+
+ /* OPT_FIRST_ADJ_WHITE_ODD: */
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_ODD\n");
+ s->val[OPT_FIRST_ADJ_WHITE_ODD].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_odd[0]));
+
+ /* OPT_FIRST_ADJ_WHITE_EVEN: */
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_EVEN\n");
+ s->val[OPT_FIRST_ADJ_WHITE_EVEN].w =
+ (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_even[0]));
+
+ /* OPT_DENSITY: */
+ DBG (DBG_sane_option, "OPT_DENSITY\n");
+ s->val[OPT_DENSITY].w = (SANE_Word) s->data.maintenance.density_adj;
+
+ /* OPT_NREGX_REVERSE: */
+ DBG (DBG_sane_option, "OPT_NREGX_REVERSE\n");
+ s->val[OPT_NREGX_REVERSE].w = (SANE_Word) s->data.maintenance.nregx_reverse;
+
+ /* OPT_NREGY_REVERSE: */
+ DBG (DBG_sane_option, "OPT_NREGY_REVERSE\n");
+ s->val[OPT_NREGY_REVERSE].w = (SANE_Word) s->data.maintenance.nregy_reverse;
+
+ /* OPT_NSCANS_REVERSE_ADF: */
+ DBG (DBG_sane_option, "OPT_NSCANS_REVERSE_ADF\n");
+ s->val[OPT_NSCANS_REVERSE_ADF].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_reverse_adf[0]));
+
+ /* OPT_REVERSE_TIME: */
+ DBG (DBG_sane_option, "OPT_REVERSE_TIME\n");
+ s->val[OPT_REVERSE_TIME].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.reverse_time[0]));
+
+ /* OPT_NCHARS: */
+ DBG (DBG_sane_option, "OPT_NCHARS\n");
+ s->val[OPT_NCHARS].w =
+ (SANE_Word) _4btol (&(s->data.maintenance.nchars[0]));
+
+ DBG (DBG_proc, "<< update_hs2p_data\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hs2p_open (HS2P_Scanner * s)
+{
+ SANE_Status status;
+ DBG (DBG_proc, ">> hs2p_open\n");
+ DBG (DBG_info, ">> hs2p_open: trying to open: name=\"%s\" fd=%d\n",
+ s->hw->sane.name, s->fd);
+ if ((status =
+ sanei_scsi_open (s->hw->sane.name, &s->fd, &sense_handler,
+ &(s->hw->sense_data))) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: open of %s failed: %d %s\n",
+ s->hw->sane.name, status, sane_strstatus (status));
+ return (status);
+ }
+ DBG (DBG_info, ">>hs2p_open: OPENED \"%s\" fd=%d\n", s->hw->sane.name,
+ s->fd);
+
+ if ((status = test_unit_ready (s->fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "hs2p_open: test_unit_ready() failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+ DBG (DBG_proc, "<< hs2p_open\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hs2p_close (HS2P_Scanner * s)
+{
+
+ DBG (DBG_proc, ">> hs2p_close\n");
+
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+
+ DBG (DBG_proc, "<< hs2p_close\n");
+ return SANE_STATUS_GOOD;
+}
+
+#include <stdarg.h>
+static SANE_Status
+get_hs2p_data (HS2P_Scanner * s, ...)
+{
+ SANE_Status status;
+ SANE_Byte *buf;
+ size_t *len = &(s->data.bufsize);
+ int dtc, fd = s->fd;
+ u_long dtq = 0; /* two bytes */
+ va_list ap;
+
+ DBG (DBG_proc, ">> get_hs2p_data\n");
+ if (fd < 0)
+ {
+ status = hs2p_open (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_hs2p_data: error opening scanner: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ }
+
+ for (va_start (ap, s), dtc = va_arg (ap, int); dtc != DATA_TYPE_EOL;
+ dtc = va_arg (ap, int))
+ {
+ DBG (DBG_proc, ">> get_hs2p_data 0x%2.2x\n", (int) dtc);
+ switch (dtc)
+ {
+ case DATA_TYPE_GAMMA:
+ buf = &(s->data.gamma[0]);
+ *len = sizeof (s->data.gamma);
+ break;
+ case DATA_TYPE_ENDORSER:
+ buf = &(s->data.endorser[0]);
+ *len = sizeof (s->data.endorser);
+ break;
+ case DATA_TYPE_SIZE:
+ buf = &(s->data.size);
+ *len = sizeof (s->data.size);
+ break;
+ case DATA_TYPE_PAGE_LEN:
+ buf = s->data.nlines;
+ *len = sizeof (s->data.nlines);
+ break;
+ case DATA_TYPE_MAINTENANCE:
+ buf = (SANE_Byte *) & (s->data.maintenance);
+ *len = sizeof (s->data.maintenance);
+ break;
+ case DATA_TYPE_ADF_STATUS:
+ buf = &(s->data.adf_status);
+ *len = sizeof (s->data.adf_status);
+ break;
+ case DATA_TYPE_IMAGE:
+ case DATA_TYPE_HALFTONE:
+ default:
+ DBG (DBG_info, "Data Type Code %2.2x not handled.\n", dtc);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (DBG_info,
+ "get_hs2p_data calling read_data for dtc=%2.2x and bufsize=%lu\n",
+ (int) dtc, (u_long) * len);
+ status = read_data (s->fd, buf, len, (SANE_Byte) dtc, dtq);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_scanner_data: ERROR %s\n",
+ sane_strstatus (status));
+ }
+ }
+ va_end (ap);
+
+ if (fd < 0)
+ { /* need to return fd to original state */
+ status = hs2p_close (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_hs2p_data: error closing fd: %s\n",
+ sane_strstatus (status));
+ }
+ }
+ DBG (DBG_proc, "<< get_hs2p_data: %d\n", status);
+ return (status);
+}
+
+static SANE_Status
+print_maintenance_data (MAINTENANCE_DATA * d)
+{
+ DBG (DBG_proc, ">> print_maintenance_data: \n");
+
+ DBG (DBG_LEVEL, "nregx_adf = %d\n", d->nregx_adf);
+ DBG (DBG_LEVEL, "nregy_adf = %d\n", d->nregy_adf);
+
+ DBG (DBG_LEVEL, "nregx_book = %d\n", d->nregx_book);
+ DBG (DBG_LEVEL, "nregy_book = %d\n", d->nregy_book);
+
+ DBG (DBG_LEVEL, "nscans_adf = %lu\n", _4btol (&(d->nscans_adf[0])));
+ DBG (DBG_LEVEL, "nscans_adf = %lu\n", _4btol (&(d->nscans_adf[0])));
+
+ DBG (DBG_LEVEL, "lamp time = %lu\n", _4btol (&(d->lamp_time[0])));
+
+ DBG (DBG_LEVEL, "eo_odd = %d\n", d->eo_odd);
+ DBG (DBG_LEVEL, "eo_even = %d\n", d->eo_even);
+
+ DBG (DBG_LEVEL, "black_level_odd = %d\n", d->black_level_odd);
+ DBG (DBG_LEVEL, "black_level_even = %d\n", d->black_level_even);
+
+ DBG (DBG_LEVEL, "white_level_odd = %lu\n",
+ _2btol (&(d->white_level_odd[0])));
+ DBG (DBG_LEVEL, "white_level_even = %lu\n",
+ _2btol (&(d->white_level_even[0])));
+
+ DBG (DBG_LEVEL, "first_adj_white_odd = %lu\n",
+ _2btol (&(d->first_adj_white_odd[0])));
+ DBG (DBG_LEVEL, "first_adj_white_even = %lu\n",
+ _2btol (&(d->first_adj_white_even[0])));
+
+ DBG (DBG_LEVEL, "density_adj = %d\n", d->density_adj);
+
+ DBG (DBG_LEVEL, "nregx_reverse = %d\n", d->nregx_reverse);
+ DBG (DBG_LEVEL, "nregy_reverse = %d\n", d->nregy_reverse);
+
+ DBG (DBG_LEVEL, "nscans_reverse_adf = %lu\n",
+ _4btol (&(d->nscans_reverse_adf[0])));
+
+ DBG (DBG_LEVEL, "reverse_time = %lu\n", _4btol (&(d->reverse_time[0])));
+
+ DBG (DBG_LEVEL, "nchars = %lu\n", _4btol (&(d->nchars[0])));
+
+ DBG (DBG_proc, "<< print_maintenance_data: \n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ HS2P_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_String_Const name;
+ SANE_Int paper_id;
+
+
+
+ name = s->opt[option].name ? s->opt[option].name : "(nil)";
+ if (info)
+ *info = 0;
+ DBG (DBG_proc, ">> sane_control_option: %s option=%d name=%s\n",
+ action == SANE_ACTION_GET_VALUE ? "SET" : "GET", option, name);
+
+ if (s->scanning)
+ return (SANE_STATUS_DEVICE_BUSY);
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (DBG_proc, "sane_control_option get_value option=%d\n", option);
+ switch (option)
+ {
+ /* word options: */
+ 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_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = s->val[option].w;
+ return (SANE_STATUS_GOOD);
+
+ /* bool options: */
+ /*case OPT_AUTOBORDER: case OPT_DESKEW: case OPT_CHECK_ADF: case OPT_BATCH: */
+ case OPT_PREVIEW:
+ case OPT_SCAN_WAIT_MODE:
+ case OPT_DUPLEX:
+ case OPT_AUTO_SIZE:
+ case OPT_NEGATIVE:
+ case OPT_ENDORSER:
+ case OPT_SMOOTHING:
+ case OPT_WHITE_BALANCE:
+ case OPT_PREFEED:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_PADDING:
+ *(SANE_Bool *) val = s->val[option].w;
+ return (SANE_STATUS_GOOD);
+
+ /* string options: */
+ /* case OPT_ADF: */
+ /* case OPT_BITORDER: */
+ /* case OPT_ROTATION */
+ /* case OPT_SECTION: */
+ case OPT_INQUIRY:
+ case OPT_SCAN_SOURCE:
+ case OPT_PAGE_ORIENTATION:
+ case OPT_PAPER_SIZE:
+ case OPT_SCAN_MODE:
+ case OPT_ENDORSER_STRING:
+ case OPT_COMPRESSION:
+ case OPT_NOISEREMOVAL:
+ case OPT_GRAYFILTER:
+ case OPT_HALFTONE_CODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_GAMMA:
+ case OPT_AUTOSEP:
+ case OPT_AUTOBIN:
+ case OPT_PADDING_TYPE:
+ DBG (DBG_proc, "STRING=%s\n", s->val[option].s);
+ strcpy (val, s->val[option].s);
+ return (SANE_STATUS_GOOD);
+ DBG (DBG_proc, "sizeof(val)=%lu sizeof(s)=%lu\n",
+ (u_long) sizeof (val), (u_long) sizeof (s->val[option].s));
+ return (SANE_STATUS_GOOD);
+
+ /* gamma */
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* MAINTENANCE DATA */
+ case OPT_DATA_GROUP:
+ case OPT_UPDATE:
+ return SANE_STATUS_GOOD;
+ case OPT_NREGX_ADF:
+ DBG (DBG_sane_option, "OPT_NREGX_ADF\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_adf;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGY_ADF:
+ DBG (DBG_sane_option, "OPT_NREGY_ADF\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_book;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGX_BOOK:
+ DBG (DBG_sane_option, "OPT_NREGX_BOOK\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_book;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGY_BOOK:
+ DBG (DBG_sane_option, "OPT_NREGY_BOOK\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregy_book;
+ return SANE_STATUS_GOOD;
+ case OPT_NSCANS_ADF:
+ DBG (DBG_sane_option, "OPT_NSCANS_ADF\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_adf[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_NSCANS_BOOK:
+ DBG (DBG_sane_option, "OPT_NSCANS_BOOK\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_book[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_LAMP_TIME:
+ DBG (DBG_sane_option, "OPT_LAMP_TIME\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.lamp_time[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_EO_ODD:
+ DBG (DBG_sane_option, "OPT_EO_ODD\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.eo_odd;
+ return SANE_STATUS_GOOD;
+ case OPT_EO_EVEN:
+ DBG (DBG_sane_option, "OPT_EO_EVEN\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.eo_even;
+ return SANE_STATUS_GOOD;
+ case OPT_BLACK_LEVEL_ODD:
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_ODD\n");
+ *(SANE_Word *) val =
+ (SANE_Word) s->data.maintenance.black_level_odd;
+ return SANE_STATUS_GOOD;
+ case OPT_BLACK_LEVEL_EVEN:
+ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_EVEN\n");
+ *(SANE_Word *) val =
+ (SANE_Word) s->data.maintenance.black_level_even;
+ return SANE_STATUS_GOOD;
+ case OPT_WHITE_LEVEL_ODD:
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_ODD\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_odd[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_WHITE_LEVEL_EVEN:
+ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_EVEN\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _2btol (&(s->data.maintenance.white_level_even[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_FIRST_ADJ_WHITE_ODD:
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_ODD\n");
+ *(SANE_Word *) val =
+ (SANE_Word)
+ _2btol (&(s->data.maintenance.first_adj_white_odd[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_FIRST_ADJ_WHITE_EVEN:
+ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_EVEN\n");
+ *(SANE_Word *) val =
+ (SANE_Word)
+ _2btol (&(s->data.maintenance.first_adj_white_even[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_DENSITY:
+ DBG (DBG_sane_option, "OPT_DENSITY\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.density_adj;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGX_REVERSE:
+ DBG (DBG_sane_option, "OPT_NREGX_REVERSE\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_reverse;
+ return SANE_STATUS_GOOD;
+ case OPT_NREGY_REVERSE:
+ DBG (DBG_sane_option, "OPT_NREGY_REVERSE\n");
+ *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregy_reverse;
+ return SANE_STATUS_GOOD;
+ case OPT_NSCANS_REVERSE_ADF:
+ DBG (DBG_sane_option, "OPT_NSCANS_REVERSE_ADF\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nscans_reverse_adf[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_REVERSE_TIME:
+ DBG (DBG_sane_option, "OPT_REVERSE_TIME\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.reverse_time[0]));
+ return SANE_STATUS_GOOD;
+ case OPT_NCHARS:
+ DBG (DBG_sane_option, "OPT_NCHARS\n");
+ *(SANE_Word *) val =
+ (SANE_Word) _4btol (&(s->data.maintenance.nchars[0]));
+ return (SANE_STATUS_GOOD);
+
+ default:
+ DBG (DBG_proc, "sane_control_option:invalid option number %d\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (DBG_proc, "sane_control_option set_value\n");
+ switch (s->opt[option].type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %d\n",
+ name, option, *(SANE_Word *) val);
+ break;
+ case SANE_TYPE_FIXED:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %f\n",
+ name, option, SANE_UNFIX (*(SANE_Word *) val));
+ break;
+ case SANE_TYPE_STRING:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %s\n",
+ name, option, (char *) val);
+ break;
+ case SANE_TYPE_BUTTON:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d]\n",
+ name, option);
+ update_hs2p_data (s);
+ break;
+ default:
+ DBG (DBG_proc, "sane_control_option: set_value %s [#%d]\n", name,
+ option);
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+ if ((status =
+ sanei_constrain_value (s->opt + option, val,
+ info)) != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE; /* disable auto size */
+ /* make sure that paper-size is set to custom */
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ s->val[option].w = *(SANE_Word *) val;
+ /* resets the paper format to user defined */
+ if (strcmp (s->val[OPT_PAPER_SIZE].s, paper_list[0]) != 0) /* CUSTOM PAPER SIZE */
+ {
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[OPT_PAPER_SIZE].s)
+ free (s->val[OPT_PAPER_SIZE].s);
+ s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]); /* CUSTOM PAPER SIZE */
+ }
+ /* fall through */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ /* fall through */
+ /*case OPT_ACE_FUNCTION: case OPT_ACE_SENSITIVITY: */
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_NUM_OPTS:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ /* string options */
+ case OPT_NOISEREMOVAL:
+ case OPT_AUTOSEP:
+ case OPT_AUTOBIN:
+ case OPT_COMPRESSION:
+ case OPT_PADDING_TYPE:
+ case OPT_GRAYFILTER:
+ case OPT_HALFTONE_CODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_ENDORSER_STRING:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ /* boolean options: */
+ case OPT_PREVIEW:
+ case OPT_DUPLEX:
+ case OPT_NEGATIVE:
+ case OPT_SCAN_WAIT_MODE:
+ case OPT_ENDORSER:
+ case OPT_SMOOTHING:
+ case OPT_WHITE_BALANCE:
+ case OPT_PREFEED:
+ case OPT_PADDING:
+ s->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_GRAY:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* options with side effect */
+ case OPT_GAMMA:
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ if (!strcmp ((SANE_String) val, "User"))
+ {
+ s->val[OPT_CUSTOM_GAMMA].b = SANE_TRUE;
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ /* Brightness and Contrast do not work when downloading Gamma Table */
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ s->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val;
+ if (s->val[OPT_CUSTOM_GAMMA].w)
+ {
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ s->val[option].w = *(SANE_Word *) val;
+ s->val[OPT_X_RESOLUTION].w = *(SANE_Word *) val;
+ s->val[OPT_Y_RESOLUTION].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+ case OPT_SCAN_SOURCE:
+ /* a string option */
+ /* Since the scanner ejects the sheet in ADF mode
+ * it is impossible to scan multiple sections in one document
+ * In ADF mode, because of mechanical limitations:
+ * the minimum document size is (x,y)=(69mm x 120mm)
+ */
+ if (info && strcmp ((char *) s->val[option].s, (char *) val))
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (!strcmp ("ADF", (SANE_String) val))
+ {
+ s->opt[OPT_ENDORSER].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_ENDORSER_STRING].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_PREFEED].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_DUPLEX].cap &= ~SANE_CAP_INACTIVE;
+ /*s->opt[OPT_PADDING].cap &= ~SANE_CAP_INACTIVE; */
+ }
+ else
+ { /* Flatbed */
+ s->opt[OPT_ENDORSER].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_ENDORSER_STRING].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_PREFEED].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+ case OPT_SCAN_MODE:
+ /* a string option */
+ /* scan mode != lineart disables compression, setting it to 'none' */
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if (!strcmp (SM_LINEART, (SANE_String) val))
+ {
+ s->image_composition = LINEART;
+ s->opt[OPT_COMPRESSION].cap &= ~SANE_CAP_INACTIVE; /* enable compression control */
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; /* enable threshold control */
+ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; /* disable brightness control */
+ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; /* disable contrast control */
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; /* disable gamma */
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; /* disable gamma */
+ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; /* disable gamma */
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* disable halftone code */
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* disable halftone pattern */
+ }
+ else
+ {
+ if (!strcmp (SM_HALFTONE, (SANE_String) val))
+ {
+ s->image_composition = HALFTONE;
+ s->opt[OPT_HALFTONE_CODE].cap &= ~SANE_CAP_INACTIVE; /* enable halftone code */
+ s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; /* enable halftone pattern */
+ }
+ else if (!strcmp (SM_4BITGRAY, (SANE_String) val) ||
+ !strcmp (SM_6BITGRAY, (SANE_String) val) ||
+ !strcmp (SM_8BITGRAY, (SANE_String) val))
+ {
+ s->image_composition = GRAYSCALE;
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; /* enable gamma */
+ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; /* enable brightness */
+ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; /* enable contrast */
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* disable threshold */
+ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE; /* disable compression */
+ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* disable halftone code */
+ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* disable halftone pattern */
+ if (s->val[OPT_COMPRESSION].s
+ && get_compression_id (s->val[OPT_COMPRESSION].s) !=
+ 0)
+ {
+ free (s->val[OPT_COMPRESSION].s);
+ s->val[OPT_COMPRESSION].s =
+ strdup (compression_list[0]);
+ }
+ }
+ }
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_ORIENTATION:
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ /* set val to current selected paper size */
+ paper_id = get_paper_id ((SANE_String) s->val[OPT_PAPER_SIZE].s);
+ goto paper_id;
+ case OPT_PAPER_SIZE:
+ /* a string option */
+ /* changes geometry options, therefore _RELOAD_PARAMS and _RELOAD_OPTIONS */
+ s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE; /* disable auto size */
+ if (strcmp (s->val[option].s, (SANE_String) val))
+ {
+ paper_id = get_paper_id ((SANE_String) val);
+
+ /* paper_id 0 is a special case (custom) that
+ * disables the paper size control of geometry
+ */
+ paper_id:
+ if (paper_id != 0)
+ {
+ double x_max, y_max, x, y, temp;
+
+ x_max = SANE_UNFIX (s->hw->info.x_range.max);
+ y_max = SANE_UNFIX (s->hw->info.y_range.max);
+
+ /* a dimension of 0.0 (or less) is replaced with the max value */
+ x = (paper_sizes[paper_id].width <= 0.0) ? x_max :
+ paper_sizes[paper_id].width;
+ y = (paper_sizes[paper_id].length <= 0.0) ? y_max :
+ paper_sizes[paper_id].length;
+
+ if (info)
+ *info |=
+ SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ if (!strcmp (s->val[OPT_PAGE_ORIENTATION].s, LANDSCAPE)) /* swap */
+ {
+ temp = y_max;
+ y_max = x_max;
+ x_max = temp;
+ temp = y;
+ y = x;
+ x = temp;
+ }
+
+ 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 (MIN (x, x_max));
+ s->val[OPT_BR_Y].w = SANE_FIX (MIN (y, y_max));
+ }
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ }
+ return SANE_STATUS_GOOD;
+ case OPT_UPDATE: /* SANE_TYPE_BUTTON */
+ DBG (DBG_info,
+ "OPT_UPDATE: ready to call get_hs2p_data: fd=%d\n", s->fd);
+ get_hs2p_data (s,
+ /* DATA_TYPE_GAMMA, */
+ /* DATA_TYPE_ENDORSER, */
+ /* DATA_TYPE_SIZE, */
+ /* DATA_TYPE_PAGE_LEN, */
+ DATA_TYPE_MAINTENANCE,
+ /* DATA_TYPE_ADF_STATUS, */
+ /* DATA_TYPE_IMAGE, */
+ /* DATA_TYPE_HALFTONE, */
+ DATA_TYPE_EOL); /* va_list end */
+ update_hs2p_data (s);
+ if (DBG_LEVEL >= DBG_info)
+ print_maintenance_data (&(s->data.maintenance));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+ }
+ return (SANE_STATUS_GOOD);
+ }
+
+ DBG (DBG_proc, "<< sane_control_option\n");
+ return (SANE_STATUS_INVAL);
+
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ HS2P_Scanner *s = handle;
+ DBG (DBG_proc, ">> sane_get_parameters\n");
+
+ if (!s->scanning)
+ {
+ int width, length, xres, yres;
+ const char *mode;
+
+ memset (&s->params, 0, sizeof (s->params)); /* CLEAR SANE_Parameters */
+
+ width =
+ (int) (SANE_UNFIX (s->val[OPT_BR_X].w) -
+ SANE_UNFIX (s->val[OPT_TL_X].w));
+ length =
+ (int) (SANE_UNFIX (s->val[OPT_BR_Y].w) -
+ SANE_UNFIX (s->val[OPT_TL_Y].w));
+ xres = s->val[OPT_X_RESOLUTION].w;
+ yres = s->val[OPT_Y_RESOLUTION].w;
+ DBG (DBG_proc,
+ ">>sane_get_parameters: (W/L)=(%d/%d) (xres/yres)=(%d/%d) mud=%d\n",
+ width, length, xres, yres, s->hw->info.mud);
+
+ /* make best-effort guess at what parameters will look like once scanning starts. */
+ if (xres > 0 && yres > 0 && width > 0 && length > 0)
+ { /* convert from mm to pixels */
+ s->params.pixels_per_line =
+ width * xres / s->hw->info.mud / MM_PER_INCH;
+ s->params.lines = length * yres / s->hw->info.mud / MM_PER_INCH;
+ }
+
+ mode = s->val[OPT_SCAN_MODE].s;
+ if ((strcmp (mode, SM_LINEART) == 0) ||
+ (strcmp (mode, SM_HALFTONE)) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ /* if the scanner truncates to the byte boundary, so: chop! */
+ s->params.pixels_per_line = s->params.bytes_per_line * 8;
+ s->params.depth = 1;
+ }
+ else /* if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) */
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ }
+ s->params.last_frame = SANE_TRUE;
+ }
+ else
+ DBG (DBG_proc, "sane_get_parameters: scanning, so can't get params\n");
+
+ if (params)
+ *params = s->params;
+
+ DBG (DBG_proc,
+ "%d pixels per line, %d bytes per line, %d lines high, total %lu bytes, "
+ "dpi=%ld\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read,
+ (long) SANE_UNFIX (s->val[OPT_Y_RESOLUTION].w));
+
+ DBG (DBG_proc, "<< sane_get_parameters\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+set_window_data (HS2P_Scanner * s, SWD * wbuf)
+{
+ struct hs2p_window_data *data;
+ int i, nwin, id, xres, yres, xmax, ymax;
+ long ulx, uly, width, length, number, bytes;
+ double offset;
+
+ DBG (DBG_proc, ">> set_window_data: sizeof(*wbuf)=%lu; window len=%lu\n",
+ (u_long) sizeof (*wbuf), (u_long) sizeof (wbuf->data));
+
+ /* initialize our window buffer with zeros */
+ DBG (DBG_proc, ">> set_window_data: CLEARING wbuf\n");
+ memset (wbuf, 0, sizeof (*wbuf));
+
+ /* Header */
+ DBG (DBG_proc,
+ ">> set_window_data: writing Window Descriptor Length =%lu\n",
+ (u_long) sizeof (wbuf->data));
+ _lto2b (sizeof (wbuf->data), &wbuf->hdr.len[0]);
+
+ /* X-Axis Resolution 100-800dpi in 1 dpi steps */
+ xres = s->val[OPT_X_RESOLUTION].w;
+ if (xres < s->hw->info.resMinX || xres > s->hw->info.resMaxX)
+ {
+ DBG (DBG_error, "XRESOLUTION %d IS NOT WITHIN [%d, %d]\n", xres,
+ s->hw->info.resMinX, s->hw->info.resMaxX);
+ return (SANE_STATUS_INVAL);
+ }
+
+ /* Y-Axis Resolution 100-800dpi in 1 dpi steps */
+ yres = s->val[OPT_Y_RESOLUTION].w;
+ if (yres < s->hw->info.resMinY || yres > s->hw->info.resMaxY)
+ {
+ DBG (DBG_error, "YRESOLUTION %d IS NOT WITHIN [%d, %d]\n", yres,
+ s->hw->info.resMinY, s->hw->info.resMaxY);
+ return (SANE_STATUS_INVAL);
+ }
+
+ ulx = (long) SANE_UNFIX (s->val[OPT_TL_X].w);
+ uly = (long) SANE_UNFIX (s->val[OPT_TL_Y].w);
+ DBG (DBG_info, "set_window_data: upperleft=(%ld,%ld)\n", ulx, uly);
+
+ width = (long) SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); /* Window Width */
+ length = (long) SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); /* Window Length */
+ DBG (DBG_info, "set_window_data: WxL= %ld x %ld\n", width, length);
+
+ /* NOTE: the width in inches converted to byte unit must be the following values or less
+ * Binary: 620 bytes
+ * 4-bits gray: 2480 bytes
+ * 8-bits gray: 4960 bytes
+ */
+ if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_LINEART))
+ {
+ bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w / 8.0);
+ if (bytes > 620)
+ {
+ DBG (DBG_error,
+ "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n",
+ width, s->val[OPT_X_RESOLUTION].w, bytes);
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_4BITGRAY))
+ {
+ bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w / 2.0);
+ if (bytes > 2480)
+ {
+ DBG (DBG_error,
+ "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n",
+ width, s->val[OPT_X_RESOLUTION].w, bytes);
+ return (SANE_STATUS_INVAL);
+ }
+ }
+ else if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_8BITGRAY))
+ {
+ bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w);
+ if (bytes > 4960)
+ {
+ DBG (DBG_error,
+ "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n",
+ width, s->val[OPT_X_RESOLUTION].w, bytes);
+ return (SANE_STATUS_INVAL);
+ }
+ }
+
+
+ if (strcmp (s->val[OPT_SCAN_SOURCE].s, scan_source_list[ADF]) == 0)
+ {
+ offset = (SANE_UNFIX (s->hw->info.x_range.max) - width) / 2.0;
+ DBG (DBG_info, "set_window_data: ADF origin offset=%f\n", offset);
+
+ ulx += (long) offset;
+ }
+
+
+ if (strcmp (s->val[OPT_SCAN_SOURCE].s, scan_source_list[FB]) == 0)
+ { /* FB */
+ xmax = 298; /*mm */
+ ymax = 432;
+ }
+ else
+ { /* ADF */
+ xmax = 298;
+ ymax = 2000;
+ }
+
+ /* Boundary Conditions when BMU = MM */
+ number = ulx + width;
+ if (number <= 0 || number > xmax)
+ {
+ DBG (DBG_error, "NOT WITHIN BOUNDS: ulx=%ld width=%ld sum=%ld\n",
+ ulx, width, number);
+ return (SANE_STATUS_INVAL);
+ }
+ number = uly + length;
+ if (number <= 0 || number > ymax)
+ {
+ DBG (DBG_error, "NOT WITHIN BOUNDS: uly=%ld length=%ld sum=%ld\n",
+ uly, length, number);
+ return (SANE_STATUS_INVAL);
+ }
+
+
+
+ /* For each window (up to 2 if we're duplexing) */
+ nwin = (s->val[OPT_DUPLEX].w == SANE_TRUE) ? 2 : 1;
+ for (i = 0; i < nwin; i++)
+ {
+ data = &(wbuf->data[i]);
+ data->window_id = i;
+ data->auto_bit &= 0xFE; /* Auto bit set to 0 since auto function isn't supported */
+
+ _lto2b (xres, &data->xres[0]); /* Set X resolution */
+ _lto2b (yres, &data->yres[0]); /* Set Y resolution */
+
+ _lto4b (ulx, &data->ulx[0]); /* X-Axis Upper Left */
+ _lto4b (uly, &data->uly[0]); /* Y-Axis Upper Left */
+
+ _lto4b (width, &data->width[0]); /* Window Width */
+ _lto4b (length, &data->length[0]); /* Window Length */
+
+
+
+
+
+
+ data->brightness = s->val[OPT_BRIGHTNESS].w; /* black-white: 1-255; 0 is default 128 */
+ data->threshold = s->val[OPT_THRESHOLD].w; /* light-dark: 1-255; 0 is default 128 */
+ data->contrast = s->val[OPT_CONTRAST].w; /* low-high: 1-255: 0 is default 128 */
+ if (data->brightness == 128)
+ data->brightness = 0;
+ if (data->threshold == 128)
+ data->threshold = 0;
+ if (data->contrast == 128)
+ data->contrast = 0;
+
+ data->image_composition = s->image_composition;
+ data->bpp = s->bpp = s->params.depth;
+
+ /* Byte 27, 347 Halftone Code: if HALFTONE, then either DITHER or ERROR_DIFFUSION */
+ if (s->image_composition == HALFTONE)
+ { /* Then let's use pattern selected by user */
+ data->halftone_code =
+ (get_halftone_code_id (s->val[OPT_HALFTONE_CODE].s) ==
+ 0) ? DITHER : ERROR_DIFFUSION;
+ data->halftone_id =
+ get_halftone_pattern_val (s->val[OPT_HALFTONE_PATTERN].s);
+ }
+ else
+ {
+ data->halftone_code = DITHER; /* 00H reserved */
+ data->halftone_id = 0x01; /* 00H reserved */
+ }
+
+
+
+ /* Byte 29, 349: RIF:reserved:padding type */
+ if (data->image_composition == LINEART
+ || data->image_composition == HALFTONE)
+ {
+ if (s->val[OPT_NEGATIVE].w)
+ data->byte29 |= (1 << 7); /* set bit 7 */
+ else
+ data->byte29 &= ~(1 << 7); /* unset bit 7 */
+ }
+ /* Padding Type */
+ data->byte29 |=
+ (paddingtype[get_paddingtype_id (s->val[OPT_PADDING_TYPE].s)].
+ val & 0x07);
+
+ /* Bit Ordering:
+ * Manual Says DEFAULT: [1111 1111][1111 1000]
+ * Bits15-8 reserved;
+ * Bit7: '0'-Normal '1'-Mirroring
+ * Bit6-4: Reserved
+ * Bit3: '0'-arrangement from MSB in grayscale mode
+ * '1'-arrangement from LSB in grayscale mode
+ * 2: '0'-unpacked 4-bits grayscale [DEFAULT]
+ * '1'-packed 4-bits grayscale
+ * 1: '0'-output from LSB of each word [DEFAULT]
+ * '1'-output from MSB of each word
+ * 0: '0'-output from bit 0 of each byte [DEFAULT]
+ * '1'-output from bit 7 of each byte
+ */
+ _lto2b (0x007, &data->bit_ordering[0]); /* Set to Packed4bitGray, MSB, MSbit */
+
+ /* Compression Type and Argument NOT SUPPORTED in this scanner */
+ data->compression_type = 0x00;
+ data->compression_arg = 0x02;
+
+ /* Byte42: MRIF:Filtering:GammaID */
+ if (data->image_composition == GRAYSCALE)
+ {
+ if (s->val[OPT_NEGATIVE].w)
+ data->byte42 &= ~(1 << 7); /* unset bit 7 */
+ else
+ data->byte42 |= (1 << 7); /* set bit 7 */
+ data->byte42 |= (get_grayfilter_val (s->val[OPT_GRAYFILTER].s) & (7 << 4)); /* set bits 6-4 to GRAYFILTER */
+ }
+ else
+ {
+ data->byte42 &= ~(1 << 7); /* unset bit 7 */
+ data->byte42 &= ~(7 << 4); /* unset bits 6-4 */
+ }
+ /* Bytes 45, 365 Binary Filtering for lineart and halftone can be set when option IPU is installed */
+ if ((id = get_noisematrix_id (s->val[OPT_NOISEREMOVAL].s)) != 0)
+ {
+ data->binary_filtering |= (1 << 7); /* set bit 7 */
+ data->binary_filtering |= noisematrix[id].val; /* 00H, 01H, 02H; 03H:Reserved */
+ }
+ if (s->val[OPT_SMOOTHING].w == SANE_TRUE)
+ data->binary_filtering |= (1 << 6); /* set bit 6 */
+
+ /* Automatic separation, automatic binarization, and SECTION is available if Image Processing Unit is installed */
+ if (s->hw->info.hasIPU)
+ {
+ /* Byte 48: Automatic Separation */
+ data->automatic_separation =
+ get_auto_separation_val (s->val[OPT_AUTOSEP].s);
+ /* Byte 50: Automatic Binarization */
+ data->automatic_binarization =
+ get_auto_binarization_val (s->val[OPT_AUTOBIN].s);
+ /* fill in values for each section
+ for(j=0; j<NumSec; j++){
+ wbuf[i].winsec[j].ulx
+ wbuf[i].winsec[j].uly
+ wbuf[i].winsec[j].width
+ wbuf[i].winsec[j].length
+ wbuf[i].winsec[j].binary_filtering
+ wbuf[i].winsec[j].threshold
+ wbuf[i].winsec[j].image_composition
+ wbuf[i].winsec[j].halftone_id
+ wbuf[i].winsec[j].halftone_arg
+ }
+ */
+ }
+ }
+ DBG (DBG_proc, "<< set_window_data\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_start (SANE_Handle handle) /* begin scanning */
+{
+ HS2P_Scanner *s = handle;
+ SANE_Status status;
+ SWD wbuf; /* Set Window Data: hdr + data */
+ GWD gbuf; /* Get Window Data: hdr + data */
+ SANE_Byte mode, prefeed, mwt = 0;
+
+ DBG (DBG_proc, ">> sane_start\n");
+ s->cancelled = SANE_FALSE;
+
+ if (s->another_side)
+ {
+ /* Number of bytes to read for one side of sheet */
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ DBG (DBG_info,
+ "SIDE#2 %d pixels per line, %d bytes, %d lines high, dpi=%d\n",
+ s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (int) s->val[OPT_Y_RESOLUTION].w);
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+ s->another_side = SANE_FALSE; /* This is side 2, so no more sides */
+ DBG (DBG_proc, "<< sane_start\n");
+ return (SANE_STATUS_GOOD);
+ }
+
+ if (s->scanning)
+ {
+ DBG (DBG_info, "sane_start: device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* Let's start a new scan */
+
+ if ((status = sane_get_parameters (s, 0)) != SANE_STATUS_GOOD)
+ { /* get preliminary parameters */
+ DBG (DBG_error, "sane_start: sane_get_parameters failed: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ DBG (DBG_info, ">> sane_start: trying to open: name=\"%s\" fd=%d\n",
+ s->hw->sane.name, s->fd);
+ if ((status =
+ sanei_scsi_open (s->hw->sane.name, &s->fd, &sense_handler,
+ &(s->hw->sense_data))) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: open of %s failed: %d %s\n",
+ s->hw->sane.name, status, sane_strstatus (status));
+ return (status);
+ }
+ DBG (DBG_info, ">>sane_start: OPENED \"%s\" fd=%d\n", s->hw->sane.name,
+ s->fd);
+
+ if ((status = test_unit_ready (s->fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: test_unit_ready() failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return status;
+ }
+
+
+ if ((status = reserve_unit (s->fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: reserve_unit() failed: %s\n",
+ sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ /* NOW SET UP SCANNER ONCE PER BATCH */
+
+ DBG (DBG_info, "sane_start: setting basic measurement unit to mm\n");
+ if ((status = set_basic_measurement_unit (s->fd, s->hw->info.bmu)))
+ {
+ DBG (DBG_error, "set_basic_measurment_unit failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ if (get_scan_source_id (s->val[OPT_SCAN_SOURCE].s) == 0)
+ {
+ mode = FLATBED;
+ }
+ else
+ {
+ mode = (s->val[OPT_DUPLEX].w) ? DUPLEX : SIMPLEX;
+ }
+
+ prefeed = s->val[OPT_PREFEED].w ? 0x04 : 0x00;
+ DBG (DBG_info, "sane_start: setting scan source to %d %s\n", mode,
+ (SANE_String) s->val[OPT_SCAN_SOURCE].s);
+ DBG (DBG_info, "sane_start: setting prefeed to %d\n", prefeed);
+ if ((status =
+ set_adf_control (s->fd, &mode, &prefeed, &mwt)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: error set_adf_control: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (SANE_STATUS_INVAL);
+ }
+
+
+ DBG (DBG_info, "sane_start: setting endorser control to %d\n",
+ s->val[OPT_ENDORSER].w);
+ if ((status =
+ set_endorser_control (s->fd,
+ &s->val[OPT_ENDORSER].w)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_endorser_control failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ if (s->val[OPT_ENDORSER].w)
+ {
+ DBG (DBG_info, "sane_start: setting endorser string to %s\n",
+ s->val[OPT_ENDORSER_STRING].s);
+ if ((status =
+ set_endorser_string (s->fd,
+ (SANE_String) s->val[OPT_ENDORSER_STRING].
+ s)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_endorser_string failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ }
+
+ DBG (DBG_info, "sane_start: setting scan_wait_mode to %d\n",
+ s->val[OPT_SCAN_WAIT_MODE].w);
+ if ((status =
+ set_scan_wait_mode (s->fd,
+ s->val[OPT_SCAN_WAIT_MODE].w)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_scan_wait_mode failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (DBG_info, "sane_start: setting white_balance to %d\n",
+ s->val[OPT_WHITE_BALANCE].w);
+ if ((status =
+ set_white_balance (s->fd,
+ &s->val[OPT_WHITE_BALANCE].w)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_white_balance failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ if (s->val[OPT_CUSTOM_GAMMA].b)
+ { /* Custom Gamma needs to be sent to scanner */
+ DBG (DBG_info, "sane_start: setting custom gamma\n");
+ if ((status = hs2p_send_gamma (s)))
+ {
+ DBG (DBG_error, "hs2p_send_gamma failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ /* We succeeded, so we don't need to upload this vector again (unless user modifies gamma table) */
+ s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE;
+ }
+
+
+ DBG (DBG_info, "sane_start: filling in window data buffer \n");
+ if ((status = set_window_data (s, &wbuf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "set_window_data failed: %s\n",
+ sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (DBG_info, "sane_start: sending SET WINDOW DATA\n");
+ if ((status = set_window (s->fd, &wbuf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "SET WINDOW DATA failed: %s\n",
+ sane_strstatus (status));
+ print_window_data (&wbuf);
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ DBG (DBG_info, "sane_start: sending GET WINDOW\n");
+ memset (&gbuf, 0, sizeof (gbuf)); /* CLEAR wbuf */
+ if ((status = get_window (s->fd, &gbuf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ release_unit (s->fd);
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ /* DONE WITH SETTING UP SCANNER ONCE PER BATCH */
+
+ s->EOM = SANE_FALSE;
+ if (mode != FLATBED)
+ {
+ if ((status =
+ get_hs2p_data (s, DATA_TYPE_ADF_STATUS,
+ DATA_TYPE_EOL)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sane_start: error reading adf_status: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+ if ((s->data.adf_status & 0x00) == 0x01)
+ {
+ DBG (DBG_warning, "sane_start: No document on ADF\n");
+ return (SANE_STATUS_NO_DOCS);
+ }
+ else if ((s->data.adf_status & 0x02) == 0x02)
+ {
+ DBG (DBG_warning, "sane_start: ADF cover open!\n");
+ return (SANE_STATUS_COVER_OPEN);
+ }
+ }
+
+
+ status = trigger_scan (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "start of scan failed: %s\n", sane_strstatus (status));
+ print_window_data (&wbuf);
+ /* this line introduced not to freeze xscanimage */
+ /*do_cancel (s); */
+ return status;
+ }
+ /* Wait for scanner to become ready to transmit data */
+ status = hs2p_wait_ready (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "GET DATA STATUS failed: %s\n",
+ sane_strstatus (status));
+ return (status);
+ }
+
+ s->another_side = (mode == DUPLEX) ? SANE_TRUE : SANE_FALSE;
+ /* Number of bytes to read for one side of sheet */
+ DBG (DBG_info, "ANOTHER SIDE = %s\n", (s->another_side) ? "TRUE" : "FALSE");
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ DBG (DBG_info, "%d pixels per line, %d bytes, %d lines high, dpi=%d\n",
+ s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (int) s->val[OPT_Y_RESOLUTION].w);
+ s->scanning = SANE_TRUE;
+ s->cancelled = SANE_FALSE;
+
+ DBG (DBG_proc, "<< sane_start\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ HS2P_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread, bytes_requested, i, start;
+ SANE_Byte color;
+ DBG (DBG_proc, ">> sane_read\n");
+
+ *len = 0;
+
+ DBG (DBG_info, "sane_read: bytes left to read: %ld\n",
+ (u_long) s->bytes_to_read);
+
+ if (s->bytes_to_read == 0)
+ { /* We've reached the end of one side of sheet */
+ if (!s->another_side)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+ else
+ {
+ /* let frontend call sane_start again to reset bytes_to_read */
+ DBG (DBG_proc, "<< sane_read: getting another side\n");
+ return (SANE_STATUS_EOF);
+ }
+ }
+
+ if (s->cancelled)
+ {
+ DBG (DBG_info, "sane_read: cancelled!\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ if (!s->scanning)
+ {
+ DBG (DBG_info, "sane_read: scanning is false!\n");
+ return (do_cancel (s));
+ }
+
+ nread = max_len;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ bytes_requested = nread;
+ start = 0;
+
+pad:
+ if (s->EOM)
+ {
+ if (s->val[OPT_PADDING].w)
+ {
+ DBG (DBG_info, "sane_read s->EOM padding from %ld to %ld\n",
+ (u_long) start, (u_long) bytes_requested);
+ color = (s->val[OPT_NEGATIVE].w) ? 0 : 255;
+ /* pad to requested length */
+ for (i = start; i < bytes_requested; i++)
+ buf[i] = color;
+ nread = bytes_requested; /* we've padded to bytes_requested */
+ *len = nread;
+ s->bytes_to_read -= nread;
+ }
+ else /* TRUNCATE: should never reach here */
+ {
+ *len = nread;
+ s->bytes_to_read = 0; /* EOM */
+ }
+ }
+ else
+ {
+ DBG (DBG_info, "sane_read: trying to read %ld bytes\n", (u_long) nread);
+ status = read_data (s->fd, buf, &nread, DATA_TYPE_IMAGE, DTQ);
+ switch (status)
+ {
+ case SANE_STATUS_NO_DOCS:
+ DBG (DBG_error, "sane_read: End-Of-Medium detected\n");
+ s->EOM = SANE_TRUE;
+ /*
+ * If status != SANE_STATUS_GOOD, then sense_handler() has already
+ * been called and the sanei_* functions have already gotten the
+ * sense data buffer (which apparently clears the error conditionn)
+ * so the following doesn't work:
+ get_sense_data (s->fd, &(s->hw->sense_data));
+ print_sense_data (&(s->hw->sense_data));
+ */
+ start = (isset_ILI (s->hw->sense_data)) ? /* Invalid Length Indicator */
+ bytes_requested - _4btol (s->hw->sense_data.information) : nread;
+ goto pad;
+ break;
+ case SANE_STATUS_GOOD:
+ *len = nread;
+ s->bytes_to_read -= nread;
+ break;
+ default:
+ DBG (DBG_error, "sane_read: read error\n");
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ }
+ DBG (DBG_proc, "<< sane_read\n");
+ return (SANE_STATUS_GOOD);
+}
+
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ HS2P_Scanner *s = handle;
+ DBG (DBG_proc, ">> sane_cancel\n");
+
+ if (s->scanning)
+ { /* if batchmode is enabled, then call set_window to abort the batch
+ if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE) {
+ DBG(5, "sane_cancel: calling set_window to abort batch\n");
+ set_window(s, BH_BATCH_ABORT);
+ } */
+ do_cancel (s);
+ }
+
+
+
+ DBG (DBG_proc, "<< sane_cancel\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (DBG_proc, ">> sane_set_io_mode (handle = %p, non_blocking = %d)\n",
+ handle, non_blocking);
+ DBG (DBG_proc, "<< sane_set_io_mode\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+#ifdef NONBLOCKSUPPORTED
+ HS2P_Scanner *s = handle;
+#endif
+ DBG (DBG_proc, ">> sane_get_select_fd (handle = %p, fd = %p)\n", handle,
+ (void *) fd);
+
+#ifdef NONBLOCKSUPPORTED
+ if (s->fd < 0)
+ {
+ DBG (DBG_proc, "<< sane_get_select_fd\n");
+ return SANE_STATUS_INVAL;
+ }
+ *fd = s->fd;
+ return SANE_STATUS_GOOD;
+#else
+ handle = handle;
+ fd = fd; /* get rid of compiler warning */
+ DBG (DBG_proc, "<< sane_get_select_fd\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif
+}