diff options
Diffstat (limited to 'backend/teco2.c')
-rw-r--r-- | backend/teco2.c | 3455 |
1 files changed, 3455 insertions, 0 deletions
diff --git a/backend/teco2.c b/backend/teco2.c new file mode 100644 index 0000000..9bc8bf4 --- /dev/null +++ b/backend/teco2.c @@ -0,0 +1,3455 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2002-2003 Frank Zago (sane at zago dot net) + Copyright (C) 2003-2008 Gerard Klaver (gerard at gkall dot hobby dot nl) + + 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. +*/ + +/* + $Id$ + TECO scanner VM3575, VM656A, VM6575, VM6586, VM356A, VM3564 + update 2003/02/14, Patch for VM356A Gerard Klaver + update 2003/03/19, traces, tests VM356A Gerard Klaver, Michael Hoeller + update 2003/07/19, white level calibration, color modes VM3564, VM356A, VM3575 + Gerard Klaver, Michael Hoeller + update 2004/01/15, white level , red, green, and blue calibration and + leave out highest and lowest value and then divide + VM3564, VM356A and VM3575: Gerard Klaver + update 2004/08/04, white level, red, green and blue calibration for the VM6575 + changed default SANE_TECO_CAL_ALGO to 1 for VM3564 and VM6575 + preview value changed to 75 dpi for VM6575 + leave out highest and lowest value and then divide + VM656A, VM6586 + update 2004/08/05, use of SANE_VALUE_SCAN_MODE_LINEART, _GRAY, and _COLOR, + changed use of %d to %ld (when bytes values are displayed) + update 2005/03/04, use of __sane_unused__ + update 2005/07/29. Removed using teco_request_sense (dev) routine for VM3564 + update 2008/01/12, Update teco_request_sense routine due to no + init value for size. +*/ + +/*--------------------------------------------------------------------------*/ + +#define BUILD 10 /* 2008/01/12 */ +#define BACKEND_NAME teco2 +#define TECO2_CONFIG_FILE "teco2.conf" + +/*--------------------------------------------------------------------------*/ + +#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/lassert.h" + +#include "teco2.h" + +/* This is used to debug the backend without having the real hardware. */ +#undef sim +#ifdef sim +#define sanei_scsi_cmd2(a, b, c, d, e, f, g) SANE_STATUS_GOOD +#define sanei_scsi_open(a, b, c, d) 0 +#define sanei_scsi_cmd(a, b, c, d, e) SANE_STATUS_GOOD +#define sanei_scsi_close(a) SANE_STATUS_GOOD +#endif + +/* For debugging purposes: output a stream straight out from the + * scanner without reordering the colors, 0=normal, 1 = raw. */ +static int raw_output = 0; + +/*--------------------------------------------------------------------------*/ + +/* Lists of possible scan modes. */ +static SANE_String_Const scan_mode_list[] = { + SANE_VALUE_SCAN_MODE_LINEART, + SANE_VALUE_SCAN_MODE_GRAY, + SANE_VALUE_SCAN_MODE_COLOR, + NULL +}; + +/*--------------------------------------------------------------------------*/ + +/* List of color dropout. */ +static SANE_String_Const filter_color_list[] = { + "Red", + "Green", + "Blue", + NULL +}; +static const int filter_color_val[] = { + 0, + 1, + 2 +}; + +/*--------------------------------------------------------------------------*/ + +/* List of dithering options. */ +static SANE_String_Const dither_list[] = { + "Line art", + "2x2", + "3x3", + "4x4 bayer", + "4x4 smooth", + "8x8 bayer", + "8x8 smooth", + "8x8 horizontal", + "8x8 vertical", + NULL +}; +static const int dither_val[] = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08 +}; + +/*--------------------------------------------------------------------------*/ + +static const SANE_Range threshold_range = { + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; + +/*--------------------------------------------------------------------------*/ + +static const SANE_Range red_level_range = { + 0, /* minimum */ + 64, /* maximum */ + 1 /* quantization */ +}; + +/*--------------------------------------------------------------------------*/ + +static const SANE_Range green_level_range = { + 0, /* minimum */ + 64, /* maximum */ + 1 /* quantization */ +}; + +/*--------------------------------------------------------------------------*/ + +static const SANE_Range blue_level_range = { + 0, /* minimum */ + 64, /* maximum */ + 1 /* quantization */ +}; + +/*--------------------------------------------------------------------------*/ + +/* Gamma range */ +static const SANE_Range gamma_range = { + 0, /* minimum */ + 255, /* 255 maximum */ + 0 /* quantization */ +}; + +/*--------------------------------------------------------------------------*/ + +static const struct dpi_color_adjust vm3564_dpi_color_adjust[] = { + + /*dpi, color sequence R G or B, 0 (-) or 1 (+) color skewing, lines skewing */ + {25, 1, 0, 2, 0, 0}, + {50, 1, 2, 0, 0, 1}, + {75, 1, 0, 2, 1, 1}, + {150, 1, 0, 2, 1, 2}, + {225, 1, 0, 2, 1, 3}, + {300, 1, 0, 2, 1, 4}, + {375, 1, 0, 2, 1, 5}, + {450, 1, 0, 2, 1, 6}, + {525, 1, 0, 2, 1, 7}, + {600, 1, 0, 2, 1, 8}, + /* must be the last entry */ + {0, 0, 0, 0, 0, 0} +}; + +/* Used for VM356A only */ + +static const struct dpi_color_adjust vm356a_dpi_color_adjust[] = { + + /* All values with ydpi > 300 result in a wrong proportion for */ + /* the scan. The proportion can be adjusted with the following */ + /* command: convert -geometry (dpi/max_xdpi * 100%)x100% */ + /* max_xdpi is for the vm356a constant with 300 dpi */ + /* e.g. 600dpi adjust with: convert -geometry 200%x100% */ + + /* All resolutions in increments of 25 are testede, only the shown */ + /* bring back good results */ + + /*dpi, color sequence R G or B, 0 (-) or 1 (+) color skewing, lines skewing */ + {25, 1, 0, 2, 0, 0}, + {50, 1, 2, 0, 0, 1}, + {75, 1, 0, 2, 1, 1}, + {150, 1, 0, 2, 1, 2}, + {225, 1, 0, 2, 1, 3}, + {300, 1, 0, 2, 1, 4}, + {375, 1, 0, 2, 1, 5}, + {450, 1, 0, 2, 1, 6}, + {525, 1, 0, 2, 1, 7}, + {600, 1, 0, 2, 1, 8}, + + /* must be the last entry */ + {0, 0, 0, 0, 0, 0} +}; + +/* Used for the VM3575 only */ + +static const struct dpi_color_adjust vm3575_dpi_color_adjust[] = { + + /* All values with ydpi > 300 result in a wrong proportion for */ + /* the scan. The proportion can be adjusted with the following */ + /* command: convert -geometry (dpi/max_xdpi * 100%)x100% */ + /* max_xdpi is for the vm3575 constant with 300 dpi */ + /* e.g. 600dpi adjust with: convert -geometry 200%x100% */ + + {50, 2, 0, 1, 1, 1}, + {60, 2, 1, 0, 0, 2}, +/* {75, 2, 0, 1, 1, 2}, NOK, effects in scan, also with twain driver*/ + {100, 2, 1, 0, 0, 3}, + {120, 2, 0, 1, 1, 3}, + {150, 2, 0, 1, 1, 4}, +/* {180, 2, 1, 0, 0, 4}, NOK, skewing problem*/ + {225, 2, 0, 1, 1, 6}, + {300, 2, 0, 1, 1, 8}, + {375, 2, 0, 1, 1, 10}, + {450, 2, 0, 1, 1, 12}, + {525, 2, 0, 1, 1, 14}, + {600, 2, 0, 1, 1, 16}, + + /* must be the last entry */ + {0, 0, 0, 0, 0, 0} +}; + +static const struct dpi_color_adjust vm656a_dpi_color_adjust[1] = { + {0, 0, 1, 2, 0, 0} +}; + +static const struct dpi_color_adjust vm6575_dpi_color_adjust[] = { + {75, 1, 0, 2, 1, 1}, + {150, 1, 0, 2, 1, 2}, + {300, 1, 0, 2, 1, 4}, + {600, 1, 0, 2, 1, 8}, +}; + +static const struct dpi_color_adjust vm6586_dpi_color_adjust[] = { + {25, 1, 0, 2, 1, 0}, + {40, 1, 0, 2, 1, 0}, + {50, 1, 0, 2, 1, 0}, + {75, 1, 2, 0, 0, 1}, + {150, 1, 0, 2, 1, 1}, + {160, 1, 0, 2, 1, 1}, + {175, 1, 0, 2, 1, 1}, + {180, 1, 0, 2, 1, 1}, + {300, 1, 0, 2, 1, 2}, + {320, 1, 0, 2, 1, 2}, + {325, 1, 0, 2, 1, 2}, + {450, 1, 0, 2, 1, 3}, + {460, 1, 0, 2, 1, 3}, + {600, 1, 0, 2, 1, 4}, + {620, 1, 0, 2, 1, 6}, + {625, 1, 0, 2, 1, 6}, + {750, 1, 0, 2, 1, 12}, + {760, 1, 0, 2, 1, 12}, + {900, 1, 0, 2, 1, 12}, + {1050, 1, 0, 2, 1, 15}, + {1200, 1, 0, 2, 1, 15}, + + /* must be the last entry */ + {0, 0, 0, 0, 0, 0} +}; + +static const struct dpi_color_adjust default_dpi_color_adjust[1] = { + {0, 0, 1, 2, 0, 0} +}; + +/* For all scanners. Must be reasonable (eg. between 50 and 300) and + * appear in the ...._dpi_color_adjust list of all supported scanners. */ +#define DEF_RESOLUTION 150 + +/*--------------------------------------------------------------------------*/ + +/* Define the supported scanners and their characteristics. */ +static const struct scanners_supported scanners[] = { + + {6, "TECO VM3564", + TECO_VM3564, + "Relisys", "AVEC II S3", + {1, 600, 1}, /* resolution */ + 300, 600, /* max x and Y resolution */ + 2550, 12, 3, 1, /* calibration */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm3564_dpi_color_adjust}, + + {6, "TECO VM356A", + TECO_VM356A, + "Relisys", "APOLLO Express 3", + {1, 600, 1}, /* resolution */ + 300, 600, /* max x and Y resolution */ + 2550, 12, 3, 1, /* calibration */ + /* dots/inch * x-length, calibration samples, tot.bytes for 3 colors, default SANE_TECO2_CAL_ALGO value */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm356a_dpi_color_adjust}, + + {6, "TECO VM356A", + TECO_VM356A, + "Primax", "Jewel 4800", + {1, 600, 1}, /* resolution */ + 300, 600, /* max x and Y resolution */ + 2550, 12, 3, 1, /* calibration */ + /* dots/inch * x-length, calibration samples, tot.bytes for 3 colors, default SANE_TECO2_CAL_ALGO value */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm356a_dpi_color_adjust}, + + {6, "TECO VM3575", + TECO_VM3575, + "Relisys", "SCORPIO Super 3", + {1, 600, 1}, /* resolution */ + 300, 600, /* max x and Y resolution */ + 2550, 12, 6, 1, /* calibration */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm3575_dpi_color_adjust}, + + {6, "TECO VM3575", + TECO_VM3575, + "Relisys", "AVEC Super 3", + {1, 600, 1}, /* resolution */ + 300, 600, /* max x and Y resolution */ + 2550, 12, 6, 1, /* calibration */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm3575_dpi_color_adjust}, + + {6, "TECO VM656A", + TECO_VM656A, + "Relisys", "APOLLO Express 6", + {1, 600, 1}, /* resolution */ + 600, 1200, /* max x and Y resolution */ + 5100, 8, 6, 0, /* calibration */ + {SANE_FIX (0), SANE_FIX (210), 0}, + {SANE_FIX (0), SANE_FIX (297), 0}, + vm656a_dpi_color_adjust}, + + {6, "TECO VM6575", + TECO_VM6575, + "Relisys", "SCORPIO Pro", + {1, 600, 1}, /* resolution */ + 600, 1200, /* max x and Y resolution */ + 5100, 8, 6, 1, /* calibration */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm6575_dpi_color_adjust}, + + {6, "TECO VM6575", + TECO_VM6575, + "Primax", "Profi 9600", + {1, 600, 1}, /* resolution */ + 600, 1200, /* max x and Y resolution */ + 5100, 8, 6, 1, /* calibration */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm6575_dpi_color_adjust}, + + {6, "TECO VM6586", + TECO_VM6586, + "Relisys", "SCORPIO Pro-S", + {1, 600, 1}, /* resolution */ + 600, 1200, /* max x and Y resolution */ + 5100, 8, 6, 0, /* calibration */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm6586_dpi_color_adjust}, + + {6, "TECO VM6586", + TECO_VM6586, + "Primax", "Profi 19200", + {1, 600, 1}, /* resolution */ + 600, 1200, /* max x and Y resolution */ + 5100, 8, 6, 0, /* calibration */ + {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, + {SANE_FIX (0), SANE_FIX (11.7 * MM_PER_INCH), 0}, + vm6586_dpi_color_adjust} +}; + +/*--------------------------------------------------------------------------*/ + +/* List of scanner attached. */ +static Teco_Scanner *first_dev = NULL; +static int num_devices = 0; +static const SANE_Device **devlist = NULL; + + +/* Local functions. */ + +/* Display a buffer in the log. Display by lines of 16 bytes. */ +static void +hexdump (int level, const char *comment, unsigned char *buf, const int length) +{ + int i; + char line[128]; + char *ptr; + char asc_buf[17]; + char *asc_ptr; + + DBG (level, " %s\n", comment); + + i = 0; + goto start; + + do + { + if (i < length) + { + ptr += sprintf (ptr, " %2.2x", *buf); + + if (*buf >= 32 && *buf <= 127) + { + asc_ptr += sprintf (asc_ptr, "%c", *buf); + } + else + { + asc_ptr += sprintf (asc_ptr, "."); + } + } + else + { + /* After the length; do nothing. */ + ptr += sprintf (ptr, " "); + } + + i++; + buf++; + + if ((i % 16) == 0) + { + /* It's a new line */ + DBG (level, " %s %s\n", line, asc_buf); + + start: + ptr = line; + *ptr = '\0'; + asc_ptr = asc_buf; + *asc_ptr = '\0'; + + ptr += sprintf (ptr, " %3.3d:", i); + } + + } + while (i < ((length + 15) & ~15)); +} + +/* Returns the length of the longest string, including the terminating + * character. */ +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; +} + +/* Lookup a string list from one array and return its index. */ +static int +get_string_list_index (SANE_String_Const list[], SANE_String_Const name) +{ + int index; + + index = 0; + while (list[index] != NULL) + { + if (strcmp (list[index], name) == 0) + { + return (index); + } + index++; + } + + DBG (DBG_error, "name %s not found in list\n", name); + + assert (0); /* bug in backend, core dump */ + + return (-1); +} + +/* Initialize a scanner entry. Return an allocated scanner with some + * preset values. */ +static Teco_Scanner * +teco_init (void) +{ + Teco_Scanner *dev; + + DBG (DBG_proc, "teco_init: enter\n"); + + /* Allocate a new scanner entry. */ + dev = malloc (sizeof (Teco_Scanner)); + if (dev == NULL) + { + return NULL; + } + + memset (dev, 0, sizeof (Teco_Scanner)); + + /* Allocate the buffer used to transfer the SCSI data. */ + dev->buffer_size = 64 * 1024; + dev->buffer = malloc (dev->buffer_size); + if (dev->buffer == NULL) + { + free (dev); + return NULL; + } + + dev->sfd = -1; + + DBG (DBG_proc, "teco_init: exit\n"); + + return (dev); +} + +/* Closes an open scanner. */ +static void +teco_close (Teco_Scanner * dev) +{ + DBG (DBG_proc, "teco_close: enter\n"); + + if (dev->sfd != -1) + { + sanei_scsi_close (dev->sfd); + dev->sfd = -1; + } + + DBG (DBG_proc, "teco_close: exit\n"); +} + +/* Frees the memory used by a scanner. */ +static void +teco_free (Teco_Scanner * dev) +{ + int i; + + DBG (DBG_proc, "teco_free: enter\n"); + + if (dev == NULL) + return; + + teco_close (dev); + if (dev->devicename) + { + free (dev->devicename); + } + if (dev->buffer) + { + free (dev->buffer); + } + for (i = 1; i < OPT_NUM_OPTIONS; i++) + { + if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s) + { + free (dev->val[i].s); + } + } + if (dev->resolutions_list) + free (dev->resolutions_list); + + free (dev); + + DBG (DBG_proc, "teco_free: exit\n"); +} + +/* Inquiry a device and returns TRUE if is supported. */ +static int +teco_identify_scanner (Teco_Scanner * dev) +{ + CDB cdb; + SANE_Status status; + size_t size; + int i; + + DBG (DBG_proc, "teco_identify_scanner: enter\n"); + + size = 5; + MKSCSI_INQUIRY (cdb, size); + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status) + { + DBG (DBG_error, + "teco_identify_scanner: inquiry failed with status %s\n", + sane_strstatus (status)); + return (SANE_FALSE); + } + + size = dev->buffer[4] + 5; /* total length of the inquiry data */ + +#ifndef sim + if (size < 53) + { + DBG (DBG_error, + "teco_identify_scanner: not enough data to identify device\n"); + return (SANE_FALSE); + } +#endif +#ifdef sim + { +#if 0 + /* vm3564 */ + unsigned char table[] = { + 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x10, 0x52, 0x45, + 0x4c, 0x49, 0x53, 0x59, 0x53, 0x20, 0x41, 0x56, 0x45, 0x43, + 0x20, 0x49, 0x49, 0x20, 0x53, 0x33, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x31, 0x2e, 0x30, 0x37, 0x31, 0x2e, 0x30, 0x37, + 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x33, + 0x35, 0x36, 0x34, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01, + 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08, + 0x01, 0x00 + }; +#endif +#if 0 + /* vm356A */ + unsigned char table[] = { + 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x10, 0x50, 0x72, + 0x69, 0x6d, 0x61, 0x78, 0x20, 0x20, 0x4a, 0x65, 0x77, 0x65, + 0x6c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x31, 0x2e, 0x30, 0x30, 0x31, 0x2e, 0x30, 0x30, + 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x33, + 0x35, 0x36, 0x41, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01, + 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08, + 0x10, 0x00 + }; +#endif +#if 1 + /* vm3575 */ + unsigned char table[] = { + 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x00, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x6c, 0x61, 0x74, + 0x62, 0x65, 0x64, 0x20, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65, + 0x72, 0x20, 0x31, 0x2e, 0x30, 0x33, 0x31, 0x2e, 0x30, 0x33, + 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x33, + 0x35, 0x37, 0x35, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01, + 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08, + 0x01, 0x00 + }; +#endif +#if 0 + /* vm6586 */ + unsigned char table[] = { + 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x00, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x6c, 0x61, 0x74, + 0x62, 0x65, 0x64, 0x20, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65, + 0x72, 0x20, 0x33, 0x2e, 0x30, 0x31, 0x33, 0x2e, 0x30, 0x31, + 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x36, + 0x35, 0x38, 0x36, 0x20, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01, + 0x02, 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08, + 0x01, 0x00 + }; +#endif +#if 0 + /* vm656A */ + unsigned char table[] = { + 0x06, 0x00, 0x02, 0x02, 0x43, 0x00, 0x00, 0x00, 0x52, 0x45, + 0x4c, 0x49, 0x53, 0x59, 0x53, 0x20, 0x41, 0x50, 0x4f, 0x4c, + 0x4c, 0x4f, 0x20, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, + 0x20, 0x36, 0x31, 0x2e, 0x30, 0x33, 0x31, 0x2e, 0x30, 0x33, + 0x00, 0x01, 0x54, 0x45, 0x43, 0x4f, 0x20, 0x56, 0x4d, 0x36, + 0x35, 0x36, 0x41, 0x00, 0x01, 0x01, 0x2c, 0x00, 0x01, 0x02, + 0x58, 0x09, 0xf6, 0x0d, 0xaf, 0x01, 0x2c, 0x00, 0x08, 0x01, + 0x00, 0x00 + }; +#endif + size = sizeof (table); + memcpy (dev->buffer, table, sizeof (table)); + } +#endif + + MKSCSI_INQUIRY (cdb, size); + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status) + { + DBG (DBG_error, + "teco_identify_scanner: inquiry failed with status %s\n", + sane_strstatus (status)); + return (SANE_FALSE); + } + + hexdump (DBG_info2, "inquiry", dev->buffer, size); + + dev->scsi_type = dev->buffer[0] & 0x1f; + memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08); + dev->scsi_vendor[0x08] = 0; + memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010); + dev->scsi_product[0x10] = 0; + memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04); + dev->scsi_version[0x04] = 0; + memcpy (dev->scsi_teco_name, dev->buffer + 0x2A, 0x0B); + dev->scsi_teco_name[0x0B] = 0; + + DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\" \"%s\"\n", + dev->scsi_vendor, dev->scsi_product, dev->scsi_version, + dev->scsi_teco_name); + + /* Lookup through the supported scanners table to find if this + * backend supports that one. */ + for (i = 0; i < NELEMS (scanners); i++) + { + if (dev->scsi_type == scanners[i].scsi_type && + strcmp (dev->scsi_teco_name, scanners[i].scsi_teco_name) == 0) + { + + DBG (DBG_error, "teco_identify_scanner: scanner supported\n"); + + /*patch for VM356A Jewel or Apollo text in frontend-+ others-------------- */ + + if (strncmp (dev->scsi_vendor, "RELISYS", 1) == 0 || + strncmp (dev->scsi_vendor, " ", 1) == 0) + { + DBG (DBG_error, + "teco_identify_scanner: scanner detected with this teco_name and first brand/name entry in table\n"); + + dev->def = &(scanners[i]); + + return (SANE_TRUE); + } + else + { + i++; + + DBG (DBG_error, + "teco_identify_scanner: scanner detected with this teco_name and second brand/name entry in table\n"); + + dev->def = &(scanners[i]); + + return (SANE_TRUE); + } + + } + } + + DBG (DBG_proc, "teco_identify_scanner: exit, device not supported\n"); + + return (SANE_FALSE); +} + +/* Set a window. */ +static SANE_Status +teco_set_window (Teco_Scanner * dev) +{ + size_t size; /* significant size of window */ + CDB cdb; + unsigned char window[56]; + SANE_Status status; + int i; + + DBG (DBG_proc, "teco_set_window: enter\n"); + + /* size of the whole windows block */ + size = 0; /* keep gcc quiet */ + switch (dev->def->tecoref) + { + case TECO_VM3575: + size = 53; + break; + case TECO_VM3564: + case TECO_VM356A: + case TECO_VM656A: + case TECO_VM6575: + case TECO_VM6586: + size = 56; + break; + default: + assert (0); + } + + /* Check that window is big enough */ + assert (size <= sizeof (window)); + + MKSCSI_SET_WINDOW (cdb, size); + + memset (window, 0, size); + + /* size of the windows descriptor block */ + window[7] = size - 8; + + /* X and Y resolution */ + Ito16 (dev->x_resolution, &window[10]); + Ito16 (dev->y_resolution, &window[12]); + + /* Upper Left (X,Y) */ + Ito32 (dev->x_tl, &window[14]); + Ito32 (dev->y_tl, &window[18]); + + /* Width and length */ + Ito32 (dev->width, &window[22]); + Ito32 (dev->length, &window[26]); + + /* Image Composition */ + switch (dev->scan_mode) + { + case TECO_BW: + window[31] = dev->val[OPT_THRESHOLD].w; + window[33] = 0x00; + i = get_string_list_index (dither_list, dev->val[OPT_DITHER].s); + window[36] = dither_val[i]; + break; + case TECO_GRAYSCALE: + window[33] = 0x02; + break; + case TECO_COLOR: + window[33] = 0x05; + break; + } + + /* Depth */ + window[34] = dev->depth; + + /* Lamp to use */ + i = get_string_list_index (filter_color_list, dev->val[OPT_FILTER_COLOR].s); + window[48] = filter_color_val[i]; + + /* Unknown - invariants */ + window[31] = 0x80; + window[37] = 0x80; + + /* Set the expected size line and number of lines. */ + if (dev->def->tecoref == TECO_VM656A || + dev->def->tecoref == TECO_VM6575 || dev->def->tecoref == TECO_VM6586) + { + + switch (dev->scan_mode) + { + case TECO_BW: + case TECO_GRAYSCALE: + Ito16 (dev->params.bytes_per_line, &window[52]); + break; + case TECO_COLOR: + Ito16 (dev->params.bytes_per_line / 3, &window[52]); + break; + } + Ito16 (dev->params.lines, &window[54]); + } + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + hexdump (DBG_info2, "windows", window, size); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + window, size, NULL, NULL); + + DBG (DBG_proc, "teco_set_window: exit, status=%d\n", status); + + return status; +} + +/* Park the CCD */ +static SANE_Status +teco_reset_window (Teco_Scanner * dev) +{ + SANE_Status status; + CDB cdb; + + DBG (DBG_proc, "teco_reset_window: enter\n"); + + MKSCSI_OBJECT_POSITION (cdb, 0); + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); + + DBG (DBG_proc, "teco_reset_window: leave, status=%d\n", status); + + return status; +} + +#ifdef sim +unsigned char *image_buf; +int image_buf_begin; +#endif + +/* Read the size of the scan. */ +static SANE_Status +teco_get_scan_size (Teco_Scanner * dev) +{ + size_t size; + CDB cdb; + SANE_Status status; + + DBG (DBG_proc, "teco_get_scan_size: enter\n"); + + size = 0x12; + MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size); + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + assert (size == 0x12); + + hexdump (DBG_info2, "teco_get_scan_size return", dev->buffer, size); + +#ifndef sim + dev->params.lines = B16TOI (&dev->buffer[12]); + dev->bytes_per_raster = B16TOI (&dev->buffer[14]); + + switch (dev->scan_mode) + { + case TECO_BW: + dev->params.bytes_per_line = B16TOI (&dev->buffer[14]); + dev->params.pixels_per_line = dev->params.bytes_per_line * 8; + break; + case TECO_GRAYSCALE: + dev->params.pixels_per_line = B16TOI (&dev->buffer[14]); + dev->params.bytes_per_line = dev->params.pixels_per_line; + break; + case TECO_COLOR: + dev->params.pixels_per_line = B16TOI (&dev->buffer[14]); + dev->params.bytes_per_line = dev->params.pixels_per_line * 3; + break; + } +#else + { + size_t s1, s2; + int fd; + char *name; + + switch (dev->def->tecoref) + { + case TECO_VM3575: + + if (dev->scan_mode == TECO_GRAYSCALE) + { + dev->params.pixels_per_line = 850; + dev->params.bytes_per_line = 850; + dev->params.lines = 1170; + dev->bytes_per_raster = 850; + + name = "/home/fzago/sane/teco2/vm3575/gr100d-2.raw"; + + } + else + { + switch (dev->val[OPT_RESOLUTION].w) + { + case 50: + dev->params.pixels_per_line = 425; + dev->params.lines = 585; + name = "/home/fzago/sane/teco2/vm3575/out12-co50.raw"; + break; + case 60: + dev->params.pixels_per_line = 510; + dev->params.lines = 702; + name = "/home/fzago/sane/teco2/vm3575/out12-co60.raw"; + break; + case 75: + dev->params.pixels_per_line = 638; + dev->params.lines = 431; + name = "/home/fzago/sane/teco2/vm3575/out12-co75.raw"; + break; + case 100: + dev->params.pixels_per_line = 402; + dev->params.lines = 575; + name = "/home/fzago/sane/teco2/vm3575/out12-co100.raw"; + break; + } + + dev->params.bytes_per_line = dev->params.pixels_per_line * 3; + dev->bytes_per_raster = dev->params.pixels_per_line; + } + break; + + case TECO_VM6586: + switch (dev->val[OPT_RESOLUTION].w) + { + case 150: + dev->params.pixels_per_line = 297; + dev->params.lines = 296; + name = "/home/fzago/sane/teco2/vm6586/150dpi.raw"; + break; + case 600: + dev->params.pixels_per_line = 1186; + dev->params.lines = 1186; + name = "/home/fzago/sane/teco2/vm6586/600dpi.raw"; + break; + } + + dev->params.bytes_per_line = dev->params.pixels_per_line * 3; + dev->bytes_per_raster = dev->params.pixels_per_line; + } + + + s1 = dev->params.bytes_per_line * dev->params.lines; + image_buf = malloc (s1); + assert (image_buf); + image_buf_begin = 0; + + fd = open (name, O_RDONLY); + assert (fd >= 0); + s2 = read (fd, image_buf, s1); + assert (s1 == s2); + close (fd); + + } +#endif + + DBG (DBG_proc, "teco_get_scan_size: exit, status=%d\n", status); + + return (status); +} + +/* Wait until the scanner is ready to send the data. This is the + almost the same code as teco_get_scan_size(). This function has to + be called once after the scan has started. */ +static SANE_Status +teco_wait_for_data (Teco_Scanner * dev) +{ + size_t size; + CDB cdb; + SANE_Status status; + int i; + +#ifdef sim + return SANE_STATUS_GOOD; +#endif + + DBG (DBG_proc, "teco_wait_for_data: enter\n"); + + for (i = 0; i < 60; i++) + { + + size = 0x12; + MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + assert (size == 0x12); + + hexdump (DBG_info2, "teco_wait_for_data return", dev->buffer, size); + + switch (dev->def->tecoref) + { + case TECO_VM3564: + case TECO_VM356A: + if (dev->buffer[11] > 0x01) + { + return SANE_STATUS_GOOD; + } + + sleep (1); + break; + /* For VM3575, VM6575, VM656A, VM6586 until otherwise default value is used */ + default: + if (dev->buffer[11] == 0x80) + { + return SANE_STATUS_GOOD; + } + + sleep (1); + break; + } + } + + DBG (DBG_proc, "teco_wait_for_data: scanner not ready to send data (%d)\n", + status); + + return SANE_STATUS_DEVICE_BUSY; +} + +/* Do the calibration stuff. Get 12 or 8 lines of data. Each pixel is coded + * in 6 bytes (2 per color) or 3 bytes (3564 and 356A). To do the calibration, + * allocates an array big enough for one line, read the 12 or 8 lines of calibration, + * subtract the highest and lowest value and do a average. + * The input line is 1 raster for each color. However + * the output line is interlaced (ie RBG for the first pixel, then RGB + * for the second, and so on...). The output line are the value to use + * to compensate for the white point. + * There is two algorithms: + * + * The range goes from 0 to 0xfff, and the average is 0x800. So if + * the average input is 0x700, the output value for that dot must be + * 0x1000-0x700=0x900. + * + * and + * + * the calibration needs to be a multiplication factor, to + * compensate for the too strong or too weak pixel in the sensor. + * + * See more info in doc/teco/teco2.txt + * + **/ +static SANE_Status +teco_do_calibration (Teco_Scanner * dev) +{ + SANE_Status status; + CDB cdb; + size_t size; + int i; + int j; + int colsub0_0, colsub0_1, colsub0_2; + int colsub1_0, colsub1_1, colsub1_2; + int *tmp_buf, *tmp_min_buf, *tmp_max_buf; /* hold the temporary calibration */ + size_t tmp_buf_size, tmp_min_buf_size, tmp_max_buf_size; + const char *calibration_algo; + int cal_algo; + + colsub0_0 = 0; + colsub0_1 = 0; + colsub0_2 = 0; + colsub1_0 = 0; + colsub1_1 = 0; + colsub1_2 = 0; + + DBG (DBG_proc, "teco_do_calibration: enter\n"); + + /* Get default calibration algorithm. */ + cal_algo = dev->def->cal_algo; + if ((calibration_algo = getenv ("SANE_TECO2_CAL_ALGO")) != NULL) + { + cal_algo = atoi (calibration_algo); + } + if (cal_algo != 0 && cal_algo != 1) + { + DBG (DBG_error, "Invalid calibration algorithm (%d)\n", cal_algo); + cal_algo = 0; + } + + switch (dev->def->tecoref) + { + case TECO_VM3564: + case TECO_VM356A: + /* white level red, green and blue calibration correction */ + /* 0x110 or 272 is middle value */ + colsub0_1 = 240 + (dev->val[OPT_WHITE_LEVEL_R].w); + colsub0_2 = 240 + (dev->val[OPT_WHITE_LEVEL_G].w); + colsub0_0 = 240 + (dev->val[OPT_WHITE_LEVEL_B].w); + /* 14000 is middle value */ + colsub1_1 = 12720 + (40 * dev->val[OPT_WHITE_LEVEL_R].w); + colsub1_2 = 12720 + (40 * dev->val[OPT_WHITE_LEVEL_G].w); + colsub1_0 = 12720 + (40 * dev->val[OPT_WHITE_LEVEL_B].w); + break; + case TECO_VM3575: + case TECO_VM6575: + /* 0x1100 or 4352 is middle value */ + colsub0_1 = 4096 + (8 * dev->val[OPT_WHITE_LEVEL_R].w); + colsub0_2 = 4096 + (8 * dev->val[OPT_WHITE_LEVEL_G].w); + colsub0_0 = 4096 + (8 * dev->val[OPT_WHITE_LEVEL_B].w); + /* 4206639 is middle value */ + colsub1_1 = 4078639 + (4000 * dev->val[OPT_WHITE_LEVEL_R].w); + colsub1_2 = 4078639 + (4000 * dev->val[OPT_WHITE_LEVEL_G].w); + colsub1_0 = 4078639 + (4000 * dev->val[OPT_WHITE_LEVEL_B].w); + break; + /* old default value */ + case TECO_VM656A: + case TECO_VM6586: + colsub0_1 = 0x1000; + colsub0_2 = 0x1000; + colsub0_0 = 0x1000; + colsub1_1 = 4206639; + colsub1_2 = 4206639; + colsub1_0 = 4206639; + break; + default: + colsub0_1 = 0x1000; + colsub0_2 = 0x1000; + colsub0_0 = 0x1000; + colsub1_1 = 4206639; + colsub1_2 = 4206639; + colsub1_0 = 4206639; + break; + } + + tmp_buf_size = dev->def->cal_length * 3 * sizeof (int); + tmp_min_buf_size = dev->def->cal_length * 3 * sizeof (int); + tmp_max_buf_size = dev->def->cal_length * 3 * sizeof (int); + tmp_buf = malloc (tmp_buf_size); + tmp_min_buf = malloc (tmp_min_buf_size); + tmp_max_buf = malloc (tmp_max_buf_size); + memset (tmp_buf, 0, tmp_buf_size); + switch (dev->def->tecoref) + { + case TECO_VM3564: + case TECO_VM356A: + memset (tmp_min_buf, 0xff, tmp_min_buf_size); + memset (tmp_max_buf, 0x00, tmp_max_buf_size); + break; + case TECO_VM3575: + case TECO_VM656A: + case TECO_VM6575: + case TECO_VM6586: + memset (tmp_min_buf, 0xffff, tmp_min_buf_size); + memset (tmp_max_buf, 0x0000, tmp_max_buf_size); + break; + } + + if ((tmp_buf == NULL) || (tmp_min_buf == NULL) || (tmp_max_buf == NULL)) + { + DBG (DBG_proc, "teco_do_calibration: not enough memory (%ld bytes)\n", + (long) tmp_buf_size); + return (SANE_STATUS_NO_MEM); + } + + for (i = 0; i < dev->def->cal_lines; i++) + { + + MKSCSI_VENDOR_SPEC (cdb, SCSI_VENDOR_09, 6); + + /* No idea what that is. */ + switch (dev->scan_mode) + { + case TECO_BW: + cdb.data[2] = 0x02; + break; + case TECO_GRAYSCALE: + cdb.data[2] = 0x01; + break; + case TECO_COLOR: + cdb.data[2] = 0x00; + break; + } + + /* Length of the scanner * number of bytes per color */ + size = dev->def->cal_length * dev->def->cal_col_len; + cdb.data[3] = (size >> 8) & 0xff; + cdb.data[4] = (size >> 0) & 0xff; + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "teco_do_calibration: cannot read from the scanner\n"); + free (tmp_buf); + return status; + } + +#ifdef sim + for (j = 0; j < dev->def->cal_length; j++) + { + dev->buffer[6 * j + 0] = 0x10; + dev->buffer[6 * j + 1] = 0x12; + dev->buffer[6 * j + 2] = 0x14; + dev->buffer[6 * j + 3] = 0x16; + dev->buffer[6 * j + 4] = 0x20; + dev->buffer[6 * j + 5] = 0x25; + } +#endif + /*hexdump (DBG_info2, "got calibration line:", dev->buffer, size); */ + + for (j = 0; j < dev->def->cal_length; j++) + { + switch (dev->def->tecoref) + { + case TECO_VM3575: + case TECO_VM6575: + case TECO_VM656A: + case TECO_VM6586: + tmp_buf[3 * j + 0] += + (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0]; + /* get lowest value */ + if (tmp_min_buf[3 * j + 0] >> ((dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0])) + { + tmp_min_buf[3 * j + 0] = (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0]; + } + /* get highest value */ + if (tmp_max_buf[3 * j + 0] < ((dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0])) + { + tmp_max_buf[3 * j + 0] = (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0]; + } + tmp_buf[3 * j + 1] += + (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2]; + /* get lowest value */ + if (tmp_min_buf[3 * j + 1] >> ((dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2])) + { + tmp_min_buf[3 * j + 1] = (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2]; + } + /* get highest value */ + if (tmp_max_buf[3 * j + 1] < ((dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2])) + { + tmp_max_buf[3 * j + 1] = (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2]; + } + tmp_buf[3 * j + 2] += + (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4]; + /* get lowest value */ + if (tmp_min_buf[3 * j + 2] >> ((dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4])) + { + tmp_min_buf[3 * j + 2] = (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4]; + } + /* get highest value */ + if (tmp_max_buf[3 * j + 2] < ((dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4])) + { + tmp_max_buf[3 * j + 2] = (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4]; + } + break; + case TECO_VM3564: + case TECO_VM356A: + tmp_buf[3 * j + 0] += dev->buffer[3 * j + 0]; + /* get lowest value */ + if (tmp_min_buf[3 * j + 0] >> dev->buffer[3 * j + 0]) + { + tmp_min_buf[3 * j + 0] = dev->buffer[3 * j + 0]; + } + /* get highest value */ + if (tmp_max_buf[3 * j + 0] < dev->buffer[3 * j + 0]) + { + tmp_max_buf[3 * j + 0] = dev->buffer[3 * j + 0]; + } + tmp_buf[3 * j + 1] += dev->buffer[3 * j + 1]; + /* get lowest value */ + if (tmp_min_buf[3 * j + 1] >> dev->buffer[3 * j + 1]) + { + tmp_min_buf[3 * j + 1] = dev->buffer[3 * j + 1]; + } + /* get hightest value */ + if (tmp_max_buf[3 * j + 1] < dev->buffer[3 * j + 1]) + { + tmp_max_buf[3 * j + 1] = dev->buffer[3 * j + 1]; + } + tmp_buf[3 * j + 2] += dev->buffer[3 * j + 2]; + /* get lowest value */ + if (tmp_min_buf[3 * j + 2] >> dev->buffer[3 * j + 2]) + { + tmp_min_buf[3 * j + 2] = dev->buffer[3 * j + 2]; + } + /* get highest value */ + if (tmp_max_buf[3 * j + 2] < dev->buffer[3 * j + 2]) + { + tmp_max_buf[3 * j + 2] = dev->buffer[3 * j + 2]; + } + break; +/* default: + tmp_buf[3 * j + 0] += + (dev->buffer[6 * j + 1] << 8) + dev->buffer[6 * j + 0]; + tmp_buf[3 * j + 1] += + (dev->buffer[6 * j + 3] << 8) + dev->buffer[6 * j + 2]; + tmp_buf[3 * j + 2] += + (dev->buffer[6 * j + 5] << 8) + dev->buffer[6 * j + 4]; + break; */ + } + } + } + + /* hexdump (DBG_info2, "calibration before average:", tmp_buf, tmp_buf_size); */ + /* hexdump (DBG_info2, "calibration before average min value:", tmp_min_buf, tmp_min_buf_size); */ + /* hexdump (DBG_info2, "calibration before average max value:", tmp_max_buf, tmp_max_buf_size); */ + + /* Do the average. Since we got 12 or 8 lines, divide all values by 10 or 6 + * and create the final calibration value that compensates for the + * white values read. */ + switch (dev->def->tecoref) + { + case TECO_VM356A: + case TECO_VM3564: + case TECO_VM3575: + case TECO_VM6575: + case TECO_VM656A: + case TECO_VM6586: + for (j = 0; j < dev->def->cal_length; j++) + { + /* subtract lowest and highest value */ + tmp_buf[j] = tmp_buf[j] - (tmp_min_buf[j] + tmp_max_buf[j]); + tmp_buf[j + dev->def->cal_length] = tmp_buf[j + dev->def->cal_length] + - (tmp_min_buf[j + dev->def->cal_length] + + tmp_max_buf[j + dev->def->cal_length]); + tmp_buf[j + 2 * dev->def->cal_length] = tmp_buf[j + 2 * dev->def->cal_length] + - (tmp_min_buf[j + 2 * dev->def->cal_length] + + tmp_max_buf[j + 2 *dev->def->cal_length]); + /* sequence colors first color row one then two and last three */ + if (cal_algo == 1) + { + tmp_buf[j] = (colsub1_0 * (dev->def->cal_lines - 2)) / tmp_buf[j]; + tmp_buf[j + dev->def->cal_length] = (colsub1_1 * (dev->def->cal_lines - 2)) + / tmp_buf[j + dev->def->cal_length]; + tmp_buf[j + 2 * dev->def->cal_length] = (colsub1_2 * (dev->def->cal_lines - 2)) + / tmp_buf[j + 2 * dev->def->cal_length]; + } + else + { + tmp_buf[j] = colsub0_0 - (tmp_buf[j] / (dev->def->cal_lines - 2)); + tmp_buf[j + dev->def->cal_length] = colsub0_1 - (tmp_buf[j + dev->def->cal_length] + / (dev->def->cal_lines - 2)); + tmp_buf[j + 2 * dev->def->cal_length] = colsub0_2 + - (tmp_buf[j + 2 * dev->def->cal_length] / (dev->def->cal_lines - 2)); + } + } + break; +/* default: + for (j = 0; j < (3 * dev->def->cal_length); j++) + { + if (cal_algo == 1) + tmp_buf[j] = (colsub1_1 * dev->def->cal_lines) / tmp_buf[j]; + else + tmp_buf[j] = colsub0_1 - (tmp_buf[j] / dev->def->cal_lines); + } + break; */ + } + + /*hexdump (DBG_info2, "calibration after average:", tmp_buf, tmp_buf_size); */ + + /* Build the calibration line to send. */ + for (j = 0; j < dev->def->cal_length; j++) + { + + switch (dev->def->tecoref) + { + case TECO_VM3575: + case TECO_VM6575: + case TECO_VM656A: + case TECO_VM6586: + dev->buffer[6 * j + 0] = + (tmp_buf[0 * dev->def->cal_length + j] >> 0) & 0xff; + dev->buffer[6 * j + 1] = + (tmp_buf[0 * dev->def->cal_length + j] >> 8) & 0xff; + + dev->buffer[6 * j + 2] = + (tmp_buf[1 * dev->def->cal_length + j] >> 0) & 0xff; + dev->buffer[6 * j + 3] = + (tmp_buf[1 * dev->def->cal_length + j] >> 8) & 0xff; + + dev->buffer[6 * j + 4] = + (tmp_buf[2 * dev->def->cal_length + j] >> 0) & 0xff; + dev->buffer[6 * j + 5] = + (tmp_buf[2 * dev->def->cal_length + j] >> 8) & 0xff; + break; + case TECO_VM3564: + case TECO_VM356A: + dev->buffer[3 * j + 0] = (tmp_buf[3 * j + 0] >> 0) & 0xff; + + dev->buffer[3 * j + 1] = (tmp_buf[3 * j + 1] >> 0) & 0xff; + + dev->buffer[3 * j + 2] = (tmp_buf[3 * j + 2] >> 0) & 0xff; + break; + } + } + + free (tmp_buf); + tmp_buf = NULL; + + /* Send the calibration line. The CDB is the same as the previous + * one, except for the command. */ + + cdb.data[0] = 0x0E; + size = dev->def->cal_length * dev->def->cal_col_len; + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + /*hexdump (DBG_info2, "calibration line sent:", dev->buffer, size); */ + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + dev->buffer, size, NULL, NULL); + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "teco_do_calibration: calibration line was not sent correctly\n"); + return status; + } + + DBG (DBG_proc, "teco_do_calibration: leave\n"); + + return SANE_STATUS_GOOD; +} + +/*------------request sense command 03----------------*/ +static SANE_Status +teco_request_sense (Teco_Scanner * dev) +{ + SANE_Status status; + unsigned char buf[255]; + CDB cdb; + size_t size; + /* size = 0; */ + + DBG (DBG_proc, "teco_request_sense: enter\n"); + + size = sizeof (buf); + MKSCSI_REQUEST_SENSE (cdb, size); + + /*size = cdb.data[5]; + + hexdump (DBG_info2, "teco_request_sense", cdb.data, cdb.len); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); */ + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, buf, &size); + + hexdump (DBG_info2, "teco_request_sense:", buf, size); + + DBG (DBG_proc, "teco_request_sense: exit, status=%d\n", status); + + return (status); +} + +/*----------------------------------------------------*/ + +/* Send the gamma */ +static SANE_Status +teco_send_gamma (Teco_Scanner * dev) +{ + CDB cdb; + SANE_Status status; + struct + { + unsigned char gamma_R[GAMMA_LENGTH]; + unsigned char gamma_G[GAMMA_LENGTH]; /* also gray */ + unsigned char gamma_B[GAMMA_LENGTH]; + } + param; + size_t i; + size_t size; + + DBG (DBG_proc, "teco_send_gamma: enter\n"); + + size = sizeof (param); + assert (size == 3 * GAMMA_LENGTH); + MKSCSI_SEND_10 (cdb, 0x03, 0x04, size); + + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + /* Use the custom gamma. */ + if (dev->scan_mode == TECO_GRAYSCALE) + { + /* Gray */ + for (i = 0; i < GAMMA_LENGTH; i++) + { + param.gamma_R[i] = dev->gamma_GRAY[i]; + param.gamma_G[i] = dev->gamma_GRAY[i]; + param.gamma_B[i] = dev->gamma_GRAY[i]; + } + } + else + { + /* Color */ + for (i = 0; i < GAMMA_LENGTH; i++) + { + param.gamma_R[i] = dev->gamma_R[i]; + param.gamma_G[i] = dev->gamma_G[i]; + param.gamma_B[i] = dev->gamma_B[i]; + } + } + } + else + { + /* Defaults to a linear gamma for now. */ + for (i = 0; i < GAMMA_LENGTH; i++) + { + param.gamma_R[i] = i / 4; + param.gamma_G[i] = i / 4; + param.gamma_B[i] = i / 4; + } + } + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + switch (dev->def->tecoref) + { + case TECO_VM3564: + case TECO_VM356A: + status = 0; + break; + default: + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + ¶m, size, NULL, NULL); + } + DBG (DBG_proc, "teco_send_gamma: exit, status=%d\n", status); + + return (status); +} + +/* Send a vendor specific command. */ +static SANE_Status +teco_send_vendor_06 (Teco_Scanner * dev) +{ + SANE_Status status; + CDB cdb; + size_t size; + + DBG (DBG_proc, "teco_send_vendor_06: enter\n"); + + MKSCSI_VENDOR_SPEC (cdb, SCSI_VENDOR_06, 6); + + size = 4; + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + + MKSCSI_VENDOR_SPEC (cdb, SCSI_VENDOR_1C, 6); + + size = 4; + memset (dev->buffer, 0, size); + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + dev->buffer, size, NULL, NULL); + + DBG (DBG_proc, "teco_send_vendor_06: exit, status=%d\n", status); + + return (status); +} + +/* Start a scan. */ +static SANE_Status +teco_scan (Teco_Scanner * dev) +{ + CDB cdb; + SANE_Status status; + + DBG (DBG_proc, "teco_scan: enter\n"); + + MKSCSI_SCAN (cdb); + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); + + DBG (DBG_proc, "teco_scan: exit, status=%d\n", status); + + return status; +} + +/* SCSI sense handler. Callback for SANE. */ +static SANE_Status +teco_sense_handler (int scsi_fd, unsigned char *result, void __sane_unused__ *arg) +{ + int asc, ascq, sensekey; + int len; + + DBG (DBG_proc, "teco_sense_handler (scsi_fd = %d)\n", scsi_fd); + + sensekey = get_RS_sense_key (result); + len = 7 + get_RS_additional_length (result); + + hexdump (DBG_info2, "sense", result, len); + + if (get_RS_error_code (result) != 0x70) + { + DBG (DBG_error, + "teco_sense_handler: invalid sense key error code (%d)\n", + get_RS_error_code (result)); + + return SANE_STATUS_IO_ERROR; + } + + if (get_RS_ILI (result) != 0) + { + DBG (DBG_sense, "teco_sense_handler: short read\n"); + } + + if (len < 14) + { + DBG (DBG_error, "teco_sense_handler: sense too short, no ASC/ASCQ\n"); + + return SANE_STATUS_IO_ERROR; + } + + asc = get_RS_ASC (result); + ascq = get_RS_ASCQ (result); + + DBG (DBG_sense, "teco_sense_handler: sense=%d, ASC/ASCQ=%02x%02x\n", + sensekey, asc, ascq); + + switch (sensekey) + { + case 0x00: /* no sense */ + case 0x02: /* not ready */ + case 0x03: /* medium error */ + case 0x05: + case 0x06: + break; + } + + DBG (DBG_sense, + "teco_sense_handler: unknown error condition. Please report it to the backend maintainer\n"); + + return SANE_STATUS_IO_ERROR; +} + +/* Attach a scanner to this backend. */ +static SANE_Status +attach_scanner (const char *devicename, Teco_Scanner ** devp) +{ + Teco_Scanner *dev; + int sfd; + + DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename); + + if (devp) + *devp = NULL; + + /* Check if we know this device name. */ + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devicename) == 0) + { + if (devp) + { + *devp = dev; + } + DBG (DBG_info, "device is already known\n"); + return SANE_STATUS_GOOD; + } + } + + /* Allocate a new scanner entry. */ + dev = teco_init (); + if (dev == NULL) + { + DBG (DBG_error, "ERROR: not enough memory\n"); + return SANE_STATUS_NO_MEM; + } + + DBG (DBG_info, "attach_scanner: opening %s\n", devicename); + + if (sanei_scsi_open (devicename, &sfd, teco_sense_handler, dev) != 0) + { + DBG (DBG_error, "ERROR: attach_scanner: open failed\n"); + teco_free (dev); + return SANE_STATUS_INVAL; + } + +#ifdef sim + sfd = -1; +#endif + + /* Fill some scanner specific values. */ + dev->devicename = strdup (devicename); + dev->sfd = sfd; + + /* Now, check that it is a scanner we support. */ + if (teco_identify_scanner (dev) == SANE_FALSE) + { + DBG (DBG_error, + "ERROR: attach_scanner: scanner-identification failed\n"); + teco_free (dev); + return SANE_STATUS_INVAL; + } + + teco_close (dev); + + /* If this scanner only supports a given number of resolutions, + * build that list now. */ + if (dev->def->color_adjust[0].resolution != 0) + { + int num_entries; + int i; + + num_entries = 0; + while (dev->def->color_adjust[num_entries].resolution != 0) + num_entries++; + + dev->resolutions_list = malloc (sizeof (SANE_Word) * (num_entries + 1)); + + if (dev->resolutions_list == NULL) + { + DBG (DBG_error, + "ERROR: attach_scanner: scanner-identification failed\n"); + teco_free (dev); + return SANE_STATUS_NO_MEM; + } + + dev->resolutions_list[0] = num_entries; + for (i = 0; i < num_entries; i++) + { + dev->resolutions_list[i + 1] = dev->def->color_adjust[i].resolution; + } + } + else + { + dev->resolutions_list = NULL; + } + + /* Set the default options for that scanner. */ + dev->sane.name = dev->devicename; + dev->sane.vendor = dev->def->real_vendor; + dev->sane.model = dev->def->real_product; + dev->sane.type = SANE_I18N ("flatbed scanner"); + + /* Link the scanner with the others. */ + dev->next = first_dev; + first_dev = dev; + + if (devp) + { + *devp = dev; + } + + num_devices++; + + DBG (DBG_proc, "attach_scanner: exit\n"); + + return SANE_STATUS_GOOD; +} + +static SANE_Status +attach_one (const char *dev) +{ + attach_scanner (dev, NULL); + return SANE_STATUS_GOOD; +} + +/* Reset the options for that scanner. */ +static void +teco_init_options (Teco_Scanner * dev) +{ + int i; + + /* Pre-initialize the options. */ + memset (dev->opt, 0, sizeof (dev->opt)); + memset (dev->val, 0, sizeof (dev->val)); + + for (i = 0; i < OPT_NUM_OPTIONS; ++i) + { + dev->opt[i].size = sizeof (SANE_Word); + dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + + /* Number of options. */ + dev->opt[OPT_NUM_OPTS].name = ""; + dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; + dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; + dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS; + + /* Mode group */ + dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); + dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */ + dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_MODE_GROUP].cap = 0; + dev->opt[OPT_MODE_GROUP].size = 0; + dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* Scanner supported modes */ + dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + dev->opt[OPT_MODE].type = SANE_TYPE_STRING; + dev->opt[OPT_MODE].size = max_string_size (scan_mode_list); + dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + dev->opt[OPT_MODE].constraint.string_list = scan_mode_list; + dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */ + + /* X and Y resolution */ + dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; + dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; + dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_RESOLUTION].constraint.range = &dev->def->res_range; + dev->val[OPT_RESOLUTION].w = DEF_RESOLUTION; + + /* Geometry group */ + dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); + dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */ + dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_GEOMETRY_GROUP].cap = 0; + dev->opt[OPT_GEOMETRY_GROUP].size = 0; + dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* Upper left X */ + dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; + dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; + dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; + dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED; + dev->opt[OPT_TL_X].unit = SANE_UNIT_MM; + dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_X].constraint.range = &dev->def->x_range; + dev->val[OPT_TL_X].w = dev->def->x_range.min; + + /* Upper left Y */ + dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; + dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; + dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; + dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; + dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM; + dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_Y].constraint.range = &dev->def->y_range; + dev->val[OPT_TL_Y].w = dev->def->y_range.min; + + /* Bottom-right x */ + dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; + dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; + dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; + dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED; + dev->opt[OPT_BR_X].unit = SANE_UNIT_MM; + dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_X].constraint.range = &dev->def->x_range; + dev->val[OPT_BR_X].w = dev->def->x_range.max; + + /* Bottom-right y */ + dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; + dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; + dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; + dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; + dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM; + dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_Y].constraint.range = &dev->def->y_range; + dev->val[OPT_BR_Y].w = dev->def->y_range.max; + + /* Enhancement group */ + dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); + dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */ + dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; + dev->opt[OPT_ENHANCEMENT_GROUP].size = 0; + dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* Halftone pattern */ + dev->opt[OPT_DITHER].name = "dither"; + dev->opt[OPT_DITHER].title = SANE_I18N ("Dither"); + dev->opt[OPT_DITHER].desc = SANE_I18N ("Dither"); + dev->opt[OPT_DITHER].type = SANE_TYPE_STRING; + dev->opt[OPT_DITHER].size = max_string_size (dither_list); + dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_DITHER].constraint_type = SANE_CONSTRAINT_STRING_LIST; + dev->opt[OPT_DITHER].constraint.string_list = dither_list; + dev->val[OPT_DITHER].s = strdup (dither_list[0]); + + /* custom-gamma table */ + dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; + + /* red gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range; + dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R; + + /* green and gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range; + dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G; + + /* blue gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range; + dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B; + + /* grayscale gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &gamma_range; + dev->val[OPT_GAMMA_VECTOR_GRAY].wa = dev->gamma_GRAY; + + /* Color filter */ + dev->opt[OPT_FILTER_COLOR].name = "color-filter"; + dev->opt[OPT_FILTER_COLOR].title = "Color dropout"; + dev->opt[OPT_FILTER_COLOR].desc = "Color dropout"; + dev->opt[OPT_FILTER_COLOR].type = SANE_TYPE_STRING; + dev->opt[OPT_FILTER_COLOR].size = max_string_size (filter_color_list); + dev->opt[OPT_FILTER_COLOR].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_FILTER_COLOR].constraint_type = SANE_CONSTRAINT_STRING_LIST; + dev->opt[OPT_FILTER_COLOR].constraint.string_list = filter_color_list; + dev->val[OPT_FILTER_COLOR].s = (SANE_Char *) strdup (filter_color_list[0]); + + /* Threshold */ + dev->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; + dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; + dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; + dev->opt[OPT_THRESHOLD].type = SANE_TYPE_INT; + dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; + dev->opt[OPT_THRESHOLD].size = sizeof (SANE_Int); + dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_THRESHOLD].constraint.range = &threshold_range; + dev->val[OPT_THRESHOLD].w = 128; + + /* preview */ + dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; + dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + dev->val[OPT_PREVIEW].w = SANE_FALSE; + + /* red level calibration manual correction */ + dev->opt[OPT_WHITE_LEVEL_R].name = SANE_NAME_WHITE_LEVEL_R; + dev->opt[OPT_WHITE_LEVEL_R].title = SANE_TITLE_WHITE_LEVEL_R; + dev->opt[OPT_WHITE_LEVEL_R].desc = SANE_DESC_WHITE_LEVEL_R; + dev->opt[OPT_WHITE_LEVEL_R].type = SANE_TYPE_INT; + dev->opt[OPT_WHITE_LEVEL_R].unit = SANE_UNIT_NONE; + dev->opt[OPT_WHITE_LEVEL_R].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_WHITE_LEVEL_R].constraint.range = &red_level_range; + dev->val[OPT_WHITE_LEVEL_R].w = 32; /* to get middle value */ + + /* green level calibration manual correction */ + dev->opt[OPT_WHITE_LEVEL_G].name = SANE_NAME_WHITE_LEVEL_G; + dev->opt[OPT_WHITE_LEVEL_G].title = SANE_TITLE_WHITE_LEVEL_G; + dev->opt[OPT_WHITE_LEVEL_G].desc = SANE_DESC_WHITE_LEVEL_G; + dev->opt[OPT_WHITE_LEVEL_G].type = SANE_TYPE_INT; + dev->opt[OPT_WHITE_LEVEL_G].unit = SANE_UNIT_NONE; + dev->opt[OPT_WHITE_LEVEL_G].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_WHITE_LEVEL_G].constraint.range = &green_level_range; + dev->val[OPT_WHITE_LEVEL_G].w = 32; /* to get middle value */ + + /* blue level calibration manual correction */ + dev->opt[OPT_WHITE_LEVEL_B].name = SANE_NAME_WHITE_LEVEL_B; + dev->opt[OPT_WHITE_LEVEL_B].title = SANE_TITLE_WHITE_LEVEL_B; + dev->opt[OPT_WHITE_LEVEL_B].desc = SANE_DESC_WHITE_LEVEL_B; + dev->opt[OPT_WHITE_LEVEL_B].type = SANE_TYPE_INT; + dev->opt[OPT_WHITE_LEVEL_B].unit = SANE_UNIT_NONE; + dev->opt[OPT_WHITE_LEVEL_B].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_WHITE_LEVEL_B].constraint.range = &blue_level_range; + dev->val[OPT_WHITE_LEVEL_B].w = 32; /* to get middle value */ + + /* Lastly, set the default scan mode. This might change some + * values previously set here. */ + sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE, + (SANE_String_Const *) scan_mode_list[0], NULL); +} + +/* + * Wait until the scanner is ready. + */ +static SANE_Status +teco_wait_scanner (Teco_Scanner * dev) +{ + SANE_Status status; + int timeout; + CDB cdb; + + DBG (DBG_proc, "teco_wait_scanner: enter\n"); + + MKSCSI_TEST_UNIT_READY (cdb); + + /* Set the timeout to 60 seconds. */ + timeout = 60; + + while (timeout > 0) + { + + /* test unit ready */ + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd (dev->sfd, cdb.data, cdb.len, NULL, NULL); + + if (status == SANE_STATUS_GOOD) + { + return SANE_STATUS_GOOD; + } + + sleep (1); + }; + + DBG (DBG_proc, "teco_wait_scanner: scanner not ready\n"); + return (SANE_STATUS_IO_ERROR); +} + +/* + * Adjust the rasters. This function is used during a color scan, + * because the scanner does not present a format sane can interpret + * directly. + * + * The scanner sends the colors by rasters (B then G then R), whereas + * sane is waiting for a group of 3 bytes per color. To make things + * funnier, the rasters are shifted. As a result, color planes appear to be shifted be a few pixels. + * + * The order of the color is dependant on each scanners. Also the same + * scanner can change the order depending on the resolution. + * + * For instance, the VM6586 at 300dpi has a color shift of 2 lines. The rasters sent are: + * starts with two blue rasters - BB, + * then red in added - BRBR + * then green - BRG ... (most of the picture) + * then blue is removed - RGRG + * and finaly only green stays - GG + * + * Overall there is the same number of RGB rasters. + * The VM3575 is a variant (when factor_x is 0). It does not keep the same order, + * but reverses it, eg: + * BB RBRB GRB... GRGR GG + * (ie it adds the new color in front of the previous one, instead of after). + * + * So this function reorders all that mess. It gets the input from + * dev->buffer and write the output in dev->image. size_in the the + * length of the valid data in dev->buffer. */ + +#define COLOR_0 (color_adjust->z3_color_0) +#define COLOR_1 (color_adjust->z3_color_1) +#define COLOR_2 (color_adjust->z3_color_2) + +static void +teco_adjust_raster (Teco_Scanner * dev, size_t size_in) +{ + int nb_rasters; /* number of rasters in dev->buffer */ + + int raster; /* current raster number in buffer */ + int line; /* line number for that raster */ + int color; /* color for that raster */ + size_t offset; + const struct dpi_color_adjust *color_adjust = dev->color_adjust; + + DBG (DBG_proc, "teco_adjust_raster: enter\n"); + + color = 0; /* keep gcc quiet */ + + assert (dev->scan_mode == TECO_COLOR); + assert ((size_in % dev->params.bytes_per_line) == 0); + + if (size_in == 0) + { + return; + } + + /* + * The color coding is one line for each color (in the RGB order). + * Recombine that stuff to create a RGB value for each pixel. + */ + + nb_rasters = size_in / dev->raster_size; + + for (raster = 0; raster < nb_rasters; raster++) + { + + /* + * Find the color and the line which this raster belongs to. + */ + line = 0; + if (dev->raster_num < color_adjust->color_shift) + { + /* Top of the picture. */ + if (color_adjust->factor_x) + { + color = COLOR_2; + } + else + { + color = COLOR_1; + } + line = dev->raster_num; + } + else if (dev->raster_num < (3 * color_adjust->color_shift)) + { + /* Top of the picture. */ + if ((dev->raster_num - color_adjust->color_shift) % 2) + { + color = COLOR_0; + line = (dev->raster_num - color_adjust->color_shift) / 2; + } + else + { + if (color_adjust->factor_x) + { + color = COLOR_2; + } + else + { + color = COLOR_1; + } + line = (dev->raster_num + color_adjust->color_shift) / 2; + } + } + else if (dev->raster_num >= + dev->raster_real - color_adjust->color_shift) + { + /* Bottom of the picture. */ + if (color_adjust->factor_x) + { + color = COLOR_1; + } + else + { + color = COLOR_2; + } + line = dev->line; + } + else if (dev->raster_num >= + dev->raster_real - 3 * color_adjust->color_shift) + { + /* Bottom of the picture. */ + if ((dev->raster_real - dev->raster_num - + color_adjust->color_shift) % 2) + { + if (color_adjust->factor_x) + { + color = COLOR_1; + line = dev->line; + } + else + { + color = COLOR_0; + line = dev->line + color_adjust->color_shift - 1; + } + } + else + { + if (color_adjust->factor_x) + { + color = COLOR_0; + line = dev->line + color_adjust->color_shift; + } + else + { + color = COLOR_2; + line = dev->line; + } + } + } + else + { + /* Middle of the picture. */ + switch ((dev->raster_num) % 3) + { + case 0: + color = COLOR_2; + if (color_adjust->factor_x) + line = dev->raster_num / 3 + color_adjust->color_shift; + else + line = dev->raster_num / 3 - color_adjust->color_shift; + break; + case 1: + color = COLOR_0; + line = dev->raster_num / 3; + break; + case 2: + color = COLOR_1; + if (color_adjust->factor_x) + line = dev->raster_num / 3 - color_adjust->color_shift; + else + line = dev->raster_num / 3 + color_adjust->color_shift; + break; + } + } + + /* Adjust the line number relative to the image. */ + line -= dev->line; + + offset = dev->image_end + line * dev->params.bytes_per_line; + + assert (offset <= (dev->image_size - dev->params.bytes_per_line)); + + /* Copy the raster to the temporary image. */ + { + int i; + unsigned char *src = dev->buffer + raster * dev->raster_size; + unsigned char *dest = dev->image + offset + color; + + for (i = 0; i < dev->raster_size; i++) + { + *dest = *src; + src++; + dest += 3; + } + } + + DBG (DBG_info, "raster=%d, line=%d, color=%d\n", dev->raster_num, + dev->line + line, color); + + if ((color_adjust->factor_x == 0 && color == COLOR_2) || + (color_adjust->factor_x == 1 && color == COLOR_1)) + { + /* This blue raster completes a new line */ + dev->line++; + dev->image_end += dev->params.bytes_per_line; + } + + dev->raster_num++; + } + + DBG (DBG_proc, "teco_adjust_raster: exit\n"); +} + +/* Read the image from the scanner and fill the temporary buffer with it. */ +static SANE_Status +teco_fill_image (Teco_Scanner * dev) +{ + SANE_Status status; + size_t size; + CDB cdb; + + DBG (DBG_proc, "teco_fill_image: enter\n"); + + assert (dev->image_begin == dev->image_end); + assert (dev->real_bytes_left > 0); + + /* Copy the complete lines, plus the incompletes + * ones. We don't keep the real end of data used + * in image, so we copy the biggest possible. + * + * This is a no-op for non color images. + */ + memmove (dev->image, dev->image + dev->image_begin, dev->raster_ahead); + + dev->image_begin = 0; + dev->image_end = 0; + + while (dev->real_bytes_left) + { + /* + * Try to read the maximum number of bytes. + */ + size = dev->real_bytes_left; + if (size > dev->image_size - dev->raster_ahead - dev->image_end) + { + size = dev->image_size - dev->raster_ahead - dev->image_end; + } + if (size > dev->buffer_size) + { + size = dev->buffer_size; + } + + /* Limit to 0x2000 bytes as does the TWAIN driver. */ + if (size > 0x2000) + size = 0x2000; + + /* Round down to a multiple of line size. */ + size = size - (size % dev->params.bytes_per_line); + + if (size == 0) + { + /* Probably reached the end of the buffer. + * Check, just in case. */ + assert (dev->image_end != 0); + return (SANE_STATUS_GOOD); + } + + DBG (DBG_info, "teco_fill_image: to read = %ld bytes (bpl=%d)\n", + (long) size, dev->params.bytes_per_line); + + MKSCSI_READ_10 (cdb, 0, 0, size); + cdb.data[5] = size / dev->params.bytes_per_line; + + hexdump (DBG_info2, "teco_fill_image: READ_10 CDB", cdb.data, cdb.len); + + hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + +#ifdef sim + memcpy (dev->buffer, image_buf + image_buf_begin, size); + image_buf_begin += size; +#endif + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, "teco_fill_image: cannot read from the scanner\n"); + return status; + } + + DBG (DBG_info, "teco_fill_image: real bytes left = %ld\n", + (long) dev->real_bytes_left); + + if (dev->scan_mode == TECO_COLOR && + dev->def->tecoref != TECO_VM656A && raw_output == 0) + { + teco_adjust_raster (dev, size); + } + else + { + memcpy (dev->image + dev->image_end, dev->buffer, size); + dev->image_end += size; + } + dev->real_bytes_left -= size; + } + + return (SANE_STATUS_GOOD); /* unreachable */ +} + +/* Copy from the raw buffer to the buffer given by the backend. + * + * len in input is the maximum length available in buf, and, in + * output, is the length written into buf. + */ +static void +teco_copy_raw_to_frontend (Teco_Scanner * dev, SANE_Byte * buf, size_t * len) +{ + size_t size; + + size = dev->image_end - dev->image_begin; + if (size > *len) + { + size = *len; + } + *len = size; + + switch (dev->scan_mode) + { + case TECO_BW: + { + /* Invert black and white. */ + unsigned char *src = dev->image + dev->image_begin; + size_t i; + + for (i = 0; i < size; i++) + { + *buf = *src ^ 0xff; + src++; + buf++; + } + } + break; + + case TECO_GRAYSCALE: + case TECO_COLOR: + memcpy (buf, dev->image + dev->image_begin, size); + break; + } + + dev->image_begin += size; +} + +/* Stop a scan. */ +static SANE_Status +do_cancel (Teco_Scanner * dev) +{ + DBG (DBG_sane_proc, "do_cancel enter\n"); + + if (dev->scanning == SANE_TRUE) + { + + /* Reset the scanner */ + teco_reset_window (dev); + teco_close (dev); + } + + dev->scanning = SANE_FALSE; + + DBG (DBG_sane_proc, "do_cancel exit\n"); + + return SANE_STATUS_CANCELLED; +} + +/*--------------------------------------------------------------------------*/ + +/* Sane entry points */ + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) +{ + FILE *fp; + char dev_name[PATH_MAX]; + size_t len; + + DBG_INIT (); + + DBG (DBG_sane_init, "sane_init\n"); + + DBG (DBG_error, "This is sane-teco2 version %d.%d-%d\n", SANE_CURRENT_MAJOR, + V_MINOR, BUILD); + DBG (DBG_error, "(C) 2002 - 2003 by Frank Zago, update 2003 - 2008 by Gerard Klaver\n"); + + if (version_code) + { + *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD); + } + + fp = sanei_config_open (TECO2_CONFIG_FILE); + if (!fp) + { + /* default to /dev/scanner instead of insisting on config file */ + attach_scanner ("/dev/scanner", 0); + return SANE_STATUS_GOOD; + } + + while (sanei_config_read (dev_name, sizeof (dev_name), fp)) + { + if (dev_name[0] == '#') /* ignore line comments */ + continue; + len = strlen (dev_name); + + if (!len) + continue; /* ignore empty lines */ + + sanei_config_attach_matching_devices (dev_name, attach_one); + } + + fclose (fp); + + DBG (DBG_proc, "sane_init: leave\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) +{ + Teco_Scanner *dev; + int i; + + DBG (DBG_proc, "sane_get_devices: enter\n"); + + 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; + + DBG (DBG_proc, "sane_get_devices: exit\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + Teco_Scanner *dev; + SANE_Status status; + int i; + + DBG (DBG_proc, "sane_open: enter\n"); + + /* search for devicename */ + if (devicename[0]) + { + DBG (DBG_info, "sane_open: devicename=%s\n", devicename); + + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devicename) == 0) + { + break; + } + } + + if (!dev) + { + status = attach_scanner (devicename, &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) + { + DBG (DBG_error, "No scanner found\n"); + + return SANE_STATUS_INVAL; + } + + teco_init_options (dev); + + /* Initialize the gamma table. */ + for (i = 0; i < GAMMA_LENGTH; i++) + { + dev->gamma_R[i] = i / 4; + dev->gamma_G[i] = i / 4; + dev->gamma_B[i] = i / 4; + dev->gamma_GRAY[i] = i / 4; + } + + *handle = dev; + + DBG (DBG_proc, "sane_open: exit\n"); + + return SANE_STATUS_GOOD; +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Teco_Scanner *dev = handle; + + DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option); + + if ((unsigned) option >= OPT_NUM_OPTIONS) + { + return NULL; + } + + DBG (DBG_proc, "sane_get_option_descriptor: exit\n"); + + return dev->opt + option; +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) +{ + Teco_Scanner *dev = handle; + SANE_Status status; + SANE_Word cap; + + DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n", + option, action); + + if (info) + { + *info = 0; + } + + if (dev->scanning) + { + return SANE_STATUS_DEVICE_BUSY; + } + + if (option < 0 || option >= OPT_NUM_OPTIONS) + { + return SANE_STATUS_INVAL; + } + + cap = dev->opt[option].cap; + if (!SANE_OPTION_IS_ACTIVE (cap)) + { + return SANE_STATUS_INVAL; + } + + if (action == SANE_ACTION_GET_VALUE) + { + + switch (option) + { + /* word options */ + case OPT_NUM_OPTS: + case OPT_RESOLUTION: + case OPT_TL_Y: + case OPT_BR_Y: + case OPT_TL_X: + case OPT_BR_X: + case OPT_CUSTOM_GAMMA: + case OPT_PREVIEW: + case OPT_THRESHOLD: + case OPT_WHITE_LEVEL_R: + case OPT_WHITE_LEVEL_G: + case OPT_WHITE_LEVEL_B: + *(SANE_Word *) val = dev->val[option].w; + return SANE_STATUS_GOOD; + + /* string options */ + case OPT_MODE: + case OPT_DITHER: + case OPT_FILTER_COLOR: + strcpy (val, dev->val[option].s); + return SANE_STATUS_GOOD; + + /* Gamma */ + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + case OPT_GAMMA_VECTOR_GRAY: + memcpy (val, dev->val[option].wa, dev->opt[option].size); + return SANE_STATUS_GOOD; + + default: + return SANE_STATUS_INVAL; + } + } + else if (action == SANE_ACTION_SET_VALUE) + { + + if (!SANE_OPTION_IS_SETTABLE (cap)) + { + DBG (DBG_error, "could not set option, not settable\n"); + return SANE_STATUS_INVAL; + } + + status = sanei_constrain_value (dev->opt + option, val, info); + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, "could not set option, invalid value\n"); + return status; + } + + switch (option) + { + + /* Numeric side-effect options */ + case OPT_TL_Y: + case OPT_BR_Y: + case OPT_TL_X: + case OPT_BR_X: + case OPT_THRESHOLD: + case OPT_RESOLUTION: + case OPT_WHITE_LEVEL_R: + case OPT_WHITE_LEVEL_G: + case OPT_WHITE_LEVEL_B: + if (info) + { + *info |= SANE_INFO_RELOAD_PARAMS; + } + dev->val[option].w = *(SANE_Word *) val; + return SANE_STATUS_GOOD; + + /* Numeric side-effect free options */ + case OPT_PREVIEW: + dev->val[option].w = *(SANE_Word *) val; + return SANE_STATUS_GOOD; + + /* String side-effect free options */ + case OPT_DITHER: + free (dev->val[option].s); + dev->val[option].s = (SANE_String) strdup (val); + return SANE_STATUS_GOOD; + + case OPT_FILTER_COLOR: + free (dev->val[option].s); + dev->val[option].s = (SANE_String) strdup (val); + return SANE_STATUS_GOOD; + + /* String side-effect options */ + case OPT_MODE: + if (strcmp (dev->val[option].s, val) == 0) + return SANE_STATUS_GOOD; + + free (dev->val[OPT_MODE].s); + dev->val[OPT_MODE].s = (SANE_Char *) strdup (val); + + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_DITHER].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_FILTER_COLOR].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_B].cap |= SANE_CAP_INACTIVE; + + + /* This the default resolution range, except for the + * VM3575, VM3564, VM356A and VM6586 in color mode. */ + dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_RESOLUTION].constraint.range = &dev->def->res_range; + + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) + { + dev->scan_mode = TECO_BW; + dev->depth = 8; + dev->opt[OPT_DITHER].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_FILTER_COLOR].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; + } + else if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) + { + dev->scan_mode = TECO_GRAYSCALE; + dev->depth = 8; + + switch (dev->def->tecoref) + { + case TECO_VM3564: + case TECO_VM356A: + dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE; + break; + case TECO_VM3575: + case TECO_VM6575: + dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= + ~SANE_CAP_INACTIVE; + } + break; + case TECO_VM656A: + case TECO_VM6586: + dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= + ~SANE_CAP_INACTIVE; + } + break; + } + dev->opt[OPT_FILTER_COLOR].cap &= ~SANE_CAP_INACTIVE; + } + else if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dev->scan_mode = TECO_COLOR; + dev->depth = 8; + + switch (dev->def->tecoref) + { + case TECO_VM3564: + case TECO_VM356A: + dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE; + break; + case TECO_VM3575: + case TECO_VM6575: + dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + break; + case TECO_VM656A: + case TECO_VM6586: + dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + break; + } + /* The VM3575, VM3564, VM356A and VM6586 supports only a handful of resolution. Do that here. + * Ugly! */ + if (dev->resolutions_list != NULL) + { + int i; + + dev->opt[OPT_RESOLUTION].constraint_type = + SANE_CONSTRAINT_WORD_LIST; + dev->opt[OPT_RESOLUTION].constraint.word_list = + dev->resolutions_list; + + /* If the resolution isn't in the list, set a default. */ + for (i = 1; i <= dev->resolutions_list[0]; i++) + { + if (dev->resolutions_list[i] >= + dev->val[OPT_RESOLUTION].w) + break; + } + if (i > dev->resolutions_list[0]) + { + /* Too big. Take default. */ + dev->val[OPT_RESOLUTION].w = DEF_RESOLUTION; + } + else + { + /* Take immediate superioir value. */ + dev->val[OPT_RESOLUTION].w = dev->resolutions_list[i]; + } + } + } + + if (info) + { + *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + } + return SANE_STATUS_GOOD; + + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + case OPT_GAMMA_VECTOR_GRAY: + memcpy (dev->val[option].wa, val, dev->opt[option].size); + return SANE_STATUS_GOOD; + + case OPT_CUSTOM_GAMMA: + dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val; + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + /* use custom_gamma_table */ + if (dev->scan_mode == TECO_GRAYSCALE) + { + dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE; + } + else + { + /* color mode */ + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + } + else + { + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + } + if (info) + { + *info |= SANE_INFO_RELOAD_OPTIONS; + } + return SANE_STATUS_GOOD; + + default: + return SANE_STATUS_INVAL; + } + } + + DBG (DBG_proc, "sane_control_option: exit, bad\n"); + + return SANE_STATUS_UNSUPPORTED; +} + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + Teco_Scanner *dev = handle; + + DBG (DBG_proc, "sane_get_parameters: enter\n"); + + if (!(dev->scanning)) + { + + /* Setup the parameters for the scan. These values will be re-used + * in the SET WINDOWS command. */ + if (dev->val[OPT_PREVIEW].w == SANE_TRUE) + { + /* for VM356A, no good 50 dpi scan possible, now leave as */ + + switch (dev->def->tecoref) + { + case TECO_VM356A: + case TECO_VM6575: + dev->x_resolution = 75; + dev->y_resolution = 75; + break; + /* For VM3575, VM656A, VM6586 etc until otherwise default value is used */ + default: + dev->x_resolution = 50; + dev->y_resolution = 50; + break; + } + dev->x_tl = 0; + dev->y_tl = 0; + dev->x_br = mmToIlu (SANE_UNFIX (dev->def->x_range.max)); + dev->y_br = mmToIlu (SANE_UNFIX (dev->def->y_range.max)); + } + else + { + dev->x_resolution = dev->val[OPT_RESOLUTION].w; + dev->y_resolution = dev->val[OPT_RESOLUTION].w; + + dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)); + dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w)); + dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)); + dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w)); + } + + if (dev->x_resolution > dev->def->x_resolution_max) + { + dev->x_resolution = dev->def->x_resolution_max; + } + + /* Check the corners are OK. */ + if (dev->x_tl > dev->x_br) + { + int s; + s = dev->x_tl; + dev->x_tl = dev->x_br; + dev->x_br = s; + } + if (dev->y_tl > dev->y_br) + { + int s; + s = dev->y_tl; + dev->y_tl = dev->y_br; + dev->y_br = s; + } + + dev->width = dev->x_br - dev->x_tl; + dev->length = dev->y_br - dev->y_tl; + + /* Prepare the parameters for the caller. */ + memset (&dev->params, 0, sizeof (SANE_Parameters)); + + dev->params.last_frame = SANE_TRUE; + + switch (dev->scan_mode) + { + case TECO_BW: + dev->params.format = SANE_FRAME_GRAY; + dev->params.pixels_per_line = + ((dev->width * dev->x_resolution) / + dev->def->x_resolution_max) & ~0x7; + dev->params.bytes_per_line = dev->params.pixels_per_line / 8; + dev->params.depth = 1; + dev->color_adjust = NULL; + break; + case TECO_GRAYSCALE: + dev->params.format = SANE_FRAME_GRAY; + dev->params.pixels_per_line = + ((dev->width * dev->x_resolution) / dev->def->x_resolution_max); + if ((dev->def->tecoref == TECO_VM656A + || dev->def->tecoref == TECO_VM6586) + && ((dev->width * dev->x_resolution) % + dev->def->x_resolution_max != 0)) + { + /* Round up */ + dev->params.pixels_per_line += 1; + } + dev->params.bytes_per_line = dev->params.pixels_per_line; + dev->params.depth = 8; + dev->color_adjust = NULL; + break; + case TECO_COLOR: + dev->params.format = SANE_FRAME_RGB; + dev->params.pixels_per_line = + ((dev->width * dev->x_resolution) / dev->def->x_resolution_max); + if ((dev->def->tecoref == TECO_VM656A + || dev->def->tecoref == TECO_VM6586) + && ((dev->width * dev->x_resolution) % + dev->def->x_resolution_max != 0)) + { + /* Round up */ + dev->params.pixels_per_line += 1; + } + dev->params.bytes_per_line = dev->params.pixels_per_line * 3; + dev->params.depth = 8; + + if (dev->resolutions_list != NULL) + { + /* This scanner has a fixed number of supported + * resolutions. Find the color shift for that + * resolution. */ + + int i; + for (i = 0; + dev->def->color_adjust[i].resolution != dev->y_resolution; + i++); + + dev->color_adjust = &dev->def->color_adjust[i]; + } + else + { + dev->color_adjust = &dev->def->color_adjust[0]; + } + break; + } + + dev->params.lines = + (dev->length * dev->y_resolution) / dev->def->x_resolution_max; + } + + /* Return the current values. */ + if (params) + { + *params = (dev->params); + } + + DBG (DBG_proc, "sane_get_parameters: exit\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + Teco_Scanner *dev = handle; + SANE_Status status; + + DBG (DBG_proc, "sane_start: enter\n"); + + if (!(dev->scanning)) + { + + sane_get_parameters (dev, NULL); + + /* Open again the scanner. */ + if (sanei_scsi_open + (dev->devicename, &(dev->sfd), teco_sense_handler, dev) != 0) + { + DBG (DBG_error, "ERROR: sane_start: open failed\n"); + return SANE_STATUS_INVAL; + } + + /* The scanner must be ready. */ + status = teco_wait_scanner (dev); + if (status) + { + teco_close (dev); + return status; + } + + status = teco_set_window (dev); + if (status) + { + teco_close (dev); + return status; + } + + status = teco_get_scan_size (dev); + if (status) + { + teco_close (dev); + return status; + } + + /* Compute the length necessary in image. The first part will store + * the complete lines, and the rest is used to stored ahead + * rasters. + */ + if (dev->color_adjust) + { + dev->raster_ahead = + (2 * dev->color_adjust->color_shift) * dev->params.bytes_per_line; + } + else + { + dev->raster_ahead = 0; + } + dev->image_size = dev->buffer_size + dev->raster_ahead; + dev->image = malloc (dev->image_size); + if (dev->image == NULL) + { + return SANE_STATUS_NO_MEM; + } + + /* Rasters are meaningfull only in color mode. */ + dev->raster_size = dev->params.pixels_per_line; + dev->raster_real = dev->params.lines * 3; + dev->raster_num = 0; + dev->line = 0; + +#if 1 + status = teco_do_calibration (dev); + if (status) + { + teco_close (dev); + return status; + } +#endif + + switch (dev->def->tecoref) + { + /* case TECO_VM3564: not for VM3564 */ + case TECO_VM356A: +/*--------------request sense for first time loop---*/ + status = teco_request_sense (dev); + if (status) + { + teco_close (dev); + return status; + } + break; + default: + break; + } + + status = teco_send_gamma (dev); + if (status) + { + teco_close (dev); + return status; + } + + status = teco_set_window (dev); + if (status) + { + teco_close (dev); + return status; + } + switch (dev->def->tecoref) + { + case TECO_VM3564: + case TECO_VM356A: + break; + default: + status = teco_send_vendor_06 (dev); + if (status) + { + teco_close (dev); + return status; + } + } + status = teco_scan (dev); + if (status) + { + teco_close (dev); + return status; + } + + status = teco_wait_for_data (dev); + if (status) + { + teco_close (dev); + return status; + } + } + + dev->image_end = 0; + dev->image_begin = 0; + + dev->bytes_left = dev->params.bytes_per_line * dev->params.lines; + dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines; + + dev->scanning = SANE_TRUE; + + DBG (DBG_proc, "sane_start: exit\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, + SANE_Int * len) +{ + SANE_Status status; + Teco_Scanner *dev = handle; + size_t size; + int buf_offset; /* offset into buf */ + + DBG (DBG_proc, "sane_read: enter\n"); + + *len = 0; + + if (!(dev->scanning)) + { + /* OOPS, not scanning */ + return do_cancel (dev); + } + + if (dev->bytes_left <= 0) + { + return (SANE_STATUS_EOF); + } + + buf_offset = 0; + + do + { + if (dev->image_begin == dev->image_end) + { + /* Fill image */ + status = teco_fill_image (dev); + if (status != SANE_STATUS_GOOD) + { + return (status); + } + } + + /* Something must have been read */ +#if 1 + assert (dev->image_begin != dev->image_end); +#else + if (dev->image_begin == dev->image_end) + { + DBG (DBG_info, "sane_read: nothing read\n"); + return SANE_STATUS_IO_ERROR; + } +#endif + + /* Copy the data to the frontend buffer. */ + size = max_len - buf_offset; + if (size > dev->bytes_left) + { + size = dev->bytes_left; + } + teco_copy_raw_to_frontend (dev, buf + buf_offset, &size); + + buf_offset += size; + + dev->bytes_left -= size; + *len += size; + + } + while ((buf_offset != max_len) && dev->bytes_left); + + DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n", (long) dev->bytes_left); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) +{ + SANE_Status status; + Teco_Scanner *dev = handle; + + DBG (DBG_proc, "sane_set_io_mode: enter\n"); + + if (dev->scanning == SANE_FALSE) + { + return SANE_STATUS_INVAL; + } + + if (non_blocking == SANE_FALSE) + { + status = SANE_STATUS_GOOD; + } + else + { + status = SANE_STATUS_UNSUPPORTED; + } + + DBG (DBG_proc, "sane_set_io_mode: exit\n"); + + return status; +} + +SANE_Status +sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) +{ + DBG (DBG_proc, "sane_get_select_fd: enter\n"); + + DBG (DBG_proc, "sane_get_select_fd: exit\n"); + + return SANE_STATUS_UNSUPPORTED; +} + +void +sane_cancel (SANE_Handle handle) +{ + Teco_Scanner *dev = handle; + + DBG (DBG_proc, "sane_cancel: enter\n"); + + do_cancel (dev); + + DBG (DBG_proc, "sane_cancel: exit\n"); +} + +void +sane_close (SANE_Handle handle) +{ + Teco_Scanner *dev = handle; + Teco_Scanner *dev_tmp; + + DBG (DBG_proc, "sane_close: enter\n"); + + do_cancel (dev); + teco_close (dev); + + /* Unlink dev. */ + if (first_dev == dev) + { + first_dev = dev->next; + } + else + { + dev_tmp = first_dev; + while (dev_tmp->next && dev_tmp->next != dev) + { + dev_tmp = dev_tmp->next; + } + if (dev_tmp->next != NULL) + { + dev_tmp->next = dev_tmp->next->next; + } + } + + teco_free (dev); + num_devices--; + + DBG (DBG_proc, "sane_close: exit\n"); +} + +void +sane_exit (void) +{ + DBG (DBG_proc, "sane_exit: enter\n"); + + while (first_dev) + { + sane_close (first_dev); + } + + if (devlist) + { + free (devlist); + devlist = NULL; + } + + DBG (DBG_proc, "sane_exit: exit\n"); +} |