summaryrefslogtreecommitdiff
path: root/backend/coolscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/coolscan.c')
-rw-r--r--backend/coolscan.c4195
1 files changed, 4195 insertions, 0 deletions
diff --git a/backend/coolscan.c b/backend/coolscan.c
new file mode 100644
index 0000000..00a12e4
--- /dev/null
+++ b/backend/coolscan.c
@@ -0,0 +1,4195 @@
+/* ------------------------------------------------------------------------- */
+/* sane - Scanner Access Now Easy.
+ coolscan.c , version 0.4.4
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements a SANE backend for COOLSCAN flatbed scanners. */
+
+/* ------------------------------------------------------------------------- */
+
+
+/* SANE-FLOW-DIAGRAMM
+
+ - sane_init() : initialize backend, attach scanners
+ . - sane_get_devices() : query list of scanner-devices
+ . - sane_open() : open a particular scanner-device
+ . . - sane_set_io_mode : set blocking-mode
+ . . - sane_get_select_fd : get scanner-fd
+ . . - sane_get_option_descriptor() : get option informations
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image aquisition
+ . . - sane_get_parameters() : returns actual scan-parameters
+ . . - sane_read() : read image-data (from pipe)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner-device
+ - sane_exit() : terminate use of backend
+ */
+
+#ifdef _AIX
+# include "lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "lalloca.h"
+
+#include <errno.h>
+#include <math.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_thread.h"
+
+#include "../include/sane/sanei_config.h"
+#define COOLSCAN_CONFIG_FILE "coolscan.conf"
+#include "../include/sane/sanei_backend.h"
+
+#include "coolscan.h"
+#include "coolscan-scsidef.h"
+
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/* ------------------------------------------------------------------------- */
+static const SANE_Int resolution_list[] =
+{
+ 25,
+ 2700, 1350, 900, 675, 540, 450, 385, 337, 300, 270, 245, 225, 207,
+ 192, 180, 168, 158, 150, 142, 135, 128, 122, 117, 112, 108
+};
+
+
+#define coolscan_do_scsi_open(dev, fd, handler) sanei_scsi_open(dev, fd, handler)
+#define coolscan_do_scsi_close(fd) sanei_scsi_close(fd)
+
+#define COOLSCAN_MAX_RETRY 25
+
+
+static SANE_Status sense_handler (int scsi_fd, unsigned char * result, void *arg);
+static int coolscan_check_values (Coolscan_t * s);
+static int get_internal_info (Coolscan_t *);
+static void coolscan_get_inquiry_values (Coolscan_t *);
+static void hexdump (int level, char *comment, unsigned char *p, int l);
+static int swap_res (Coolscan_t * s);
+/* --------------------------- COOLSCAN_DO_SCSI_CMD ----------------------- */
+static int
+do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, unsigned char *out, size_t out_len)
+{
+ int ret;
+ size_t ol = out_len;
+
+ hexdump (20, "", cmd, cmd_len);
+
+ ret = sanei_scsi_cmd (fd, cmd, cmd_len, out, &ol);
+ if ((out_len != 0) && (out_len != ol))
+ {
+ DBG (1, "sanei_scsi_cmd: asked %lu bytes, got %lu\n",
+ (u_long) out_len, (u_long) ol);
+ }
+ if (ret)
+ {
+ DBG (1, "sanei_scsi_cmd: returning 0x%08x\n", ret);
+ }
+ DBG (10, "sanei_scsi_cmd: returning %lu bytes:\n", (u_long) ol);
+ if (out != NULL && out_len != 0)
+ hexdump (15, "", out, (out_len > 0x60) ? 0x60 : out_len);
+
+ return ret;
+}
+
+
+static int
+request_sense_parse (unsigned char *sensed_data)
+{
+ int ret, sense, asc, ascq;
+ sense = get_RS_sense_key (sensed_data);
+ asc = get_RS_ASC (sensed_data);
+ ascq = get_RS_ASCQ (sensed_data);
+
+ ret = SANE_STATUS_IO_ERROR;
+
+ switch (sense)
+ {
+ case 0x0:
+ DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq);
+ return SANE_STATUS_GOOD;
+
+ case 0x1:
+ if ((0x37 == asc) && (0x00 == ascq)) {
+ DBG (1, "\t%d/%d/%d: Rounded Parameter\n", sense, asc, ascq);
+ ret = SANE_STATUS_GOOD;
+ }
+ else if ((0x61 == asc) && (0x02 == ascq))
+ DBG (1, "\t%d/%d/%d: Out Of Focus\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x2:
+ if ((0x4 == asc) && (0x1 == ascq)) {
+ DBG (10, "\t%d/%d/%d: Logical unit is in process of becomming ready\n",
+ sense, asc, ascq);
+ ret = SANE_STATUS_DEVICE_BUSY;
+ }
+ else if ((0x3A == asc) && (0x00 == ascq))
+ {
+ DBG (1, "\t%d/%d/%d: No Diapo inserted\n", sense, asc, ascq);
+ ret = SANE_STATUS_GOOD;
+ }
+ else if ((0x60 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Lamp Failure\n", sense, asc, ascq);
+ else
+ {
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ ret = SANE_STATUS_GOOD;
+ }
+ break;
+
+ case 0x3:
+ if ((0x3b == asc) && (0xe == ascq))
+ DBG (1, "\t%d/%d/%d: Medium source element empty\n", sense, asc, ascq);
+ else if ((0x53 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Media Load of Eject Failed\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x4:
+ if ((0x15 == asc) && (0x1 == ascq))
+ DBG (1, "\t%d/%d/%d: Mechanical Positioning Error\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x5:
+ if ((0x00 == asc) && (0x5 == ascq))
+ DBG (1, "\t%d/%d/%d: End-Of-Data Detected\n", sense, asc, ascq);
+ else if ((0x1a == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Parameter List Length Error\n", sense, asc, ascq);
+ else if ((0x20 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Command Operation Code\n", sense, asc, ascq);
+ else if ((0x24 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Field In CDB\n", sense, asc, ascq);
+ else if ((0x25 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Logical Unit Not Supported\n", sense, asc, ascq);
+ else if ((0x26 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Field in Parameter List\n", sense, asc, ascq);
+ else if ((0x2c == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Command Sequence Error\n", sense, asc, ascq);
+ else if ((0x39 == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Saving Parameters Not Supported\n", sense, asc, ascq);
+ else if ((0x3d == asc) && (0x00 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Bits In Identify Message\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0x6:
+ if ((0x29 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Power On, Reset, or Bus Device Reset Occured\n", sense, asc, ascq);
+ else if ((0x2a == asc) && (0x1 == ascq))
+ DBG (1, "\t%d/%d/%d: Mode Parameters Changed\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ case 0xb:
+ if ((0x43 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Message Error\n", sense, asc, ascq);
+ else if ((0x47 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: SCSI Parity Error\n", sense, asc, ascq);
+ else if ((0x48 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Initiator Detected Error Message Received\n", sense, asc, ascq);
+ else if ((0x49 == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Invalid Message Error\n", sense, asc, ascq);
+ else if ((0x4e == asc) && (0x0 == ascq))
+ DBG (1, "\t%d/%d/%d: Overlapped Commands Attempted\n", sense, asc, ascq);
+ else
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+
+ default:
+ DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
+ break;
+ } /* switch */
+ return ret;
+}
+
+/*
+ * wait_scanner should spin until TEST_UNIT_READY returns 0 (GOOD)
+ * returns 0 on success,
+ * returns -1 on error.
+ */
+static int
+wait_scanner (Coolscan_t * s)
+{
+ int ret = -1;
+ int cnt = 0;
+ DBG (10, "wait_scanner: Testing if scanner is ready\n");
+
+ while (ret != 0)
+ {
+ ret = do_scsi_cmd (s->sfd, test_unit_ready.cmd,
+ test_unit_ready.size, 0, 0);
+
+ if (ret == SANE_STATUS_DEVICE_BUSY)
+ {
+ usleep (500000); /* wait 0.5 seconds */
+ if (cnt++ > 40)
+ { /* 20 sec. max (prescan takes up to 15 sec. */
+ DBG (1, "wait_scanner: scanner does NOT get ready\n");
+ return -1;
+ }
+ }
+ else if (ret == SANE_STATUS_GOOD)
+ {
+ DBG (10, "wait_scanner: scanner is ready\n");
+ return ret;
+ }
+ else
+ {
+ DBG (1, "wait_scanner: test unit ready failed (%s)\n",
+ sane_strstatus (ret));
+ }
+ }
+ return 0;
+}
+
+/* ------------------------- COOLSCAN GRAB SCANNER ----------------------------- */
+
+
+/* coolscan_grab_scanner should go through the following command sequence:
+ * TEST UNIT READY
+ * CHECK CONDITION \
+ * REQUEST SENSE > These should be handled automagically by
+ * UNIT ATTENTION / the kernel if they happen (powerup/reset)
+ * TEST UNIT READY
+ * GOOD
+ * RESERVE UNIT
+ * GOOD
+ *
+ * It is then responsible for installing appropriate signal handlers
+ * to call emergency_give_scanner() if user aborts.
+ */
+
+static int
+coolscan_grab_scanner (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "grabbing scanner\n");
+
+ wait_scanner (s); /* wait for scanner ready, if not print
+ sense and return 1 */
+ ret = do_scsi_cmd (s->sfd, reserve_unit.cmd, reserve_unit.size, NULL, 0);
+ if (ret)
+ return ret;
+
+ DBG (10, "scanner reserved\n");
+ return 0;
+}
+
+/*
+ * Convert a size in ilu to the units expected by the scanner
+ */
+
+static int
+resDivToVal (int res_div)
+{
+ if (res_div < 1 || res_div > resolution_list[0])
+ {
+ DBG (1, "Invalid resolution divisor %d \n", res_div);
+ return 2700;
+ }
+ else
+ {
+ return resolution_list[res_div];
+ }
+}
+
+static int
+resValToDiv (int res_val)
+{
+ int res_div;
+ int max_res = resolution_list[0];
+ for (res_div = 1; res_div <= max_res; res_div++)
+ {
+ if (resolution_list[res_div] == res_val)
+ break;
+ }
+ if (res_div > max_res)
+ {
+ DBG (1, "Invalid resolution value\n");
+ return 1;
+ }
+ else
+ {
+ return res_div;
+ }
+}
+/*
+ * use mode select to force a mesurement divisor of 2700
+ */
+static unsigned char mode_select[] =
+{
+ MODE_SELECT, 0x10, 0, 0, 20, 0,
+ 0, 0, 0, 8,
+ 0, 0, 0, 0, 0, 0, 0, 1,
+ 3, 6, 0, 0, 0xA, 0x8C, 0, 0};
+
+static int
+select_MUD (Coolscan_t * s)
+{
+ return do_scsi_cmd (s->sfd, mode_select, 26, NULL, 0);
+}
+
+static int
+coolscan_autofocus_LS30 (Coolscan_t * s)
+{
+ int x, y;
+
+ wait_scanner(s);
+ memcpy(s->buffer, autofocusLS30.cmd, autofocusLS30.size);
+ memcpy(s->buffer+ autofocusLS30.size, autofocuspos, 9);
+
+ x = s->xmaxpix - (s->brx + s->tlx) / 2;
+ y = (s->bry + s->tly) / 2;
+
+ DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
+
+ do_scsi_cmd (s->sfd, s->buffer,
+ autofocusLS30.size + 9, NULL, 0);
+ /* Trashes when used in combination with scsi-driver AM53C974.o */
+ do_scsi_cmd (s->sfd, command_c1.cmd,
+ command_c1.size, NULL, 0);
+
+ DBG (10, "\tWaiting end of Autofocus\n");
+ wait_scanner (s);
+ DBG (10, "AutoFocused.\n");
+ return 0;
+}
+
+static int
+coolscan_autofocus (Coolscan_t * s)
+{
+ int x, y;
+
+ if(s->LS>=2)
+ { return coolscan_autofocus_LS30(s);
+ }
+
+ wait_scanner(s);
+ memcpy(s->buffer, autofocus.cmd, autofocus.size);
+
+ x = s->xmaxpix - (s->brx + s->tlx) / 2;
+ y = (s->bry + s->tly) / 2;
+
+ DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
+
+ set_AF_XPoint (s->buffer, x);
+ set_AF_YPoint (s->buffer, y);
+
+ set_AF_transferlength (s->buffer, 0); /* should be 8 !*/
+ do_scsi_cmd (s->sfd, s->buffer,
+ autofocus.size + AF_Point_length, NULL, 0);
+
+ sleep(5); /* autofocus takes a minimum of 5 sec. */
+
+ DBG (10, "\tWaiting end of Autofocus\n");
+ wait_scanner (s);
+ DBG (10, "AutoFocused.\n");
+ return 0;
+}
+
+/*
+ static int
+ coolscan_abort_scan (Coolscan_t * s)
+ {
+ int ret;
+
+ DBG (5, "Aborting scan...\n");
+ ret = do_scsi_cmd (s->sfd, sabort.cmd, sabort.size, NULL, 0);
+ if (ret)
+ DBG (5, "Scan Aborted\n");
+ else
+ DBG (5, "Not scanning\n");
+ return 0;
+ }
+ */
+static int
+coolscan_mode_sense (Coolscan_t * s)
+{
+ int ret, len;
+
+ DBG (10, "Mode Sense...\n");
+ len = 12;
+ set_MS_DBD (mode_sense.cmd, 1);
+ set_MS_len (mode_sense.cmd, len);
+ ret = do_scsi_cmd (s->sfd, mode_sense.cmd, mode_sense.size,
+ s->buffer, len);
+
+ if (ret == 0)
+ {
+ s->MUD = get_MS_MUD (s->buffer);
+ DBG (10, "\tMode Sensed (MUD is %d)\n", s->MUD);
+ }
+ return ret;
+}
+
+static int
+coolscan_object_discharge (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "Trying to discharge object...\n");
+
+ memcpy (s->buffer, object_position.cmd, object_position.size);
+ set_OP_autofeed (s->buffer, OP_Discharge);
+ ret = do_scsi_cmd (s->sfd, s->buffer,
+ object_position.size, NULL, 0);
+ wait_scanner (s);
+ DBG (10, "Object discharged.\n");
+ return ret;
+}
+
+static int
+coolscan_object_feed (Coolscan_t * s)
+{
+ int ret;
+ DBG (10, "Trying to feed object...\n");
+ if (!s->asf)
+ {
+ DBG (10, "\tAutofeeder not present.\n");
+ return 0;
+ }
+ memcpy (s->buffer, object_position.cmd, object_position.size);
+ set_OP_autofeed (s->buffer, OP_Feed);
+ ret = do_scsi_cmd (s->sfd, s->buffer,
+ object_position.size, NULL, 0);
+ wait_scanner (s);
+ DBG (10, "Object fed.\n");
+ return ret;
+}
+
+/* coolscan_give_scanner should go through the following sequence:
+ * OBJECT POSITION DISCHARGE
+ * GOOD
+ * RELEASE UNIT
+ * GOOD
+ */
+static int
+coolscan_give_scanner (Coolscan_t * s)
+{
+ DBG (10, "trying to release scanner ...\n");
+ coolscan_object_discharge (s);
+ wait_scanner (s);
+ do_scsi_cmd (s->sfd, release_unit.cmd, release_unit.size, NULL, 0);
+ DBG (10, "scanner released\n");
+ return 0;
+}
+
+
+static int
+coolscan_set_window_param_LS20 (Coolscan_t * s, int prescan)
+{
+ unsigned char buffer_r[max_WDB_size];
+ int ret;
+
+ wait_scanner (s);
+ memset (buffer_r, '\0', max_WDB_size); /* clear buffer */
+ memcpy (buffer_r, window_descriptor_block.cmd,
+ window_descriptor_block.size); /* copy preset data */
+
+ set_WD_wid (buffer_r, WD_wid_all); /* window identifier */
+ set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */
+
+ set_WD_negative (buffer_r, s->negative); /* Negative/positive slide */
+
+ if (prescan)
+ {
+ set_WD_scanmode (buffer_r, WD_Prescan);
+ }
+ else
+ {
+ set_WD_scanmode (buffer_r, WD_Scan);
+
+ /* geometry */
+ set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */
+ set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */
+
+ /* the coolscan uses the upper right corner as the origin of coordinates */
+ /* xmax and ymax are given in 1200 dpi */
+ set_WD_ULX (buffer_r, (s->xmaxpix - s->brx));
+ set_WD_ULY (buffer_r, s->tly); /* upper_edge y */
+ set_WD_width (buffer_r, (s->brx - s->tlx + 1));
+ set_WD_length (buffer_r, (s->bry - s->tly + 1));
+
+ /* BTC */
+ if (s->brightness == 128)
+ {
+ set_WD_brightness (buffer_r, 0);
+ }
+ else
+ {
+ set_WD_brightness (buffer_r, s->brightness); /* brightness */
+ }
+
+ if (s->contrast == 128)
+ {
+ set_WD_contrast (buffer_r, 0);
+ }
+ else
+ {
+ set_WD_contrast (buffer_r, s->contrast); /* contrast */
+ }
+
+ /* scanmode */
+ if (s->colormode == GREYSCALE)
+ set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */
+ else
+ set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */
+
+ set_WD_dropoutcolor (buffer_r, s->dropoutcolor); /* Which color to scan with when grayscale scan */
+ set_WD_transfermode (buffer_r, WD_LineSequence);
+ set_WD_gammaselection (buffer_r, s->gammaselection); /* monitor/linear */
+
+ set_WD_shading (buffer_r, WD_Shading_ON); /* default for non-manufacturing */
+
+ if (1 == s->LS)
+ { /* Analog gamma reserved on LS-1000 */
+ set_WD_analog_gamma_R (buffer_r, 0);
+ set_WD_analog_gamma_G (buffer_r, 0);
+ set_WD_analog_gamma_R (buffer_r, 0);
+ }
+ else
+ {
+ /* Quote spec: "It is recomended that analog gamma bits 5, 4 and 3 be
+ * set to 1 (OFF) when the object type of byte 48 is positive and the
+ * gamma specificateion of byte 51 is linear, and to 0 (ON) in all
+ * other cases." */
+ /*
+ int foo;
+ if ((buffer_r[48] == WD_Positive) && (buffer_r[51] == WD_Linear))
+ foo = WD_Analog_Gamma_OFF;
+ else
+ foo = WD_Analog_Gamma_ON;
+ set_WD_analog_gamma_R (buffer_r, foo);
+ set_WD_analog_gamma_G (buffer_r, foo);
+ set_WD_analog_gamma_B (buffer_r, foo);
+ */
+ set_WD_analog_gamma_R (buffer_r, s->analog_gamma_r);
+ set_WD_analog_gamma_G (buffer_r, s->analog_gamma_g);
+ set_WD_analog_gamma_B (buffer_r, s->analog_gamma_b);
+ if (s->gamma_bind)
+ {
+ set_WD_LUT_R (buffer_r, 1);
+ set_WD_LUT_G (buffer_r, 1);
+ set_WD_LUT_B (buffer_r, 1);
+ }
+ else
+ {
+ set_WD_LUT_R (buffer_r, 1);
+ set_WD_LUT_G (buffer_r, 2);
+ set_WD_LUT_B (buffer_r, 3);
+ }
+ }
+ set_WD_averaging (buffer_r, s->averaging);
+
+ set_WD_brightness_R (buffer_r, s->brightness_R);
+ set_WD_brightness_G (buffer_r, s->brightness_G);
+ set_WD_brightness_B (buffer_r, s->brightness_B);
+
+ set_WD_contrast_R (buffer_r, s->contrast_R);
+ set_WD_contrast_G (buffer_r, s->contrast_G);
+ set_WD_contrast_B (buffer_r, s->contrast_B);
+
+ set_WD_exposure_R (buffer_r, s->exposure_R);
+ set_WD_exposure_G (buffer_r, s->exposure_G);
+ set_WD_exposure_B (buffer_r, s->exposure_B);
+ set_WD_shift_R (buffer_r, s->shift_R);
+ set_WD_shift_G (buffer_r, s->shift_G);
+ set_WD_shift_B (buffer_r, s->shift_B);
+
+
+ /* FIXME: LUT-[RGB] */
+ /* FIXME: stop on/off */
+ }
+
+ DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
+ s->x_nres, s->y_nres, s->tlx, s->tly);
+ DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
+ s->brx - s->tlx, s->MUD, s->brx);
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+ DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ /* prepare SCSI-BUFFER */
+ memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */
+ memcpy ((s->buffer + set_window.size), /* add WPDB */
+ window_parameter_data_block.cmd,
+ window_parameter_data_block.size);
+ set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size); /* set WD_len */
+ memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
+ buffer_r, window_descriptor_block.size);
+
+ hexdump (15, "Window set", buffer_r, s->wdb_len);
+
+ set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
+ window_descriptor_block.size));
+
+ ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
+ window_parameter_data_block.size +
+ window_descriptor_block.size,
+ NULL, 0);
+ DBG (10, "window set.\n");
+ return ret;
+}
+
+static int
+coolscan_set_window_param_LS30 (Coolscan_t * s, int wid, int prescan)
+{
+ unsigned char buffer_r[max_WDB_size];
+ int ret;
+
+ wait_scanner (s);
+ memset (buffer_r, '\0', max_WDB_size); /* clear buffer */
+ memcpy (buffer_r, window_descriptor_block_LS30.cmd,
+ window_descriptor_block_LS30.size); /* copy preset data */
+
+ set_WD_wid (buffer_r, wid); /* window identifier */
+ set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */
+
+ /* geometry */
+ set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */
+ set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */
+
+ if (prescan)
+ {
+ set_WD_scanmode_LS30 (buffer_r, WD_Prescan);
+ set_WD_Xres (buffer_r, resDivToVal (1)); /* x res. in dpi */
+ set_WD_Yres (buffer_r, resDivToVal (1)); /* y res. in dpi */
+ buffer_r[0x29]=0x81;
+ buffer_r[0x2a]=0x04;
+ buffer_r[0x2b]=0x02;
+ buffer_r[0x2c]=0x01;
+ buffer_r[0x2d]=0xff;
+ buffer_r[0x30]=0x00;
+ buffer_r[0x31]=0x00;
+ buffer_r[0x32]=0x00;
+ buffer_r[0x33]=0x00;
+ set_WD_width (buffer_r,(2592));
+ set_WD_length (buffer_r,(3894));
+ }
+ else
+ {
+ set_WD_scanmode_LS30 (buffer_r, WD_Scan);
+
+ /* the coolscan LS-30 uses the upper left corner
+ as the origin of coordinates */
+ /* xmax and ymax are given in 1200 dpi */
+ set_WD_ULX (buffer_r, s->tlx);
+ set_WD_ULY (buffer_r, s->tly); /* upper_edge y */
+ set_WD_width (buffer_r, (s->brx - s->tlx+1));
+ set_WD_length (buffer_r, (s->bry - s->tly+1));
+
+ /* BTC */
+ if (s->brightness == 128)
+ {
+ buffer_r[0x32]=0x00;
+ }
+ else
+ {
+ buffer_r[0x32]=s->brightness; /* brightness */
+ }
+
+ if (s->contrast == 128)
+ {
+ buffer_r[0x33]=0x00;
+ }
+ else
+ {
+ buffer_r[0x33]=s->contrast; /* contrast */
+ }
+
+ /* scanmode */
+ if (s->colormode == GREYSCALE)
+ set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */
+ else
+ set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */
+
+ set_WD_composition (buffer_r, WD_comp_rgb_full); /* allways RGB composition */
+
+ /* Bits per pixel */
+ set_WD_bitsperpixel(buffer_r, s->bits_per_color);
+
+ buffer_r[0x29]=0x81;
+ buffer_r[0x2a]=0x01;
+ buffer_r[0x2b]=0x02;
+ buffer_r[0x2c]=0x01;
+ buffer_r[0x2d]=0xff;
+ buffer_r[0x30]=0x00;
+
+ }
+ set_WD_negative_LS30(buffer_r, s->negative); /* Negative/positive slide */
+
+ switch(wid)
+ { case 1: set_gain_LS30(buffer_r,(s->exposure_R*s->pretv_r)/50);
+ break;
+ case 2: set_gain_LS30(buffer_r,(s->exposure_G*s->pretv_g)/50);
+ break;
+ case 3: set_gain_LS30(buffer_r,(s->exposure_B*s->pretv_b)/50);
+ break;
+ }
+
+ DBG (10, "\texpo_r=%d, expo_g=%d, expob=%d\n",
+ s->exposure_R, s->exposure_G, s->exposure_B);
+ DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
+ s->pretv_r, s->pretv_g, s->pretv_b);
+ DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
+ s->x_nres, s->y_nres, s->tlx, s->tly);
+ DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
+ s->brx - s->tlx, s->MUD, s->brx);
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+ DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ /* prepare SCSI-BUFFER */
+ memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */
+ memcpy ((s->buffer + set_window.size), /* add WPDB */
+ window_parameter_data_block.cmd,
+ window_parameter_data_block.size);
+ set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size_LS30); /* set WD_len */
+ memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
+ buffer_r, window_descriptor_block_LS30.size);
+
+ hexdump (15, "Window set", buffer_r, s->wdb_len);
+
+ set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
+ window_descriptor_block_LS30.size));
+
+ ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
+ window_parameter_data_block.size +
+ window_descriptor_block_LS30.size,
+ NULL, 0);
+ DBG (10, "window set.\n");
+ return ret;
+}
+
+static int
+coolscan_set_window_param (Coolscan_t * s, int prescan)
+{
+ int ret;
+ ret=0;
+ DBG (10, "set_window_param\n");
+
+ if(s->LS<2) /* distinquish between old and new scanners */
+ { ret=coolscan_set_window_param_LS20 (s,prescan);
+ }
+ else
+ { do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
+ wait_scanner (s);
+ wait_scanner (s);
+ coolscan_set_window_param_LS30(s,1,prescan);
+ ret=coolscan_set_window_param_LS30(s,2,prescan);
+ ret=coolscan_set_window_param_LS30(s,3,prescan);
+ if(s->colormode&0x08)
+ { ret=coolscan_set_window_param_LS30(s,9,prescan);
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * The only purpose of get_window is debugging. None of the return parameters
+ * is currently used.
+ */
+static int
+coolscan_get_window_param_LS30 (Coolscan_t * s, int wid,int prescanok)
+{
+ int translen;
+ unsigned char *buf;
+
+ DBG (10, "GET_WINDOW_PARAM\n");
+ /* wait_scanner (s); */
+
+ translen = window_parameter_data_block.size + window_descriptor_block_LS30.size;
+
+ /* prepare SCSI-BUFFER */
+ memset (s->buffer, '\0', max_WDB_size); /* clear buffer */
+
+ set_SW_xferlen (get_window.cmd, translen); /* Transfer length */
+ get_window.cmd[5]= wid; /* window identifier */
+
+ hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
+ do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
+ s->buffer, translen);
+
+ buf = s->buffer + window_parameter_data_block.size;
+ hexdump (10, "Window get", buf, 117);
+
+ s->brightness = buf[0x32]; /* brightness */
+ s->contrast = buf[0x33]; /* contrast */
+ DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
+
+ /* Useful? */
+ s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */
+
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+
+ if(prescanok)
+ { switch(wid)
+ { case 1: s->pretv_r = get_gain_LS30(buf);
+ break;
+ case 2: s->pretv_g = get_gain_LS30(buf);
+ break;
+ case 3: s->pretv_b = get_gain_LS30(buf);
+ break;
+ }
+ }
+
+ /* Should this one be set at all, here? */
+ s->transfermode = get_WD_transfermode (buf);
+
+ s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */
+ DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
+ s->pretv_r, s->pretv_g, s->pretv_b);
+
+ DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ DBG (10, "get_window_param - return\n");
+ return 0;
+}
+
+/*
+ * The only purpose of get_window is debugging. None of the return parameters
+ * is currently used.
+ */
+static int
+coolscan_get_window_param_LS20 (Coolscan_t * s)
+{
+ int translen;
+ unsigned char *buf;
+
+ DBG (10, "GET_WINDOW_PARAM\n");
+ wait_scanner (s);
+
+ translen = window_parameter_data_block.size + window_descriptor_block.size;
+
+ /* prepare SCSI-BUFFER */
+ memset (s->buffer, '\0', max_WDB_size); /* clear buffer */
+
+ set_SW_xferlen (get_window.cmd, translen); /* Transfer length */
+
+ hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
+ do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
+ s->buffer, translen);
+
+ buf = s->buffer + window_parameter_data_block.size;
+ hexdump (10, "Window get", buf, 117);
+
+ /* BTC */
+ s->brightness = get_WD_brightness (buf); /* brightness */
+ s->contrast = get_WD_contrast (buf); /* contrast */
+ DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
+
+ if (WD_comp_gray == get_WD_composition (buf))
+ s->colormode = GREYSCALE;
+ else
+ s->colormode = RGB;
+
+ /* Useful? */
+ s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */
+
+ DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
+ s->colormode, s->bits_per_color);
+
+
+ s->dropoutcolor = get_WD_dropoutcolor (buf); /* Which color to scan with when grayscale scan */
+
+ /* Should this one be set at all, here? */
+ s->transfermode = get_WD_transfermode (buf);
+
+ s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */
+
+ DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
+ s->negative, s->dropoutcolor, s->preview, s->transfermode,
+ s->gammaselection);
+
+ /* Should this one be set at all? */
+ s->shading = get_WD_shading (buf);
+ s->averaging = get_WD_averaging (buf);
+ DBG (10, "get_window_param - return\n");
+ return 0;
+}
+
+/*
+ * The only purpose of get_window is debugging. None of the return parameters
+ * is currently used.
+ */
+static int
+coolscan_get_window_param (Coolscan_t * s, int prescanok)
+{
+ int ret;
+ DBG (10, "get_window_param\n");
+
+ ret=0;
+ if(s->LS<2) /* distinquish between old and new scanners */
+ { ret=coolscan_get_window_param_LS20 (s);
+ }
+ else
+ {
+ ret=coolscan_get_window_param_LS30(s,1,prescanok);
+ ret=coolscan_get_window_param_LS30(s,2,prescanok);
+ ret=coolscan_get_window_param_LS30(s,3,prescanok);
+ if(s->colormode&0x08)
+ { ret=coolscan_get_window_param_LS30(s,9,prescanok);
+ }
+ }
+ return ret;
+}
+
+static int
+coolscan_start_scanLS30 (Coolscan_t * s)
+{ int channels;
+ DBG (10, "starting scan\n");
+
+ channels=1;
+ memcpy (s->buffer, scan.cmd, scan.size);
+ switch(s->colormode)
+ { case RGB:
+ case GREYSCALE:
+ channels=s->buffer[4]=0x03; /* window 1 */
+ s->buffer[6]=0x01; /* window 1 */
+ s->buffer[7]=0x02; /* window 2 */
+ s->buffer[8]=0x03; /* window 3 */
+
+ break;
+ case RGBI:
+ channels=s->buffer[4]=0x04; /* window 1 */
+ s->buffer[6]=0x01; /* window 1 */
+ s->buffer[7]=0x02; /* window 2 */
+ s->buffer[8]=0x03; /* window 3 */
+ s->buffer[9]=0x09; /* window 3 */
+ break;
+ case IRED:
+ channels=s->buffer[4]=0x01; /* window 1 */
+ s->buffer[8]=0x09; /* window 3 */
+ break;
+ }
+
+ return do_scsi_cmd (s->sfd, s->buffer, scan.size+channels, NULL, 0);
+}
+
+static int
+coolscan_start_scan (Coolscan_t * s)
+{
+ DBG (10, "starting scan\n");
+ if(s->LS>=2)
+ { return coolscan_start_scanLS30(s);
+ }
+ return do_scsi_cmd (s->sfd, scan.cmd, scan.size, NULL, 0);
+}
+
+
+static int
+prescan (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "Starting prescan...\n");
+ if(s->LS<2)
+ { coolscan_set_window_param (s, 1);
+ }
+ else
+ {
+ do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
+ wait_scanner (s);
+ wait_scanner (s);
+ coolscan_set_window_param_LS30 (s,1,1);
+ coolscan_set_window_param_LS30 (s,2,1);
+ coolscan_set_window_param_LS30 (s,3,1);
+
+ }
+ ret = coolscan_start_scan(s);
+
+ sleep(8); /* prescan takes a minimum of 10 sec. */
+ wait_scanner (s);
+ DBG (10, "Prescan done\n");
+ return ret;
+}
+
+static SANE_Status
+do_prescan_now (Coolscan_t * scanner)
+{
+
+ DBG (10, "do_prescan_now \n");
+ if (scanner->scanning == SANE_TRUE)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (scanner->sfd < 0)
+ { /* first call */
+ if (sanei_scsi_open (scanner->sane.name,
+ &(scanner->sfd),
+ sense_handler, 0) != SANE_STATUS_GOOD)
+ {
+ DBG (1, "do_prescan_now: open of %s failed:\n",
+ scanner->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ scanner->scanning = SANE_TRUE;
+
+
+ if (coolscan_check_values (scanner) != 0)
+ { /* Verify values */
+ DBG (1, "ERROR: invalid scan-values\n");
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (coolscan_grab_scanner (scanner))
+ {
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ DBG (5, "WARNING: unable to reserve scanner: device busy\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ prescan (scanner);
+ if(scanner->LS<2)
+ { get_internal_info(scanner);
+ }
+ coolscan_get_window_param (scanner,1);
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ return SANE_STATUS_GOOD;
+}
+
+
+static int
+send_one_LUT (Coolscan_t * s, SANE_Word * LUT, int reg)
+{
+ int i;
+ short lutval;
+ short bytesperval;
+ unsigned char *gamma, *gamma_p;
+ unsigned short *gamma_s;
+
+ DBG (10, "send LUT\n");
+
+ if(s->LS<2)
+ { set_S_datatype_code (send.cmd, R_user_reg_gamma);
+ bytesperval=1;
+ }
+ else
+ {
+ send.cmd[0x02]=3;
+ send.cmd[0x05]=1;
+ bytesperval=2;
+ }
+
+ set_S_xfer_length (send.cmd, s->lutlength*bytesperval);
+ set_S_datatype_qual_upper (send.cmd, reg);
+
+ gamma = alloca (send.size + s->lutlength*2);
+ memcpy (gamma, send.cmd, send.size);
+ if(s->LS<2)
+ { gamma_p = &gamma[send.size];
+ for (i = 0; i < s->lutlength; i++)
+ {
+ if (LUT[i] > 255)
+ LUT[i] = 255; /* broken gtk */
+ *gamma_p++ = (unsigned char) LUT[i];
+ }
+ }
+ else if(s->LS==2)
+ { gamma_s = (unsigned short*)( &gamma[send.size]);
+ for (i = 0; i < s->lutlength; i++)
+ {
+ if(s->negative)
+ {
+ lutval=(unsigned short)(LUT[(s->lutlength-i)]);
+ }
+ else
+ {
+ lutval=(unsigned short)(LUT[i]);
+ }
+ if (LUT[i] >= s->max_lut_val)
+ LUT[i] = s->max_lut_val-1; /* broken gtk */
+ if(s->low_byte_first) /* if on little endian machine: */
+ {
+ lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */
+ }
+ *gamma_s++ = lutval;
+ }
+ }
+ else if(s->LS==3)
+ { gamma_s = (unsigned short*)( &gamma[send.size]);
+ for (i = 0; i < s->lutlength; i++)
+ {
+ if(s->negative)
+ {
+ lutval=(unsigned short)(LUT[s->lutlength-i]);
+ }
+ else
+ {
+ lutval=(unsigned short)(LUT[i]);
+ }
+ if (LUT[i] >= s->max_lut_val)
+ LUT[i] = s->max_lut_val-1; /* broken gtk */
+ if(s->low_byte_first) /* if on little endian machine: */
+ { lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */
+ }
+ *gamma_s++ = lutval;
+ }
+ }
+ return do_scsi_cmd (s->sfd, gamma, send.size + s->lutlength*bytesperval, NULL, 0);
+}
+
+
+static int
+send_LUT (Coolscan_t * s)
+{
+ wait_scanner (s);
+ if (s->gamma_bind)
+ {
+ send_one_LUT (s, s->gamma, S_DQ_Reg1);
+ if(s->LS>=2)
+ { send_one_LUT (s, s->gamma, S_DQ_Reg2);
+ send_one_LUT (s, s->gamma, S_DQ_Reg3);
+ if(s->colormode&0x08)
+ { send_one_LUT (s, s->gamma, S_DQ_Reg9);
+ }
+
+ }
+ }
+ else
+ {
+ send_one_LUT (s, s->gamma_r, S_DQ_Reg1);
+ send_one_LUT (s, s->gamma_g, S_DQ_Reg2);
+ send_one_LUT (s, s->gamma_b, S_DQ_Reg3);
+ if(s->colormode&0x08)
+ { send_one_LUT (s, s->gamma_r, S_DQ_Reg9);
+ }
+ }
+ return 0;
+}
+
+
+static int
+coolscan_read_data_block (Coolscan_t * s, unsigned int datatype, unsigned int length)
+{
+ int r;
+
+ DBG (10, "read_data_block (type= %x length = %d)\n",datatype,length);
+ /*wait_scanner(s); */
+
+ set_R_datatype_code (sread.cmd, datatype);
+ sread.cmd[4]=00;
+ sread.cmd[5]=00;
+ set_R_xfer_length (sread.cmd, length);
+
+ r = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, length);
+ return ((r != 0) ? -1 : (int) length);
+}
+
+
+static void
+coolscan_do_inquiry (Coolscan_t * s)
+{
+ int size;
+
+ DBG (10, "do_inquiry\n");
+ memset (s->buffer, '\0', 256); /* clear buffer */
+ size = 36; /* Hardcoded, and as specified by Nikon */
+ /* then get inquiry with actual size */
+ set_inquiry_return_size (inquiry.cmd, size);
+ do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size, s->buffer, size);
+}
+
+static int
+coolscan_identify_scanner (Coolscan_t * s)
+{
+ unsigned char vendor[9];
+ unsigned char product[0x11];
+ unsigned char version[5];
+ unsigned char *pp;
+ int i;
+
+ vendor[8] = product[0x10] = version[4] = 0;
+ DBG (10, "identify_scanner\n");
+ coolscan_do_inquiry (s); /* get inquiry */
+ if (get_inquiry_periph_devtype (s->buffer) != IN_periph_devtype_scanner)
+ {
+ DBG (5, "identify_scanner: not a scanner\n");
+ return 1;
+ } /* no, continue searching */
+
+ coolscan_get_inquiry_values (s);
+
+ get_inquiry_vendor ((char *)s->buffer, (char *)vendor);
+ get_inquiry_product ((char *)s->buffer, (char *)product);
+ get_inquiry_version ((char *)s->buffer, (char *)version);
+
+ if (strncmp ("Nikon ", (char *)vendor, 8))
+ {
+ DBG (5, "identify_scanner: \"%s\" isn't a Nikon product\n", vendor);
+ return 1;
+ } /* Not a Nikon product */
+
+ pp = &vendor[8];
+ vendor[8] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ pp = &product[0x10];
+ product[0x10] = ' ';
+ while (*(pp - 1) == ' ')
+ {
+ *pp-- = '\0';
+ } /* leave one blank at the end! */
+
+ pp = &version[4];
+ version[4] = ' ';
+ while (*pp == ' ')
+ {
+ *pp-- = '\0';
+ }
+
+ DBG (10, "Found Nikon scanner %sversion %s on device %s\n",
+ product, version, s->devicename);
+
+ /* look for scanners that do not give all inquiry-informations */
+ /* and if possible use driver-known inquiry-data */
+ if (get_inquiry_additional_length (s->buffer) >= 0x1f)
+ {
+ /* Now identify full supported scanners */
+ for (i = 0; i < known_scanners; i++)
+ {
+ if (!strncmp ((char *)product, scanner_str[i], strlen (scanner_str[i])))
+ {
+ s->LS = i;
+ return 0;
+ }
+ }
+ if (s->cont)
+ return 0;
+ else
+ return 1;
+ }
+ else
+ return 1;
+}
+
+static int
+pixels_per_line (Coolscan_t * s)
+{
+ int pic_dot;
+ if(s->LS<2)
+ { pic_dot = (s->brx - s->tlx + s->x_nres) / s->x_nres;
+ }
+ else
+ { pic_dot = (s->brx - s->tlx + 1) / s->x_nres;
+ }
+ DBG (10, "pic_dot=%d\n", pic_dot);
+ return pic_dot;
+}
+
+static int
+lines_per_scan (Coolscan_t * s)
+{
+ int pic_line;
+ if(s->LS<2)
+ { pic_line = (s->bry - s->tly + s->y_nres) / s->y_nres;
+ }
+ else
+ { pic_line = (( s->bry - s->tly + 1.0 ) / s->y_nres);
+ }
+ DBG (10, "pic_line=%d\n", pic_line);
+ return pic_line;
+}
+
+static int
+scan_bytes_per_line (Coolscan_t * s)
+{ int bpl;
+ switch(s->colormode)
+ { case RGB:
+ case GREYSCALE:
+ bpl=pixels_per_line (s) * 3;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ case RGBI:
+ case IRED:
+ bpl=pixels_per_line (s) * 4;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ }
+ return 0;
+}
+
+static int
+write_bytes_per_line (Coolscan_t * s)
+{ int bpl;
+ switch(s->colormode)
+ { case RGB:
+ bpl=pixels_per_line (s) * 3;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ case RGBI:
+ bpl=pixels_per_line (s) * 4;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ case IRED:
+ case GREYSCALE:
+ bpl= pixels_per_line (s) ;
+ if(s->bits_per_color>8) bpl=bpl*2;
+ return bpl;
+ break;
+ }
+ return 0;
+}
+
+
+static void
+coolscan_trim_rowbufsize (Coolscan_t * s)
+{
+ unsigned int row_len;
+ row_len = scan_bytes_per_line (s);
+ s->row_bufsize = (s->row_bufsize < row_len) ? s->row_bufsize
+ : s->row_bufsize - (s->row_bufsize % row_len);
+ DBG (10, "trim_bufsize to %d\n", s->row_bufsize);
+}
+
+static int
+coolscan_check_values (Coolscan_t * s)
+{
+ DBG (10, "check_values\n");
+ /* -------------------------- asf --------------------------------- */
+ if (s->asf != 0)
+ {
+ if (s->autofeeder == 0)
+ {
+ DBG (1, "ERROR: ASF-MODE NOT SUPPORTED BY SCANNER, ABORTING\n");
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* test_little_endian */
+
+static SANE_Bool
+coolscan_test_little_endian(void)
+{
+ SANE_Int testvalue = 255;
+ unsigned char *firstbyte = (unsigned char *) &testvalue;
+
+ if (*firstbyte == 255)
+ { return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+static int
+get_inquiery_part_LS30 (Coolscan_t * s, unsigned char part)
+{
+ int size;
+ int ret;
+
+ /* Get length of reponse */
+ inquiry.cmd[1]=0x01;
+ inquiry.cmd[2]=part;
+ size=4;
+ set_inquiry_return_size (inquiry.cmd, size);
+ ret = do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
+ s->buffer, size);
+ size=get_inquiry_length(s->buffer);
+ size+=4;
+ /* then get inquiry with actual size */
+ set_inquiry_return_size (inquiry.cmd, size);
+ ret = do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
+ s->buffer, size);
+ return size;
+}
+
+static int
+coolscan_read_var_data_block (Coolscan_t * s,int datatype)
+{
+ int r;
+ int size;
+
+ DBG (10, "read_data_block (type= %x)\n",datatype);
+ /*wait_scanner(s); */
+
+ sread.cmd[2]=datatype;
+ sread.cmd[4]=00;
+ sread.cmd[5]=03;
+ size=6;
+ set_R_xfer_length (sread.cmd, size);
+ r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
+ s->buffer, size);
+ size=s->buffer[5];
+ set_R_xfer_length (sread.cmd, size);
+ r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
+ s->buffer, size);
+ return ((r != 0) ? -1 : size);
+}
+
+static int
+get_inquiery_LS30 (Coolscan_t * s)
+{
+ unsigned char part;
+ unsigned char parts[5];
+ int size;
+ int i;
+
+ /* Get vector of inquiery parts */
+ size=get_inquiery_part_LS30(s, (unsigned char) 0);
+ /* Get the parts of inquiery */
+ for(i=0;i<5;i++)
+ { parts[i]=((unsigned char *)s->buffer)[4+11+i];
+ }
+ for(i=0;i<5;i++)
+ { part=parts[i];
+ size=get_inquiery_part_LS30 (s, part);
+ switch(part)
+ { case 0x0c1:/* max size and resolution */
+ s->adbits = 8;
+ s->outputbits = 8;
+ s->maxres = getnbyte(s->buffer+0x12,2)-1;
+ s->xmaxpix = getnbyte(s->buffer+0x53,2)-1;
+ s->ymaxpix = getnbyte(s->buffer+0x3c,2)-1;
+ break;
+ case 0x0d1:
+ break;
+ case 0x0e1:
+ break;
+ case 0x0f0:
+ break;
+ case 0x0f8:
+ break;
+ }
+ }
+
+ /* get windows */
+ coolscan_get_window_param_LS30 (s,0,0);
+ s->xmax = get_WD_width(s->buffer);
+ s->ymax = get_WD_length(s->buffer);
+ coolscan_get_window_param_LS30 (s,1,0);
+ coolscan_get_window_param_LS30 (s,2,0);
+ coolscan_get_window_param_LS30 (s,3,0);
+ coolscan_get_window_param_LS30 (s,4,0);
+ coolscan_get_window_param_LS30 (s,9,0);
+
+ s->analoggamma = 0;
+ return 1;
+}
+
+static int
+get_feeder_type_LS30 (Coolscan_t * s)
+{
+ int size;
+ unsigned char *ptr;
+ int ima;
+
+ /* find out about Film-strip-feeder or Mount-Feeder */
+ size=get_inquiery_part_LS30(s, (unsigned char) 1);
+ if(strncmp((char *)s->buffer+5,"Strip",5)==0)
+ { s->feeder=STRIP_FEEDER;
+ s->autofeeder = 1;
+ }
+ if(strncmp((char *)s->buffer+5,"Mount",5)==0)
+ { s->feeder=MOUNT_FEEDER;
+ }
+ /* find out about Film-strip-feeder positions*/
+ if(s->feeder==STRIP_FEEDER)
+ { size=coolscan_read_var_data_block (s,(int)0x88);
+ if(size>=4)
+ { s->numima=s->buffer[3];
+ if(s->numima>6) s->numima=6; /* limit to 6 images for now */
+ if(s->numima>(size-4)/16) s->numima=(size-4)/16;
+ ptr=s->buffer+4;
+ for(ima=0;ima<s->numima;ima++)
+ { s->ipos[ima].start=getnbyte(ptr,4);
+ s->ipos[ima].offset=getnbyte(ptr+4,4);
+ s->ipos[ima].end=getnbyte(ptr+8,4);
+ s->ipos[ima].height=getnbyte(ptr+12,4);
+ ptr+=16;
+ }
+ }
+ s->posima=0;
+ }
+ return 1;
+}
+
+
+static int
+get_internal_info_LS20 (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "get_internal_info\n");
+ wait_scanner (s);
+ memset (s->buffer, '\0', DI_length); /* clear buffer */
+
+ set_R_datatype_code (sread.cmd, R_device_internal_info);
+ set_R_datatype_qual_upper (sread.cmd, R_DQ_none);
+ set_R_xfer_length (sread.cmd, DI_length);
+ /* then get inquiry with actual size */
+ ret = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
+ s->buffer, DI_length);
+
+ s->adbits = get_DI_ADbits (s->buffer);
+ s->outputbits = get_DI_Outputbits (s->buffer);
+ s->maxres = get_DI_MaxResolution (s->buffer);
+ s->xmax = get_DI_Xmax (s->buffer);
+ s->ymax = get_DI_Ymax (s->buffer);
+ s->xmaxpix = get_DI_Xmaxpixel (s->buffer);
+ s->ymaxpix = get_DI_Ymaxpixel (s->buffer);
+ s->ycurrent = get_DI_currentY (s->buffer);
+ s->currentfocus = get_DI_currentFocus (s->buffer);
+ s->currentscanpitch = get_DI_currentscanpitch (s->buffer);
+ s->autofeeder = get_DI_autofeeder (s->buffer);
+ s->analoggamma = get_DI_analoggamma (s->buffer);
+ s->derr[0] = get_DI_deviceerror0 (s->buffer);
+ s->derr[1] = get_DI_deviceerror1 (s->buffer);
+ s->derr[2] = get_DI_deviceerror2 (s->buffer);
+ s->derr[3] = get_DI_deviceerror3 (s->buffer);
+ s->derr[4] = get_DI_deviceerror4 (s->buffer);
+ s->derr[5] = get_DI_deviceerror5 (s->buffer);
+ s->derr[6] = get_DI_deviceerror6 (s->buffer);
+ s->derr[7] = get_DI_deviceerror7 (s->buffer);
+ s->wbetr_r = get_DI_WBETR_R (s->buffer);
+ s->webtr_g = get_DI_WBETR_G (s->buffer);
+ s->webtr_b = get_DI_WBETR_B (s->buffer);
+ s->pretv_r = get_DI_PRETV_R (s->buffer);
+ s->pretv_g = get_DI_PRETV_G (s->buffer);
+ s->pretv_r = get_DI_PRETV_R (s->buffer);
+ s->cetv_r = get_DI_CETV_R (s->buffer);
+ s->cetv_g = get_DI_CETV_G (s->buffer);
+ s->cetv_b = get_DI_CETV_B (s->buffer);
+ s->ietu_r = get_DI_IETU_R (s->buffer);
+ s->ietu_g = get_DI_IETU_G (s->buffer);
+ s->ietu_b = get_DI_IETU_B (s->buffer);
+ s->limitcondition = get_DI_limitcondition (s->buffer);
+ s->offsetdata_r = get_DI_offsetdata_R (s->buffer);
+ s->offsetdata_g = get_DI_offsetdata_G (s->buffer);
+ s->offsetdata_b = get_DI_offsetdata_B (s->buffer);
+ get_DI_poweron_errors (s->buffer, s->power_on_errors);
+
+ DBG (10,
+ "\tadbits=%d\toutputbits=%d\tmaxres=%d\txmax=%d\tymax=%d\n"
+ "\txmaxpix=%d\tymaxpix=%d\tycurrent=%d\tcurrentfocus=%d\n"
+ "\tautofeeder=%s\tanaloggamma=%s\tcurrentscanpitch=%d\n",
+ s->adbits, s->outputbits, s->maxres, s->xmax, s->ymax,
+ s->xmaxpix, s->ymaxpix, s->ycurrent, s->currentfocus,
+ s->autofeeder ? "Yes" : "No", s->analoggamma ? "Yes" : "No",
+ s->currentscanpitch);
+ DBG (10,
+ "\tWhite balance exposure time var [RGB]=\t%d %d %d\n"
+ "\tPrescan result exposure time var [RGB]=\t%d %d %d\n"
+ "\tCurrent exposure time var.[RGB]=\t%d %d %d\n"
+ "\tInternal exposure time unit[RGB]=\t%d %d %d\n",
+ s->wbetr_r, s->webtr_g, s->webtr_b, s->pretv_r, s->pretv_g,
+ s->pretv_r, s->cetv_r, s->cetv_g, s->cetv_b, s->ietu_r,
+ s->ietu_g, s->ietu_b);
+ DBG (10,
+ "\toffsetdata_[rgb]=\t0x%x 0x%x 0x%x\n"
+ "\tlimitcondition=0x%x\n"
+ "\tdevice error code = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n"
+ "\tpower-on errors = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ s->offsetdata_r, s->offsetdata_g, s->offsetdata_b,
+ s->limitcondition,
+ s->derr[0], s->derr[1], s->derr[2], s->derr[3], s->derr[4],
+ s->derr[5], s->derr[6], s->derr[7],
+ s->power_on_errors[0], s->power_on_errors[1],
+ s->power_on_errors[2], s->power_on_errors[3],
+ s->power_on_errors[4], s->power_on_errors[5],
+ s->power_on_errors[6], s->power_on_errors[7]);
+
+ return ret;
+}
+
+static int
+get_internal_info (Coolscan_t * s)
+{
+ int ret;
+
+ DBG (10, "get_internal_info\n");
+
+ if(s->LS<2) /* distinquish between old and new scanners */
+ { ret=get_internal_info_LS20 (s);
+ }
+ else
+ { ret=get_inquiery_LS30 (s);
+ }
+ return ret;
+}
+
+static void
+coolscan_get_inquiry_values (Coolscan_t * s)
+{
+ unsigned char *inquiry_block;
+
+ DBG (10, "get_inquiry_values\n");
+
+ inquiry_block = (unsigned char *) s->buffer;
+ s->inquiry_len = 36;
+
+ get_inquiry_vendor ((char *)inquiry_block, (char *)s->vendor);
+ s->vendor[8] = '\0';
+ get_inquiry_product ((char *)inquiry_block, (char *)s->product);
+ s->product[16] = '\0';
+ get_inquiry_version ((char *)inquiry_block, (char *)s->version);
+ s->version[4] = '\0';
+
+ if (s->inquiry_len < 36)
+ {
+ DBG (1, "WARNING: inquiry return block is unexpected short (%d instead of 36).\n", s->inquiry_len);
+ }
+ s->inquiry_wdb_len = 117;
+ return;
+}
+
+static void
+coolscan_initialize_values (Coolscan_t * s)
+{
+ int i;
+ DBG (10, "initialize_values\n");
+ /* Initialize us structure */
+ if(s->LS<2) /* LS-20 or LS-10000 */
+ { select_MUD (s); /* must be before mode_sense - not for LS-30*/
+ coolscan_mode_sense (s); /* Obtain MUD (Measurement Unit Divisor) */
+ get_internal_info (s); /* MUST be called first. */
+ s->wdb_len = 117;
+ }
+ if(s->LS>=2) /* LS-30 */
+ {
+ get_inquiery_LS30(s); /* Info about scanner*/
+ select_MUD (s); /* must be before mode_sense */
+ get_feeder_type_LS30(s);
+ s->wdb_len = 117;
+ }
+
+ s->cont = 0; /* do not continue if scanner is unknown */
+ s->verbose = 2; /* 1=verbose,2=very verbose */
+
+
+ s->x_nres = s->y_nres = 2; /* 2 => 1350 dpi */
+ s->x_p_nres = s->y_p_nres = 9; /* 9 => 300 dpi */
+ s->tlx = 0;
+ s->tly = 0;
+ s->brx = s->xmaxpix; /* 2700 / 1200; */
+ s->bry = s->ymaxpix; /* 2700 / 1200; */
+
+
+ s->set_auto = 0; /* Always 0 on Nikon LS-{100|2}0 */
+ s->preview = 0; /* 1 for preview */
+ s->colormode = RGB; /* GREYSCALE or RGB */
+ s->colormode_p = RGB; /* GREYSCALE or RGB for preview*/
+ s->asf = 0; /* 1 if asf shall be used */
+ s->gammaselection = WD_Linear;
+
+ s->brightness = 128;
+ s->brightness_R = 128;
+ s->brightness_G = 128;
+ s->brightness_B = 128;
+ s->contrast = 128;
+ s->contrast_R = 128;
+ s->contrast_G = 128;
+ s->contrast_B = 128;
+
+ s->exposure_R = 50;
+ s->exposure_G = 50;
+ s->exposure_B = 50;
+
+ s->pretv_r=40000;
+ s->pretv_g=40000;
+ s->pretv_b=40000;
+
+ s->shift_R = 128;
+ s->shift_G = 128;
+ s->shift_B = 128;
+
+ s->ired_red=60;
+ s->ired_green=1;
+ s->ired_blue=1;
+
+ s->prescan = 1;
+ s->bits_per_color = 8;
+ s->rgb_control = 0;
+ s->gamma_bind = 1;
+ switch(s->LS)
+ { case 0:s->lutlength=2048;
+ s->max_lut_val=256;
+ break;
+ case 1:s->lutlength=512;
+ s->max_lut_val=512;
+ break;
+ case 2:s->lutlength=1024;
+ s->max_lut_val=1024;
+ break;
+ case 3:s->lutlength=4096;
+ s->max_lut_val=4096;
+ break;
+ }
+ for (i = 0; i < s->lutlength; i++)
+ {
+ s->gamma[i] =((short)((((double)i)/s->lutlength)*s->max_lut_val));
+ s->gamma_r[i] = s->gamma[i];
+ s->gamma_g[i] = s->gamma[i];
+ s->gamma_b[i] = s->gamma[i];
+ }
+
+ if (coolscan_test_little_endian() == SANE_TRUE)
+ {
+ s->low_byte_first = 1; /* in 2 byte mode send lowbyte first */
+ DBG(10,"backend runs on little endian machine\n");
+ }
+ else
+ {
+ s->low_byte_first = 0; /* in 2 byte mode send highbyte first */
+ DBG(10,"backend runs on big endian machine\n");
+ }
+}
+
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (level, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+
+static SANE_Status
+sense_handler (int scsi_fd, unsigned char * result, void *arg)
+{
+ scsi_fd = scsi_fd;
+ arg = arg;
+
+ if (result[0] != 0x70)
+ {
+ return SANE_STATUS_IO_ERROR; /* we only know about this one */
+ }
+ return request_sense_parse(result);
+
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* ilu per mm */
+
+#define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 2700.0))
+#define mmToIlu(mm) ((mm) / length_quant)
+#define iluToMm(ilu) ((ilu) * length_quant)
+
+#define P_200_TO_255(per) SANE_UNFIX((per + 100) * 255/200 )
+#define P_100_TO_255(per) SANE_UNFIX(per * 255/100 )
+
+static const char negativeStr[] = "Negative";
+static const char positiveStr[] = "Positive";
+static SANE_String_Const type_list[] =
+{
+ positiveStr,
+ negativeStr,
+ 0
+};
+
+static const char colorStr[] = SANE_VALUE_SCAN_MODE_COLOR;
+static const char grayStr[] = SANE_VALUE_SCAN_MODE_GRAY;
+static const char rgbiStr[] = "RGBI";
+static const char iredStr[] = "Infrared";
+
+static SANE_String_Const scan_mode_list_LS20[] =
+{
+ colorStr,
+ grayStr,
+ NULL
+};
+
+static SANE_String_Const scan_mode_list_LS30[] =
+{
+ colorStr,
+ grayStr,
+#ifdef HAS_IRED
+ rgbiStr,
+#endif /* HAS_IRED */
+ NULL
+};
+
+static SANE_Int bit_depth_list[9];
+
+static const char neverStr[] = "never";
+static const char previewStr[] = "before preview";
+static const char scanStr[] = "before scan";
+static const char preandscanStr[] = "before preview and scan";
+static SANE_String_Const autofocus_mode_list[] =
+{
+ neverStr,
+ previewStr,
+ scanStr,
+ preandscanStr,
+ NULL
+};
+
+static SANE_String_Const source_list[4] =
+{NULL, NULL, NULL, NULL};
+
+static const SANE_Range gamma_range_8 =
+{
+ 0, /* minimum */
+ 255, /* maximum */
+ 1 /* quantization */
+};
+
+
+static const SANE_Range gamma_range_9 =
+{
+ 0, /* minimum */
+ 511, /* maximum */
+ 1 /* quantization */
+};
+
+static const SANE_Range gamma_range_10 =
+{
+ 0, /* minimum */
+ 1023, /* maximum */
+ 1 /* quantization */
+};
+
+static const SANE_Range gamma_range_12 =
+{
+ 0, /* minimum */
+ 4096, /* maximum */
+ 1 /* quantization */
+};
+
+static const SANE_Range brightness_range =
+{
+ -5,
+ +5,
+ 1
+};
+
+static const SANE_Range contrast_range =
+{
+ -5,
+ +5,
+ 0
+};
+
+static const SANE_Range exposure_range =
+{
+ 24,
+ 400,
+ 2
+};
+
+static const SANE_Range shift_range =
+{
+ -15,
+ +15,
+ 0
+};
+
+static int num_devices;
+static Coolscan_t *first_dev;
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+do_eof (Coolscan_t * scanner)
+{
+ DBG (10, "do_eof\n");
+
+ if (scanner->pipe >= 0)
+ {
+ close (scanner->pipe);
+ scanner->pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+}
+
+static SANE_Status
+do_cancel (Coolscan_t * scanner)
+{
+ DBG (10, "do_cancel\n");
+ swap_res (scanner);
+ scanner->scanning = SANE_FALSE;
+
+ do_eof (scanner); /* close pipe and reposition scanner */
+
+ if (scanner->reader_pid != -1)
+ {
+ int exit_status;
+
+ DBG (10, "do_cancel: kill reader_process\n");
+
+ /* ensure child knows it's time to stop: */
+ sanei_thread_kill (scanner->reader_pid);
+ while (sanei_thread_waitpid(scanner->reader_pid, &exit_status) !=
+ scanner->reader_pid );
+ scanner->reader_pid = -1;
+ }
+
+ if (scanner->sfd >= 0)
+ {
+ coolscan_give_scanner (scanner);
+ DBG (10, "do_cancel: close filedescriptor\n");
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+static SANE_Status
+attach_scanner (const char *devicename, Coolscan_t ** devp)
+{
+ Coolscan_t *dev;
+ int sfd;
+
+ DBG (10, "attach_scanner: %s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (5, "attach_scanner: scanner already attached (is ok)!\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (10, "attach_scanner: opening %s\n", devicename);
+ if (sanei_scsi_open (devicename, &sfd, sense_handler, 0) != 0)
+ {
+ DBG (1, "attach_scanner: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (NULL == (dev = malloc (sizeof (*dev))))
+ return SANE_STATUS_NO_MEM;
+
+
+ dev->row_bufsize = (sanei_scsi_max_request_size < (64 * 1024)) ?
+ sanei_scsi_max_request_size : 64 * 1024;
+
+ if ((dev->buffer = malloc (dev->row_bufsize)) == NULL)
+/* if ((dev->buffer = malloc (sanei_scsi_max_request_size)) == NULL)*/
+ return SANE_STATUS_NO_MEM;
+
+ if ((dev->obuffer = malloc (dev->row_bufsize)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev->devicename = strdup (devicename);
+ dev->sfd = sfd;
+
+ /* Nikon manual: Step 1 */
+ if (coolscan_identify_scanner (dev) != 0)
+ {
+ DBG (1, "attach_scanner: scanner-identification failed\n");
+ sanei_scsi_close (dev->sfd);
+ free (dev->buffer);
+ free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Get MUD (via mode_sense), internal info (via get_internal_info), and
+ * initialize values */
+ coolscan_initialize_values (dev);
+
+ /* Why? */
+ sanei_scsi_close (dev->sfd);
+ dev->sfd = -1;
+
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->vendor;
+ dev->sane.model = dev->product;
+ dev->sane.type = "slide scanner";
+
+ dev->x_range.min = SANE_FIX (0);
+ dev->x_range.quant = SANE_FIX (length_quant);
+ dev->x_range.max = SANE_FIX ((double) ((dev->xmaxpix) * length_quant));
+
+ dev->y_range.min = SANE_FIX (0.0);
+ dev->y_range.quant = SANE_FIX (length_quant);
+ dev->y_range.max = SANE_FIX ((double) ((dev->ymaxpix) * length_quant));
+
+ /* ...and this?? */
+ dev->dpi_range.min = SANE_FIX (108);
+ dev->dpi_range.quant = SANE_FIX (0);
+ dev->dpi_range.max = SANE_FIX (dev->maxres);
+ DBG (10, "attach: dev->dpi_range.max = %f\n",
+ SANE_UNFIX (dev->dpi_range.max));
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ DBG (10, "attach_scanner done\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *devName)
+{
+ return attach_scanner(devName, 0);
+}
+
+static RETSIGTYPE
+sigterm_handler (int signal)
+{
+ signal = signal;
+ sanei_scsi_req_flush_all (); /* flush SCSI queue */
+ _exit (SANE_STATUS_GOOD);
+}
+
+
+typedef struct Color_correct_s
+{ int sum; /* number of pixels summed so far */
+ double sumr; /* sum of red pixel values*/
+ double sumi; /* sum of infrared pixel values*/
+ double sumri; /* sum of red*ired pixel values*/
+ double sumii; /* sum of ired*ired pixel values*/
+ double sumrr; /* sum of ired*ired pixel values*/
+ int mr; /* factor between red and ired values (*256) */
+ int br; /* offset of ired values */
+} ColorCorrect;
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix
+
+ taks: Correct the infrared channel
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ int *lutr - lookup table for red correction
+ int *lutg - lookup table for red correction
+ int *lutb - lookup table for red correction
+ int *lutr - lookup table for red correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 19.6.1999
+
+ ----------------------------------------------------------------*/
+
+static int Calc_fix_LUT(Coolscan_t * s)
+{ int uselutr,uselutg,uselutb,useluti;
+/* static int irmulr= -34*25; */
+ int irmulr= -64*25;
+ int irmulg= -1*25;
+ int irmulb= -0*25;
+ int irmuli= 256*25;
+ int div;
+ int i;
+
+ irmulr=s->ired_red*(25);
+ irmulg=s->ired_green*(25);
+ irmulb=s->ired_blue*(25);
+ irmuli=25*256;
+
+ if(s->LS==2) /* TODO: right conversion factors for 10 and 12 bit */
+ { div=4;
+ }
+ else if(s->LS==3)
+ { div=16;
+ }
+ else
+ { return 0;
+ }
+
+ memset(s->lutr, 0,256*4);
+ memset(s->lutg, 0,256*4);
+ memset(s->lutb, 0,256*4);
+ memset(s->luti, 0,256*4);
+
+ for(i=0;i<s->lutlength;i++)
+ { if(s->gamma_bind)
+ { uselutr=uselutg=uselutb=useluti=s->gamma[i]/div;
+ }
+ else
+ { uselutr=s->gamma_r[i]/div;
+ uselutg=s->gamma_g[i]/div;
+ uselutb=s->gamma_b[i]/div;
+ useluti=s->gamma_r[i]/div;
+ }
+ s->lutr[uselutr]=(int)(irmulr*pow((double)i,(double)0.333333));
+ s->lutg[uselutg]=(int)(irmulg*pow((double)i,(double)0.333333));
+ s->lutb[uselutb]=(int)(irmulb*pow((double)i,(double)0.333333));
+ s->luti[useluti]=(int)(irmuli*pow((double)i,(double)0.333333));
+ if(uselutr<255)
+ { if(s->lutr[uselutr+1]==0) s->lutr[uselutr+1]=s->lutr[uselutr];
+ }
+ if(uselutg<255)
+ { if(s->lutg[uselutg+1]==0) s->lutg[uselutg+1]=s->lutg[uselutg];
+ }
+ if(uselutb<255)
+ { if(s->lutb[uselutb+1]==0) s->lutb[uselutb+1]=s->lutb[uselutb];
+ }
+ if(useluti<255)
+ { if(s->luti[useluti+1]==0) s->luti[useluti+1]=s->luti[useluti];
+ }
+ }
+ /* DEBUG
+ for(i=0;i<255;i++)
+ { fprintf(stderr,"%d %d %d %d\n"
+ ,s->lutr[i],s->lutg[i],s->lutb[i],s->luti[i]);
+ }
+ */
+ return 1;
+}
+
+
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix
+
+ taks: Correct the infrared channel
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ int *lutr - lookup table for red correction
+ int *lutg - lookup table for red correction
+ int *lutb - lookup table for red correction
+ int *lutr - lookup table for red correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 19.6.1999
+
+ ----------------------------------------------------------------*/
+
+static int RGBIfix(Coolscan_t * scanner,
+ unsigned char* rgbimat,
+ unsigned char* orgbimat,
+ int size,
+ int *lutr,
+ int *lutg,
+ int *lutb,
+ int *luti)
+
+{
+ unsigned char *pr,*pg,*pb,*pi;
+ unsigned char *opr,*opg,*opb,*opi;
+
+ int r,g,b,i;
+ int ii;
+ int x;
+ for(x=0;x<size;x++)
+ {
+ pr=rgbimat+x*4;
+ pg=pr+1;
+ pb=pg+1;
+ pi=pb+1;
+ opr=orgbimat+x*4;
+ opg=opr+1;
+ opb=opg+1;
+ opi=opb+1;
+ r=lutr[(*pr)];
+ g=lutg[(*pg)];
+ b=lutb[(*pb)];
+ i=luti[(*pi)];
+ ii= i-r-g-b;
+ (*opr)=(*pr);
+ (*opg)=(*pg);
+ (*opb)=(*pb);
+ if(ii<0)ii=0;
+ if(ii>255*256)ii=255*256;
+ if(scanner->negative)
+ {
+ (*opi)=(unsigned char)(255-(ii>>8));
+ }
+ else
+ {
+ (*opi)=(unsigned char)(ii>>8);
+ }
+ }
+ return 1;
+}
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix16
+
+ taks: Correct the infrared channel for 16 bit images
+ (doesn't do anything for now)
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ int *lutr - lookup table for red correction
+ int *lutg - lookup table for red correction
+ int *lutb - lookup table for red correction
+ int *lutr - lookup table for red correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 19.6.1999
+
+ ----------------------------------------------------------------*/
+
+static int RGBIfix16(Coolscan_t * scanner,
+ unsigned short* rgbimat,
+ unsigned short* orgbimat,
+ int size,
+ int *lutr,
+ int *lutg,
+ int *lutb,
+ int *luti)
+
+{
+ unsigned short *pr,*pg,*pb,*pi;
+ unsigned short *opr,*opg,*opb,*opi;
+ int x;
+
+ scanner = scanner; lutr = lutr; lutg = lutg; lutb = lutb; luti = luti;
+
+ for(x=0;x<size;x++)
+ {
+ pr=rgbimat+x*4;
+ pg=pr+1;
+ pb=pg+1;
+ pi=pb+1;
+ opr=orgbimat+x*4;
+ opg=opr+1;
+ opb=opg+1;
+ opi=opb+1;
+ (*opr)=(((*pr)&0x00ff)<<8)+(((*pr)&0xff00)>>8);
+ (*opg)=(((*pg)&0x00ff)<<8)+(((*pg)&0xff00)>>8);
+ (*opb)=(((*pb)&0x00ff)<<8)+(((*pb)&0xff00)>>8);
+ (*opi)=(((*pi)&0x00ff)<<8)+(((*pi)&0xff00)>>8);
+ }
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------
+
+ function: rgb2g
+
+ taks: Convert RGB data to grey
+
+ import: unsigned char * rgbimat - RGB - matrix from scanner
+ int size - size of input data (num pixel)
+
+ export: unsigned char * gomat - Grey matrix
+
+ written by: Andreas RICK 13.7.1999
+
+ ----------------------------------------------------------------*/
+#define RtoG ((int)(0.27*256))
+#define GtoG ((int)(0.54*256))
+#define BtoG ((int)(0.19*256))
+
+static int rgb2g(unsigned char* rgbimat,unsigned char* gomat,
+ int size)
+
+{ unsigned char *pr,*pg,*pb;
+ unsigned char *opg;
+
+ int g;
+ int x;
+ for(x=0;x<size;x++)
+ {
+ pr=rgbimat+x*3;
+ pg=pr+1;
+ pb=pg+1;
+ opg=gomat+x;
+ g= RtoG*(*pr) + GtoG*(*pg) + BtoG*(*pb);
+ (*opg)=(unsigned char)(g>>8);
+ }
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------
+
+ function: RGBIfix1
+
+ taks: Correct the infrared channel.
+ The input image data is the output of scaning
+ with LUT. To calculate the original values
+ the lutr and luti is applied.
+ The infrared values is corrected by:
+
+ Ir=mr*lutr(r)+luti(i)
+
+ import: unsigned char * rgbimat - RGBI - matrix from scanner
+ int size - number of pixels to correct
+ ColorCorrect *cc,
+ int *lutr - lookup table for red correction
+ int *luti - lookup table for ired correction
+
+ export: unsigned char * orgbimat - RGBI - corrected matrix
+
+ written by: Andreas RICK 3.7.1999
+
+ ----------------------------------------------------------------*/
+#if 0
+static int RGBIfix1(unsigned char* rgbimat,unsigned char* orgbimat,
+ int size,
+ int *lutr,
+ int *lutg,
+ int *lutb,
+ int *luti)
+
+{ unsigned char *pr,*pg,*pb,*pi;
+ unsigned char *opr,*opg,*opb,*opi;
+ ColorCorrect cc;
+ int r,i;
+ static int thresi=100;
+ int ii;
+ int x;
+
+ lutg = lutg; lutb = lutb;
+
+ /* calculate regression between r and ir */
+ cc.sum=0;
+ cc.sumr=cc.sumii=cc.sumrr=cc.sumi=cc.sumri=0.0;
+ for(x=0;x<size;x++)
+ { pr=rgbimat+x*4;
+ pi=pr+3;
+ r=lutr[(*pr)];
+ i=luti[(*pi)];
+ /* r=(*pr);
+ i=(*pi); */
+ if((*pi)>thresi)
+ { cc.sum++;
+ cc.sumr+=r;
+ cc.sumii+=(i*i);
+ cc.sumrr+=(r*r);
+ cc.sumi+=i;
+ cc.sumri+=(i*r);
+ }
+ }
+ if((cc.sumii!=0)&&(cc.sum!=0))
+ { double dn,dz,dm;
+ dz=(cc.sumri-cc.sumr*cc.sumi/cc.sum);
+ dn=(cc.sumrr-cc.sumr*cc.sumr/cc.sum);
+ DBG (2, "Reg:dz:%e dn:%e\n",dz,dn);
+ if(dn!=0)
+ { dm=(dz/dn);
+ cc.mr=(int)(dm*1024);
+ }
+ else
+ { cc.mr=0;
+ dm=0;
+ }
+ cc.br=(int)((cc.sumi-dm*cc.sumr)/cc.sum);
+ }
+ else
+ { cc.mr=0;
+ }
+ DBG (2, "Regression: size:%d I=%d/1024*R b:%d s:%d sr:%e si:%e sii:%e sri:%e srr:%e\n",
+ size,cc.mr,cc.br,cc.sum,cc.sumr,cc.sumi,cc.sumii,cc.sumri,cc.sumrr);
+ for(x=0;x<size;x++)
+ {
+
+ pr=rgbimat+x*4;
+ pg=pr+1;
+ pb=pg+1;
+ pi=pb+1;
+ opr=orgbimat+x*4;
+ opg=opr+1;
+ opb=opg+1;
+ opi=opb+1;
+ r=lutr[(*pr)];
+ i=luti[(*pi)];
+ /* r=(*pr);
+ i=(*pi); */
+ ii= ((i-((r*cc.mr)>>10)-cc.br)>>2) +128;
+ (*opr)=(*pr);
+ (*opg)=(*pg);
+ (*opb)=(*pb);
+ if(ii<0) ii=0;
+ if(ii>255) ii=255;
+ (*opi)=(unsigned char)(ii);
+ }
+ return 1;
+}
+
+#endif
+/* This function is executed as a child process. */
+static int
+reader_process (void *data )
+{
+ int status;
+ unsigned int i;
+ unsigned char h;
+ unsigned int data_left;
+ unsigned int data_to_read;
+ unsigned int data_to_write;
+ FILE *fp;
+ sigset_t sigterm_set, ignore_set;
+ struct SIGACTION act;
+ unsigned int bpl, linesPerBuf, lineOffset;
+ unsigned char r_data, g_data, b_data;
+ unsigned int j, line;
+ Coolscan_t * scanner = (Coolscan_t*)data;
+
+ if (sanei_thread_is_forked ())
+ {
+ DBG (10, "reader_process started (forked)\n");
+ close (scanner->pipe);
+ scanner->pipe = -1;
+
+ sigfillset ( &ignore_set );
+ sigdelset ( &ignore_set, SIGTERM );
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset ( &ignore_set, SIGUSR2 );
+#endif
+ sigprocmask( SIG_SETMASK, &ignore_set, 0 );
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+ }
+ else
+ {
+ DBG (10, "reader_process started (as thread)\n");
+ }
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+
+ fp = fdopen ( scanner->reader_fds, "w");
+ if (!fp)
+ {
+ DBG (1, "reader_process: couldn't open pipe!\n");
+ return 1;
+ }
+
+ DBG (10, "reader_process: starting to READ data\n");
+
+ data_left = scan_bytes_per_line (scanner) *
+ lines_per_scan (scanner);
+
+ /*scanner->row_bufsize = sanei_scsi_max_request_size;*/
+ coolscan_trim_rowbufsize (scanner); /* trim bufsize */
+
+ DBG (10, "reader_process: reading %u bytes in blocks of %u bytes\n",
+ data_left, scanner->row_bufsize);
+
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = sigterm_handler;
+ sigaction (SIGTERM, &act, 0);
+ /* wait_scanner(scanner); */
+ do
+ {
+ data_to_read = (data_left < scanner->row_bufsize) ?
+ data_left : scanner->row_bufsize;
+
+ data_to_write=data_to_read;
+
+ status = coolscan_read_data_block (scanner
+ ,R_datatype_imagedata,data_to_read);
+ if (status == 0)
+ {
+ continue;
+ }
+ if (status == -1)
+ {
+ DBG (1, "reader_process: unable to get image data from scanner!\n");
+ fclose (fp);
+ return (-1);
+ }
+
+ if (scanner->LS == 1) { /* mirror image for LS-1000 */
+ bpl = scan_bytes_per_line(scanner);
+ linesPerBuf = data_to_read / bpl;
+
+ for (line = 0, lineOffset = 0; line < linesPerBuf;
+ line++, lineOffset += bpl ) {
+
+ if (scanner->colormode == RGB) {
+ for (j = 0; j < bpl/2 ; j += 3) {
+ r_data=scanner->buffer[lineOffset + j];
+ g_data=scanner->buffer[lineOffset + j + 1];
+ b_data=scanner->buffer[lineOffset + j + 2];
+
+ scanner->buffer[lineOffset + j] =
+ scanner->buffer[lineOffset + bpl -1 - j - 2 ];
+ scanner->buffer[lineOffset + j + 1] =
+ scanner->buffer[lineOffset + bpl -1 - j - 1 ];
+ scanner->buffer[lineOffset + j + 2] =
+ scanner->buffer[lineOffset + bpl -1 - j ];
+
+ scanner->buffer[lineOffset + bpl -1 - j - 2 ] = r_data;
+ scanner->buffer[lineOffset + bpl -1 - j - 1] = g_data;
+ scanner->buffer[lineOffset + bpl -1 - j] = b_data;
+ }
+ }
+ else {
+ for (j = 0; j < bpl/2; j++) {
+ r_data=scanner->buffer[lineOffset + j];
+ scanner->buffer[lineOffset + j] =
+ scanner->buffer[lineOffset + bpl - 1 - j];
+ scanner->buffer[lineOffset + bpl - 1 - j] = r_data;
+ }
+ }
+ }
+ }
+ if(scanner->colormode==RGBI)
+ { /* Correct Infrared Channel */
+ if(scanner->bits_per_color>8)
+ {
+ RGBIfix16(scanner, (unsigned short * ) scanner->buffer,
+ (unsigned short * )scanner->obuffer,
+ data_to_read/8,scanner->lutr,
+ scanner->lutg,scanner->lutb,scanner->luti);
+ }
+ else
+ {
+ RGBIfix(scanner,scanner->buffer,scanner->obuffer,
+ data_to_read/4,scanner->lutr,
+ scanner->lutg,scanner->lutb,scanner->luti);
+ }
+ }
+ else if((scanner->colormode==GREYSCALE)&&(scanner->LS>=2))
+ { /* Convert to Grey */
+ data_to_write/=3;
+ rgb2g(scanner->buffer,scanner->obuffer,data_to_write);
+ }
+ else
+ { /* or just copy */
+ memcpy (scanner->obuffer, scanner->buffer,data_to_read);
+ }
+ if((!scanner->low_byte_first)&&(scanner->bits_per_color>8))
+ { for(i=0;i<data_to_write;i++) /* inverse byteorder */
+ { h=scanner->obuffer[i];
+ scanner->obuffer[i]=scanner->obuffer[i+1];
+ i++;
+ scanner->obuffer[i]=h;
+ }
+ }
+ fwrite (scanner->obuffer, 1, data_to_write, fp);
+ fflush (fp);
+ data_left -= data_to_read;
+ DBG (10, "reader_process: buffer of %d bytes read; %d bytes to go\n",
+ data_to_read, data_left);
+ }
+ while (data_left);
+
+ fclose (fp);
+
+ DBG (10, "reader_process: finished reading data\n");
+
+ return 0;
+}
+
+static SANE_Status
+init_options (Coolscan_t * scanner)
+{
+ int i;
+ int bit_depths;
+
+ DBG (10, "init_options\n");
+
+ memset (scanner->opt, 0, sizeof (scanner->opt));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ scanner->opt[i].size = sizeof (SANE_Word);
+ scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ /* "Mode" group: */
+ scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ scanner->opt[OPT_MODE_GROUP].desc = "";
+ scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_MODE_GROUP].cap = 0;
+ scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ if(scanner->LS<2)
+ { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS20);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS20;
+ }
+ else
+ { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS30);
+ scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS30;
+ }
+
+ /* source */
+ source_list[0] = "Slide";
+ source_list[1] = "Automatic Slide Feeder";
+ source_list[2] = NULL;
+ if (!scanner->autofeeder)
+ {
+ scanner->opt[OPT_SOURCE].cap = SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_SOURCE].size = max_string_size (source_list);
+ scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
+
+ /* negative */
+ scanner->opt[OPT_TYPE].name = "type";
+ scanner->opt[OPT_TYPE].title = "Film type";
+ scanner->opt[OPT_TYPE].desc =
+ "Select the film type (positive (slide) or negative)";
+ scanner->opt[OPT_TYPE].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_TYPE].size = max_string_size (type_list);
+ scanner->opt[OPT_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_TYPE].constraint.string_list = type_list;
+
+ scanner->opt[OPT_PRESCAN].name = "prescan";
+ scanner->opt[OPT_PRESCAN].title = "Prescan";
+ scanner->opt[OPT_PRESCAN].desc =
+ "Perform a prescan during preview";
+ scanner->opt[OPT_PRESCAN].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_PRESCAN].unit = SANE_UNIT_NONE;
+
+ scanner->opt[OPT_PRESCAN_NOW].name = "prescan now";
+ scanner->opt[OPT_PRESCAN_NOW].title = "Prescan now";
+ scanner->opt[OPT_PRESCAN_NOW].desc =
+ "Perform a prescan now";
+ scanner->opt[OPT_PRESCAN_NOW].type = SANE_TYPE_BUTTON;
+ scanner->opt[OPT_PRESCAN_NOW].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_PRESCAN_NOW].size = 0;
+ scanner->opt[OPT_PRESCAN_NOW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ scanner->opt[OPT_PRESCAN_NOW].constraint_type = SANE_CONSTRAINT_NONE;
+ scanner->opt[OPT_PRESCAN_NOW].constraint.string_list = 0;
+
+ /* bit depth */
+
+ bit_depths=0;
+ bit_depth_list[++bit_depths] = 8;
+ if (scanner->LS==2)
+ {
+ bit_depth_list[++bit_depths] = 10;
+ }
+ if (scanner->LS==3)
+ {
+ bit_depth_list[++bit_depths] = 12;
+ }
+
+ bit_depth_list[0] = bit_depths;
+
+ scanner->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ scanner->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ scanner->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
+ scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list;
+
+ /* resolution */
+ scanner->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ scanner->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_RESOLUTION].constraint.word_list = resolution_list;
+
+ scanner->opt[OPT_PREVIEW_RESOLUTION].name = "preview-resolution";
+ scanner->opt[OPT_PREVIEW_RESOLUTION].title = "Preview resolution";
+ scanner->opt[OPT_PREVIEW_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].type = SANE_TYPE_INT;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].unit = SANE_UNIT_DPI;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ scanner->opt[OPT_PREVIEW_RESOLUTION].constraint.word_list = resolution_list;
+
+ /* "Geometry" group: */
+ scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
+ scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_X].constraint.range = &(scanner->x_range);
+
+ /* top-left y */
+ scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_TL_Y].constraint.range = &(scanner->y_range);
+
+ /* bottom-right x */
+ scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_X].constraint.range = &(scanner->x_range);
+
+ /* bottom-right y */
+ scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BR_Y].constraint.range = &(scanner->y_range);
+
+
+ /* ------------------------------ */
+
+ /* "Enhancement" group: */
+ scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+
+ scanner->opt[OPT_GAMMA_BIND].name = "gamma-bind";
+ scanner->opt[OPT_GAMMA_BIND].title = "Gamma bind";
+ scanner->opt[OPT_GAMMA_BIND].desc =
+ "Use same gamma correction for all colours";
+ scanner->opt[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_GAMMA_BIND].unit = SANE_UNIT_NONE;
+
+
+ scanner->opt[OPT_ANALOG_GAMMA].name = "analog_gamma";
+ scanner->opt[OPT_ANALOG_GAMMA].title = "Analog Gamma";
+ scanner->opt[OPT_ANALOG_GAMMA].desc = "Analog Gamma";
+ scanner->opt[OPT_ANALOG_GAMMA].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
+ if (!scanner->analoggamma)
+ {
+ scanner->opt[OPT_ANALOG_GAMMA].cap = SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_AVERAGING].name = "averaging";
+ scanner->opt[OPT_AVERAGING].title = "Averaging";
+ scanner->opt[OPT_AVERAGING].desc = "Averaging";
+ scanner->opt[OPT_AVERAGING].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_AVERAGING].unit = SANE_UNIT_NONE;
+
+
+ scanner->opt[OPT_RGB_CONTROL].name = "rgb-control";
+ scanner->opt[OPT_RGB_CONTROL].title = "RGB control";
+ scanner->opt[OPT_RGB_CONTROL].desc =
+ "toggles brightness/contrast control over individual colours";
+ scanner->opt[OPT_RGB_CONTROL].type = SANE_TYPE_BOOL;
+ scanner->opt[OPT_RGB_CONTROL].unit = SANE_UNIT_NONE;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_RGB_CONTROL].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+
+ /* brightness */
+ scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_R_BRIGHTNESS].name = "red-brightness";
+ scanner->opt[OPT_R_BRIGHTNESS].title = "Red brightness";
+ scanner->opt[OPT_R_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_R_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_R_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_BRIGHTNESS].constraint.range = &brightness_range;
+ scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_G_BRIGHTNESS].name = "green-brightness";
+ scanner->opt[OPT_G_BRIGHTNESS].title = "Green brightness";
+ scanner->opt[OPT_G_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_G_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_G_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_BRIGHTNESS].constraint.range = &brightness_range;
+ scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_B_BRIGHTNESS].name = "blue-brightness";
+ scanner->opt[OPT_B_BRIGHTNESS].title = "Blue brightness";
+ scanner->opt[OPT_B_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ scanner->opt[OPT_B_BRIGHTNESS].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_B_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_BRIGHTNESS].constraint.range = &brightness_range;
+ scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ /* contrast */
+ scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_CONTRAST].constraint.range = &contrast_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_R_CONTRAST].name = "red-contrast";
+ scanner->opt[OPT_R_CONTRAST].title = "Red contrast";
+ scanner->opt[OPT_R_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_R_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_R_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_CONTRAST].constraint.range = &contrast_range;
+ scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_G_CONTRAST].name = "green-contrast";
+ scanner->opt[OPT_G_CONTRAST].title = "Green contrast";
+ scanner->opt[OPT_G_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_G_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_G_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_CONTRAST].constraint.range = &contrast_range;
+ scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_B_CONTRAST].name = "blue-contrast";
+ scanner->opt[OPT_B_CONTRAST].title = "Blue contrast";
+ scanner->opt[OPT_B_CONTRAST].desc = SANE_DESC_CONTRAST;
+ scanner->opt[OPT_B_CONTRAST].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_CONTRAST].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_B_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_CONTRAST].constraint.range = &contrast_range;
+ scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_EXPOSURE].name = "exposure";
+ scanner->opt[OPT_EXPOSURE].title = "Exposure";
+ scanner->opt[OPT_EXPOSURE].desc = "";
+ scanner->opt[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_EXPOSURE].constraint.range = &exposure_range;
+
+ scanner->opt[OPT_R_EXPOSURE].name = "red-exposure";
+ scanner->opt[OPT_R_EXPOSURE].title = "Red exposure";
+ scanner->opt[OPT_R_EXPOSURE].desc = "";
+ scanner->opt[OPT_R_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_R_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_R_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_EXPOSURE].constraint.range = &exposure_range;
+
+ scanner->opt[OPT_G_EXPOSURE].name = "green-exposure";
+ scanner->opt[OPT_G_EXPOSURE].title = "Green exposure";
+ scanner->opt[OPT_G_EXPOSURE].desc = "";
+ scanner->opt[OPT_G_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_G_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_EXPOSURE].constraint.range = &exposure_range;
+ scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_B_EXPOSURE].name = "blue-exposure";
+ scanner->opt[OPT_B_EXPOSURE].title = "Blue exposre";
+ scanner->opt[OPT_B_EXPOSURE].desc = "";
+ scanner->opt[OPT_B_EXPOSURE].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ scanner->opt[OPT_B_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_EXPOSURE].constraint.range = &exposure_range;
+ scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ }
+
+ scanner->opt[OPT_R_SHIFT].name = "red-shift";
+ scanner->opt[OPT_R_SHIFT].title = "Red shift";
+ scanner->opt[OPT_R_SHIFT].desc = "";
+ scanner->opt[OPT_R_SHIFT].type = SANE_TYPE_INT;
+ scanner->opt[OPT_R_SHIFT].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_R_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_R_SHIFT].constraint.range = &shift_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_R_SHIFT].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_G_SHIFT].name = "green-shift";
+ scanner->opt[OPT_G_SHIFT].title = "Green shift";
+ scanner->opt[OPT_G_SHIFT].desc = "";
+ scanner->opt[OPT_G_SHIFT].type = SANE_TYPE_INT;
+ scanner->opt[OPT_G_SHIFT].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_G_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_G_SHIFT].constraint.range = &shift_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_G_SHIFT].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+ scanner->opt[OPT_B_SHIFT].name = "blue-shift";
+ scanner->opt[OPT_B_SHIFT].title = "Blue shift";
+ scanner->opt[OPT_B_SHIFT].desc = "";
+ scanner->opt[OPT_B_SHIFT].type = SANE_TYPE_INT;
+ scanner->opt[OPT_B_SHIFT].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_B_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_B_SHIFT].constraint.range = &shift_range;
+ if(scanner->LS>=2)
+ { scanner->opt[OPT_B_SHIFT].cap |= SANE_CAP_INACTIVE;
+ }
+
+ /* R+G+B gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ if (scanner->LS == 1)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap = SANE_CAP_INACTIVE;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ /* red gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR_R].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ /* green gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR_G].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ /* blue gamma vector */
+ scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ switch(scanner->LS)
+ { case 0:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_8;
+ scanner->lutlength=2048;
+ break;
+ case 1:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_9;
+ scanner->lutlength=512;
+ break;
+ case 2:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_10;
+ scanner->lutlength=1024;
+ break;
+ case 3:
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_12;
+ scanner->lutlength=4096;
+ break;
+ }
+ scanner->opt[OPT_GAMMA_VECTOR_B].size = scanner->lutlength * sizeof (SANE_Word);
+ scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+
+
+ /* ------------------------------ */
+
+ /* "Advanced" group: */
+ scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
+ scanner->opt[OPT_ADVANCED_GROUP].desc = "";
+ scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
+ scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
+ scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* preview */
+ scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+
+ /* Autofocus */
+ scanner->opt[OPT_AUTOFOCUS].name = "Autofocus";
+ scanner->opt[OPT_AUTOFOCUS].title ="Autofocus";
+ scanner->opt[OPT_AUTOFOCUS].desc = "When to do autofocussing";
+ scanner->opt[OPT_AUTOFOCUS].type = SANE_TYPE_STRING;
+ scanner->opt[OPT_AUTOFOCUS].size = max_string_size (autofocus_mode_list);
+ scanner->opt[OPT_AUTOFOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ scanner->opt[OPT_AUTOFOCUS].constraint.string_list = autofocus_mode_list;
+
+ scanner->opt[OPT_IRED_RED].name = "IRED cor. red";
+ scanner->opt[OPT_IRED_RED].title ="IRED cor. red";
+ scanner->opt[OPT_IRED_RED].desc = "Correction of infrared from red";
+ scanner->opt[OPT_IRED_RED].type = SANE_TYPE_INT;
+ scanner->opt[OPT_IRED_RED].unit = SANE_UNIT_NONE;
+ scanner->opt[OPT_IRED_RED].constraint_type = SANE_CONSTRAINT_RANGE;
+ scanner->opt[OPT_IRED_RED].constraint.range = &gamma_range_8;
+ scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_ADVANCED;
+ if(scanner->LS<2)
+ { scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_INACTIVE;
+ }
+
+
+
+ /* scanner->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; */
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize;
+
+ DBG_INIT ();
+ sanei_thread_init ();
+
+ DBG (10, "sane_init\n");
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (COOLSCAN_CONFIG_FILE);
+ if (!fp)
+ {
+ attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#')
+ continue; /* ignore line comments */
+ len = strlen (dev_name);
+
+ if (!len)
+ continue; /* ignore empty lines */
+
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ /*attach_scanner (dev_name, 0);*/
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Coolscan_t *dev, *next;
+
+ DBG (10, "sane_exit\n");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free (dev->devicename);
+ free (dev->buffer);
+ free (dev->obuffer);
+ free (dev);
+ }
+}
+
+/* ----------------------------- SANE GET DEVICES -------------------------- */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only)
+{
+
+ static const SANE_Device **devlist = 0;
+ Coolscan_t *dev;
+ int i;
+
+ local_only = local_only;
+
+ DBG (10, "sane_get_devices\n");
+
+ if (devlist)
+ free (devlist);
+
+ devlist = calloc (num_devices + 1, sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Coolscan_t *dev;
+ SANE_Status status;
+
+ DBG (10, "sane_open\n");
+
+ if (devicename[0])
+ { /* search for devicename */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_scanner (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ dev = first_dev; /* empty devicname -> use first device */
+ }
+
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ dev->sfd = -1;
+ dev->pipe = -1;
+ dev->scanning = SANE_FALSE;
+
+ init_options (dev);
+ *handle = dev;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (10, "sane_close\n");
+ if (((Coolscan_t *) handle)->scanning)
+ do_cancel (handle);
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_get_option_descriptor %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ return &scanner->opt[option];
+}
+
+/*
+ static void
+ worddump(char *comment, SANE_Word * p, int l)
+ {
+ int i;
+ char line[128];
+ char *ptr;
+
+ DBG (5, "%s\n", comment);
+ ptr = line;
+ for (i = 0; i < l; i++, p++)
+ {
+ if ((i % 8) == 0)
+ {
+ if (ptr != line)
+ {
+ *ptr = '\0';
+ DBG (5, "%s\n", line);
+ ptr = line;
+ }
+ sprintf (ptr, "%3.3d:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %4.4d", *p);
+ ptr += 5;
+ }
+ *ptr = '\0';
+ DBG (5, "%s\n", line);
+ }
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val,
+ SANE_Int * info)
+{
+ Coolscan_t *scanner = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ if (info)
+ *info = 0;
+
+ if (scanner->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+
+ cap = scanner->opt[option].cap;
+
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (10, "sane_control_option %d, get value\n", option);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_TL_X:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tlx));
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tly));
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->brx));
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->bry));
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW:
+ *(SANE_Word *) val = scanner->preview;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOFOCUS:
+ switch(scanner->autofocus)
+ { case AF_NEVER: strcpy (val,neverStr);
+ break;
+ case AF_PREVIEW:strcpy (val,previewStr);
+ break;
+ case AF_SCAN:if(scanner->LS>=2) strcpy (val,scanStr);
+ break;
+ case AF_PREANDSCAN:if(scanner->LS>=2) strcpy (val,preandscanStr);
+ break;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) val = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION:
+ *(SANE_Word *) val = resDivToVal (scanner->x_nres);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW_RESOLUTION:
+ *(SANE_Word *) val = resDivToVal (scanner->x_p_nres);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BIT_DEPTH:
+ *(SANE_Word *) val = scanner->bits_per_color;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast_R - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast_G - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_CONTRAST:
+ *(SANE_Word *) val = scanner->contrast_B - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness_R - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness_G - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_BRIGHTNESS:
+ *(SANE_Word *) val = scanner->brightness_B - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_R * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_R * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_G * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_EXPOSURE:
+ *(SANE_Word *) val = scanner->exposure_B * 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_SHIFT:
+ *(SANE_Word *) val = scanner->shift_R - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_G_SHIFT:
+ *(SANE_Word *) val = scanner->shift_G - 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_B_SHIFT:
+ *(SANE_Word *) val = scanner->shift_B - 128;
+ return SANE_STATUS_GOOD;
+
+
+ case OPT_IRED_RED:
+ *(SANE_Word *) val = scanner->ired_red;
+ return SANE_STATUS_GOOD;
+
+ /* string options: */
+ case OPT_TYPE:
+ strcpy (val, ((scanner->negative) ? negativeStr : positiveStr));
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ switch(scanner->colormode)
+ { case RGB: strcpy (val,colorStr);
+ break;
+ case GREYSCALE:strcpy (val,grayStr);
+ break;
+ case RGBI:if(scanner->LS>=2) strcpy (val,rgbiStr);
+ else strcpy (val,colorStr);
+ break;
+ case IRED:if(scanner->LS>=2) strcpy (val,iredStr);
+ else strcpy (val,grayStr);
+ break;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN:
+ *(SANE_Word *) val = (scanner->prescan) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN_NOW:
+ return SANE_STATUS_GOOD;
+
+ case OPT_RGB_CONTROL:
+ *(SANE_Word *) val = (scanner->rgb_control) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_BIND:
+ *(SANE_Word *) val = (scanner->gamma_bind) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_ANALOG_GAMMA:
+ *(SANE_Word *) val =
+ (scanner->analog_gamma_r) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AVERAGING:
+ *(SANE_Word *) val = (scanner->averaging) ? SANE_TRUE : SANE_FALSE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR:
+ memcpy (val, scanner->gamma, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR_R:
+ memcpy (val, scanner->gamma_r, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR_G:
+ memcpy (val, scanner->gamma_g, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, scanner->gamma_b, scanner->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if (strcmp (val, "Automatic Slide Feeder") == 0)
+ {
+ /* Feed/Discharge/update filename/etc */
+ }
+ else
+ {
+ /* Reset above */
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ }
+
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (10, "sane_control_option %d, set value\n", option);
+
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (scanner->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ case OPT_GAMMA_BIND:
+ scanner->gamma_bind = (*(SANE_Word *) val == SANE_TRUE);
+ if (scanner->LS != 1)
+ {
+ if (scanner->gamma_bind)
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+
+ }
+ else
+ {
+ scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+
+ }
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_ANALOG_GAMMA:
+ scanner->analog_gamma_r = scanner->analog_gamma_g =
+ scanner->analog_gamma_b = (*(SANE_Word *) val == SANE_TRUE);
+ return SANE_STATUS_GOOD;
+
+ case OPT_AVERAGING:
+ scanner->averaging = (*(SANE_Word *) val == SANE_TRUE);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN:
+ scanner->prescan = (*(SANE_Word *) val == SANE_TRUE);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PRESCAN_NOW:
+ do_prescan_now(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BIT_DEPTH:
+ scanner->bits_per_color=(*(SANE_Word *)val);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+
+ case OPT_RGB_CONTROL:
+ scanner->rgb_control = (*(SANE_Word *) val == SANE_TRUE);
+ if (scanner->rgb_control)
+ {
+ scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->contrast_R = 128;
+ scanner->contrast_G = 128;
+ scanner->contrast_B = 128;
+ scanner->brightness_R = 128;
+ scanner->brightness_G = 128;
+ scanner->brightness_B = 128;
+ scanner->exposure_R = 50;
+ scanner->exposure_G = 50;
+ scanner->exposure_B = 50;
+ }
+ else
+ {
+ scanner->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+
+ scanner->contrast = 128;
+ scanner->brightness = 128;
+ scanner->exposure_R = 50;
+ scanner->exposure_G = 50;
+ scanner->exposure_B = 50;
+
+ scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
+
+ scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION:
+ scanner->y_nres = scanner->x_nres =
+ resValToDiv (*(SANE_Word *) val);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW_RESOLUTION:
+ scanner->y_p_nres = scanner->x_p_nres =
+ resValToDiv (*(SANE_Word *) val);
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ scanner->tlx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ scanner->tly = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ scanner->brx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ scanner->bry = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_NUM_OPTS:
+ return SANE_STATUS_GOOD;
+
+ case OPT_PREVIEW:
+ scanner->preview = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOFOCUS:
+ if(strcmp(val,neverStr)==0)
+ { scanner->autofocus=AF_NEVER;
+ }
+ if(strcmp(val,previewStr)==0)
+ { scanner->autofocus=AF_PREVIEW;
+ }
+ if(strcmp(val,scanStr)==0)
+ { scanner->autofocus=AF_SCAN;
+ }
+ if(strcmp(val,preandscanStr)==0)
+ { scanner->autofocus=AF_PREANDSCAN;;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ scanner->contrast = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_R_CONTRAST:
+ scanner->contrast_R = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_G_CONTRAST:
+ scanner->contrast_G = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_B_CONTRAST:
+ scanner->contrast_B = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ scanner->brightness = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_R_BRIGHTNESS:
+ scanner->brightness_R = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_G_BRIGHTNESS:
+ scanner->brightness_G = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_B_BRIGHTNESS:
+ scanner->brightness_B = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_EXPOSURE:
+ scanner->exposure_R = *(SANE_Word *) val / 2;
+ scanner->exposure_G = *(SANE_Word *) val / 2;
+ scanner->exposure_B = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+ case OPT_R_EXPOSURE:
+ scanner->exposure_R = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+ case OPT_G_EXPOSURE:
+ scanner->exposure_G = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+ case OPT_B_EXPOSURE:
+ scanner->exposure_B = *(SANE_Word *) val / 2;
+ return SANE_STATUS_GOOD;
+
+ case OPT_R_SHIFT:
+ scanner->shift_R = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_G_SHIFT:
+ scanner->shift_G = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+ case OPT_B_SHIFT:
+ scanner->shift_B = *(SANE_Word *) val + 128;
+ return SANE_STATUS_GOOD;
+
+ case OPT_IRED_RED:
+ scanner->ired_red= *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ scanner->asf = (strcmp (val, "Automatic...") == 0);
+ return SANE_STATUS_GOOD;
+
+ case OPT_TYPE:
+ scanner->negative = (strcmp (val, negativeStr) == 0);
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+ case OPT_MODE:
+ if(strcmp(val,colorStr)==0)
+ { scanner->colormode=RGB;
+ scanner->colormode_p=RGB;
+ }
+ if(strcmp(val,grayStr)==0)
+ { scanner->colormode=GREYSCALE;
+ scanner->colormode_p=GREYSCALE;
+ }
+ if(strcmp(val,rgbiStr)==0)
+ { scanner->colormode=RGBI;
+ scanner->colormode_p=RGB;
+ }
+ if(strcmp(val,iredStr)==0)
+ { scanner->colormode=IRED;
+ scanner->colormode_p=GREYSCALE;
+ }
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR:
+ memcpy (scanner->gamma, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_R:
+ memcpy (scanner->gamma_r, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_G:
+ memcpy (scanner->gamma_g, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (scanner->gamma_b, val, scanner->opt[option].size);
+ if(scanner->LS>2) Calc_fix_LUT(scanner);
+ return SANE_STATUS_GOOD;
+
+ } /* switch */
+ } /* else */
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_get_parameters");
+ switch(scanner->colormode)
+ { case RGB:
+ params->format = SANE_FRAME_RGB;
+ break;
+#ifdef HAS_IRED
+ case RGBI:
+ params->format = SANE_FRAME_RGBA;
+ break;
+#endif /* HAS_RGBI */
+ case GREYSCALE:
+ params->format = SANE_FRAME_GRAY;
+ break;
+ }
+
+ params->depth = scanner->bits_per_color>8?16:8;
+ params->pixels_per_line = pixels_per_line (scanner);
+ params->lines = lines_per_scan (scanner);
+ params->bytes_per_line = write_bytes_per_line (scanner);
+ params->last_frame = 1;
+ return SANE_STATUS_GOOD;
+}
+static int
+swap_res (Coolscan_t * s)
+{
+ if (s->preview)
+ {
+ /* swap preview/scan resolutions */
+ int xres, yres, cmode;
+ xres = s->x_nres;
+ yres = s->y_nres;
+ s->x_nres = s->x_p_nres;
+ s->y_nres = s->y_p_nres;
+
+ s->x_p_nres = xres;
+ s->y_p_nres = yres;
+ cmode=s->colormode;
+ s->colormode=s->colormode_p;
+ s->colormode_p=cmode;
+ }
+ return 0;
+}
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Coolscan_t *scanner = handle;
+ int fds[2];
+
+ DBG (10, "sane_start\n");
+ if (scanner->scanning == SANE_TRUE)
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if (scanner->sfd < 0)
+ { /* first call */
+ if (sanei_scsi_open (scanner->sane.name,
+ &(scanner->sfd),
+ sense_handler, 0) != SANE_STATUS_GOOD)
+ {
+ DBG (1, "sane_start: open of %s failed:\n",
+ scanner->sane.name);
+ return SANE_STATUS_INVAL;
+ }
+ }
+ scanner->scanning = SANE_TRUE;
+
+
+ if (coolscan_check_values (scanner) != 0)
+ { /* Verify values */
+ DBG (1, "ERROR: invalid scan-values\n");
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_INVAL;
+ }
+
+ if (coolscan_grab_scanner (scanner))
+ {
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ DBG (5, "WARNING: unable to reserve scanner: device busy\n");
+ scanner->scanning = SANE_FALSE;
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* hoho, step 2c, -perm */
+ coolscan_object_feed (scanner);
+
+ swap_res (scanner);
+ if (!scanner->preview)
+ { if(scanner->autofocus & 0x02)
+ { coolscan_autofocus (scanner);
+ }
+ }
+ else
+ {
+ if(scanner->autofocus & 0x01)
+ { coolscan_autofocus (scanner);
+ }
+ if (scanner->prescan) {
+ prescan (scanner);
+ if(scanner->LS<2)
+ { get_internal_info(scanner);
+ }
+ coolscan_get_window_param (scanner,1);
+ }
+ }
+ /*read_LUT(scanner); */
+ if(scanner->LS<2)
+ { send_LUT (scanner);
+ coolscan_set_window_param (scanner, 0);
+ coolscan_get_window_param (scanner,0);
+ coolscan_start_scan (scanner);
+ }
+ else
+ { coolscan_set_window_param (scanner, 0);
+ send_LUT (scanner);
+ Calc_fix_LUT(scanner);
+ coolscan_start_scan (scanner);
+ wait_scanner (scanner);
+ coolscan_get_window_param (scanner,0);
+ }
+
+ DBG (10, "bytes per line = %d\n", scan_bytes_per_line (scanner));
+ DBG (10, "pixels_per_line = %d\n", pixels_per_line (scanner));
+ DBG (10, "lines = %d\n", lines_per_scan (scanner));
+ DBG (10, "negative = %d\n", scanner->negative);
+ DBG (10, "brightness (halftone) = %d\n", scanner->brightness);
+ DBG (10, "contrast (halftone) = %d\n", scanner->contrast);
+ DBG (10, "fast preview function = %d\n", scanner->preview);
+
+ /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
+ if (pipe (fds) < 0)
+ {
+ DBG (1, "ERROR: could not create pipe\n");
+
+ swap_res (scanner);
+ scanner->scanning = SANE_FALSE;
+ coolscan_give_scanner (scanner);
+ sanei_scsi_close (scanner->sfd);
+ scanner->sfd = -1;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scanner->pipe = fds[0];
+ scanner->reader_fds = fds[1];
+ scanner->reader_pid = sanei_thread_begin( reader_process, (void*)scanner );
+ if (scanner->reader_pid == -1)
+ {
+ DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
+ strerror (errno));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (sanei_thread_is_forked ())
+ {
+ close (scanner->reader_fds);
+ scanner->reader_fds = -1;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ Coolscan_t *scanner = handle;
+ ssize_t nread;
+
+ *len = 0;
+
+ nread = read (scanner->pipe, buf, max_len);
+ DBG (10, "sane_read: read %ld bytes\n", (long) nread);
+
+ if (!(scanner->scanning))
+ {
+ return do_cancel (scanner);
+ }
+
+ if (nread < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ do_cancel (scanner);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *len = nread;
+
+ if (nread == 0)
+ return do_eof (scanner); /* close pipe */
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Coolscan_t *s = handle;
+
+ if (s->reader_pid != -1)
+ {
+ sanei_thread_kill ( s->reader_pid );
+ sanei_thread_waitpid( s->reader_pid, NULL );
+ s->reader_pid = -1;
+ }
+ swap_res (s);
+ s->scanning = SANE_FALSE;
+}
+
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_set_io_mode: non_blocking=%d\n", non_blocking);
+
+ if (!scanner->scanning)
+ return SANE_STATUS_INVAL;
+
+ if (fcntl (scanner->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Coolscan_t *scanner = handle;
+
+ DBG (10, "sane_get_select_fd\n");
+
+ if (!scanner->scanning)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ *fd = scanner->pipe;
+
+ return SANE_STATUS_GOOD;
+}