diff options
Diffstat (limited to 'backend/umax.c')
-rw-r--r-- | backend/umax.c | 8131 |
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 ---------------------------------------- */ |