summaryrefslogtreecommitdiff
path: root/backend/snapscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/snapscan.c')
-rw-r--r--backend/snapscan.c2646
1 files changed, 2646 insertions, 0 deletions
diff --git a/backend/snapscan.c b/backend/snapscan.c
new file mode 100644
index 0000000..44b757b
--- /dev/null
+++ b/backend/snapscan.c
@@ -0,0 +1,2646 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997-2005, 2013 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Simon Munton, 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$
+ SANE SnapScan backend */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_thread.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define MINOR_VERSION 4
+#define BUILD 53
+#define BACKEND_NAME snapscan
+
+#ifdef __GNUC__
+#define UNUSEDARG __attribute__ ((unused))
+#else
+#define UNUSEDARG
+#endif
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/saneopts.h"
+
+#include "snapscan.h"
+
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+#define LIMIT(x,min,max) MIN(MAX(x, min), max)
+
+#ifdef INOPERATIVE
+#define P_200_TO_255(per) SANE_UNFIX(255.0*((per + 100)/200.0))
+#endif
+
+#include "../include/sane/sanei_config.h"
+
+/* debug levels */
+#define DL_INFO 10
+#define DL_MINOR_INFO 15
+#define DL_MAJOR_ERROR 1
+#define DL_MINOR_ERROR 2
+#define DL_DATA_TRACE 50
+#define DL_OPTION_TRACE 70
+#define DL_CALL_TRACE 30
+#define DL_VERBOSE 20
+
+#define CHECK_STATUS(s,caller,cmd) \
+if ((s) != SANE_STATUS_GOOD) { DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", caller, (cmd), sane_strstatus(s)); return s; }
+
+/*----- internal scanner operations -----*/
+
+/* hardware configuration byte masks */
+
+#define HCFG_ADC 0x80 /* AD converter 1 ==> 10bit, 0 ==> 8bit */
+#define HCFG_ADF 0x40 /* automatic document feeder */
+#define HCFG_TPO 0x20 /* transparency option */
+#define HCFG_RB 0x10 /* ring buffer */
+#define HCFG_HT16 0x08 /* 16x16 halftone matrices */
+#define HCFG_HT8 0x04 /* 8x8 halftone matrices */
+#define HCFG_SRA 0x02 /* scanline row average (high-speed colour) */
+#define HCFG_CAL_ALLOWED 0x01 /* 1 ==> calibration allowed */
+
+#define HCFG_HT 0x0C /* support halftone matrices at all */
+
+#define MM_PER_IN 25.4 /* # millimetres per inch */
+#define IN_PER_MM 0.03937 /* # inches per millimetre */
+
+#define GAMMA_8BIT 0
+#define GAMMA_16BIT 1
+#define GAMMA_12_16BIT 2
+
+#ifndef SANE_I18N
+#define SANE_I18N(text) text
+#endif
+
+/* authorization stuff */
+static SANE_Auth_Callback auth = NULL;
+#if UNUSED
+static SANE_Char username[SANE_MAX_USERNAME_LEN];
+static SANE_Char password[SANE_MAX_PASSWORD_LEN];
+#endif
+
+/* function prototypes */
+
+static void gamma_n (double gamma, int brightness, int contrast,
+ u_char *buf, int length, int gamma_mode);
+static void gamma_to_sane (int length, u_char *in, SANE_Int *out);
+
+static size_t max_string_size(SANE_String_Const strings[]);
+
+/* inline functions */
+static inline SnapScan_Mode actual_mode (SnapScan_Scanner *pss)
+{
+ if (pss->preview == SANE_TRUE)
+ return pss->preview_mode;
+ return pss->mode;
+}
+
+static inline int is_colour_mode (SnapScan_Mode m)
+{
+ return (m == MD_COLOUR) || (m == MD_BILEVELCOLOUR);
+}
+
+static inline int calibration_line_length(SnapScan_Scanner *pss)
+{
+ int pos_factor;
+ int pixel_length;
+
+ switch (pss->pdev->model)
+ {
+ case STYLUS_CX1500:
+ case PRISA5000E:
+ case PRISA5000:
+ case PRISA5150:
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ pos_factor = pss->actual_res / 2;
+ pixel_length = pos_factor * 8.5;
+ break;
+ case SCANWIT2720S:
+ pixel_length = 2550;
+ break;
+ default:
+ pos_factor = pss->actual_res;
+ pixel_length = pos_factor * 8.5;
+ break;
+ }
+
+ if(is_colour_mode(actual_mode(pss))) {
+ return 3 * pixel_length;
+ } else {
+ return pixel_length;
+ }
+}
+
+/*----- global data structures and access utilities -----*/
+
+/* available device list */
+
+static SnapScan_Device *first_device = NULL; /* device list head */
+static SANE_Int n_devices = 0; /* the device count */
+static SANE_Char *default_firmware_filename;
+static SANE_Bool cancelRead;
+
+/* list returned from sane_get_devices() */
+static const SANE_Device **get_devices_list = NULL;
+
+/* external routines */
+#include "snapscan-scsi.c"
+#include "snapscan-sources.c"
+#include "snapscan-usb.c"
+#include "snapscan-options.c"
+
+/* Initialize gamma tables */
+static SANE_Status alloc_gamma_tables(SnapScan_Scanner * ps)
+{
+ static const char me[] = "alloc_gamma_tables";
+
+ ps->gamma_length = 1 << ps->bpp;
+ DBG (DL_MINOR_INFO, "%s: using 4*%d bytes for gamma table\n",
+ me,
+ ps->gamma_length);
+
+ ps->gamma_tables =
+ (SANE_Int *) malloc(4 * ps->gamma_length * sizeof(SANE_Int));
+
+ if (!ps->gamma_tables)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ps->gamma_table_gs = &ps->gamma_tables[0 * ps->gamma_length];
+ ps->gamma_table_r = &ps->gamma_tables[1 * ps->gamma_length];
+ ps->gamma_table_g = &ps->gamma_tables[2 * ps->gamma_length];
+ ps->gamma_table_b = &ps->gamma_tables[3 * ps->gamma_length];
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status init_gamma(SnapScan_Scanner * ps)
+{
+ u_char *gamma;
+
+ gamma = (u_char*) malloc(ps->gamma_length * sizeof(u_char) * 2);
+
+ if (!gamma)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Default tables */
+ gamma_n (SANE_UNFIX(ps->gamma_gs), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_gs);
+
+ gamma_n (SANE_UNFIX(ps->gamma_r), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_r);
+
+ gamma_n (SANE_UNFIX(ps->gamma_g), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_g);
+
+ gamma_n (SANE_UNFIX(ps->gamma_b), ps->bright, ps->contrast, gamma, ps->bpp, 1);
+ gamma_to_sane (ps->gamma_length, gamma, ps->gamma_table_b);
+
+ free (gamma);
+ return SANE_STATUS_GOOD;
+}
+
+/* Max string size */
+
+static size_t max_string_size (SANE_String_Const strings[])
+{
+ size_t size;
+ size_t 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;
+}
+
+/* gamma table computation */
+static void gamma_n (double gamma, int brightness, int contrast,
+ u_char *buf, int bpp, int gamma_mode)
+{
+ int i;
+ double i_gamma = 1.0/gamma;
+ int length = 1 << bpp;
+ int max = length - 1;
+ double mid = max / 2.0;
+
+ for (i = 0; i < length; i++)
+ {
+ int x;
+ double val = (i - mid) * (1.0 + contrast / 100.0)
+ + (1.0 + brightness / 100.0) * mid;
+ val = LIMIT(val, 0, max);
+ switch (gamma_mode)
+ {
+ case GAMMA_16BIT:
+ x = LIMIT(65535*pow ((double) val/max, i_gamma) + 0.5, 0, 65535);
+
+ buf[2*i] = (u_char) x;
+ buf[2*i + 1] = (u_char) (x >> 8);
+ break;
+ case GAMMA_12_16BIT:
+ buf[2*i] = (u_char) i;
+ buf[2*i + 1] = (u_char) (i >> 8);
+ break;
+ case GAMMA_8BIT:
+ buf[i] =
+ (u_char) LIMIT(255*pow ((double) val/max, i_gamma) + 0.5, 0, 255);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void gamma_from_sane (int length, SANE_Int *in, u_char *out, int gamma_mode)
+{
+ int i;
+ for (i = 0; i < length; i++)
+ if (gamma_mode != GAMMA_8BIT)
+ {
+ out[2*i] = (u_char) LIMIT(in[i], 0, 65535);
+ out[2*i + 1] = (u_char) (LIMIT(in[i], 0, 65535) >> 8);
+ }
+ else
+ out[i] = (u_char) LIMIT(in[i] / 256, 0, 255);
+}
+
+static void gamma_to_sane (int length, u_char *in, SANE_Int *out)
+{
+ int i;
+ for (i = 0; i < length; i++)
+ out[i] = in[2*i] + 256 * in[2*i + 1];
+}
+
+/* dispersed-dot dither matrices; this is discussed in Foley, Van Dam,
+ Feiner and Hughes: Computer Graphics: principles and practice,
+ 2nd ed. (Addison-Wesley), pp 570-571.
+
+ The function mfDn computes the nth dispersed-dot dither matrix Dn
+ given D(n/2) and n; n is presumed to be a power of 2. D8 and D16
+ are the matrices of interest to us, since the SnapScan supports
+ only 8x8 and 16x16 dither matrices. */
+
+static u_char D2[] ={0, 2, 3, 1};
+
+static u_char D4[16], D8[64], D16[256];
+
+static void mkDn (u_char *Dn, u_char *Dn_half, unsigned n)
+{
+ unsigned int x, y;
+ for (y = 0; y < n; y++) {
+ for (x = 0; x < n; x++) {
+ /* Dn(x,y) = D2(2*x/n, 2*y/n) +4*Dn_half(x%(n/2), y%(n/2)) */
+ Dn[y*n + x] = D2[((int)(2*y/n))*2 + (int)(2*x/n)]
+ + 4*Dn_half[(y%(n/2))*(n/2) + x%(n/2)];
+ }
+ }
+}
+
+static SANE_Bool device_already_in_list (SnapScan_Device *current,
+ SANE_String_Const name)
+{
+ for ( ; NULL != current; current = current->pnext)
+ {
+ if (0 == strcmp (name, current->dev.name))
+ return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+static SANE_Char* get_driver_name(SnapScan_Model model_num) {
+ SANE_Int i;
+ for (i=0; i<known_drivers; i++) {
+ if (drivers[i].id == model_num) break;
+ }
+ if (i == known_drivers) {
+ DBG(0, "Implementation error: Driver name not found\n");
+ return ("Unknown");
+ }
+ return (drivers[i].driver_name);
+}
+
+static SANE_Status snapscani_check_device(
+ int fd,
+ SnapScan_Bus bus_type,
+ char* vendor,
+ char* model,
+ SnapScan_Model* model_num
+) {
+ static const char me[] = "snapscani_check_device";
+ SANE_Status status = SANE_STATUS_GOOD;
+ int supported_vendor = 0;
+ int i;
+
+ DBG (DL_CALL_TRACE, "%s()\n", me);
+
+ /* check that the device is legitimate */
+ if ((status = mini_inquiry (bus_type, fd, vendor, model)) != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: mini_inquiry failed with %s.\n",
+ me,
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DL_VERBOSE,
+ "%s: Is vendor \"%s\" model \"%s\" a supported scanner?\n",
+ me,
+ vendor,
+ model);
+
+ /* check if this is one of our supported vendors */
+ for (i = 0; i < known_vendors; i++)
+ {
+ if (0 == strcasecmp (vendor, vendors[i]))
+ {
+ supported_vendor = 1;
+ break;
+ }
+ }
+ if (supported_vendor)
+ {
+ /* Known vendor. Check if it is one of our supported models */
+ *model_num = snapscani_get_model_id(model, fd, bus_type);
+ }
+ if (!supported_vendor || UNKNOWN == model_num)
+ {
+ DBG (DL_MINOR_ERROR,
+ "%s: \"%s %s\" is not one of %s\n",
+ me,
+ vendor,
+ model,
+ "AGFA SnapScan 300, 310, 600, 1212, 1236, e10, e20, e25, e26, "
+ "e40, e42, e50, e52 or e60\n"
+ "Acer 300, 310, 610, 610+, "
+ "620, 620+, 640, 1240, 3300, 4300 or 5300\n"
+ "Guillemot MaxiScan A4 Deluxe");
+ status = SANE_STATUS_INVAL;
+ } else {
+ DBG(DL_VERBOSE, "%s: Autodetected driver: %s\n", me, get_driver_name(*model_num));
+ }
+ return status;
+}
+
+static SANE_Status snapscani_init_device_structure(
+ SnapScan_Device **pd,
+ const SnapScan_Bus bus_type,
+ SANE_String_Const name,
+ const char* vendor,
+ const char* model,
+ const SnapScan_Model model_num
+) {
+ static const char me[] = "snapscani_init_device_structure";
+ SANE_Status status = SANE_STATUS_GOOD;;
+
+ DBG (DL_CALL_TRACE, "%s()\n", me);
+
+ (*pd) = (SnapScan_Device *) malloc (sizeof (SnapScan_Device));
+ if (!(*pd))
+ {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory allocating device.", me);
+ return SANE_STATUS_NO_MEM;
+ }
+ (*pd)->dev.name = strdup (name);
+ if (strcmp(vendor, "Color") == 0) {
+ (*pd)->dev.vendor = strdup ("Acer");
+ } else {
+ (*pd)->dev.vendor = strdup (vendor);
+ }
+ (*pd)->dev.model = strdup (model);
+ if (model_num == SCANWIT2720S)
+ {
+ (*pd)->dev.type = strdup (SNAPSCAN_FS_TYPE);
+ }
+ else
+ {
+ (*pd)->dev.type = strdup (SNAPSCAN_TYPE);
+ }
+ (*pd)->bus = bus_type;
+ (*pd)->model = model_num;
+
+ if (!(*pd)->dev.name || !(*pd)->dev.vendor || !(*pd)->dev.model || !(*pd)->dev.type)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory allocating device descriptor strings.\n",
+ me);
+ free (*pd);
+ return SANE_STATUS_NO_MEM;
+ }
+ (*pd)->x_range.min = x_range_fb.min;
+ (*pd)->x_range.quant = x_range_fb.quant;
+ (*pd)->x_range.max = x_range_fb.max;
+ (*pd)->y_range.min = y_range_fb.min;
+ (*pd)->y_range.quant = y_range_fb.quant;
+ (*pd)->y_range.max = y_range_fb.max;
+ (*pd)->firmware_filename = NULL;
+
+ (*pd)->pnext = first_device;
+ first_device = (*pd);
+ n_devices++;
+ return status;
+}
+
+static SANE_Status add_scsi_device (SANE_String_Const full_name)
+{
+ int fd;
+ static const char me[] = "add_scsi_device";
+ SANE_Status status = SANE_STATUS_GOOD;
+ SnapScan_Device *pd;
+ SnapScan_Model model_num = UNKNOWN;
+ SnapScan_Bus bus_type = SCSI;
+ char vendor[8];
+ char model[17];
+ SANE_Char *name = NULL;
+
+ DBG (DL_CALL_TRACE, "%s(%s)\n", me, full_name);
+
+ sanei_config_get_string(full_name, &name);
+ if (!name)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ /* Avoid adding the same device more then once */
+ if (device_already_in_list (first_device, name)) {
+ free(name);
+ name = 0;
+ return SANE_STATUS_GOOD;
+ }
+
+ vendor[0] = model[0] = '\0';
+
+ DBG (DL_VERBOSE, "%s: Detected (kind of) a SCSI device\n", me);
+
+ status = sanei_scsi_open (name, &fd, sense_handler, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error opening device %s: %s\n",
+ me,
+ name,
+ sane_strstatus (status));
+ } else {
+ status = snapscani_check_device(fd, bus_type, vendor, model, &model_num);
+ sanei_scsi_close(fd);
+ }
+ if (status == SANE_STATUS_GOOD) {
+ status = snapscani_init_device_structure(
+ &pd,
+ bus_type,
+ name,
+ vendor,
+ model,
+ model_num
+ );
+ }
+ free(name);
+ name = 0;
+ return status;
+}
+
+static SANE_Status add_usb_device (SANE_String_Const full_name) {
+ static const char me[] = "add_usb_device";
+ int fd;
+ SnapScan_Device *pd;
+ SnapScan_Model model_num = UNKNOWN;
+ SANE_Word vendor_id, product_id;
+ int supported_usb_vendor = 0;
+ char vendor[8];
+ char model[17];
+ SANE_Status status = SANE_STATUS_GOOD;
+ SnapScan_Bus bus_type = USB;
+ int i;
+ SANE_Char *name = NULL;
+
+ DBG (DL_CALL_TRACE, "%s(%s)\n", me, full_name);
+ sanei_config_get_string(full_name, &name);
+ if (!name)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ /* Avoid adding the same device more then once */
+ if (device_already_in_list (first_device, name)) {
+ free(name);
+ name = 0;
+ return SANE_STATUS_GOOD;
+ }
+
+ vendor[0] = model[0] = '\0';
+
+ DBG (DL_VERBOSE, "%s: Detected (kind of) an USB device\n", me);
+ bus_type = USB;
+ status = snapscani_usb_shm_init();
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ status = snapscani_usb_open (name, &fd, sense_handler, NULL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error opening device %s: %s\n",
+ me,
+ name,
+ sane_strstatus (status));
+ } else {
+ if (sanei_usb_get_vendor_product(fd, &vendor_id, &product_id) ==
+ SANE_STATUS_GOOD)
+ {
+ /* check for known USB vendors to avoid hanging scanners by
+ inquiry-command.
+ */
+ DBG(DL_INFO, "%s: Checking if 0x%04x is a supported USB vendor ID\n",
+ me, vendor_id);
+ for (i = 0; i < known_usb_vendor_ids; i++) {
+ if (vendor_id == usb_vendor_ids[i]) {
+ supported_usb_vendor = 1;
+ }
+ }
+ if (!supported_usb_vendor) {
+ DBG(DL_MINOR_ERROR,
+ "%s: USB vendor ID 0x%04x is currently NOT supported by the snapscan backend.\n",
+ me, vendor_id);
+ status=SANE_STATUS_INVAL;
+ snapscani_usb_close(fd);
+ }
+ }
+ }
+ if (status == SANE_STATUS_GOOD) {
+ status = snapscani_check_device(fd, bus_type, vendor, model, &model_num);
+ snapscani_usb_close(fd);
+ }
+ /* deinit shared memory, will be initialized again in open_scanner */
+ snapscani_usb_shm_exit();
+ if (status == SANE_STATUS_GOOD) {
+ status = snapscani_init_device_structure(
+ &pd,
+ bus_type,
+ name,
+ vendor,
+ model,
+ model_num
+ );
+ }
+ free(name);
+ name = 0;
+ return status;
+}
+
+/* find_device: find a device in the available list by name
+
+ ARG: the device name
+
+ RET: a pointer to the corresponding device record, or NULL if there
+ is no such device */
+
+static SnapScan_Device *find_device (SANE_String_Const name)
+{
+ static char me[] = "find_device";
+ SnapScan_Device *psd;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ for (psd = first_device; psd; psd = psd->pnext)
+ {
+ if (strcmp (psd->dev.name, name) == 0)
+ return psd;
+ }
+ return NULL;
+}
+
+/*----- functions in the scanner interface -----*/
+
+SANE_Status sane_init (SANE_Int *version_code,
+ SANE_Auth_Callback authorize)
+{
+ static const char me[] = "sane_snapscan_init";
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ DBG (DL_VERBOSE, "%s: Snapscan backend version %d.%d.%d\n",
+ me,
+ SANE_CURRENT_MAJOR, MINOR_VERSION, BUILD);
+
+ if (version_code != NULL)
+ {
+ *version_code =
+ SANE_VERSION_CODE (SANE_CURRENT_MAJOR, MINOR_VERSION, BUILD);
+ }
+
+ auth = authorize;
+ /* Initialize data structures */
+ default_firmware_filename = NULL;
+ first_device = NULL;
+ n_devices = 0;
+
+ sanei_usb_init();
+ sanei_thread_init();
+ /* build a device structure */
+ fp = sanei_config_open (SNAPSCAN_CONFIG_FILE);
+ if (!fp)
+ {
+ /* default to DEFAULT_DEVICE instead of insisting on config file */
+ DBG (DL_INFO,
+ "%s: configuration file not found, defaulting to %s.\n",
+ me,
+ DEFAULT_DEVICE);
+ status = add_scsi_device (DEFAULT_DEVICE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MINOR_ERROR,
+ "%s: failed to add device \"%s\"\n",
+ me,
+ dev_name);
+ }
+ }
+ else
+ {
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ len = strlen (dev_name);
+ if (!len)
+ continue; /* ignore empty lines */
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ if (strncasecmp(dev_name, FIRMWARE_KW, strlen(FIRMWARE_KW)) == 0) {
+ if (!default_firmware_filename) {
+ sanei_config_get_string(dev_name + strlen(FIRMWARE_KW), &default_firmware_filename);
+ if (default_firmware_filename == NULL) {
+ DBG (0, "%s: Illegal firmware entry %s.\n", me, dev_name);
+ }
+ }
+ }
+ else if (strncasecmp(dev_name, OPTIONS_KW, strlen(OPTIONS_KW)) == 0)
+ continue; /* ignore options lines */
+
+ else if (strncmp(dev_name, "usb", 3) == 0) {
+ sanei_usb_attach_matching_devices (dev_name, add_usb_device);
+ }
+ else if (strncmp(dev_name, "scsi", 4) == 0) {
+ sanei_config_attach_matching_devices (dev_name, add_scsi_device);
+ }
+ else if (strstr (dev_name, "usb")) {
+ add_usb_device(dev_name);
+ }
+ else {
+ add_scsi_device(dev_name);
+ }
+ }
+ fclose (fp);
+ }
+
+ /* compute the dither matrices */
+
+ mkDn (D4, D2, 4);
+ mkDn (D8, D4, 8);
+ mkDn (D16, D8, 16);
+ /* scale the D8 matrix from 0..63 to 0..255 */
+ {
+ u_char i;
+ for (i = 0; i < 64; i++)
+ D8[i] = (u_char) (4 * D8[i] + 2);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static void free_device_list(SnapScan_Device *psd) {
+ if (psd->pnext != NULL) {
+ free_device_list(psd->pnext);
+ }
+ free(psd);
+}
+
+void sane_exit (void)
+{
+ DBG (DL_CALL_TRACE, "sane_snapscan_exit\n");
+
+ if (get_devices_list)
+ free (get_devices_list);
+ get_devices_list = NULL;
+
+ /* just for safety, reset things to known values */
+ auth = NULL;
+
+ if (first_device) {
+ free_device_list(first_device);
+ first_device = NULL;
+ }
+ n_devices = 0;
+}
+
+
+SANE_Status sane_get_devices (const SANE_Device ***device_list,
+ SANE_Bool local_only)
+{
+ static const char *me = "sane_snapscan_get_devices";
+ DBG (DL_CALL_TRACE,
+ "%s (%p, %ld)\n",
+ me,
+ (const void *) device_list,
+ (long) local_only);
+
+ /* Waste the last list returned from this function */
+ if (NULL != get_devices_list)
+ free (get_devices_list);
+
+ *device_list =
+ (const SANE_Device **) malloc ((n_devices + 1) * sizeof (SANE_Device *));
+
+ if (*device_list)
+ {
+ int i;
+ SnapScan_Device *pdev;
+ for (i = 0, pdev = first_device; pdev; i++, pdev = pdev->pnext)
+ (*device_list)[i] = &(pdev->dev);
+ (*device_list)[i] = 0x0000 /*NULL */;
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR, "%s: out of memory\n", me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ get_devices_list = *device_list;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ static const char *me = "sane_snapscan_open";
+ SnapScan_Device *psd;
+ SANE_Status status;
+
+ DBG (DL_CALL_TRACE, "%s (%s, %p)\n", me, name, (void *) h);
+
+ /* possible authorization required */
+
+ /* no device name: use first device */
+ if ((strlen(name) == 0) && (first_device != NULL))
+ {
+ name = first_device->dev.name;
+ }
+
+ /* device exists? */
+ psd = find_device (name);
+ if (!psd)
+ {
+ DBG (DL_MINOR_ERROR,
+ "%s: device \"%s\" not in current device list.\n",
+ me,
+ name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* create and initialize the scanner structure */
+
+ *h = (SnapScan_Scanner *) calloc (sizeof (SnapScan_Scanner), 1);
+ if (!*h)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory creating scanner structure.\n",
+ me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ {
+ SnapScan_Scanner *pss = *(SnapScan_Scanner **) h;
+
+ {
+ pss->devname = strdup (name);
+ if (!pss->devname)
+ {
+ free (*h);
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory copying device name.\n",
+ me);
+ return SANE_STATUS_NO_MEM;
+ }
+ pss->pdev = psd;
+ pss->opens = 0;
+ pss->sense_str = NULL;
+ pss->as_str = NULL;
+ pss->phys_buf_sz = DEFAULT_SCANNER_BUF_SZ;
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
+ pss->phys_buf_sz *= 2;
+ if (psd->bus == SCSI) {
+ pss->phys_buf_sz = sanei_scsi_max_request_size;
+ }
+ DBG (DL_DATA_TRACE,
+ "%s: Allocating %lu bytes as scanner buffer.\n",
+ me, (u_long) pss->phys_buf_sz);
+ pss->buf = (u_char *) malloc(pss->phys_buf_sz);
+ if (!pss->buf) {
+ DBG (DL_MAJOR_ERROR,
+ "%s: out of memory creating scanner buffer.\n",
+ me);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DL_VERBOSE,
+ "%s: allocated scanner structure at %p\n",
+ me,
+ (void *) pss);
+ }
+ status = snapscani_usb_shm_init();
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ status = open_scanner (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: open_scanner failed, status: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ DBG (DL_MINOR_INFO, "%s: waiting for scanner to warm up.\n", me);
+ status = wait_scanner_ready (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error waiting for scanner to warm up: %s\n",
+ me,
+ sane_strstatus(status));
+ free (pss);
+ return status;
+ }
+ DBG (DL_MINOR_INFO, "%s: performing scanner self test.\n", me);
+ status = send_diagnostic (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MINOR_INFO, "%s: send_diagnostic reports %s\n",
+ me, sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+ DBG (DL_MINOR_INFO, "%s: self test passed.\n", me);
+
+ /* option initialization depends on getting the hardware configuration
+ byte */
+ status = inquiry (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error in inquiry command: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+
+ if (pss->pdev->bus == USB)
+ {
+ if (sanei_usb_get_vendor_product(pss->fd, &pss->usb_vendor, &pss->usb_product) != SANE_STATUS_GOOD)
+ {
+ pss->usb_vendor = 0;
+ pss->usb_product = 0;
+ }
+ /* Download Firmware for USB scanners */
+ if (pss->hwst & 0x02)
+ {
+ char vendor[8];
+ char model[17];
+
+ status = download_firmware(pss);
+ CHECK_STATUS (status, me, "download_firmware");
+ /* send inquiry command again, wait for scanner to initialize */
+ status = wait_scanner_ready(pss);
+ CHECK_STATUS (status, me, "wait_scanner_ready after firmware upload");
+ status = mini_inquiry (pss->pdev->bus, pss->fd, vendor, model);
+ CHECK_STATUS (status, me, "mini_inquiry after firmware upload");
+ /* The model identifier may change after firmware upload */
+ DBG (DL_INFO,
+ "%s (after firmware upload): Checking if \"%s\" is a supported scanner\n",
+ me,
+ model);
+ /* Check if it is one of our supported models */
+ pss->pdev->model = snapscani_get_model_id(model, pss->fd, pss->pdev->bus);
+
+ if (pss->pdev->model == UNKNOWN) {
+ DBG (DL_MINOR_ERROR,
+ "%s (after firmware upload): \"%s\" is not a supported scanner\n",
+ me,
+ model);
+ }
+ /* run "real" inquiry command once again for option initialization */
+ status = inquiry (pss);
+ CHECK_STATUS (status, me, "inquiry after firmware upload");
+ }
+ }
+ close_scanner(pss);
+
+ status = alloc_gamma_tables (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error in alloc_gamma_tables: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+
+ init_options (pss);
+ status = init_gamma (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error in init_gamma: %s\n",
+ me,
+ sane_strstatus (status));
+ free (pss);
+ return status;
+ }
+
+ pss->state = ST_IDLE;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void sane_close (SANE_Handle h)
+{
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ DBG (DL_CALL_TRACE, "sane_snapscan_close (%p)\n", (void *) h);
+ switch (pss->state)
+ {
+ case ST_SCAN_INIT:
+ case ST_SCANNING:
+ release_unit (pss);
+ break;
+ default:
+ break;
+ }
+ close_scanner (pss);
+ snapscani_usb_shm_exit();
+ free (pss->gamma_tables);
+ free (pss->buf);
+ free (pss);
+}
+
+
+
+SANE_Status sane_get_parameters (SANE_Handle h,
+ SANE_Parameters *p)
+{
+ static const char *me = "sane_snapscan_get_parameters";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SnapScan_Mode mode = actual_mode(pss);
+
+ DBG (DL_CALL_TRACE, "%s (%p, %p)\n", me, (void *) h, (void *) p);
+
+ p->last_frame = SANE_TRUE; /* we always do only one frame */
+
+ if ((pss->state == ST_SCAN_INIT) || (pss->state == ST_SCANNING))
+ {
+ /* we are in the middle of a scan, so we can use the data
+ that the scanner has reported */
+ if (pss->psrc != NULL)
+ {
+ DBG(DL_DATA_TRACE, "%s: Using source chain data\n", me);
+ /* use what the source chain says */
+ p->pixels_per_line = pss->psrc->pixelsPerLine(pss->psrc);
+ p->bytes_per_line = pss->psrc->bytesPerLine(pss->psrc);
+ /* p->lines = pss->psrc->remaining(pss->psrc)/p->bytes_per_line; */
+ p->lines = pss->lines;
+ }
+ else
+ {
+ DBG(DL_DATA_TRACE, "%s: Using current data\n", me);
+ /* estimate based on current data */
+ p->pixels_per_line = pss->pixels_per_line;
+ p->bytes_per_line = pss->bytes_per_line;
+ p->lines = pss->lines;
+ if (mode == MD_BILEVELCOLOUR)
+ p->bytes_per_line = p->pixels_per_line*3;
+ }
+ }
+ else
+ {
+ /* no scan in progress. The scanner data may not be up to date.
+ we have to calculate an estimate. */
+ double width, height;
+ int dpi;
+ double dots_per_mm;
+
+ DBG(DL_DATA_TRACE, "%s: Using estimated data\n", me);
+ width = SANE_UNFIX (pss->brx - pss->tlx);
+ height = SANE_UNFIX (pss->bry - pss->tly);
+ dpi = pss->res;
+ dots_per_mm = dpi / MM_PER_IN;
+ p->pixels_per_line = width * dots_per_mm;
+ p->lines = height * dots_per_mm;
+ switch (mode)
+ {
+ case MD_COLOUR:
+ case MD_BILEVELCOLOUR:
+ p->bytes_per_line = 3 * p->pixels_per_line * ((pss->bpp_scan+7)/8);
+ break;
+ case MD_LINEART:
+ p->bytes_per_line = (p->pixels_per_line + 7) / 8;
+ break;
+ default:
+ /* greyscale */
+ p->bytes_per_line = p->pixels_per_line * ((pss->bpp_scan+7)/8);
+ break;
+ }
+ }
+ p->format = (is_colour_mode(mode)) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ if (mode == MD_LINEART)
+ p->depth = 1;
+ else if (pss->pdev->model == SCANWIT2720S)
+ p->depth = 16;
+ else if (pss->preview)
+ p->depth = 8;
+ else
+ p->depth = pss->val[OPT_BIT_DEPTH].w;
+
+ DBG (DL_DATA_TRACE, "%s: depth = %ld\n", me, (long) p->depth);
+ DBG (DL_DATA_TRACE, "%s: lines = %ld\n", me, (long) p->lines);
+ DBG (DL_DATA_TRACE,
+ "%s: pixels per line = %ld\n",
+ me,
+ (long) p->pixels_per_line);
+ DBG (DL_DATA_TRACE,
+ "%s: bytes per line = %ld\n",
+ me,
+ (long) p->bytes_per_line);
+
+ return status;
+}
+
+/* scan data reader routine for child process */
+
+#define READER_WRITE_SIZE 4096
+
+static void reader (SnapScan_Scanner *pss)
+{
+ static char me[] = "Child reader process";
+ SANE_Status status;
+ SANE_Byte *wbuf = NULL;
+
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ wbuf = (SANE_Byte*) malloc(READER_WRITE_SIZE);
+ if (wbuf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: failed to allocate write buffer.\n", me);
+ return;
+ }
+
+ while ((pss->preadersrc->remaining(pss->preadersrc) > 0) && !cancelRead)
+ {
+ SANE_Int ndata = READER_WRITE_SIZE;
+ status = pss->preadersrc->get(pss->preadersrc, wbuf, &ndata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: %s on read.\n",
+ me,
+ sane_strstatus (status));
+ return;
+ }
+ {
+ SANE_Byte *buf = wbuf;
+ DBG (DL_DATA_TRACE, "READ %d BYTES (%d)\n", ndata, cancelRead);
+ while (ndata > 0)
+ {
+ int written = write (pss->rpipe[1], buf, ndata);
+ DBG (DL_DATA_TRACE, "WROTE %d BYTES\n", written);
+ if (written == -1)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: error writing scan data on parent pipe.\n",
+ me);
+ perror ("pipe error: ");
+ }
+ else
+ {
+ ndata -= written;
+ buf += written;
+ }
+ }
+ }
+ }
+}
+
+/** signal handler to kill the child process
+ */
+static RETSIGTYPE usb_reader_process_sigterm_handler( int signo )
+{
+ DBG( DL_INFO, "(SIG) reader_process: terminated by signal %d\n", signo );
+ cancelRead = SANE_TRUE;
+}
+
+static RETSIGTYPE sigalarm_handler( int signo UNUSEDARG)
+{
+ DBG( DL_INFO, "ALARM!!!\n" );
+}
+
+/** executed as a child process
+ * read the data from the driver and send them to the parent process
+ */
+static int reader_process( void *args )
+{
+ SANE_Status status;
+ struct SIGACTION act;
+ sigset_t ignore_set;
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) args;
+
+ if( sanei_thread_is_forked()) {
+ DBG( DL_MINOR_INFO, "reader_process started (forked)\n" );
+ /* child process - close read side, make stdout the write side of the pipe */
+ close( pss->rpipe[0] );
+ pss->rpipe[0] = -1;
+ } else {
+ DBG( DL_MINOR_INFO, "reader_process started (as thread)\n" );
+ }
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGUSR1 );
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ memset ( &act, 0, sizeof (act));
+ sigaction( SIGTERM, &act, 0 );
+
+ cancelRead = SANE_FALSE;
+
+ /* install the signal handler */
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = usb_reader_process_sigterm_handler;
+ sigaction( SIGUSR1, &act, 0 );
+
+ status = create_base_source (pss, SCSI_SRC, &(pss->preadersrc));
+ if (status == SANE_STATUS_GOOD)
+ {
+ reader (pss);
+ }
+ else
+ {
+ DBG (DL_MAJOR_ERROR,
+ "Reader process: failed to create SCSISource.\n");
+ }
+ pss->preadersrc->done(pss->preadersrc);
+ free(pss->preadersrc);
+ pss->preadersrc = 0;
+ close( pss->rpipe[1] );
+ pss->rpipe[1] = -1;
+ DBG( DL_MINOR_INFO, "reader_process: finished reading data\n" );
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status start_reader (SnapScan_Scanner *pss)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ static char me[] = "start_reader";
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ pss->nonblocking = SANE_FALSE;
+ pss->rpipe[0] = pss->rpipe[1] = -1;
+ pss->child = -1;
+
+ if (pipe (pss->rpipe) != -1)
+ {
+ pss->orig_rpipe_flags = fcntl (pss->rpipe[0], F_GETFL, 0);
+ pss->child = sanei_thread_begin(reader_process, (void *) pss);
+
+ cancelRead = SANE_FALSE;
+
+ if (pss->child == -1)
+ {
+ /* we'll have to read in blocking mode */
+ DBG (DL_MAJOR_ERROR,
+ "%s: Error while calling sanei_thread_begin; must read in blocking mode.\n",
+ me);
+ close (pss->rpipe[0]);
+ close (pss->rpipe[1]);
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+ if (sanei_thread_is_forked())
+ {
+ /* parent; close write side */
+ close (pss->rpipe[1]);
+ pss->rpipe[1] = -1;
+ }
+ pss->nonblocking = SANE_TRUE;
+ }
+ return status;
+}
+
+static SANE_Status send_gamma_table (SnapScan_Scanner *pss, u_char dtc, u_char dtcq)
+{
+ static char me[] = "send_gamma_table";
+ SANE_Status status = SANE_STATUS_GOOD;
+ status = send (pss, dtc, dtcq);
+ CHECK_STATUS (status, me, "send");
+ switch (pss->pdev->model)
+ {
+ case PERFECTION1270:
+ case PERFECTION1670:
+ case PERFECTION2480:
+ case PERFECTION3490:
+ /* Some epson scanners need the gamma table twice */
+ status = send (pss, dtc, dtcq);
+ CHECK_STATUS (status, me, "2nd send");
+ break;
+ case PRISA5150:
+ /* 5150 needs the gamma table twice, with dtc = 0x04 for the second one */
+ status = send (pss, DTC_GAMMA2, dtcq);
+ CHECK_STATUS (status, me, "2nd send");
+ break;
+ default:
+ break;
+ }
+ return status;
+}
+
+static SANE_Status download_gamma_tables (SnapScan_Scanner *pss)
+{
+ static char me[] = "download_gamma_tables";
+ SANE_Status status = SANE_STATUS_GOOD;
+ double gamma_gs = SANE_UNFIX (pss->gamma_gs);
+ double gamma_r = SANE_UNFIX (pss->gamma_r);
+ double gamma_g = SANE_UNFIX (pss->gamma_g);
+ double gamma_b = SANE_UNFIX (pss->gamma_b);
+ SnapScan_Mode mode = actual_mode (pss);
+ int dtcq_gamma_gray;
+ int dtcq_gamma_red;
+ int dtcq_gamma_green;
+ int dtcq_gamma_blue;
+ int gamma_mode = GAMMA_8BIT;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ switch (mode)
+ {
+ case MD_COLOUR:
+ break;
+ case MD_BILEVELCOLOUR:
+ if (!pss->halftone)
+ {
+ gamma_r =
+ gamma_g =
+ gamma_b = 1.0;
+ }
+ break;
+ case MD_LINEART:
+ if (!pss->halftone)
+ gamma_gs = 1.0;
+ break;
+ default:
+ /* no further action for greyscale */
+ break;
+ }
+
+ switch (pss->bpp)
+ {
+ case 10:
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY10;
+ dtcq_gamma_red = DTCQ_GAMMA_RED10;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN10;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE10;
+ break;
+ case 12:
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 16bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY12_16BIT;
+ dtcq_gamma_red = DTCQ_GAMMA_RED12_16BIT;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN12_16BIT;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE12_16BIT;
+ gamma_mode = GAMMA_12_16BIT;
+ }
+ else
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY12;
+ dtcq_gamma_red = DTCQ_GAMMA_RED12;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN12;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE12;
+ }
+ break;
+ case 14:
+ if (pss->bpp_scan == 16)
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 16bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY14_16BIT;
+ dtcq_gamma_red = DTCQ_GAMMA_RED14_16BIT;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN14_16BIT;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE14_16BIT;
+ gamma_mode = GAMMA_16BIT;
+ }
+ else
+ {
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY14;
+ dtcq_gamma_red = DTCQ_GAMMA_RED14;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN14;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE14;
+ }
+ break;
+ default:
+ DBG (DL_DATA_TRACE, "%s: Sending 8bit gamma table for %d bpp\n", me, pss->bpp);
+ dtcq_gamma_gray = DTCQ_GAMMA_GRAY8;
+ dtcq_gamma_red = DTCQ_GAMMA_RED8;
+ dtcq_gamma_green = DTCQ_GAMMA_GREEN8;
+ dtcq_gamma_blue = DTCQ_GAMMA_BLUE8;
+ break;
+ }
+
+ if (is_colour_mode(mode))
+ {
+ if (pss->val[OPT_CUSTOM_GAMMA].b)
+ {
+ if (pss->val[OPT_GAMMA_BIND].b)
+ {
+ /* Use greyscale gamma for all rgb channels */
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ else
+ {
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_r,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_g,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_b,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ }
+ else
+ {
+ if (pss->val[OPT_GAMMA_BIND].b)
+ {
+ /* Use greyscale gamma for all rgb channels */
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ else
+ {
+ gamma_n (gamma_r, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_red);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_g, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_green);
+ CHECK_STATUS (status, me, "send");
+
+ gamma_n (gamma_b, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_blue);
+ CHECK_STATUS (status, me, "send");
+ }
+ }
+ }
+ else
+ {
+ if(pss->val[OPT_CUSTOM_GAMMA].b)
+ {
+ gamma_from_sane (pss->gamma_length, pss->gamma_table_gs,
+ pss->buf + SEND_LENGTH, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_gray);
+ CHECK_STATUS (status, me, "send");
+ }
+ else
+ {
+ gamma_n (gamma_gs, pss->bright, pss->contrast,
+ pss->buf + SEND_LENGTH, pss->bpp, gamma_mode);
+ status = send_gamma_table(pss, DTC_GAMMA, dtcq_gamma_gray);
+ CHECK_STATUS (status, me, "send");
+ }
+ }
+ return status;
+}
+
+static SANE_Status download_halftone_matrices (SnapScan_Scanner *pss)
+{
+ static char me[] = "download_halftone_matrices";
+ SANE_Status status = SANE_STATUS_GOOD;
+ if ((pss->halftone) &&
+ ((actual_mode(pss) == MD_LINEART) || (actual_mode(pss) == MD_BILEVELCOLOUR)))
+ {
+ u_char *matrix;
+ size_t matrix_sz;
+ u_char dtcq;
+
+ if (pss->dither_matrix == dm_dd8x8)
+ {
+ matrix = D8;
+ matrix_sz = sizeof (D8);
+ }
+ else
+ {
+ matrix = D16;
+ matrix_sz = sizeof (D16);
+ }
+
+ memcpy (pss->buf + SEND_LENGTH, matrix, matrix_sz);
+
+ if (is_colour_mode(actual_mode(pss)))
+ {
+ if (matrix_sz == sizeof (D8))
+ dtcq = DTCQ_HALFTONE_COLOR8;
+ else
+ dtcq = DTCQ_HALFTONE_COLOR16;
+
+ /* need copies for green and blue bands */
+ memcpy (pss->buf + SEND_LENGTH + matrix_sz,
+ matrix,
+ matrix_sz);
+ memcpy (pss->buf + SEND_LENGTH + 2 * matrix_sz,
+ matrix,
+ matrix_sz);
+ }
+ else
+ {
+ if (matrix_sz == sizeof (D8))
+ dtcq = DTCQ_HALFTONE_BW8;
+ else
+ dtcq = DTCQ_HALFTONE_BW16;
+ }
+
+ status = send (pss, DTC_HALFTONE, dtcq);
+ CHECK_STATUS (status, me, "send");
+ }
+ return status;
+}
+
+static SANE_Status measure_transfer_rate (SnapScan_Scanner *pss)
+{
+ static char me[] = "measure_transfer_rate";
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (pss->hconfig & HCFG_RB)
+ {
+ /* We have a ring buffer. We simulate one round of a read-store
+ cycle on the size of buffer we will be using. For this read only,
+ the buffer size must be rounded to a 128-byte boundary. */
+
+ DBG (DL_VERBOSE, "%s: have ring buffer\n", me);
+ if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
+ {
+ /* Epson 2480: read a multiple of bytes per line, limit to less than 0xfff0 */
+ if (pss->bytes_per_line > 0xfff0)
+ pss->expected_read_bytes = 0xfff0;
+ else
+ pss->expected_read_bytes = (0xfff0 / pss->bytes_per_line) * pss->bytes_per_line;
+ }
+ else
+ pss->expected_read_bytes =
+ (pss->buf_sz%128) ? (pss->buf_sz/128 + 1)*128 : pss->buf_sz;
+
+ status = scsi_read (pss, READ_TRANSTIME);
+ CHECK_STATUS (status, me, "scsi_read");
+ pss->expected_read_bytes = 0;
+ status = scsi_read (pss, READ_TRANSTIME);
+ CHECK_STATUS (status, me, "scsi_read");
+ }
+ else
+ {
+ /* we don't have a ring buffer. The test requires transferring one
+ scan line of data (rounded up to next 128 byte boundary). */
+
+ DBG (DL_VERBOSE, "%s: we don't have a ring buffer.\n", me);
+ pss->expected_read_bytes = pss->bytes_per_line;
+
+ if (pss->expected_read_bytes%128)
+ {
+ pss->expected_read_bytes =
+ (pss->expected_read_bytes/128 + 1)*128;
+ }
+ status = scsi_read (pss, READ_TRANSTIME);
+ CHECK_STATUS (status, me, "scsi_read");
+ DBG (DL_VERBOSE, "%s: read %ld bytes.\n", me, (long) pss->read_bytes);
+ }
+
+ pss->expected_read_bytes = 0;
+ status = scsi_read (pss, READ_TRANSTIME);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: test read failed.\n", me);
+ return status;
+ }
+
+ DBG (DL_VERBOSE, "%s: successfully calibrated transfer rate.\n", me);
+ return status;
+}
+
+
+SANE_Status sane_start (SANE_Handle h)
+{
+ static const char *me = "sane_snapscan_start";
+ SANE_Status status;
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+
+ DBG (DL_CALL_TRACE, "%s (%p)\n", me, (void *) h);
+
+ /* possible authorization required */
+
+ status = open_scanner (pss);
+ CHECK_STATUS (status, me, "open_scanner");
+
+ status = wait_scanner_ready (pss);
+ CHECK_STATUS (status, me, "wait_scanner_ready");
+
+ /* start scanning; reserve the unit first, because a release_unit is
+ necessary to abort a scan in progress */
+
+ pss->state = ST_SCAN_INIT;
+
+ if ((pss->pdev->model == SCANWIT2720S) && (pss->focus_mode == MD_AUTO))
+ {
+ status = get_focus(pss);
+ CHECK_STATUS (status, me, "get_focus");
+ }
+
+ reserve_unit(pss);
+
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ status = set_frame(pss, 0);
+ CHECK_STATUS (status, me, "set_frame");
+ status = set_focus(pss, pss->focus);
+ CHECK_STATUS (status, me, "set_focus");
+ }
+
+ /* set up the window and fetch the resulting scanner parameters */
+ status = set_window(pss);
+ CHECK_STATUS (status, me, "set_window");
+
+ status = inquiry(pss);
+ CHECK_STATUS (status, me, "inquiry");
+
+ /* download the gamma and halftone tables */
+
+ status = download_gamma_tables(pss);
+ CHECK_STATUS (status, me, "download_gamma_tables");
+
+ status = download_halftone_matrices(pss);
+ CHECK_STATUS (status, me, "download_halftone_matrices");
+
+ if (pss->val[OPT_QUALITY_CAL].b && (pss->usb_vendor == USB_VENDOR_EPSON))
+ {
+ status = calibrate(pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: calibration failed.\n", me);
+ release_unit (pss);
+ return status;
+ }
+ }
+
+ /* we must measure the data transfer rate between the host and the
+ scanner, and the method varies depending on whether there is a
+ ring buffer or not. */
+
+ status = measure_transfer_rate(pss);
+ CHECK_STATUS (status, me, "measure_transfer_rate");
+
+ /* now perform an inquiry again to retrieve the scan speed */
+ status = inquiry(pss);
+ CHECK_STATUS (status, me, "inquiry");
+
+ DBG (DL_DATA_TRACE,
+ "%s: after measuring speed:\n\t%lu bytes per scan line\n"
+ "\t%f milliseconds per scan line.\n\t==>%f bytes per millisecond\n",
+ me,
+ (u_long) pss->bytes_per_line,
+ pss->ms_per_line,
+ pss->bytes_per_line/pss->ms_per_line);
+
+
+ if (pss->val[OPT_QUALITY_CAL].b && (pss->usb_vendor != USB_VENDOR_EPSON))
+ {
+ status = calibrate(pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: calibration failed.\n", me);
+ release_unit (pss);
+ return status;
+ }
+ }
+
+ status = scan(pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: scan command failed: %s.\n", me, sane_strstatus(status));
+ release_unit (pss);
+ return status;
+ }
+
+ if (pss->pdev->model == SCANWIT2720S)
+ {
+ status = set_frame(pss, pss->frame_no);
+ CHECK_STATUS (status, me, "set_frame");
+ }
+
+ if (pss->source == SRC_ADF)
+ {
+ /* Wait for scanner ready again (e.g. until paper is loaded from an ADF) */
+ /* Maybe replace with get_data_buffer_status()? */
+ status = wait_scanner_ready (pss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: scan command failed while waiting for scanner: %s.\n", me, sane_strstatus(status));
+ release_unit (pss);
+ return status;
+ }
+ }
+
+ DBG (DL_MINOR_INFO, "%s: starting the reader process.\n", me);
+ status = start_reader(pss);
+ {
+ BaseSourceType st = FD_SRC;
+ if (status != SANE_STATUS_GOOD)
+ st = SCSI_SRC;
+ status = create_source_chain (pss, st, &(pss->psrc));
+ }
+
+ return status;
+}
+
+
+SANE_Status sane_read (SANE_Handle h,
+ SANE_Byte *buf,
+ SANE_Int maxlen,
+ SANE_Int *plen)
+{
+ static const char *me = "sane_snapscan_read";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DL_CALL_TRACE,
+ "%s (%p, %p, %ld, %p)\n",
+ me,
+ (void *) h,
+ (void *) buf,
+ (long) maxlen,
+ (void *) plen);
+
+ *plen = 0;
+
+ if (pss->state == ST_CANCEL_INIT) {
+ pss->state = ST_IDLE;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (pss->psrc == NULL || pss->psrc->remaining(pss->psrc) == 0)
+ {
+ if (pss->child != -1)
+ {
+ sanei_thread_waitpid (pss->child, 0); /* ensure no zombies */
+ pss->child = -1;
+ }
+ release_unit (pss);
+ close_scanner (pss);
+ if (pss->psrc != NULL)
+ {
+ pss->psrc->done(pss->psrc);
+ free(pss->psrc);
+ pss->psrc = NULL;
+ }
+ pss->state = ST_IDLE;
+ return SANE_STATUS_EOF;
+ }
+
+ *plen = maxlen;
+ status = pss->psrc->get(pss->psrc, buf, plen);
+
+ switch (pss->state)
+ {
+ case ST_IDLE:
+ DBG (DL_MAJOR_ERROR,
+ "%s: weird error: scanner state should not be idle on call to "
+ "sane_read.\n",
+ me);
+ break;
+ case ST_SCAN_INIT:
+ /* we've read some data */
+ pss->state = ST_SCANNING;
+ break;
+ case ST_CANCEL_INIT:
+ /* stop scanning */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+void sane_cancel (SANE_Handle h)
+{
+ char *me = "sane_snapscan_cancel";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ struct SIGACTION act;
+ SANE_Pid res;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ switch (pss->state)
+ {
+ case ST_IDLE:
+ break;
+ case ST_SCAN_INIT:
+ case ST_SCANNING:
+ /* signal a cancellation has occurred */
+ pss->state = ST_CANCEL_INIT;
+ /* signal the reader, if any */
+ if (pss->child != -1)
+ {
+ DBG( DL_INFO, ">>>>>>>> killing reader_process <<<<<<<<\n" );
+
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = sigalarm_handler;
+ sigaction( SIGALRM, &act, 0 );
+
+ if (sanei_thread_is_forked())
+ {
+ /* use SIGUSR1 to set cancelRead in child process */
+ sanei_thread_sendsig( pss->child, SIGUSR1 );
+ }
+ else
+ {
+ cancelRead = SANE_TRUE;
+ }
+
+ /* give'em 10 seconds 'til done...*/
+ alarm(10);
+ res = sanei_thread_waitpid( pss->child, 0 );
+ alarm(0);
+
+ if( res != pss->child ) {
+ DBG( DL_MINOR_ERROR,"sanei_thread_waitpid() failed !\n");
+
+ /* do it the hard way...*/
+#ifdef USE_PTHREAD
+ sanei_thread_kill( pss->child );
+#else
+ sanei_thread_sendsig( pss->child, SIGKILL );
+#endif
+ }
+ pss->child = -1;
+ DBG( DL_INFO,"reader_process killed\n");
+ }
+ release_unit (pss);
+ close_scanner (pss);
+ break;
+ case ST_CANCEL_INIT:
+ DBG (DL_INFO, "%s: cancellation already initiated.\n", me);
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR,
+ "%s: weird error: invalid scanner state (%ld).\n",
+ me,
+ (long) pss->state);
+ break;
+ }
+}
+
+SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m)
+{
+ static char me[] = "sane_snapscan_set_io_mode";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+ char *op;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ if (pss->state != ST_SCAN_INIT)
+ return SANE_STATUS_INVAL;
+
+ if (m)
+ {
+ if (pss->child == -1)
+ {
+ DBG (DL_MINOR_INFO,
+ "%s: no reader child; must use blocking mode.\n",
+ me);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ op = "ON";
+ fcntl (pss->rpipe[0], F_SETFL, O_NONBLOCK | pss->orig_rpipe_flags);
+ }
+ else
+ {
+ op = "OFF";
+ fcntl (pss->rpipe[0], F_SETFL, pss->orig_rpipe_flags);
+ }
+ DBG (DL_MINOR_INFO, "%s: turning nonblocking mode %s.\n", me, op);
+ pss->nonblocking = m;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
+{
+ static char me[] = "sane_snapscan_get_select_fd";
+ SnapScan_Scanner *pss = (SnapScan_Scanner *) h;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+
+ if (pss->state != ST_SCAN_INIT)
+ return SANE_STATUS_INVAL;
+
+ if (pss->child == -1)
+ {
+ DBG (DL_MINOR_INFO,
+ "%s: no reader child; cannot provide select file descriptor.\n",
+ me);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ *fd = pss->rpipe[0];
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * $Log$
+ * Revision 1.73 2008/11/26 21:21:29 kitno-guest
+ * * backend/ *.[ch]: nearly every backend used V_MAJOR
+ * instead of SANE_CURRENT_MAJOR in sane_init()
+ * * backend/snapscan.c: remove EXPECTED_VERSION check
+ * since new SANE standard is forward compatible
+ *
+ * Revision 1.72 2008-05-15 12:50:24 ellert-guest
+ * Fix for bug #306751: sanei-thread with pthreads on 64 bit
+ *
+ * Revision 1.71 2008-01-29 17:48:42 kitno-guest
+ * fix snapscan bug, add LiDE 600F
+ *
+ * Revision 1.70 2007-11-18 10:59:18 ellert-guest
+ * Fix handling of valid "negative" PIDs
+ *
+ * Revision 1.69 2007-11-16 08:04:02 ellert-guest
+ * Correct the test of the return value from sanei_thread_begin
+ *
+ * Revision 1.68 2006-09-03 10:00:11 oliver-guest
+ * Bugfix for firmware download by Paul Smedley
+ *
+ * Revision 1.67 2006/01/10 19:32:16 oliver-guest
+ * Added 12 bit gamma tables for Epson Stylus CX-1500
+ *
+ * Revision 1.66 2006/01/06 20:59:17 oliver-guest
+ * Some fixes for the Epson Stylus CX 1500
+ *
+ * Revision 1.65 2006/01/01 23:02:55 oliver-guest
+ * Added snapscan-data.c to Makefile.in
+ *
+ * Revision 1.64 2005/12/05 20:38:23 oliver-guest
+ * Small bugfix for Benq 5150
+ *
+ * Revision 1.63 2005/12/04 15:03:00 oliver-guest
+ * Some fixes for Benq 5150
+ *
+ * Revision 1.62 2005/12/02 19:15:42 oliver-guest
+ * Change SnapScan version number to 1.4.50
+ *
+ * Revision 1.61 2005/11/15 20:11:19 oliver-guest
+ * Enabled quality calibration for the Epson 3490
+ *
+ * Revision 1.60 2005/11/10 19:42:02 oliver-guest
+ * Added deinterlacing for Epson 3490
+ *
+ * Revision 1.59 2005/11/02 22:12:54 oliver-guest
+ * Correct cut'n'paste error
+ *
+ * Revision 1.58 2005/11/02 19:22:06 oliver-guest
+ * Fixes for Benq 5000
+ *
+ * Revision 1.57 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.56 2005/10/24 19:46:40 oliver-guest
+ * Preview and range fix for Epson 2480/2580
+ *
+ * Revision 1.55 2005/10/23 21:28:58 oliver-guest
+ * Fix for buffer size in high res modes, fixes for delay code
+ *
+ * Revision 1.54 2005/10/13 22:43:30 oliver-guest
+ * Fixes for 16 bit scan mode from Simon Munton
+ *
+ * Revision 1.53 2005/10/11 18:47:07 oliver-guest
+ * Fixes for Epson 3490 and 16 bit scan mode
+ *
+ * Revision 1.52 2005/09/28 21:33:11 oliver-guest
+ * Added 16 bit option for Epson scanners (untested)
+ *
+ * Revision 1.51 2005/08/15 18:56:55 oliver-guest
+ * Added temporary debug code for 2480/2580 distinction
+ *
+ * Revision 1.50 2005/08/15 18:06:37 oliver-guest
+ * Added support for Epson 3490/3590 (thanks to Matt Judge)
+ *
+ * Revision 1.49 2005/08/07 12:37:29 oliver-guest
+ * Use first known device if no device is specified
+ *
+ * Revision 1.48 2004/12/09 23:21:48 oliver-guest
+ * Added quality calibration for Epson 2480 (by Simon Munton)
+ *
+ * Revision 1.47 2004/12/01 22:49:14 oliver-guest
+ * Fix for allocation of gamma tables by Simon Munton
+ *
+ * Revision 1.46 2004/12/01 22:12:03 oliver-guest
+ * Added support for Epson 1270
+ *
+ * Revision 1.45 2004/10/03 17:34:36 hmg-guest
+ * 64 bit platform fixes (bug #300799).
+ *
+ * Revision 1.44 2004/09/02 20:59:12 oliver-guest
+ * Added support for Epson 2480
+ *
+ * Revision 1.43 2004/06/16 19:52:26 oliver-guest
+ * Don't enforce even number of URB packages on 1212u_2. Fixes bug #300753.
+ *
+ * Revision 1.42 2004/06/15 12:17:37 hmg-guest
+ * Only use __attribute__ if gcc is used for compilation. Some other compilers
+ * don't know __attribute__ and therefore can't compile sane-backends without this
+ * fix. See bug #300803.
+ *
+ * Revision 1.41 2004/05/26 22:37:01 oliver-guest
+ * Use shared memory for urb counters in snapscan backend
+ *
+ * Revision 1.40 2004/04/09 11:59:02 oliver-guest
+ * Fixes for pthread implementation
+ *
+ * Revision 1.39 2004/04/08 21:53:10 oliver-guest
+ * Use sanei_thread in snapscan backend
+ *
+ * Revision 1.37 2003/11/27 23:11:32 oliver-guest
+ * Send gamma table twice for Epson Perfection 1670
+ *
+ * Revision 1.36 2003/11/09 21:43:45 oliver-guest
+ * Disabled quality calibration for Epson Perfection 1670
+ *
+ * Revision 1.35 2003/11/07 23:26:49 oliver-guest
+ * Final bugfixes for bascic support of Epson 1670
+ *
+ * Revision 1.34 2003/10/21 20:43:25 oliver-guest
+ * Bugfixes for SnapScan backend
+ *
+ * Revision 1.33 2003/10/07 18:29:20 oliver-guest
+ * Initial support for Epson 1670, minor bugfix
+ *
+ * Revision 1.32 2003/09/24 18:05:39 oliver-guest
+ * Bug #300198: Check second argument of sanei_config_get_string
+ *
+ * Revision 1.31 2003/09/12 16:10:33 hmg-guest
+ * Moved union Option_Value from backend header files to sanei_backend.h. No need
+ * to copy it over and over again. Changed header inclusion order in backend
+ * files to include backend.h after sanei_backend.h. Based on a patch from stef
+ * <stef-listes@wanadoo.fr>.
+ *
+ * Revision 1.30 2003/08/19 21:05:08 oliverschwartz
+ * Scanner ID cleanup
+ *
+ * Revision 1.29 2003/04/30 20:49:40 oliverschwartz
+ * SnapScan backend 1.4.26
+ *
+ * Revision 1.58 2003/04/30 20:43:07 oliverschwartz
+ * Set backend version number to 1.4.26
+ *
+ * Revision 1.57 2003/04/02 21:17:14 oliverschwartz
+ * Fix for 1200 DPI with Acer 5000
+ *
+ * Revision 1.56 2003/02/08 10:45:09 oliverschwartz
+ * Use 600 DPI as optical resolution for Benq 5000
+ *
+ * Revision 1.55 2003/01/08 21:16:17 oliverschwartz
+ * Added support for Acer / Benq 310U
+ *
+ * Revision 1.54 2002/12/10 20:14:12 oliverschwartz
+ * Enable color offset correction for SnapScan300
+ *
+ * Revision 1.53 2002/10/31 19:29:41 oliverschwartz
+ * Set version to 1.4.17
+ *
+ * Revision 1.52 2002/10/12 10:40:48 oliverschwartz
+ * Added support for Snapscan e10
+ *
+ * Revision 1.51 2002/09/26 19:27:44 oliverschwartz
+ * Version 1.4.16
+ *
+ * Revision 1.50 2002/09/24 16:07:44 oliverschwartz
+ * Added support for Benq 5000
+ *
+ * Revision 1.49 2002/07/12 22:53:54 oliverschwartz
+ * Version 1.4.15
+ *
+ * Revision 1.48 2002/07/12 22:53:16 oliverschwartz
+ * call sanei_usb_init() before sanei_usb_attach_matching_devices()
+ *
+ * Revision 1.47 2002/06/06 21:16:23 oliverschwartz
+ * Set backend version to 1.4.14
+ *
+ * Revision 1.46 2002/06/06 20:40:01 oliverschwartz
+ * Changed default scan area for transparancy unit of SnapScan e50
+ *
+ * Revision 1.45 2002/05/02 18:29:34 oliverschwartz
+ * - Added ADF support
+ * - Fixed status handling after cancel
+ *
+ * Revision 1.44 2002/04/27 14:42:30 oliverschwartz
+ * Cleanup of debug logging
+ *
+ * Revision 1.43 2002/04/23 22:40:33 oliverschwartz
+ * Improve config file reading
+ *
+ * Revision 1.42 2002/04/10 21:00:09 oliverschwartz
+ * Check for NULL pointer before deleting device list
+ *
+ * Revision 1.41 2002/03/24 12:12:36 oliverschwartz
+ * - Moved option functions to snapscan-options.c
+ * - Autodetect USB scanners
+ * - Cleanup
+ *
+ * Revision 1.40 2002/02/09 14:55:23 oliverschwartz
+ * Added language translation support (SANE_I18N)
+ *
+ * Revision 1.39 2002/01/23 20:40:54 oliverschwartz
+ * Don't use quantization for scan area parameter
+ * Improve recognition of Acer 320U
+ * Version 1.4.7
+ *
+ * Revision 1.38 2002/01/14 21:11:56 oliverschwartz
+ * Add workaround for bug semctl() call in libc for PPC
+ *
+ * Revision 1.37 2002/01/10 21:33:12 oliverschwartz
+ * Set version number to 1.4.4
+ *
+ * Revision 1.36 2002/01/06 18:34:02 oliverschwartz
+ * Added support for Snapscan e42 thanks to Yari Ad� Petralanda
+ *
+ * Revision 1.35 2001/12/20 23:18:01 oliverschwartz
+ * Remove tmpfname
+ *
+ * Revision 1.34 2001/12/18 18:28:35 oliverschwartz
+ * Removed temporary file
+ *
+ * Revision 1.33 2001/12/12 19:43:30 oliverschwartz
+ * - Set version number to 1.4.3
+ * - Clean up CVS Log
+ *
+ * Revision 1.32 2001/12/09 23:06:45 oliverschwartz
+ * - use sense handler for USB if scanner reports CHECK_CONDITION
+ *
+ * Revision 1.31 2001/12/08 11:50:34 oliverschwartz
+ * Fix dither matrix computation
+ *
+ * Revision 1.30 2001/11/29 22:50:14 oliverschwartz
+ * Add support for SnapScan e52
+ *
+ * Revision 1.29 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.28 2001/11/25 18:51:41 oliverschwartz
+ * added support for SnapScan e52 thanks to Rui Lopes
+ *
+ * Revision 1.27 2001/11/16 20:28:35 oliverschwartz
+ * add support for Snapscan e26
+ *
+ * Revision 1.26 2001/11/16 20:23:16 oliverschwartz
+ * Merge with sane-1.0.6
+ * - Check USB vendor IDs to avoid hanging scanners
+ * - fix bug in dither matrix computation
+ *
+ * Revision 1.25 2001/10/25 11:06:22 oliverschwartz
+ * Change snapscan backend version number to 1.4.0
+ *
+ * Revision 1.24 2001/10/11 14:02:10 oliverschwartz
+ * Distinguish between e20/e25 and e40/e50
+ *
+ * Revision 1.23 2001/10/09 22:34:23 oliverschwartz
+ * fix compiler warnings
+ *
+ * Revision 1.22 2001/10/08 19:26:01 oliverschwartz
+ * - Disable quality calibration for scanners that do not support it
+ *
+ * Revision 1.21 2001/10/08 18:22:02 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.20 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.19 2001/09/17 10:01:08 sable
+ * Added model AGFA 1236U
+ *
+ * Revision 1.18 2001/09/10 10:16:32 oliverschwartz
+ * better USB / SCSI recognition, correct max scan area for 1236+TPO
+ *
+ * Revision 1.17 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.16 2001/09/07 09:42:13 oliverschwartz
+ * Sync with Sane-1.0.5
+ *
+ * Revision 1.15 2001/05/15 20:51:14 oliverschwartz
+ * check for pss->devname instead of name in sane_open()
+ *
+ * Revision 1.14 2001/04/10 13:33:06 sable
+ * Transparency adapter bug and xsane crash corrections thanks to Oliver Schwartz
+ *
+ * Revision 1.13 2001/04/10 13:00:31 sable
+ * Moving sanei_usb_* to snapscani_usb*
+ *
+ * Revision 1.12 2001/04/10 11:04:31 sable
+ * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
+ *
+ * Revision 1.11 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.4 2001/03/04 16:50:53 mikael
+ * Added Scan Mode, Geometry, Enhancement and Advanced groups. Implemented brightness and contrast controls with gamma tables. Added Quality Calibration, Analog Gamma Bind, Custom Gamma and Gamma Vector GS,R,G,B options.
+ *
+ * Revision 1.3 2001/02/16 18:32:28 mikael
+ * impl calibration, signed position, increased buffer size
+ *
+ * Revision 1.2 2001/02/10 18:18:29 mikael
+ * Extended x and y ranges
+ *
+ * Revision 1.1.1.1 2001/02/10 17:09:29 mikael
+ * Imported from snapscan-11282000.tar.gz
+ *
+ * Revision 1.10 2000/11/10 01:01:59 sable
+ * USB (kind of) autodetection
+ *
+ * Revision 1.9 2000/11/01 01:26:43 sable
+ * Support for 1212U
+ *
+ * Revision 1.8 2000/10/30 22:31:13 sable
+ * Auto preview mode
+ *
+ * Revision 1.7 2000/10/28 14:16:10 sable
+ * Bug correction for SnapScan310
+ *
+ * Revision 1.6 2000/10/28 14:06:35 sable
+ * Add support for Acer300f
+ *
+ * Revision 1.5 2000/10/15 19:52:06 cbagwell
+ * Changed USB support to a 1 line modification instead of multi-file
+ * changes.
+ *
+ * Revision 1.4 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ *
+ * Revision 1.3 2000/08/12 15:09:35 pere
+ * Merge devel (v1.0.3) into head branch.
+ *
+ * Revision 1.1.1.1.2.5 2000/07/29 16:04:33 hmg
+ * 2000-07-29 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/GUIDE: Added some comments about portability and
+ * documentation.
+ * * backend/abaton.c backend/agfafocus.c backend/apple.c
+ * backend/canon.c backend/coolscan.c backend/dc210.c backend/dc25.c
+ * backend/dll.c backend/dmc.c backend/microtek.c backend/microtek2.c
+ * backend/microtek2.c backend/mustek_pp.c backend/net.c backend/pint.c
+ * backend/pnm.c backend/qcam.c backend/ricoh.c backend/s9036.c
+ * backend/sane_strstatus.c backend/sharp.c backend/snapscan.c
+ * backend/st400.c backend/stubs.c backend/tamarack.c backend/v4l.c:
+ * Changed include statements from #include <sane/...> to
+ * #include "sane...".
+ * * backend/avision.c backend/dc25.c: Use DBG(0, ...) instead of
+ * fprintf (stderr, ...)
+ * * backend/avision.c backend/canon-sane.c backend/coolscan.c
+ * backend/dc25.c backend/microtek.c backend/microtek2.c
+ * backend/st400.c: Use sanei_config_read() instead of fgets().
+ * * backend/coolscan.desc backend/microtek.desc backend/microtek2.desc
+ * backend/st400.desc: Added :interface and :manpage entries.
+ * * backend/nec.desc: Status is beta now (was: new). Fixed typo.
+ * * doc/canon.README: Removed, because the information is included in
+ * the manpage now.
+ * * doc/Makefile.in: Added sane-coolscan to list of mapages to install.
+ * * README: Added Link to coolscan manpage.
+ * * backend/mustek.*: Update to Mustek backend 1.0-94. Fixed the
+ * #include <sane/...> bug.
+ *
+ * Revision 1.1.1.1.2.4 2000/07/25 21:47:43 hmg
+ * 2000-07-25 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/snapscan.c: Use DBG(0, ...) instead of fprintf (stderr, ...).
+ * * backend/abaton.c backend/agfafocus.c backend/apple.c backend/dc210.c
+ * backend/dll.c backend/dmc.c backend/microtek2.c backend/pint.c
+ * backend/qcam.c backend/ricoh.c backend/s9036.c backend/snapscan.c
+ * backend/tamarack.c: Use sanei_config_read instead of fgets.
+ * * backend/dc210.c backend/microtek.c backend/pnm.c: Added
+ * #include "../include/sane/config.h".
+ * * backend/dc25.c backend/m3096.c backend/sp15.c
+ * backend/st400.c: Moved #include "../include/sane/config.h" to the beginning.
+ * * AUTHORS: Changed agfa to agfafocus.
+ *
+ * Revision 1.1.1.1.2.3 2000/07/17 21:37:28 hmg
+ * 2000-07-17 Henning Meier-Geinitz <hmg@gmx.de>
+ *
+ * * backend/snapscan.c backend/snapscan-scsi.c: Replace C++ comment
+ * with C comment.
+ *
+ * Revision 1.1.1.1.2.2 2000/07/13 04:47:46 pere
+ * New snapscan backend version dated 20000514 from Steve Underwood.
+ *
+ * Revision 1.2 2000/05/14 13:30:20 coppice
+ * R, G and B images now merge correctly. Still some outstanding issues,
+ * but a lot more useful than before.
+ *
+ * Revision 1.2 2000/03/05 13:55:20 pere
+ * Merged main branch with current DEVEL_1_9.
+ *
+ * Revision 1.1.1.1.2.1 1999/09/15 18:20:44 charter
+ * Early version 1.0 snapscan.c
+ *
+ * Revision 2.2 1999/09/09 18:22:45 charter
+ * Checkpoint. Now using Sources for scanner data, and have removed
+ * references to the old snapscan-310.c stuff. This stuff must still
+ * be incorporated into the RGBRouter to get trilinear CCD SnapScan
+ * models working.
+ *
+ * Revision 2.1 1999/09/08 03:07:05 charter
+ * Start of branch 2; same as 1.47.
+ *
+ * Revision 1.47 1999/09/08 03:03:53 charter
+ * The actions for the scanner command options now use fprintf for
+ * printing, rather than DGB. I want the output to come out no matter
+ * what the value of the snapscan debug level.
+ *
+ * Revision 1.46 1999/09/07 20:53:41 charter
+ * Changed expected_data_len to bytes_remaining.
+ *
+ * Revision 1.45 1999/09/06 23:32:37 charter
+ * Split up sane_start() into sub-functions to improve readability (again).
+ * Introduced actual_mode() and is_colour_mode() (again).
+ * Fixed problems with cancellation. Works fine with my system now.
+ *
+ * Revision 1.44 1999/09/02 05:28:01 charter
+ * Added Gary Plewa's name to the list of copyrighted contributors.
+ *
+ * Revision 1.43 1999/09/02 05:23:54 charter
+ * Added Gary Plewa's patch for the Acer PRISA 620s.
+ *
+ * Revision 1.42 1999/09/02 02:05:34 charter
+ * Check-in of revision 1.42 (release 0.7 of the backend).
+ * This is part of the recovery from the great disk crash of Sept 1, 1999.
+ *
+ * Revision 1.42 1999/07/09 22:37:55 charter
+ * Potential bugfix for problems with sane_get_parameters() and
+ * the new generic scsi driver (suggested by Francois Desarmeni,
+ * Douglas Gilbert, Abel Deuring).
+ *
+ * Revision 1.41 1999/07/09 20:58:07 charter
+ * Changes to support SnapScan 1236s (Petter Reinholdsten).
+ *
+ * Revision 1.40 1998/12/16 18:43:06 charter
+ * Fixed major version problem precipitated by release of SANE-1.00.
+ *
+ * Revision 1.39 1998/09/07 06:09:26 charter
+ * Formatting (whitespace) changes.
+ *
+ * Revision 1.38 1998/09/07 06:06:01 charter
+ * Merged in Wolfgang Goeller's changes (Vuego 310S, bugfixes).
+ *
+ * Revision 1.37 1998/08/06 06:16:39 charter
+ * Now using sane_config_attach_matching_devices() in sane_snapscan_init().
+ * Change contributed by David Mosberger-Tang.
+ *
+ * Revision 1.36 1998/05/11 17:02:53 charter
+ * Added Mikko's threshold stuff.
+ *
+ * Revision 1.35 1998/03/10 23:43:23 eblot
+ * Bug correction
+ *
+ * Revision 0.72 1998/03/10 23:40:42 eblot
+ * More support for 310/600 models: color preview, large window
+ *
+ * Revision 1.35 1998/03/10 21:32:07 eblot
+ * Debugging
+ *
+ * Revision 1.34 1998/02/15 21:55:53 charter
+ * From Emmanuel Blot:
+ * First routines to support SnapScan 310 scanned data.
+ *
+ * Revision 1.33 1998/02/06 02:30:28 charter
+ * Now using a mode enum (instead of the static string pointers directly).
+ * Now check for the SnapScan 310 and 600 explicitly (start of support
+ * for these models).
+ *
+ * Revision 1.32 1998/02/01 21:56:48 charter
+ * Patches to fix compilation problems on Solaris supplied by
+ * Jim McBeath.
+ *
+ * Revision 1.31 1998/02/01 03:36:40 charter
+ * Now check for BRX < TLX and BRY < TLY and whether the area of the
+ * scanning window is approaching zero in set_window. I'm setting a
+ * minimum window size of 75x75 hardware pixels (0.25 inches a side).
+ * If the area falls to zero, the scanner seems to hang in the middle
+ * of the set_window command.
+ *
+ * Revision 1.30 1998/02/01 00:00:33 charter
+ * TLX, TLY, BRX and BRY are now lengths expressed in mm. The frontends
+ * can now allow changes in the units, and units that are more user-
+ * friendly.
+ *
+ * Revision 1.29 1998/01/31 21:09:19 charter
+ * Fixed another problem with add_device(): if mini_inquiry ends
+ * up indirectly invoking the sense handler, there'll be a segfault
+ * because the sense_handler isn't set. Had to fix sense_handler so
+ * it can handle a NULL pss pointer and then use the sanei_scsi stuff
+ * everywhere. This error is most likely to occur if the scanner is
+ * turned off.
+ *
+ * Revision 1.28 1998/01/31 18:45:22 charter
+ * Last fix botched, produced a compile error. Thought I'd already
+ * compiled successfully.
+ *
+ * Revision 1.27 1998/01/31 18:32:42 charter
+ * Fixed stupid bug in add_device that causes segfault when no snapscan
+ * found: closing a scsi fd opened with open() using sanei_scsi_close().
+ *
+ * Revision 1.26 1998/01/30 21:19:02 charter
+ * sane_snapscan_init() handles failure of add_device() in the same
+ * way when there is no snapscan.conf file available as when there is
+ * one.
+ *
+ * Revision 1.25 1998/01/30 19:41:11 charter
+ * Waiting for child process termination at regular end of scan (not
+ * just on cancellation); before I was getting zombies.
+ *
+ * Revision 1.24 1998/01/30 19:19:27 charter
+ * Changed from strncmp() to strncasecmp() to do vendor and model
+ * comparisons in sane_snapscan_init. There are some snapcsan models
+ * that use lower case.
+ * Now have debug level defines instead of raw numbers, and better debug
+ * information categories.
+ * Don't complain at debug level 0 when a snapscan isn't found on a
+ * requested device.
+ * Changed CHECK_STATUS to take caller parameter instead of always
+ * assuming an available string "me".
+ *
+ * Revision 1.23 1998/01/30 11:03:04 charter
+ * Fixed * vs [] operator precedence screwup in sane_snapscan_get_devices()
+ * that caused a segfault in scanimage -h.
+ * Fixed problem with not closing the scsi fd between certain commands
+ * that caused scanimage to hang; now using open_scanner() and close_scanner().
+ *
+ * Revision 1.22 1998/01/28 09:02:55 charter
+ * Fixed bug: zero allocation length in request sense command buffer
+ * was preventing sense information from being received. The
+ * backend now correctly waits for the scanner to warm up.
+ * Now using the hardware configuration byte to check whether
+ * both 8x8 and 16x16 halftoning should be made available.
+ *
+ * Revision 1.21 1998/01/25 09:57:57 charter
+ * Added more SCSI command buttons (and a group for them).
+ * Made the output of the Inquiry command a bit nicer.
+ *
+ * Revision 1.20 1998/01/25 08:53:14 charter
+ * Have added bi-level colour mode, with halftones too.
+ * Can now select preview mode (but it's an advanced option, since
+ * you usually don't want to do it).
+ *
+ * Revision 1.19 1998/01/25 02:25:02 charter
+ * Fixed bug: preview mode gives blank image at initial startup.
+ * Fixed bug: lineart mode goes weird after a preview or gs image.
+ * More changes to option relationships;
+ * now using test_unit_ready and send_diagnostic in sane_snapscan_open().
+ * Added negative option.
+ *
+ * Revision 1.18 1998/01/24 05:15:32 charter
+ * Now have RGB gamma correction and dispersed-dot dither halftoning
+ * for BW images. Cleaned up some spots in the code and have set up
+ * option interactions a bit better (e.g. halftoning and GS gamma
+ * correction made inactive in colour mode, etc). TL_[XY] and BR_[XY]
+ * now change in ten-pixel increments (I had problems with screwed-up
+ * scan lines when the dimensions were weird at low res... could be the
+ * problem).
+ *
+ * Revision 1.17 1998/01/23 13:03:17 charter
+ * Several changes, all aimed at getting scanning performance working
+ * correctly, and the progress/cancel window functioning. Cleaned up
+ * a few nasty things as well.
+ *
+ * Revision 1.16 1998/01/23 07:40:23 charter
+ * Reindented using GNU convention at David Mosberger-Tang's request.
+ * Also applied David's patch fixing problems on 64-bit architectures.
+ * Now using scanner's reported speed to guage amount of data to request
+ * in a read on the scsi fd---nonblocking mode operates better now.
+ * Fixed stupid bug I introduced in preview mode data transfer.
+ *
+ * Revision 1.15 1998/01/22 06:18:57 charter
+ * Raised the priority of a couple of DBG messages in reserve_unit()
+ * and release_unit(), and got rid of some unecessary ones.
+ *
+ * Revision 1.14 1998/01/22 05:15:35 charter
+ * Have replaced the bit depth option with a mode option; various
+ * changes associated with that.
+ * Also, I again close the STDERR_FILENO in the reader child and
+ * dup the STDOUT file descriptor onto it. This prevents an "X io"
+ * error when the child exits, while still allowing the use of
+ * DBG.
+ *
+ * Revision 1.13 1998/01/21 20:41:22 charter
+ * Added copyright info.
+ * Also now seem to have cancellation working. This requires using a
+ * new scanner state variable and checking in all the right places
+ * in the reader child and the sane_snapscan_read function. I've
+ * tested it using both blocking and nonblocking I/O and it seems
+ * to work both ways.
+ * I've also switched to GTK+-0.99.2 and sane-0.69, and the
+ * mysterious problems with the preview window have disappeared.
+ * Problems with scanimage doing weird things to options have also
+ * gone away and the frontends seem more stable.
+ *
+ * Revision 1.12 1998/01/21 11:05:53 charter
+ * Inoperative code largely #defined out; I had the preview window
+ * working correctly by having the window coordinates properly
+ * constrained, but now the preview window bombs with a floating-
+ * point error each time... I'm not sure yet what happened.
+ * I've also figured out that we need to use reserve_unit and
+ * release_unit in order to cancel scans in progress. This works
+ * under scanimage, but I can't seem to find a way to fit cancellation
+ * into xscanimage properly.
+ *
+ * Revision 1.11 1998/01/20 22:42:08 charter
+ * Applied Franck's patch from Dec 17; preview mode is now grayscale.
+ *
+ * Revision 1.10 1997/12/10 23:33:12 charter
+ * Slight change to some floating-point computations in the brightness
+ * and contrast stuff. The controls don't seem to do anything to the
+ * scanner though (I think these aren't actually supported in the
+ * SnapScan).
+ *
+ * Revision 1.9 1997/11/26 15:40:50 charter
+ * Brightness and contrast added by Michel.
+ *
+ * Revision 1.8 1997/11/12 12:55:40 charter
+ * No longer exec after forking to do nonblocking scanning; found how
+ * to fix the problems with SIGPIPEs from before.
+ * Now support a config file like the other scanner drivers, and
+ * can check whether a given device is an AGFA SnapScan (mini_inquiry()).
+ *
+ * Revision 1.7 1997/11/10 05:52:08 charter
+ * Now have the child reader process and pipe stuff working, and
+ * nonblocking mode. For large scans the nonblocking mode actually
+ * seems to cut down on cpu hogging (though there is still a hit).
+ *
+ * Revision 1.6 1997/11/03 07:45:54 charter
+ * Added the predef_window stuff. I've tried it with 6x4, and it seems
+ * to work; I think something gets inconsistent if a preview is
+ * performed though.
+ *
+ * Revision 1.5 1997/11/03 03:15:27 charter
+ * Global static variables have now become part of the scanner structure;
+ * the inquiry command automatically retrieves window parameters into
+ * scanner structure members. Things are a bit cleaned up.
+ *
+ * Revision 1.4 1997/11/02 23:35:28 charter
+ * After much grief.... I can finally scan reliably. Now it's a matter
+ * of getting the band arrangement sorted out.
+ *
+ * Revision 1.3 1997/10/30 07:36:37 charter
+ * Fixed a stupid bug in the #defines for the inquiry command, pointed out
+ * by Franck.
+ *
+ * Revision 1.2 1997/10/14 06:00:11 charter
+ * Option manipulation and some basic SCSI commands done; the basics
+ * for scanning are written but there are bugs. A full scan always hangs
+ * the SCSI driver, and preview mode scans complete but it isn't clear
+ * whether any meaningful data is received.
+ *
+ * Revision 1.1 1997/10/13 02:25:54 charter
+ * Initial revision
+ * */