summaryrefslogtreecommitdiff
path: root/backend/snapscan-scsi.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/snapscan-scsi.c
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/snapscan-scsi.c')
-rw-r--r--backend/snapscan-scsi.c1948
1 files changed, 1948 insertions, 0 deletions
diff --git a/backend/snapscan-scsi.c b/backend/snapscan-scsi.c
new file mode 100644
index 0000000..ac0efea
--- /dev/null
+++ b/backend/snapscan-scsi.c
@@ -0,0 +1,1948 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997, 1998, 2001, 2002, 2013 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Mikael Magnusson,
+ Max Ushakov, Andrew Goodbody, Oliver Schwartz and Kevin Charter
+
+ 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 is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+
+/* $Id$
+ SnapScan backend scsi command functions */
+
+/* scanner scsi commands */
+
+static SANE_Status download_firmware(SnapScan_Scanner * pss);
+static SANE_Status wait_scanner_ready (SnapScan_Scanner * pss);
+
+#include "snapscan-usb.h"
+
+/* find model by SCSI ID string or USB vendor/product ID */
+static SnapScan_Model snapscani_get_model_id(char* model_str, int fd, SnapScan_Bus bus_type)
+{
+ static char me[] = "snapscani_get_model_id";
+ SnapScan_Model model_id = UNKNOWN;
+ SANE_Word vendor_id, product_id;
+ int i;
+
+ DBG(DL_CALL_TRACE, "%s(%s, %d, %d)\n",me, model_str, fd, bus_type);
+ for (i = 0; i < known_scanners; i++)
+ {
+ if (0 == strcasecmp (model_str, scanners[i].scsi_name))
+ {
+ model_id = scanners[i].id;
+ break;
+ }
+ }
+ /* Also test USB vendor and product ID numbers, since some USB models use
+ identical model names.
+ */
+ if ((bus_type == USB) &&
+ (sanei_usb_get_vendor_product(fd, &vendor_id, &product_id) == SANE_STATUS_GOOD))
+ {
+ DBG(DL_MINOR_INFO,
+ "%s: looking up scanner for ID 0x%04x,0x%04x.\n",
+ me, vendor_id, product_id);
+ for (i = 0; i < known_usb_scanners; i++)
+ {
+ if ((usb_scanners[i].vendor_id == vendor_id) &&
+ (usb_scanners[i].product_id == product_id))
+ {
+ model_id = usb_scanners[i].id;
+ DBG(DL_MINOR_INFO, "%s: scanner identified\n", me);
+ break;
+ }
+ }
+ }
+ return model_id;
+}
+
+/* a sensible sense handler, courtesy of Franck;
+ the last argument is expected to be a pointer to the associated
+ SnapScan_Scanner structure */
+static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg)
+{
+ static char me[] = "sense_handler";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) arg;
+ u_char sense, asc, ascq;
+ char *sense_str = NULL, *as_str = NULL;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DL_CALL_TRACE, "%s(%ld, %p, %p)\n", me, (long) scsi_fd,
+ (void *) result, (void *) arg);
+
+ sense = result[2] & 0x0f;
+ asc = result[12];
+ ascq = result[13];
+ if (pss)
+ {
+ pss->asi1 = result[18];
+ pss->asi2 = result[19];
+ }
+ if ((result[0] & 0x80) == 0)
+ {
+ DBG (DL_DATA_TRACE, "%s: sense key is invalid.\n", me);
+ return SANE_STATUS_GOOD; /* sense key invalid */
+ } else {
+ DBG (DL_DATA_TRACE, "%s: sense key: 0x%02x, asc: 0x%02x, ascq: 0x%02x, i1: 0x%02x, i2: 0x%02x\n",
+ me, sense, asc, ascq, result[18], result[19]);
+ }
+
+ switch (sense)
+ {
+ case 0x00:
+ /* no sense */
+ sense_str = "No sense.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ break;
+ case 0x02:
+ /* not ready */
+ sense_str = "Not ready.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ if (asc == 0x04 && ascq == 0x01)
+ {
+ /* warming up; byte 18 contains remaining seconds */
+ as_str = "Logical unit is in process of becoming ready.";
+ DBG (DL_MINOR_INFO, "%s: %s (%d seconds)\n", me, as_str, result[18]);
+ status = SANE_STATUS_DEVICE_BUSY;
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ }
+ break;
+ case 0x04:
+ /* hardware error */
+ sense_str = "Hardware error.";
+ /* byte 18 and 19 detail the hardware problems */
+ DBG (DL_MINOR_INFO, "%s: %s (0x%02x, 0x%02x)\n", me, sense_str, result[18],
+ result[19]);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x05:
+ /* illegal request */
+ sense_str = "Illegal request.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ if (asc == 0x25 && ascq == 0x00)
+ as_str = "Logical unit not supported.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x09:
+ /* process error */
+ sense_str = "Process error.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ if (asc == 0x00 && ascq == 0x05)
+ {
+ /* no documents in ADF */
+ as_str = "End of data detected.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_NO_DOCS;
+ }
+ else if (asc == 0x3b && ascq == 0x05)
+ {
+ /* paper jam in ADF */
+ as_str = "Paper jam.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_JAMMED;
+ }
+ else if (asc == 0x3b && ascq == 0x09)
+ {
+ /* scanning area exceeds end of paper in ADF */
+ as_str = "Read past end of medium.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
+ status = SANE_STATUS_EOF;
+ }
+ break;
+ case 0x0b:
+ /* Aborted command */
+ sense_str = "Aborted Command.";
+ DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ default:
+ DBG (DL_MINOR_ERROR, "%s: no handling for sense %x.\n", me, sense);
+ break;
+ }
+
+ if (pss)
+ {
+ pss->sense_str = sense_str;
+ pss->as_str = as_str;
+ }
+ return status;
+}
+
+
+static SANE_Status open_scanner (SnapScan_Scanner *pss)
+{
+ SANE_Status status;
+ DBG (DL_CALL_TRACE, "open_scanner\n");
+ if (!pss->opens)
+ {
+ if(pss->pdev->bus == SCSI)
+ {
+ status = sanei_scsi_open (pss->devname, &(pss->fd),
+ sense_handler, (void *) pss);
+ }
+ else
+ {
+ status = snapscani_usb_open (pss->devname, &(pss->fd),
+ sense_handler, (void *) pss);
+ }
+ }
+ else
+ {
+ /* already open */
+ status = SANE_STATUS_GOOD;
+ }
+ if (status == SANE_STATUS_GOOD)
+ pss->opens++;
+ return status;
+}
+
+static void close_scanner (SnapScan_Scanner *pss)
+{
+ static char me[] = "close_scanner";
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ if (pss->opens)
+ {
+ pss->opens--;
+ if (!pss->opens)
+ {
+ if(pss->pdev->bus == SCSI)
+ {
+ sanei_scsi_close (pss->fd);
+ }
+ else if(pss->pdev->bus == USB)
+ {
+ snapscani_usb_close (pss->fd);
+ }
+ } else {
+ DBG(DL_INFO, "%s: handles left: %d\n,",me, pss->opens);
+ }
+ }
+}
+
+static SANE_Status snapscan_cmd(SnapScan_Bus bus, int fd, const void *src,
+ size_t src_size, void *dst, size_t * dst_size)
+{
+ SANE_Status status;
+ DBG (DL_CALL_TRACE, "snapscan_cmd\n");
+ if(bus == USB)
+ {
+ status = snapscani_usb_cmd(fd, src, src_size, dst, dst_size);
+ }
+ else
+ {
+ status = sanei_scsi_cmd(fd, src, src_size, dst, dst_size);
+ }
+ return status;
+}
+
+/* SCSI commands */
+#define TEST_UNIT_READY 0x00
+#define INQUIRY 0x12
+#define SEND 0x2A
+#define SET_WINDOW 0x24
+#define SCAN 0x1B
+#define READ 0x28
+#define REQUEST_SENSE 0x03
+#define RESERVE_UNIT 0x16
+#define RELEASE_UNIT 0x17
+#define SEND_DIAGNOSTIC 0x1D
+#define OBJECT_POSITION 0x31
+#define GET_DATA_BUFFER_STATUS 0x34
+
+#define SCAN_LEN 6
+#define READ_LEN 10
+
+/* buffer tools */
+
+static void zero_buf (u_char * buf, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = 0x00;
+}
+
+
+#define BYTE_SIZE 8
+
+static u_short u_char_to_u_short (u_char * pc)
+{
+ u_short r = 0;
+ r |= pc[0];
+ r = r << BYTE_SIZE;
+ r |= pc[1];
+ return r;
+}
+
+static void u_short_to_u_charp (u_short x, u_char * pc)
+{
+ pc[0] = (0xFF00 & x) >> BYTE_SIZE;
+ pc[1] = (0x00FF & x);
+}
+
+static void u_int_to_u_char3p (u_int x, u_char * pc)
+{
+ pc[0] = (0xFF0000 & x) >> 2 * BYTE_SIZE;
+ pc[1] = (0x00FF00 & x) >> BYTE_SIZE;
+ pc[2] = (0x0000FF & x);
+}
+
+static void u_int_to_u_char4p (u_int x, u_char * pc)
+{
+ pc[0] = (0xFF000000 & x) >> 3 * BYTE_SIZE;
+ pc[1] = (0x00FF0000 & x) >> 2 * BYTE_SIZE;
+ pc[2] = (0x0000FF00 & x) >> BYTE_SIZE;
+ pc[3] = (0x000000FF & x);
+}
+
+/* Convert 'STRING ' to 'STRING' by adding 0 after last non-space */
+static void remove_trailing_space (char *s)
+{
+ int position;
+
+ if (NULL == s)
+ return;
+
+ for (position = strlen (s);
+ position > 0 && ' ' == s[position - 1];
+ position--)
+ {
+ /* dummy */;
+ }
+ s[position] = 0;
+}
+
+static void check_range (int *v, SANE_Range r)
+{
+ if (*v < r.min)
+ *v = r.min;
+ if (*v > r.max)
+ *v = r.max;
+}
+
+#define INQUIRY_LEN 6
+#define INQUIRY_RET_LEN 120
+#define INQUIRY_RET_LEN_EPSON 139
+#define INQUIRY_RET_LEN_5000 138
+
+#define INQUIRY_VENDOR 8 /* Offset in reply data to vendor name */
+#define INQUIRY_PRODUCT 16 /* Offset in reply data to product id */
+#define INQUIRY_REV 32 /* Offset in reply data to revision level */
+#define INQUIRY_PRL2 36 /* Product Revision Level 2 (AGFA) */
+#define INQUIRY_HCFG 37 /* Hardware Configuration (AGFA) */
+#define INQUIRY_HWST 40 /* Hardware status */
+#define INQUIRY_HWMI 41 /* HARDWARE Model ID */
+#define INQUIRY_PIX_PER_LINE 42 /* Pixels per scan line (AGFA) */
+#define INQUIRY_BYTE_PER_LINE 44 /* Bytes per scan line (AGFA) */
+#define INQUIRY_NUM_LINES 46 /* number of scan lines (AGFA) */
+#define INQUIRY_OPT_RES 48 /* optical resolution (AGFA) */
+#define INQUIRY_SCAN_SPEED 51 /* scan speed (AGFA) */
+#define INQUIRY_EXPTIME1 52 /* exposure time, first digit (AGFA) */
+#define INQUIRY_EXPTIME2 53 /* exposure time, second digit (AGFA) */
+#define INQUIRY_G2R_DIFF 54 /* green to red difference (AGFA) */
+#define INQUIRY_B2R_DIFF 55 /* green to red difference (AGFA) */
+#define INQUIRY_FIRMWARE 96 /* firmware date and time (AGFA) */
+#define INQUIRY_BYTE_PER_LINE_MSB 132 /* ?? top byte of bytes per scan line - epson 2480 */
+#define INQUIRY_EPSON_HCFG 138 /* ?? Hardware configuration (Epson) */
+
+
+/* a mini-inquiry reads only the first 36 bytes of inquiry data, and
+ returns the vendor(7 chars) and model(16 chars); vendor and model
+ must point to character buffers of size at least 8 and 17
+ respectively */
+
+static SANE_Status mini_inquiry (SnapScan_Bus bus, int fd, char *vendor, char *model)
+{
+ static const char *me = "mini_inquiry";
+ size_t read_bytes;
+ char cmd[] = {INQUIRY, 0, 0, 0, 36, 0};
+ char data[36];
+ SANE_Status status;
+
+ read_bytes = 36;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (bus, fd, cmd, sizeof (cmd), data, &read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ memcpy (vendor, data + 8, 7);
+ vendor[7] = 0;
+ memcpy (model, data + 16, 16);
+ model[16] = 0;
+
+ remove_trailing_space (vendor);
+ remove_trailing_space (model);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* TODO: Remove */
+static char *snapscani_debug_data(char *str,const SANE_Byte *data, int len) {
+ char tmpstr[10];
+ int i;
+
+ str[0]=0;
+ for(i=0; i < (len < 20 ? len : 20); i++) {
+ sprintf(tmpstr," 0x%02x",((int)data[i]) & 0xff);
+ if(i%16 == 0 && i != 0)
+ strcat(str,"\n");
+ strcat(str,tmpstr);
+ }
+ if(i < len)
+ strcat(str," ...");
+ return str;
+}
+
+static SANE_Status inquiry (SnapScan_Scanner *pss)
+{
+ static const char *me = "inquiry";
+ char tmpstr[150]; /* TODO: Remove */
+ SANE_Status status;
+ switch (pss->pdev->model)
+ {
+ case PERFECTION2480:
+ case PERFECTION3490:
+ if (pss->firmware_loaded)
+ pss->read_bytes = INQUIRY_RET_LEN_EPSON;
+ else
+ pss->read_bytes = INQUIRY_RET_LEN;
+ break;
+ case PRISA5000:
+ case PRISA5150:
+ pss->read_bytes = INQUIRY_RET_LEN_5000;
+ break;
+ default:
+ pss->read_bytes = INQUIRY_RET_LEN;
+ break;
+ }
+
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = INQUIRY;
+ pss->cmd[4] = pss->read_bytes;
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus,
+ pss->fd,
+ pss->cmd,
+ INQUIRY_LEN,
+ pss->buf,
+ &pss->read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ /* record current parameters */
+ {
+ char exptime[4] = {' ', '.', ' ', 0};
+ exptime[0] = (char) (pss->buf[INQUIRY_EXPTIME1] + '0');
+ exptime[2] = (char) (pss->buf[INQUIRY_EXPTIME2] + '0');
+ pss->ms_per_line = atof (exptime)*(float) pss->buf[INQUIRY_SCAN_SPEED];
+ DBG (DL_DATA_TRACE, "%s: exposure time: %s ms\n", me, exptime);
+ DBG (DL_DATA_TRACE, "%s: ms per line: %f\n", me, pss->ms_per_line);
+ }
+
+ switch (pss->pdev->model)
+ {
+ case SNAPSCAN:
+ case ACER300F:
+ pss->chroma_offset[R_CHAN] =
+ pss->chroma_offset[G_CHAN] =
+ pss->chroma_offset[B_CHAN] = 0;
+ pss->chroma = 0;
+ break;
+ case PERFECTION2480:
+ case PERFECTION3490:
+ if (pss->firmware_loaded)
+ {
+ snapscani_debug_data(tmpstr, pss->buf+120, 19);
+ DBG (DL_DATA_TRACE, "%s: Epson additional inquiry data:\n%s\n", me, tmpstr);
+ pss->hconfig_epson = pss->buf[INQUIRY_EPSON_HCFG];
+ }
+ /* fall through */
+ default:
+ {
+ signed char min_diff;
+ u_char r_off, g_off, b_off;
+ signed char g = (pss->buf[INQUIRY_G2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_G2R_DIFF] & 0x7F) : pss->buf[INQUIRY_G2R_DIFF];
+ signed char b = (pss->buf[INQUIRY_B2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_B2R_DIFF] & 0x7F) : pss->buf[INQUIRY_B2R_DIFF];
+ DBG (DL_DATA_TRACE, "%s: G2R_DIFF: %d\n", me, pss->buf[INQUIRY_G2R_DIFF]);
+ DBG (DL_DATA_TRACE, "%s: B2R_DIFF: %d\n", me, pss->buf[INQUIRY_B2R_DIFF]);
+
+ min_diff = MIN (MIN (b, g), 0);
+ r_off = (u_char) (0 - min_diff);
+ g_off = (u_char) (g - min_diff);
+ b_off = (u_char) (b - min_diff);
+ pss->chroma_offset[R_CHAN] = r_off;
+ pss->chroma_offset[G_CHAN] = g_off;
+ pss->chroma_offset[B_CHAN] = b_off;
+ pss->chroma = MAX(MAX(r_off, g_off), b_off);
+ DBG (DL_DATA_TRACE,
+ "%s: Chroma offsets=%d; Red=%u, Green:=%u, Blue=%u\n",
+ me, pss->chroma,
+ pss->chroma_offset[R_CHAN],
+ pss->chroma_offset[G_CHAN],
+ pss->chroma_offset[B_CHAN]);
+ }
+ break;
+ }
+
+ pss->actual_res =
+ u_char_to_u_short (pss->buf + INQUIRY_OPT_RES);
+
+ pss->pixels_per_line =
+ u_char_to_u_short (pss->buf + INQUIRY_PIX_PER_LINE);
+ pss->bytes_per_line =
+ u_char_to_u_short (pss->buf + INQUIRY_BYTE_PER_LINE);
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
+ pss->bytes_per_line += pss->buf[INQUIRY_BYTE_PER_LINE_MSB] << 16;
+ pss->lines =
+ u_char_to_u_short (pss->buf + INQUIRY_NUM_LINES) - pss->chroma;
+ /* effective buffer size must be a whole number of scan lines */
+ if (pss->lines)
+ pss->buf_sz = (pss->phys_buf_sz/pss->bytes_per_line)*pss->bytes_per_line;
+ else
+ pss->buf_sz = 0;
+ pss->bytes_remaining = pss->bytes_per_line * (pss->lines + pss->chroma);
+ pss->expected_read_bytes = 0;
+ pss->read_bytes = 0;
+ pss->hwst = pss->buf[INQUIRY_HWST];
+ if ((pss->pdev->bus == USB) && !(pss->hwst & 0x02))
+ {
+ pss->firmware_loaded = SANE_TRUE;
+ }
+ pss->hconfig = pss->buf[INQUIRY_HCFG];
+ switch (pss->pdev->model)
+ {
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ case PRISA5150:
+ case PRISA5000:
+ pss->bpp = 14;
+ break;
+ case STYLUS_CX1500:
+ case SCANWIT2720S:
+ pss->bpp = 12;
+ break;
+ default:
+ pss->bpp = 8;
+ if (pss->hconfig & HCFG_ADC)
+ pss->bpp = 10;
+ break;
+ }
+ DBG (DL_DATA_TRACE,
+ "%s: hardware config = 0x%02x\n",
+ me,
+ pss->hconfig);
+ DBG (DL_DATA_TRACE,
+ "%s: bits per pixel = %lu\n",
+ me,
+ (u_long) pss->bpp);
+ DBG (DL_DATA_TRACE,
+ "%s: pixels per scan line = %lu\n",
+ me,
+ (u_long) pss->pixels_per_line);
+ DBG (DL_DATA_TRACE,
+ "%s: bytes per scan line = %lu\n",
+ me,
+ (u_long) pss->bytes_per_line);
+ DBG (DL_DATA_TRACE,
+ "%s: number of scan lines = %lu\n",
+ me,
+ (u_long) pss->lines);
+ DBG (DL_DATA_TRACE,
+ "%s: effective buffer size = %lu bytes, %lu lines\n",
+ me,
+ (u_long) pss->buf_sz,
+ (u_long) (pss->lines ? pss->buf_sz / pss->lines : 0));
+ DBG (DL_DATA_TRACE,
+ "%s: expected total scan data: %lu bytes\n",
+ me,
+ (u_long) pss->bytes_remaining);
+
+ return status;
+}
+
+static SANE_Status test_unit_ready (SnapScan_Scanner *pss)
+{
+ static const char *me = "test_unit_ready";
+ char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static void reserve_unit (SnapScan_Scanner *pss)
+{
+ static const char *me = "reserve_unit";
+ char cmd[] = {RESERVE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: scsi command error: %s\n",
+ me,
+ sane_strstatus (status));
+ }
+}
+
+static void release_unit (SnapScan_Scanner *pss)
+{
+ static const char *me = "release_unit";
+ char cmd[] = {RELEASE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: scsi command error: %s\n",
+ me, sane_strstatus (status));
+ }
+}
+
+#define SEND_LENGTH 10
+#define DTC_HALFTONE 0x02
+#define DTC_GAMMA 0x03
+#define DTC_GAMMA2 0x04
+#define DTC_SPEED 0x81
+#define DTC_CALIBRATION 0x82
+#define DTC_CALIBRATION_BLACK 0x89
+#define DTCQ_HALFTONE_BW8 0x00
+#define DTCQ_HALFTONE_COLOR8 0x01
+#define DTCQ_HALFTONE_BW16 0x80
+#define DTCQ_HALFTONE_COLOR16 0x81
+#define DTCQ_GAMMA_GRAY8 0x00
+#define DTCQ_GAMMA_RED8 0x01
+#define DTCQ_GAMMA_GREEN8 0x02
+#define DTCQ_GAMMA_BLUE8 0x03
+#define DTCQ_GAMMA_GRAY10 0x80
+#define DTCQ_GAMMA_RED10 0x81
+#define DTCQ_GAMMA_GREEN10 0x82
+#define DTCQ_GAMMA_BLUE10 0x83
+#define DTCQ_GAMMA_GRAY12 0x90
+#define DTCQ_GAMMA_RED12 0x91
+#define DTCQ_GAMMA_GREEN12 0x92
+#define DTCQ_GAMMA_BLUE12 0x93
+#define DTCQ_GAMMA_GRAY14 0x95 /* ? */
+#define DTCQ_GAMMA_RED14 0x96
+#define DTCQ_GAMMA_GREEN14 0x97
+#define DTCQ_GAMMA_BLUE14 0x98
+#define DTCQ_GAMMA_GRAY12_16BIT 0xa0
+#define DTCQ_GAMMA_RED12_16BIT 0xa1
+#define DTCQ_GAMMA_GREEN12_16BIT 0xa2
+#define DTCQ_GAMMA_BLUE12_16BIT 0xa3
+#define DTCQ_GAMMA_GRAY14_16BIT 0xa5 /* ? */
+#define DTCQ_GAMMA_RED14_16BIT 0xa6
+#define DTCQ_GAMMA_GREEN14_16BIT 0xa7
+#define DTCQ_GAMMA_BLUE14_16BIT 0xa8
+
+static SANE_Status send (SnapScan_Scanner *pss, u_char dtc, u_char dtcq)
+{
+ static char me[] = "send";
+ SANE_Status status;
+ u_short tl; /* transfer length */
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ zero_buf (pss->buf, SEND_LENGTH);
+
+ switch (dtc)
+ {
+ case DTC_HALFTONE: /* halftone mask */
+ switch (dtcq)
+ {
+ case DTCQ_HALFTONE_BW8:
+ tl = 64; /* bw 8x8 table */
+ break;
+ case DTCQ_HALFTONE_COLOR8:
+ tl = 3 * 64; /* rgb 8x8 tables */
+ break;
+ case DTCQ_HALFTONE_BW16:
+ tl = 256; /* bw 16x16 table */
+ break;
+ case DTCQ_HALFTONE_COLOR16:
+ tl = 3 * 256; /* rgb 16x16 tables */
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: bad halftone data type qualifier 0x%x\n",
+ me, dtcq);
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case DTC_GAMMA: /* gamma function */
+ case DTC_GAMMA2:
+ switch (dtcq)
+ {
+ case DTCQ_GAMMA_GRAY8: /* 8-bit tables */
+ case DTCQ_GAMMA_RED8:
+ case DTCQ_GAMMA_GREEN8:
+ case DTCQ_GAMMA_BLUE8:
+ tl = 256;
+ break;
+ case DTCQ_GAMMA_GRAY10: /* 10-bit tables */
+ case DTCQ_GAMMA_RED10:
+ case DTCQ_GAMMA_GREEN10:
+ case DTCQ_GAMMA_BLUE10:
+ tl = 1024;
+ break;
+ case DTCQ_GAMMA_GRAY12: /* 12-bit tables */
+ case DTCQ_GAMMA_RED12:
+ case DTCQ_GAMMA_GREEN12:
+ case DTCQ_GAMMA_BLUE12:
+ tl = 4096;
+ break;
+ case DTCQ_GAMMA_GRAY12_16BIT: /* 12-bit tables with 16 bit data */
+ case DTCQ_GAMMA_RED12_16BIT:
+ case DTCQ_GAMMA_GREEN12_16BIT:
+ case DTCQ_GAMMA_BLUE12_16BIT:
+ tl = 8192;
+ break;
+ case DTCQ_GAMMA_GRAY14: /* 14-bit tables */
+ case DTCQ_GAMMA_RED14:
+ case DTCQ_GAMMA_GREEN14:
+ case DTCQ_GAMMA_BLUE14:
+ tl = 16384;
+ break;
+ case DTCQ_GAMMA_GRAY14_16BIT: /* 14-bit tables with 16 bit data */
+ case DTCQ_GAMMA_RED14_16BIT:
+ case DTCQ_GAMMA_GREEN14_16BIT:
+ case DTCQ_GAMMA_BLUE14_16BIT:
+ tl = 32768;
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: bad gamma data type qualifier 0x%x\n",
+ me, dtcq);
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ case DTC_SPEED: /* static transfer speed */
+ tl = 2;
+ break;
+ case DTC_CALIBRATION:
+ tl = calibration_line_length(pss);
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: unsupported data type code 0x%x\n",
+ me, (unsigned) dtc);
+ return SANE_STATUS_INVAL;
+ }
+
+ pss->buf[0] = SEND;
+ pss->buf[2] = dtc;
+ pss->buf[5] = dtcq;
+ pss->buf[7] = (tl >> 8) & 0xff;
+ pss->buf[8] = tl & 0xff;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
+ NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+#include "snapscan-data.c"
+static SANE_Status send_calibration_5150(SnapScan_Scanner *pss)
+{
+ static const int length = sizeof(calibration_data_5150);
+ SANE_Byte* buf = malloc (length + SEND_LENGTH);
+ SANE_Status status;
+ zero_buf (buf, SEND_LENGTH);
+ *buf = SEND;
+ *(buf + 2) = DTC_CALIBRATION;
+ *(buf + 6) = (length >> 16) & 0xff;
+ *(buf + 7) = (length >> 8) & 0xff;
+ *(buf + 8) = length & 0xff;
+ memcpy(buf + SEND_LENGTH, calibration_data_5150, length);
+ status = snapscan_cmd (
+ pss->pdev->bus, pss->fd, buf, SEND_LENGTH + length, NULL, NULL);
+ free (buf);
+ return status;
+}
+
+#define SET_WINDOW_LEN 10
+#define SET_WINDOW_HEADER 10 /* header starts */
+#define SET_WINDOW_HEADER_LEN 8
+#define SET_WINDOW_DESC 18 /* window descriptor starts */
+#define SET_WINDOW_DESC_LEN 48
+#define SET_WINDOW_TRANSFER_LEN 56
+#define SET_WINDOW_TOTAL_LEN 66
+#define SET_WINDOW_RET_LEN 0 /* no returned data */
+
+#define SET_WINDOW_P_TRANSFER_LEN 6
+#define SET_WINDOW_P_DESC_LEN 6
+
+#define SET_WINDOW_P_WIN_ID 0
+#define SET_WINDOW_P_XRES 2
+#define SET_WINDOW_P_YRES 4
+#define SET_WINDOW_P_TLX 6
+#define SET_WINDOW_P_TLY 10
+#define SET_WINDOW_P_WIDTH 14
+#define SET_WINDOW_P_LENGTH 18
+#define SET_WINDOW_P_BRIGHTNESS 22
+#define SET_WINDOW_P_THRESHOLD 23
+#define SET_WINDOW_P_CONTRAST 24
+#define SET_WINDOW_P_COMPOSITION 25
+#define SET_WINDOW_P_BITS_PER_PIX 26
+#define SET_WINDOW_P_HALFTONE_PATTERN 27
+#define SET_WINDOW_P_PADDING_TYPE 29
+#define SET_WINDOW_P_BIT_ORDERING 30
+#define SET_WINDOW_P_COMPRESSION_TYPE 32
+#define SET_WINDOW_P_COMPRESSION_ARG 33
+#define SET_WINDOW_P_HALFTONE_FLAG 35
+#define SET_WINDOW_P_DEBUG_MODE 40
+#define SET_WINDOW_P_GAMMA_NO 41
+#define SET_WINDOW_P_OPERATION_MODE 42
+#define SET_WINDOW_P_RED_UNDER_COLOR 43
+#define SET_WINDOW_P_BLUE_UNDER_COLOR 45
+#define SET_WINDOW_P_GREEN_UNDER_COLOR 44
+
+static SANE_Status prepare_set_window (SnapScan_Scanner *pss)
+{
+ static const char *me = "prepare_set_window";
+ u_char *pc;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+
+ /* basic command */
+ pc = pss->cmd;
+ pc[0] = SET_WINDOW;
+ u_int_to_u_char3p ((u_int) SET_WINDOW_TRANSFER_LEN,
+ pc + SET_WINDOW_P_TRANSFER_LEN);
+
+ /* header; we support only one window */
+ pc += SET_WINDOW_LEN;
+ u_short_to_u_charp (SET_WINDOW_DESC_LEN, pc + SET_WINDOW_P_DESC_LEN);
+
+ /* the sole window descriptor */
+ pc += SET_WINDOW_HEADER_LEN;
+ pc[SET_WINDOW_P_WIN_ID] = 0;
+ u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_XRES);
+ u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_YRES);
+ DBG (DL_CALL_TRACE, "%s Resolution: %d\n", me, pss->res);
+
+ pc[SET_WINDOW_P_BRIGHTNESS] = 128;
+ pc[SET_WINDOW_P_THRESHOLD] =
+ (u_char) (255.0*(pss->threshold / 100.0));
+ pc[SET_WINDOW_P_CONTRAST] = 128;
+
+ {
+ SnapScan_Mode mode = pss->mode;
+ pss->bpp_scan = pss->val[OPT_BIT_DEPTH].w;
+
+ if (pss->preview)
+ {
+ mode = pss->preview_mode;
+ if (pss->pdev->model != SCANWIT2720S)
+ pss->bpp_scan = 8;
+ }
+
+ DBG (DL_MINOR_INFO, "%s Mode: %d\n", me, mode);
+ switch (mode)
+ {
+ case MD_COLOUR:
+ pc[SET_WINDOW_P_COMPOSITION] = 0x05; /* multi-level RGB */
+ break;
+ case MD_BILEVELCOLOUR:
+ if (pss->halftone)
+ pc[SET_WINDOW_P_COMPOSITION] = 0x04; /* halftone RGB */
+ else
+ pc[SET_WINDOW_P_COMPOSITION] = 0x03; /* bi-level RGB */
+ pss->bpp_scan = 1;
+ break;
+ case MD_GREYSCALE:
+ pc[SET_WINDOW_P_COMPOSITION] = 0x02; /* grayscale */
+ break;
+ case MD_LINEART:
+ if (pss->halftone)
+ pc[SET_WINDOW_P_COMPOSITION] = 0x01; /* b&w halftone */
+ else
+ pc[SET_WINDOW_P_COMPOSITION] = 0x00; /* b&w (lineart) */
+ pss->bpp_scan = 1;
+ break;
+ default:
+ break;
+ }
+
+ pc[SET_WINDOW_P_BITS_PER_PIX] = pss->bpp_scan;
+ DBG (DL_INFO, "%s: bits-per-pixel set to %d\n", me, (int) pss->bpp_scan);
+ }
+ /* the RIF bit is the high bit of the padding type */
+ pc[SET_WINDOW_P_PADDING_TYPE] = 0x00 /*| (pss->negative ? 0x00 : 0x80) */ ;
+ pc[SET_WINDOW_P_HALFTONE_PATTERN] = 0;
+ pc[SET_WINDOW_P_HALFTONE_FLAG] = 0x80; /* always set; image
+ composition
+ determines whether
+ halftone is
+ actually used */
+
+ u_short_to_u_charp (0x0000, pc + SET_WINDOW_P_BIT_ORDERING); /* used? */
+ pc[SET_WINDOW_P_COMPRESSION_TYPE] = 0; /* none */
+ pc[SET_WINDOW_P_COMPRESSION_ARG] = 0; /* none applicable */
+ if(pss->pdev->model != ACER300F
+ &&
+ pss->pdev->model != SNAPSCAN310
+ &&
+ pss->pdev->model != PRISA310
+ &&
+ pss->pdev->model != PRISA610
+ ) {
+ pc[SET_WINDOW_P_DEBUG_MODE] = 2; /* use full 128k buffer */
+ if (pss->mode != MD_LINEART)
+ {
+ pc[SET_WINDOW_P_GAMMA_NO] = 0x01; /* downloaded gamma table */
+ }
+ }
+
+ pc[SET_WINDOW_P_RED_UNDER_COLOR] = 0xff; /* defaults */
+ pc[SET_WINDOW_P_BLUE_UNDER_COLOR] = 0xff;
+ pc[SET_WINDOW_P_GREEN_UNDER_COLOR] = 0xff;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status set_window (SnapScan_Scanner *pss)
+{
+ static const char *me = "set_window";
+ SANE_Status status;
+ unsigned char source;
+ u_char *pc;
+ int pos_factor;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = prepare_set_window(pss);
+ CHECK_STATUS (status, me, "prepare_set_window");
+
+ pc = pss->cmd;
+
+ /* header; we support only one window */
+ pc += SET_WINDOW_LEN;
+
+ /* the sole window descriptor */
+ pc += SET_WINDOW_HEADER_LEN;
+
+ switch (pss->pdev->model)
+ {
+ case PRISA5000:
+ case PRISA5000E:
+ case PRISA5150:
+ pos_factor = (pss->res > 600) ? 1200 : 600;
+ break;
+ case PERFECTION1270:
+ case PERFECTION1670:
+ pos_factor = (pss->res > 800) ? 1600 : 800;
+ break;
+ case PERFECTION2480:
+ pos_factor = (pss->res > 1200) ? 2400 : 1200;
+ break;
+ case PERFECTION3490:
+ pos_factor = (pss->res > 1600) ? 3200 : 1600;
+ break;
+ default:
+ pos_factor = pss->actual_res;
+ break;
+ }
+ /* it's an ugly sound if the scanner drives against the rear
+ wall, and with changing max values we better be sure */
+ check_range(&(pss->brx), pss->pdev->x_range);
+ check_range(&(pss->bry), pss->pdev->y_range);
+ {
+ int tlxp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tlx));
+ int tlyp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tly));
+ int brxp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->brx));
+ int bryp =
+ (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->bry));
+
+ /* Check for brx > tlx and bry > tly */
+ if (brxp <= tlxp) {
+ tlxp = MAX (0, brxp - 75);
+ }
+ if (bryp <= tlyp) {
+ tlyp = MAX (0, bryp - 75);
+ }
+
+ u_int_to_u_char4p (tlxp, pc + SET_WINDOW_P_TLX);
+ u_int_to_u_char4p (tlyp, pc + SET_WINDOW_P_TLY);
+ u_int_to_u_char4p (MAX (((unsigned) (brxp - tlxp)), 75),
+ pc + SET_WINDOW_P_WIDTH);
+ u_int_to_u_char4p (MAX (((unsigned) (bryp - tlyp)), 75),
+ pc + SET_WINDOW_P_LENGTH);
+ DBG (DL_INFO, "%s Width: %d\n", me, brxp-tlxp);
+ DBG (DL_INFO, "%s Length: %d\n", me, bryp-tlyp);
+ }
+
+ source = 0x0;
+ if (pss->preview) {
+ source |= 0x80; /* no high quality in preview */
+ }
+ else {
+ source |= 0x40; /* no preview */
+ }
+ if (!pss->highquality) {
+ source |= 0x80; /* no high quality */
+ }
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490)) {
+ source |= 0x40; /* 2480/3490 always need no_preview bit */
+ }
+ if (pss->source == SRC_TPO) {
+ source |= 0x08;
+ }
+ if (pss->source == SRC_ADF) {
+ source |= 0x10;
+ }
+ pc[SET_WINDOW_P_OPERATION_MODE] = source;
+ DBG (DL_MINOR_INFO, "%s: operation mode set to 0x%02x\n", me, (int) source);
+
+ do {
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ SET_WINDOW_TOTAL_LEN, NULL, NULL);
+ if (status == SANE_STATUS_DEVICE_BUSY) {
+ DBG (DL_MINOR_INFO, "%s: waiting for scanner to warm up\n", me);
+ wait_scanner_ready (pss);
+ }
+ } while (status == SANE_STATUS_DEVICE_BUSY);
+
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Status set_window_autofocus (SnapScan_Scanner *copy)
+{
+ static char me[] = "set_window_autofocus";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s(%p)\n", me, (void*)copy);
+
+ copy->res = copy->actual_res;
+ status = prepare_set_window (copy);
+ CHECK_STATUS (status, me, "prepare_set_window");
+
+ u_int_to_u_char4p (1700, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_TLY);
+ /* fill in width & height */
+ u_int_to_u_char4p (2550, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_WIDTH);
+ u_int_to_u_char4p (128, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_LENGTH);
+
+ copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_BITS_PER_PIX] = 12;
+ copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_OPERATION_MODE] = 0x49; /* focusing mode */
+ return snapscan_cmd (copy->pdev->bus, copy->fd, copy->cmd,
+ SET_WINDOW_TOTAL_LEN, NULL, NULL);
+}
+
+#define SET_FRAME_LEN 10
+
+static SANE_Status set_frame (SnapScan_Scanner *pss, SANE_Byte frame_no)
+{
+ static const char *me = "set_frame";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ DBG (DL_VERBOSE, "%s setting frame to %d\n", me, frame_no);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = OBJECT_POSITION;
+ pss->cmd[1] = 2; /* Absolute position used here to select the frame */
+ pss->cmd[4] = frame_no;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ return status;
+}
+
+static SANE_Status set_focus (SnapScan_Scanner *pss, SANE_Int focus)
+{
+ static const char *me = "set_focus";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s(%d)\n", me, focus);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = OBJECT_POSITION;
+ pss->cmd[1] = 4; /* Rotate object but here it sets the focus point */
+ pss->cmd[3] = (focus >> 8) & 0xFF;
+ pss->cmd[4] = focus & 0xFF;
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Int get_8 (u_char *p)
+{
+ SANE_Int b;
+ b = p[0] | (p[1] << 8);
+ return b;
+}
+
+static double get_val (u_char *p, SANE_Int len, SANE_Int x)
+{
+ return get_8 (p + ((x + len) << 1)) / 255.0;
+}
+
+static double sum_pixel_differences (u_char *p, int len)
+{
+ double v, m, s;
+ SANE_Int i;
+
+ s = 0;
+ for (i = 0; i < len-1; i++) {
+ v = get_val (p, len, i);
+ m = get_val (p, len, i+1);
+ s += fabs (v - m);
+ }
+ return s;
+}
+
+static SANE_Status scan (SnapScan_Scanner *pss)
+{
+ static const char *me = "scan";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = SCAN;
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SCAN_LEN, NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+/* supported read operations */
+
+#define READ_IMAGE 0x00
+#define READ_TRANSTIME 0x80
+
+/* number of bytes expected must be in pss->expected_read_bytes */
+static SANE_Status scsi_read (SnapScan_Scanner *pss, u_char read_type)
+{
+ static const char *me = "scsi_read";
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = READ;
+ pss->cmd[2] = read_type;
+ if (read_type == READ_TRANSTIME && pss->pdev->model == PERFECTION2480)
+ pss->cmd[5] = 1;
+
+ u_int_to_u_char3p (pss->expected_read_bytes, pss->cmd + 6);
+
+ pss->read_bytes = pss->expected_read_bytes;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ READ_LEN, pss->buf, &pss->read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Status get_focus (SnapScan_Scanner *pss)
+{
+ static const char *me = "get_focus";
+ SANE_Int focus_point, max_focus_point;
+ double sum, max;
+ SANE_Status status;
+ SnapScan_Scanner copy;
+
+ copy = *pss;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ reserve_unit(&copy);
+
+ status = set_window_autofocus (&copy);
+ CHECK_STATUS (status, me, "set_window_autofocus");
+
+ status = inquiry (&copy);
+ CHECK_STATUS (status, me, "inquiry");
+
+ status = scan (&copy);
+ CHECK_STATUS (status, me, "scan");
+
+ status = set_frame (&copy, copy.frame_no);
+ CHECK_STATUS (status, me, "set_frame");
+
+ DBG (DL_VERBOSE, "%s: Expected number of bytes for each read %d\n", me, (int)copy.bytes_per_line);
+ DBG (DL_VERBOSE, "%s: Expected number of pixels per line %d\n", me, (int)copy.pixels_per_line);
+ max_focus_point = -1;
+ max = -1;
+ for (focus_point = 0; focus_point <= 0x300; focus_point += 6) {
+ status = set_focus (&copy, focus_point);
+ CHECK_STATUS (status, me, "set_focus");
+
+ copy.expected_read_bytes = copy.bytes_per_line;
+ status = scsi_read (&copy, READ_IMAGE);
+ CHECK_STATUS (status, me, "scsi_read");
+
+ sum = sum_pixel_differences (copy.buf, copy.pixels_per_line);
+
+ if (sum > max) {
+ max = sum;
+ max_focus_point = focus_point;
+ }
+ }
+ pss->focus = max_focus_point;
+ DBG (DL_VERBOSE, "%s: Focus point found to be at 0x%x\n", me, max_focus_point);
+ release_unit (&copy);
+
+ wait_scanner_ready (&copy);
+ CHECK_STATUS (status, me, "wait_scanner_ready");
+
+ return status;
+}
+
+/*
+static SANE_Status request_sense (SnapScan_Scanner *pss)
+{
+ static const char *me = "request_sense";
+ size_t read_bytes = 0;
+ u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, 20, 0};
+ u_char data[20];
+ SANE_Status status;
+
+ read_bytes = 20;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd),
+ data, &read_bytes);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: scsi command error: %s\n",
+ me, sane_strstatus (status));
+ }
+ else
+ {
+ status = sense_handler (pss->fd, data, (void *) pss);
+ }
+ return status;
+}
+*/
+
+static SANE_Status send_diagnostic (SnapScan_Scanner *pss)
+{
+ static const char *me = "send_diagnostic";
+ u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0}; /* self-test */
+ SANE_Status status;
+
+ if (pss->pdev->model == PRISA620
+ ||
+ pss->pdev->model == PRISA610
+ ||
+ pss->pdev->model == SNAPSCAN1236
+ ||
+ pss->pdev->model == SCANWIT2720S
+ ||
+ pss->pdev->model == ARCUS1200)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+ return status;
+}
+
+static SANE_Status wait_scanner_ready (SnapScan_Scanner *pss)
+{
+ static char me[] = "wait_scanner_ready";
+ SANE_Status status;
+ int retries;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ /* if the tray is returning to the start position
+ no time to wait is returned by the scanner. We'll
+ try several times and sleep 1 second between each try. */
+ for (retries = 20; retries; retries--)
+ {
+ status = test_unit_ready (pss);
+ switch (status)
+ {
+ case SANE_STATUS_GOOD:
+ return status;
+ case SANE_STATUS_DEVICE_BUSY:
+ /* first additional sense byte contains time to wait */
+ {
+ int delay = pss->asi1;
+ if (delay > 0)
+ {
+ DBG (0,
+ "Scanner warming up - waiting %d seconds.\n",
+ delay);
+ sleep (delay);
+ }
+ else
+ {
+ /* This seems to happen for Epson scanners. Return
+ SANE_STATUS_GOOD and hope the scanner accepts the
+ next command... */
+ DBG (DL_CALL_TRACE, "%s: No timeout specified, returning immediately\n", me);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ break;
+ case SANE_STATUS_IO_ERROR:
+ /* hardware error; bail */
+ DBG (DL_MAJOR_ERROR, "%s: hardware error detected.\n", me);
+ return status;
+ case SANE_STATUS_JAMMED:
+ case SANE_STATUS_NO_DOCS:
+ return status;
+ default:
+ DBG (DL_MAJOR_ERROR,
+ "%s: unhandled request_sense result; trying again.\n",
+ me);
+ break;
+ }
+ }
+
+ return status;
+}
+
+#define READ_CALIBRATION 0x82
+#define READ_CALIBRATION_BLACK 0x89
+#define NUM_CALIBRATION_LINES 16
+#define NUM_CALIBRATION_LINES_EPSON 48
+#define NUM_CALIBRATION_LINES_EPSON_BLACK 128
+
+static SANE_Status calibrate_epson (SnapScan_Scanner *pss)
+{
+ u_char *buf, *pbuf;
+ int *bins;
+ static const char *me = "calibrate_epson";
+ /* pixels_per_line = 8.5 inch * resolution of one sensor */
+ int pixels_per_line = pss->actual_res * 17/4;
+ int num_bins = pixels_per_line;
+ int i, j, k, loop_inc, tl;
+ int r, g, b;
+ size_t expected_read_bytes;
+ size_t read_bytes;
+ SANE_Status status;
+ int pass;
+ int cal_lines = NUM_CALIBRATION_LINES_EPSON;
+ int dtc = READ_CALIBRATION;
+ int bytes_per_bin = 1;
+
+ /* in 16 bit mode we get two bytes of cal data per bin */
+ if (pss->bpp_scan == 16)
+ bytes_per_bin = 2;
+
+ /* calculate number of bins depending on mode and resolution
+ * colour mode requires bins for each of rgb
+ * full resolution doubles it because of second sensor line */
+ if (is_colour_mode(actual_mode(pss))) {
+ num_bins *= 3;
+ }
+ if (pss->res >= (SANE_Int)pss->actual_res) {
+ num_bins *= 2;
+ }
+
+ /* allocate memory for bins, all the red, then green, then blue */
+ bins = (int *) malloc (num_bins * sizeof (int));
+ if (!bins) {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating bins, %ld bytes.", me, (u_long)num_bins * sizeof (int));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* allocate buffer for receive data */
+ expected_read_bytes = pixels_per_line * 3 * 4;
+ buf = (u_char *) malloc (expected_read_bytes);
+ if (!buf) {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %ld bytes.", me, (u_long)expected_read_bytes);
+ free (bins);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ loop_inc = expected_read_bytes / (num_bins * bytes_per_bin);
+
+ /* do two passes, first pass does basic calibration, second does transparency adaptor if in use */
+ for (pass = 0; pass < 2; pass++) {
+ if (pass == 1) {
+ if (pss->source == SRC_TPO) {
+ /* pass 1 is for black level calibration of transparency adaptor */
+ cal_lines = NUM_CALIBRATION_LINES_EPSON_BLACK;
+ dtc = READ_CALIBRATION_BLACK;
+ } else
+ continue;
+ }
+
+ /* empty the bins */
+ for (i = 0; i < num_bins; i++)
+ bins[i] = 0;
+
+ for (i = 0; i < cal_lines; i += loop_inc) {
+ /* get the calibration data */
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = READ;
+ pss->cmd[2] = dtc;
+ pss->cmd[5] = cal_lines;
+ if (cal_lines - i > loop_inc)
+ expected_read_bytes = loop_inc * (num_bins * bytes_per_bin);
+ else
+ expected_read_bytes = (cal_lines - i) * (num_bins * bytes_per_bin);
+
+ u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
+ read_bytes = expected_read_bytes;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ READ_LEN, buf, &read_bytes);
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "read_cal_2480", sane_strstatus(status));
+ free (bins);
+ free (buf);
+ return status;
+ }
+
+ if (read_bytes != expected_read_bytes) {
+ DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
+ free (bins);
+ free (buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* add calibration results into bins */
+ pbuf = buf;
+ for (j = 0; j < (int)expected_read_bytes / (num_bins * bytes_per_bin); j++) {
+ for (k = 0; k < num_bins; k++) {
+ bins[k] += *pbuf++;
+ /* add in second byte for 16bit mode */
+ if (bytes_per_bin == 2) {
+ bins[k] += *pbuf++ * 256;
+ }
+ }
+ }
+ }
+
+ /* now make averages */
+ for (k = 0; k < num_bins; k++) {
+ bins[k] /= cal_lines;
+ /* also divide by 64 for 16bit mode */
+ if (bytes_per_bin == 2)
+ bins[k] /= 64;
+ }
+ /* now fill up result buffer */
+ r = g = b = 0;
+
+ /* create data to send back: start with r g b values, and follow with differences
+ * to previous value */
+ pbuf = pss->buf + SEND_LENGTH;
+ if (is_colour_mode(actual_mode(pss))) {
+ for (k = 0; k < num_bins / 3; k++) {
+ *pbuf++ = bins[k] - r;
+ r = bins[k];
+ *pbuf++ = bins[k + num_bins/3] - g;
+ g = bins[k + num_bins/3];
+ *pbuf++ = bins[k + 2*num_bins/3] - b;
+ b = bins[k + 2*num_bins/3];
+ }
+ } else {
+ for (k = 0; k < num_bins; k++) {
+ *pbuf++ = bins[k] - g;
+ g = bins[k];
+ }
+ }
+
+ /* send back the calibration data; round up transfer length (to match windows driver) */
+ zero_buf (pss->buf, SEND_LENGTH);
+ pss->buf[0] = SEND;
+ pss->buf[2] = dtc;
+ tl = (num_bins + 0xff) & ~0xff;
+ u_int_to_u_char3p (tl, pss->buf + 6);
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
+ NULL, NULL);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "send_cal_2480", sane_strstatus(status));
+ free (bins);
+ free (buf);
+ return status;
+ }
+ }
+
+ free (bins);
+ free (buf);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status read_calibration_data (SnapScan_Scanner *pss, void *buf, u_char num_lines)
+{
+ static const char *me = "read_calibration_data";
+ SANE_Status status;
+ size_t expected_read_bytes = num_lines * calibration_line_length(pss);
+ size_t read_bytes;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
+ pss->cmd[0] = READ;
+ pss->cmd[2] = READ_CALIBRATION;
+ pss->cmd[5] = num_lines;
+ u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
+ read_bytes = expected_read_bytes;
+
+ status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
+ READ_LEN, buf, &read_bytes);
+ CHECK_STATUS (status, me, "snapscan_cmd");
+
+ if(read_bytes != expected_read_bytes) {
+ DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status calibrate (SnapScan_Scanner *pss)
+{
+ int r;
+ int c;
+ u_char *buf;
+ static const char *me = "calibrate";
+ SANE_Status status;
+ int line_length = calibration_line_length(pss);
+
+ if ((pss->pdev->model == PERFECTION2480) ||
+ (pss->pdev->model == PERFECTION3490)) {
+ return calibrate_epson (pss);
+ }
+
+ if (pss->pdev->model == PRISA5150)
+ {
+ return send_calibration_5150(pss);
+ }
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ if (line_length) {
+ int num_lines = pss->phys_buf_sz / line_length;
+ if (num_lines > NUM_CALIBRATION_LINES)
+ {
+ num_lines = NUM_CALIBRATION_LINES;
+ } else if (num_lines == 0)
+ {
+ DBG(DL_MAJOR_ERROR, "%s: scsi request size underflow (< %d bytes)", me, line_length);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf = (u_char *) malloc(num_lines * line_length);
+ if (!buf)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %d bytes.", me, num_lines * line_length);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DL_MAJOR_ERROR, "%s: reading calibration data (%d lines)\n", me, num_lines);
+ status = read_calibration_data(pss, buf, num_lines);
+ CHECK_STATUS(status, me, "read_calibration_data");
+
+ for(c=0; c < line_length; c++) {
+ u_int sum = 0;
+ for(r=0; r < num_lines; r++) {
+ sum += buf[c + r * line_length];
+ }
+ pss->buf[c + SEND_LENGTH] = sum / num_lines;
+ }
+
+ status = send (pss, DTC_CALIBRATION, 1);
+ CHECK_STATUS(status, me, "send calibration");
+ free(buf);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status download_firmware(SnapScan_Scanner * pss)
+{
+ static const char *me = "download_firmware";
+ unsigned char *pFwBuf, *pCDB;
+ char* firmware = NULL;
+ FILE *fd;
+ size_t bufLength,cdbLength;
+ SANE_Status status = SANE_STATUS_GOOD;
+ char cModelName[8], cModel[255];
+ unsigned char bModelNo;
+
+ bModelNo =*(pss->buf + INQUIRY_HWMI);
+ zero_buf((unsigned char *)cModel, 255);
+ sprintf(cModelName, "%d", bModelNo);
+ DBG(DL_INFO, "Looking up %s\n", cModelName);
+ if (pss->pdev->firmware_filename) {
+ firmware = pss->pdev->firmware_filename;
+ } else if (default_firmware_filename) {
+ firmware = default_firmware_filename;
+ } else {
+ DBG (0,
+ "%s: No firmware entry found in config file %s.\n",
+ me,
+ SNAPSCAN_CONFIG_FILE
+ );
+ status = SANE_STATUS_INVAL;
+ }
+ if (status == SANE_STATUS_GOOD)
+ {
+ cdbLength = 10;
+ DBG(DL_INFO, "Downloading %s\n", firmware);
+ fd = fopen(firmware,"rb");
+ if(fd == NULL)
+ {
+ DBG (0, "Cannot open firmware file %s.\n", firmware);
+ DBG (0, "Edit the firmware file entry in %s.\n", SNAPSCAN_CONFIG_FILE);
+ status = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ switch (pss->pdev->model)
+ {
+ case PRISA610:
+ case PRISA310:
+ case PRISA620:
+ case PRISA1240:
+ case PRISA640:
+ case PRISA4300:
+ case PRISA4300_2:
+ case PRISA5000:
+ case PRISA5000E:
+ case PRISA5150:
+ case PRISA5300:
+ case STYLUS_CX1500:
+ /* ACER firmware files do not contain an info block */
+ fseek(fd, 0, SEEK_END);
+ bufLength = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+ break;
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ /* Epson firmware files contain an info block which
+ specifies the length of the firmware data. The
+ length information is stored at offset 0x64 from
+ end of file */
+ {
+ unsigned char size_l, size_h;
+ fseek(fd, -0x64, SEEK_END);
+ fread(&size_l, 1, 1, fd);
+ fread(&size_h, 1, 1, fd);
+ fseek(fd, 0, SEEK_SET);
+ bufLength = (size_h << 8) + size_l;
+ }
+ break;
+ default:
+ /* AGFA firmware files contain an info block which
+ specifies the length of the firmware data. The
+ length information is stored at offset 0x5e from
+ end of file */
+ {
+ unsigned char size_l, size_h;
+ fseek(fd, -0x5e, SEEK_END);
+ fread(&size_l, 1, 1, fd);
+ fread(&size_h, 1, 1, fd);
+ fseek(fd, 0, SEEK_SET);
+ bufLength = (size_h << 8) + size_l;
+ }
+ break;
+ }
+
+ DBG(DL_INFO, "Size of firmware: %lu\n", (u_long) bufLength);
+ pCDB = (unsigned char *)malloc(bufLength + cdbLength);
+ pFwBuf = pCDB + cdbLength;
+ zero_buf (pCDB, cdbLength);
+ (void)fread(pFwBuf,1,bufLength,fd);
+
+ *pCDB = SEND;
+ *(pCDB + 2) = 0x87;
+ *(pCDB + 6) = (bufLength >> 16) & 0xff;
+ *(pCDB + 7) = (bufLength >> 8) & 0xff;
+ *(pCDB + 8) = bufLength & 0xff;
+
+ status = snapscan_cmd (
+ pss->pdev->bus, pss->fd, pCDB, bufLength+cdbLength, NULL, NULL);
+ pss->firmware_loaded = SANE_TRUE;
+
+ free(pCDB);
+ fclose(fd);
+ }
+ }
+ return status;
+}
+
+/*
+ * $Log$
+ * Revision 1.58 2006/09/03 10:00:11 oliver-guest
+ * Bugfix for firmware download by Paul Smedley
+ *
+ * Revision 1.57 2006/03/20 18:20:10 oliver-guest
+ * Limit inquiry length to 120 bytes if firmware is not yet loaded
+ *
+ * Revision 1.56 2006/01/10 19:32:16 oliver-guest
+ * Added 12 bit gamma tables for Epson Stylus CX-1500
+ *
+ * Revision 1.55 2006/01/06 20:59:17 oliver-guest
+ * Some fixes for the Epson Stylus CX 1500
+ *
+ * Revision 1.54 2006/01/01 22:57:01 oliver-guest
+ * Added calibration data for Benq 5150 / 5250, preliminary support for Epson Stylus CX 1500
+ *
+ * Revision 1.53 2005/12/05 20:38:22 oliver-guest
+ * Small bugfix for Benq 5150
+ *
+ * Revision 1.52 2005/12/04 15:03:00 oliver-guest
+ * Some fixes for Benq 5150
+ *
+ * Revision 1.51 2005/11/26 18:53:03 oliver-guest
+ * Fix inquiry bug for Benq 5000
+ *
+ * Revision 1.50 2005/11/17 23:47:10 oliver-guest
+ * Revert previous 'fix', disable 2400 dpi for Epson 3490, use 1600 dpi instead
+ *
+ * Revision 1.49 2005/11/17 23:32:22 oliver-guest
+ * Fixes for Epson 3490 @ 2400 DPI
+ *
+ * Revision 1.48 2005/11/15 20:11:19 oliver-guest
+ * Enabled quality calibration for the Epson 3490
+ *
+ * Revision 1.47 2005/11/02 19:22:06 oliver-guest
+ * Fixes for Benq 5000
+ *
+ * Revision 1.46 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.45 2005/10/24 19:46:40 oliver-guest
+ * Preview and range fix for Epson 2480/2580
+ *
+ * Revision 1.44 2005/10/23 21:28:58 oliver-guest
+ * Fix for buffer size in high res modes, fixes for delay code
+ *
+ * Revision 1.43 2005/10/20 21:23:53 oliver-guest
+ * Fixes for 16 bit quality calibration by Simon Munton
+ *
+ * Revision 1.42 2005/10/13 22:43:30 oliver-guest
+ * Fixes for 16 bit scan mode from Simon Munton
+ *
+ * Revision 1.41 2005/10/11 18:47:07 oliver-guest
+ * Fixes for Epson 3490 and 16 bit scan mode
+ *
+ * Revision 1.40 2005/09/28 22:09:26 oliver-guest
+ * Reenabled enhanced inquiry command for Epson scanners (duh\!)
+ *
+ * Revision 1.39 2005/09/28 21:33:10 oliver-guest
+ * Added 16 bit option for Epson scanners (untested)
+ *
+ * Revision 1.38 2005/09/25 08:19:12 oliver-guest
+ * Removed debugging code for Epson scanners
+ *
+ * Revision 1.37 2005/09/03 11:31:31 oliver-guest
+ * Another small bugfix
+ *
+ * Revision 1.36 2005/09/03 10:52:11 oliver-guest
+ * Fixed debugging code for epson scanners
+ *
+ * Revision 1.35 2005/08/16 17:19:20 oliver-guest
+ * Make compileable again
+ *
+ * Revision 1.34 2005/08/15 18:56:55 oliver-guest
+ * Added temporary debug code for 2480/2580 distinction
+ *
+ * Revision 1.33 2005/08/15 18:06:37 oliver-guest
+ * Added support for Epson 3490/3590 (thanks to Matt Judge)
+ *
+ * Revision 1.32 2005/07/18 17:37:37 oliver-guest
+ * ZETA changes for SnapScan backend
+ *
+ * Revision 1.31 2004/12/09 23:21:48 oliver-guest
+ * Added quality calibration for Epson 2480 (by Simon Munton)
+ *
+ * Revision 1.30 2004/12/01 22:12:03 oliver-guest
+ * Added support for Epson 1270
+ *
+ * Revision 1.29 2004/10/03 17:34:36 hmg-guest
+ * 64 bit platform fixes (bug #300799).
+ *
+ * Revision 1.28 2004/09/02 20:59:11 oliver-guest
+ * Added support for Epson 2480
+ *
+ * Revision 1.27 2004/04/02 20:19:24 oliver-guest
+ * Various bugfixes for gamma corretion (thanks to Robert Tsien)
+ *
+ * Revision 1.26 2003/11/07 23:26:49 oliver-guest
+ * Final bugfixes for bascic support of Epson 1670
+ *
+ * Revision 1.25 2003/10/21 20:43:25 oliver-guest
+ * Bugfixes for SnapScan backend
+ *
+ * Revision 1.24 2003/10/07 19:41:34 oliver-guest
+ * Updates for Epson Perfection 1670
+ *
+ * Revision 1.23 2003/08/19 21:05:08 oliverschwartz
+ * Scanner ID cleanup
+ *
+ * Revision 1.22 2003/04/30 20:49:39 oliverschwartz
+ * SnapScan backend 1.4.26
+ *
+ * Revision 1.37 2003/04/30 20:42:19 oliverschwartz
+ * Added support for Agfa Arcus 1200 (supplied by Valtteri Vuorikoski)
+ *
+ * Revision 1.36 2003/04/02 21:17:13 oliverschwartz
+ * Fix for 1200 DPI with Acer 5000
+ *
+ * Revision 1.35 2003/02/08 10:45:09 oliverschwartz
+ * Use 600 DPI as optical resolution for Benq 5000
+ *
+ * Revision 1.34 2002/12/10 20:14:12 oliverschwartz
+ * Enable color offset correction for SnapScan300
+ *
+ * Revision 1.33 2002/09/24 16:07:48 oliverschwartz
+ * Added support for Benq 5000
+ *
+ * Revision 1.32 2002/06/06 20:40:01 oliverschwartz
+ * Changed default scan area for transparancy unit of SnapScan e50
+ *
+ * Revision 1.31 2002/05/02 18:28:44 oliverschwartz
+ * Added ADF support
+ *
+ * Revision 1.30 2002/04/27 14:41:22 oliverschwartz
+ * Print number of open handles in close_scanner()
+ *
+ * Revision 1.29 2002/04/10 21:46:48 oliverschwartz
+ * Removed illegal character
+ *
+ * Revision 1.28 2002/04/10 21:01:02 oliverschwartz
+ * Disable send_diagnostic() for 1236s
+ *
+ * Revision 1.27 2002/03/24 12:11:20 oliverschwartz
+ * Get name of firmware file in sane_init
+ *
+ * Revision 1.26 2002/01/23 20:42:41 oliverschwartz
+ * Improve recognition of Acer 320U
+ * Add sense_handler code for sense code 0x0b
+ * Fix for spaces in model strings
+ *
+ * Revision 1.25 2001/12/12 19:44:59 oliverschwartz
+ * Clean up CVS log
+ *
+ * Revision 1.24 2001/12/09 23:01:00 oliverschwartz
+ * - use sense handler for USB
+ * - fix scan mode
+ *
+ * Revision 1.23 2001/12/08 11:53:31 oliverschwartz
+ * - Additional logging in sense handler
+ * - Fix wait_scanner_ready() if device reports busy
+ * - Fix scanning mode (preview/normal)
+ *
+ * Revision 1.22 2001/11/27 23:16:17 oliverschwartz
+ * - Fix color alignment for SnapScan 600
+ * - Added documentation in snapscan-sources.c
+ * - Guard against TL_X < BR_X and TL_Y < BR_Y
+ *
+ * Revision 1.21 2001/10/21 08:49:37 oliverschwartz
+ * correct number of scan lines for calibration thanks to Mikko Ty���vi
+ *
+ * Revision 1.20 2001/10/12 20:54:04 oliverschwartz
+ * enable gamma correction for Snapscan 1236, e20 and e50 scanners
+ *
+ * Revision 1.19 2001/10/11 14:02:10 oliverschwartz
+ * Distinguish between e20/e25 and e40/e50
+ *
+ * Revision 1.18 2001/10/09 22:34:23 oliverschwartz
+ * fix compiler warnings
+ *
+ * Revision 1.17 2001/10/08 19:26:01 oliverschwartz
+ * - Disable quality calibration for scanners that do not support it
+ *
+ * Revision 1.16 2001/10/08 18:22:01 oliverschwartz
+ * - Disable quality calibration for Acer Vuego 310F
+ * - Use sanei_scsi_max_request_size as scanner buffer size
+ * for SCSI devices
+ * - Added new devices to snapscan.desc
+ *
+ * Revision 1.15 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * Revision 1.14 2001/09/17 10:01:08 sable
+ * Added model AGFA 1236U
+ *
+ * Revision 1.13 2001/09/09 18:06:32 oliverschwartz
+ * add changes from Acer (new models; automatic firmware upload for USB scanners); fix distorted colour scans after greyscale scans (call set_window only in sane_start); code cleanup
+ *
+ * Revision 1.12 2001/04/10 13:00:31 sable
+ * Moving sanei_usb_* to snapscani_usb*
+ *
+ * Revision 1.11 2001/04/10 11:04:31 sable
+ * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
+ *
+ * Revision 1.10 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.9 2000/11/10 01:01:59 sable
+ * USB (kind of) autodetection
+ *
+ * Revision 1.8 2000/11/01 01:26:43 sable
+ * Support for 1212U
+ *
+ * Revision 1.7 2000/10/30 22:32:20 sable
+ * Support for vuego310s vuego610s and 1236s
+ *
+ * Revision 1.6 2000/10/29 22:44:55 sable
+ * Bug correction for 1236s
+ *
+ * Revision 1.5 2000/10/28 14:16:10 sable
+ * Bug correction for SnapScan310
+ *
+ * Revision 1.4 2000/10/28 14:06:35 sable
+ * Add support for Acer300f
+ *
+ * Revision 1.3 2000/10/15 19:52:06 cbagwell
+ * Changed USB support to a 1 line modification instead of multi-file
+ * changes.
+ *
+ * Revision 1.2 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ *
+ * */