summaryrefslogtreecommitdiff
path: root/backend/umax.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/umax.c
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/umax.c')
-rw-r--r--backend/umax.c8131
1 files changed, 8131 insertions, 0 deletions
diff --git a/backend/umax.c b/backend/umax.c
new file mode 100644
index 0000000..b2ceb00
--- /dev/null
+++ b/backend/umax.c
@@ -0,0 +1,8131 @@
+/* --------------------------------------------------------------------------------------------------------- */
+
+/* sane - Scanner Access Now Easy.
+
+ umax.c
+
+ (C) 1997-2007 Oliver Rauch
+
+ 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.
+
+ This file implements a SANE backend for UMAX flatbed scanners. */
+
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+#define BUILD 45
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+
+/* SANE-FLOW-DIAGRAMM
+
+ - sane_init() : initialize backend, attach scanners(devicename,0)
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev)
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option information
+ . . - 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)
+in ADF mode this is done often:
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+
+ . . - sane_cancel() : cancel operation, kill reader_process
+
+ . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle
+ - sane_exit() : terminate use of backend, free devicename and device-struture
+*/
+
+
+/* ------------------------------------------------------------ DBG OUTPUT LEVELS -------------------------- */
+
+
+#define DBG_error0 0
+#define DBG_error 1
+#define DBG_sense 2
+#define DBG_warning 3
+#define DBG_inquiry 4
+#define DBG_info 5
+#define DBG_info2 6
+#define DBG_proc 7
+#define DBG_read 8
+#define DBG_sane_init 10
+#define DBG_sane_proc 11
+#define DBG_sane_info 12
+#define DBG_sane_option 13
+
+
+/* ------------------------------------------------------------ SANE DEFINES ------------------------------- */
+
+#define BACKEND_NAME umax
+#define UMAX_CONFIG_FILE "umax.conf"
+
+/* ------------------------------------------------------------ SANE INTERNATIONALISATION ------------------ */
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/* ------------------------------------------------------------ INCLUDES ----------------------------------- */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_thread.h"
+
+#ifdef UMAX_ENABLE_USB
+# include "sane/sanei_usb.h"
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "umax-scsidef.h"
+#include "umax-scanner.c"
+
+#ifdef UMAX_ENABLE_USB
+# include "umax-usb.c"
+#endif
+
+#include "umax.h"
+
+/* ------------------------------------------------------------ SANE DEFINES ------------------------------- */
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------ OPTION DEFINITIONS ------------------------- */
+
+#define SANE_NAME_BATCH_SCAN_START "batch-scan-start"
+#define SANE_TITLE_BATCH_SCAN_START "Batch scan start"
+#define SANE_DESC_BATCH_SCAN_START "set for first scan of batch"
+
+#define SANE_NAME_BATCH_SCAN_LOOP "batch-scan-loop"
+#define SANE_TITLE_BATCH_SCAN_LOOP "Batch scan loop"
+#define SANE_DESC_BATCH_SCAN_LOOP "set for middle scans of batch"
+
+#define SANE_NAME_BATCH_SCAN_END "batch-scan-end"
+#define SANE_TITLE_BATCH_SCAN_END "Batch scan end"
+#define SANE_DESC_BATCH_SCAN_END "set for last scan of batch"
+
+#define SANE_NAME_BATCH_NEXT_TL_Y "batch-scan-next-tl-y"
+#define SANE_TITLE_BATCH_NEXT_TL_Y "Batch scan next top left Y"
+#define SANE_DESC_BATCH_NEXT_TL_Y "Set top left Y position for next scan"
+
+#define SANE_UMAX_NAME_SELECT_CALIBRATON_EXPOSURE_TIME "select-calibration-exposure-time"
+#define SANE_UMAX_TITLE_SELECT_CALIBRATION_EXPOSURE_TIME "Set calibration exposure time"
+#define SANE_UMAX_DESC_SELECT_CALIBRATION_EXPOSURE_TIME "Allow different settings for calibration and scan exposure times"
+
+/* ------------------------------------------------------------ STRING DEFINITIONS ------------------------- */
+
+#define FLB_STR SANE_I18N("Flatbed")
+#define UTA_STR SANE_I18N("Transparency Adapter")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+#define LINEART_STR SANE_VALUE_SCAN_MODE_LINEART
+#define HALFTONE_STR SANE_VALUE_SCAN_MODE_HALFTONE
+#define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY
+#define COLOR_LINEART_STR SANE_VALUE_SCAN_MODE_COLOR_LINEART
+#define COLOR_HALFTONE_STR SANE_VALUE_SCAN_MODE_COLOR_HALFTONE
+#define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR
+
+/* ------------------------------------------------------------ DEFINITIONS -------------------------------- */
+
+#define P_200_TO_255(per) ( (SANE_UNFIX(per) + 100) * 255/200 )
+#define P_100_TO_255(per) SANE_UNFIX(per * 255/100 )
+#define P_100_TO_254(per) 1+SANE_UNFIX(per * 254/100 )
+
+/* ------------------------------------------------------------ GLOBAL VARIABLES --------------------------- */
+
+
+static SANE_String scan_mode_list[7];
+static SANE_String_Const source_list[4];
+static SANE_Int bit_depth_list[9];
+static SANE_Auth_Callback frontend_authorize_callback;
+
+static int umax_scsi_buffer_size_min = 32768; /* default: minimum scsi buffer size: 32 KB */
+static int umax_scsi_buffer_size_max = 131072; /* default: maximum scsi buffer size: 128 KB */
+
+/* number of lines that shall be scanned in one buffer for preview if possible */
+/* this value should not be too large because it defines the step size in which */
+/* the scanned parts are displayed while preview scan is in progress */
+static int umax_preview_lines = 10; /* default: 10 preview lines */
+/* number of lines that shall be scanned in one buffer for scan if possible */
+static int umax_scan_lines = 40; /* default: 40 scan lines */
+static int umax_scsi_maxqueue = 2; /* use command queueing depth 2 as default */
+static int umax_handle_bad_sense_error = 0; /* default: handle bad sense error code as device busy */
+static int umax_execute_request_sense = 0; /* default: do not use request sense in do_calibration */
+static int umax_force_preview_bit_rgb = 0; /* default: do not force preview bit in real color scan */
+static int umax_slow = -1; /* don`t use slow scanning speed */
+static int umax_smear = -1; /* don`t care about image smearing problem */
+static int umax_calibration_area = -1; /* -1=auto, 0=calibration on image, 1=calibration for full ccd */
+static int umax_calibration_width_offset = -99999; /* -99999=auto */
+static int umax_calibration_width_offset_batch = -99999; /* -99999=auto */
+static int umax_calibration_bytespp = -1; /* -1=auto */
+static int umax_exposure_time_rgb_bind = -1; /* -1=auto */
+static int umax_invert_shading_data = -1; /* -1=auto */
+static int umax_lamp_control_available = 0; /* 0=disabled */
+static int umax_gamma_lsb_padded = -1; /* -1=auto */
+static int umax_connection_type = 1; /* 1=scsi, 2=usb */
+
+/* ------------------------------------------------------------ CALIBRATION MODE --------------------------- */
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+
+#define CALIB_MODE_0000 SANE_I18N("Use Image Composition")
+#define CALIB_MODE_1111 SANE_I18N("Bi-level black and white (lineart mode)")
+#define CALIB_MODE_1110 SANE_I18N("Dithered/halftone black & white (halftone mode)")
+#define CALIB_MODE_1101 SANE_I18N("Multi-level black & white (grayscale mode)")
+#define CALIB_MODE_1010 SANE_I18N("Multi-level RGB color (one pass color)")
+#define CALIB_MODE_1001 SANE_I18N("Ignore calibration")
+
+static SANE_String_Const calibration_list[] =
+{
+ CALIB_MODE_0000,
+ CALIB_MODE_1111,
+ CALIB_MODE_1110,
+ CALIB_MODE_1101,
+ CALIB_MODE_1010,
+ CALIB_MODE_1001,
+ 0
+};
+
+#endif
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+enum
+{
+ UMAX_CALIBRATION_AREA_IMAGE = 0,
+ UMAX_CALIBRATION_AREA_CCD
+};
+
+static const SANE_Int pattern_dim_list[] =
+{
+ 5, /* # of elements */
+ 2, 4, 6, 8, 12
+};
+
+static const SANE_Range u8_range =
+{
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+
+static const SANE_Range percentage_range =
+{
+ -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 1 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+static const SANE_Range percentage_range_100 =
+{
+ 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */
+ 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */
+ 0 << SANE_FIXED_SCALE_SHIFT /* quantization */
+};
+
+static int num_devices = 0;
+static const SANE_Device **devlist = NULL;
+static Umax_Device *first_dev = NULL;
+static Umax_Scanner *first_handle = NULL;
+
+
+/* ------------------------------------------------------------ MATH-HELPERS ------------------------------- */
+
+
+#define min(a, b) (((a)<(b))?(a):(b))
+#define max(a, b) (((a)>(b))?(a):(b))
+#define inrange(minimum, val, maximum) (min(maximum, max(val, minimum)))
+
+
+/* ------------------------------------------------------------ umax_test_little_endian -------------------- */
+
+static SANE_Bool umax_test_little_endian(void)
+{
+ SANE_Int testvalue = 255;
+ unsigned char *firstbyte = (unsigned char *) &testvalue;
+
+ if (*firstbyte == 255)
+ {
+ return SANE_TRUE;
+ }
+
+ return SANE_FALSE;
+}
+
+/* ------------------------------------------------------------ DBG_inq_nz --------------------------------- */
+
+
+static void DBG_inq_nz(char *text, int flag)
+{
+ if (flag != 0) { DBG(DBG_inquiry,"%s", text); }
+}
+
+
+/*------------------------------------------------------------- DBG_sense_nz ------------------------------- */
+
+
+static void DBG_sense_nz(char *text, int flag)
+{
+ if (flag != 0) { DBG(DBG_sense,"%s", text); }
+}
+
+
+/* ------------------------------------------------------------ UMAX PRINT INQUIRY ------------------------- */
+
+
+static void umax_print_inquiry(Umax_Device *dev)
+{
+ unsigned char *inquiry_block;
+ int i;
+
+ inquiry_block=dev->buffer[0];
+
+ DBG(DBG_inquiry,"INQUIRY:\n");
+ DBG(DBG_inquiry,"========\n");
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"vendor........................: '%s'\n", dev->vendor);
+ DBG(DBG_inquiry,"product.......................: '%s'\n", dev->product);
+ DBG(DBG_inquiry,"version.......................: '%s'\n", dev->version);
+
+ DBG(DBG_inquiry,"peripheral qualifier..........: %d\n", get_inquiry_periph_qual(inquiry_block));
+ DBG(DBG_inquiry,"peripheral device type........: %d\n", get_inquiry_periph_devtype(inquiry_block));
+
+ DBG_inq_nz("RMB bit set (reserved)\n", get_inquiry_rmb(inquiry_block));
+ DBG_inq_nz("0x01 bit 6\n", get_inquiry_0x01_bit6(inquiry_block));
+ DBG_inq_nz("0x01 bit 5\n", get_inquiry_0x01_bit5(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"CBHS value range..............: %s\n", cbhs_str[dev->inquiry_cbhs]);
+ DBG(DBG_inquiry,"scanmode......................: %s\n", scanmode_str[get_inquiry_scanmode(inquiry_block)]);
+ if (dev->inquiry_transavail)
+ {
+ DBG(DBG_inquiry,"UTA (transparency)............: available\n");
+
+ if (get_inquiry_translamp(inquiry_block) == 0)
+ { DBG(DBG_inquiry,"UTA lamp status ..............: off\n"); }
+ else
+ { DBG(DBG_inquiry,"UTA lamp status ..............: on\n"); }
+
+ DBG(DBG_inquiry,"\n");
+ }
+
+ DBG(DBG_inquiry,"inquiry block length..........: %d bytes\n", dev->inquiry_len);
+ if (dev->inquiry_len<=0x8e)
+ {
+ DBG(DBG_inquiry, "Inquiry block is unexpected short, should be at least 147 bytes\n");
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"ISO Version (reserved).......: %d\n", get_inquiry_iso_version(inquiry_block));
+ DBG(DBG_inquiry,"ECMA Version (reserved).......: %d\n", get_inquiry_ecma_version(inquiry_block));
+ DBG(DBG_inquiry,"ANSI Version .................: %d\n", get_inquiry_ansi_version(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ DBG_inq_nz("AENC bit set (reserved)\n", get_inquiry_aenc(inquiry_block));
+ DBG_inq_nz("TmIOP bit set (reserved)\n", get_inquiry_tmiop(inquiry_block));
+ DBG_inq_nz("0x03 bit 5\n", get_inquiry_0x03_bit5(inquiry_block));
+ DBG_inq_nz("0x03 bit 4\n", get_inquiry_0x03_bit4(inquiry_block));
+
+ DBG(DBG_inquiry,"reserved byte 0x05 = %d\n", get_inquiry_0x05(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x06 = %d\n", get_inquiry_0x06(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"scsi features (%02x):\n", get_inquiry_scsi_byte(inquiry_block));
+ DBG(DBG_inquiry,"-------------------\n");
+ DBG_inq_nz(" - relative address\n", get_inquiry_scsi_reladr(inquiry_block));
+ DBG_inq_nz(" - wide bus 32 bit\n", get_inquiry_scsi_wbus32(inquiry_block));
+ DBG_inq_nz(" - wide bus 16 bit\n", get_inquiry_scsi_wbus16(inquiry_block));
+ DBG_inq_nz(" - syncronous neg.\n", get_inquiry_scsi_sync(inquiry_block));
+ DBG_inq_nz(" - linked commands\n", get_inquiry_scsi_linked(inquiry_block));
+ DBG_inq_nz(" - (reserved)\n", get_inquiry_scsi_R(inquiry_block));
+ DBG_inq_nz(" - command queueing\n", get_inquiry_scsi_cmdqueue(inquiry_block));
+ DBG_inq_nz(" - sftre\n", get_inquiry_scsi_sftre(inquiry_block));
+
+ /* 0x24 */
+ if (dev->inquiry_len<=0x24)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"f/w support function:\n");
+ DBG(DBG_inquiry,"---------------------\n");
+ DBG_inq_nz(" - quality calibration\n", dev->inquiry_quality_ctrl);
+ DBG_inq_nz(" - fast preview function\n", dev->inquiry_preview);
+ DBG_inq_nz(" - shadow compensation by f/w\n", get_inquiry_fw_shadow_comp(inquiry_block));
+ DBG_inq_nz(" - reselection phase\n", get_inquiry_fw_reselection(inquiry_block));
+ DBG_inq_nz(" - lamp intensity control\n", dev->inquiry_lamp_ctrl);
+ DBG_inq_nz(" - batch scan function\n", get_inquiry_fw_batch_scan(inquiry_block));
+ DBG_inq_nz(" - calibration mode control by driver\n", get_inquiry_fw_calibration(inquiry_block));
+
+ /* 0x36, 0x37 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x36 = %d\n", get_inquiry_0x36(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x37 = %d\n", get_inquiry_0x37(inquiry_block));
+
+ if (get_inquiry_fw_adjust_exposure_tf(inquiry_block))
+ {
+ int unit;
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"adjust exposure time function\n");
+ unit=get_inquiry_exposure_time_step_unit(inquiry_block);
+ DBG(DBG_inquiry,"exposure time step units......: %d micro-sec\n", unit);
+ DBG(DBG_inquiry,"exposure time maximum.........: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_max(inquiry_block));
+ DBG(DBG_inquiry,"exposure time minimum (LHG)...: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_lhg_min(inquiry_block));
+ DBG(DBG_inquiry,"exposure time minimum color...: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_color_min(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB (LH).: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_lh_def_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA (LH): %d micro-sec\n",
+ unit*get_inquiry_exposure_time_lh_def_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB gray.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_gray_def_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA gray: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_gray_def_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB red..: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_r_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB grn..: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_g_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default FB blue.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_b_fb(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA red.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_r_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA grn.: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_g_uta(inquiry_block));
+ DBG(DBG_inquiry,"exposure time default UTA blue: %d micro-sec\n",
+ unit*get_inquiry_exposure_time_def_b_uta(inquiry_block));
+ }
+
+
+ /* 0x60 */
+ if (dev->inquiry_len<=0x60)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"scan modes (%02x):\n", get_inquiry_sc_feature_byte0(inquiry_block));
+ DBG(DBG_inquiry,"----------------\n");
+ DBG_inq_nz(" - three passes color mode\n", get_inquiry_sc_three_pass_color(inquiry_block));
+ DBG_inq_nz(" - single pass color mode\n", get_inquiry_sc_one_pass_color(inquiry_block));
+ DBG_inq_nz(" - lineart mode\n", dev->inquiry_lineart);
+ DBG_inq_nz(" - halftone mode\n", dev->inquiry_halftone);
+ DBG_inq_nz(" - gray mode\n", dev->inquiry_gray);
+ DBG_inq_nz(" - color mode\n", dev->inquiry_color);
+ DBG_inq_nz(" - transparency (UTA)\n", dev->inquiry_uta);
+ DBG_inq_nz(" - automatic document feeder (ADF)\n", dev->inquiry_adf);
+
+ /* 0x61 */
+ if (dev->inquiry_len<=0x61)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"scanner capability (%02x, %02x, %02x):\n",
+ get_inquiry_sc_feature_byte1(inquiry_block),
+ get_inquiry_sc_feature_byte2(inquiry_block),
+ get_inquiry_sc_feature_byte3(inquiry_block));
+ DBG(DBG_inquiry,"--------------------------------\n");
+ DBG_inq_nz(" - double resolution\n", dev->inquiry_dor);
+ DBG_inq_nz(" - send high byte first\n", get_inquiry_sc_high_byte_first(inquiry_block));
+ DBG_inq_nz(" - bi-level image reverse\n", dev->inquiry_reverse);
+ DBG_inq_nz(" - multi-level image reverse\n", dev->inquiry_reverse_multi);
+ DBG_inq_nz(" - support shadow function\n", dev->inquiry_shadow);
+ DBG_inq_nz(" - support highlight function\n", dev->inquiry_highlight);
+ DBG_inq_nz(" - f/w downloadable\n", get_inquiry_sc_downloadable_fw(inquiry_block));
+ DBG_inq_nz(" - paper length can reach to 14 inch\n", get_inquiry_sc_paper_length_14(inquiry_block));
+
+ /* 0x62 */
+ if (dev->inquiry_len<=0x62)
+ {
+ return;
+ }
+
+ DBG_inq_nz(" - shading data/gain uploadable\n", get_inquiry_sc_uploadable_shade(inquiry_block));
+ DBG_inq_nz(" - analog gamma correction\n", dev->inquiry_analog_gamma);
+ DBG_inq_nz(" - x, y coordinate base\n", get_inquiry_xy_coordinate_base(inquiry_block));
+ DBG_inq_nz(" - lineart starts with LSB\n", get_inquiry_lineart_order(inquiry_block));
+ DBG_inq_nz(" - start density \n", get_inquiry_start_density(inquiry_block));
+ DBG_inq_nz(" - hardware x scaling\n", get_inquiry_hw_x_scaling(inquiry_block));
+ DBG_inq_nz(" - hardware y scaling\n", get_inquiry_hw_y_scaling(inquiry_block));
+
+ /* 0x63 */
+ if (dev->inquiry_len<=0x63)
+ {
+ return;
+ }
+
+ DBG_inq_nz(" + ADF: no paper\n", get_inquiry_ADF_no_paper(inquiry_block));
+ DBG_inq_nz(" + ADF: cover open\n", get_inquiry_ADF_cover_open(inquiry_block));
+ DBG_inq_nz(" + ADF: paper jam\n", get_inquiry_ADF_paper_jam(inquiry_block));
+ DBG_inq_nz(" - unknwon flag; 0x63 bit 3\n", get_inquiry_0x63_bit3(inquiry_block));
+ DBG_inq_nz(" - unknown lfag: 0x63 bit 4\n", get_inquiry_0x63_bit4(inquiry_block));
+ DBG_inq_nz(" - lens calib in doc pos\n", get_inquiry_lens_cal_in_doc_pos(inquiry_block));
+ DBG_inq_nz(" - manual focus\n", get_inquiry_manual_focus(inquiry_block));
+ DBG_inq_nz(" - UTA lens calib pos selectable\n", get_inquiry_sel_uta_lens_cal_pos(inquiry_block));
+
+ /* 0x64 - 0x68*/
+ if (dev->inquiry_len<=0x68)
+ {
+ return;
+ }
+
+ if (dev->inquiry_gamma_dwload)
+ {
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"gamma download available\n");
+ DBG_inq_nz("gamma download type 2\n", get_inquiry_gamma_type_2(inquiry_block));
+ DBG(DBG_inquiry,"lines of gamma curve: %s\n", gamma_lines_str[get_inquiry_gamma_lines(inquiry_block)]);
+
+ /* 0x66 */
+ DBG_inq_nz("gamma input 8 bits/pixel support\n", get_inquiry_gib_8bpp(inquiry_block));
+ DBG_inq_nz("gamma input 9 bits/pixel support\n", get_inquiry_gib_9bpp(inquiry_block));
+ DBG_inq_nz("gamma input 10 bits/pixel support\n", get_inquiry_gib_10bpp(inquiry_block));
+ DBG_inq_nz("gamma input 12 bits/pixel support\n", get_inquiry_gib_12bpp(inquiry_block));
+ DBG_inq_nz("gamma input 14 bits/pixel support\n", get_inquiry_gib_14bpp(inquiry_block));
+ DBG_inq_nz("gamma input 16 bits/pixel support\n", get_inquiry_gib_16bpp(inquiry_block));
+ DBG_inq_nz("0x66 bit 6\n", get_inquiry_0x66_bit6(inquiry_block));
+ DBG_inq_nz("0x66 bit 7\n", get_inquiry_0x66_bit7(inquiry_block));
+
+ /* 0x68 */
+ DBG_inq_nz("gamma output 8 bits/pixel support\n", get_inquiry_gob_8bpp(inquiry_block));
+ DBG_inq_nz("gamma output 9 bits/pixel support\n", get_inquiry_gob_9bpp(inquiry_block));
+ DBG_inq_nz("gamma output 10 bits/pixel support\n", get_inquiry_gob_10bpp(inquiry_block));
+ DBG_inq_nz("gamma output 12 bits/pixel support\n", get_inquiry_gob_12bpp(inquiry_block));
+ DBG_inq_nz("gamma output 14 bits/pixel support\n", get_inquiry_gob_14bpp(inquiry_block));
+ DBG_inq_nz("gamma output 16 bits/pixel support\n", get_inquiry_gob_16bpp(inquiry_block));
+ DBG_inq_nz("0x68 bit 6\n", get_inquiry_0x68_bit6(inquiry_block));
+ DBG_inq_nz("0x68 bit 7\n", get_inquiry_0x68_bit7(inquiry_block));
+ }
+
+ /* 0x64 - 0x68 reserved bits */
+ DBG_inq_nz("0x64 bit 2\n", get_inquiry_0x64_bit2(inquiry_block));
+ DBG_inq_nz("0x64 bit 3\n", get_inquiry_0x64_bit3(inquiry_block));
+ DBG_inq_nz("0x64 bit 4\n", get_inquiry_0x64_bit4(inquiry_block));
+ DBG_inq_nz("0x64 bit 6\n", get_inquiry_0x64_bit6(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x65 = %d\n", get_inquiry_0x65(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x67 = %d\n", get_inquiry_0x67(inquiry_block));
+
+
+ /* 0x69 */
+ if (dev->inquiry_len<=0x69)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ if (get_inquiry_hda(inquiry_block))
+ {
+ DBG(DBG_inquiry,"halftone download available\n");
+ DBG(DBG_inquiry,"halftone pattern download max matrix %dx%d\n",
+ get_inquiry_max_halftone_matrix(inquiry_block),
+ get_inquiry_max_halftone_matrix(inquiry_block));
+ }
+
+ /* 0x6a */
+ if (dev->inquiry_len<=0x6a)
+ {
+ return;
+ }
+
+ DBG_inq_nz("built-in halftone patterns:\n", get_inquiry_halftones_supported(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 2x2\n", get_inquiry_halftones_2x2(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 4x4\n", get_inquiry_halftones_4x4(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 6x6\n", get_inquiry_halftones_6x6(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 8x8\n", get_inquiry_halftones_8x8(inquiry_block));
+ DBG_inq_nz("built-in halftone pattern size ............: 12x12\n", get_inquiry_halftones_12x12(inquiry_block));
+
+ /* 0x6b, 0x6c */
+ DBG(DBG_inquiry,"reserved byte 0x6b = %d\n", get_inquiry_0x6b(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x6c = %d\n", get_inquiry_0x6c(inquiry_block));
+
+
+ /* 0x6d */
+ if (dev->inquiry_len<=0x6d)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"color sequence............................: %s\n",
+ color_sequence_str[get_inquiry_colorseq(inquiry_block)]);
+ DBG_inq_nz("color ordering support....................: pixel\n",
+ get_inquiry_color_order_pixel(inquiry_block));
+ DBG_inq_nz("color ordering support....................: line without CCD distance\n",
+ get_inquiry_color_order_line_no_ccd(inquiry_block));
+ DBG_inq_nz("color ordering support....................: plane\n",
+ get_inquiry_color_order_plane(inquiry_block));
+ DBG_inq_nz("color ordering support....................: line with CCD distance\n",
+ get_inquiry_color_order_line_w_ccd(inquiry_block));
+ DBG_inq_nz("color ordering support....................: (reserved)\n",
+ get_inquiry_color_order_reserved(inquiry_block));
+
+ /* 0x6e */
+ if (dev->inquiry_len<=0x71)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"maximum video memory......................: %d KB\n", dev->inquiry_vidmem/1024);
+
+ /* 0x72 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x72 = %d\n", get_inquiry_0x72(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* 0x73/0x94 - 0x75/0x96 */
+ if (dev->inquiry_len<=0x75)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"optical resolution........................: %d dpi\n", dev->inquiry_optical_res);
+ DBG(DBG_inquiry,"maximum x-resolution......................: %d dpi\n", dev->inquiry_x_res);
+ DBG(DBG_inquiry,"maximum y-resolution......................: %d dpi\n", dev->inquiry_y_res);
+
+ /* ---------- */
+
+ /* 0x76 0x77 */
+ if (dev->inquiry_len<=0x77)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"FB (flatbed-mode):\n");
+ DBG(DBG_inquiry,"FB maximum scan width.....................: %2.2f inch\n", dev->inquiry_fb_width);
+ DBG(DBG_inquiry,"FB maximum scan length....................: %2.2f inch\n", dev->inquiry_fb_length);
+
+ /* ---------- */
+
+ /* 0x7a - 0x81 */
+ if (dev->inquiry_len<=0x81)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"UTA (transparency-mode):\n");
+ DBG(DBG_inquiry,"UTA x-original point......................: %2.2f inch\n", dev->inquiry_uta_x_off);
+ DBG(DBG_inquiry,"UTA y-original point......................: %2.2f inch\n", dev->inquiry_uta_y_off);
+ DBG(DBG_inquiry,"UTA maximum scan width....................: %2.2f inch\n", dev->inquiry_uta_width);
+ DBG(DBG_inquiry,"UTA maximum scan length...................: %2.2f inch\n", dev->inquiry_uta_length);
+
+ /* ---------- */
+
+ /* 0x82-0x85 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x82 = %d\n", get_inquiry_0x82(inquiry_block));
+
+ /* ---------- */
+
+ /* 0x83/0xa0 - 0x85/0xa2 */
+ if (dev->inquiry_len<=0x85)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"DOR (double optical resolution-mode):\n");
+ DBG(DBG_inquiry,"DOR optical resolution....................: %d dpi\n", dev->inquiry_dor_optical_res);
+ DBG(DBG_inquiry,"DOR maximum x-resolution..................: %d dpi\n", dev->inquiry_dor_x_res);
+ DBG(DBG_inquiry,"DOR maximum y-resolution..................: %d dpi\n", dev->inquiry_dor_y_res);
+
+ /* 0x86 - 0x8d */
+ if (dev->inquiry_len<=0x8d)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"DOR x-original point......................: %2.2f inch\n", dev->inquiry_dor_x_off);
+ DBG(DBG_inquiry,"DOR y-original point......................: %2.2f inch\n", dev->inquiry_dor_y_off);
+ DBG(DBG_inquiry,"DOR maximum scan width....................: %2.2f inch\n", dev->inquiry_dor_width);
+ DBG(DBG_inquiry,"DOR maximum scan length...................: %2.2f inch\n", dev->inquiry_dor_length);
+ DBG(DBG_inquiry,"\n");
+
+ /* ---------- */
+
+ /* 0x8e */
+ DBG(DBG_inquiry,"reserved byte 0x8e = %d\n", get_inquiry_0x8e(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* ---------- */
+
+ /* 0x8f */
+ if (dev->inquiry_len<=0x8f)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"last calibration lamp density.............: %d\n",
+ get_inquiry_last_calibration_lamp_density(inquiry_block));
+
+ /* 0x90 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x90 = %d\n", get_inquiry_0x90(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* 0x91 */
+ if (dev->inquiry_len<=0x91)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"lamp warmup maximum time..................: %d sec\n", dev->inquiry_max_warmup_time);
+
+ /* 0x92 0x93 */
+ if (dev->inquiry_len<=0x93)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"window descriptor block length............: %d bytes\n", get_inquiry_wdb_length(inquiry_block));
+
+ /* ----------------- */
+
+ /* 0x97 */
+ if (dev->inquiry_len<=0x97)
+ {
+ return;
+ }
+
+ if (get_inquiry_analog_gamma_table(inquiry_block) == 0)
+ {
+ DBG(DBG_inquiry,"no analog gamma function\n");
+ }
+ else
+ {
+ DBG(DBG_inquiry,"mp 8832 analog gamma table\n");
+ }
+
+ /* 0x98, 0x99 */
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x98 = %d\n", get_inquiry_0x98(inquiry_block));
+ DBG(DBG_inquiry,"reserved byte 0x99 = %d\n", get_inquiry_0x99(inquiry_block));
+ DBG(DBG_inquiry,"\n");
+
+ /* 0x9a */
+ if (dev->inquiry_len<=0x9a)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"maximum calibration data lines for shading: %d\n",
+ get_inquiry_max_calibration_data_lines(inquiry_block));
+
+ /* 0x9b */
+ if (dev->inquiry_len<=0x9b)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"fb/uta: color line arrangement mode.......: %d\n",
+ get_inquiry_fb_uta_line_arrangement_mode(inquiry_block));
+
+ /* 0x9c */
+ if (dev->inquiry_len<=0x9c)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"adf: color line arrangement mode.......: %d\n",
+ get_inquiry_adf_line_arrangement_mode(inquiry_block));
+
+ /* 0x9d */
+ if (dev->inquiry_len<=0x9d)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"CCD line distance.........................: %d\n",
+ get_inquiry_CCD_line_distance(inquiry_block));
+
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"reserved byte 0x9e = %d\n", get_inquiry_0x9e(inquiry_block));
+
+ /* 0xa2 following */
+ if (dev->inquiry_len<=0xa2)
+ {
+ return;
+ }
+
+ DBG(DBG_inquiry,"\n");
+ for(i=0xa3; i<dev->inquiry_len; i++)
+ {
+ DBG(DBG_inquiry,"unknown reserved byte 0x%x = %d\n", i, inquiry_block[i]);
+ }
+}
+
+
+/* ------------------------------------------------------------ CBHS_CORRECT ------------------------------- */
+
+
+static int umax_cbhs_correct(int minimum, int cbhs, int maximum)
+{
+ int range = maximum - minimum + 1;
+
+ if (range == 256)
+ {
+ return cbhs;
+ }
+
+ return (int)( (cbhs/256.0)*range + minimum );
+}
+
+
+/* ------------------------------------------------------------ SENSE_HANDLER ------------------------------ */
+
+
+static SANE_Status sense_handler(int scsi_fd, unsigned char *result, void *arg) /* is called by sanei_scsi */
+{
+ unsigned char asc, ascq, sensekey;
+ int asc_ascq, len;
+ Umax_Device *dev = arg;
+
+ DBG(DBG_proc, "check condition sense handler (scsi_fd = %d)\n", scsi_fd);
+
+ sensekey = get_RS_sense_key(result);
+ asc = get_RS_ASC(result);
+ ascq = get_RS_ASCQ(result);
+ asc_ascq = (int)(256 * asc + ascq);
+ len = 7 + get_RS_additional_length(result);
+
+ if ( get_RS_error_code(result) != 0x70 )
+ {
+ DBG(DBG_error, "invalid sense key error code (%d)\n", get_RS_error_code(result));
+
+ switch (dev->handle_bad_sense_error)
+ {
+ default:
+ case 0:
+ DBG(DBG_error, "=> handled as DEVICE BUSY!\n");
+ return SANE_STATUS_DEVICE_BUSY;
+
+ case 1:
+ DBG(DBG_error, "=> handled as ok!\n");
+ return SANE_STATUS_GOOD;
+
+ case 2:
+ DBG(DBG_error, "=> handled as i/o error!\n");
+ return SANE_STATUS_IO_ERROR;
+
+ case 3:
+ DBG(DBG_error, "=> ignored, sense handler does continue\n");
+ }
+ }
+
+ DBG(DBG_sense, "check condition sense: %s\n", sense_str[sensekey]);
+
+ /* when we reach here then we have no valid data in buffer[0] */
+ /* but it may be helpful to have the result data in buffer[0] */
+ memset(dev->buffer[0], 0, rs_return_block_size); /* clear sense data buffer */
+ memcpy(dev->buffer[0], result, len+1); /* copy sense data to buffer */
+
+ if (len > 0x15)
+ {
+ int scanner_error = get_RS_scanner_error_code(result);
+
+ if (scanner_error < 100)
+ {
+ DBG(DBG_sense, "-> %s (#%d)\n", scanner_error_str[scanner_error], scanner_error);
+ }
+ else
+ {
+ DBG(DBG_sense, "-> error %d\n", scanner_error);
+ }
+ }
+
+ if (get_RS_ILI(result) != 0)
+ {
+ DBG(DBG_sense, "-> ILI-ERROR: requested data length is larger than actual length\n");
+ }
+
+ switch(sensekey)
+ {
+ case 0x00: /* no sense */
+ return SANE_STATUS_GOOD;
+ break;
+
+
+ case 0x03: /* medium error */
+ if (asc_ascq == 0x1400)
+ {
+ DBG(DBG_sense, "-> misfeed, paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ else if (asc_ascq == 0x1401)
+ {
+ DBG(DBG_sense, "-> adf not ready\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ else
+ {
+ DBG(DBG_sense, "-> unknown medium error: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ break;
+
+
+ case 0x04: /* hardware error */
+ if (asc_ascq == 0x4000)
+ {
+ DBG(DBG_sense, "-> diagnostic error:\n");
+ if (len >= 0x13)
+ {
+ DBG_sense_nz(" dim light\n", get_RS_asb_dim_light(result));
+ DBG_sense_nz(" no light\n", get_RS_asb_no_light(result));
+ DBG_sense_nz(" sensor or motor error\n", get_RS_asb_sensor_motor(result));
+ DBG_sense_nz(" too light\n", get_RS_asb_too_light(result));
+ DBG_sense_nz(" calibration error\n", get_RS_asb_calibration(result));
+ DBG_sense_nz(" rom error\n", get_RS_asb_rom(result));
+ DBG_sense_nz(" ram error\n", get_RS_asb_ram(result));
+ DBG_sense_nz(" cpu error\n", get_RS_asb_cpu(result));
+ DBG_sense_nz(" scsi error\n", get_RS_asb_scsi(result));
+ DBG_sense_nz(" timer error\n", get_RS_asb_timer(result));
+ DBG_sense_nz(" filter motor error\n", get_RS_asb_filter_motor(result));
+ DBG_sense_nz(" dc adjust error\n", get_RS_asb_dc_adjust(result));
+ DBG_sense_nz(" uta home sensor or motor error\n", get_RS_asb_uta_sensor(result));
+ }
+ }
+ else
+ {
+ DBG(DBG_sense, "-> unknown hardware error: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+
+ case 0x05: /* illegal request */
+ if (asc_ascq == 0x2000)
+ {
+ DBG(DBG_sense, "-> invalid command operation code\n");
+ }
+ else if (asc_ascq == 0x2400)
+ {
+ DBG(DBG_sense, "-> illegal field in CDB\n");
+ }
+ else if (asc_ascq == 0x2500)
+ {
+ DBG(DBG_sense, "-> logical unit not supported\n");
+ }
+ else if (asc_ascq == 0x2600)
+ {
+ DBG(DBG_sense, "-> invalid field in parameter list\n");
+ }
+ else if (asc_ascq == 0x2c01)
+ {
+ DBG(DBG_sense, "-> too many windows specified\n");
+ }
+ else if (asc_ascq == 0x2c02)
+ {
+ DBG(DBG_sense, "-> invalid combination of windows specified\n");
+ }
+ else
+ {
+ DBG(DBG_sense, "-> illegal request: asc=%d, ascq=%d\n", asc, ascq);
+ }
+
+ if (len >= 0x11)
+ {
+ if (get_RS_SKSV(result) != 0)
+ {
+ if (get_RS_CD(result) == 0)
+ {
+ DBG(DBG_sense, "-> illegal parameter in CDB\n");
+ }
+ else
+ {
+ DBG(DBG_sense, "-> illegal parameter is in the data parameters sent during data out phase\n");
+ }
+
+ DBG(DBG_sense, "-> error detected in byte %d\n", get_RS_field_pointer(result));
+ }
+ }
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+
+ case 0x06: /* unit attention */
+ if (asc_ascq == 0x2900)
+ {
+ DBG(DBG_sense, "-> power on, reset or bus device reset\n");
+ }
+ else if (asc_ascq == 0x3f01)
+ {
+ DBG(DBG_sense, "-> microcode has been changed\n");
+ }
+ else
+ {
+ DBG(DBG_sense, "-> unit attention: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ break;
+
+
+ case 0x09: /* vendor specific */
+
+ if (asc == 0x00)
+ {
+ DBG(DBG_sense, "-> button protocoll\n");
+ if (ascq & 1)
+ {
+ dev->button0_pressed = 1;
+ DBG(DBG_sense, "-> button 0 pressed\n");
+ }
+
+ if (ascq & 2)
+ {
+ dev->button1_pressed = 1;
+ DBG(DBG_sense, "-> button 1 pressed\n");
+ }
+
+ if (ascq & 4)
+ {
+ dev->button2_pressed = 1;
+ DBG(DBG_sense, "-> button 2 pressed\n");
+ }
+ return SANE_STATUS_GOOD;
+ }
+ else if (asc_ascq == 0x8001)
+ {
+ DBG(DBG_sense, "-> lamp warmup\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ else if (asc_ascq == 0x8002)
+ {
+ DBG(DBG_sense, "-> calibration by driver\n");
+ if (dev)
+ {
+ dev->do_calibration = 1; /* set flag for calibration by driver */
+ }
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG(DBG_sense, "-> vendor specific sense-code: asc=%d, ascq=%d\n", asc, ascq);
+ }
+ break;
+
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ UMAX SET RGB BIND -------------------------- */
+
+static void umax_set_rgb_bind(Umax_Scanner *scanner)
+{
+ if ( (scanner->val[OPT_RGB_BIND].w == SANE_FALSE) &&
+ (strcmp(scanner->val[OPT_MODE].s, COLOR_STR) == 0) ) /* enable rgb options */
+ {
+ if (scanner->device->inquiry_analog_gamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else /* only show gray options */
+ {
+ if (scanner->device->inquiry_analog_gamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+
+ if ( (scanner->device->inquiry_exposure_adj) && (scanner->val[OPT_SELECT_EXPOSURE_TIME].w) )
+ {
+ if ( (scanner->val[OPT_RGB_BIND].w == SANE_FALSE) &&
+ (!scanner->device->exposure_time_rgb_bind) &&
+ (strcmp(scanner->val[OPT_MODE].s, COLOR_STR) == 0) ) /* enable rgb exposure time options */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* exposure time setting for calibration enabled */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* no separate settings for calibration exposure times */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* enable gray exposure time options */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* exposure time setting for calibration enabled */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* no separate settings for calibration exposure times */
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ }
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+}
+
+/* ------------------------------------------------------------ UMAX CALCULATE PIXELS ---------------------- */
+
+static int umax_calculate_pixels(int scansize_pixel, int resolution, int resolution_base, int coordinate_base)
+/* scansize_pixel = size in pixels at 1200 dpi */
+/* resolution = scan resolution */
+/* resolution_base = this is the optical resolution * 1 or * 2 */
+/* coordinate_base = this is 1200 dpi */
+{
+ unsigned int intsize_inch, intsize_pixel, diffsize_pixel, missing_pixels, del_pixel_1, pix;
+ int toomuch;
+
+ intsize_inch = scansize_pixel / coordinate_base; /* full inches */
+ intsize_pixel = intsize_inch * resolution; /* pixels in full inches */
+
+ diffsize_pixel = scansize_pixel % coordinate_base; /* missing pixels in last inch at 1200 dpi */
+ missing_pixels = diffsize_pixel * resolution_base / coordinate_base; /* missing pixels at resolution_base dpi */
+ del_pixel_1 = resolution_base - resolution; /* pixels to erase in one inch */
+ toomuch = 0; /* number of pixels that must be deleted in last inch */
+
+ if (del_pixel_1 != 0) /* search the number of pixels that must deleted */
+ {
+ pix = 0;
+ while (pix <= missing_pixels)
+ {
+ toomuch++;
+ pix = toomuch * resolution_base/del_pixel_1;
+ }
+
+ if (pix > missing_pixels)
+ {
+ toomuch--;
+ }
+ }
+
+ return (intsize_pixel + missing_pixels - toomuch);
+}
+
+/* ------------------------------------------------------------ UMAX FORGET LINE --------------------------- */
+
+
+static int umax_forget_line(Umax_Device *dev, int color)
+/* tests if line related to optical resolution has to be skipped for selected resolution */
+/* returns 0 if line is ok, -1 if line has to be skipped */
+{
+ unsigned int opt_res = dev->relevant_optical_res * dev->scale_y;
+ unsigned int forget;
+
+ dev->pixelline_opt_res++; /* increment number of lines in optical res */
+
+ if (opt_res != dev->y_resolution) /* are there any lines to skip ? */
+ {
+
+ forget = (dev->pixelline_del[color] * opt_res)/(opt_res - dev->y_resolution);
+
+ if (dev->pixelline_optic[color]++ == forget)
+ {
+ dev->pixelline_del[color]++; /* inc pointer to next line to skip */
+ return(-1); /* skip line */
+ }
+ }
+
+ return(0); /* ok, order this line */
+}
+
+
+/* ------------------------------------------------------------ UMAX ORDER LINE TO PIXEL ------------------- */
+
+
+static void umax_order_line_to_pixel(Umax_Device *dev, unsigned char *source, int color)
+/* reads a one-color line and writes it into a pixel-ordered-buffer if line */
+/* is not skipped */
+/* color = 0:red, 1:green, 2:blue */
+{
+ unsigned int i;
+ unsigned int line = dev->pixelline_next[color]; /* bufferlinenumber */
+ unsigned char *dest = dev->pixelbuffer;
+
+ if (dest != NULL)
+ {
+ if (dev->bits_per_pixel_code == 1) /* 24 bpp */
+ {
+ dest += line * dev->width_in_pixels * 3 + color;
+
+ for (i=0; i<dev->width_in_pixels; i++) /* cp each pixel into pixelbuffer */
+ {
+ *dest++ = *source++;
+ dest++;
+ dest++;
+ }
+ }
+ else /* > 24 bpp */
+ {
+ dest += line * dev->width_in_pixels * 6 + color * 2;
+
+ for(i=0; i<dev->width_in_pixels; i++) /* cp each pixel into pixelbuffer */
+ {
+ *dest++ = *source++; /* byte order correct ? , don't know ! */
+ *dest++ = *source++;
+
+ dest++; dest++;
+ dest++; dest++;
+ }
+ }
+
+ line++;
+ if (line >= dev->pixelline_max)
+ {
+ line = 0;
+ }
+
+ dev->pixelline_next[color] = line; /* next line of this color */
+ dev->pixelline_ready[color]++; /* number of ready lines for color */
+
+ DBG(DBG_read, "merged line as color %d to line %d\n", color, dev->pixelline_ready[color]);
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX ORDER LINE ---------------------------- */
+
+
+static void umax_order_line(Umax_Device *dev, unsigned char *source)
+{
+ unsigned int CCD_distance = dev->CCD_distance * dev->scale_y;
+ unsigned int length = (dev->scanlength * dev->scale_y * dev->relevant_optical_res) / dev->y_coordinate_base;
+ unsigned int color;
+
+ do /* search next valid line */
+ {
+ if (dev->pixelline_opt_res < CCD_distance)
+ {
+ color = dev->CCD_color[0]; /* color 0 */
+ }
+ else if (dev->pixelline_opt_res < CCD_distance * 3)
+ {
+ color = dev->CCD_color[1 + ((dev->pixelline_opt_res - CCD_distance) % 2)]; /* color 1,2 */
+ }
+ else if (dev->pixelline_opt_res < length * 3 - CCD_distance * 3)
+ {
+ color = dev->CCD_color[3 + (dev->pixelline_opt_res % 3)]; /* color 3,4,5 */
+ }
+ else if (dev->pixelline_opt_res < length * 3 - CCD_distance)
+ {
+ color = dev->CCD_color[6 + ((dev->pixelline_opt_res - length*3 + CCD_distance*3) % 2)]; /* color 6,7 */
+ }
+ else
+ {
+ color = dev->CCD_color[8]; /* color 8 */
+ }
+ } while(umax_forget_line(dev, color) != 0); /* until found correct line */
+
+ umax_order_line_to_pixel(dev, source, color);
+}
+
+
+/* ------------------------------------------------------------ UMAX GET PIXEL LINE ------------------------ */
+
+
+static unsigned char * umax_get_pixel_line(Umax_Device *dev)
+{
+ unsigned char *source = NULL;
+
+ if (dev->pixelbuffer != NULL)
+ {
+ if ( (dev->pixelline_ready[0] > dev->pixelline_written) &&
+ (dev->pixelline_ready[1] > dev->pixelline_written) &&
+ (dev->pixelline_ready[2] > dev->pixelline_written) )
+ {
+ source = dev->pixelbuffer + dev->pixelline_read * dev->width_in_pixels * 3;
+
+ dev->pixelline_written++;
+ dev->pixelline_read++;
+
+ if (dev->pixelline_read >= dev->pixelline_max)
+ {
+ dev->pixelline_read = 0;
+ }
+ }
+ }
+
+ return source;
+}
+
+
+/* ============================================================ Switches between the SCSI and USB commands = */
+
+/* ------------------------------------------------------------ UMAX SCSI CMD ------------------------------ */
+
+static SANE_Status umax_scsi_cmd(Umax_Device *dev, const void *src, size_t src_size, void *dst, size_t * dst_size)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_cmd(dev->sfd, src, src_size, dst, dst_size);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_cmd(dev->sfd, src, src_size, dst, dst_size);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI OPEN EXTENDED -------------------- */
+
+static SANE_Status umax_scsi_open_extended(const char *devicename, Umax_Device *dev,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg, int *buffersize)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_open_extended(devicename, &dev->sfd, handler, handler_arg, buffersize);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_open_extended(devicename, &dev->sfd, handler, handler_arg, buffersize);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI OPEN ----------------------------- */
+
+static SANE_Status umax_scsi_open(const char *devicename, Umax_Device *dev,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_open(devicename, &dev->sfd, handler, handler_arg);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_open(devicename, &dev->sfd, handler, handler_arg);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI CLOSE ---------------------------- */
+
+static void umax_scsi_close(Umax_Device *dev)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ sanei_scsi_close(dev->sfd);
+ dev->sfd=-1;
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ sanei_umaxusb_close(dev->sfd);
+ dev->sfd=-1;
+ break;
+#endif
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI REQ ENTER ------------------------ */
+
+static SANE_Status umax_scsi_req_enter(Umax_Device *dev, const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_req_enter (dev->sfd, src, src_size, dst, dst_size, idp);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_req_enter (dev->sfd, src, src_size, dst, dst_size, idp);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI REQ WAIT ------------------------- */
+
+static SANE_Status umax_scsi_req_wait(Umax_Device *dev, void *id)
+{
+ switch (dev->connection_type)
+ {
+ case SANE_UMAX_SCSI:
+ return sanei_scsi_req_wait(id);
+ break;
+
+#ifdef UMAX_ENABLE_USB
+ case SANE_UMAX_USB:
+ return sanei_umaxusb_req_wait(id);
+ break;
+#endif
+
+ default:
+ return(SANE_STATUS_INVAL);
+ }
+}
+
+/* ------------------------------------------------------------ UMAX SCSI GET LAMP STATUS ------------------ */
+
+
+static SANE_Status umax_scsi_get_lamp_status(Umax_Device *dev, int *lamp_on)
+{
+ SANE_Status status;
+ size_t size = 1;
+
+ DBG(DBG_proc, "umax_scsi_get_lamp_status\n");
+
+ status = umax_scsi_cmd(dev, get_lamp_status.cmd, get_lamp_status.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_scsi_get_lamp_status: command returned status %s\n", sane_strstatus(status));
+ return status;
+ }
+
+ *lamp_on = get_lamp_status_lamp_on(dev->buffer[0]);
+
+ DBG(DBG_info, "lamp_status = %d\n", *lamp_on);
+
+ return status;
+}
+
+/* ------------------------------------------------------------ UMAX SCSI SET LAMP STATUS ------------------ */
+
+
+static SANE_Status umax_scsi_set_lamp_status(Umax_Device *dev, int lamp_on)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_scsi_set_lamp_status\n");
+ DBG(DBG_info, "lamp_status=%d\n", lamp_on);
+
+ set_lamp_status_lamp_on(set_lamp_status.cmd, lamp_on);
+ status = umax_scsi_cmd(dev, set_lamp_status.cmd, set_lamp_status.size, NULL, NULL);
+
+ if (status)
+ {
+ DBG(DBG_error, "umax_scsi_set_lamp_status: command returned status %s\n", sane_strstatus(status));
+ }
+
+ return status;
+}
+
+/* ------------------------------------------------------------ UMAX SET LAMP STATUS ----------------------- */
+
+static SANE_Status umax_set_lamp_status(SANE_Handle handle, int lamp_on)
+{
+ Umax_Scanner *scanner = handle;
+ int lamp_status;
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_set_lamp_status\n");
+
+ if (umax_scsi_open(scanner->device->sane.name, scanner->device, sense_handler,
+ scanner->device) != SANE_STATUS_GOOD )
+ {
+ DBG(DBG_error, "ERROR: umax_set_lamp_status: open of %s failed:\n", scanner->device->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = umax_scsi_get_lamp_status(scanner->device, &lamp_status);
+
+ if (!status)
+ {
+ status = umax_scsi_set_lamp_status(scanner->device, lamp_on);
+ }
+
+ umax_scsi_close(scanner->device);
+
+ return status;
+}
+
+/* ------------------------------------------------------------ UMAX GET DATA BUFFER STATUS ---------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED /* NOT USED */
+static SANE_Status umax_get_data_buffer_status(Umax_Device *dev)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "get_data_buffer_status\n");
+ set_GDBS_wait(get_data_buffer_status.cmd,1); /* wait for scanned data */
+ status = umax_scsi_cmd(dev, get_data_buffer_status.cmd, get_data_buffer_status.size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_get_data_buffer_status: command returned status %s\n", sane_strstatus(status));
+ }
+
+ return status;
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX DO REQUEST SENSE ---------------------- */
+
+
+static void umax_do_request_sense(Umax_Device *dev)
+{
+ size_t size = rs_return_block_size;
+ SANE_Status status;
+
+ DBG(DBG_proc, "do_request_sense\n");
+ set_RS_allocation_length(request_sense.cmd, rs_return_block_size);
+ status = umax_scsi_cmd(dev, request_sense.cmd, request_sense.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_request_sense: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX WAIT SCANNER -------------------------- */
+
+
+static SANE_Status umax_wait_scanner(Umax_Device *dev)
+{
+ SANE_Status status;
+ int cnt = 0;
+
+ DBG(DBG_proc, "wait_scanner\n");
+
+ do
+ {
+ if (cnt > 100) /* maximal 100 * 0.5 sec = 50 sec */
+ {
+ DBG(DBG_warning, "scanner does not get ready\n");
+ return -1;
+ }
+ /* test unit ready */
+ status = umax_scsi_cmd(dev, test_unit_ready.cmd, test_unit_ready.size, NULL, NULL);
+ cnt++;
+
+ if (status)
+ {
+ if (cnt == 1)
+ {
+ DBG(DBG_info2, "scanner reports %s, waiting ...\n", sane_strstatus(status));
+ }
+
+ usleep(500000); /* wait 0.5 seconds */
+ }
+ } while (status != SANE_STATUS_GOOD );
+
+ DBG(DBG_info, "scanner ready\n");
+
+ return status;
+}
+
+#define WAIT_SCANNER { int status = umax_wait_scanner(dev); if (status) return status; }
+
+
+/* ------------------------------------------------------------ UMAX GRAB SCANNER -------------------------- */
+
+
+static int umax_grab_scanner(Umax_Device *dev)
+{
+ int status;
+
+ DBG(DBG_proc, "grab_scanner\n");
+
+ WAIT_SCANNER; /* wait for scanner ready */
+ status = umax_scsi_cmd(dev, reserve_unit.cmd, reserve_unit.size, NULL, NULL);
+
+ if (status)
+ {
+ DBG(DBG_error, "umax_grab_scanner: command returned status %s\n", sane_strstatus(status));
+ }
+ else
+ {
+ DBG(DBG_info, "scanner reserved\n");
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX REPOSITION SCANNER -------------------- */
+
+
+static int umax_reposition_scanner(Umax_Device *dev)
+{
+ int status;
+ int pause;
+
+ pause = dev->pause_after_reposition + dev->pause_for_moving * (dev->upper_left_y + dev->scanlength) /
+ ( (dev->inquiry_fb_length * dev->y_coordinate_base) );
+
+ DBG(DBG_info2, "trying to reposition scanner ...\n");
+ status = umax_scsi_cmd(dev, object_position.cmd, object_position.size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_reposition_scanner: command returned status %s\n", sane_strstatus(status));
+ return status;
+ }
+
+ if (pause > 0) /* predefined time to wait (Astra 2400S) */
+ {
+ DBG(DBG_info2, "pause for repositioning %d msec ...\n", pause);
+ usleep(((long) pause) * 1000);
+ DBG(DBG_info, "repositioning pause done\n");
+ }
+ else if (pause == 0) /* use TEST UNIT READY */
+ {
+ WAIT_SCANNER;
+ DBG(DBG_info, "scanner repositioned\n");
+ }
+ else /* pause < 0 : return without any pause */
+ {
+ DBG(DBG_info, "not waiting for finishing reposition scanner\n");
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ UMAX GIVE SCANNER -------------------------- */
+
+
+static int umax_give_scanner(Umax_Device *dev)
+{
+ int status;
+
+ DBG(DBG_info2, "trying to release scanner ...\n");
+ status = umax_scsi_cmd(dev, release_unit.cmd, release_unit.size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_give_scanner: command returned status %s\n", sane_strstatus(status));
+ }
+ else
+ {
+ DBG(DBG_info, "scanner released\n");
+ }
+
+ if (!dev->batch_scan || dev->batch_end)
+ {
+ umax_reposition_scanner(dev);
+ }
+ else
+ {
+ usleep(200000); /* 200 ms pause to make sure program does not exit before scanner is ready */
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND GAMMA DATA ----------------------- */
+
+
+static void umax_send_gamma_data(Umax_Device *dev, void *gamma_data, int color)
+{
+ unsigned char *data = gamma_data;
+ unsigned char *dest;
+ int length;
+ SANE_Status status;
+
+ DBG(DBG_proc, "send_gamma_data\n");
+
+ if (dev->inquiry_gamma_dwload == 0)
+ {
+ DBG(DBG_error, "ERROR: gamma download not available\n");
+ return;
+ }
+
+ memcpy(dev->buffer[0], send.cmd, send.size); /* send */
+ set_S_datatype_code(dev->buffer[0], S_datatype_gamma); /* gamma curve */
+
+ dest = dev->buffer[0] + send.size;
+
+ if (dev->inquiry_gamma_DCF == 0) /* gamma format type 0 */
+ {
+ DBG(DBG_info, "using gamma download curve format type 0\n");
+
+ memcpy(dest, gamma_DCF0.cmd, gamma_DCF0.size);
+
+ if (color == 1) /* one color */
+ {
+ set_DCF0_gamma_lines(dest, DCF0_gamma_one_line);
+
+ set_DCF0_gamma_color(dest, 0, DCF0_gamma_color_gray); /* grayscale */
+ if ( (dev->colormode == RGB) && (dev->three_pass != 0) ) /* 3 pass color */
+ {
+ set_DCF0_gamma_color(dest, 0, dev->three_pass_color); /* set color */
+ }
+
+ dest = dest + 2;
+ memcpy(dest, data, 1024); /* copy data */
+
+ set_S_xfer_length(dev->buffer[0], 1026); /* set length */
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + 1026, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=0, one color): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ else /* three colors */
+ {
+ set_DCF0_gamma_lines(dest, DCF0_gamma_three_lines);
+
+ set_DCF0_gamma_color(dest, 0, DCF0_gamma_color_red); /* red */
+ set_DCF0_gamma_color(dest, 1, DCF0_gamma_color_green); /* green */
+ set_DCF0_gamma_color(dest, 2, DCF0_gamma_color_blue); /* blue */
+
+ dest = dest + 2;
+ memcpy(dest, data, 1024); /* copy red data */
+
+ dest = dest + 1025;
+ data = data + 1024;
+ memcpy(dest, data, 1024); /* copy green data */
+
+ dest = dest + 1025;
+ data = data + 1024;
+ memcpy(dest, data, 1024); /* copy blue data */
+
+ set_S_xfer_length(dev->buffer[0], 3076); /* set length */
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + 3076, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=0, RGB): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ }
+ else if (dev->inquiry_gamma_DCF == 1) /* gamma format type 1 */
+ {
+ DBG(DBG_info, "using gamma download curve format type 1\n");
+
+ memcpy(dest, gamma_DCF1.cmd, gamma_DCF1.size);
+
+ set_DCF1_gamma_color(dest, DCF1_gamma_color_gray); /* grayscale */
+ if ( (dev->colormode == RGB) && (dev->three_pass != 0) ) /* 3 pass color */
+ {
+ set_DCF1_gamma_color(dest, dev->three_pass_color); /* set color */
+ }
+
+ dest = dest + 2;
+ memcpy(dest, data, 256); /* copy data */
+
+ set_S_xfer_length(dev->buffer[0], 258); /* set length */
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + 258, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=1): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ else if (dev->inquiry_gamma_DCF == 2) /* gamma format type 2 */
+ {
+ DBG(DBG_info, "using gamma download curve format type 2\n");
+
+ memcpy(dest, gamma_DCF2.cmd, gamma_DCF2.size);
+
+ set_DCF2_gamma_color(dest, DCF2_gamma_color_gray); /* grayscale */
+ if ( (dev->colormode == RGB) && (dev->three_pass != 0) ) /* 3 pass color */
+ { set_DCF2_gamma_color(dest, dev->three_pass_color); } /* set color */
+
+ if (color == 1)
+ {
+ set_DCF2_gamma_lines(dest, DCF2_gamma_one_line);
+ }
+ else
+ {
+ set_DCF2_gamma_lines(dest, DCF2_gamma_three_lines);
+ }
+
+ set_DCF2_gamma_input_bits(dest, dev->gamma_input_bits_code);
+ set_DCF2_gamma_output_bits(dest, dev->bits_per_pixel_code);
+
+ dest = dev->buffer[0] + send.size + gamma_DCF2.size; /* write to dest */
+
+ if (dev->gamma_input_bits_code & 32)
+ {
+ length = 65536; /* 16 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 16)
+ {
+ length = 16384; /* 14 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 8)
+ {
+ length = 4096; /* 12 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 4)
+ {
+ length = 1024; /* 10 input bits */
+ }
+ else if (dev->gamma_input_bits_code & 2)
+ {
+ length = 512; /* 9 input bits */
+ }
+ else
+ {
+ length = 256; /* 8 input bits */
+ }
+
+ if (dev->bits_per_pixel_code != 1) /* more than 8 output bits per pixel */
+ {
+ length = length * 2; /* = 2 output bytes */
+ }
+
+ if (dev->bufsize >= color*length+gamma_DCF2.size)
+ {
+ set_S_xfer_length(dev->buffer[0], color*length+gamma_DCF2.size); /* set length */
+ memcpy(dest, data, color*length); /* copy data */
+
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size+gamma_DCF2.size + length * color, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_gamma_data(DCF=2): command returned status %s\n", sane_strstatus(status));
+ }
+ }
+ else
+ {
+ DBG(DBG_error, "ERROR: too small scsi buffer (%d bytes) to send gamma data\n", dev->bufsize);
+ }
+ }
+ else
+ {
+ DBG(DBG_error, "ERROR: unknown gamma download curve type for this scanner\n");
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND DATA ---------------------------- */
+
+
+static void umax_send_data(Umax_Device *dev, void *data, int size, int datatype)
+{
+ unsigned char *dest;
+ SANE_Status status;
+
+ memcpy(dev->buffer[0], send.cmd, send.size); /* send */
+ set_S_datatype_code(dev->buffer[0], datatype); /* set datatype */
+ set_S_xfer_length(dev->buffer[0], size); /* bytes */
+
+ dest=dev->buffer[0] + send.size;
+ memcpy(dest, data, size); /* copy data */
+
+ status = umax_scsi_cmd(dev, dev->buffer[0], send.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_send_data: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND HALFTONE PATTERN ----------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+static void umax_send_halftone_pattern(Umax_Device *dev, void *data, int size)
+{
+ DBG(DBG_proc,"send_halftone_pattern\n");
+ umax_send_data(dev, data, size*size, S_datatype_halftone);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX SEND SHADING DATA -------------------- */
+
+
+static void umax_send_shading_data(Umax_Device *dev, void *data, int size)
+{
+ DBG(DBG_proc,"send_shading_data\n");
+ umax_send_data(dev, data, size, S_datatype_shading);
+}
+
+
+/* ------------------------------------------------------------ UMAX SEND GAIN DATA ----------------------- */
+
+#ifndef UMAX_HIDE_UNUSED
+static void umax_send_gain_data(Umax_Device *dev, void *data, int size)
+{
+ DBG(DBG_proc,"send_gain_data\n");
+ umax_send_data(dev, data, size, S_datatype_gain);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX QUEUE READ IMAGE DATA REQ ------------- */
+
+static SANE_Status umax_queue_read_image_data_req(Umax_Device *dev, unsigned int length, int bufnr)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_queue_read_image_data_req for buffer[%d], length = %d\n", bufnr, length);
+
+ set_R_xfer_length(sread.cmd, length); /* set length */
+ set_R_datatype_code(sread.cmd, R_datatype_imagedata); /* set datatype */
+
+ dev->length_queued[bufnr] = length; /* set length request */
+ dev->length_read[bufnr] = length; /* set length request, can be changed asyncronous by umax_scsi_req_enter */
+
+ status = umax_scsi_req_enter(dev, sread.cmd, sread.size, dev->buffer[bufnr], &(dev->length_read[bufnr]), &(dev->queue_id[bufnr]));
+ if (status)
+ {
+ DBG(DBG_error, "umax_queue_read_image_data_req: command returned status %s\n", sane_strstatus(status));
+ return -1;
+ }
+ else
+ {
+ DBG(DBG_info2, "umax_queue_read_image_data_req: id for buffer[%d] is %p\n", bufnr, dev->queue_id[bufnr]);
+ }
+
+ return length;
+}
+
+/* ------------------------------------------------------------ UMAX WAIT QUEUED IMAGE DATA ---------------- */
+
+
+static int umax_wait_queued_image_data(Umax_Device *dev, int bufnr)
+{
+ SANE_Status status;
+
+ DBG(DBG_proc, "umax_wait_queued_image_data for buffer[%d] (id=%p)\n", bufnr, dev->queue_id[bufnr]);
+
+ status = umax_scsi_req_wait(dev, dev->queue_id[bufnr]);
+ if (status)
+ {
+ DBG(DBG_error, "umax_wait_queued_image_data: wait returned status %s\n", sane_strstatus(status));
+ return -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ UMAX READ DATA ----------------------------- */
+
+
+static int umax_read_data(Umax_Device *dev, size_t length, int datatype)
+{
+ SANE_Status status;
+
+ set_R_xfer_length(sread.cmd, length); /* set length */
+ set_R_datatype_code(sread.cmd, datatype); /* set datatype */
+
+ status = umax_scsi_cmd(dev, sread.cmd, sread.size, dev->buffer[0], &length);
+ if (status)
+ {
+ DBG(DBG_error, "umax_read_data: command returned status %s\n", sane_strstatus(status));
+ return -1;
+ }
+
+ return length;
+}
+
+
+/* ------------------------------------------------------------ UMAX READ SHADING DATA -------------------- */
+
+
+static int umax_read_shading_data(Umax_Device *dev, unsigned int length)
+{
+ DBG(DBG_proc,"read_shading_data\n");
+ return umax_read_data(dev, length, R_datatype_shading);
+}
+
+
+/* ------------------------------------------------------------ UMAX READ GAIN DATA ----------------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+static int umax_read_gain_data(Umax_Device *dev, unsigned int length)
+{
+ DBG(DBG_proc,"read_gain_data\n");
+ return umax_read_data(dev, length, R_datatype_gain);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX READ IMAGE DATA ---------------------- */
+
+
+#ifndef UMAX_HIDE_UNUSED
+static int umax_read_image_data(Umax_Device *dev, unsigned int length)
+{
+ DBG(DBG_proc,"read_image_data\n");
+ WAIT_SCANNER;
+ return umax_read_data(dev, length, R_datatype_imagedata);
+}
+#endif
+
+
+/* ------------------------------------------------------------ UMAX CORRECT LIGHT ------------------------- */
+
+
+static int umax_correct_light(int light, int analog_gamma_byte) /* correct highlight/shadow if analog gamma is set */
+{
+ double analog_gamma;
+ analog_gamma=analog_gamma_table[analog_gamma_byte];
+ return( (int) 255 * pow( ((double) light)/255.0 , (1.0/analog_gamma) )+.5 );
+}
+
+
+/* ------------------------------------------------------------ UMAX SET WINDOW PARAM ---------------------- */
+
+
+/* set_window_param sets all the window parameters. This means building a */
+/* fairly complicated SCSI command before sending it... */
+
+static SANE_Status umax_set_window_param(Umax_Device *dev)
+{
+ SANE_Status status;
+ int num_dblocks = 1; /* number of window descriptor blocks, usually 1 or 3 */
+ unsigned char buffer_r[max_WDB_size], buffer_g[max_WDB_size], buffer_b[max_WDB_size];
+
+ DBG(DBG_proc, "set_window_param\n");
+ memset(buffer_r, '\0', max_WDB_size); /* clear buffer */
+ set_WDB_length(dev->wdb_len); /* length of win descriptor block */
+ memcpy(buffer_r, window_descriptor_block.cmd, window_descriptor_block.size); /* copy preset data */
+
+ set_WD_wid(buffer_r, 0); /* window identifier */
+ set_WD_auto(buffer_r, dev->set_auto); /* 0 or 1: don't know what it is */
+
+ /* geometry */
+ set_WD_Xres(buffer_r, dev->x_resolution); /* x resolution in dpi */
+ set_WD_Yres(buffer_r, dev->y_resolution); /* y resolution in dpi */
+ set_WD_ULX(buffer_r, dev->upper_left_x); /* left_edge x */
+ set_WD_ULY(buffer_r, dev->upper_left_y); /* upper_edge y */
+ set_WD_width(buffer_r, dev->scanwidth); /* width */
+ set_WD_length(buffer_r, dev->scanlength); /* length */
+
+ /* BTC */
+ set_WD_brightness(buffer_r, dev->brightness); /* brightness, only halftone */
+ set_WD_threshold(buffer_r, dev->threshold); /* threshold, only lineart */
+ set_WD_contrast(buffer_r, dev->contrast); /* contrast, only halftone */
+
+ /* scanmode, preset to LINEART */
+ set_WD_composition(buffer_r, WD_comp_lineart); /* image composition */
+ /* = (scan-mode) */
+ set_WD_bitsperpixel(buffer_r, WD_bits_1); /* bits/pixel (1,8,9,10,12,14,16) */
+ set_WD_halftone(buffer_r, dev->halftone); /* select halftone-pattern */
+ set_WD_RIF(buffer_r, dev->reverse); /* reverse, invert black and white */
+ set_WD_speed(buffer_r, dev->WD_speed); /* set speed */
+ set_WD_select_color(buffer_r, WD_color_gray); /* color for window-block */
+
+ /* set highlight and shadow in dependence of analog gamma */
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_r, dev->analog_gamma_r));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_r, dev->analog_gamma_r));
+
+ /* scan options */
+ set_WD_gamma(buffer_r, dev->digital_gamma_r); /* set digital gamma */
+ set_WD_module(buffer_r, dev->module); /* flatbed or transparency */
+ set_WD_CBHS(buffer_r, dev->cbhs_range); /* 50 or 255 */
+ set_WD_FF(buffer_r, dev->fix_focus_position); /* fix focus position */
+ set_WD_RMIF(buffer_r, dev->reverse_multi); /* reverse color-values */
+ set_WD_FDC(buffer_r, dev->lens_cal_in_doc_pos); /* lens calibration in document position */
+ set_WD_PF(buffer_r, dev->disable_pre_focus); /* disable pre focus */
+ set_WD_LCL(buffer_r, dev->holder_focus_pos_0mm); /* 0.6mm <-> 0.0mm holder focus position */
+ set_WD_HBT(buffer_r, dev->low_byte_first); /* set byte order for 16 bit scanners */
+ set_WD_DOR(buffer_r, dev->dor); /* double-resolution-mode */
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_r); /* scan exposure time */
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_r);/* calibration exposure time */
+
+ set_WD_batch(buffer_r, dev->batch_scan); /* batch or normal scan */
+ set_WD_MF(buffer_r, dev->manual_focus); /* automatic <-> manual focus */
+ set_WD_line_arrangement(buffer_r, WD_line_arrengement_by_fw); /* line arrangement by scanner */
+ set_WD_warmup(buffer_r, dev->warmup); /* warmup */
+
+ set_WD_calibration(buffer_r, dev->calibration); /* image calibration */
+
+ set_WD_color_sequence(buffer_r, WD_color_sequence_RGB); /* sequence RGB */
+ set_WD_color_ordering(buffer_r, WD_color_ordering_pixel); /* set to pixel for pbm, pgm, pnm-file */
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_r ); /* analog gamma */
+ set_WD_lamp_c_density(buffer_r, dev->c_density); /* calibrat. lamp density */
+ set_WD_lamp_s_density(buffer_r, dev->s_density); /* scan lamp density */
+ set_WD_next_upper_left(buffer_r, dev->batch_next_tl_y); /* batch scan next top left y position */
+ set_WD_pixel_count(buffer_r, dev->width_in_pixels); /* pixel count */
+ set_WD_line_count(buffer_r, dev->length_in_pixels); /* line count */
+ set_WD_x_coordinate_base(buffer_r, dev->x_coordinate_base); /* dpi (1200) */
+ set_WD_y_coordinate_base(buffer_r, dev->y_coordinate_base); /* dpi (1200) */
+ set_WD_calibration_data_lines(buffer_r, dev->calib_lines); /* required lines for calibration by driver */
+
+
+ switch(dev->colormode)
+ {
+ case LINEART: /* LINEART */
+ set_WD_composition(buffer_r, WD_comp_lineart);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+
+ set_WD_select_color(buffer_r, WD_color_gray);
+ break;
+
+ case HALFTONE: /* HALFTONE */
+ set_WD_composition(buffer_r, WD_comp_dithered);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+
+ set_WD_select_color(buffer_r, WD_color_gray);
+ break;
+
+ case GRAYSCALE: /* GRAYSCALE */
+ set_WD_composition(buffer_r, WD_comp_gray);
+ set_WD_bitsperpixel(buffer_r, dev->bits_per_pixel);
+
+ set_WD_select_color(buffer_r, WD_color_gray);
+ break;
+
+ case RGB_LINEART: /* COLOR MODES */
+ case RGB_HALFTONE:
+ case RGB:
+ if (dev->colormode == RGB_LINEART )
+ {
+ set_WD_composition(buffer_r, WD_comp_rgb_bilevel);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+ }
+ else if (dev->colormode == RGB_HALFTONE )
+ {
+ set_WD_composition(buffer_r, WD_comp_rgb_dithered);
+ set_WD_bitsperpixel(buffer_r, WD_bits_1);
+ }
+ else /* RGB */
+ {
+ set_WD_composition(buffer_r, WD_comp_rgb_full);
+ set_WD_bitsperpixel(buffer_r, dev->bits_per_pixel);
+ }
+
+ if (dev->three_pass == 0)
+ { /* singlepass */
+ num_dblocks = 3;
+
+ if (dev->do_color_ordering != 0)
+ {
+ set_WD_line_arrangement(buffer_r, WD_line_arrengement_by_driver);
+
+ if (dev->CCD_distance == 0)
+ {
+ set_WD_color_ordering(buffer_r, WD_color_ordering_line_no_ccd);
+ }
+ else
+ {
+ set_WD_color_ordering(buffer_r, WD_color_ordering_line_w_ccd);
+ }
+ }
+
+ memcpy(buffer_g, buffer_r, max_WDB_size); /* copy WDB for green */
+ memcpy(buffer_b, buffer_r, max_WDB_size); /* copy WDB for blue */
+
+ set_WD_wid(buffer_r, WD_wid_red); /* window identifier red */
+ set_WD_wid(buffer_g, WD_wid_green); /* window identifier green */
+ set_WD_wid(buffer_b, WD_wid_blue); /* window identifier blue */
+
+ set_WD_select_color(buffer_r, WD_color_red); /* select red for this window */
+ set_WD_select_color(buffer_g, WD_color_green); /* select green for this window */
+ set_WD_select_color(buffer_b, WD_color_blue); /* select blue for this window */
+
+ set_WD_gamma(buffer_r, dev->digital_gamma_r); /* digital gamma */
+ set_WD_gamma(buffer_g, dev->digital_gamma_g);
+ set_WD_gamma(buffer_b, dev->digital_gamma_b);
+
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_r); /* analog gamma */
+ set_WD_analog_gamma(buffer_g, dev->analog_gamma_g);
+ set_WD_analog_gamma(buffer_b, dev->analog_gamma_b);
+
+ /* set highlight in dependence of analog gamma */
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_r, dev->analog_gamma_r));
+ set_WD_highlight(buffer_g, umax_correct_light(dev->highlight_g, dev->analog_gamma_g));
+ set_WD_highlight(buffer_b, umax_correct_light(dev->highlight_b, dev->analog_gamma_b));
+
+ /* set shadow in dependence of analog gamma */
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_r, dev->analog_gamma_r));
+ set_WD_shadow(buffer_g, umax_correct_light(dev->shadow_g, dev->analog_gamma_g));
+ set_WD_shadow(buffer_b, umax_correct_light(dev->shadow_b, dev->analog_gamma_b));
+
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_r); /* set scan exposure times */
+ set_WD_scan_exposure_level(buffer_g, dev->exposure_time_scan_g);
+ set_WD_scan_exposure_level(buffer_b, dev->exposure_time_scan_b);
+
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_r);/* set calib exp times */
+ set_WD_calibration_exposure_level(buffer_g, dev->exposure_time_calibration_g);
+ set_WD_calibration_exposure_level(buffer_b, dev->exposure_time_calibration_b);
+ }
+ else
+ { /* threepass */
+ set_WD_wid(buffer_r, 0); /* window identifier */
+ set_WD_color_ordering(buffer_r, WD_color_ordering_plane); /* ???? */
+
+ if (dev->colormode == RGB_LINEART )
+ {
+ set_WD_composition(buffer_r, WD_comp_lineart); /* color-lineart-mode */
+ }
+ else if (dev->colormode == RGB_HALFTONE )
+ {
+ set_WD_composition(buffer_r, WD_comp_dithered); /* color-halftone-mode */
+ }
+ else /* RGB */
+ {
+ set_WD_composition(buffer_r, WD_comp_gray); /* color-mode */
+ }
+
+ switch (dev->three_pass_color)
+ {
+ case WD_wid_red:
+ set_WD_select_color(buffer_r, WD_color_red); /* color red */
+ set_WD_gamma(buffer_r, dev->digital_gamma_r);
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_r);
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_r, dev->analog_gamma_r));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_r, dev->analog_gamma_r));
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_r);
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_r);
+ break;
+
+ case WD_wid_green:
+ set_WD_select_color(buffer_r, WD_color_green); /* color green */
+ set_WD_gamma(buffer_r, dev->digital_gamma_g);
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_g);
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_g, dev->analog_gamma_g));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_g, dev->analog_gamma_g));
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_g);
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_g);
+ break;
+
+ case WD_wid_blue:
+ set_WD_select_color(buffer_r, WD_color_blue); /* color blue */
+ set_WD_gamma(buffer_r, dev->digital_gamma_b);
+ set_WD_analog_gamma(buffer_r, dev->analog_gamma_b);
+ set_WD_highlight(buffer_r, umax_correct_light(dev->highlight_b, dev->analog_gamma_b));
+ set_WD_shadow(buffer_r, umax_correct_light(dev->shadow_b, dev->analog_gamma_b));
+ set_WD_scan_exposure_level(buffer_r, dev->exposure_time_scan_b);
+ set_WD_calibration_exposure_level(buffer_r, dev->exposure_time_calibration_b);
+ break;
+
+ } /* switch dev->three_pass_color */
+
+ } /* if (single_pass) else (three_pass) */
+ break;
+ } /* switch dev->colormode, case RGB */
+
+ /* prepare SCSI-BUFFER */
+ memcpy(dev->buffer[0], set_window.cmd, set_window.size); /* SET-WINDOW cmd */
+ memcpy(WPDB_OFF(dev->buffer[0]), window_parameter_data_block.cmd, window_parameter_data_block.size); /* WPDB */
+ set_WPDB_wdbnum(WPDB_OFF(dev->buffer[0]), num_dblocks); /* set WD_len */
+ memcpy(WDB_OFF(dev->buffer[0],1), buffer_r, window_descriptor_block.size); /* add WD_block */
+
+ if ( num_dblocks == 3) /* if singelpass RGB */
+ {
+ memcpy(WDB_OFF(dev->buffer[0],2), buffer_g, window_descriptor_block.size); /* add green */
+ memcpy(WDB_OFF(dev->buffer[0],3), buffer_b, window_descriptor_block.size); /* add blue */
+ }
+
+
+ DBG(DBG_info2, "window descriptor block created with %d bytes\n", dev->wdb_len);
+
+ set_SW_xferlen(dev->buffer[0], (window_parameter_data_block.size + (window_descriptor_block.size * num_dblocks)));
+
+ status = umax_scsi_cmd(dev, dev->buffer[0], set_window.size + window_parameter_data_block.size +
+ (window_descriptor_block.size * num_dblocks), NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_set_window_param: command returned status %s\n", sane_strstatus(status));
+ }
+ else
+ {
+ DBG(DBG_info, "window(s) set\n");
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX DO INQUIRY ---------------------------- */
+
+
+static void umax_do_inquiry(Umax_Device *dev)
+{
+ size_t size;
+ SANE_Status status;
+
+ DBG(DBG_proc,"do_inquiry\n");
+ memset(dev->buffer[0], '\0', 256); /* clear buffer */
+
+ size = 5;
+
+ set_inquiry_return_size(inquiry.cmd, size); /* first get only 5 bytes to get size of inquiry_return_block */
+ status = umax_scsi_cmd(dev, inquiry.cmd, inquiry.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_inquiry: command returned status %s\n", sane_strstatus(status));
+ }
+
+ size = get_inquiry_additional_length(dev->buffer[0]) + 5;
+
+ set_inquiry_return_size(inquiry.cmd, size); /* then get inquiry with actual size */
+ status = umax_scsi_cmd(dev, inquiry.cmd, inquiry.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_inquiry: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX START SCAN ---------------------------- */
+
+
+static SANE_Status umax_start_scan(Umax_Device *dev)
+{
+ int size = 1;
+ SANE_Status status;
+
+ DBG(DBG_proc,"start_scan\n");
+
+ if (dev->adf) /* ADF selected: test for ADF errors */
+ {
+ umax_do_inquiry(dev); /* get inquiry */
+
+ if (get_inquiry_ADF_paper_jam(dev->buffer[0])) /* test for ADF paper jam */
+ {
+ DBG(DBG_error,"ERROR: umax_start_scan: ADF paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ else if (get_inquiry_ADF_cover_open(dev->buffer[0])) /* test for ADF cover open */
+ {
+ DBG(DBG_error,"ERROR: umax_start_scan: ADF cover open\n");
+ return SANE_STATUS_COVER_OPEN;
+ }
+ else if (get_inquiry_ADF_no_paper(dev->buffer[0])) /* test for ADF no paper */
+ {
+ DBG(DBG_error,"ERROR: umax_start_scan: ADF no paper\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ }
+
+ set_SC_quality(scan.cmd, dev->quality); /* 1=qual, 0=fast */
+ set_SC_adf( scan.cmd, dev->adf); /* ADF, 0=off, 1=use */
+ set_SC_preview(scan.cmd, dev->preview); /* 1=preview */
+
+ set_SC_wid(scan.cmd, 1, 0); /* Window-Identifier */
+
+ set_SC_xfer_length(scan.cmd, size); /* following Bytes */
+
+ DBG(DBG_info,"starting scan\n");
+
+ status = umax_scsi_cmd(dev, scan.cmd, scan.size + size, NULL, NULL);
+ if (status)
+ {
+ DBG(DBG_error, "umax_start_scan: command returned status %s\n", sane_strstatus(status));
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX DO CALIBRATION ------------------------ */
+
+
+static SANE_Status umax_do_calibration(Umax_Device *dev)
+{
+ SANE_Status status;
+ unsigned int width = 0;
+ unsigned int lines = 0;
+ unsigned int bytespp = 0;
+
+ DBG(DBG_proc,"do_calibration\n");
+
+ status = umax_wait_scanner(dev);
+
+ if ((status == SANE_STATUS_GOOD) && (dev->do_calibration != 0)) /* calibration by driver */
+ {
+ unsigned char *shading_data = 0;
+ unsigned int i, j;
+ long *average;
+
+
+ DBG(DBG_info,"driver is doing calibration\n");
+
+
+ if (umax_execute_request_sense)
+ {
+ DBG(DBG_info,"request sense call is enabled\n");
+ memset(dev->buffer[0], 0, rs_return_block_size); /* clear sense data buffer */
+ umax_do_request_sense(dev); /* new request-sense call to get all data */
+ }
+ else
+ {
+ DBG(DBG_info,"request sense call is disabled\n");
+ }
+
+ if (get_RS_SCC_condition_code(dev->buffer[0]) != 1)
+ {
+ DBG(DBG_warning,"WARNING: missing information about shading-data\n");
+ DBG(DBG_warning," driver tries to guess missing values!\n");
+
+ if ((dev->calibration_area != UMAX_CALIBRATION_AREA_CCD) && (!dev->batch_scan))
+ /* calibration is done with image geometry and depth */
+ {
+ DBG(DBG_warning," Calibration is done with selected image geometry and depth!\n");
+
+ width = dev->scanwidth * dev->relevant_optical_res / dev->x_coordinate_base;
+
+ if (dev->calibration_width_offset > -99999) /* driver or user (umax.conf) define an offset */
+ {
+ width = width + dev->calibration_width_offset;
+ DBG(DBG_warning," Using calibration width offset of %d\n", dev->calibration_width_offset);
+ }
+
+ if (dev->colormode == RGB)
+ {
+ width = width * 3;
+ }
+
+ lines = dev->calib_lines;
+
+ if (dev->gamma_input_bits_code <= 1)
+ {
+ bytespp = 1; /* 8 bit mode */
+ }
+ else
+ {
+ bytespp = 2; /* 16 bit mode */
+ }
+ }
+ else /* calibration is done with full scanarea and full depth */
+ {
+ DBG(DBG_warning," Calibration is done for each CCD pixel with full depth!\n");
+
+ width = (int)(dev->inquiry_fb_width * dev->inquiry_optical_res);
+
+ if (dev->batch_scan)
+ {
+ if (dev->calibration_width_offset_batch > -99999) /* driver or user (umax.conf) define an offset for batch scanning */
+ {
+ width = width + dev->calibration_width_offset_batch;
+ DBG(DBG_warning," Using calibration width offset for batch scanning of %d\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else /* normal scan */
+ {
+ if (dev->calibration_width_offset > -99999) /* driver or user (umax.conf) define an offset */
+ {
+ width = width + dev->calibration_width_offset;
+ DBG(DBG_warning," Using calibration width offset of %d\n", dev->calibration_width_offset);
+ }
+ }
+
+ if (dev->colormode == RGB)
+ {
+ width = width * 3;
+ }
+
+ lines = dev->calib_lines;
+
+ if (dev->gamma_input_bits_code <= 1)
+ {
+ bytespp = 1; /* 8 bit mode */
+ }
+ else
+ {
+ bytespp = 2; /* 16 bit mode */
+ }
+ }
+ }
+ else
+ {
+ lines = get_RS_SCC_calibration_lines(dev->buffer[0]);
+ bytespp = get_RS_SCC_calibration_bytespp(dev->buffer[0]);
+ width = get_RS_SCC_calibration_bytesperline(dev->buffer[0]) / bytespp;
+ }
+
+ if (dev->calibration_bytespp > 0) /* correct bytespp if necessary and driver knows about it or user did select it */
+ {
+ bytespp = dev->calibration_bytespp;
+ }
+
+ DBG(DBG_info,"scanner sends %d lines with %d pixels and %d bytes/pixel\n", lines, width, bytespp);
+
+ if (width * bytespp > dev->bufsize)
+ {
+ DBG(DBG_error,"ERROR: scsi buffer is to small for one shading line, calibration aborted\n");
+ DBG(DBG_error,"=> change umax.conf options scsi-buffer-size-min and scsi-buffer-size-max\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* UMAX S12 sends a kind of uncalibrated image data, bright -> 255, dark -> 0 */
+ /* (although 0 is not black) my scanner sends values around 220 */
+ /* for some scanners the data is simply sent back, other scanners want 255-value as awnswer */
+
+ average = calloc(width, sizeof(long));
+ if (average == 0)
+ {
+ DBG(DBG_error,"ERROR: could not allocate memory for averaging shading data: calibration aborted\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ shading_data = calloc(width, bytespp);
+ if (shading_data == 0)
+ {
+ DBG(DBG_error,"ERROR: could not allocate memory for shading data: calibration aborted\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (bytespp == 1) /* 1 byte per pixel */
+ {
+ DBG(DBG_info,"calculating average value for 8 bit shading data!\n");
+
+ for (i=0; i<lines; i++)
+ {
+ umax_read_shading_data(dev, width * bytespp);
+
+ for (j=0; j<width; j++)
+ {
+ average[j] += (long) dev->buffer[0][j];
+ }
+
+ DBG(DBG_read,"8 bit shading-line %d read\n", i+1);
+ }
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[j] = (unsigned char) (average[j] / lines);
+ }
+ }
+ else if (dev->low_byte_first) /* 2 bytes per pixel with low byte first */
+ {
+ DBG(DBG_info,"calculating average value for 16 bit shading data (low byte first)!\n");
+ for (i=0; i<lines; i++)
+ {
+ umax_read_shading_data(dev, width * bytespp);
+
+ for (j=0; j<width; j++)
+ {
+ average[j] += (long) 256 * dev->buffer[0][2*j+1] + dev->buffer[0][2*j] ;
+ }
+
+ DBG(DBG_read,"16 bit shading-line %d read\n", i+1);
+ }
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[2*j+1] = (unsigned char) (average[j] / (256 * lines));
+ shading_data[2*j] = (unsigned char) (average[j] / lines);
+ }
+ }
+ else /* 2 bytes per pixel with highbyte first */
+ {
+ DBG(DBG_info,"calculating average value for 16 bit shading data (high byte first)!\n");
+ for (i=0; i<lines; i++)
+ {
+ umax_read_shading_data(dev, width * bytespp);
+
+ for (j=0; j<width; j++)
+ {
+ average[j] += (long) 256 * dev->buffer[0][2*j] + dev->buffer[0][2*j + 1] ;
+ }
+
+ DBG(DBG_read,"16 bit shading-line %d read\n", i+1);
+ }
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[2*j] = (unsigned char) (average[j] / (256 * lines));
+ shading_data[2*j+1] = (unsigned char) (average[j] / lines);
+ }
+ }
+
+ free(average);
+
+ if ( (dev->invert_shading_data) ) /* invert data */
+ {
+ if (bytespp == 1)
+ {
+ DBG(DBG_info,"inverting 8 bit shading data\n");
+
+ for (j=0; j<width; j++)
+ {
+ shading_data[j] = 255 - shading_data[j];
+ }
+ }
+ else
+ {
+ unsigned int value;
+
+ DBG(DBG_info,"inverting 16 bit shading data\n");
+
+ for (j=0; j<width; j++)
+ {
+ value = shading_data[2*j] + shading_data[2*j+1] * 256;
+ value = 65535 - value;
+ shading_data[2*j] = (unsigned char) value/256;
+ shading_data[2*j+1] = (unsigned char) value & 255;
+ }
+ }
+ }
+
+ umax_send_shading_data(dev, shading_data, width * bytespp);
+ DBG(DBG_info,"shading-data sent\n");
+ free(shading_data);
+
+ status = umax_start_scan(dev); /* now start real scan */
+
+ dev->do_calibration = 0;
+ }
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ UMAX DO NEW INQUIRY ------------------------ */
+
+
+static void umax_do_new_inquiry(Umax_Device *dev, size_t size) /* call inquiry again if wrong length */
+{
+ SANE_Status status;
+
+ DBG(DBG_proc,"do_new_inquiry\n");
+ memset(dev->buffer[0], '\0', 256); /* clear buffer */
+
+ set_inquiry_return_size(inquiry.cmd, size);
+ status = umax_scsi_cmd(dev, inquiry.cmd, inquiry.size, dev->buffer[0], &size);
+ if (status)
+ {
+ DBG(DBG_error, "umax_do_new_inquiry: command returned status %s\n", sane_strstatus(status));
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX CORRECT INQUIRY ----------------------- */
+
+
+static void umax_correct_inquiry(Umax_Device *dev, char *vendor, char *product, char *version)
+{
+ DBG(DBG_info, "umax_correct_inquiry(\"%s %s %s\")\n", vendor, product, version);
+
+ if (!strncmp(vendor, "UMAX ", 5))
+ {
+ if (!strncmp(product, "Astra 600S ", 11))
+ {
+ int add_len = get_inquiry_additional_length(dev->buffer[0]);
+
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (add_len == 0x8f)
+ {
+ DBG(DBG_warning," - correcting wrong inquiry data\n");
+ umax_do_new_inquiry(dev, 0x9b); /* get inquiry with correct length */
+ set_inquiry_length(dev->buffer[0], 0x9e); /* correct inquiry len */
+ /* correct color-ordering from pixel to line_with_ccd_distance */
+ set_inquiry_color_order(dev->buffer[0], IN_color_ordering_line_w_ccd);
+ set_inquiry_fb_uta_line_arrangement_mode(dev->buffer[0], 32);
+ set_inquiry_CCD_line_distance(dev->buffer[0], 8);
+ /* we should reset ADF-bit here too */
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf */
+ {
+ DBG(DBG_warning," - activating inversion of shading data\n");
+ dev->invert_shading_data = 1;
+ }
+ }
+ }
+ else if (!strncmp(product, "Astra 610S ", 11))
+ {
+ int add_len = get_inquiry_additional_length(dev->buffer[0]);
+
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (add_len == 0x8f)
+ {
+ DBG(DBG_warning," - correcting wrong inquiry data\n");
+ umax_do_new_inquiry(dev, 0x9b); /* get inquiry with correct length */
+ set_inquiry_length(dev->buffer[0], 0x9e); /* correct inquiry len */
+ /* correct color-ordering from pixel to line_with_ccd_distance */
+ set_inquiry_color_order(dev->buffer[0], IN_color_ordering_line_w_ccd);
+ set_inquiry_fb_uta_line_arrangement_mode(dev->buffer[0], 33);
+ set_inquiry_CCD_line_distance(dev->buffer[0], 8);
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf */
+ {
+ DBG(DBG_warning," - activating inversion of shading data\n");
+ dev->invert_shading_data = 1;
+ }
+ }
+ }
+ else if ( (!strncmp(product, "Astra 1200S ", 12)) ||
+ (!strncmp(product, "Perfection600 ", 14)) )
+ {
+ DBG(DBG_warning,"using standard options for %s\n", product);
+ }
+ else if (!strncmp(product, "Astra 1220S ", 12))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->gamma_lsb_padded == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ DBG(DBG_warning," - 16 bit gamma table is created lsb padded\n");
+ dev->gamma_lsb_padded = 1;
+ }
+
+ if (!strncmp(version, "V1.5 ", 4))
+ {
+ DBG(DBG_warning," - lamp control enabled for version %s\n", version);
+ dev->lamp_control_available = 1;
+ }
+ }
+ else if (!strncmp(product, "Astra 2100S ", 12))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - lamp control enabled\n");
+ dev->lamp_control_available = 1;
+
+ if (dev->calibration_bytespp == -1) /* no calibration-bytespp defined in umax.conf */
+ {
+ DBG(DBG_warning," - setting calibration_bytespp = 1\n");
+ dev->calibration_bytespp = 1; /* scanner says 2 bytespp for calibration but 1 bytepp is correct */
+ }
+ }
+ else if (!strncmp(product, "Astra 2200 ", 11))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - lamp control enabled\n");
+ dev->lamp_control_available = 1;
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+
+ if (dev->calibration_bytespp == -1) /* no calibration-bytespp defined in umax.conf */
+ {
+ DBG(DBG_warning," - setting calibration_bytespp = 2\n");
+ dev->calibration_bytespp = 2;
+ }
+
+ DBG(DBG_warning," - common x and y resolution\n");
+ dev->common_xy_resolutions = 1;
+
+ if (dev->connection_type == SANE_UMAX_USB)
+ {
+ DBG(DBG_warning," - disabling quality calibration for USB connection\n");
+ set_inquiry_fw_quality(dev->buffer[0], 0);
+ }
+ }
+ else if (!strncmp(product, "Astra 2400S ", 12))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - defining pauses\n");
+ dev->pause_for_color_calibration = 7000; /* pause between start_scan and do_calibration in ms */
+ dev->pause_for_gray_calibration = 4000; /* pause between start_scan and do_calibration in ms */
+ dev->pause_after_calibration = 0000; /* pause between do_calibration and read data in ms */
+ dev->pause_after_reposition = 3000; /* pause after repostion scanner in ms */
+ dev->pause_for_moving = 3000; /* pause for moving scanhead over full area */
+
+ DBG(DBG_warning," - correcting ADF bit in inquiry\n");
+ set_inquiry_sc_adf(dev->buffer[0], 1); /* set second bit that indicates ADF is supported */
+ }
+ else if (!strncmp(product, "Vista-T630 ", 11))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->slow == -1) /* option is not predefined in umax.conf */
+ {
+ DBG(DBG_warning," - activating slow option\n");
+ dev->slow = 1;
+ }
+ }
+ else if (!strncmp(product, "UC630 ", 6))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - reposition_scanner waits until move of scan head has finished\n");
+ dev->pause_after_reposition = 0; /* call wait_scanner */
+ }
+ else if (!strncmp(product, "UC840 ", 6))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - reposition_scanner waits until move of scan head has finished\n");
+ dev->pause_after_reposition = 0; /* call wait_scanner */
+ }
+ else if (!strncmp(product, "UC1260 ", 7))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 1\n");
+ dev->inquiry_gamma_DCF = 1; /* define gamma download curve format */
+ DBG(DBG_warning," - reposition_scanner waits until move of scan head has finished\n");
+ dev->pause_after_reposition = 0; /* call wait_scanner */
+ }
+ else if (!strncmp(product, "UC1200S ", 8))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 1\n");
+ dev->inquiry_gamma_DCF = 1; /* define gamma download curve format */
+ }
+ else if (!strncmp(product, "UC1200SE ", 9))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 0\n");
+ dev->inquiry_gamma_DCF = 0; /* define gamma download curve format */
+ }
+ else if (!strncmp(product, "ARCUS PLUS ", 11))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ DBG(DBG_warning," - setting gamma download curve format to type 0\n");
+ dev->inquiry_gamma_DCF = 0; /* define gamma download curve format */
+ }
+ else if ( (!strncmp(product, "UMAX S-12G ", 11)) ||
+ (!strncmp(product, "UMAX S-12 ", 10)) ||
+ (!strncmp(product, "SuperVista S-12 ", 16)) )
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ DBG(DBG_warning," - setting maximum calibration data lines to 66\n");
+ set_inquiry_max_calibration_data_lines(dev->buffer[0], 66);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = -1;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+ }
+ else if (!strncmp(product, "Mirage D-16L ", 13))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 308;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ }
+ else if (!strncmp(product, "PowerLook III ", 14))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 28;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 828;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else if (!strncmp(product, "Power Look 2000", 15))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 22;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 24;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else if (!strncmp(product, "PowerLook 2100XL", 16))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 52;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 1052;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+
+ dev->force_quality_calibration = 1;
+ DBG(DBG_warning," - always set quality calibration\n");
+
+ /* the scanner uses the same exposure times for red, green and blue exposure_time_rgb_bind = 1 */
+ }
+ else if (!strncmp(product, "PowerLook 3000 ", 15))
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 52;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ /* not tested */
+ dev->calibration_width_offset_batch = 1052;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ else
+ {
+ DBG(DBG_warning,"using standard options for %s\n", product);
+ }
+ }
+ else if (!strncmp(vendor, "LinoHell ", 9))
+ {
+ if ( (!strncmp(product, "Office ", 7)) || (!strncmp(product, "JADE ", 5)) ) /* is a Supervista S-12 */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ DBG(DBG_warning," - setting maximum calibration data lines to 66\n");
+ set_inquiry_max_calibration_data_lines(dev->buffer[0], 66);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = -1;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+ }
+ else if (!strncmp(product, "OPAL2 ", 6)) /* looks like a Mirage II */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->gamma_lsb_padded == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ DBG(DBG_warning," - 16 bit gamma table is created lsb padded\n");
+ dev->gamma_lsb_padded = 1;
+ }
+ }
+ }
+ else if (!strncmp(vendor, "Linotype ", 9))
+ {
+ if (!strncmp(product, "SAPHIR4 ", 8)) /* is a Powerlook III */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 28;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 828;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ }
+ else if (!strncmp(vendor, "HDM ", 4))
+ {
+ if (!strncmp(product, "LS4H1S ", 7)) /* is a Powerlook III */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = 28;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+ /* calibration_area = image */
+
+ if (dev->calibration_width_offset_batch == -99999) /* no calibration-width-offset for batch scanning defined in umax.conf */
+ {
+ dev->calibration_width_offset_batch = 828;
+ DBG(DBG_warning," - adding calibration width offset for batch scanning of %d pixels\n", dev->calibration_width_offset_batch);
+ }
+ }
+ }
+ else if (!strncmp(vendor, "ESCORT ", 7))
+ {
+ if (!strncmp(product, "Galleria 600S ", 14)) /* this is an Astra 600S */
+ {
+ int add_len = get_inquiry_additional_length(dev->buffer[0]);
+
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ if (add_len == 0x8f)
+ {
+ DBG(DBG_warning," - correcting wrong inquiry data\n");
+ umax_do_new_inquiry(dev, 0x9b); /* get inquiry with correct length */
+ set_inquiry_length(dev->buffer[0], 0x9e); /* correct inquiry len */
+ /* correct color-ordering from pixel to line_with_ccd_distance */
+ set_inquiry_color_order(dev->buffer[0], IN_color_ordering_line_w_ccd);
+ set_inquiry_fb_uta_line_arrangement_mode(dev->buffer[0], 32);
+ set_inquiry_CCD_line_distance(dev->buffer[0], 8);
+ /* we should reset ADF-bit here too */
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf */
+ {
+ DBG(DBG_warning," - activating inversion of shading data\n");
+ dev->invert_shading_data = 1;
+ }
+ }
+ }
+ }
+ else if (!strncmp(vendor, "TriGem ", 7))
+ {
+ if (!strncmp(product, "PowerScanII ", 12)) /* is a Supervista S-12 */
+ {
+ DBG(DBG_warning,"setting up special options for %s\n", product);
+
+ DBG(DBG_warning," - setting maximum calibration data lines to 66\n");
+ set_inquiry_max_calibration_data_lines(dev->buffer[0], 66);
+
+ if (dev->calibration_width_offset == -99999) /* no calibration-width-offset defined in umax.conf */
+ {
+ dev->calibration_width_offset = -1;
+ DBG(DBG_warning," - adding calibration width offset of %d pixels\n", dev->calibration_width_offset);
+ }
+
+ if (dev->calibration_area == -1) /* no calibration area defined in umax.conf */
+ {
+ DBG(DBG_warning," - calibration by driver is done for each CCD pixel\n");
+ dev->calibration_area = UMAX_CALIBRATION_AREA_CCD;
+ }
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX IDENTIFY SCANNER ---------------------- */
+
+
+static int umax_identify_scanner(Umax_Device *dev)
+{
+ char vendor[10];
+ char product[0x12];
+ char version[6];
+ char *pp;
+
+ DBG(DBG_proc,"identify_scanner\n");
+ umax_do_inquiry(dev); /* get inquiry */
+ if (get_inquiry_periph_devtype(dev->buffer[0]) != IN_periph_devtype_scanner) { return 1; } /* no scanner */
+
+ get_inquiry_vendor( (char *)dev->buffer[0], vendor); vendor[8] = ' '; vendor[9] = '\0';
+ get_inquiry_product((char *)dev->buffer[0], product); product[16] = ' '; product[17] = '\0';
+ get_inquiry_version((char *)dev->buffer[0], version); version[4] = ' '; version[5] = '\0';
+
+ pp = &vendor[8];
+ while (*(pp-1) == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &product[0x10];
+ while (*(pp-1) == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &version[4];
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ DBG(DBG_info, "Found %s scanner %sversion %s on device %s\n", vendor, product, version, dev->devicename);
+
+ /* look for scanners that do not give all inquiry-information */
+ /* and if possible use driver-known inquiry-data */
+
+ if (get_inquiry_additional_length(dev->buffer[0])>=0x8f)
+ {
+ int i = 0;
+ while (strncmp("END_OF_LIST", scanner_str[2*i], 11) != 0) /* Now identify full supported scanners */
+ {
+ if (!strncmp(vendor, scanner_str[2*i], strlen(scanner_str[2*i])) )
+ {
+ if (!strncmp(product, scanner_str[2*i+1], strlen(scanner_str[2*i+1])) )
+ {
+ umax_correct_inquiry(dev, vendor, product, version);
+ return 0;
+ }
+ }
+ i++;
+ }
+
+ if (strncmp(vendor, "UMAX ", 5)) { return 1; } /* not UMAX then abort */
+
+ DBG(DBG_error0, "WARNING: %s scanner %s version %s on device %s\n"
+ "is currently an unrecognized device for this backend version.\n"
+ "Please make sure you use the most recent version of the umax backend.\n"
+ "You can download new umax-backend versions from:\n"
+ "http://www.rauch-domain.de/sane-umax\n",
+ vendor, product, version, dev->devicename);
+
+ DBG(DBG_error0,
+ "Inquiry seems to be ok.\n"
+ "******************************************************************\n"
+ "*** !!!! CONTINUE AT YOUR OWN RISK !!!! ***\n"
+ "******************************************************************\n"
+ "If you already use the most recent umax-backend version\n"
+ "then please contact me: Oliver.Rauch@rauch-domain.de\n");
+
+ return 0;
+ }
+ else /* inquiry-data not complete */
+ if (!strncmp(vendor, "UMAX ", 5)) /* test UMAX-scanners with short inquiry */
+ {
+ inquiry_blk inq_data;
+ int i;
+
+ for(i=0; i < known_inquiry; i++)
+ {
+ inq_data = *inquiry_table[i];
+ if (!strncmp(product, inq_data.scanner, strlen(inq_data.scanner)))
+ {
+ DBG(DBG_warning, "inquiry-block-length: %d\n", get_inquiry_additional_length(dev->buffer[0])+5);
+ DBG(DBG_warning, "using driver-internal inquiry-data for this scanner!\n");
+
+ /* copy driver-defined inquiry-data into inquiry-block */
+ memcpy(dev->buffer[0]+0x24, inq_data.inquiry, inq_data.inquiry_len-0x24);
+
+ /* correct variables */
+ set_inquiry_sc_uta(dev->buffer[0], get_inquiry_transavail(dev->buffer[0])); /* transparancy available ? */
+ set_inquiry_sc_adf(dev->buffer[0], get_inquiry_scanmode(dev->buffer[0])); /* automatic document feeder available ? */
+
+ set_inquiry_length(dev->buffer[0], inq_data.inquiry_len);
+ umax_correct_inquiry(dev, vendor, product, version);
+
+ return 0; /* ok */
+ }
+ }
+ DBG(DBG_error0, "ERROR: %s scanner %s version %s on device %s\n"
+ "is currently an unrecognized device, and inquiry is too short,\n"
+ "so we are not able to continue!\n"
+ "Please make sure you use the most recent version of the umax backend.\n"
+ "You can download new umax-backend versions from:\n"
+ "http://www.rauch-domain.de/sane-umax\n"
+ "You already use the most recent umax-backend version:\n"
+ "Please contact me: Oliver.Rauch@rauch-domain.de\n",
+ vendor, product, version, dev->devicename);
+ }
+
+ return 1; /* NO SUPPORTED SCANNER: short inquiry-block and unknown scanner */
+}
+
+
+/* ------------------------------------------------------------ UMAX TRIM BUFSIZE -------------------------- */
+
+
+static void umax_trim_rowbufsize(Umax_Device *dev)
+{
+ unsigned int lines=0;
+
+ if (dev->row_bufsize > dev->row_len)
+ {
+ lines = dev->row_bufsize / dev->row_len;
+
+ if (lines > dev->lines_max) /* reduce number of lines to scan if set up in config file */
+ {
+ lines = dev->lines_max;
+ }
+
+ dev->row_bufsize = lines * dev->row_len;
+ }
+
+ DBG(DBG_proc,"trim_rowbufsize: row_bufsize = %d bytes = %d lines\n", dev->row_bufsize, lines);
+}
+
+
+/* ------------------------------------------------------------ UMAX CALCULATE EXPOSURE TIME --------------- */
+
+
+static void umax_calculate_exposure_time(Umax_Device *dev, int def, int *value)
+{
+ int level;
+
+ DBG(DBG_proc,"calculate_exposure_time\n");
+ if ( (*value))
+ {
+ if ( (*value) == -1 ) { (*value) = def; }
+ else
+ {
+ level = (*value) / dev->inquiry_exposure_time_step_unit;
+ (*value) = inrange(dev->use_exposure_time_min, level, dev->inquiry_exposure_time_max);
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------ UMAX CHECK VALUES -------------------------- */
+
+
+static int umax_check_values(Umax_Device *dev)
+{
+ double inquiry_x_orig;
+ double inquiry_y_orig;
+ double inquiry_width;
+ double inquiry_length;
+ unsigned int maxwidth;
+ unsigned int maxlength;
+
+ DBG(DBG_proc,"check_values\n");
+
+ /* ------------------------------- flatbed ------------------------------- */
+
+ dev->module = WD_module_flatbed; /* reset scanmode to flatbed first */
+
+ /* --------------------------------- uta --------------------------------- */
+
+ if (dev->uta != 0)
+ {
+ dev->module = WD_module_transparency;
+ if ( (dev->inquiry_uta == 0) || (dev->inquiry_transavail == 0) )
+ {
+ DBG(DBG_error, "ERROR: transparency mode not supported by scanner\n");
+ return(1);
+ }
+ }
+
+ /* --------------------------------- adf --------------------------------- */
+
+ if (dev->adf != 0)
+ {
+ if (dev->inquiry_adf == 0)
+ {
+ DBG(DBG_error,"ERROR: adf mode not supported by scanner\n");
+ return(1);
+ }
+ }
+
+ /* --------------------------------- dor --------------------------------- */
+
+ if (dev->dor != 0)
+ {
+ if (dev->inquiry_dor == 0)
+ {
+ DBG(DBG_error, "ERROR: double optical resolution not supported by scanner\n");
+ return(1);
+ }
+ }
+
+ /* ------------------------------- resolution ------------------------ */
+
+ if (dev->dor == 0) /* standard (FB) */
+ {
+ dev->relevant_optical_res = dev->inquiry_optical_res;
+ dev->relevant_max_x_res = dev->inquiry_x_res;
+ dev->relevant_max_y_res = dev->inquiry_y_res;
+ }
+ else /* DOR mode */
+ {
+ dev->relevant_optical_res = dev->inquiry_dor_optical_res;
+ dev->relevant_max_x_res = dev->inquiry_dor_x_res;
+ dev->relevant_max_y_res = dev->inquiry_dor_y_res;
+ }
+
+ if (dev->x_resolution <= 0)
+ {
+ DBG(DBG_error,"ERROR: no x-resolution given\n");
+ return(1);
+ }
+
+ if (dev->x_resolution > dev->relevant_max_x_res)
+ {
+ dev->x_resolution = dev->relevant_max_x_res;
+ }
+
+ if (dev->x_resolution > dev->relevant_optical_res)
+ {
+ dev->scale_x = 2;
+ }
+ else
+ {
+ dev->scale_x = 1;
+ }
+
+ if (dev->y_resolution <= 0)
+ {
+ DBG(DBG_error,"ERROR: no y-resolution given\n");
+ return(1);
+ }
+
+ if (dev->y_resolution > dev->relevant_max_y_res)
+ {
+ dev->y_resolution = dev->relevant_max_y_res;
+ }
+
+ if (dev->y_resolution > dev->relevant_optical_res)
+ {
+ dev->scale_y = 2;
+ }
+ else if (dev->y_resolution > dev->relevant_optical_res/2)
+ {
+ dev->scale_y = 1;
+ }
+ else
+ {
+ /* astra 600S and 610S need this in umax_forget_line */
+ dev->scale_y = 0.5;
+ }
+
+
+ /* ------------------------------- scanarea ------------------------ */
+
+ if (dev->module == WD_module_flatbed) /* flatbed mode */
+ {
+ inquiry_x_orig = 0; /* flatbed origin */
+ inquiry_y_orig = 0;
+ inquiry_width = dev->inquiry_fb_width; /* flatbed width */
+ inquiry_length = dev->inquiry_fb_length;
+ }
+ else /* transparency mode */
+ {
+ inquiry_x_orig = dev->inquiry_uta_x_off; /* uta origin */
+ inquiry_y_orig = dev->inquiry_uta_y_off;
+ inquiry_width = dev->inquiry_uta_x_off + dev->inquiry_uta_width; /* uta width */
+ inquiry_length = dev->inquiry_uta_y_off + dev->inquiry_uta_length;
+ }
+
+ if (dev->dor != 0)
+ {
+ inquiry_x_orig = dev->inquiry_dor_x_off; /* dor origin */
+ inquiry_y_orig = dev->inquiry_dor_y_off;
+ inquiry_width = dev->inquiry_dor_x_off + dev->inquiry_dor_width; /* dor width */
+ inquiry_length = dev->inquiry_dor_y_off + dev->inquiry_dor_length;
+ }
+
+ /* limit the size to what the scanner can scan. */
+ /* this is particularly important because the scanners don't have */
+ /* built-in checks and will happily grind their gears if this is exceeded. */
+
+
+ maxwidth = inquiry_width * dev->x_coordinate_base - dev->upper_left_x - 1;
+
+ if ( (dev->scanwidth <= 0) || (dev->scanwidth > maxwidth) )
+ {
+ dev->scanwidth = maxwidth;
+ }
+
+ if (dev->upper_left_x < inquiry_x_orig)
+ {
+ dev->upper_left_x = inquiry_x_orig;
+ }
+
+
+ maxlength = inquiry_length * dev->y_coordinate_base - dev->upper_left_y - 1;
+
+ if ( (dev->scanlength <= 0) || (dev->scanlength > maxlength) )
+ {
+ dev->scanlength = maxlength;
+ }
+
+ if (dev->upper_left_y < inquiry_y_orig)
+ {
+ dev->upper_left_y = inquiry_y_orig;
+ }
+
+
+ /* Now calculate width and length in pixels */
+ dev->width_in_pixels = umax_calculate_pixels(dev->scanwidth, dev->x_resolution,
+ dev->relevant_optical_res * dev->scale_x, dev->x_coordinate_base);
+
+ dev->length_in_pixels = umax_calculate_pixels(dev->scanlength, dev->y_resolution,
+ dev->relevant_optical_res * dev->scale_y, dev->y_coordinate_base);
+
+ if ((dev->scanwidth <= 0) || (dev->scanlength <= 0))
+ {
+ DBG(DBG_error,"ERROR: scanwidth or scanlength not given\n");
+ return(1);
+ }
+
+ if (dev->bits_per_pixel_code == 1)
+ {
+ dev->bytes_per_color = 1;
+ }
+ else
+ {
+ dev->bytes_per_color = 2;
+ }
+
+ switch(dev->colormode)
+ {
+ case LINEART:
+ dev->width_in_pixels -= dev->width_in_pixels % 8;
+ dev->row_len = (dev->width_in_pixels / 8);
+ break;
+
+ case HALFTONE:
+ dev->width_in_pixels -= dev->width_in_pixels % 8;
+ dev->row_len = (dev->width_in_pixels / 8);
+ break;
+
+ case GRAYSCALE:
+ dev->row_len = dev->width_in_pixels * dev->bytes_per_color;
+ break;
+
+ case RGB_LINEART:
+ case RGB_HALFTONE:
+ if (dev->three_pass)
+ {
+ dev->row_len = dev->width_in_pixels / 8 ;
+ }
+ else
+ {
+ dev->row_len = (dev->width_in_pixels / 8 ) * 3;
+ }
+ break;
+
+ case RGB:
+ if (dev->three_pass) /* three (24bpp) or six (30bpp) bytes per pixel */
+ {
+ dev->row_len = dev->width_in_pixels * dev->bytes_per_color;
+ }
+ else
+ {
+ dev->row_len = dev->width_in_pixels * 3 * dev->bytes_per_color;
+ }
+ break;
+ }
+
+
+ /* ------------------------------- wdb length ------------------------ */
+
+ if (dev->wdb_len <= 0)
+ {
+ dev->wdb_len = dev->inquiry_wdb_len;
+ if (dev->wdb_len <= 0)
+ {
+ DBG(DBG_error,"ERROR: wdb-length not given\n");
+ return(1);
+ }
+ }
+
+ if (dev->wdb_len > used_WDB_size)
+ {
+ DBG(DBG_warning,"WARNING:window descriptor block too long, will be shortned!\n");
+ dev->wdb_len = used_WDB_size;
+ }
+
+ /* ----------------------------- cbhs-range ----------------------------- */
+
+ dev->threshold = umax_cbhs_correct(dev->inquiry_threshold_min, dev->threshold , dev->inquiry_threshold_max);
+ dev->contrast = umax_cbhs_correct(dev->inquiry_contrast_min, dev->contrast , dev->inquiry_contrast_max);
+ dev->brightness = umax_cbhs_correct(dev->inquiry_brightness_min, dev->brightness, dev->inquiry_brightness_max);
+
+ dev->highlight_r = umax_cbhs_correct(dev->inquiry_highlight_min, dev->highlight_r, dev->inquiry_highlight_max);
+ dev->highlight_g = umax_cbhs_correct(dev->inquiry_highlight_min, dev->highlight_g, dev->inquiry_highlight_max);
+ dev->highlight_b = umax_cbhs_correct(dev->inquiry_highlight_min, dev->highlight_b, dev->inquiry_highlight_max);
+
+ dev->shadow_r = umax_cbhs_correct(dev->inquiry_shadow_min, dev->shadow_r, dev->inquiry_shadow_max-1);
+ dev->shadow_g = umax_cbhs_correct(dev->inquiry_shadow_min, dev->shadow_g, dev->inquiry_shadow_max-1);
+ dev->shadow_b = umax_cbhs_correct(dev->inquiry_shadow_min, dev->shadow_b, dev->inquiry_shadow_max-1);
+
+ if (dev->shadow_r >= dev->highlight_r)
+ {
+ dev->shadow_r = dev->highlight_r-1;
+ }
+ if (dev->shadow_g >= dev->highlight_g)
+ {
+ dev->shadow_g = dev->highlight_g-1;
+ }
+ if (dev->shadow_b >= dev->highlight_b)
+ {
+ dev->shadow_b = dev->highlight_b-1;
+ }
+
+ /* ----------------------- quality calibration and preview -------------- */
+
+ if (dev->inquiry_preview == 0)
+ {
+ if (dev->preview)
+ {
+ DBG(DBG_warning, "WARNING: fast preview function not supported by scanner\n");
+ dev->preview = 0;
+ }
+ }
+
+ /* always set calibration lines because we also need this value if the scanner
+ requeires calibration by driver */
+ dev->calib_lines = dev->inquiry_max_calib_lines;
+
+ if (dev->force_quality_calibration)
+ {
+ dev->quality = 1; /* always use quality calibration */
+ }
+ else if (dev->inquiry_quality_ctrl == 0)
+ {
+ if (dev->quality)
+ {
+ DBG(DBG_warning, "WARNING: quality calibration not supported by scanner\n");
+ dev->quality = 0;
+ }
+ }
+ else
+ {
+ if (dev->preview != 0)
+ {
+ DBG(DBG_info, "quality calibration disabled in preview mode\n");
+ dev->quality = 0; /* do not use quality calibration in preview mode */
+ }
+ }
+
+ /* --------------------------- lamp intensity control ------------------- */
+
+ if (dev->inquiry_lamp_ctrl == 0)
+ {
+ if (dev->c_density || dev->s_density)
+ {
+ DBG(DBG_warning, "WARNING: scanner doesn't support lamp intensity control\n");
+ }
+ dev->c_density = dev->s_density = 0;
+ }
+
+
+ /* --------------------------- reverse (negative) ----------------------- */
+
+ if (dev->reverse != 0)
+ {
+ if ( (dev->colormode == LINEART) || (dev->colormode == HALFTONE) ||
+ (dev->colormode == RGB_LINEART) || (dev->colormode == RGB_HALFTONE) )
+ {
+ if (dev->inquiry_reverse == 0)
+ {
+ DBG(DBG_error, "ERROR: reverse for bi-level-image not supported\n");
+ return(1);
+ }
+ }
+ else
+ { dev->reverse = 0; }
+ }
+
+ if (dev->reverse_multi != 0)
+ {
+ if ((dev->colormode == RGB) || (dev->colormode == GRAYSCALE) )
+ {
+ if (dev->inquiry_reverse_multi == 0)
+ {
+ DBG(DBG_error, "ERROR: reverse for multi-level-image not supported\n");
+ return(1);
+ }
+ }
+ else
+ {
+ dev->reverse_multi = 0;
+ }
+ }
+
+ /* ----------------------------- analog gamma ---------------------------- */
+
+ if (dev->inquiry_analog_gamma == 0)
+ {
+ if (dev->analog_gamma_r + dev->analog_gamma_g + dev->analog_gamma_b != 0)
+ {
+ DBG(DBG_warning,"WARNING: analog gamma correction not supported by scanner!\n");
+ }
+ dev->analog_gamma_r = dev->analog_gamma_g = dev->analog_gamma_b = 0;
+ }
+
+ /* ---------------------------- digital gamma ---------------------------- */
+
+ if ( (dev->digital_gamma_r == 0) || (dev->digital_gamma_g == 0) ||
+ (dev->digital_gamma_b == 0) )
+ {
+ if (dev->inquiry_gamma_dwload == 0)
+ {
+ DBG(DBG_warning, "WARNING: gamma download not available\n");
+ dev->digital_gamma_r = dev->digital_gamma_g = dev->digital_gamma_b = 15;
+ }
+ }
+
+ /* ---------------------------- speed and smear ------------------------- */
+
+ if (dev->slow == 1)
+ {
+ dev->WD_speed = WD_speed_slow;
+ }
+ else
+ {
+ dev->WD_speed = WD_speed_fast;
+ }
+
+ if (dev->smear == 1)
+ {
+ dev->WD_speed += WD_speed_smear;
+ }
+
+ /* ---------------------- test bits per pixel --------------------------- */
+
+ if ( ( (dev->inquiry_GIB | 1) & dev->gamma_input_bits_code) == 0 )
+ {
+ DBG(DBG_warning,"WARNING: selected gamma input bits not supported, gamma ignored\n");
+ dev->gamma_input_bits_code = 1;
+ dev->digital_gamma_r = dev->digital_gamma_g = dev->digital_gamma_b = 15;
+ }
+
+ if ( ( (dev->inquiry_GOB | 1) & dev->bits_per_pixel_code) == 0 )
+ {
+ DBG(DBG_error,"ERROR: selected bits per pixel not supported\n");
+ return(1);
+ }
+
+ /* ----------------------- scan mode dependencies ------------------------ */
+
+ switch(dev->colormode)
+ {
+ case LINEART: /* ------------ LINEART ------------- */
+ case RGB_LINEART: /* ---------- RGB_LINEART ----------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_l_min;
+
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_l_fb_def;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_l_uta_def;
+ }
+
+ if (dev->inquiry_lineart == 0)
+ {
+ DBG(DBG_error,"ERROR: lineart mode not supported by scanner\n");
+ return(1);
+ }
+ break;
+
+ case HALFTONE: /* ----------- HALFTONE------------ */
+ case RGB_HALFTONE: /* --------- RGB_HALFTONE---------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_h_min;
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_h_fb_def;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_h_uta_def;
+ }
+
+ if (dev->inquiry_halftone == 0)
+ {
+ DBG(DBG_error,"ERROR: halftone mode not supported by scanner\n");
+ return(1);
+ }
+ break;
+
+ case GRAYSCALE: /* ---------- GRAYSCALE ------------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_g_min;
+
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_g_fb_def;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_g_uta_def;
+ }
+
+ if (dev->inquiry_gray == 0)
+ {
+ DBG(DBG_error, "ERROR: grayscale mode not supported by scanner\n");
+ return(1);
+ }
+ break;
+
+ case RGB: /* ----------------- COLOR ---------- */
+ dev->use_exposure_time_min = dev->inquiry_exposure_time_c_min;
+ if (dev->module == WD_module_flatbed)
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_c_fb_def_r;
+ dev->use_exposure_time_def_g = dev->inquiry_exposure_time_c_fb_def_g;
+ dev->use_exposure_time_def_b = dev->inquiry_exposure_time_c_fb_def_b;
+ }
+ else
+ {
+ dev->use_exposure_time_def_r = dev->inquiry_exposure_time_c_uta_def_r;
+ dev->use_exposure_time_def_g = dev->inquiry_exposure_time_c_uta_def_g;
+ dev->use_exposure_time_def_b = dev->inquiry_exposure_time_c_uta_def_b;
+ }
+
+ if (dev->inquiry_color == 0)
+ {
+ DBG(DBG_error,"ERROR: color mode not supported by scanner\n");
+ return(1);
+ }
+
+ if (dev->inquiry_one_pass_color)
+ {
+ DBG(DBG_info,"using one pass scanning mode\n");
+
+ if (dev->inquiry_color_order & IN_color_ordering_pixel)
+ {
+ DBG(DBG_info,"scanner uses color-pixel-ordering\n");
+ }
+ else if (dev->inquiry_color_order & IN_color_ordering_line_no_ccd)
+ {
+ dev->CCD_distance = 0;
+ dev->do_color_ordering = 1;
+ DBG(DBG_info,"scanner uses color-line-ordering without CCD-distance\n");
+ }
+ else if (dev->inquiry_color_order & IN_color_ordering_line_w_ccd)
+ {
+ dev->CCD_distance = dev->inquiry_CCD_line_distance;
+ dev->do_color_ordering = 1;
+ switch (dev->inquiry_fb_uta_color_arrangement) /* define color order for line ordering */
+ {
+ case 1:
+ dev->CCD_color[0] = CCD_color_green;
+
+ dev->CCD_color[1] = CCD_color_blue;
+ dev->CCD_color[2] = CCD_color_green;
+
+ dev->CCD_color[3] = CCD_color_blue;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_green;
+
+ dev->CCD_color[6] = CCD_color_blue;
+ dev->CCD_color[7] = CCD_color_red;
+
+ dev->CCD_color[8] = CCD_color_red;
+ break;
+
+ case 2:
+ dev->CCD_color[0] = CCD_color_blue;
+
+ dev->CCD_color[1] = CCD_color_green;
+ dev->CCD_color[2] = CCD_color_blue;
+
+ dev->CCD_color[3] = CCD_color_green;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_green;
+ dev->CCD_color[7] = CCD_color_red;
+
+ dev->CCD_color[8] = CCD_color_red;
+ break;
+
+ case 3:
+ dev->CCD_color[0] = CCD_color_red;
+
+ dev->CCD_color[1] = CCD_color_blue;
+ dev->CCD_color[2] = CCD_color_red;
+
+ dev->CCD_color[3] = CCD_color_blue;
+ dev->CCD_color[4] = CCD_color_green;
+ dev->CCD_color[5] = CCD_color_red;
+
+ dev->CCD_color[6] = CCD_color_blue;
+ dev->CCD_color[7] = CCD_color_green;
+
+ dev->CCD_color[8] = CCD_color_green;
+ break;
+
+ case 4: /* may be wrong !!! */
+ dev->CCD_color[0] = CCD_color_red;
+
+ dev->CCD_color[1] = CCD_color_green;
+ dev->CCD_color[2] = CCD_color_red;
+
+ dev->CCD_color[3] = CCD_color_green;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_green;
+ dev->CCD_color[7] = CCD_color_blue;
+
+ dev->CCD_color[8] = CCD_color_blue;
+ break;
+
+ case 32: /* not defined from UMAX, for Astra 600S */
+ dev->CCD_color[0] = CCD_color_green;
+
+ dev->CCD_color[1] = CCD_color_green;
+ dev->CCD_color[2] = CCD_color_blue;
+
+ dev->CCD_color[3] = CCD_color_green;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_red;
+ dev->CCD_color[7] = CCD_color_blue;
+
+ dev->CCD_color[8] = CCD_color_red;
+ break;
+
+ case 33: /* not defined from UMAX, for Astra 610S */
+ dev->CCD_color[0] = CCD_color_red;
+
+ dev->CCD_color[1] = CCD_color_red;
+ dev->CCD_color[2] = CCD_color_blue;
+
+ dev->CCD_color[3] = CCD_color_red;
+ dev->CCD_color[4] = CCD_color_green;
+ dev->CCD_color[5] = CCD_color_blue;
+
+ dev->CCD_color[6] = CCD_color_green;
+ dev->CCD_color[7] = CCD_color_blue;
+
+ dev->CCD_color[8] = CCD_color_green;
+ break;
+
+ default:
+ dev->CCD_color[0] = CCD_color_green;
+
+ dev->CCD_color[1] = CCD_color_blue;
+ dev->CCD_color[2] = CCD_color_green;
+
+ dev->CCD_color[3] = CCD_color_blue;
+ dev->CCD_color[4] = CCD_color_red;
+ dev->CCD_color[5] = CCD_color_green;
+
+ dev->CCD_color[6] = CCD_color_blue;
+ dev->CCD_color[7] = CCD_color_red;
+
+ dev->CCD_color[8] = CCD_color_red;
+ }
+ DBG(DBG_info,"scanner uses color-line-ordering with CCD-distance of %d lines\n", dev->CCD_distance);
+ }
+ else
+ {
+ DBG(DBG_error,"ERROR: color-ordering-type not supported \n");
+ return(1);
+ }
+ }
+ else
+ {
+ DBG(DBG_info,"using three pass scanning mode\n");
+ dev->three_pass=1;
+ }
+ break;
+ } /* switch */
+
+ /* ----------------------------- color ordering ------------------------ */
+
+ if (dev->do_color_ordering != 0)
+ {
+ if ( (dev->colormode != RGB) || (dev->three_pass != 0) )
+ {
+ dev->do_color_ordering = 0; /* color ordering not necessery */
+ }
+ }
+
+ return(0);
+}
+
+
+/* ------------------------------------------------------------ UMAX GET INQUIRY VALUES -------------------- */
+
+
+static void umax_get_inquiry_values(Umax_Device *dev)
+{
+ unsigned char * inquiry_block;
+
+ DBG(DBG_proc,"get_inquiry_values\n");
+
+ inquiry_block = dev->buffer[0];
+ dev->inquiry_len = get_inquiry_additional_length(dev->buffer[0])+5;
+ dev->cbhs_range = dev->inquiry_cbhs = get_inquiry_CBHS(inquiry_block);
+
+ if (dev->cbhs_range > IN_CBHS_255)
+ {
+ dev->cbhs_range = IN_CBHS_255;
+ }
+
+ if (dev->cbhs_range == IN_CBHS_50)
+ {
+ dev->inquiry_contrast_min = 103; /* minimum value for c */
+ dev->inquiry_contrast_max = 153; /* maximum value for c */
+ dev->inquiry_brightness_min = 78; /* minimum value for b */
+ dev->inquiry_brightness_max = 178; /* maximum value for b */
+ dev->inquiry_threshold_min = 78; /* minimum value for t */
+ dev->inquiry_threshold_max = 178; /* maximum value for t */
+ dev->inquiry_highlight_min = 1; /* minimum value for h */
+ dev->inquiry_highlight_max = 50; /* maximum value for h */
+ dev->inquiry_shadow_min = 0; /* minimum value for s */
+ dev->inquiry_shadow_max = 49; /* maximum value for s */
+ }
+
+ get_inquiry_vendor( (char *)inquiry_block, dev->vendor); dev->vendor[8] ='\0';
+ get_inquiry_product((char *)inquiry_block, dev->product); dev->product[16]='\0';
+ get_inquiry_version((char *)inquiry_block, dev->version); dev->version[4] ='\0';
+
+ dev->inquiry_batch_scan = get_inquiry_fw_batch_scan(inquiry_block);
+ dev->inquiry_quality_ctrl = get_inquiry_fw_quality(inquiry_block);
+ dev->inquiry_preview = get_inquiry_fw_fast_preview(inquiry_block);
+ dev->inquiry_lamp_ctrl = get_inquiry_fw_lamp_int_cont(inquiry_block);
+ dev->inquiry_calibration = get_inquiry_fw_calibration(inquiry_block);
+ dev->inquiry_transavail = get_inquiry_transavail(inquiry_block);
+ dev->inquiry_adfmode = get_inquiry_scanmode(inquiry_block);
+
+ if (dev->inquiry_len<=0x8f)
+ {
+ DBG(DBG_warning, "WARNING: inquiry return block is unexpected short.\n");
+ }
+
+ dev->inquiry_uta = get_inquiry_sc_uta(inquiry_block);
+ dev->inquiry_adf = get_inquiry_sc_adf(inquiry_block);
+
+ dev->inquiry_one_pass_color = get_inquiry_sc_one_pass_color(inquiry_block);
+ dev->inquiry_three_pass_color = get_inquiry_sc_three_pass_color(inquiry_block);
+ dev->inquiry_color = get_inquiry_sc_color(inquiry_block);
+ dev->inquiry_gray = get_inquiry_sc_gray(inquiry_block);
+ dev->inquiry_halftone = get_inquiry_sc_halftone(inquiry_block);
+ dev->inquiry_lineart = get_inquiry_sc_lineart(inquiry_block);
+
+ dev->inquiry_exposure_adj = get_inquiry_fw_adjust_exposure_tf(inquiry_block);
+ dev->inquiry_exposure_time_step_unit = get_inquiry_exposure_time_step_unit(inquiry_block);
+ dev->inquiry_exposure_time_max = get_inquiry_exposure_time_max(inquiry_block);
+
+ /* --- lineart --- */
+ dev->inquiry_exposure_time_l_min = get_inquiry_exposure_time_lhg_min(inquiry_block);
+ dev->inquiry_exposure_time_l_fb_def = get_inquiry_exposure_time_lh_def_fb(inquiry_block);
+ dev->inquiry_exposure_time_l_uta_def = get_inquiry_exposure_time_lh_def_uta(inquiry_block);
+
+ /* --- halftone --- */
+ dev->inquiry_exposure_time_h_min = get_inquiry_exposure_time_lhg_min(inquiry_block);
+ dev->inquiry_exposure_time_h_fb_def = get_inquiry_exposure_time_lh_def_fb(inquiry_block);
+ dev->inquiry_exposure_time_h_uta_def = get_inquiry_exposure_time_lh_def_uta(inquiry_block);
+
+ /* --- grayscale --- */
+ dev->inquiry_exposure_time_g_min = get_inquiry_exposure_time_lhg_min(inquiry_block);
+ dev->inquiry_exposure_time_g_fb_def = get_inquiry_exposure_time_gray_def_fb(inquiry_block);
+ dev->inquiry_exposure_time_g_uta_def = get_inquiry_exposure_time_gray_def_uta(inquiry_block);
+
+ /* --- color --- */
+ dev->inquiry_exposure_time_c_min = get_inquiry_exposure_time_color_min(inquiry_block);
+ dev->inquiry_exposure_time_c_fb_def_r = get_inquiry_exposure_time_def_r_fb(inquiry_block);
+ dev->inquiry_exposure_time_c_fb_def_g = get_inquiry_exposure_time_def_g_fb(inquiry_block);
+ dev->inquiry_exposure_time_c_fb_def_b = get_inquiry_exposure_time_def_g_fb(inquiry_block);
+ dev->inquiry_exposure_time_c_uta_def_r = get_inquiry_exposure_time_def_r_uta(inquiry_block);
+ dev->inquiry_exposure_time_c_uta_def_g = get_inquiry_exposure_time_def_g_uta(inquiry_block);
+ dev->inquiry_exposure_time_c_uta_def_b = get_inquiry_exposure_time_def_b_uta(inquiry_block);
+
+
+ dev->inquiry_dor = get_inquiry_sc_double_res(inquiry_block);
+ dev->inquiry_reverse = get_inquiry_sc_bi_image_reverse(inquiry_block);
+ dev->inquiry_reverse_multi = get_inquiry_sc_multi_image_reverse(inquiry_block);
+ dev->inquiry_shadow = 1 - get_inquiry_sc_no_shadow(inquiry_block);
+ dev->inquiry_highlight = 1 - get_inquiry_sc_no_highlight(inquiry_block);
+ dev->inquiry_analog_gamma = get_inquiry_analog_gamma(inquiry_block);
+ dev->inquiry_lineart_order = get_inquiry_lineart_order(inquiry_block);
+
+ dev->inquiry_lens_cal_in_doc_pos = get_inquiry_manual_focus(inquiry_block);
+ dev->inquiry_manual_focus = get_inquiry_manual_focus(inquiry_block);
+ dev->inquiry_sel_uta_lens_cal_pos = get_inquiry_manual_focus(inquiry_block);
+
+ dev->inquiry_gamma_dwload = get_inquiry_gamma_download_available(inquiry_block);
+
+ if (get_inquiry_gamma_type_2(inquiry_block) != 0)
+ {
+ dev->inquiry_gamma_DCF = 2;
+ }
+
+ dev->inquiry_GIB = get_inquiry_gib(inquiry_block);
+ dev->inquiry_GOB = get_inquiry_gob(inquiry_block);
+ dev->inquiry_color_order = get_inquiry_color_order(inquiry_block);
+ dev->inquiry_vidmem = get_inquiry_max_vidmem(inquiry_block);
+
+ /* optical resolution = [0x73] * 100 + [0x94] , 0x94 is not always defined */
+ dev->inquiry_optical_res = 100 * get_inquiry_max_opt_res(inquiry_block);
+ if (dev->inquiry_len > 0x94)
+ {
+ dev->inquiry_optical_res += get_inquiry_optical_resolution_residue(inquiry_block);
+ }
+
+ /* x resolution = [0x74] * 100 + [0x95] , 0x95 is not always defined */
+ dev->inquiry_x_res = 100 * get_inquiry_max_x_res(inquiry_block);
+ if (dev->inquiry_len > 0x95)
+ {
+ dev->inquiry_x_res+= get_inquiry_x_resolution_residue(inquiry_block);
+ };
+
+ /* y resolution = [0x75] * 100 + [0x96] , 0x96 is not always defined */
+ dev->inquiry_y_res = 100 * get_inquiry_max_y_res(inquiry_block);
+ if (dev->inquiry_len > 0x96)
+ {
+ dev->inquiry_y_res+= get_inquiry_y_resolution_residue(inquiry_block);
+ }
+
+
+ /* optical resolution = [0x83] * 100 + [0xa0] , 0xa0 is not always defined */
+ dev->inquiry_dor_optical_res = 100 * get_inquiry_dor_max_opt_res(inquiry_block);
+ if (dev->inquiry_len > 0xa0)
+ {
+ dev->inquiry_dor_optical_res += get_inquiry_dor_optical_resolution_residue(inquiry_block);
+ }
+
+ /* x resolution = [0x84] * 100 + [0xa1] , 0xa1 is not always defined */
+ dev->inquiry_dor_x_res = 100 * get_inquiry_dor_max_x_res(inquiry_block);
+ if (dev->inquiry_len > 0xa1)
+ {
+ dev->inquiry_dor_x_res+= get_inquiry_dor_x_resolution_residue(inquiry_block);
+ }
+
+ /* y resolution = [0x85] * 100 + [0xa2] , 0xa2 is not always defined */
+ dev->inquiry_dor_y_res = 100 * get_inquiry_dor_max_y_res(inquiry_block);
+ if (dev->inquiry_len > 0xa2)
+ {
+ dev->inquiry_dor_y_res+= get_inquiry_dor_y_resolution_residue(inquiry_block);
+ }
+
+ if (dev->inquiry_dor) /* DOR mode available ? */
+ {
+ /* if DOR resolutions are not defined, use double of standard resolution */
+
+ if (dev->inquiry_dor_optical_res == 0)
+ {
+ dev->inquiry_dor_optical_res = dev->inquiry_optical_res * 2;
+ }
+
+ if (dev->inquiry_dor_x_res == 0)
+ {
+ dev->inquiry_dor_x_res = dev->inquiry_x_res * 2;
+ }
+
+ if (dev->inquiry_dor_y_res == 0)
+ {
+ dev->inquiry_dor_y_res = dev->inquiry_y_res * 2;
+ }
+ }
+
+ dev->inquiry_fb_width = (double)get_inquiry_fb_max_scan_width(inquiry_block) * 0.01;
+ dev->inquiry_fb_length = (double)get_inquiry_fb_max_scan_length(inquiry_block) * 0.01;
+
+ dev->inquiry_uta_width = (double)get_inquiry_uta_max_scan_width(inquiry_block) * 0.01;
+ dev->inquiry_uta_length = (double)get_inquiry_uta_max_scan_length(inquiry_block) * 0.01;
+ dev->inquiry_uta_x_off = (double)get_inquiry_uta_x_original_point(inquiry_block) * 0.01;
+ dev->inquiry_uta_y_off = (double)get_inquiry_uta_y_original_point(inquiry_block) * 0.01;
+
+ dev->inquiry_dor_width = (double)get_inquiry_dor_max_scan_width(inquiry_block) * 0.01;
+ dev->inquiry_dor_length = (double)get_inquiry_dor_max_scan_length(inquiry_block) * 0.01;
+ dev->inquiry_dor_x_off = (double)get_inquiry_dor_x_original_point(inquiry_block) * 0.01;
+ dev->inquiry_dor_y_off = (double)get_inquiry_dor_y_original_point(inquiry_block) * 0.01;
+
+ dev->inquiry_max_warmup_time = get_inquiry_lamp_warmup_maximum_time(inquiry_block) * 2;
+
+ dev->inquiry_wdb_len = get_inquiry_wdb_length(inquiry_block);
+
+ /* it is not guaranteed that the following values are in the inquiry return block */
+
+ /* 0x9a */
+ if (dev->inquiry_len<=0x9a)
+ {
+ return;
+ }
+ dev->inquiry_max_calib_lines = get_inquiry_max_calibration_data_lines(inquiry_block);
+
+ /* 0x9b */
+ if (dev->inquiry_len<=0x9b)
+ {
+ return;
+ }
+ dev->inquiry_fb_uta_color_arrangement = get_inquiry_fb_uta_line_arrangement_mode(inquiry_block);
+
+ /* 0x9c */
+ if (dev->inquiry_len<=0x9c)
+ {
+ return;
+ }
+ dev->inquiry_adf_color_arrangement = get_inquiry_adf_line_arrangement_mode(inquiry_block);
+
+ /* 0x9d */
+ if (dev->inquiry_len<=0x9d)
+ {
+ return;
+ }
+ dev->inquiry_CCD_line_distance = get_inquiry_CCD_line_distance(inquiry_block);
+
+ return;
+}
+
+
+/* ------------------------------------------------------------ UMAX CALCULATE ANALOG GAMMA ---------------- */
+
+
+static int umax_calculate_analog_gamma(double value)
+{
+ int gamma;
+
+ if (value < 1.0)
+ { value=1.0; }
+
+ if (value > 2.0)
+ { value=2.0; }
+
+ gamma=0; /* select gamma_value from analog_gamma_table */
+ while (value>analog_gamma_table[gamma])
+ {
+ gamma++;
+ }
+
+ if (gamma)
+ {
+ if ((analog_gamma_table[gamma-1] + analog_gamma_table[gamma]) /2 > value)
+ {
+ gamma--;
+ }
+ }
+
+ return(gamma);
+}
+
+/* ------------------------------------------------------------ UMAX OUTPUT IMAGE DATA -------------------- */
+
+static void umax_output_image_data(Umax_Device *dev, FILE *fp, unsigned int data_to_read, int bufnr)
+{
+ if (dev->do_color_ordering == 0) /* pixel ordering */
+ {
+ if ((dev->inquiry_lineart_order) && (dev->colormode == LINEART)) /* lineart with LSB first */
+ {
+ unsigned int i, j;
+ int new, old;
+
+ for (i=0; i<data_to_read; i++)
+ {
+ old = dev->buffer[bufnr][i];
+ new = 0;
+ for (j=0; j<8; j++) /* reverse bit order of 1 byte */
+ {
+ new = (new << 1) + (old & 1);
+ old = old >> 1;
+ }
+ dev->buffer[bufnr][i]=new;
+ }
+ }
+ fwrite(dev->buffer[bufnr], 1, data_to_read, fp);
+ }
+ else /* line ordering */
+ {
+ unsigned char *linesource = dev->buffer[bufnr];
+ unsigned char *pixelsource;
+ int bytes = 1;
+ int lines;
+ int i;
+
+ if (dev->bits_per_pixel_code != 1) /* >24 bpp */
+ {
+ bytes = 2;
+ }
+
+ lines = data_to_read / (dev->width_in_pixels * bytes);
+
+ for(i=0; i<lines; i++)
+ {
+ umax_order_line(dev, linesource);
+ linesource += dev->width_in_pixels * bytes;
+
+ pixelsource = umax_get_pixel_line(dev);
+ if (pixelsource != NULL)
+ {
+ fwrite(pixelsource, bytes, dev->width_in_pixels * 3, fp);
+ }
+ }
+ }
+}
+
+/* ------------------------------------------------------------ UMAX READER PROCESS ------------------------ */
+
+
+static int umax_reader_process(Umax_Device *dev, FILE *fp, unsigned int image_size)
+{
+ int status;
+ int bytes = 1;
+ int queue_filled = 0;
+ unsigned int bufnr_queue = 0;
+ unsigned int bufnr_read = 0;
+ unsigned int data_left_to_read = image_size;
+ unsigned int data_left_to_queue = image_size;
+ unsigned int data_to_read;
+ unsigned int data_to_queue;
+
+ dev->row_bufsize = dev->bufsize;
+ umax_trim_rowbufsize(dev); /* trim bufsize */
+
+ if (dev->bits_per_pixel_code != 1) /* >24 bpp */
+ {
+ bytes = 2;
+ }
+
+ DBG(DBG_read,"reading %u bytes in blocks of %u bytes\n", image_size, dev->row_bufsize);
+
+ if (dev->pixelbuffer != NULL) /* buffer exists? */
+ {
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+ }
+
+ if (dev->do_color_ordering != 0)
+ {
+ DBG(DBG_info,"ordering from line-order to pixel-order\n");
+
+ dev->pixelline_max = 3 * dev->CCD_distance * dev->scale_y + 2;
+
+ dev->pixelbuffer = malloc(dev->width_in_pixels * dev->pixelline_max * bytes * 3);
+
+ if (dev->pixelbuffer == NULL) /* NO MEMORY */
+ {
+ return -1;
+ }
+ }
+
+ WAIT_SCANNER;
+
+ do
+ {
+ if (data_left_to_queue)
+ {
+ data_to_queue = (data_left_to_queue < dev->row_bufsize) ? data_left_to_queue : dev->row_bufsize;
+
+ /* umax_get_data_buffer_status(dev); */
+
+ status = umax_queue_read_image_data_req(dev, data_to_queue, bufnr_queue);
+
+ if (status == 0) /* no error but nothing queued */
+ {
+ continue;
+ }
+
+ if (status == -1) /* error */
+ {
+ DBG(DBG_error,"ERROR: umax_reader_process: unable to queue read image data request!\n");
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+ return(-1);
+ }
+
+ data_left_to_queue -= data_to_queue;
+ DBG(DBG_read, "umax_reader_process: read image data queued for buffer[%d] \n", bufnr_queue);
+
+ bufnr_queue++;
+ if (bufnr_queue >= dev->scsi_maxqueue)
+ {
+ bufnr_queue = 0;
+ queue_filled = 1; /* ok, we can start to read the queued buffers - if not already started */
+ }
+
+ if (!data_left_to_queue)
+ {
+ queue_filled = 1; /* ok, we can start to read the queued buffer(s) - all read requests are send */
+ }
+ }
+
+ if (queue_filled) /* queue filled, ok we can read data */
+ {
+ status = umax_wait_queued_image_data(dev, bufnr_read);
+
+ if (status == -1)
+ {
+ DBG(DBG_error,"ERROR: umax_reader_process: unable to get image data from scanner!\n");
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+ return(-1);
+ }
+
+ data_to_read = dev->length_read[bufnr_read]; /* number of bytes in buffer */
+ umax_output_image_data(dev, fp, data_to_read, bufnr_read);
+
+ data_left_to_read -= data_to_read;
+ DBG(DBG_read, "umax_reader_process: buffer of %d bytes read; %d bytes to go\n", data_to_read, data_left_to_read);
+
+ /* if we did not get all requested data increase data_left_to_queue so that we get all needed data */
+ if (dev->length_read[bufnr_read] != dev->length_queued[bufnr_read])
+ {
+ data_left_to_queue += dev->length_queued[bufnr_read] - dev->length_read[bufnr_read];
+ }
+
+ bufnr_read++;
+ if (bufnr_read >= dev->scsi_maxqueue)
+ {
+ bufnr_read = 0;
+ }
+ }
+ } while (data_left_to_read);
+
+ free(dev->pixelbuffer);
+ dev->pixelbuffer = NULL;
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------ UMAX INITIALIZE VALUES --------------------- */
+
+
+static void umax_initialize_values(Umax_Device *dev) /* called each time before setting scan-values */
+{ /* Initialize dev structure */
+ DBG(DBG_proc,"initialize_values\n");
+
+ dev->three_pass = 0; /* 1 if threepas_mode only */
+ dev->row_len = -1;
+ dev->max_value = 255; /* maximum value */
+
+ dev->wdb_len = 0;
+ dev->width_in_pixels = 0; /* scan width in pixels */
+ dev->length_in_pixels = 0; /* scan length in pixels */
+ dev->scanwidth = 0; /* width in inch at x_coordinate_base dpi */
+ dev->scanlength = 0; /* length in inch at y_coordinate_base dpi */
+ dev->x_resolution = 0;
+ dev->y_resolution = 0;
+ dev->upper_left_x = 0; /* at 1200pt/inch */
+ dev->upper_left_y = 0; /* at 1200pt/inch */
+ dev->bytes_per_color = 0; /* bytes for each color */
+
+ dev->bits_per_pixel = 8; /* number of bits per pixel */
+ dev->bits_per_pixel_code = 1; /* 1 = 8/24 bpp, 2 = 9/27 bpp, 4 = 10/30 bpp */
+ dev->gamma_input_bits_code = 1; /* 8 = 12/36 bpp, 16 = 14/42 bpp, 32 = 16/48 bpp */
+ dev->set_auto = 0; /* 0 or 1 */
+ dev->preview = 0; /* 1 for preview */
+ dev->quality = 0; /* quality calibration */
+ dev->warmup = 0; /* warmup-bit */
+ dev->fix_focus_position = 0; /* fix focus position */
+ dev->lens_cal_in_doc_pos = 0; /* lens calibration in document position */
+ dev->disable_pre_focus = 0; /* disable pre focus */
+ dev->holder_focus_pos_0mm = 0; /* 0.6mm <-> 0.0mm holder focus position */
+ dev->manual_focus = 0; /* automatic <-> manual focus */
+ dev->colormode = 0; /* LINEART, HALFTONE, GRAYSCALE or RGB */
+ dev->adf = 0; /* 1 if adf shall be used */
+ dev->uta = 0; /* 1 if uta shall be used */
+ dev->module = WD_module_flatbed;
+ dev->cbhs_range = WD_CBHS_255;
+ dev->dor = 0;
+ dev->halftone = WD_halftone_8x8_1;
+ dev->reverse = 0;
+ dev->reverse_multi = 0;
+ dev->calibration = 0;
+
+ dev->exposure_time_calibration_r = 0; /* use this for calibration */
+ dev->exposure_time_calibration_g = 0; /* use this for calibration */
+ dev->exposure_time_calibration_b = 0; /* use this for calibration */
+ dev->exposure_time_scan_r = 0; /* use this for scan */
+ dev->exposure_time_scan_g = 0; /* use this for scan */
+ dev->exposure_time_scan_b = 0; /* use this for scan */
+
+ dev->c_density = WD_lamp_c_density_auto; /* calibration lamp density */
+ dev->s_density = WD_lamp_s_density_auto; /* next scan lamp density */
+
+ dev->threshold = 128; /* threshold for lineart mode */
+ dev->brightness = 128; /* brightness for halftone mode */
+ dev->contrast = 128; /* contrast for halftone mode */
+ dev->highlight_r = 255; /* highlight gray/red */
+ dev->highlight_g = 255; /* highlight green */
+ dev->highlight_b = 255; /* highlight blue */
+ dev->shadow_r = 0; /* shadow gray/red */
+ dev->shadow_g = 0; /* shadow green */
+ dev->shadow_b = 0; /* shadow blue */
+
+ dev->digital_gamma_r = WD_gamma_normal;
+ dev->digital_gamma_g = WD_gamma_normal;
+ dev->digital_gamma_b = WD_gamma_normal;
+
+ dev->analog_gamma_r = 0; /* analog gamma for red and gray to 1.0 */
+ dev->analog_gamma_g = 0; /* analog gamma for green to 1.0 */
+ dev->analog_gamma_b = 0; /* analog gamma for blue to 1.0 */
+
+
+ dev->pixelline_ready[0] = 0; /* reset all values for color ordering */
+ dev->pixelline_ready[1] = 0;
+ dev->pixelline_ready[2] = 0;
+ dev->pixelline_next[0] = 0;
+ dev->pixelline_next[1] = 0;
+ dev->pixelline_next[2] = 0;
+ dev->pixelline_del[0] = 1;
+ dev->pixelline_del[1] = 1;
+ dev->pixelline_del[2] = 1;
+ dev->pixelline_optic[0] = 1;
+ dev->pixelline_optic[1] = 1;
+ dev->pixelline_optic[2] = 1;
+ dev->pixelline_max = 0;
+ dev->pixelline_opt_res = 0;
+ dev->pixelline_read = 0;
+ dev->pixelline_written = 0;
+ dev->CCD_distance = 0;
+
+ dev->calib_lines = 0; /* request calibration lines */
+ dev->do_calibration = 0; /* no calibration by driver */
+ dev->do_color_ordering = 0; /* no line- to pixel-mode ordering */
+
+ dev->button0_pressed = 0; /* reset button 0 pressed flag */
+ dev->button1_pressed = 0; /* reset button 1 pressed flag */
+ dev->button2_pressed = 0; /* reset button 2 pressed flag */
+}
+
+
+/* ------------------------------------------------------------ UMAX INIT ---------------------------------- */
+
+
+static void umax_init(Umax_Device *dev) /* umax_init is called once while driver-initialization */
+{
+ DBG(DBG_proc,"init\n");
+
+ dev->devicename = NULL;
+ dev->pixelbuffer = NULL;
+
+ /* config file or predefined settings */
+ if (dev->connection_type == SANE_UMAX_SCSI)
+ {
+ dev->request_scsi_maxqueue = umax_scsi_maxqueue;
+ }
+ else /* SANE_UMAX_USB, USB does not support command queueing */
+ {
+ DBG(DBG_info2, "setting request_scsi_maxqueue = 1 for USB connection\n");
+ dev->request_scsi_maxqueue = 1;
+ }
+
+ dev->request_preview_lines = umax_preview_lines;
+ dev->request_scan_lines = umax_scan_lines;
+ dev->handle_bad_sense_error = umax_handle_bad_sense_error;
+ dev->execute_request_sense = umax_execute_request_sense;
+ dev->scsi_buffer_size_min = umax_scsi_buffer_size_min;
+ dev->scsi_buffer_size_max = umax_scsi_buffer_size_max;
+ dev->force_preview_bit_rgb = umax_force_preview_bit_rgb;
+ dev->slow = umax_slow;
+ dev->smear = umax_smear;
+ dev->calibration_area = umax_calibration_area;
+ dev->calibration_width_offset = umax_calibration_width_offset;
+ dev->calibration_width_offset_batch = umax_calibration_width_offset_batch;
+ dev->calibration_bytespp = umax_calibration_bytespp;
+ dev->exposure_time_rgb_bind = umax_exposure_time_rgb_bind;
+ dev->invert_shading_data = umax_invert_shading_data;
+ dev->lamp_control_available = umax_lamp_control_available;
+ dev->gamma_lsb_padded = umax_gamma_lsb_padded;
+
+ DBG(DBG_info, "request_scsi_maxqueue = %d\n", dev->request_scsi_maxqueue);
+ DBG(DBG_info, "request_preview_lines = %d\n", dev->request_preview_lines);
+ DBG(DBG_info, "request_scan_lines = %d\n", dev->request_scan_lines);
+ DBG(DBG_info, "handle_bad_sense_error = %d\n", dev->handle_bad_sense_error);
+ DBG(DBG_info, "execute_request_sense = %d\n", dev->execute_request_sense);
+ DBG(DBG_info, "scsi_buffer_size_min = %d\n", dev->scsi_buffer_size_min);
+ DBG(DBG_info, "scsi_buffer_size_max = %d\n", dev->scsi_buffer_size_max);
+ DBG(DBG_info, "force_preview_bit_rgb = %d\n", dev->force_preview_bit_rgb);
+ DBG(DBG_info, "slow = %d\n", dev->slow);
+ DBG(DBG_info, "smear = %d\n", dev->smear);
+ DBG(DBG_info, "calibration_area = %d\n", dev->calibration_area);
+ DBG(DBG_info, "calibration_width_offset = %d\n", dev->calibration_width_offset);
+ DBG(DBG_info, "calibration_width_offset_batch = %d\n", dev->calibration_width_offset_batch);
+ DBG(DBG_info, "calibration_bytespp = %d\n", dev->calibration_bytespp);
+ DBG(DBG_info, "exposure_time_rgb_bind = %d\n", dev->exposure_time_rgb_bind);
+ DBG(DBG_info, "invert_shading_data = %d\n", dev->invert_shading_data);
+ DBG(DBG_info, "lamp_control_available = %d\n", dev->lamp_control_available);
+
+
+ dev->inquiry_len = 0;
+ dev->inquiry_wdb_len = -1;
+ dev->inquiry_optical_res = -1;
+ dev->inquiry_x_res = -1;
+ dev->inquiry_y_res = -1;
+ dev->inquiry_fb_width = -1;
+ dev->inquiry_fb_length = -1;
+ dev->inquiry_uta_width = -1;
+ dev->inquiry_uta_length = -1;
+ dev->inquiry_dor_width = -1;
+ dev->inquiry_dor_length = -1;
+ dev->inquiry_exposure_adj = 0;
+ dev->inquiry_exposure_time_step_unit = -1; /* exposure time unit in micro sec */
+ dev->inquiry_exposure_time_max = -1; /* exposure time maximum */
+ dev->inquiry_exposure_time_l_min = -1; /* exposure time minimum for lineart */
+ dev->inquiry_exposure_time_l_fb_def = -1; /* exposure time default for lineart flatbed */
+ dev->inquiry_exposure_time_l_uta_def = -1; /* exposure time default for lineart uta */
+ dev->inquiry_exposure_time_h_min = -1; /* exposure time minimum for halftone */
+ dev->inquiry_exposure_time_h_fb_def = -1; /* exposure time default for halftone flatbed */
+ dev->inquiry_exposure_time_h_uta_def = -1; /* exposure time default for halftone uta */
+ dev->inquiry_exposure_time_g_min = -1; /* exposure time minimum for grayscale */
+ dev->inquiry_exposure_time_g_fb_def = -1; /* exposure time default for grayscale flatbed */
+ dev->inquiry_exposure_time_g_uta_def = -1; /* exposure time default for grayscale uta */
+ dev->inquiry_exposure_time_c_min = -1; /* exposure time minimum for color */
+ dev->inquiry_exposure_time_c_fb_def_r = -1; /* exposure time default for color flatbed red */
+ dev->inquiry_exposure_time_c_fb_def_g = -1; /* exposure time default for color flatbed green */
+ dev->inquiry_exposure_time_c_fb_def_b = -1; /* exposure time default for color flatbed blue */
+ dev->inquiry_exposure_time_c_uta_def_r = -1; /* exposure time default for color uta red */
+ dev->inquiry_exposure_time_c_uta_def_g = -1; /* exposure time default for color uta green */
+ dev->inquiry_exposure_time_c_uta_def_b = -1; /* exposure time default for color uta blue */
+ dev->inquiry_max_warmup_time = 0; /* maximum warmup time */
+ dev->inquiry_cbhs = WD_CBHS_255;
+ dev->inquiry_contrast_min = 1; /* minimum value for c */
+ dev->inquiry_contrast_max = 255; /* maximum value for c */
+ dev->inquiry_brightness_min = 1; /* minimum value for b */
+ dev->inquiry_brightness_max = 255; /* maximum value for b */
+ dev->inquiry_threshold_min = 1; /* minimum value for t */
+ dev->inquiry_threshold_max = 255; /* maximum value for t */
+ dev->inquiry_highlight_min = 1; /* minimum value for h */
+ dev->inquiry_highlight_max = 255; /* maximum value for h */
+ dev->inquiry_shadow_min = 0; /* minimum value for s */
+ dev->inquiry_shadow_max = 254; /* maximum value for s */
+ dev->inquiry_quality_ctrl = 0;
+ dev->inquiry_preview = 0;
+ dev->inquiry_lamp_ctrl = 0;
+ dev->inquiry_transavail = 0;
+ dev->inquiry_uta = 0;
+ dev->inquiry_adfmode = 0;
+ dev->inquiry_adf = 0;
+ dev->inquiry_dor = 0;
+ dev->inquiry_reverse = 0;
+ dev->inquiry_reverse_multi = 0;
+ dev->inquiry_analog_gamma = 0;
+ dev->inquiry_gamma_dwload = 0;
+ dev->inquiry_one_pass_color = 0;
+ dev->inquiry_three_pass_color = 0;
+ dev->inquiry_color = 0;
+ dev->inquiry_gray = 0;
+ dev->inquiry_halftone = 0;
+ dev->inquiry_lineart = 0;
+ dev->inquiry_calibration = 1;
+ dev->inquiry_shadow = 0;
+ dev->inquiry_highlight = 0;
+ dev->inquiry_gamma_DCF = -1;
+ dev->inquiry_max_calib_lines = 66; /* most scanners use 66 lines, so lets define it as default */
+
+ dev->common_xy_resolutions = 0;
+
+ dev->x_coordinate_base = 1200; /* these are the 1200pt/inch */
+ dev->y_coordinate_base = 1200; /* these are the 1200pt/inch */
+
+ dev->button0_pressed = 0; /* reset button 0 pressed flag */
+ dev->button1_pressed = 0; /* reset button 1 pressed flag */
+ dev->button2_pressed = 0; /* reset button 2 pressed flag */
+
+ dev->pause_for_color_calibration = 0; /* pause between start_scan and do_calibration in ms */
+ dev->pause_for_gray_calibration = 0; /* pause between start_scan and do_calibration in ms */
+ dev->pause_after_calibration = 0; /* pause between do_calibration and read data in ms */
+ dev->pause_after_reposition = -1; /* pause after repostion scanner in ms, -1 = do not wait */
+ dev->pause_for_moving = 0; /* pause for moving scanhead over full area */
+
+ if (umax_test_little_endian() == SANE_TRUE)
+ {
+ dev->low_byte_first = 1; /* in 2 byte mode send lowbyte first */
+ DBG(DBG_info, "backend runs on little endian machine\n");
+ }
+ else
+ {
+ dev->low_byte_first = 0; /* in 2 byte mode send highbyte first */
+ DBG(DBG_info, "backend runs on big endian machine\n");
+ }
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ DBG(DBG_info,"variable scsi buffer size (usage of sanei_scsi_open_extended)\n");
+#else
+ DBG(DBG_info,"fixed scsi buffer size = %d bytes\n", sanei_scsi_max_request_size);
+#endif
+}
+
+
+/* ------------------------------------------------------------ MAX STRING SIZE ---------------------------- */
+
+
+static size_t max_string_size(SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+
+ return max_size;
+}
+
+
+/* ------------------------------------------------------------ DO CANCEL ---------------------------------- */
+
+
+static SANE_Status do_cancel(Umax_Scanner *scanner)
+{
+ SANE_Pid pid;
+ int status;
+
+ DBG(DBG_sane_proc,"do_cancel\n");
+
+ scanner->scanning = SANE_FALSE;
+
+ if (scanner->reader_pid != -1)
+ {
+ DBG(DBG_sane_info,"killing reader_process\n");
+
+ sanei_thread_kill(scanner->reader_pid);
+ pid = sanei_thread_waitpid(scanner->reader_pid, &status);
+
+ if (pid == -1)
+ {
+ DBG(DBG_sane_info, "do_cancel: sanei_thread_waitpid failed, already terminated ? (%s)\n", strerror(errno));
+ }
+ else
+ {
+ DBG(DBG_sane_info, "do_cancel: reader_process terminated with status: %s\n", sane_strstatus(status));
+ }
+
+ scanner->reader_pid = -1;
+
+ if (scanner->device->pixelbuffer != NULL) /* pixelbuffer exists? */
+ {
+ free(scanner->device->pixelbuffer); /* free pixelbuffer */
+ scanner->device->pixelbuffer = NULL;
+ }
+ }
+
+ sanei_scsi_req_flush_all(); /* flush SCSI queue, when we do not do this then sanei_scsi crashes next time */
+
+ if (scanner->device->sfd != -1) /* make sure we have a working filedescriptor */
+ {
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ DBG(DBG_sane_info,"closing scannerdevice filedescriptor\n");
+ umax_scsi_close(scanner->device);
+ }
+
+ scanner->device->three_pass_color = 1; /* reset color in color scanning */
+
+ return SANE_STATUS_CANCELLED;
+}
+
+
+/* ------------------------------------------------------------ ATTACH SCANNER ----------------------------- */
+
+
+static SANE_Status attach_scanner(const char *devicename, Umax_Device **devp, int connection_type)
+{
+ Umax_Device *dev;
+ int i;
+
+ DBG(DBG_sane_proc,"attach_scanner: %s, connection_type %d\n", devicename, connection_type);
+
+ for (dev = first_dev; dev; dev = dev->next) /* search is scanner already is listed in devicelist */
+ {
+ if (strcmp(dev->sane.name, devicename) == 0) /* scanner is already listed */
+ {
+ if (devp)
+ {
+ *devp = dev; /* return pointer to device */
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* scanner has not been attached yet */
+
+ dev = malloc( sizeof(*dev) );
+ if (!dev)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(dev, '\0', sizeof(Umax_Device)); /* clear structure */
+
+ /* If connection type is not known (==0) then try to open the device as an USB device. */
+ /* If it fails, try the SCSI method. */
+
+#ifdef UMAX_ENABLE_USB
+ dev->connection_type = connection_type; /* 0 = unknown, 1=scsi, 2=usb */
+
+ if (dev->connection_type != SANE_UMAX_SCSI)
+ {
+ dev->bufsize = 16384; /* 16KB */
+ DBG(DBG_info, "attach_scanner: opening usb device %s\n", devicename);
+
+ if (sanei_umaxusb_open(devicename, &dev->sfd, sense_handler, dev) == SANE_STATUS_GOOD)
+ {
+ dev->connection_type = SANE_UMAX_USB;
+ }
+ else /* opening usb device failed */
+ {
+ if (dev->connection_type == SANE_UMAX_USB) /* we know it is not a scsi device: error */
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: opening usb device %s failed\n", devicename);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(DBG_info, "attach_scanner: failed to open %s as usb device\n", devicename);
+ }
+ }
+#else
+ dev->connection_type = SANE_UMAX_SCSI;
+#endif
+
+ if (dev->connection_type != SANE_UMAX_USB) /* not an USB device, then try as SCSI */
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ dev->bufsize = 16384; /* 16KB */
+ DBG(DBG_info, "attach_scanner: opening scsi device %s\n", devicename);
+
+ if (sanei_scsi_open_extended(devicename, &dev->sfd, sense_handler, dev, (int *) &dev->bufsize) != 0)
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: opening scsi device %s failed\n", devicename);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dev->bufsize < 4096) /* < 4KB */
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: sanei_scsi_open_extended returned too small scsi buffer\n");
+ umax_scsi_close(dev);
+ free(dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG(DBG_info, "attach_scanner: sanei_scsi_open_extended returned scsi buffer size = %d\n", dev->bufsize);
+#else
+ dev->bufsize = sanei_scsi_max_request_size;
+
+ if (sanei_scsi_open(devicename, dev, sense_handler, dev) != 0)
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: opening scsi device %s failed\n", devicename);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ dev->connection_type = SANE_UMAX_SCSI; /* set connection type (may have been unknown == 0) */
+ }
+
+ DBG(DBG_info, "attach_scanner: allocating SCSI buffer[0]\n");
+ dev->buffer[0] = malloc(dev->bufsize); /* allocate buffer */
+
+ for (i=1; i<SANE_UMAX_SCSI_MAXQUEUE; i++)
+ {
+ dev->buffer[i] = NULL;
+ }
+
+ if (!dev->buffer[0]) /* malloc failed */
+ {
+ DBG(DBG_error, "ERROR: attach scanner: could not allocate buffer[0]\n");
+ umax_scsi_close(dev);
+ free(dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->scsi_maxqueue = 1; /* only one buffer outside the reader process */
+
+ umax_init(dev); /* preset values in structure dev */
+ umax_initialize_values(dev); /* reset values */
+
+ dev->devicename = strdup(devicename);
+
+ if (umax_identify_scanner(dev) != 0)
+ {
+ DBG(DBG_error, "ERROR: attach_scanner: scanner-identification failed\n");
+ umax_scsi_close(dev);
+ free(dev->buffer[0]);
+ free(dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dev->slow == -1) /* option is not predefined in umax.conf and not by backend */
+ {
+ dev->slow = 0;
+ }
+
+ if (dev->smear == -1) /* option is not predefined in umax.conf and not by backend */
+ {
+ dev->smear = 0;
+ }
+
+ if (dev->invert_shading_data == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ dev->invert_shading_data = 0;
+ }
+
+ if (dev->gamma_lsb_padded == -1) /* nothing defined in umax.conf and not by backend */
+ {
+ dev->gamma_lsb_padded = 0;
+ }
+
+ umax_get_inquiry_values(dev);
+ umax_print_inquiry(dev);
+ DBG(DBG_inquiry,"\n");
+ DBG(DBG_inquiry,"==================== end of inquiry ====================\n");
+ DBG(DBG_inquiry,"\n");
+
+ umax_scsi_close(dev);
+
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->vendor;
+ dev->sane.model = dev->product;
+ dev->sane.type = "flatbed scanner";
+
+ if (strcmp(dev->sane.model,"PSD ") == 0)
+ {
+ dev->sane.type = "page scanner";
+ }
+
+ dev->x_range.min = SANE_FIX(0);
+ dev->x_range.quant = SANE_FIX(0);
+ dev->x_range.max = SANE_FIX(dev->inquiry_fb_width * MM_PER_INCH);
+
+ dev->y_range.min = SANE_FIX(0);
+ dev->y_range.quant = SANE_FIX(0);
+ dev->y_range.max = SANE_FIX(dev->inquiry_fb_length * MM_PER_INCH);
+
+#if UMAX_RESOLUTION_PERCENT_STEP
+ dev->x_dpi_range.min = SANE_FIX(dev->inquiry_optical_res/100);
+ dev->x_dpi_range.quant = SANE_FIX(dev->inquiry_optical_res/100);
+#else
+ dev->x_dpi_range.min = SANE_FIX(5);
+ dev->x_dpi_range.quant = SANE_FIX(5);
+#endif
+ dev->x_dpi_range.max = SANE_FIX(dev->inquiry_x_res);
+
+#if UMAX_RESOLUTION_PERCENT_STEP
+ dev->y_dpi_range.min = SANE_FIX(dev->inquiry_optical_res/100);
+ dev->y_dpi_range.quant = SANE_FIX(dev->inquiry_optical_res/100);
+#else
+ dev->y_dpi_range.min = SANE_FIX(5);
+ dev->y_dpi_range.quant = SANE_FIX(5);
+#endif
+ dev->y_dpi_range.max = SANE_FIX(dev->inquiry_y_res);
+
+ dev->analog_gamma_range.min = SANE_FIX(1.0);
+ dev->analog_gamma_range.quant = SANE_FIX(0.01);
+ dev->analog_gamma_range.max = SANE_FIX(2.0);
+
+ DBG(DBG_info,"x_range.max = %f\n", SANE_UNFIX(dev->x_range.max));
+ DBG(DBG_info,"y_range.max = %f\n", SANE_UNFIX(dev->y_range.max));
+ DBG(DBG_info,"x_dpi_range.max = %f\n", SANE_UNFIX(dev->x_dpi_range.max));
+ DBG(DBG_info,"y_dpi_range.max = %f\n", SANE_UNFIX(dev->y_dpi_range.max));
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ READER PROCESS SIGTERM HANDLER ------------ */
+
+
+static RETSIGTYPE reader_process_sigterm_handler(int signal)
+{
+ DBG(DBG_sane_info,"reader_process: terminated by signal %d\n", signal);
+
+ sanei_scsi_req_flush_all(); /* flush SCSI queue */
+
+ _exit (SANE_STATUS_GOOD);
+}
+
+
+/* ------------------------------------------------------------ READER PROCESS ----------------------------- */
+
+
+static int reader_process(void *data) /* executed as a child process or as thread */
+{
+ Umax_Scanner *scanner = (Umax_Scanner *)data;
+ FILE *fp;
+ int status;
+ unsigned int data_length;
+ struct SIGACTION act;
+ unsigned int i;
+
+ if (sanei_thread_is_forked())
+ {
+ DBG(DBG_sane_proc,"reader_process started (forked)\n");
+ close(scanner->pipe_read_fd);
+ scanner->pipe_read_fd = -1;
+
+ /* sanei_scsi crashes when the scsi commands are not flushed, done in reader_process_sigterm_handler */
+ memset(&act, 0, sizeof (act)); /* define SIGTERM-handler */
+ act.sa_handler = reader_process_sigterm_handler;
+ sigaction(SIGTERM, &act, 0);
+ }
+ else
+ {
+ DBG(DBG_sane_proc,"reader_process started (as thread)\n");
+ }
+
+
+ scanner->device->scsi_maxqueue = scanner->device->request_scsi_maxqueue;
+
+ if (scanner->device->request_scsi_maxqueue > 1)
+ {
+ for (i = 1; i<SANE_UMAX_SCSI_MAXQUEUE; i++)
+ {
+ if (scanner->device->buffer[i])
+ {
+ DBG(DBG_info, "reader_process: freeing SCSI buffer[%d]\n", i);
+ free(scanner->device->buffer[i]); /* free buffer */
+ scanner->device->buffer[i] = NULL;
+ }
+ }
+
+ for (i = 1; i<scanner->device->request_scsi_maxqueue; i++)
+ {
+ DBG(DBG_info, "reader_process: allocating SCSI buffer[%d]\n", i);
+ scanner->device->buffer[i] = malloc(scanner->device->bufsize); /* allocate buffer */
+
+ if (!scanner->device->buffer[i]) /* malloc failed */
+ {
+ DBG(DBG_warning, "WARNING: reader_process: only allocated %d/%d scsi buffers\n", i, scanner->device->request_scsi_maxqueue);
+ scanner->device->scsi_maxqueue = i;
+ break; /* leave for loop */
+ }
+ }
+ }
+
+ data_length = scanner->params.lines * scanner->params.bytes_per_line;
+
+ fp = fdopen(scanner->pipe_write_fd, "w");
+ if (!fp)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG(DBG_sane_info,"reader_process: starting to READ data\n");
+
+ status = umax_reader_process(scanner->device, fp, data_length);
+ fclose(fp); /* close write end of pipe */
+
+ for (i = 1; i<scanner->device->request_scsi_maxqueue; i++)
+ {
+ if (scanner->device->buffer[i])
+ {
+ DBG(DBG_info, "reader_process: freeing SCSI buffer[%d]\n", i);
+ free(scanner->device->buffer[i]); /* free buffer */
+ scanner->device->buffer[i] = NULL;
+ }
+ }
+ DBG(DBG_sane_info,"reader_process: finished reading data\n");
+
+ return status;
+}
+
+
+/* ------------------------------------------------------------ INIT OPTIONS ------------------------------- */
+
+
+static SANE_Status init_options(Umax_Scanner *scanner)
+{
+ int i;
+ int scan_modes;
+ int bit_depths;
+
+ DBG(DBG_sane_proc,"init_options\n");
+
+ memset(scanner->opt, 0, sizeof (scanner->opt));
+ memset(scanner->val, 0, sizeof (scanner->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ scanner->opt[i].size = sizeof (SANE_Word);
+ scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ scanner->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; /* empty string */
+ scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ scanner->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ scanner->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
+ scanner->opt[OPT_MODE_GROUP].desc = "";
+ scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_MODE_GROUP].cap = 0;
+ scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ scan_modes = -1;
+
+ if (scanner->device->inquiry_lineart)
+ {
+ scan_mode_list[++scan_modes] = LINEART_STR;
+ }
+
+ if (scanner->device->inquiry_halftone)
+ {
+ scan_mode_list[++scan_modes]= HALFTONE_STR;
+ }
+
+ if (scanner->device->inquiry_gray)
+ {
+ scan_mode_list[++scan_modes]= GRAY_STR;
+ }
+
+ if (scanner->device->inquiry_color)
+ {
+/*
+ if (scanner->device->inquiry_lineart)
+ { scan_mode_list[++scan_modes]= COLOR_LINEART_STR; }
+
+ if (scanner->device->inquiry_halftone)
+ { scan_mode_list[++scan_modes]= COLOR_HALFTONE_STR; }
+*/
+ scan_mode_list[++scan_modes]= COLOR_STR;
+ }
+
+ scan_mode_list[scan_modes + 1] = 0;
+
+ {
+ int i=0;
+ source_list[i++]= FLB_STR;
+
+ if (scanner->device->inquiry_adfmode)
+ {
+ source_list[i++] = ADF_STR;
+ }
+
+ if (scanner->device->inquiry_transavail)
+ {
+ source_list[i++] = UTA_STR;
+ }
+
+ source_list[i] = 0;
+ }
+
+ /* scan mode */
+ scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_MODE].size = max_string_size((SANE_String_Const *) scan_mode_list);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list = (SANE_String_Const *) scan_mode_list;
+ scanner->val[OPT_MODE].s = (SANE_Char*)strdup(scan_mode_list[0]);
+
+ /* source */
+ scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_SOURCE].size = max_string_size(source_list);
+ scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
+ scanner->val[OPT_SOURCE].s = (SANE_Char*)strdup(source_list[0]);
+
+ /* x-resolution */
+ scanner->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_X_RESOLUTION].constraint.range = &scanner->device->x_dpi_range;
+ scanner->val[OPT_X_RESOLUTION].w = 100 << SANE_FIXED_SCALE_SHIFT;
+
+ /* y-resolution */
+ scanner->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ scanner->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_Y_RESOLUTION].constraint.range = &scanner->device->y_dpi_range;
+ scanner->val[OPT_Y_RESOLUTION].w = 100 << SANE_FIXED_SCALE_SHIFT;
+ scanner->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+
+ /* bind resolution */
+ scanner->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
+ scanner->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
+ scanner->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
+ scanner->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_RESOLUTION_BIND].w = SANE_TRUE;
+ if (scanner->device->common_xy_resolutions) /* disable bind if x and y res have to be the same */
+ {
+ scanner->opt[OPT_RESOLUTION_BIND].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* negative */
+ scanner->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ scanner->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ scanner->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ scanner->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_reverse_multi == 0)
+ {
+ scanner->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* "Geometry" group: */
+ scanner->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
+ scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
+ scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_X].constraint.range = &(scanner->device->x_range);
+ scanner->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_X].constraint.range = &(scanner->device->x_range);
+ scanner->val[OPT_BR_X].w = scanner->device->x_range.max;
+
+ /* bottom-right y */
+ scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_BR_Y].w = scanner->device->y_range.max;
+
+ /* ------------------------------ */
+
+
+ /* "Enhancement" group: */
+ scanner->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
+ scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ /* bit depth */
+ bit_depths = 0;
+
+ if (scanner->device->inquiry_GOB & 1)
+ {
+ bit_depth_list[++bit_depths] = 8;
+ }
+
+ if (scanner->device->inquiry_GOB & 2)
+ {
+ bit_depth_list[++bit_depths] = 9;
+ }
+
+ if (scanner->device->inquiry_GOB & 4)
+ {
+ bit_depth_list[++bit_depths] = 10;
+ }
+
+ if (scanner->device->inquiry_GOB & 8)
+ {
+ bit_depth_list[++bit_depths] = 12;
+ }
+
+ if (scanner->device->inquiry_GOB & 16)
+ {
+ bit_depth_list[++bit_depths] = 14;
+ }
+
+ if (scanner->device->inquiry_GOB & 32)
+ {
+ bit_depth_list[++bit_depths] = 16;
+ }
+
+ bit_depth_list[0] = bit_depths;
+
+ scanner->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ scanner->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
+ scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list;
+ scanner->val[OPT_BIT_DEPTH].w = bit_depth_list[1];
+
+
+ /* quality-calibration */
+ scanner->opt[OPT_QUALITY].name = SANE_NAME_QUALITY_CAL;
+ scanner->opt[OPT_QUALITY].title = SANE_TITLE_QUALITY_CAL;
+ scanner->opt[OPT_QUALITY].desc = SANE_DESC_QUALITY_CAL;
+ scanner->opt[OPT_QUALITY].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_QUALITY].w = SANE_FALSE;
+
+ if ((scanner->device->inquiry_quality_ctrl == 0) || (scanner->device->force_quality_calibration) )
+ {
+ scanner->opt[OPT_QUALITY].cap |= SANE_CAP_INACTIVE;
+ }
+ else /* enable quality calibration when available */
+ {
+ scanner->val[OPT_QUALITY].w = SANE_TRUE;
+ }
+
+
+ /* double optical resolution */
+ scanner->opt[OPT_DOR].name = SANE_NAME_DOR;
+ scanner->opt[OPT_DOR].title = SANE_TITLE_DOR;
+ scanner->opt[OPT_DOR].desc = SANE_DESC_DOR;
+ scanner->opt[OPT_DOR].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_DOR].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_dor == 0)
+ {
+ scanner->opt[OPT_DOR].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* warmup */
+ scanner->opt[OPT_WARMUP].name = SANE_NAME_WARMUP;
+ scanner->opt[OPT_WARMUP].title = SANE_TITLE_WARMUP;
+ scanner->opt[OPT_WARMUP].desc = SANE_DESC_WARMUP;
+ scanner->opt[OPT_WARMUP].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_WARMUP].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_max_warmup_time == 0)
+ {
+ scanner->opt[OPT_WARMUP].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_RGB_BIND].name = SANE_NAME_RGB_BIND;
+ scanner->opt[OPT_RGB_BIND].title = SANE_TITLE_RGB_BIND;
+ scanner->opt[OPT_RGB_BIND].desc = SANE_DESC_RGB_BIND;
+ scanner->opt[OPT_RGB_BIND].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_RGB_BIND].w = SANE_FALSE;
+
+ /* brightness */
+ scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
+ scanner->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CONTRAST].constraint.range = &percentage_range;
+ scanner->val[OPT_CONTRAST].w = 0;
+
+
+ /* threshold */
+ scanner->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ scanner->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_THRESHOLD].constraint.range = &percentage_range_100;
+ scanner->val[OPT_THRESHOLD].w = SANE_FIX(50);
+
+
+ /* ------------------------------ */
+
+
+ /* highlight, white level */
+ scanner->opt[OPT_HIGHLIGHT].name = SANE_NAME_HIGHLIGHT;
+ scanner->opt[OPT_HIGHLIGHT].title = SANE_TITLE_HIGHLIGHT;
+ scanner->opt[OPT_HIGHLIGHT].desc = SANE_DESC_HIGHLIGHT;
+ scanner->opt[OPT_HIGHLIGHT].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT].w = SANE_FIX(100);
+
+ scanner->opt[OPT_HIGHLIGHT_R].name = SANE_NAME_HIGHLIGHT_R;
+ scanner->opt[OPT_HIGHLIGHT_R].title = SANE_TITLE_HIGHLIGHT_R;
+ scanner->opt[OPT_HIGHLIGHT_R].desc = SANE_DESC_HIGHLIGHT_R;
+ scanner->opt[OPT_HIGHLIGHT_R].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT_R].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT_R].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT_R].w = SANE_FIX(100);
+
+ scanner->opt[OPT_HIGHLIGHT_G].name = SANE_NAME_HIGHLIGHT_G;
+ scanner->opt[OPT_HIGHLIGHT_G].title = SANE_TITLE_HIGHLIGHT_G;
+ scanner->opt[OPT_HIGHLIGHT_G].desc = SANE_DESC_HIGHLIGHT_G;
+ scanner->opt[OPT_HIGHLIGHT_G].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT_G].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT_G].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT_G].w = SANE_FIX(100);
+
+ scanner->opt[OPT_HIGHLIGHT_B].name = SANE_NAME_HIGHLIGHT_B;
+ scanner->opt[OPT_HIGHLIGHT_B].title = SANE_TITLE_HIGHLIGHT_B;
+ scanner->opt[OPT_HIGHLIGHT_B].desc = SANE_DESC_HIGHLIGHT_B;
+ scanner->opt[OPT_HIGHLIGHT_B].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_HIGHLIGHT_B].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_HIGHLIGHT_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HIGHLIGHT_B].constraint.range = &percentage_range_100;
+ scanner->val[OPT_HIGHLIGHT_B].w = SANE_FIX(100);
+
+
+ /* shadow, black level */
+ scanner->opt[OPT_SHADOW].name = SANE_NAME_SHADOW;
+ scanner->opt[OPT_SHADOW].title = SANE_TITLE_SHADOW;
+ scanner->opt[OPT_SHADOW].desc = SANE_DESC_SHADOW;
+ scanner->opt[OPT_SHADOW].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW].w = 0;
+
+ scanner->opt[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R;
+ scanner->opt[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R;
+ scanner->opt[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R;
+ scanner->opt[OPT_SHADOW_R].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW_R].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW_R].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW_R].w = 0;
+
+ scanner->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW_G;
+ scanner->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW_G;
+ scanner->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW_G;
+ scanner->opt[OPT_SHADOW_G].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW_G].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW_G].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW_G].w = 0;
+
+ scanner->opt[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B;
+ scanner->opt[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B;
+ scanner->opt[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B;
+ scanner->opt[OPT_SHADOW_B].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SHADOW_B].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SHADOW_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SHADOW_B].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SHADOW_B].w = 0;
+
+
+
+ /* analog-gamma */
+ scanner->opt[OPT_ANALOG_GAMMA].name = SANE_NAME_ANALOG_GAMMA;
+ scanner->opt[OPT_ANALOG_GAMMA].title = SANE_TITLE_ANALOG_GAMMA;
+ scanner->opt[OPT_ANALOG_GAMMA].desc = SANE_DESC_ANALOG_GAMMA;
+ scanner->opt[OPT_ANALOG_GAMMA].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ scanner->opt[OPT_ANALOG_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R;
+ scanner->opt[OPT_ANALOG_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R;
+ scanner->opt[OPT_ANALOG_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R;
+ scanner->opt[OPT_ANALOG_GAMMA_R].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA_R].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA_R].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ scanner->opt[OPT_ANALOG_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G;
+ scanner->opt[OPT_ANALOG_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G;
+ scanner->opt[OPT_ANALOG_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G;
+ scanner->opt[OPT_ANALOG_GAMMA_G].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA_G].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA_G].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ scanner->opt[OPT_ANALOG_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B;
+ scanner->opt[OPT_ANALOG_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B;
+ scanner->opt[OPT_ANALOG_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B;
+ scanner->opt[OPT_ANALOG_GAMMA_B].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_ANALOG_GAMMA_B].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].constraint.range = &(scanner->device->analog_gamma_range);
+ scanner->val[OPT_ANALOG_GAMMA_B].w = 1 << SANE_FIXED_SCALE_SHIFT;
+
+
+ /* custom-gamma table */
+ scanner->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ scanner->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ scanner->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ scanner->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_CUSTOM_GAMMA].w = SANE_TRUE;
+
+ /* grayscale gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR].wa = scanner->gamma_table[0];
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &scanner->output_range;
+ scanner->opt[OPT_GAMMA_VECTOR].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ scanner->output_range.min = 0;
+
+ if (bit_depth_list[1] == 8)
+ {
+ scanner->output_range.max = 255; /* 8 bits/pixel */
+ }
+ else
+ {
+ scanner->output_range.max = 65535; /* 9-16 bits/pixel */
+ }
+
+ scanner->output_range.quant = 0;
+
+ /* red gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_R].wa = scanner->gamma_table[1];
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_R].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ /* green gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_G].wa = scanner->gamma_table[2];
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_G].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ /* blue gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->val[OPT_GAMMA_VECTOR_B].wa = scanner->gamma_table[3];
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(scanner->gamma_range);
+ scanner->opt[OPT_GAMMA_VECTOR_B].size = scanner->gamma_length * sizeof(SANE_Word);
+
+ /* halftone dimension */
+ scanner->opt[OPT_HALFTONE_DIMENSION].name = SANE_NAME_HALFTONE_DIMENSION;
+ scanner->opt[OPT_HALFTONE_DIMENSION].title = SANE_TITLE_HALFTONE_DIMENSION;
+ scanner->opt[OPT_HALFTONE_DIMENSION].desc = SANE_DESC_HALFTONE_DIMENSION;
+ scanner->opt[OPT_HALFTONE_DIMENSION].type = SANE_TYPE_INT;
+ scanner->opt[OPT_HALFTONE_DIMENSION].unit = SANE_UNIT_PIXEL;
+ scanner->opt[OPT_HALFTONE_DIMENSION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_HALFTONE_DIMENSION].constraint.word_list = pattern_dim_list;
+ scanner->val[OPT_HALFTONE_DIMENSION].w = pattern_dim_list[1];
+
+ /* halftone pattern */
+ scanner->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ scanner->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT;
+ scanner->opt[OPT_HALFTONE_PATTERN].size = 0;
+ scanner->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_HALFTONE_PATTERN].constraint.range = &u8_range;
+ scanner->val[OPT_HALFTONE_PATTERN].wa = scanner->halftone_pattern;
+
+
+ /* ------------------------------ */
+
+
+ /* "Advanced" group: */
+ scanner->opt[OPT_ADVANCED_GROUP].title = SANE_I18N("Advanced");
+ scanner->opt[OPT_ADVANCED_GROUP].desc = "";
+ scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ /* ------------------------------ */
+
+
+ /* select exposure time */
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].name = SANE_NAME_SELECT_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].title = SANE_TITLE_SELECT_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].desc = SANE_DESC_SELECT_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_SELECT_EXPOSURE_TIME].w = SANE_FALSE;
+
+ /* select calibration exposure time */
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].name = SANE_UMAX_NAME_SELECT_CALIBRATON_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].title = SANE_UMAX_TITLE_SELECT_CALIBRATION_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].desc = SANE_UMAX_DESC_SELECT_CALIBRATION_EXPOSURE_TIME;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w = SANE_FALSE;
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time */
+ scanner->opt[OPT_CAL_EXPOS_TIME].name = SANE_NAME_CAL_EXPOS_TIME;
+ scanner->opt[OPT_CAL_EXPOS_TIME].title = SANE_TITLE_CAL_EXPOS_TIME;
+ scanner->opt[OPT_CAL_EXPOS_TIME].desc = SANE_DESC_CAL_EXPOS_TIME;
+ scanner->opt[OPT_CAL_EXPOS_TIME].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME].w = scanner->device->inquiry_exposure_time_g_fb_def *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time red */
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].name = SANE_NAME_CAL_EXPOS_TIME_R;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].title = SANE_TITLE_CAL_EXPOS_TIME_R;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].desc = SANE_DESC_CAL_EXPOS_TIME_R;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME_R].w = scanner->device->inquiry_exposure_time_c_fb_def_r *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time green */
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].name = SANE_NAME_CAL_EXPOS_TIME_G;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].title = SANE_TITLE_CAL_EXPOS_TIME_G;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].desc = SANE_DESC_CAL_EXPOS_TIME_G;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME_G].w = scanner->device->inquiry_exposure_time_c_fb_def_g *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration exposure time blue */
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].name = SANE_NAME_CAL_EXPOS_TIME_B;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].title = SANE_TITLE_CAL_EXPOS_TIME_B;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].desc = SANE_DESC_CAL_EXPOS_TIME_B;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_CAL_EXPOS_TIME_B].w = scanner->device->inquiry_exposure_time_c_fb_def_b *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time */
+ scanner->opt[OPT_SCAN_EXPOS_TIME].name = SANE_NAME_SCAN_EXPOS_TIME;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].title = SANE_TITLE_SCAN_EXPOS_TIME;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].desc = SANE_DESC_SCAN_EXPOS_TIME;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME].w = scanner->device->inquiry_exposure_time_g_fb_def *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time red */
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].name = SANE_NAME_SCAN_EXPOS_TIME_R;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].title = SANE_TITLE_SCAN_EXPOS_TIME_R;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].desc = SANE_DESC_SCAN_EXPOS_TIME_R;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME_R].w = scanner->device->inquiry_exposure_time_c_fb_def_r *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time green */
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].name = SANE_NAME_SCAN_EXPOS_TIME_G;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].title = SANE_TITLE_SCAN_EXPOS_TIME_G;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].desc = SANE_DESC_SCAN_EXPOS_TIME_G;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME_G].w = scanner->device->inquiry_exposure_time_c_fb_def_g *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+
+ /* scan exposure time blue */
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].name = SANE_NAME_SCAN_EXPOS_TIME_B;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].title = SANE_TITLE_SCAN_EXPOS_TIME_B;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].desc = SANE_DESC_SCAN_EXPOS_TIME_B;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].unit = SANE_UNIT_MICROSECOND;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].constraint.range = &(scanner->exposure_time_range);
+ scanner->val[OPT_SCAN_EXPOS_TIME_B].w = scanner->device->inquiry_exposure_time_c_fb_def_b *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_exposure_adj == 0)
+ {
+ scanner->opt[OPT_SELECT_EXPOSURE_TIME].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ /* ------------------------------ */
+
+
+ /* select calibration lamp density */
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].name = SANE_NAME_SELECT_LAMP_DENSITY;
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].title = SANE_TITLE_SELECT_LAMP_DENSITY;
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].desc = SANE_DESC_SELECT_LAMP_DENSITY;
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_SELECT_LAMP_DENSITY].w = SANE_FALSE;
+
+ /* calibration lamp density */
+ scanner->opt[OPT_CAL_LAMP_DEN].name = SANE_NAME_CAL_LAMP_DEN;
+ scanner->opt[OPT_CAL_LAMP_DEN].title = SANE_TITLE_CAL_LAMP_DEN;
+ scanner->opt[OPT_CAL_LAMP_DEN].desc = SANE_DESC_CAL_LAMP_DEN;
+ scanner->opt[OPT_CAL_LAMP_DEN].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_CAL_LAMP_DEN].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_CAL_LAMP_DEN].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CAL_LAMP_DEN].constraint.range = &percentage_range_100;
+ scanner->val[OPT_CAL_LAMP_DEN].w = SANE_FIX(50);
+ scanner->opt[OPT_CAL_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+
+ /* scan lamp density */
+ scanner->opt[OPT_SCAN_LAMP_DEN].name = SANE_NAME_SCAN_LAMP_DEN;
+ scanner->opt[OPT_SCAN_LAMP_DEN].title = SANE_TITLE_SCAN_LAMP_DEN;
+ scanner->opt[OPT_SCAN_LAMP_DEN].desc = SANE_DESC_SCAN_LAMP_DEN;
+ scanner->opt[OPT_SCAN_LAMP_DEN].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_SCAN_LAMP_DEN].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_SCAN_LAMP_DEN].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_SCAN_LAMP_DEN].constraint.range = &percentage_range_100;
+ scanner->val[OPT_SCAN_LAMP_DEN].w = SANE_FIX(50);
+ scanner->opt[OPT_SCAN_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_lamp_ctrl == 0)
+ {
+ scanner->opt[OPT_SELECT_LAMP_DENSITY].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* disable pre focus */
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].name = "disable-pre-focus";
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].title = SANE_I18N("Disable pre focus");
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].desc = SANE_I18N("Do not calibrate focus");
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_DISABLE_PRE_FOCUS].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_manual_focus == 0)
+ {
+ scanner->opt[OPT_DISABLE_PRE_FOCUS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* manual pre focus */
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].name = "manual-pre-focus";
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].title = SANE_I18N("Manual pre focus");
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].desc = "";
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_MANUAL_PRE_FOCUS].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_manual_focus == 0)
+ {
+ scanner->opt[OPT_MANUAL_PRE_FOCUS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* fix focus position */
+ scanner->opt[OPT_FIX_FOCUS_POSITION].name = "fix-focus-position";
+ scanner->opt[OPT_FIX_FOCUS_POSITION].title = SANE_I18N("Fix focus position");
+ scanner->opt[OPT_FIX_FOCUS_POSITION].desc = "";
+ scanner->opt[OPT_FIX_FOCUS_POSITION].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_FIX_FOCUS_POSITION].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_manual_focus == 0)
+ {
+ scanner->opt[OPT_FIX_FOCUS_POSITION].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* lens calibration in doc position */
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].name = "lens-calibration-in-doc-position";
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].title = SANE_I18N("Lens calibration in doc position");
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].desc = SANE_I18N("Calibrate lens focus in document position");
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_LENS_CALIBRATION_DOC_POS].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_lens_cal_in_doc_pos == 0)
+ {
+ scanner->opt[OPT_LENS_CALIBRATION_DOC_POS].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* 0mm holder focus position */
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].name = "holder-focus-position-0mm";
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].title = SANE_I18N("Holder focus position 0mm");
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].desc = SANE_I18N("Use 0mm holder focus position instead of 0.6mm");
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_HOLDER_FOCUS_POS_0MM].w = SANE_FALSE;
+
+ if (scanner->device->inquiry_sel_uta_lens_cal_pos == 0)
+ {
+ scanner->opt[OPT_HOLDER_FOCUS_POS_0MM].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* lamp on */
+ scanner->opt[OPT_LAMP_ON].name = "lamp-on";
+ scanner->opt[OPT_LAMP_ON].title = SANE_I18N("Lamp on");
+ scanner->opt[OPT_LAMP_ON].desc = SANE_I18N("Turn on scanner lamp");
+ scanner->opt[OPT_LAMP_ON].type = SANE_TYPE_BUTTON;
+ scanner->opt[OPT_LAMP_ON].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_LAMP_ON].size = 0;
+ scanner->opt[OPT_LAMP_ON].constraint_type = SANE_CONSTRAINT_NONE;
+ scanner->val[OPT_LAMP_ON].w = 0;
+
+ if (!scanner->device->lamp_control_available) /* lamp state can not be controlled by driver */
+ {
+ scanner->opt[OPT_LAMP_ON].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* lamp off */
+ scanner->opt[OPT_LAMP_OFF].name = "lamp-off";
+ scanner->opt[OPT_LAMP_OFF].title = SANE_I18N("Lamp off");
+ scanner->opt[OPT_LAMP_OFF].desc = SANE_I18N("Turn off scanner lamp");
+ scanner->opt[OPT_LAMP_OFF].type = SANE_TYPE_BUTTON;
+ scanner->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_LAMP_OFF].size = 0;
+ scanner->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE;
+ scanner->val[OPT_LAMP_OFF].w = 0;
+
+ if (!scanner->device->lamp_control_available) /* lamp state can not be controlled by driver */
+ {
+ scanner->opt[OPT_LAMP_OFF].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* lamp off at exit */
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].name = "lamp-off-at-exit";
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].title = SANE_I18N("Lamp off at exit");
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].desc = SANE_I18N("Turn off lamp when program exits");
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_LAMP_OFF_AT_EXIT].w = SANE_FALSE;
+
+ if (!scanner->device->lamp_control_available) /* lamp state can not be controlled by driver */
+ {
+ scanner->opt[OPT_LAMP_OFF_AT_EXIT].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+ /* batch-scan-start */
+ scanner->opt[OPT_BATCH_SCAN_START].name = SANE_NAME_BATCH_SCAN_START;
+ scanner->opt[OPT_BATCH_SCAN_START].title = SANE_TITLE_BATCH_SCAN_START;
+ scanner->opt[OPT_BATCH_SCAN_START].desc = SANE_DESC_BATCH_SCAN_START;
+ scanner->opt[OPT_BATCH_SCAN_START].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_BATCH_SCAN_START].w = SANE_FALSE;
+
+ /* batch-scan-loop */
+ scanner->opt[OPT_BATCH_SCAN_LOOP].name = SANE_NAME_BATCH_SCAN_LOOP;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].title = SANE_TITLE_BATCH_SCAN_LOOP;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].desc = SANE_DESC_BATCH_SCAN_LOOP;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_BATCH_SCAN_LOOP].w = SANE_FALSE;
+
+ /* batch-scan-end */
+ scanner->opt[OPT_BATCH_SCAN_END].name = SANE_NAME_BATCH_SCAN_END;
+ scanner->opt[OPT_BATCH_SCAN_END].title = SANE_TITLE_BATCH_SCAN_END;
+ scanner->opt[OPT_BATCH_SCAN_END].desc = SANE_DESC_BATCH_SCAN_END;
+ scanner->opt[OPT_BATCH_SCAN_END].type = SANE_TYPE_BOOL;
+ scanner->val[OPT_BATCH_SCAN_END].w = SANE_FALSE;
+
+ /* batch-scan-next-y */
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].name = SANE_NAME_BATCH_NEXT_TL_Y;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].title = SANE_TITLE_BATCH_NEXT_TL_Y;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].desc = SANE_DESC_BATCH_NEXT_TL_Y;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].constraint.range = &(scanner->device->y_range);
+ scanner->val[OPT_BATCH_NEXT_TL_Y].w = 0xFFFF; /* mark value as not touched */
+
+ if (scanner->device->inquiry_batch_scan == 0)
+ {
+ scanner->opt[OPT_BATCH_SCAN_START].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BATCH_SCAN_LOOP].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BATCH_SCAN_END].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BATCH_NEXT_TL_Y].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* ------------------------------ */
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ /* calibration mode */
+ scanner->opt[OPT_CALIB_MODE].name = "calibrationmode";
+ scanner->opt[OPT_CALIB_MODE].title = SANE_I18N("Calibration mode");
+ scanner->opt[OPT_CALIB_MODE].desc = SANE_I18N("Define calibration mode");
+ scanner->opt[OPT_CALIB_MODE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_CALIB_MODE].size = max_string_size(calibration_list);
+ scanner->opt[OPT_CALIB_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_CALIB_MODE].constraint.string_list = calibration_list;
+ scanner->val[OPT_CALIB_MODE].s = (SANE_Char*)strdup(calibration_list[0]);
+
+ if (scanner->device->inquiry_calibration == 0)
+ {
+ scanner->opt[OPT_CALIB_MODE].cap |= SANE_CAP_INACTIVE;
+ }
+#endif
+
+ /* preview */
+ scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ scanner->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ sane_control_option(scanner, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String *) scan_mode_list[scan_modes], NULL );
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ ATTACH ONE SCSI ----------------------------- */
+
+/* callback function for sanei_config_attach_matching_devices(dev_name, attach_one_scsi) */
+static SANE_Status attach_one_scsi(const char *name)
+{
+ attach_scanner(name, 0, SANE_UMAX_SCSI);
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ ATTACH ONE USB ------------------------------ */
+
+/* callback function for sanei_usb_attach_matching_devices(dev_name, attach_one_usb) */
+static SANE_Status attach_one_usb(const char *name)
+{
+ attach_scanner(name, 0, SANE_UMAX_USB);
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ UMAX TEST CONFIGURE OPTION ------------------ */
+
+static SANE_Status umax_test_configure_option(const char *option_str, char *test_name, int *test_value, int test_min, int test_max)
+/* returns with 1 if option was found, 0 if option was not found */
+{
+ const char *value_str;
+ char *end_ptr;
+ int value;
+
+ if (strncmp(option_str, test_name, strlen(test_name)) == 0)
+ {
+ value_str = sanei_config_skip_whitespace(option_str+strlen(test_name));
+
+ errno = 0;
+ value = strtol(value_str, &end_ptr, 10);
+ if (end_ptr == value_str || errno)
+ {
+ DBG(DBG_error, "ERROR: invalid value \"%s\" for option %s in %s\n", value_str, test_name, UMAX_CONFIG_FILE);
+ }
+ else
+ {
+ if (value < test_min)
+ {
+ DBG(DBG_error, "ERROR: value \"%d\" is too small for option %s in %s\n", value, test_name, UMAX_CONFIG_FILE);
+ value = test_min;
+ }
+ else if (value > test_max)
+ {
+ DBG(DBG_error, "ERROR: value \"%d\" is too large for option %s in %s\n", value, test_name, UMAX_CONFIG_FILE);
+ value = test_max;
+ }
+
+ *test_value = value;
+
+ DBG(DBG_info, "option %s = %d\n", test_name, *test_value);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------ SANE INIT ---------------------------------- */
+
+
+SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char config_line[PATH_MAX];
+ const char *option_str;
+ size_t len;
+ FILE *fp;
+
+ /* we have to initialize these global variables here because sane_init can be called several times */
+ num_devices = 0;
+ devlist = NULL;
+ first_dev = NULL;
+ first_handle = NULL;
+
+ DBG_INIT();
+
+ DBG(DBG_sane_init,"sane_init\n");
+ DBG(DBG_error,"This is sane-umax version %d.%d build %d\n", SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+#ifdef UMAX_ENABLE_USB
+ DBG(DBG_error,"compiled with USB support for Astra 2200\n");
+#else
+ DBG(DBG_error,"no USB support for Astra 2200\n");
+#endif
+ DBG(DBG_error,"(C) 1997-2002 by Oliver Rauch\n");
+ DBG(DBG_error,"EMAIL: Oliver.Rauch@rauch-domain.de\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ frontend_authorize_callback = authorize; /* store frontend authorize callback */
+
+ sanei_thread_init(); /* must be called before any other sanei_thread call */
+
+#ifdef UMAX_ENABLE_USB
+ sanei_usb_init();
+ sanei_pv8630_init();
+#endif
+
+ fp = sanei_config_open(UMAX_CONFIG_FILE);
+ if (!fp)
+ {
+ /* no config-file: try /dev/scanner and /dev/usbscanner. */
+ attach_scanner("/dev/scanner", 0, SANE_UMAX_SCSI);
+#ifdef UMAX_ENABLE_USB
+ attach_scanner("/dev/usbscanner", 0, SANE_UMAX_USB);
+#endif
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG(DBG_info, "reading configure file %s\n", UMAX_CONFIG_FILE);
+
+ while(sanei_config_read(config_line, sizeof(config_line), fp))
+ {
+ if (config_line[0] == '#')
+ {
+ continue; /* ignore line comments */
+ }
+
+ if (strncmp(config_line, "option", 6) == 0)
+ {
+ option_str = sanei_config_skip_whitespace(config_line+6);
+
+ if (umax_test_configure_option(option_str, "scsi-maxqueue", &umax_scsi_maxqueue, 1, SANE_UMAX_SCSI_MAXQUEUE));
+ else if (umax_test_configure_option(option_str, "scsi-buffer-size-min", &umax_scsi_buffer_size_min, 4096, 1048576));
+ else if (umax_test_configure_option(option_str, "scsi-buffer-size-max", &umax_scsi_buffer_size_max, 4096, 1048576));
+ else if (umax_test_configure_option(option_str, "preview-lines", &umax_preview_lines, 1, 65535));
+ else if (umax_test_configure_option(option_str, "scan-lines", &umax_scan_lines, 1, 65535));
+ else if (umax_test_configure_option(option_str, "handle-bad-sense-error", &umax_handle_bad_sense_error, 0, 3));
+ else if (umax_test_configure_option(option_str, "execute-request-sense", &umax_execute_request_sense, 0, 1));
+ else if (umax_test_configure_option(option_str, "force-preview-bit-rgb", &umax_force_preview_bit_rgb, 0, 1));
+ else if (umax_test_configure_option(option_str, "slow-speed", &umax_slow, -1, 1));
+ else if (umax_test_configure_option(option_str, "care-about-smearing", &umax_smear, -1, 1));
+ else if (umax_test_configure_option(option_str, "calibration-full-ccd", &umax_calibration_area, -1, 1));
+ else if (umax_test_configure_option(option_str, "calibration-width-offset-batch", &umax_calibration_width_offset_batch, -99999, 65535));
+ else if (umax_test_configure_option(option_str, "calibration-width-offset", &umax_calibration_width_offset, -99999, 65535));
+ else if (umax_test_configure_option(option_str, "calibration-bytes-pixel", &umax_calibration_bytespp, -1, 2));
+ else if (umax_test_configure_option(option_str, "exposure-time-rgb-bind", &umax_exposure_time_rgb_bind, -1, 1));
+ else if (umax_test_configure_option(option_str, "invert-shading-data", &umax_invert_shading_data, -1, 1));
+ else if (umax_test_configure_option(option_str, "lamp-control-available", &umax_lamp_control_available, 0, 1));
+ else if (umax_test_configure_option(option_str, "gamma-lsb-padded", &umax_gamma_lsb_padded, -1, 1));
+ else if (umax_test_configure_option(option_str, "connection-type", &umax_connection_type, 1, 2));
+ else
+ {
+ DBG(DBG_error,"ERROR: unknown option \"%s\" in %s\n", option_str, UMAX_CONFIG_FILE);
+ }
+ continue;
+ }
+ else if (strncmp(config_line, "scsi", 4) == 0)
+ {
+ DBG(DBG_info,"sanei_config_attach_matching_devices(%s)\n", config_line);
+ sanei_config_attach_matching_devices(config_line, attach_one_scsi);
+ continue;
+ }
+ else if (strncmp(config_line, "usb", 3) == 0)
+ {
+#ifdef UMAX_ENABLE_USB
+ DBG(DBG_info,"sanei_usb_attach_matching_devices(%s)\n", config_line);
+ sanei_usb_attach_matching_devices(config_line, attach_one_usb);
+#else
+ DBG(DBG_info,"USB not supported, ignoring config line: %s\n", config_line);
+#endif
+ continue;
+ }
+
+ len = strlen(config_line);
+
+ if (!len) /* ignore empty lines */
+ {
+ continue;
+ }
+
+ /* umax_connection_type is set by umax.conf: 1=scsi, 2=usb */
+ attach_scanner(config_line, 0, umax_connection_type); /* try to open as devicename */
+ }
+
+ DBG(DBG_info, "finished reading configure file\n");
+
+ fclose(fp);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE EXIT ---------------------------------- */
+
+
+void sane_exit(void)
+{
+ Umax_Device *dev, *next;
+
+ DBG(DBG_sane_init,"sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free(dev->devicename);
+ free(dev);
+ }
+
+ if (devlist)
+ {
+ free(devlist);
+ }
+}
+
+
+/* ------------------------------------------------------------ SANE GET DEVICES --------------------------- */
+
+
+SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ Umax_Device *dev;
+ int i;
+
+ DBG(DBG_sane_init,"sane_get_devices(local_only = %d)\n", local_only);
+
+ if (devlist)
+ {
+ free(devlist);
+ }
+
+ devlist = malloc((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ i = 0;
+
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ {
+ devlist[i++] = &dev->sane;
+ }
+
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE OPEN ---------------------------------- */
+
+
+SANE_Status sane_open(SANE_String_Const devicename, SANE_Handle *handle)
+{
+ Umax_Device *dev;
+ SANE_Status status;
+ Umax_Scanner *scanner;
+ unsigned int i, j;
+
+ DBG(DBG_sane_init,"sane_open\n");
+
+ if (devicename[0]) /* search for devicename */
+ {
+ DBG(DBG_sane_info,"sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp(dev->sane.name, devicename) == 0)
+ {
+ break; /* device found, exit for loop */
+ }
+ }
+
+ if (!dev) /* no device found */
+ {
+ status = attach_scanner(devicename, &dev, 0 /* connection-type not known */); /* try to open devicename and set dev */
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG(DBG_sane_info,"sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev) /* no device found */
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ scanner = malloc(sizeof (*scanner));
+ if (!scanner)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(scanner, 0, sizeof (*scanner));
+
+ scanner->device = dev;
+
+ if (scanner->device->inquiry_GIB & 32)
+ {
+ scanner->gamma_length = 65536; /* 16 bits input */
+ DBG(DBG_sane_info, "Using 16 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 16)
+ {
+ scanner->gamma_length = 16384; /* 14 bits input */
+ DBG(DBG_sane_info, "Using 14 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 8)
+ {
+ scanner->gamma_length = 4096; /* 12 bits input */
+ DBG(DBG_sane_info, "Using 12 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 4)
+ {
+ scanner->gamma_length = 1024; /* 10 bits input */
+ DBG(DBG_sane_info, "Using 10 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 2)
+ {
+ scanner->gamma_length = 512; /* 9 bits input */
+ DBG(DBG_sane_info, "Using 9 bits for gamma input\n");
+ }
+ else
+ {
+ scanner->gamma_length = 256; /* 8 bits input */
+ DBG(DBG_sane_info, "Using 8 bits for gamma input\n");
+ }
+
+ scanner->output_bytes = 1; /* 8 bits output */
+
+ scanner->gamma_range.min = 0;
+ scanner->gamma_range.max = scanner->gamma_length-1;
+ scanner->gamma_range.quant = 0;
+
+ scanner->gamma_table[0] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+ scanner->gamma_table[1] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+ scanner->gamma_table[2] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+ scanner->gamma_table[3] = (SANE_Int *) malloc(scanner->gamma_length * sizeof(SANE_Int));
+
+ for (j = 0; j < scanner->gamma_length; ++j) /* gamma_table[0] : converts GIB to GOB */
+ {
+ scanner->gamma_table[0][j] = j * scanner->device->max_value / scanner->gamma_length;
+ }
+
+ for (i = 1; i < 4; ++i) /* gamma_table[1,2,3] : doesn't convert anything (GIB->GIB) */
+ {
+ for (j = 0; j < scanner->gamma_length; ++j)
+ {
+ scanner->gamma_table[i][j] = j;
+ }
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_c_min *
+ scanner->device->inquiry_exposure_time_step_unit;
+ scanner->exposure_time_range.quant = scanner->device->inquiry_exposure_time_step_unit;
+ scanner->exposure_time_range.max = scanner->device->inquiry_exposure_time_max *
+ scanner->device->inquiry_exposure_time_step_unit;
+
+ init_options(scanner);
+
+ scanner->next = first_handle; /* insert newly opened handle into list of open handles: */
+ first_handle = scanner;
+
+ *handle = scanner;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE CLOSE --------------------------------- */
+
+
+void sane_close(SANE_Handle handle)
+{
+ Umax_Scanner *prev, *scanner;
+
+ DBG(DBG_sane_init,"sane_close\n");
+
+ if (!first_handle)
+ {
+ DBG(DBG_error, "ERROR: sane_close: no handles opened\n");
+ return;
+ }
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+
+ for (scanner = first_handle; scanner; scanner = scanner->next)
+ {
+ if (scanner == handle)
+ {
+ break;
+ }
+
+ prev = scanner;
+ }
+
+ if (!scanner)
+ {
+ DBG(DBG_error, "ERROR: sane_close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (scanner->scanning) /* stop scan if still scanning */
+ {
+ do_cancel(handle);
+ }
+
+ if (scanner->device->lamp_control_available) /* lamp state can be controlled by driver */
+ {
+ if (scanner->val[OPT_LAMP_OFF_AT_EXIT].w) /* turn off scanner lamp on sane_close */
+ {
+ umax_set_lamp_status(handle, 0 /* lamp off */);
+ }
+ }
+
+ if (prev)
+ {
+ prev->next = scanner->next;
+ }
+ else
+ {
+ first_handle = scanner->next;
+ }
+
+ free(scanner->gamma_table[0]); /* free custom gamma tables */
+ free(scanner->gamma_table[1]);
+ free(scanner->gamma_table[2]);
+ free(scanner->gamma_table[3]);
+
+ free(scanner->device->buffer[0]); /* free buffer allocated by umax_initialize_values */
+ scanner->device->buffer[0] = NULL;
+ scanner->device->bufsize = 0;
+
+ free(scanner); /* free scanner */
+}
+
+
+/* ------------------------------------------------------------ SANE GET OPTION DESCRIPTOR ----------------- */
+
+
+const SANE_Option_Descriptor *sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_option,"sane_get_option_descriptor %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ return 0;
+ }
+
+ return scanner->opt + option;
+}
+
+/* ------------------------------------------------------------ UMAX SET MAX GEOMETRY ---------------------- */
+
+static void umax_set_max_geometry(Umax_Scanner *scanner)
+{
+
+ if (scanner->val[OPT_DOR].w)
+ {
+ scanner->device->x_range.min = SANE_FIX(scanner->device->inquiry_dor_x_off * MM_PER_INCH);
+ scanner->device->x_range.max = SANE_FIX( (scanner->device->inquiry_dor_x_off + scanner->device->inquiry_dor_width) * MM_PER_INCH);
+ scanner->device->y_range.min = SANE_FIX(scanner->device->inquiry_dor_y_off * MM_PER_INCH);
+ scanner->device->y_range.max = SANE_FIX( (scanner->device->inquiry_dor_y_off + scanner->device->inquiry_dor_length) * MM_PER_INCH);
+
+ scanner->device->x_dpi_range.max = SANE_FIX(scanner->device->inquiry_dor_x_res);
+ scanner->device->y_dpi_range.max = SANE_FIX(scanner->device->inquiry_dor_y_res);
+ }
+ else if ( (strcmp(scanner->val[OPT_SOURCE].s, FLB_STR) == 0) || (strcmp(scanner->val[OPT_SOURCE].s, ADF_STR) == 0) )
+ {
+ scanner->device->x_range.min = 0;
+ scanner->device->x_range.max = SANE_FIX(scanner->device->inquiry_fb_width * MM_PER_INCH);
+ scanner->device->y_range.min = 0;
+ scanner->device->y_range.max = SANE_FIX(scanner->device->inquiry_fb_length * MM_PER_INCH);
+
+ scanner->device->x_dpi_range.max = SANE_FIX(scanner->device->inquiry_x_res);
+ scanner->device->y_dpi_range.max = SANE_FIX(scanner->device->inquiry_y_res);
+ }
+ else if (strcmp(scanner->val[OPT_SOURCE].s, UTA_STR) == 0)
+ {
+ scanner->device->x_range.min = SANE_FIX(scanner->device->inquiry_uta_x_off * MM_PER_INCH);
+ scanner->device->x_range.max = SANE_FIX( (scanner->device->inquiry_uta_x_off + scanner->device->inquiry_uta_width) * MM_PER_INCH);
+ scanner->device->y_range.min = SANE_FIX(scanner->device->inquiry_uta_y_off * MM_PER_INCH);
+ scanner->device->y_range.max = SANE_FIX( ( scanner->device->inquiry_uta_y_off + scanner->device->inquiry_uta_length) * MM_PER_INCH);
+
+ scanner->device->x_dpi_range.max = SANE_FIX(scanner->device->inquiry_x_res);
+ scanner->device->y_dpi_range.max = SANE_FIX(scanner->device->inquiry_y_res);
+ }
+
+ DBG(DBG_info,"x_range = [%f .. %f]\n", SANE_UNFIX(scanner->device->x_range.min), SANE_UNFIX(scanner->device->x_range.max));
+ DBG(DBG_info,"y_range = [%f .. %f]\n", SANE_UNFIX(scanner->device->y_range.min), SANE_UNFIX(scanner->device->y_range.max));
+ DBG(DBG_info,"x_dpi_range = [1 .. %f]\n", SANE_UNFIX(scanner->device->x_dpi_range.max));
+ DBG(DBG_info,"y_dpi_range = [1 .. %f]\n", SANE_UNFIX(scanner->device->y_dpi_range.max));
+
+ /* make sure geometry selection is in bounds */
+
+ if ( scanner->val[OPT_TL_X].w < scanner->device->x_range.min)
+ {
+ scanner->val[OPT_TL_X].w = scanner->device->x_range.min;
+ }
+
+ if (scanner->val[OPT_TL_Y].w < scanner->device->y_range.min)
+ {
+ scanner->val[OPT_TL_Y].w = scanner->device->y_range.min;
+ }
+
+ if ( scanner->val[OPT_BR_X].w > scanner->device->x_range.max)
+ {
+ scanner->val[OPT_BR_X].w = scanner->device->x_range.max;
+ }
+
+ if (scanner->val[OPT_BR_Y].w > scanner->device->y_range.max)
+ {
+ scanner->val[OPT_BR_Y].w = scanner->device->y_range.max;
+ }
+}
+
+/* ------------------------------------------------------------ SANE CONTROL OPTION ------------------------ */
+
+
+SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void *val, SANE_Int *info)
+{
+ Umax_Scanner *scanner = handle;
+ SANE_Status status;
+ SANE_Word w, cap;
+ SANE_String_Const name;
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (scanner->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = scanner->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ name = scanner->opt[option].name;
+ if (!name)
+ {
+ name = "(no name)";
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG(DBG_sane_option,"get %s [#%d]\n", name, option);
+
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION_BIND:
+ 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_PREVIEW:
+ case OPT_BIT_DEPTH:
+ case OPT_NEGATIVE:
+ case OPT_BATCH_SCAN_START:
+ case OPT_BATCH_SCAN_LOOP:
+ case OPT_BATCH_SCAN_END:
+ case OPT_BATCH_NEXT_TL_Y:
+ case OPT_QUALITY:
+ case OPT_DOR:
+ case OPT_WARMUP:
+ case OPT_RGB_BIND:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_HALFTONE_DIMENSION:
+ case OPT_SELECT_EXPOSURE_TIME:
+ case OPT_SELECT_CAL_EXPOSURE_TIME:
+ case OPT_CAL_EXPOS_TIME:
+ case OPT_CAL_EXPOS_TIME_R:
+ case OPT_CAL_EXPOS_TIME_G:
+ case OPT_CAL_EXPOS_TIME_B:
+ case OPT_SCAN_EXPOS_TIME:
+ case OPT_SCAN_EXPOS_TIME_R:
+ case OPT_SCAN_EXPOS_TIME_G:
+ case OPT_SCAN_EXPOS_TIME_B:
+ case OPT_CAL_LAMP_DEN:
+ case OPT_SCAN_LAMP_DEN:
+ case OPT_DISABLE_PRE_FOCUS:
+ case OPT_MANUAL_PRE_FOCUS:
+ case OPT_FIX_FOCUS_POSITION:
+ case OPT_LENS_CALIBRATION_DOC_POS:
+ case OPT_HOLDER_FOCUS_POS_0MM:
+ case OPT_LAMP_OFF_AT_EXIT:
+ case OPT_SELECT_LAMP_DENSITY:
+ *(SANE_Word *) val = scanner->val[option].w;
+ return SANE_STATUS_GOOD;
+
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ case OPT_HALFTONE_PATTERN:
+ memcpy (val, scanner->val[option].wa, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_SOURCE:
+ /* fall through */
+ case OPT_MODE:
+ /* fall through */
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ case OPT_CALIB_MODE:
+ /* fall through */
+#endif
+ strcpy (val, scanner->val[option].s);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ switch (scanner->opt[option].type)
+ {
+ case SANE_TYPE_INT:
+ DBG(DBG_sane_option, "set %s [#%d] to %d\n", name, option, *(SANE_Word *) val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ DBG(DBG_sane_option, "set %s [#%d] to %f\n", name, option, SANE_UNFIX(*(SANE_Word *) val));
+ break;
+
+ case SANE_TYPE_STRING:
+ DBG(DBG_sane_option, "set %s [#%d] to %s\n", name, option, (char *) val);
+ break;
+
+ case SANE_TYPE_BOOL:
+ DBG(DBG_sane_option, "set %s [#%d] to %d\n", name, option, *(SANE_Word *) val);
+ break;
+
+ default:
+ DBG(DBG_sane_option, "set %s [#%d]\n", name, option);
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE(cap))
+ {
+ DBG(DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value(scanner->opt+option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_X_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_NEGATIVE:
+ case OPT_BATCH_SCAN_START:
+ case OPT_BATCH_SCAN_LOOP:
+ case OPT_BATCH_SCAN_END:
+ case OPT_BATCH_NEXT_TL_Y:
+ case OPT_QUALITY:
+ case OPT_WARMUP:
+ case OPT_PREVIEW:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_THRESHOLD:
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ case OPT_CAL_EXPOS_TIME:
+ case OPT_CAL_EXPOS_TIME_R:
+ case OPT_CAL_EXPOS_TIME_G:
+ case OPT_CAL_EXPOS_TIME_B:
+ case OPT_SCAN_EXPOS_TIME:
+ case OPT_SCAN_EXPOS_TIME_R:
+ case OPT_SCAN_EXPOS_TIME_G:
+ case OPT_SCAN_EXPOS_TIME_B:
+ case OPT_CAL_LAMP_DEN:
+ case OPT_SCAN_LAMP_DEN:
+ case OPT_DISABLE_PRE_FOCUS:
+ case OPT_MANUAL_PRE_FOCUS:
+ case OPT_FIX_FOCUS_POSITION:
+ case OPT_LENS_CALIBRATION_DOC_POS:
+ case OPT_HOLDER_FOCUS_POS_0MM:
+ case OPT_LAMP_OFF_AT_EXIT:
+ scanner->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_DOR:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val; /* update valure for umax_set_max_geometry */
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ DBG(DBG_info,"sane_control_option: set DOR = %d\n", scanner->val[option].w);
+ umax_set_max_geometry(scanner);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_BIT_DEPTH:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ scanner->output_range.min = 0;
+ scanner->output_range.quant = 0;
+
+ if (scanner->val[option].w == 8) /* 8 bit mode */
+ {
+ scanner->output_bytes = 1; /* 1 bytes output */
+ scanner->output_range.max = 255;
+ }
+ else /* > 8 bit mode */
+ {
+ scanner->output_bytes = 2; /* 2 bytes output */
+
+ if (scanner->device->gamma_lsb_padded) /* e.g. astra 1220s need lsb padded data */
+ {
+ scanner->output_range.max = (int) pow(2, scanner->val[option].w) - 1;
+ }
+ else
+ {
+ scanner->output_range.max = 65535; /* define maxval for msb padded data */
+ }
+ }
+
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_RGB_BIND:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ umax_set_rgb_bind(scanner);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_RESOLUTION_BIND:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if (scanner->val[option].w == SANE_FALSE)
+ { /* don't bind */
+ scanner->opt[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ }
+ else
+ { /* bind */
+ scanner->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_SELECT_EXPOSURE_TIME:
+ case OPT_SELECT_CAL_EXPOSURE_TIME:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_FALSE)
+ {
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else /* exposure time selection active */
+ {
+ scanner->opt[OPT_SELECT_CAL_EXPOSURE_TIME].cap &= ~SANE_CAP_INACTIVE;
+
+ if ( (strcmp(scanner->val[OPT_MODE].s, COLOR_STR) != 0) ||
+ (scanner->val[OPT_RGB_BIND].w == SANE_TRUE) ||
+ (scanner->device->exposure_time_rgb_bind) ) /* RGB bind */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else /* no RGB bind */
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_SELECT_LAMP_DENSITY:
+ {
+ if (scanner->val[option].w != *(SANE_Word *) val)
+ {
+ scanner->val[option].w = *(SANE_Word *) val;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if (scanner->val[option].w == SANE_FALSE)
+ {
+ scanner->opt[OPT_CAL_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_LAMP_DEN].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->opt[OPT_CAL_LAMP_DEN].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_LAMP_DEN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ /* side-effect-free word-array options: */
+ case OPT_HALFTONE_PATTERN:
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (scanner->val[option].wa, val, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* single string-option with side-effect: */
+ case OPT_SOURCE:
+ {
+ if (scanner->val[option].s)
+ {
+ free(scanner->val[option].s);
+ }
+ scanner->val[option].s = (SANE_Char *) strdup(val); /* update string for umax_set_max_geometry */
+
+ DBG(DBG_info,"sane_control_option: set SOURCE = %s\n", (SANE_Char *) val);
+ umax_set_max_geometry(scanner);
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+ }
+ break;
+
+ /* side-effect-free single-string options: */
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ case OPT_CALIB_MODE:
+ /* fall through */
+#endif
+ {
+ if (scanner->val[option].s)
+ {
+ free (scanner->val[option].s);
+ }
+ scanner->val[option].s = (SANE_Char*)strdup(val);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* options with side-effects: */
+
+ case OPT_CUSTOM_GAMMA:
+ {
+ w = *(SANE_Word *) val;
+ if (w == scanner->val[OPT_CUSTOM_GAMMA].w) { return SANE_STATUS_GOOD; }
+
+ scanner->val[OPT_CUSTOM_GAMMA].w = w;
+ if (w) /* use custom_gamma_table */
+ {
+ const char *mode = scanner->val[OPT_MODE].s;
+
+ if ( (strcmp(mode, LINEART_STR) == 0) ||
+ (strcmp(mode, HALFTONE_STR) == 0) ||
+ (strcmp(mode, GRAY_STR) == 0) )
+ { scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; }
+ else if (strcmp(mode, COLOR_STR) == 0)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ else /* don't use custom_gamma_table */
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_MODE:
+ {
+ int halftoning;
+
+ if (scanner->val[option].s)
+ {
+ free (scanner->val[option].s);
+ }
+
+ scanner->val[option].s = (SANE_Char*)strdup(val);
+
+ if (info)
+ {
+ *info |=SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+
+ scanner->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_RGB_BIND].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CAL_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME_B].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+
+ halftoning = (strcmp(val, HALFTONE_STR) == 0 || strcmp(val, COLOR_HALFTONE_STR) == 0);
+
+ if (halftoning || strcmp(val, LINEART_STR) == 0 || strcmp(val, COLOR_LINEART_STR) == 0)
+ { /* one bit modes */
+ if (scanner->device->inquiry_reverse)
+ {
+ scanner->opt[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (halftoning)
+ { /* halftoning modes */
+ scanner->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+/* disable halftone pattern download options */
+#if 0
+ scanner->opt[OPT_HALFTONE_DIMENSION].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->val[OPT_HALFTONE_DIMENSION].w)
+ {
+ scanner->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+#endif
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_h_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ else
+ { /* lineart modes */
+ scanner->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_l_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ }
+ else
+ { /* multi-bit modes(gray or color) */
+ scanner->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+
+ if (scanner->device->inquiry_highlight)
+ {
+ scanner->opt[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_shadow)
+ {
+ scanner->opt[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_reverse_multi)
+ {
+ scanner->opt[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->device->inquiry_gamma_dwload)
+ {
+ scanner->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ scanner->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+ }
+
+ if (scanner->device->inquiry_analog_gamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ scanner->opt[OPT_CAL_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_SCAN_EXPOS_TIME].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ if (strcmp(val, COLOR_STR) == 0)
+ {
+ if ( (scanner->device->inquiry_analog_gamma) ||
+ (scanner->device->inquiry_highlight) ||
+ (scanner->device->inquiry_shadow) ||
+ (scanner->device->inquiry_exposure_adj) )
+ {
+ scanner->opt[OPT_RGB_BIND].cap &= ~SANE_CAP_INACTIVE;
+ }
+
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_c_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ else /* grayscale */
+ {
+ scanner->exposure_time_range.min = scanner->device->inquiry_exposure_time_g_min
+ * scanner->device->inquiry_exposure_time_step_unit;
+ }
+ }
+
+ umax_set_rgb_bind(scanner);
+
+ if (scanner->val[OPT_CUSTOM_GAMMA].w)
+ {
+ if (strcmp(val, GRAY_STR) == 0)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp(val, COLOR_STR) == 0)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_HALFTONE_DIMENSION: /* halftone pattern dimension affects halftone pattern option: */
+ {
+ unsigned dim = *(SANE_Word *) val;
+
+ scanner->val[option].w = dim;
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ scanner->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+
+ if (dim > 0)
+ {
+ scanner->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_HALFTONE_PATTERN].size = dim * sizeof (SANE_Word);
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ case OPT_LAMP_ON:
+ {
+ if (!umax_set_lamp_status(handle, 1 /* lamp on */))
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+
+ case OPT_LAMP_OFF:
+ {
+ if (!umax_set_lamp_status(handle, 0 /* lamp off */))
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ }
+ } /* else */
+ return SANE_STATUS_INVAL;
+}
+
+
+/* ------------------------------------------------------------ SANE GET PARAMETERS ------------------------ */
+
+
+SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ Umax_Scanner *scanner = handle;
+ const char *mode;
+
+ DBG(DBG_sane_info,"sane_get_parameters\n");
+
+ if (!scanner->scanning)
+ { /* not scanning, so lets use recent values */
+ double width, length, x_dpi, y_dpi;
+
+ memset(&scanner->params, 0, sizeof (scanner->params));
+
+ width = SANE_UNFIX(scanner->val[OPT_BR_X].w - scanner->val[OPT_TL_X].w);
+ length = SANE_UNFIX(scanner->val[OPT_BR_Y].w - scanner->val[OPT_TL_Y].w);
+ x_dpi = SANE_UNFIX(scanner->val[OPT_X_RESOLUTION].w);
+ y_dpi = SANE_UNFIX(scanner->val[OPT_Y_RESOLUTION].w);
+
+ if ( (scanner->val[OPT_RESOLUTION_BIND].w == SANE_TRUE) || (scanner->val[OPT_PREVIEW].w == SANE_TRUE) )
+ {
+ y_dpi = x_dpi;
+ }
+
+ if (x_dpi > 0.0 && y_dpi > 0.0 && width > 0.0 && length > 0.0)
+ {
+ double x_dots_per_mm = x_dpi / MM_PER_INCH;
+ double y_dots_per_mm = y_dpi / MM_PER_INCH;
+
+ scanner->params.pixels_per_line = width * x_dots_per_mm;
+ scanner->params.lines = length * y_dots_per_mm;
+ }
+ }
+
+ mode = scanner->val[OPT_MODE].s;
+
+ if (strcmp(mode, LINEART_STR) == 0 || strcmp(mode, HALFTONE_STR) == 0)
+ {
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.bytes_per_line = (scanner->params.pixels_per_line + 7) / 8;
+ scanner->params.depth = 1;
+ }
+ else if (strcmp(mode, GRAY_STR) == 0)
+ {
+ scanner->params.format = SANE_FRAME_GRAY;
+ scanner->params.bytes_per_line = scanner->params.pixels_per_line * scanner->output_bytes;
+ scanner->params.depth = 8 * scanner->output_bytes;
+ }
+ else if (strcmp(mode, COLOR_LINEART_STR) == 0 || strcmp(mode, COLOR_HALFTONE_STR) == 0 )
+ {
+ if (scanner->device->inquiry_one_pass_color)
+ {
+ scanner->device->three_pass = 0;
+ scanner->params.format = SANE_FRAME_RGB;
+ scanner->params.bytes_per_line = 3 * scanner->params.pixels_per_line;
+ scanner->params.depth = 8;
+ }
+ else /* three pass color */
+ {
+ scanner->device->three_pass = 1;
+ scanner->params.format = SANE_FRAME_RED + scanner->device->three_pass_color - 1;
+ scanner->params.bytes_per_line = scanner->params.pixels_per_line;
+ scanner->params.depth = 8;
+ }
+ }
+ else /* RGB */
+ {
+ if (scanner->device->inquiry_one_pass_color)
+ {
+ scanner->device->three_pass = 0;
+ scanner->params.format = SANE_FRAME_RGB;
+ scanner->params.bytes_per_line = 3 * scanner->params.pixels_per_line * scanner->output_bytes;
+ scanner->params.depth = 8 * scanner->output_bytes;
+ }
+ else /* three pass color */
+ {
+ scanner->device->three_pass = 1;
+ scanner->params.format = SANE_FRAME_RED + scanner->device->three_pass_color - 1;
+ scanner->params.bytes_per_line = scanner->params.pixels_per_line * scanner->output_bytes;
+ scanner->params.depth = 8 * scanner->output_bytes;
+ }
+ }
+
+ scanner->params.last_frame = (scanner->params.format != SANE_FRAME_RED && scanner->params.format != SANE_FRAME_GREEN);
+
+ if (params)
+ {
+ *params = scanner->params;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE START --------------------------------- */
+
+
+SANE_Status sane_start(SANE_Handle handle)
+{
+ Umax_Scanner *scanner = handle;
+ const char *mode;
+ double xbasedots, ybasedots;
+ const char *scan_source;
+ int pause;
+ int status;
+ int fds[2];
+
+ DBG(DBG_sane_init,"sane_start\n");
+
+ /* Initialize reader_pid to invalid so a subsequent error and following call
+ * to do_cancel() won't trip over it. */
+ scanner->reader_pid = -1;
+
+ mode = scanner->val[OPT_MODE].s;
+
+ if (scanner->device->sfd == -1) /* first call, don`t run this routine again on multi frame or multi image scan */
+ {
+ umax_initialize_values(scanner->device); /* reset values */
+
+ scanner->device->three_pass_color = 1;
+
+ /* test for adf and uta */
+ scan_source = scanner->val[OPT_SOURCE].s;
+
+ if (strcmp(scan_source, UTA_STR) == 0)
+ {
+ if ( (scanner->device->inquiry_uta != 0) && (scanner->device->inquiry_transavail != 0) )
+ {
+ scanner->device->uta = 1;
+ }
+ else
+ {
+ DBG(DBG_error,"ERROR: Transparency Adapter not available\n");
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else /* Test if ADF is selected */
+ {
+ scanner->device->uta = 0;
+
+ if (strcmp(scan_source, ADF_STR) == 0)
+ {
+ if ( (scanner->device->inquiry_adf) && (scanner->device->inquiry_adfmode) )
+ {
+ scanner->device->adf = 1;
+ }
+ else
+ {
+ DBG(DBG_error,"ERROR: Automatic Document Feeder not available\n");
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else
+ {
+ scanner->device->adf = 0;
+ }
+ }
+
+ if (scanner->device->inquiry_GIB & 32) /* 16 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 32;
+ DBG(DBG_sane_info, "Using 16 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 16) /* 14 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 16;
+ DBG(DBG_sane_info, "Using 14 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 8) /* 12 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 8;
+ DBG(DBG_sane_info, "Using 12 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 4) /* 10 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 4;
+ DBG(DBG_sane_info, "Using 10 bits for gamma input\n");
+ }
+ else if (scanner->device->inquiry_GIB & 2) /* 9 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 2;
+ DBG(DBG_sane_info, "Using 9 bits for gamma input\n");
+ }
+ else /* 8 bit input mode */
+ {
+ scanner->device->gamma_input_bits_code = 1;
+ DBG(DBG_sane_info, "Using 8 bits for gamma input\n");
+ }
+
+ if (scanner->val[OPT_BIT_DEPTH].w == 16) /* 16 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 16;
+ scanner->device->bits_per_pixel_code = 32;
+ scanner->device->max_value = 65535;
+ DBG(DBG_sane_info,"Using 16 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 14) /* 14 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 14;
+ scanner->device->bits_per_pixel_code = 16;
+ scanner->device->max_value = 16383;
+ DBG(DBG_sane_info,"Using 14 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 12) /* 12 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 12;
+ scanner->device->bits_per_pixel_code = 8;
+ scanner->device->max_value = 4095;
+ DBG(DBG_sane_info,"Using 12 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 10) /* 10 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 10;
+ scanner->device->bits_per_pixel_code = 4;
+ scanner->device->max_value = 1023;
+ DBG(DBG_sane_info,"Using 10 bits for output\n");
+ }
+ else if (scanner->val[OPT_BIT_DEPTH].w == 9) /* 9 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 9;
+ scanner->device->bits_per_pixel_code = 2;
+ scanner->device->max_value = 511;
+ DBG(DBG_sane_info,"Using 9 bits for output\n");
+ }
+ else /* 8 bit output mode */
+ {
+ scanner->device->bits_per_pixel = 8;
+ scanner->device->bits_per_pixel_code = 1;
+ scanner->device->max_value = 255;
+ DBG(DBG_sane_info,"Using 8 bits for output\n");
+ }
+
+ scanner->device->reverse = scanner->device->reverse_multi = scanner->val[OPT_NEGATIVE].w;
+
+ scanner->device->threshold = P_100_TO_255(scanner->val[OPT_THRESHOLD].w);
+ scanner->device->brightness = P_200_TO_255(scanner->val[OPT_BRIGHTNESS].w);
+ scanner->device->contrast = P_200_TO_255(scanner->val[OPT_CONTRAST].w);
+
+ scanner->device->batch_scan = ( scanner->val[OPT_BATCH_SCAN_START].w ||
+ scanner->val[OPT_BATCH_SCAN_LOOP].w ||
+ scanner->val[OPT_BATCH_SCAN_END].w );
+ scanner->device->batch_end = scanner->val[OPT_BATCH_SCAN_END].w;
+ scanner->device->batch_next_tl_y = SANE_UNFIX(scanner->val[OPT_BATCH_NEXT_TL_Y].w) * scanner->device->y_coordinate_base / MM_PER_INCH;
+
+ if (scanner->val[OPT_BATCH_NEXT_TL_Y].w == 0xFFFF) /* option not set: use br_y => scanhead stops at end of batch area */
+ {
+ scanner->device->batch_next_tl_y = SANE_UNFIX(scanner->val[OPT_BR_Y].w) * scanner->device->y_coordinate_base / MM_PER_INCH;
+ }
+
+ if ((scanner->device->batch_scan) && !scanner->val[OPT_BATCH_SCAN_START].w)
+ {
+ scanner->device->calibration = 9; /* no calibration - otherwise the scanhead will go into calibration position */
+ }
+ else
+ {
+ scanner->device->calibration = 0; /* calibration defined by image type */
+ }
+
+ scanner->device->quality = scanner->val[OPT_QUALITY].w;
+ scanner->device->dor = scanner->val[OPT_DOR].w;
+ scanner->device->preview = scanner->val[OPT_PREVIEW].w;
+ scanner->device->warmup = scanner->val[OPT_WARMUP].w;
+
+ scanner->device->fix_focus_position = scanner->val[OPT_FIX_FOCUS_POSITION].w;
+ scanner->device->lens_cal_in_doc_pos = scanner->val[OPT_LENS_CALIBRATION_DOC_POS].w;
+ scanner->device->disable_pre_focus = scanner->val[OPT_DISABLE_PRE_FOCUS].w;
+ scanner->device->holder_focus_pos_0mm = scanner->val[OPT_HOLDER_FOCUS_POS_0MM].w;
+ scanner->device->manual_focus = scanner->val[OPT_MANUAL_PRE_FOCUS].w;
+
+ scanner->device->analog_gamma_r =
+ scanner->device->analog_gamma_g =
+ scanner->device->analog_gamma_b = umax_calculate_analog_gamma(SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA].w));
+
+ scanner->device->highlight_r =
+ scanner->device->highlight_g =
+ scanner->device->highlight_b = P_100_TO_255(scanner->val[OPT_HIGHLIGHT].w);
+
+ scanner->device->shadow_r =
+ scanner->device->shadow_g =
+ scanner->device->shadow_b = P_100_TO_255(scanner->val[OPT_SHADOW].w);
+
+ if (scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE)
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* separate calibration exposure time */
+ {
+ scanner->device->exposure_time_calibration_r =
+ scanner->device->exposure_time_calibration_g =
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_CAL_EXPOS_TIME].w;
+ }
+ else /* same exposure times for calibration as for scanning */
+ {
+ scanner->device->exposure_time_calibration_r =
+ scanner->device->exposure_time_calibration_g =
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_SCAN_EXPOS_TIME].w;
+ }
+
+ scanner->device->exposure_time_scan_r =
+ scanner->device->exposure_time_scan_g =
+ scanner->device->exposure_time_scan_b = scanner->val[OPT_SCAN_EXPOS_TIME].w;
+ }
+
+ if (scanner->val[OPT_SELECT_LAMP_DENSITY].w == SANE_TRUE)
+ {
+ scanner->device->c_density = P_100_TO_254(scanner->val[OPT_CAL_LAMP_DEN].w);
+ scanner->device->s_density = P_100_TO_254(scanner->val[OPT_SCAN_LAMP_DEN].w);
+ }
+
+ if (strcmp(mode, LINEART_STR) == 0)
+ {
+ scanner->device->colormode = LINEART;
+ }
+ else if (strcmp(mode, HALFTONE_STR) == 0)
+ {
+ scanner->device->colormode = HALFTONE;
+ }
+ else if (strcmp(mode, GRAY_STR) == 0)
+ {
+ scanner->device->colormode = GRAYSCALE;
+ }
+ else if (strcmp(mode, COLOR_LINEART_STR) == 0)
+ {
+ scanner->device->colormode = RGB_LINEART;
+ }
+ else if (strcmp(mode, COLOR_HALFTONE_STR) == 0)
+ {
+ scanner->device->colormode = RGB_HALFTONE;
+ }
+ else if (strcmp(mode, COLOR_STR) == 0)
+ {
+ scanner->device->colormode = RGB;
+ if (scanner->val[OPT_RGB_BIND].w == SANE_FALSE)
+ {
+ scanner->device->analog_gamma_r =
+ umax_calculate_analog_gamma( SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA_R].w) );
+ scanner->device->analog_gamma_g =
+ umax_calculate_analog_gamma( SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA_G].w) );
+ scanner->device->analog_gamma_b =
+ umax_calculate_analog_gamma( SANE_UNFIX(scanner->val[OPT_ANALOG_GAMMA_B].w) );
+
+ scanner->device->highlight_r = P_100_TO_255(scanner->val[OPT_HIGHLIGHT_R].w);
+ scanner->device->highlight_g = P_100_TO_255(scanner->val[OPT_HIGHLIGHT_G].w);
+ scanner->device->highlight_b = P_100_TO_255(scanner->val[OPT_HIGHLIGHT_B].w);
+
+ scanner->device->shadow_r = P_100_TO_255(scanner->val[OPT_SHADOW_R].w);
+ scanner->device->shadow_g = P_100_TO_255(scanner->val[OPT_SHADOW_G].w);
+ scanner->device->shadow_b = P_100_TO_255(scanner->val[OPT_SHADOW_B].w);
+
+ if ((scanner->val[OPT_SELECT_EXPOSURE_TIME].w == SANE_TRUE) && (!scanner->device->exposure_time_rgb_bind))
+ {
+ if (scanner->val[OPT_SELECT_CAL_EXPOSURE_TIME].w) /* separate calibration exposure time */
+ {
+ scanner->device->exposure_time_calibration_r = scanner->val[OPT_CAL_EXPOS_TIME_R].w;
+ scanner->device->exposure_time_calibration_g = scanner->val[OPT_CAL_EXPOS_TIME_G].w;
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_CAL_EXPOS_TIME_B].w;
+ }
+ else /* same exposure times for calibration as for scanning */
+ {
+ scanner->device->exposure_time_calibration_r = scanner->val[OPT_SCAN_EXPOS_TIME_R].w;
+ scanner->device->exposure_time_calibration_g = scanner->val[OPT_SCAN_EXPOS_TIME_G].w;
+ scanner->device->exposure_time_calibration_b = scanner->val[OPT_SCAN_EXPOS_TIME_B].w;
+ }
+
+ scanner->device->exposure_time_scan_r = scanner->val[OPT_SCAN_EXPOS_TIME_R].w;
+ scanner->device->exposure_time_scan_g = scanner->val[OPT_SCAN_EXPOS_TIME_G].w;
+ scanner->device->exposure_time_scan_b = scanner->val[OPT_SCAN_EXPOS_TIME_B].w;
+ }
+ }
+ }
+
+ if (scanner->device->force_preview_bit_rgb != 0) /* in RGB-mode set preview bit, eg. for UMAX S6E */
+ {
+ if (scanner->device->colormode == RGB)
+ {
+ DBG(DBG_sane_info,"setting preview bit = 1 (option force-preview-bit-rgb)\n");
+ scanner->device->preview = SANE_TRUE;
+ }
+ }
+
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_0000) == 0)
+ {
+ scanner->device->calibration = 0;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1111) == 0)
+ {
+ scanner->device->calibration = 15;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1110) == 0)
+ {
+ scanner->device->calibration = 14;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1101) == 0)
+ {
+ scanner->device->calibration = 13;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1010) == 0)
+ {
+ scanner->device->calibration = 10;
+ }
+ else if (strcmp(scanner->val[OPT_CALIB_MODE].s, CALIB_MODE_1001) == 0)
+ {
+ scanner->device->calibration = 9;
+ }
+#endif
+
+ /* get and set geometric values for scanning */
+ scanner->device->x_resolution = SANE_UNFIX(scanner->val[OPT_X_RESOLUTION].w);
+ scanner->device->y_resolution = SANE_UNFIX(scanner->val[OPT_Y_RESOLUTION].w);
+
+ if ( (scanner->val[OPT_RESOLUTION_BIND].w == SANE_TRUE) || (scanner->val[OPT_PREVIEW].w == SANE_TRUE) )
+ {
+ scanner->device->y_resolution = scanner->device->x_resolution;
+ }
+
+ xbasedots = scanner->device->x_coordinate_base / MM_PER_INCH;
+ ybasedots = scanner->device->y_coordinate_base / MM_PER_INCH;
+
+#if 0
+ scanner->device->upper_left_x = ((int) (SANE_UNFIX(scanner->val[OPT_TL_X].w) * xbasedots)) & 65534;
+ scanner->device->upper_left_y = ((int) (SANE_UNFIX(scanner->val[OPT_TL_Y].w) * ybasedots)) & 65534;
+
+ scanner->device->scanwidth = ((int)((SANE_UNFIX(scanner->val[OPT_BR_X].w - scanner->val[OPT_TL_X].w)) * xbasedots)) & 65534;
+ scanner->device->scanlength = ((int)((SANE_UNFIX(scanner->val[OPT_BR_Y].w - scanner->val[OPT_TL_Y].w)) * ybasedots)) & 65534;
+#endif
+
+ scanner->device->upper_left_x = (int) (SANE_UNFIX(scanner->val[OPT_TL_X].w) * xbasedots);
+ scanner->device->upper_left_y = (int) (SANE_UNFIX(scanner->val[OPT_TL_Y].w) * ybasedots);
+
+ scanner->device->scanwidth = (int)((SANE_UNFIX(scanner->val[OPT_BR_X].w - scanner->val[OPT_TL_X].w)) * xbasedots);
+ scanner->device->scanlength = (int)((SANE_UNFIX(scanner->val[OPT_BR_Y].w - scanner->val[OPT_TL_Y].w)) * ybasedots);
+
+
+ if (umax_check_values(scanner->device) != 0)
+ {
+ DBG(DBG_error,"ERROR: invalid scan-values\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ /* The scanner defines a x-origin-offset for DOR mode, this offset is used for the */
+ /* x range in this backend, so the frontend/user knows the correct positions related to */
+ /* scanner's surface. But the scanner wants x values from origin 0 instead */
+ /* of the x-origin defined by the scanner`s inquiry */
+ if (scanner->device->dor != 0) /* dor mode active */
+ {
+ DBG(DBG_info,"substracting DOR x-origin-offset from upper left x\n");
+ scanner->device->upper_left_x -= scanner->device->inquiry_dor_x_off * scanner->device->x_coordinate_base; /* correct DOR x-origin */
+
+ if (scanner->device->upper_left_x < 0) /* rounding errors may create a negative value */
+ {
+ scanner->device->upper_left_x = 0; /* but negative values are not allowed */
+ }
+ }
+
+ scanner->params.bytes_per_line = scanner->device->row_len;
+ scanner->params.pixels_per_line = scanner->device->width_in_pixels;
+ scanner->params.lines = scanner->device->length_in_pixels;
+
+
+ /* set exposure times */
+ if ( scanner->device->inquiry_exposure_adj )
+ {
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_r, &scanner->device->exposure_time_calibration_r);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_g, &scanner->device->exposure_time_calibration_g);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_b, &scanner->device->exposure_time_calibration_b);
+
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_r, &scanner->device->exposure_time_scan_r);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_g, &scanner->device->exposure_time_scan_g);
+ umax_calculate_exposure_time(scanner->device, scanner->device->use_exposure_time_def_b, &scanner->device->exposure_time_scan_b);
+ }
+ else
+ {
+ scanner->device->exposure_time_calibration_r = scanner->device->exposure_time_calibration_g = scanner->device->exposure_time_calibration_b =
+ scanner->device->exposure_time_scan_r = scanner->device->exposure_time_scan_g = scanner->device->exposure_time_scan_b = 0;
+ }
+
+
+ scanner->scanning = SANE_TRUE;
+ sane_get_parameters(scanner, 0);
+
+ DBG(DBG_sane_info,"x_resolution (dpi) = %u\n", scanner->device->x_resolution);
+ DBG(DBG_sane_info,"y_resolution (dpi) = %u\n", scanner->device->y_resolution);
+ DBG(DBG_sane_info,"x_coordinate_base (dpi) = %u\n", scanner->device->x_coordinate_base);
+ DBG(DBG_sane_info,"y_coordinate_base (dpi) = %u\n", scanner->device->y_coordinate_base);
+ DBG(DBG_sane_info,"upper_left_x (xbase) = %d\n", scanner->device->upper_left_x);
+ DBG(DBG_sane_info,"upper_left_y (ybase) = %d\n", scanner->device->upper_left_y);
+ DBG(DBG_sane_info,"scanwidth (xbase) = %u\n", scanner->device->scanwidth);
+ DBG(DBG_sane_info,"scanlength (ybase) = %u\n", scanner->device->scanlength);
+ DBG(DBG_sane_info,"width in pixels = %u\n", scanner->device->width_in_pixels);
+ DBG(DBG_sane_info,"length in pixels = %u\n", scanner->device->length_in_pixels);
+ DBG(DBG_sane_info,"bits per pixel/color = %u\n", scanner->device->bits_per_pixel);
+ DBG(DBG_sane_info,"bytes per line = %d\n", scanner->params.bytes_per_line);
+ DBG(DBG_sane_info,"pixels_per_line = %d\n", scanner->params.pixels_per_line);
+ DBG(DBG_sane_info,"lines = %d\n", scanner->params.lines);
+ DBG(DBG_sane_info,"negative = %d\n", scanner->device->reverse);
+ DBG(DBG_sane_info,"threshold (lineart) = %d\n", scanner->device->threshold);
+ DBG(DBG_sane_info,"brightness (halftone) = %d\n", scanner->device->brightness);
+ DBG(DBG_sane_info,"contrast (halftone) = %d\n", scanner->device->contrast);
+
+ DBG(DBG_sane_info,"analog_gamma = %d %d %d\n",
+ scanner->device->analog_gamma_r,
+ scanner->device->analog_gamma_g,
+ scanner->device->analog_gamma_b);
+ DBG(DBG_sane_info,"highlight = %d %d %d\n",
+ scanner->device->highlight_r,
+ scanner->device->highlight_g,
+ scanner->device->highlight_b);
+ DBG(DBG_sane_info,"shadow = %d %d %d\n",
+ scanner->device->shadow_r,
+ scanner->device->shadow_g,
+ scanner->device->shadow_b);
+ DBG(DBG_sane_info,"calibrat. exposure time = %d %d %d\n",
+ scanner->device->exposure_time_calibration_r,
+ scanner->device->exposure_time_calibration_g,
+ scanner->device->exposure_time_calibration_b);
+ DBG(DBG_sane_info,"scan exposure time = %d %d %d\n",
+ scanner->device->exposure_time_scan_r,
+ scanner->device->exposure_time_scan_g,
+ scanner->device->exposure_time_scan_b);
+
+#ifdef UMAX_CALIBRATION_MODE_SELECTABLE
+ DBG(DBG_sane_info,"calibration = %s\n", scanner->val[OPT_CALIB_MODE].s);
+#endif
+ DBG(DBG_sane_info,"calibration mode number = %d\n", scanner->device->calibration);
+
+ DBG(DBG_sane_info,"batch scan = %d\n", scanner->device->batch_scan);
+ DBG(DBG_sane_info,"batch end = %d\n", scanner->device->batch_end);
+ DBG(DBG_sane_info,"batch next top left y = %d\n", scanner->device->batch_next_tl_y);
+ DBG(DBG_sane_info,"quality calibration = %d\n", scanner->device->quality);
+ DBG(DBG_sane_info,"warm up = %d\n", scanner->device->warmup);
+ DBG(DBG_sane_info,"fast preview function = %d\n", scanner->device->preview);
+ DBG(DBG_sane_info,"DOR = %d\n", scanner->device->dor);
+ DBG(DBG_sane_info,"ADF = %d\n", scanner->device->adf);
+ DBG(DBG_sane_info,"manual focus = %d\n", scanner->device->manual_focus);
+ DBG(DBG_sane_info,"fix focus position = %d\n", scanner->device->fix_focus_position);
+ DBG(DBG_sane_info,"disable pre focus = %d\n", scanner->device->disable_pre_focus);
+ DBG(DBG_sane_info,"lens cal in doc pos = %d\n", scanner->device->lens_cal_in_doc_pos);
+ DBG(DBG_sane_info,"holder focus pos 0mm = %d\n", scanner->device->holder_focus_pos_0mm);
+
+ if (scanner->val[OPT_PREVIEW].w) /* preview mode */
+ {
+ scanner->device->lines_max = scanner->device->request_preview_lines;
+ }
+ else /* scan mode */
+ {
+ scanner->device->lines_max = scanner->device->request_scan_lines;
+ }
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ {
+ unsigned int scsi_bufsize = 0;
+
+ scsi_bufsize = scanner->device->width_in_pixels * scanner->device->lines_max;
+
+ if (scsi_bufsize == 0) /* no scsi buffer size, take scanner buffer size */
+ {
+ scsi_bufsize = scanner->device->inquiry_vidmem;
+ }
+
+ if (scsi_bufsize < scanner->device->scsi_buffer_size_min) /* make sure buffer has at least minimum size */
+ {
+ scsi_bufsize = scanner->device->scsi_buffer_size_min;
+ }
+ else if (scsi_bufsize > scanner->device->scsi_buffer_size_max) /* make sure buffer does not exceed maximum size */
+ {
+ scsi_bufsize = scanner->device->scsi_buffer_size_max;
+ }
+
+ if (umax_scsi_open_extended(scanner->device->sane.name, scanner->device, sense_handler,
+ scanner->device, (int *) &scsi_bufsize) != 0)
+ {
+ DBG(DBG_error, "ERROR: sane_start: open failed\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (scsi_bufsize < scanner->device->scsi_buffer_size_min) /* minimum size must be available */
+ {
+ DBG(DBG_error, "ERROR: sane_start: umax_scsi_open_extended returned too small scsi buffer\n");
+ umax_scsi_close((scanner->device));
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG(DBG_info, "sane_start: umax_scsi_open_extended returned scsi buffer size = %d\n", scsi_bufsize);
+
+ if (scsi_bufsize < scanner->device->width_in_pixels) /* print warning when buffer is smaller than one scanline */
+ {
+ DBG(DBG_warning, "WARNING: sane_start: scsi buffer is smaller than one scanline\n");
+ }
+
+ if (scsi_bufsize != scanner->device->bufsize)
+ {
+ DBG(DBG_info, "sane_start: buffer size has changed, reallocating buffer\n");
+
+ if (scanner->device->buffer[0])
+ {
+ DBG(DBG_info, "sane_start: freeing SCSI buffer[0]\n");
+ free(scanner->device->buffer[0]); /* free buffer */
+ }
+
+ scanner->device->bufsize = scsi_bufsize;
+
+ DBG(DBG_info, "sane_start: allocating SCSI buffer[0]\n");
+ scanner->device->buffer[0] = malloc(scanner->device->bufsize); /* allocate buffer */
+
+ if (!scanner->device->buffer[0]) /* malloc failed */
+ {
+ DBG(DBG_error, "ERROR: sane_start: could not allocate buffer[0]\n");
+ umax_scsi_close(scanner->device);
+ scanner->device->bufsize = 0;
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ }
+#else
+ if ( umax_scsi_open(scanner->device->sane.name, scanner->device, sense_handler,
+ scanner->device) != SANE_STATUS_GOOD )
+ {
+ scanner->scanning = SANE_FALSE;
+ DBG(DBG_error, "ERROR: sane_start: open of %s failed:\n", scanner->device->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* there is no need to reallocate the buffer because the size is fixed */
+#endif
+
+ /* grab scanner */
+ if (umax_grab_scanner(scanner->device))
+ {
+ umax_scsi_close(scanner->device);
+ scanner->scanning = SANE_FALSE;
+ DBG(DBG_warning,"WARNING: unable to reserve scanner: device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+/* halftone pattern download is not ready in this version */
+#if 0
+ /* send halftonepattern */
+ if ( (strcmp(mode, HALFTONE_STR) == 0) || (strcmp(mode, COLOR_HALFTONE_STR) == 0) )
+ {
+ umax_send_halftone_pattern(scanner->device, (char *) &(scanner->halftone_pattern[0]),
+ scanner->val[OPT_HALFTONE_DIMENSION].w );
+ scanner->device->halftone = WD_halftone_download;
+ } /* end of send halftonepattern */
+#endif
+
+ } /* ------------ end of first call -------------- */
+
+
+ /* send gammacurves */
+ if (scanner->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
+ {
+ if (strcmp(mode, COLOR_STR) == 0)
+ {
+ if (scanner->device->three_pass == 0) /* one pass color scan */
+ {
+ unsigned int i, dest, color, value;
+ char *gamma;
+
+ gamma = malloc( (size_t) (3 * scanner->gamma_length * scanner->output_bytes) );
+ if (gamma == NULL)
+ {
+ DBG(DBG_warning,"WARNING: not able to allocate memory for gamma table, gamma ignored !!!\n");
+ }
+ else
+ {
+ dest=0;
+ for(color=1; color <= 3; color++)
+ {
+ for(i=0; i < scanner->gamma_length; i++)
+ {
+ value = scanner->gamma_table[color][i];
+ if (scanner->output_bytes == 2)
+ {
+ gamma[dest++] = scanner->gamma_table[0][value] / 256;
+ }
+ gamma[dest++] = (scanner->gamma_table[0][value] & 255);
+ }
+ }
+
+ DBG(DBG_sane_info,"sending 3 * %d bytes of gamma data for RGB\n",
+ scanner->gamma_length * scanner->output_bytes);
+
+ umax_send_gamma_data(scanner->device, &gamma[0], 3);
+ scanner->device->digital_gamma_r =
+ scanner->device->digital_gamma_g =
+ scanner->device->digital_gamma_b = WD_gamma_download;
+ free(gamma);
+ }
+ }
+ else /* three pass color scan */
+ {
+ unsigned int i, dest, color, value;
+ char *gamma;
+
+ gamma = malloc( (size_t) (scanner->gamma_length * scanner->output_bytes) );
+ if (gamma == NULL)
+ {
+ DBG(DBG_warning,"not able to allocate memory for gamma table, gamma ignored !!!\n");
+ }
+ else
+ {
+ dest = 0;
+ color = scanner->device->three_pass_color;
+
+ for(i = 0; i < scanner->gamma_length; i++)
+ {
+ value = scanner->gamma_table[color][i];
+
+ if (scanner->output_bytes == 2)
+ {
+ gamma[dest++] = scanner->gamma_table[0][value] / 256;
+ }
+ gamma[dest++] = (scanner->gamma_table[0][value] & 255);
+ }
+
+ DBG(DBG_sane_info,"sending %d bytes of gamma data for color %d\n",
+ scanner->gamma_length * scanner->output_bytes, color);
+
+ umax_send_gamma_data(scanner->device, &gamma[0], 1);
+ scanner->device->digital_gamma_r =
+ scanner->device->digital_gamma_g =
+ scanner->device->digital_gamma_b = WD_gamma_download;
+ free(gamma);
+ }
+ }
+ }
+ else if (strcmp(mode, GRAY_STR) == 0) /* grayscale scan */
+ {
+ unsigned int i, dest;
+ char *gamma;
+
+ gamma = malloc( (size_t) (scanner->gamma_length * scanner->output_bytes) );
+ if (gamma == NULL)
+ {
+ DBG(DBG_warning,"WARNING: not able to allocate memory for gamma table, gamma ignored !!!\n");
+ }
+ else
+ {
+ dest=0;
+ for(i=0; i < scanner->gamma_length; i++)
+ {
+ if (scanner->output_bytes == 2)
+ {
+ gamma[dest++] = scanner->gamma_table[0][i] / 256;
+ }
+ gamma[dest++] = (scanner->gamma_table[0][i] & 255);
+ }
+
+ DBG(DBG_sane_info,"sending %d bytes of gamma data for gray\n",
+ scanner->gamma_length * scanner->output_bytes);
+
+ umax_send_gamma_data(scanner->device, &gamma[0], 1);
+ scanner->device->digital_gamma_r = WD_gamma_download;
+ free(gamma);
+ }
+ }
+ } /* end of send gammacurves */
+
+ if ( scanner->device->three_pass_color > WD_wid_red) /* three pass scan, not first pass */
+ {
+ umax_reposition_scanner(scanner->device);
+ }
+
+ umax_set_window_param(scanner->device);
+ status = umax_start_scan(scanner->device);
+ if (status) /* errror */
+ {
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ return status;
+ }
+
+ pause = scanner->device->pause_for_color_calibration;
+
+ if (scanner->device->colormode != RGB)
+ {
+ pause = scanner->device->pause_for_gray_calibration;
+ }
+
+ if (pause) /* Astra 2400S needs this pause (7sec in color, 4sec in gray mode) */
+ {
+ DBG(DBG_info2,"pause for calibration %d msec ...\n", pause);
+ usleep(((long) pause) * 1000); /* time in ms */
+ DBG(DBG_info2,"pause done\n");
+ }
+
+ status = umax_do_calibration(scanner->device);
+ if (status) /* errror */
+ {
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ return status;
+ }
+
+ if (scanner->device->pause_after_calibration) /* may be usefull */
+ {
+ DBG(DBG_info2,"pause after calibration %d msec ...\n", scanner->device->pause_after_calibration);
+ usleep(((long) scanner->device->pause_after_calibration) * 1000); /* time in ms */
+ DBG(DBG_info2,"pause done\n");
+ }
+
+
+ if (pipe(fds) < 0)
+ {
+ DBG(DBG_error,"ERROR: could not create pipe\n");
+ scanner->scanning = SANE_FALSE;
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scanner->pipe_read_fd = fds[0];
+ scanner->pipe_write_fd = fds[1];
+
+ /* start reader_process, deponds on OS if fork() or threads are used */
+ scanner->reader_pid = sanei_thread_begin(reader_process, (void *) scanner);
+
+ if (scanner->reader_pid == -1)
+ {
+ DBG(DBG_error, "ERROR: sanei_thread_begin failed (%s)\n", strerror(errno));
+ scanner->scanning = SANE_FALSE;
+ umax_give_scanner(scanner->device); /* reposition and release scanner */
+ umax_scsi_close(scanner->device);
+ return SANE_STATUS_NO_MEM; /* any other reason than no memory possible ? */
+ }
+
+ if (sanei_thread_is_forked())
+ {
+ close(scanner->pipe_write_fd);
+ scanner->pipe_write_fd = -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE READ ---------------------------------- */
+
+
+SANE_Status sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
+{
+ Umax_Scanner *scanner = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ nread = read(scanner->pipe_read_fd, buf, max_len);
+
+ DBG(DBG_sane_info, "sane_read: read %ld bytes\n", (long) nread);
+
+ if (!(scanner->scanning)) /* OOPS, not scanning */
+ {
+ return do_cancel(scanner);
+ }
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ DBG(DBG_sane_info, "sane_read: EAGAIN\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel(scanner); /* we had an error, stop scanner */
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0) /* EOF */
+ {
+ if ( (scanner->device->three_pass == 0) ||
+ (scanner->device->colormode <= RGB_LINEART) ||
+ (++(scanner->device->three_pass_color) > 3) )
+ {
+ do_cancel(scanner);
+ }
+
+ DBG(DBG_sane_proc,"closing read end of pipe\n");
+
+ if (scanner->pipe_read_fd >= 0)
+ {
+ close(scanner->pipe_read_fd);
+ scanner->pipe_read_fd = -1;
+ }
+
+ return SANE_STATUS_EOF;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE CANCEL -------------------------------- */
+
+
+void sane_cancel(SANE_Handle handle)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_init,"sane_cancel\n");
+
+ if (scanner->scanning)
+ {
+ do_cancel(scanner);
+ }
+}
+
+
+/* ------------------------------------------------------------ SANE SET IO MODE --------------------------- */
+
+
+SANE_Status sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_init,"sane_set_io_mode: non_blocking=%d\n", non_blocking);
+
+ if (!scanner->scanning) { return SANE_STATUS_INVAL; }
+
+ if (fcntl(scanner->pipe_read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/* ------------------------------------------------------------ SANE GET SELECT FD ------------------------- */
+
+
+SANE_Status sane_get_select_fd(SANE_Handle handle, SANE_Int *fd)
+{
+ Umax_Scanner *scanner = handle;
+
+ DBG(DBG_sane_init,"sane_get_select_fd\n");
+
+ if (!scanner->scanning)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = scanner->pipe_read_fd;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* ------------------------------------------------------------ EOF ---------------------------------------- */