diff options
Diffstat (limited to 'backend/hs2p.c')
-rw-r--r-- | backend/hs2p.c | 3332 |
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 +} |