From 6e9c41a892ed0e0da326e0278b3221ce3f5713b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 6 Oct 2014 14:00:40 +0200 Subject: Initial import of sane-backends version 1.0.24-1.2 --- backend/coolscan.c | 4195 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4195 insertions(+) create mode 100644 backend/coolscan.c (limited to 'backend/coolscan.c') 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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;imanumima;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;ilutlength;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;x255*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>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>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;xthresi) + { 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>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;iobuffer[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; +} -- cgit v1.2.3