summaryrefslogtreecommitdiff
path: root/backend/nec.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/nec.c')
-rw-r--r--backend/nec.c3717
1 files changed, 3717 insertions, 0 deletions
diff --git a/backend/nec.c b/backend/nec.c
new file mode 100644
index 0000000..c7af955
--- /dev/null
+++ b/backend/nec.c
@@ -0,0 +1,3717 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000-2001 Kazuya Fukuda, based on sharp.c, which is
+ based on canon.c.
+
+ 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 NEC flatbed scanners. */
+
+/*
+ Version 0.12
+ - Remove references to sharp backend (grep for "JX").
+ - Check for HAVE_SYS_SHM_H before including sys/shm.h and
+ disable shared memory support if necessary.
+ - free devlist allocated in sane_get_devices() in sane_exit()
+ - resolution setting bug fixed(PC-IN500/4C 10dpi step)
+ - remove resolution list
+ Version 0.11
+ - get_data_buffer_status is not called in sane_get_parameter and
+ sane_read_direct, sane_read_shuffled.
+ - change some #include <> to ""
+ Version 0.10
+ - First release!
+ - suppoted scanner
+ PC-IN500/4C available
+ MultiReder 300U/300S series not available
+ MultiReder 600U/600S series not available
+ MultiReader PetiScan series not available
+*/
+#include "../include/sane/config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_scsi.h"
+
+/* QUEUEDEBUG should be undefined unless you want to play
+ with the sanei_scsi.c under Linux and/or with the Linux's SG driver,
+ or your suspect problems with command queueing
+*/
+#define QUEUEDEBUG
+/*#define DEBUG*/
+#ifdef DEBUG
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+/* USE_FORK: fork a special reader process
+ disable shared memory support.
+*/
+#if 0
+#ifdef HAVE_SYS_SHM_H
+#define USE_FORK
+#endif
+#endif
+
+#ifdef USE_FORK
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#endif /* USE_FORK */
+
+#ifndef USE_CUSTOM_GAMMA
+#define USE_CUSTOM_GAMMA
+#endif
+#ifndef USE_COLOR_THRESHOLD
+#define USE_COLOR_THRESHOLD
+#endif
+/* enable a short list of some standard resolutions. XSane provides
+ its own resolution list; therefore its is generally not reasonable
+ to enable this list, if you mainly using XSane. But it might be handy
+ if you are working with xscanimage
+*/
+/* #define USE_RESOLUTION_LIST */
+
+#define BACKEND_NAME nec
+#include "../include/sane/sanei_backend.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define DEFAULT_MUD_1200 1200
+
+#define PIX_TO_MM(x, mud) ((x) * 25.4 / mud)
+#define MM_TO_PIX(x, mud) ((x) * mud / 25.4)
+
+#include "../include/sane/sanei_config.h"
+#define NEC_CONFIG_FILE "nec.conf"
+
+#include "nec.h"
+
+static int num_devices = 0;
+static NEC_Device *first_dev = NULL;
+static NEC_Scanner *first_handle = NULL;
+static const SANE_Device **devlist = 0;
+
+typedef enum
+ {
+ MODES_LINEART = 0,
+ MODES_GRAY,
+ MODES_COLOR,
+ MODES_LINEART_COLOR
+ }
+Modes;
+
+#define M_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define M_GRAY SANE_VALUE_SCAN_MODE_GRAY
+#define M_LINEART_COLOR "Lineart Color"
+#define M_COLOR SANE_VALUE_SCAN_MODE_COLOR
+static const SANE_String_Const mode_list[] =
+{
+#if 0
+ M_LINEART, M_GRAY, M_LINEART_COLOR, M_COLOR,
+#endif
+ M_LINEART, M_GRAY, M_COLOR,
+ 0
+};
+
+#define M_BILEVEL "none"
+#define M_BAYER "Dither Bayer"
+#define M_SPIRAL "Dither Spiral"
+#define M_DISPERSED "Dither Dispersed"
+#define M_ERRDIFFUSION "Error Diffusion"
+
+#define M_DITHER1 "Dither 1"
+#define M_DITHER2 "Dither 2"
+#define M_DITHER3 "Dither 3"
+#define M_DITHERUSER "User defined"
+
+static const SANE_String_Const halftone_list[] =
+{
+ M_BILEVEL, M_DITHER1, M_DITHER2, M_DITHER3,
+ 0
+};
+
+#define LIGHT_GREEN "green"
+#define LIGHT_RED "red"
+#define LIGHT_BLUE "blue"
+#define LIGHT_NONE "none"
+#define LIGHT_WHITE "white"
+
+static const SANE_String_Const light_color_list[] =
+{
+ LIGHT_GREEN, LIGHT_RED, LIGHT_BLUE, LIGHT_NONE,
+ 0
+};
+
+/* possible values for ADF/FSU selection */
+static SANE_String use_adf = "Automatic Document Feeder";
+static SANE_String use_fsu = "Transparency Adapter";
+static SANE_String use_simple = "Flatbed";
+
+#define HAVE_FSU 1
+#define HAVE_ADF 2
+
+/* The follow #defines are used in NEC_Scanner.adf_fsu_mode
+ and as indexes for the arrays x_ranges, y_ranges in NEC_Device
+*/
+#define SCAN_SIMPLE 0
+#define SCAN_WITH_FSU 1
+#define SCAN_WITH_ADF 2
+
+#define LOAD_PAPER 1
+#define UNLOAD_PAPER 0
+
+#define PAPER_MAX 10
+#define W_LETTER "11\"x17\""
+#define INVOICE "8.5\"x5.5\""
+static const SANE_String_Const paper_list_pcinxxx[] =
+{
+ "A3", "A4", "A5", "A6", "B4", "B5",
+ W_LETTER, "Legal", "Letter", INVOICE,
+ 0
+};
+
+static const SANE_String_Const paper_list_pcin500[] =
+{
+ "A4", "A5", "A6", "B5",
+ 0
+};
+
+
+#define CRT1 "CRT1"
+#define CRT2 "CRT2"
+#define PRINTER1 "PRINTER1"
+#define PRINTER2 "PRINTER2"
+#define NONE "NONE"
+/* #define CUSTOM "CUSTOM" */
+static const SANE_String_Const gamma_list[] =
+{
+ CRT1, CRT2, PRINTER1, PRINTER2, NONE,
+ 0
+};
+
+#if 0
+#define SPEED_NORMAL "Normal"
+#define SPEED_FAST "Fast"
+static const SANE_String_Const speed_list[] =
+{
+ SPEED_NORMAL, SPEED_FAST,
+ 0
+};
+#endif
+
+#ifdef USE_RESOLUTION_LIST
+#define RESOLUTION_MAX_PCINXXX 8
+static const SANE_String_Const resolution_list_pcinxxx[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "600", "Select",
+ 0
+};
+
+#define RESOLUTION_MAX_PCIN500 8
+static const SANE_String_Const resolution_list_pcin500[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "480", "Select",
+ 0
+};
+#endif
+
+#define EDGE_NONE "None"
+#define EDGE_MIDDLE "Middle"
+#define EDGE_STRONG "Strong"
+#define EDGE_BLUR "Blur"
+static const SANE_String_Const edge_emphasis_list[] =
+{
+ EDGE_NONE, EDGE_MIDDLE, EDGE_STRONG, EDGE_BLUR,
+ 0
+};
+
+#ifdef USE_CUSTOM_GAMMA
+static const SANE_Range u8_range =
+ {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+ };
+#endif
+
+static SANE_Status
+sense_handler(int fd, u_char *sense_buffer, void *ss)
+{
+ int sense_key;
+ NEC_Sense_Data *sdat = (NEC_Sense_Data *) ss;
+
+ fd = fd; /* silence compilation warnings */
+
+ #define add_sense_code sense_buffer[12]
+ #define add_sense_qual sense_buffer[13]
+
+ memcpy(sdat->sb, sense_buffer, 16);
+
+ DBG(10, "sense code: %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ sense_buffer[0], sense_buffer[1], sense_buffer[2], sense_buffer[3],
+ sense_buffer[4], sense_buffer[5], sense_buffer[6], sense_buffer[7],
+ sense_buffer[8], sense_buffer[9], sense_buffer[10], sense_buffer[11],
+ sense_buffer[12], sense_buffer[13], sense_buffer[14], sense_buffer[15]);
+
+ sense_key = sense_buffer[1] & 0x0F;
+ /* do we have additional information ? */
+ if (sense_buffer[7] >= 5)
+ {
+ if (sdat->model == PCIN500)
+ {
+ switch (sense_key)
+ {
+ case 0x02: /* not ready */
+ switch (add_sense_code)
+ {
+ case 0x80:
+ switch (add_sense_qual & 0xf0)
+ {
+ case 0x10:
+ DBG(1, "Scanner not ready: memory error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x20:
+ DBG(1, "Scanner not ready: hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x30:
+ DBG(1, "Scanner not ready: optical error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x40:
+ DBG(1, "Scanner not ready: optical error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x50:
+ DBG(1, "Scanner not ready: marker error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x60:
+ DBG(1, "Scanner not ready: mechanical error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x70:
+ DBG(1, "Scanner not ready: hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x80:
+ DBG(1, "Scanner not ready: hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x90:
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ case 0x03: /* medium error */
+ DBG(5, "medium error: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x04: /* hardware error */
+ DBG(1, "general hardware error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x05: /* illegal request */
+ DBG(10, "error: illegal request\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x06: /* unit attention */
+ DBG(5, "unit attention: exact reason not documented\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x0B: /* data remains */
+ DBG(5, "error: aborted command\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG(5, "error: sense code not documented\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+ return SANE_STATUS_IO_ERROR;
+}
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static u_char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< test_unit_ready ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+request_sense (int fd, void *sense_buf, size_t *sense_size)
+{
+ static u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, SENSE_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< request_sense ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_buf, sense_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+inquiry (int fd, void *inq_buf, size_t *inq_size)
+{
+ static u_char cmd[] = {INQUIRY, 0, 0, 0, INQUIRY_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< inquiry ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), inq_buf, inq_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status
+mode_select_mud (int fd, int mud)
+{
+ static u_char cmd[6 + MODEPARAM_LEN] =
+ {MODE_SELECT6, 0x10, 0, 0, MODEPARAM_LEN, 0};
+ mode_select_param *mp;
+ SANE_Status status;
+ DBG (11, "<< mode_select_mud ");
+
+ mp = (mode_select_param *)(cmd + 6);
+ memset (mp, 0, MODEPARAM_LEN);
+ mp->mode_param_header1 = 11;
+ mp->page_code = 3;
+ mp->page_length = 6;
+ mp->mud[0] = mud >> 8;
+ mp->mud[1] = mud & 0xFF;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+mode_select_adf_fsu (int fd, int mode)
+{
+ static u_char cmd[6 + MODE_SUBDEV_LEN] =
+ {MODE_SELECT6, 0x10, 0, 0, MODE_SUBDEV_LEN, 0};
+ mode_select_subdevice *mp;
+ SANE_Status status;
+ DBG (11, "<< mode_select_adf_fsu ");
+
+ mp = (mode_select_subdevice *)(cmd + 6);
+ memset (mp, 0, MODE_SUBDEV_LEN);
+ mp->page_code = 0x20;
+ mp->page_length = 26;
+ switch (mode)
+ {
+ case SCAN_SIMPLE:
+ mp->a_mode = 0x40;
+ mp->f_mode = 0x40;
+ break;
+ case SCAN_WITH_FSU:
+ mp->a_mode = 0;
+ mp->f_mode = 0x40;
+ break;
+ case SCAN_WITH_ADF:
+ mp->a_mode = 0x40;
+ mp->f_mode = 0;
+ break;
+ }
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status wait_ready(int fd);
+
+static SANE_Status
+mode_sense (int fd, void *modeparam_buf, size_t * modeparam_size,
+ int page)
+{
+ static u_char cmd[6] = {MODE_SENSE6, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< mode_sense ");
+ cmd[0] = 0x1a;
+ cmd[2] = page;
+ cmd[4] = *modeparam_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), modeparam_buf,
+ modeparam_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status
+scan (int fd)
+{
+ static u_char cmd[] = {SCAN, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< scan ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+send_diagnostics (int fd)
+{
+ static u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< send_diagnostics ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+set_window (int fd, window_param *wp, int len)
+{
+ static u_char cmd[10 + WINDOW_LEN] =
+ {SET_WINDOW, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ window_param *winp;
+ SANE_Status status;
+ DBG (11, "<< set_window ");
+
+ cmd[8] = len;
+ winp = (window_param *)(cmd + 10);
+ memset (winp, 0, WINDOW_LEN);
+ memcpy (winp, wp, len);
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+
+}
+
+static SANE_Status
+get_window (int fd, void *buf, size_t * buf_size)
+{
+ static u_char cmd[10] = {GET_WINDOW, 0, 0, 0, 0, 0, 0, 0, WINDOW_LEN, 0};
+ SANE_Status status;
+ DBG (11, "<< get_window ");
+
+ cmd[8] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+get_data_buffer_status (int fd, void *buf, size_t *buf_size)
+{
+ static u_char cmd[10] =
+ {GET_DATA_BUFFER_STATUS, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< get_data_buffer_status ");
+
+ cmd[8] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+#ifdef USE_FORK
+
+/* the following four functions serve simply the purpose
+ to avoid "over-optimised" code when reader_process and
+ read_data wait for the buffer to become ready. The simple
+ while-loops in these functions which check the buffer
+ status may be optimised so that the machine code only
+ operates with registers instead of using the variable
+ values stored in memory. (This is only a workaround -
+ it would be better to set a compiler pragma, which ensures
+ that the program looks into the RAM in these while loops --
+ but unfortunately I could not find appropriate information
+ about this at least for gcc, not to speak about other
+ compilers...
+ Abel)
+*/
+
+static int
+cancel_requested(NEC_Scanner *s)
+{
+ return s->rdr_ctl->cancel;
+}
+
+static SANE_Status
+rdr_status(NEC_Scanner *s)
+{
+ return s->rdr_ctl->status;
+}
+
+static int
+buf_status(NEC_shmem_ctl *s)
+{
+ return s->shm_status;
+}
+
+static int
+reader_running(NEC_Scanner *s)
+{
+ return s->rdr_ctl->running;
+}
+
+static int
+reader_process(NEC_Scanner *s)
+{
+ SANE_Status status;
+ sigset_t sigterm_set;
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int full_count = 0, counted;
+ size_t waitindex, cmdindex;
+ size_t bytes_to_queue;
+ size_t nread;
+ size_t max_bytes_per_read;
+ int max_queue;
+ int i;
+ NEC_shmem_ctl *bc;
+
+ s->rdr_ctl->running = 1;
+ DBG(11, "<< reader_process\n");
+
+ sigemptyset (&sigterm_set);
+
+ bytes_to_queue = s->bytes_to_read;
+
+ max_bytes_per_read = s->dev->info.bufsize / s->params.bytes_per_line;
+ if (max_bytes_per_read)
+ max_bytes_per_read *= s->params.bytes_per_line;
+ else
+ /* this is a really tiny buffer..*/
+ max_bytes_per_read = s->dev->info.bufsize;
+
+ /* wait_ready(s->fd); */
+
+ if (s->dev->info.queued_reads <= s->dev->info.buffers)
+ max_queue = s->dev->info.queued_reads;
+ else
+ max_queue = s->dev->info.buffers;
+ for (i = 0; i < max_queue; i++)
+ {
+ bc = &s->rdr_ctl->buf_ctl[i];
+ if (bytes_to_queue)
+ {
+ nread = bytes_to_queue;
+ if (nread > max_bytes_per_read)
+ nread = max_bytes_per_read;
+ bc->used = nread;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_enter...\n");
+#endif
+ status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
+ bc->buffer,
+ &bc->used,
+ &bc->qid);
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_enter ok\n");
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ bc->shm_status = SHM_BUSY;
+ bc->nreq = bc->used;
+ bytes_to_queue -= bc->nreq;
+ }
+ else
+ {
+ bc->used = 0;
+ bc->shm_status = SHM_EMPTY;
+ }
+ }
+ waitindex = 0;
+ cmdindex = i % s->dev->info.buffers;
+
+ while(s->bytes_to_read > 0)
+ {
+ if (cancel_requested(s))
+ {
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: flushing requests...\n");
+#endif
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: flushing requests ok\n");
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+
+ bc = &s->rdr_ctl->buf_ctl[waitindex];
+ if (bc->shm_status == SHM_BUSY)
+ {
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: waiting for data %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_wait...\n");
+#endif
+ status = sanei_scsi_req_wait(bc->qid);
+#ifdef QUEUEDEBUG
+ DBG(2, "reader: req_wait ok\n");
+#endif
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: data received %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ s->bytes_to_read -= bc->used;
+ bytes_to_queue += bc->nreq - bc->used;
+ bc->start = 0;
+ bc->shm_status = SHM_FULL;
+
+ waitindex++;
+ if (waitindex == s->dev->info.buffers)
+ waitindex = 0;
+
+ }
+
+ if (bytes_to_queue)
+ {
+ /* wait until the next buffer is completely read via read_data */
+ bc = &s->rdr_ctl->buf_ctl[cmdindex];
+ counted = 0;
+ while (buf_status(bc) != SHM_EMPTY)
+ {
+ if (!counted)
+ {
+ counted = 1;
+ full_count++;
+ }
+ if (cancel_requested(s))
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+ }
+
+ nread = bytes_to_queue;
+ if (nread > max_bytes_per_read)
+ nread = max_bytes_per_read;
+ bc->used = nread;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+ status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
+ bc->buffer, &bc->used, &bc->qid);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s",
+ sane_strstatus(status));
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->status = status;
+ s->rdr_ctl->running = 0;
+ return 2;
+ }
+ bc->shm_status = SHM_BUSY;
+ bc->nreq = nread;
+ bytes_to_queue -= nread;
+
+ cmdindex++;
+ if (cmdindex == s->dev->info.buffers)
+ cmdindex = 0;
+ }
+
+ if (cancel_requested(s))
+ {
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ sanei_scsi_req_flush_all_extended(s->fd);
+#else
+ sanei_scsi_req_flush_all();
+#endif
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->status = SANE_STATUS_CANCELLED;
+ s->rdr_ctl->running = 0;
+ DBG(11, " reader_process (cancelled) >>\n");
+ return 1;
+ }
+ }
+
+ DBG(1, "buffer full conditions: %i\n", full_count);
+ DBG(11, " reader_process>>\n");
+
+ s->rdr_ctl->running = 0;
+ return 0;
+}
+
+static SANE_Status
+read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size)
+{
+ size_t copysize, copied = 0;
+ NEC_shmem_ctl *bc;
+
+ DBG(11, "<< read_data ");
+
+ bc = &s->rdr_ctl->buf_ctl[s->read_buff];
+
+ while (copied < *buf_size)
+ {
+ /* wait until the reader process delivers data or a scanner error occurs: */
+ while ( buf_status(bc) != SHM_FULL
+ && rdr_status(s) == SANE_STATUS_GOOD)
+ {
+ usleep(10); /* could perhaps be longer. make this user configurable?? */
+ }
+
+ if (rdr_status(s) != SANE_STATUS_GOOD)
+ {
+ return rdr_status(s);
+ DBG(11, ">>\n");
+ }
+
+ copysize = bc->used - bc->start;
+
+ if (copysize > *buf_size - copied )
+ copysize = *buf_size - copied;
+
+ memcpy(buf, &(bc->buffer[bc->start]), copysize);
+
+ copied += copysize;
+ buf = &buf[copysize];
+
+ bc->start += copysize;
+ if (bc->start >= bc->used)
+ {
+ bc->start = 0;
+ bc->shm_status = SHM_EMPTY;
+ s->read_buff++;
+ if (s->read_buff == s->dev->info.buffers)
+ s->read_buff = 0;
+ bc = &s->rdr_ctl->buf_ctl[s->read_buff];
+ }
+ }
+
+ DBG(11, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+#else /* don't USE_FORK: */
+
+static SANE_Status
+read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size)
+{
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t remain = *buf_size;
+ size_t nread;
+ DBG (11, "<< read_data ");
+
+ /* sane_read_shuffled requires that read_data returns
+ exactly *buf_size bytes, so it must be guaranteed here.
+ Further make sure that not more bytes are read in than
+ sanei_scsi_max_request_size allows, to avoid a failure
+ of the read command
+ */
+ while (remain > 0)
+ {
+ nread = remain;
+ if (nread > s->dev->info.bufsize)
+ nread = s->dev->info.bufsize;
+ cmd[6] = nread >> 16;
+ cmd[7] = nread >> 8;
+ cmd[8] = nread;
+ status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd),
+ &buf[*buf_size - remain], &nread);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(11, ">>\n");
+ return(status);
+ }
+ remain -= nread;
+ }
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+ DBG (10, "<< max_string_size ");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ DBG (10, ">>\n");
+ return max_size;
+}
+
+static SANE_Status
+wait_ready(int fd)
+{
+ SANE_Status status;
+ int retry = 0;
+
+ while ((status = test_unit_ready (fd)) != SANE_STATUS_GOOD)
+ {
+ DBG (5, "wait_ready failed (%d)\n", retry);
+ DBG (5, "wait_ready status = (%d)\n", status);
+ if (retry++ > 15){
+ return SANE_STATUS_IO_ERROR;
+ }
+ sleep(3);
+ }
+ return (status);
+
+}
+
+static SANE_Status
+attach (const char *devnam, NEC_Device ** devp)
+{
+ SANE_Status status;
+ NEC_Device *dev;
+ NEC_Sense_Data sensedat;
+
+ int fd;
+ unsigned char inquiry_data[INQUIRY_LEN];
+ const unsigned char *model_name;
+ mode_sense_param msp;
+ size_t buf_size;
+ DBG (10, "<< attach ");
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+
+ sensedat.model = unknown;
+ sensedat.complain_on_adf_error = 0;
+ DBG (3, "attach: opening %s\n", devnam);
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ {
+ int bufsize = 4096;
+ status = sanei_scsi_open_extended (devnam, &fd, &sense_handler, &sensedat, &bufsize);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+ if (bufsize < 4096)
+ {
+ DBG(1, "attach: open failed. no memory\n");
+ sanei_scsi_close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+#else
+ status = sanei_scsi_open (devnam, &fd, &sense_handler, &sensedat);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
+ return (status);
+ }
+#endif
+
+ DBG (3, "attach: sending INQUIRY\n");
+ memset (inquiry_data, 0, sizeof (inquiry_data));
+ buf_size = sizeof (inquiry_data);
+ status = inquiry (fd, inquiry_data, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+
+ if (inquiry_data[0] == 6 && strncmp ((char *)inquiry_data + 8, "NEC", 3) == 0)
+ {
+ if (strncmp ((char *)inquiry_data + 16, "PC-IN500/4C", 11) == 0)
+ sensedat.model = PCIN500;
+ else
+ sensedat.model = unknown;
+ }
+
+ if (sensedat.model == unknown)
+ {
+ DBG (1, "attach: device doesn't look like a NEC scanner\n");
+ DBG (1, " : Only PC-IN500/4C is supported.\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending TEST_UNIT_READY\n");
+ status = test_unit_ready (fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ sanei_scsi_close (fd);
+ return (status);
+ }
+ DBG (3, "attach: sending MODE SELECT\n");
+
+ if (sensedat.model == PCIN500)
+ status = mode_select_mud (fd, DEFAULT_MUD_1200);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT_MUD failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "attach: sending MODE SENSE/MUP page\n");
+ memset (&msp, 0, sizeof (msp));
+ buf_size = sizeof (msp);
+ status = mode_sense (fd, &msp, &buf_size, 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE/MUP page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+#ifdef DEBUG_NEC
+ DBG (3,"attach: MODE SENSE parameter\n");
+ DBG(11, "%02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ msp.mode_data_length,
+ msp.mode_param_header2,
+ msp.mode_param_header3,
+ msp.mode_desciptor_length,
+ msp.page_code,
+ msp.page_length,
+ msp.bmu,
+ msp.res2,
+ msp.mud[0],
+ msp.mud[1],
+ msp.res3,
+ msp.res4);
+#endif
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = (SANE_String) strdup (devnam);
+ dev->sane.vendor = "NEC";
+ model_name = inquiry_data + 16;
+ dev->sane.model = strndup ((const char *)model_name, 10);
+ dev->sane.type = "flatbed scanner";
+
+ dev->sensedat.model = sensedat.model;
+
+ DBG (5, "dev->sane.name = %s\n", dev->sane.name);
+ DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor);
+ DBG (5, "dev->sane.model = %s\n", dev->sane.model);
+ DBG (5, "dev->sane.type = %s\n", dev->sane.type);
+
+ if (sensedat.model == PCIN500)
+ dev->info.res_range.quant = 10;
+ else
+ dev->info.res_range.quant = 0;
+
+ dev->info.tl_x_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
+ dev->info.br_x_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
+ dev->info.br_y_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.br_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+ dev->info.br_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
+
+ if (sensedat.model == PCIN500)
+ dev->info.res_default = 15;
+ else
+ dev->info.res_default = 150;
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(209);
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(296);
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297);
+
+ dev->info.bmu = msp.bmu;
+ dev->info.mud = (msp.mud[0] << 8) + msp.mud[1];
+
+ dev->info.adf_fsu_installed = 0;
+ if (dev->sensedat.model == PCIN500)
+ {
+ dev->info.res_range.max = 48;
+ dev->info.res_range.min = 5;
+
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ }
+ else
+ {
+ dev->info.res_range.max = 400;
+ dev->info.res_range.min = 50;
+
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
+ }
+ sanei_scsi_close (fd);
+
+ dev->info.threshold_range.min = 1;
+ dev->info.threshold_range.max = 255;
+ dev->info.threshold_range.quant = 0;
+
+ dev->info.tint_range.min = 1;
+ dev->info.tint_range.max = 255;
+ dev->info.tint_range.quant = 0;
+
+ dev->info.color_range.min = 1;
+ dev->info.color_range.max = 255;
+ dev->info.color_range.quant = 0;
+
+ DBG (5, "res_default=%d\n", dev->info.res_default);
+ DBG (5, "res_range.max=%d\n", dev->info.res_range.max);
+ DBG (5, "res_range.min=%d\n", dev->info.res_range.min);
+ DBG (5, "res_range.quant=%d\n", dev->info.res_range.quant);
+
+ DBG (5, "x_default=%f\n", SANE_UNFIX(dev->info.x_default));
+ DBG (5, "tl_x_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].max));
+ DBG (5, "tl_x_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].min));
+ DBG (5, "tl_x_range[0].quant=%d\n", dev->info.tl_x_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "br_x_range[0].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].max));
+ DBG (5, "br_x_range[0].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].min));
+ DBG (5, "br_x_range[0].quant=%d\n", dev->info.br_x_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "y_default=%f\n", SANE_UNFIX(dev->info.y_default));
+ DBG (5, "tl_y_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].max));
+ DBG (5, "tl_y_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].min));
+ DBG (5, "tl_y_range[0].quant=%d\n", dev->info.tl_y_ranges[SCAN_SIMPLE].quant);
+ DBG (5, "br_y_range[0].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].max));
+ DBG (5, "br_y_range[0].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].min));
+ DBG (5, "br_y_range[0].quant=%d\n", dev->info.br_y_ranges[SCAN_SIMPLE].quant);
+
+ if (dev->info.adf_fsu_installed & HAVE_FSU)
+ {
+ DBG (5, "tl_x_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "tl_x_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "tl_x_range[1].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "br_x_range[1].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "br_x_range[1].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "br_x_range[1].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "tl_y_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "tl_y_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "tl_y_range[1].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_FSU].quant);
+ DBG (5, "br_y_range[1].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].max));
+ DBG (5, "br_y_range[1].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].min));
+ DBG (5, "br_y_range[1].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_FSU].quant);
+ }
+
+ if (dev->info.adf_fsu_installed & HAVE_ADF)
+ {
+ DBG (5, "tl_x_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "tl_x_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "tl_x_range[2].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "br_x_range[2].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "br_x_range[2].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "br_x_range[2].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "tl_y_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "tl_y_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "tl_y_range[2].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_ADF].quant);
+ DBG (5, "br_y_range[2].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].max));
+ DBG (5, "br_y_range[2].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].min));
+ DBG (5, "br_y_range[2].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_ADF].quant);
+ }
+
+ DBG (5, "bmu=%d\n", dev->info.bmu);
+ DBG (5, "mud=%d\n", dev->info.mud);
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = dev;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+/* Enabling / disabling of gamma options.
+ Depends on many user settable options, so lets put it into
+ one function to be called by init_options and by sane_control_option
+
+*/
+#ifdef USE_CUSTOM_GAMMA
+static void
+set_gamma_caps(NEC_Scanner *s)
+{
+ /* neither fixed nor custom gamma for line art modes */
+ if ( strcmp(s->val[OPT_MODE].s, M_LINEART) == 0
+ || strcmp(s->val[OPT_MODE].s, M_LINEART_COLOR) == 0)
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp(s->val[OPT_MODE].s, M_GRAY) == 0)
+ {
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ /* color mode */
+ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+ s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ }
+}
+#endif /* USE_CUSTOM_GAMMA */
+
+/* The next function is a slightly modified version of sanei_constrain_value
+ Instead of returning status information like STATUS_INVAL, it adjusts
+ an invaild value to the nearest allowed one.
+*/
+static void
+clip_value (const SANE_Option_Descriptor * opt, void * value)
+{
+ const SANE_String_Const * string_list;
+ const SANE_Word * word_list;
+ int i, num_matches, match;
+ const SANE_Range * range;
+ SANE_Word w, v;
+ size_t len;
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ w = *(SANE_Word *) value;
+ range = opt->constraint.range;
+
+ if (w < range->min)
+ w = range->min;
+ else if (w > range->max)
+ w = range->max;
+
+ if (range->quant)
+ {
+ v = (w - range->min + range->quant/2) / range->quant;
+ w = v * range->quant + range->min;
+ *(SANE_Word*) value = w;
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ w = *(SANE_Word *) value;
+ word_list = opt->constraint.word_list;
+ for (i = 1; w != word_list[i]; ++i)
+ if (i >= word_list[0])
+ /* somewhat arbitrary... Would be better to have a default value
+ explicitly defined.
+ */
+ *(SANE_Word*) value = word_list[1];
+ break;
+
+ case SANE_CONSTRAINT_STRING_LIST:
+ /* Matching algorithm: take the longest unique match ignoring
+ case. If there is an exact match, it is admissible even if
+ the same string is a prefix of a longer option name. */
+ string_list = opt->constraint.string_list;
+ len = strlen (value);
+
+ /* count how many matches of length LEN characters we have: */
+ num_matches = 0;
+ match = -1;
+ for (i = 0; string_list[i]; ++i)
+ if (strncasecmp (value, string_list[i], len) == 0
+ && len <= strlen (string_list[i]))
+ {
+ match = i;
+ if (len == strlen (string_list[i]))
+ {
+ /* exact match... */
+ if (strcmp (value, string_list[i]) != 0)
+ /* ...but case differs */
+ strcpy (value, string_list[match]);
+ }
+ ++num_matches;
+ }
+
+ if (num_matches > 1)
+ /* xxx quite arbitrary... We could also choose the first match
+ */
+ strcpy(value, string_list[match]);
+ else if (num_matches == 1)
+ strcpy (value, string_list[match]);
+ else
+ strcpy (value, string_list[0]);
+
+ default:
+ break;
+ }
+}
+
+/* make sure that enough memory is allocated for each string,
+ so that the strcpy in sane_control_option / set value cannot
+ write behind the end of the allocated memory.
+*/
+static SANE_Status
+init_string_option(NEC_Scanner *s, SANE_String_Const name,
+ SANE_String_Const title, SANE_String_Const desc,
+ const SANE_String_Const *string_list, int option, int default_index)
+{
+ int i;
+
+ s->opt[option].name = name;
+ s->opt[option].title = title;
+ s->opt[option].desc = desc;
+ s->opt[option].type = SANE_TYPE_STRING;
+ s->opt[option].size = max_string_size (string_list);
+ s->opt[option].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[option].constraint.string_list = string_list;
+ s->val[option].s = malloc(s->opt[option].size);
+ if (s->val[option].s == 0)
+ {
+ for (i = 1; i < NUM_OPTIONS; i++)
+ {
+ if (s->val[i].s && s->opt[i].type == SANE_TYPE_STRING)
+ free(s->val[i].s);
+ }
+ return SANE_STATUS_NO_MEM;
+ }
+ strcpy(s->val[option].s, string_list[default_index]);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (NEC_Scanner * s)
+{
+ int i, default_source;
+ SANE_Word scalar;
+ DBG (10, "<< init_options ");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->val[i].s = 0;
+ }
+
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* Mode group: */
+ s->opt[OPT_MODE_GROUP].title = "Scan Mode";
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ init_string_option(s, SANE_NAME_SCAN_MODE, SANE_TITLE_SCAN_MODE,
+ SANE_DESC_SCAN_MODE, mode_list, OPT_MODE, MODES_COLOR);
+
+ /* half tone */
+ init_string_option(s, SANE_NAME_HALFTONE_PATTERN, SANE_TITLE_HALFTONE_PATTERN,
+ SANE_DESC_HALFTONE " (not support)", halftone_list, OPT_HALFTONE, 0);
+
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+
+ i = 0;
+ default_source = -1;
+
+ if (s->dev->info.adf_fsu_installed & HAVE_ADF)
+ {
+ s->dev->info.scansources[i++] = use_adf;
+ default_source = SCAN_WITH_ADF;
+ }
+ if (s->dev->info.adf_fsu_installed & HAVE_FSU)
+ {
+ s->dev->info.scansources[i++] = use_fsu;
+ if (default_source < 0)
+ default_source = SCAN_WITH_FSU;
+ }
+ s->dev->info.scansources[i++] = use_simple;
+ if (default_source < 0)
+ default_source = SCAN_SIMPLE;
+ s->dev->info.scansources[i] = 0;
+
+ init_string_option(s, SANE_NAME_SCAN_SOURCE, SANE_TITLE_SCAN_SOURCE,
+ SANE_DESC_SCAN_SOURCE, (SANE_String_Const*)s->dev->info.scansources,
+ OPT_SCANSOURCE, 0);
+
+ if (i < 2)
+ s->opt[OPT_SCANSOURCE].cap |= SANE_CAP_INACTIVE;
+
+ if (s->dev->sensedat.model == PCIN500)
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_pcin500, OPT_PAPER, 0);
+ else
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_pcinxxx, OPT_PAPER, 1);
+
+ /* gamma */
+ init_string_option(s, "Gamma", "Gamma", "Gamma", gamma_list, OPT_GAMMA, 0);
+
+ /* Resolution Group */
+ s->opt[OPT_RESOLUTION_GROUP].title = "Resolution";
+ s->opt[OPT_RESOLUTION_GROUP].desc = "";
+ s->opt[OPT_RESOLUTION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_RESOLUTION_GROUP].cap = 0;
+ s->opt[OPT_RESOLUTION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+#ifdef USE_RESOLUTION_LIST
+ /* select resolution */
+ if (s->dev->sensedat.model == PCIN500)
+ init_string_option(s, "Resolution", "Resolution", "Resolution",
+ resolution_list_pcin500, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCIN500);
+ else
+ init_string_option(s, "Resolution", "Resolution", "Resolution",
+ resolution_list_pcinxxx, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCINXXX);
+#endif
+
+ /* x & y resolution */
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION"(x 10)";
+ else
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_RESOLUTION].constraint.range = &s->dev->info.res_range;
+ s->val[OPT_RESOLUTION].w = s->dev->info.res_default;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &s->dev->info.tl_x_ranges[default_source];
+ s->val[OPT_TL_X].w = s->dev->info.tl_x_ranges[default_source].min;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &s->dev->info.tl_y_ranges[default_source];
+ s->val[OPT_TL_Y].w = s->dev->info.tl_y_ranges[default_source].min;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &s->dev->info.br_x_ranges[default_source];
+ scalar = s->dev->info.x_default;
+ clip_value (&s->opt[OPT_BR_X], &scalar);
+ s->val[OPT_BR_X].w = scalar;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &s->dev->info.br_y_ranges[default_source];
+ scalar = s->dev->info.y_default;
+ clip_value (&s->opt[OPT_BR_X], &scalar);
+ s->val[OPT_BR_Y].w = scalar;
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* edge emphasis */
+ init_string_option(s, "Edge emphasis", "Edge emphasis",
+ "Edge emphasis", edge_emphasis_list,
+ OPT_EDGE_EMPHASIS, 0);
+
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_EDGE_EMPHASIS].cap |= SANE_CAP_INACTIVE;
+
+ /* OR */
+ s->opt[OPT_OR].name = "OR";
+ s->opt[OPT_OR].title = "OR";
+ s->opt[OPT_OR].desc = "Select OR emphancement";
+ s->opt[OPT_OR].type = SANE_TYPE_BOOL;
+ s->val[OPT_OR].w = SANE_FALSE;
+
+ /* EDGE */
+ s->opt[OPT_EDGE].name = "edge";
+ s->opt[OPT_EDGE].title = "Edge";
+ s->opt[OPT_EDGE].desc = "Select Edge emphancement";
+ s->opt[OPT_EDGE].type = SANE_TYPE_BOOL;
+ s->val[OPT_EDGE].w = SANE_FALSE;
+
+ /* NR */
+ s->opt[OPT_NR].name = "NR";
+ s->opt[OPT_NR].title = "NR";
+ s->opt[OPT_NR].desc = "Select noise reduction";
+ s->opt[OPT_NR].type = SANE_TYPE_BOOL;
+ s->val[OPT_NR].w = SANE_FALSE;
+
+ if (s->dev->sensedat.model != PCIN500)
+ {
+ s->opt[OPT_EDGE].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_NR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_OR].cap |= SANE_CAP_INACTIVE;
+ }
+ /* tint */
+ s->opt[OPT_TINT].name = "tint";
+ s->opt[OPT_TINT].title = "Tint";
+ s->opt[OPT_TINT].desc = "Select tint";
+ s->opt[OPT_TINT].type = SANE_TYPE_INT;
+ s->opt[OPT_TINT].unit = SANE_UNIT_NONE;
+ s->opt[OPT_TINT].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TINT].constraint.range = &s->dev->info.tint_range;
+ s->val[OPT_TINT].w = 128;
+ if (s->dev->sensedat.model != PCIN500)
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+
+ /* color */
+ s->opt[OPT_COLOR].name = "color";
+ s->opt[OPT_COLOR].title = "Color";
+ s->opt[OPT_COLOR].desc = "Select color";
+ s->opt[OPT_COLOR].type = SANE_TYPE_INT;
+ s->opt[OPT_COLOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_COLOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_COLOR].constraint.range = &s->dev->info.color_range;
+ s->val[OPT_COLOR].w = 128;
+ if (s->dev->sensedat.model != PCIN500)
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+
+ /* threshold */
+ s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD].w = 128;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].name = SANE_NAME_THRESHOLD "-red";
+ /* xxx the titles and decriptions are confusing:
+ "set white point (red)"
+ Any idea? maybe "threshold to get the red component on"
+ */
+ s->opt[OPT_THRESHOLD_R].title = SANE_TITLE_THRESHOLD " (red)";
+ s->opt[OPT_THRESHOLD_R].desc = SANE_DESC_THRESHOLD " (red)";
+ s->opt[OPT_THRESHOLD_R].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_R].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_R].w = 128;
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_THRESHOLD_G].name = SANE_NAME_THRESHOLD "-green";
+ s->opt[OPT_THRESHOLD_G].title = SANE_TITLE_THRESHOLD " (green)";
+ s->opt[OPT_THRESHOLD_G].desc = SANE_DESC_THRESHOLD " (green)";
+ s->opt[OPT_THRESHOLD_G].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_G].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_G].w = 128;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_THRESHOLD_B].name = SANE_NAME_THRESHOLD "-blue";
+ s->opt[OPT_THRESHOLD_B].title = SANE_TITLE_THRESHOLD " (blue)";
+ s->opt[OPT_THRESHOLD_B].desc = SANE_DESC_THRESHOLD " (blue)";
+ s->opt[OPT_THRESHOLD_B].type = SANE_TYPE_INT;
+ s->opt[OPT_THRESHOLD_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_THRESHOLD_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_THRESHOLD_B].constraint.range = &s->dev->info.threshold_range;
+ s->val[OPT_THRESHOLD_B].w = 128;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+
+#endif
+
+ /* light color (for gray scale and line art scans) */
+ init_string_option(s, "LightColor", "LightColor", "LightColor",
+ light_color_list, OPT_LIGHTCOLOR, 3);
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+
+ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+
+#ifdef USE_CUSTOM_GAMMA
+ /* custom-gamma table */
+ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
+ s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
+ s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
+
+ /* grayscale gamma vector */
+ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
+
+ /* red gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
+
+ /* green gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
+
+ /* blue gamma vector */
+ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+#if 0
+ s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
+ s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
+ s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
+ set_gamma_caps(s);
+#endif
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (NEC_Scanner * s)
+{
+ DBG (10, "<< do_cancel ");
+
+#ifdef USE_FORK
+ if (s->reader_pid > 0)
+ {
+ int exit_status;
+ int count = 0;
+ /* ensure child knows it's time to stop: */
+
+ DBG(11, "stopping reader process\n");
+ s->rdr_ctl->cancel = 1;
+ while(reader_running(s) && count < 100)
+ {
+ usleep(100000);
+ count++;
+ };
+ if (reader_running(s))
+ {
+ kill(s->reader_pid, SIGKILL);
+ }
+ wait(&exit_status);
+ DBG(11, "reader process stopped\n");
+
+ s->reader_pid = 0;
+ }
+
+#endif
+ s->scanning = SANE_FALSE;
+
+ if (s->fd >= 0)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ if (s->shmid != -1)
+ shmctl(s->shmid, IPC_RMID, &ds);
+ s->shmid = -1;
+ }
+#endif
+ if (s->buffer)
+ free(s->buffer);
+ s->buffer = 0;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_CANCELLED);
+}
+
+static NEC_New_Device *new_devs = 0;
+static NEC_New_Device *new_dev_pool = 0;
+
+static SANE_Status
+attach_and_list(const char *devnam)
+{
+ SANE_Status res;
+ NEC_Device *devp;
+ NEC_New_Device *np;
+
+ res = attach(devnam, &devp);
+ if (res == SANE_STATUS_GOOD)
+ {
+ if (new_dev_pool)
+ {
+ np = new_dev_pool;
+ new_dev_pool = np->next;
+ }
+ else
+ {
+ np = malloc(sizeof(NEC_New_Device));
+ if (np == 0)
+ return SANE_STATUS_NO_MEM;
+ }
+ np->next =new_devs;
+ np->dev = devp;
+ new_devs = np;
+ }
+ return res;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char devnam[PATH_MAX] = "/dev/scanner";
+ char line[PATH_MAX];
+ const char *lp;
+ char *word;
+ char *end;
+ FILE *fp;
+ int opt_index = 0;
+ int buffers[2] = {DEFAULT_BUFFERS, DEFAULT_BUFFERS};
+ int bufsize[2] = {DEFAULT_BUFSIZE, DEFAULT_BUFSIZE};
+ int queued_reads[2] = {DEFAULT_QUEUED_READS, DEFAULT_QUEUED_READS};
+ int linecount = 0;
+#if 1
+ NEC_Device nd;
+ NEC_Device *dp = &nd;
+#else
+ NEC_Device *dp;
+#endif
+ NEC_New_Device *np;
+ int i;
+
+ authorize = authorize; /* silence compilation warnings */
+
+ DBG_INIT ();
+ DBG (10, "<< sane_init ");
+
+ DBG (1, "sane_init: NEC (Ver %d.%d)\n", NEC_MAJOR, NEC_MINOR);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (NEC_CONFIG_FILE);
+ if (!fp)
+ {
+ /* use "/dev/scanner" as the default device name if no
+ config file is available
+ */
+ attach (devnam, &dp);
+ /* make sure that there are at least two buffers */
+ if (DEFAULT_BUFFERS < 2)
+ dp->info.buffers = DEFAULT_BUFFERS;
+ else
+ dp->info.buffers = 2;
+ dp->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ dp->info.queued_reads = DEFAULT_QUEUED_READS;
+ return SANE_STATUS_GOOD;
+ }
+
+ while (fgets(line, PATH_MAX, fp))
+ {
+ linecount++;
+ word = 0;
+ lp = sanei_config_get_string(line, &word);
+ if (word)
+ {
+ if (word[0] != '#')
+ {
+ if (strcmp(word, "option") == 0)
+ {
+ free(word);
+ word = 0;
+ lp = sanei_config_get_string(lp, &word);
+ if (strcmp(word, "buffers") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (end == word)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ if (i > 2)
+ buffers[opt_index] = i;
+ else
+ buffers[opt_index] = 2;
+ }
+ else if (strcmp(word, "buffersize") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (word == end)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ bufsize[opt_index] = i;
+ }
+ else if (strcmp(word, "readqueue") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ i = strtol(word, &end, 0);
+ if (word == end)
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ else
+ queued_reads[opt_index] = i;
+ }
+ else
+ {
+ DBG(1, "error in config file, line %i: unknown option\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ }
+ else
+ {
+ while (new_devs)
+ {
+ if (buffers[1] >= 2)
+ new_devs->dev->info.buffers = buffers[1];
+ else
+ new_devs->dev->info.buffers = 2;
+ if (bufsize[1] > 0)
+ new_devs->dev->info.wanted_bufsize = bufsize[1];
+ else
+ new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ if (queued_reads[1] >= 0)
+ new_devs->dev->info.queued_reads = queued_reads[1];
+ else
+ new_devs->dev->info.queued_reads = 0;
+ np = new_devs->next;
+ new_devs->next = new_dev_pool;
+ new_dev_pool = new_devs;
+ new_devs = np;
+ }
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+ sanei_config_attach_matching_devices(line, &attach_and_list);
+ buffers[1] = buffers[0];
+ bufsize[1] = bufsize[0];
+ queued_reads[1] = queued_reads[0];
+ opt_index = 1;
+ }
+ }
+ if (word) free(word);
+ }
+ }
+
+ while (new_devs)
+ {
+ if (buffers[1] >= 2)
+ new_devs->dev->info.buffers = buffers[1];
+ else
+ new_devs->dev->info.buffers = 2;
+ if (bufsize[1] > 0)
+ new_devs->dev->info.wanted_bufsize = bufsize[1];
+ else
+ new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
+ if (queued_reads[1] >= 0)
+ new_devs->dev->info.queued_reads = queued_reads[1];
+ else
+ new_devs->dev->info.queued_reads = 0;
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = 0;
+ np = new_devs->next;
+ free(new_devs);
+ new_devs = np;
+ }
+ while (new_dev_pool)
+ {
+ np = new_dev_pool->next;
+ free(new_dev_pool);
+ new_dev_pool = np;
+ }
+ fclose(fp);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+void
+sane_exit (void)
+{
+ NEC_Device *dev, *next;
+ DBG (10, "<< sane_exit ");
+
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ free ((void *) dev->sane.name);
+ free ((void *) dev->sane.model);
+ free (dev);
+ }
+ first_dev = 0;
+
+ if (devlist)
+ free(devlist);
+
+ DBG (10, ">>\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ NEC_Device *dev;
+ int i;
+ DBG (10, "<< sane_get_devices ");
+
+ local_only = local_only; /* silence compilation warnings */
+
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ i = 0;
+ for (dev = first_dev; dev; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devnam, SANE_Handle * handle)
+{
+ SANE_Status status;
+ NEC_Device *dev;
+ NEC_Scanner *s;
+#ifdef USE_CUSTOM_GAMMA
+ int i, j;
+#endif
+
+ DBG (10, "<< sane_open ");
+
+ if (devnam[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devnam) == 0)
+ break;
+ }
+
+ if (!dev)
+ {
+ status = attach (devnam, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ dev = first_dev;
+ }
+
+ if (!dev)
+ return (SANE_STATUS_INVAL);
+
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->fd = -1;
+ s->dev = dev;
+
+ s->buffer = 0;
+#ifdef USE_CUSTOM_GAMMA
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < 256; ++j)
+ s->gamma_table[i][j] = j;
+#endif
+ status = init_options (s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ /* xxx clean up mallocs */
+ return status;
+ }
+
+ s->next = first_handle;
+ first_handle = s;
+
+ *handle = s;
+
+ DBG (10, ">>\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ NEC_Scanner *s = (NEC_Scanner *) handle;
+ DBG (10, "<< sane_close ");
+
+ if (s->fd != -1)
+ sanei_scsi_close (s->fd);
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ if (s->shmid != -1)
+ shmctl(s->shmid, IPC_RMID, &ds);
+ }
+#endif
+ if (s->buffer)
+ free(s->buffer);
+ free (s);
+
+ DBG (10, ">>\n");
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ NEC_Scanner *s = handle;
+ DBG (10, "<< sane_get_option_descriptor ");
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+
+ DBG (10, ">>\n");
+ return (s->opt + option);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+#ifdef USE_CUSTOM_GAMMA
+ SANE_Word w, cap;
+#else
+ SANE_Word cap;
+#endif
+ int range_index;
+ DBG (10, "<< sane_control_option %i", option);
+
+ if (info)
+ *info = 0;
+
+ if (s->scanning)
+ return (SANE_STATUS_DEVICE_BUSY);
+ if (option >= NUM_OPTIONS)
+ return (SANE_STATUS_INVAL);
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return (SANE_STATUS_INVAL);
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_THRESHOLD:
+ case OPT_TINT:
+ case OPT_COLOR:
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_OR:
+ case OPT_NR:
+ case OPT_EDGE:
+ case OPT_PREVIEW:
+#ifdef USE_CUSTOM_GAMMA
+ case OPT_CUSTOM_GAMMA:
+#endif
+ *(SANE_Word *) val = s->val[option].w;
+#if 0 /* here, values are read; reload should not be necessary */
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+#endif
+ return (SANE_STATUS_GOOD);
+
+#ifdef USE_CUSTOM_GAMMA
+ /* word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (val, s->val[option].wa, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+#endif
+
+ /* string options: */
+ case OPT_MODE:
+ case OPT_HALFTONE:
+ case OPT_PAPER:
+ case OPT_GAMMA:
+#ifdef USE_RESOLUTION_LIST
+ case OPT_RESOLUTION_LIST:
+#endif
+ case OPT_EDGE_EMPHASIS:
+ case OPT_LIGHTCOLOR:
+ case OPT_SCANSOURCE:
+ strcpy (val, s->val[option].s);
+#if 0
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+#endif
+
+ return (SANE_STATUS_GOOD);
+
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if (info && s->val[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ case OPT_NUM_OPTS:
+ case OPT_THRESHOLD:
+ /* xxx theoretically, we could use OPT_THRESHOLD in
+ bi-level color mode to adjust all three other
+ threshold together. But this would require to set
+ the bit SANE_INFO_RELOAD_OPTIONS in *info, and that
+ would unfortunately cause a crash in both xscanimage
+ and xsane... Therefore, OPT_THRESHOLD is disabled
+ for bi-level color scan right now.
+ */
+ case OPT_TINT:
+ case OPT_COLOR:
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_OR:
+ case OPT_NR:
+ case OPT_EDGE:
+ case OPT_PREVIEW:
+ s->val[option].w = *(SANE_Word *) val;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_MODE:
+ if (strcmp (val, M_LINEART) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, M_LINEART_COLOR) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap &= ~SANE_CAP_INACTIVE;
+#endif
+ if (s->dev->sensedat.model == PCIN500)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, M_GRAY) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_TINT].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_COLOR].cap &= ~SANE_CAP_INACTIVE;
+#ifdef USE_COLOR_THRESHOLD
+ s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
+#endif
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ }
+#if 0
+ if ( strcmp (val, M_LINEART) == 0
+ || strcmp (val, M_GRAY) == 0)
+ {
+ s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+ }
+#endif
+ strcpy(s->val[option].s, val);
+#ifdef USE_CUSTOM_GAMMA
+ set_gamma_caps(s);
+#endif
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return (SANE_STATUS_GOOD);
+
+ case OPT_GAMMA:
+ case OPT_HALFTONE:
+ case OPT_EDGE_EMPHASIS:
+ case OPT_LIGHTCOLOR:
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ return (SANE_STATUS_GOOD);
+
+ case OPT_SCANSOURCE:
+ if (info && strcmp (s->val[option].s, (SANE_String) val))
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ if (strcmp(val, use_fsu) == 0)
+ range_index = SCAN_WITH_FSU;
+ else if (strcmp(val, use_adf) == 0)
+ range_index = SCAN_WITH_ADF;
+ else
+ range_index = SCAN_SIMPLE;
+
+ s->opt[OPT_TL_X].constraint.range
+ = &s->dev->info.tl_x_ranges[range_index];
+ clip_value (&s->opt[OPT_TL_X], &s->val[OPT_TL_X].w);
+
+ s->opt[OPT_TL_Y].constraint.range
+ = &s->dev->info.tl_y_ranges[range_index];
+ clip_value (&s->opt[OPT_TL_Y], &s->val[OPT_TL_Y].w);
+
+ s->opt[OPT_BR_X].constraint.range
+ = &s->dev->info.br_x_ranges[range_index];
+ clip_value (&s->opt[OPT_BR_X], &s->val[OPT_BR_X].w);
+
+ s->opt[OPT_BR_Y].constraint.range
+ = &s->dev->info.br_y_ranges[range_index];
+ clip_value (&s->opt[OPT_BR_Y], &s->val[OPT_BR_Y].w);
+
+ return (SANE_STATUS_GOOD);
+
+ case OPT_PAPER:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ strcpy(s->val[option].s, val);
+ s->val[OPT_TL_X].w = SANE_FIX(0);
+ s->val[OPT_TL_Y].w = SANE_FIX(0);
+ if (strcmp (s->val[option].s, "A3") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(297);
+ s->val[OPT_BR_Y].w = SANE_FIX(420);
+ }else if (strcmp (s->val[option].s, "A4") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(210);
+ s->val[OPT_BR_Y].w = SANE_FIX(297);
+ }else if (strcmp (s->val[option].s, "A5") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(148.5);
+ s->val[OPT_BR_Y].w = SANE_FIX(210);
+ }else if (strcmp (s->val[option].s, "A6") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(105);
+ s->val[OPT_BR_Y].w = SANE_FIX(148.5);
+ }else if (strcmp (s->val[option].s, "B4") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(250);
+ s->val[OPT_BR_Y].w = SANE_FIX(353);
+ }else if (strcmp (s->val[option].s, "B5") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(182);
+ s->val[OPT_BR_Y].w = SANE_FIX(257);
+ }else if (strcmp (s->val[option].s, W_LETTER) == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(279.4);
+ s->val[OPT_BR_Y].w = SANE_FIX(431.8);
+ }else if (strcmp (s->val[option].s, "Legal") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(355.6);
+ }else if (strcmp (s->val[option].s, "Letter") == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(279.4);
+ }else if (strcmp (s->val[option].s, INVOICE) == 0){
+ s->val[OPT_BR_X].w = SANE_FIX(215.9);
+ s->val[OPT_BR_Y].w = SANE_FIX(139.7);
+ }else{
+ }
+ return (SANE_STATUS_GOOD);
+
+#ifdef USE_RESOLUTION_LIST
+ case OPT_RESOLUTION_LIST:
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+#if 0
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+#endif
+ for (i = 0; s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]; i++) {
+ if (strcmp (val,
+ s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]) == 0){
+ s->val[OPT_RESOLUTION].w
+ = atoi(s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ }
+ }
+ return (SANE_STATUS_GOOD);
+#endif
+
+#ifdef USE_CUSTOM_GAMMA
+ /* side-effect-free word-array options: */
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy (s->val[option].wa, val, s->opt[option].size);
+ return SANE_STATUS_GOOD;
+
+ case OPT_CUSTOM_GAMMA:
+ w = *(SANE_Word *) val;
+
+ if (w == s->val[OPT_CUSTOM_GAMMA].w)
+ return SANE_STATUS_GOOD; /* no change */
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ s->val[OPT_CUSTOM_GAMMA].w = w;
+ set_gamma_caps(s);
+ return SANE_STATUS_GOOD;
+#endif
+ }
+ }
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_INVAL);
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ int width, length, res;
+ const char *mode;
+ NEC_Scanner *s = handle;
+ DBG (10, "<< sane_get_parameters ");
+
+ res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
+ if (!s->scanning)
+ {
+ /* make best-effort guess at what parameters will look like once
+ scanning starts. */
+ memset (&s->params, 0, sizeof (s->params));
+
+ width = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_X].w)
+ - SANE_UNFIX(s->val[OPT_TL_X].w),
+ s->dev->info.mud);
+ length = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_Y].w)
+ - SANE_UNFIX(s->val[OPT_TL_Y].w),
+ s->dev->info.mud);
+
+ s->width = width;
+ s->length = length;
+ s->params.pixels_per_line = width * res / s->dev->info.mud;
+ s->params.lines = length * res / s->dev->info.mud;
+
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ s->params.pixels_per_line += 1;
+ s->params.lines += 1;
+ }
+ s->unscanned_lines = s->params.lines;
+ }
+#if 0
+ else
+ {
+ buffer_status bs;
+ SANE_Status status;
+ size_t len = sizeof (buffer_status);
+ status = get_data_buffer_status (s->fd, &bs, &len);
+ DBG (11, "<< get_data_buffer_status ");
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel(s);
+ return (status);
+ }
+ DBG (11, ">>\n ");
+ {
+#ifdef DEBUG_NEC
+ int i;
+ u_char *buf = &bs;
+ DBG(11, "get data buffer status(debug):\n");
+ for (i = 0; i < len; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x \n",
+ buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7], buf[i+8],
+ buf[i+9], buf[i+10], buf[i+11]);
+ }
+#endif
+ }
+ }
+#endif
+ res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
+
+ mode = s->val[OPT_MODE].s;
+
+ if (strcmp (mode, M_LINEART) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 1;
+ s->modes = MODES_LINEART;
+ }
+ else if (strcmp (mode, M_GRAY) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->modes = MODES_GRAY;
+ }
+ else if (strcmp (mode, M_LINEART_COLOR) == 0)
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * (s->params.pixels_per_line + 7) / 8;
+ s->params.depth = 8;
+ s->modes = MODES_LINEART_COLOR;
+ }
+ else
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line = 3 * s->params.pixels_per_line;
+ s->params.depth = 8;
+ s->modes = MODES_COLOR;
+ }
+ s->params.last_frame = SANE_TRUE;
+
+ if (params)
+ *params = s->params;
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+#ifdef USE_CUSTOM_GAMMA
+
+static int
+sprint_gamma(Option_Value val, SANE_Byte *dst)
+{
+ int i;
+ SANE_Byte *p = dst;
+
+ p += sprintf((char *) p, "%i", val.wa[0]);
+ for (i = 1; i < 256; i++)
+ p += sprintf((char *) p, ",%i", val.wa[i] > 255 ? 255 : val.wa[i]);
+ return p - dst;
+}
+
+static SANE_Status
+send_ascii_gamma_tables (NEC_Scanner *s)
+{
+ SANE_Status status;
+ int i;
+
+ DBG(11, "<< send_ascii_gamma_tables ");
+
+ /* we need: 4 bytes for each gamma value (3 digits + delimiter)
+ + 10 bytes for the command header
+ i.e. 4 * 4 * 256 + 10 = 4106 bytes
+ */
+
+ if (s->dev->info.bufsize < 4106)
+ return SANE_STATUS_NO_MEM;
+
+ memset(s->buffer, 0, 4106);
+
+ i = sprint_gamma(s->val[OPT_GAMMA_VECTOR_R], &s->buffer[10]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_G], &s->buffer[10+i]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_B], &s->buffer[10+i]);
+ s->buffer[10+i++] = '/';
+ i += sprint_gamma(s->val[OPT_GAMMA_VECTOR], &s->buffer[10+i]);
+
+ DBG(12, "%s\n", &s->buffer[10]);
+
+ s->buffer[0] = SEND;
+ s->buffer[2] = 0x03;
+ s->buffer[7] = i >> 8;
+ s->buffer[8] = i & 0xff;
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0);
+
+ DBG(11, ">>\n");
+
+ return status;
+}
+#endif
+
+static SANE_Status
+send_binary_g_table(NEC_Scanner *s, SANE_Word *a, int dtq)
+{
+ SANE_Status status;
+ unsigned int i, j;
+
+ dtq = dtq; /* silence compilation warnings */
+
+ DBG(11, "<< send_binary_g_table\n");
+
+ i = 256;
+ if (s->dev->info.bufsize < i)
+ return SANE_STATUS_NO_MEM;
+ memset(s->buffer, 0, i+10);
+
+ s->buffer[0] = SEND;
+ s->buffer[2] = 0x03;
+ s->buffer[7] = i >> 8;
+ s->buffer[8] = i & 0xff;
+
+ for (i = 0; i < 256; i++)
+ {
+ s->buffer[i+11] = a[i&0xff] & 0xff;
+ }
+
+ for (j = 0; j < 256; j += 16)
+ {
+ DBG(11, "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ a[j ], a[j+1], a[j+2], a[j+3],
+ a[j+4], a[j+5], a[j+6], a[j+7],
+ a[j+8], a[j+9], a[j+10], a[j+11],
+ a[j+12], a[j+13], a[j+14], a[j+15]);
+ }
+ DBG(12, "transfer length = %d\n", i);
+ DBG(12, "buffer[7] = %d\n", s->buffer[7]);
+ DBG(12, "buffer[8] = %d\n", s->buffer[8]);
+
+ /* wait_ready(s->fd); */
+ status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0);
+
+ DBG(11, ">>\n");
+
+ return status;
+}
+
+#ifdef USE_CUSTOM_GAMMA
+static SANE_Status
+send_binary_gamma_tables (NEC_Scanner *s)
+{
+ SANE_Status status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR].wa, 0x10);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ DBG(11, "send_binary_gamma_tables\n");
+#if 0
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_R].wa, 0x11);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_G].wa, 0x12);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_B].wa, 0x13);
+#endif
+ return status;
+}
+
+static SANE_Status
+send_gamma_tables (NEC_Scanner *s)
+{
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ return send_binary_gamma_tables(s);
+ }
+ else
+ {
+ return send_ascii_gamma_tables(s);
+ }
+
+}
+#endif
+
+#ifdef USE_COLOR_THRESHOLD
+/* not used? */
+#if 0
+static SANE_Status
+send_threshold_data(NEC_Scanner *s)
+{
+ SANE_Status status;
+ SANE_Byte cmd[26] = {SEND, 0, 0x82, 0, 0, 0, 0, 0, 0, 0};
+ int len;
+
+ memset(cmd, 0, sizeof(cmd));
+ /* maximum string length: 3 bytes for each number (they are
+ restricted to the range 0..255), 3 '/' and the null-byte,
+ total: 16 bytes.
+ */
+ len = sprintf((char *) &cmd[10], "%i/%i/%i/%i",
+ s->val[OPT_THRESHOLD_R].w,
+ s->val[OPT_THRESHOLD_G].w,
+ s->val[OPT_THRESHOLD_B].w,
+ s->val[OPT_THRESHOLD].w);
+ cmd[8] = len;
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd(s->fd, cmd, len + 10, 0, 0);
+ return status;
+}
+#endif
+#endif
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char *mode, *halftone, *paper, *gamma, *edge, *lightcolor, *adf_fsu;
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+ size_t buf_size;
+ window_param wp;
+
+ DBG (10, "<< sane_start ");
+
+ /* First make sure we have a current parameter set. Some of the
+ parameters will be overwritten below, but that's OK. */
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ s->dev->sensedat.complain_on_adf_error = 1;
+
+#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
+ s->dev->info.bufsize = s->dev->info.wanted_bufsize;
+ if (s->dev->info.bufsize < 32 * 1024)
+ s->dev->info.bufsize = 32 * 1024;
+ {
+ int bsize = s->dev->info.bufsize;
+ status = sanei_scsi_open_extended (s->dev->sane.name, &s->fd,
+ &sense_handler, &s->dev->sensedat, &bsize);
+ s->dev->info.bufsize = bsize;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ return (status);
+ }
+
+ /* make sure that we got at least 32 kB. Even then, the scan will be
+ awfully slow.
+
+ */
+ if (s->dev->info.bufsize < 32 * 1024)
+ {
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+#else
+ status = sanei_scsi_open(s->dev->sane.name, &s->fd, &sense_handler,
+ &s->dev->sensedat);
+ if (s->dev->info.wanted_bufsize < sanei_scsi_max_request_size)
+ s->dev->info.bufsize = s->dev->info.wanted_bufsize;
+ else
+ s->dev->info.bufsize = sanei_scsi_max_request_size;
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ return (status);
+ }
+#endif
+
+ s->buffer = malloc(s->dev->info.bufsize);
+ if (!s->buffer) {
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ free(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+#ifdef USE_FORK
+ {
+ struct shmid_ds ds;
+ size_t n;
+
+ s->shmid = shmget(IPC_PRIVATE,
+ sizeof(NEC_rdr_ctl)
+ + s->dev->info.buffers *
+ (sizeof(NEC_shmem_ctl) + s->dev->info.bufsize),
+ IPC_CREAT | 0600);
+ if (s->shmid == -1)
+ {
+ free(s->buffer);
+ s->buffer = 0;
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+ s->rdr_ctl = (NEC_rdr_ctl*) shmat(s->shmid, 0, 0);
+ if ((int)s->rdr_ctl == -1)
+ {
+ shmctl(s->shmid, IPC_RMID, &ds);
+ free(s->buffer);
+ s->buffer = 0;
+ sanei_scsi_close(s->fd);
+ s->fd = -1;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ s->rdr_ctl->buf_ctl = (NEC_shmem_ctl*) &s->rdr_ctl[1];
+ for (n = 0; n < s->dev->info.buffers; n++)
+ {
+ s->rdr_ctl->buf_ctl[n].buffer =
+ (SANE_Byte*) &s->rdr_ctl->buf_ctl[s->dev->info.buffers]
+ + n * s->dev->info.bufsize;
+ }
+ }
+#endif /* USE_FORK */
+
+ DBG (5, "start: TEST_UNIT_READY\n");
+ status = test_unit_ready (s->fd);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "TEST UNIT READY failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ DBG (3, "start: sending MODE SELECT\n");
+ status = mode_select_mud (s->fd, s->dev->info.mud);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start: MODE_SELECT6 failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ mode = s->val[OPT_MODE].s;
+ halftone = s->val[OPT_HALFTONE].s;
+ paper = s->val[OPT_PAPER].s;
+ gamma = s->val[OPT_GAMMA].s;
+ edge = s->val[OPT_EDGE_EMPHASIS].s;
+ lightcolor = s->val[OPT_LIGHTCOLOR].s;
+ adf_fsu = s->val[OPT_SCANSOURCE].s;
+
+ if (s->val[OPT_PREVIEW].w == SANE_FALSE)
+ {
+ s->res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
+ }
+ else
+ {
+ s->res = 75;
+ }
+ s->ulx = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_X].w), s->dev->info.mud);
+ s->uly = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_Y].w), s->dev->info.mud);
+ s->threshold = s->val[OPT_THRESHOLD].w;
+
+ if (strcmp (mode, M_LINEART_COLOR) == 0)
+ s->bpp = 1;
+ else
+ s->bpp = s->params.depth;
+
+ s->adf_fsu_mode = SCAN_SIMPLE; /* default: scan without ADF and FSU */
+
+ if (strcmp(adf_fsu, use_fsu) == 0)
+ s->adf_fsu_mode = SCAN_WITH_FSU;
+ else if (strcmp(adf_fsu, use_adf) == 0)
+ s->adf_fsu_mode = SCAN_WITH_ADF;
+ else if (strcmp(adf_fsu, use_adf) == 0)
+ s->adf_fsu_mode = SCAN_SIMPLE;
+
+ /* halftone must not set to be zero PC-IN500 */
+ s->halftone = 0x01;
+ if (strcmp (mode, M_LINEART) == 0)
+ {
+ s->reverse = 0;
+ s->image_composition = 1;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->image_composition = 0;
+ s->halftone = 0x01;
+ }
+ else if (strcmp(halftone, M_DITHER1) == 0)
+ s->halftone = 0x01;
+ else if (strcmp(halftone, M_DITHER2) == 0)
+ s->halftone = 0x10;
+ else if (strcmp(halftone, M_DITHER3) == 0)
+ s->halftone = 0x20;
+ }
+ else if (strcmp (mode, M_GRAY) == 0)
+ {
+ s->image_composition = 2;
+ s->reverse = 1;
+ }
+ else if (strcmp (mode, M_LINEART_COLOR) == 0)
+ {
+ s->reverse = 1;
+ s->image_composition = 4;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->image_composition = 3;
+ s->halftone = 0x01;
+ }
+ else if (strcmp(halftone, M_DITHER1) == 0)
+ s->halftone = 0x01;
+ else if (strcmp(halftone, M_DITHER2) == 0)
+ s->halftone = 0x10;
+ else if (strcmp(halftone, M_DITHER3) == 0)
+ s->halftone = 0x20;
+ }
+ else if (strcmp (mode, M_COLOR) == 0)
+ {
+ s->image_composition = 5;
+ s->reverse = 1;
+ }
+
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ s->or = s->val[OPT_OR].w;
+ s->nr = s->val[OPT_NR].w;
+ s->edge = s->val[OPT_EDGE].w;
+ }
+ else
+ {
+ if (strcmp (edge, EDGE_NONE) == 0)
+ s->edge = 0;
+ else if (strcmp (edge, EDGE_MIDDLE) == 0)
+ s->edge = 1;
+ else if (strcmp (edge, EDGE_STRONG) == 0)
+ s->edge = 2;
+ else if (strcmp (edge, EDGE_BLUR) == 0)
+ s->edge = 3;
+ }
+
+ s->lightcolor = 3;
+ if (strcmp(lightcolor, LIGHT_GREEN) == 0)
+ s->lightcolor = 0;
+ else if (strcmp(lightcolor, LIGHT_RED) == 0)
+ s->lightcolor = 1;
+ else if (strcmp(lightcolor, LIGHT_BLUE) == 0)
+ s->lightcolor = 2;
+ else if (strcmp(lightcolor, LIGHT_NONE) == 0)
+ s->lightcolor = 3;
+
+ s->adf_scan = 0;
+
+#ifdef USE_CUSTOM_GAMMA
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+#endif
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ if (strcmp (gamma, CRT1))
+ s->gamma = 1;
+ else if (strcmp (gamma, CRT2))
+ s->gamma = 2;
+ else if (strcmp (gamma, PRINTER1))
+ s->gamma = 3;
+ else if (strcmp (gamma, PRINTER2))
+ s->gamma = 4;
+ else if (strcmp (gamma, NONE))
+ s->gamma = 5;
+ }
+#if 0
+ if (s->dev->sensedat.model != PCIN500)
+ {
+ ss.dtc = 0x03;
+ if (strcmp (gamma, GAMMA10) == 0)
+ ss.dtq = 0x01;
+ else
+ ss.dtq = 0x02;
+ ss.length = 0;
+ DBG (5, "start: SEND\n");
+ status = send (s->fd, &ss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ }
+ else
+#else
+ {
+ int i;
+ SANE_Word gtbl[256];
+#if 0
+ if (strcmp (gamma, GAMMA10) == 0)
+ for (i = 0; i < 256; i++)
+ gtbl[i] = i;
+ else
+#endif
+ {
+ gtbl[0] = 0;
+ for (i = 1; i < 256; i++)
+ gtbl[i] = 255 * exp(0.45 * log(i/255.0));
+ }
+ send_binary_g_table(s, gtbl, 0x10);
+ send_binary_g_table(s, gtbl, 0x11);
+ send_binary_g_table(s, gtbl, 0x12);
+ send_binary_g_table(s, gtbl, 0x13);
+ }
+#endif /* DEBUG_NEC */
+#ifdef USE_CUSTOM_GAMMA
+ }
+ else
+ {
+ s->gamma = 9;
+ status = send_gamma_tables(s);
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#endif
+
+ s->tint = s->val[OPT_TINT].w;
+ s->color = s->val[OPT_COLOR].w;
+
+ memset (&wp, 0, sizeof (wp));
+ /* every NEC scanner seems to have a different
+ window descriptor block...
+ */
+ if (s->dev->sensedat.model == PCIN500)
+ buf_size = sizeof(WDB) + sizeof(WDBX500);
+ else
+ buf_size = sizeof(WDB);
+
+ wp.wpdh.wdl[0] = buf_size >> 8;
+ wp.wpdh.wdl[1] = buf_size;
+ wp.wdb.x_res[0] = s->res >> 8;
+ wp.wdb.x_res[1] = s->res;
+ wp.wdb.y_res[0] = s->res >> 8;
+ wp.wdb.y_res[1] = s->res;
+ wp.wdb.x_ul[0] = s->ulx >> 24;
+ wp.wdb.x_ul[1] = s->ulx >> 16;
+ wp.wdb.x_ul[2] = s->ulx >> 8;
+ wp.wdb.x_ul[3] = s->ulx;
+ wp.wdb.y_ul[0] = s->uly >> 24;
+ wp.wdb.y_ul[1] = s->uly >> 16;
+ wp.wdb.y_ul[2] = s->uly >> 8;
+ wp.wdb.y_ul[3] = s->uly;
+ wp.wdb.width[0] = s->width >> 24;
+ wp.wdb.width[1] = s->width >> 16;
+ wp.wdb.width[2] = s->width >> 8;
+ wp.wdb.width[3] = s->width;
+ wp.wdb.length[0] = s->length >> 24;
+ wp.wdb.length[1] = s->length >> 16;
+ wp.wdb.length[2] = s->length >> 8;
+ wp.wdb.length[3] = s->length;
+ wp.wdb.brightness = 0;
+ wp.wdb.threshold = s->threshold;
+ wp.wdb.brightness = 128;
+ wp.wdb.contrast = 128;
+ wp.wdb.image_composition = s->image_composition;
+ if (s->image_composition <= 2 || s->image_composition >= 5)
+ wp.wdb.bpp = s->bpp;
+ else
+ wp.wdb.bpp = 1;
+ wp.wdb.ht_pattern[0] = 0;
+ wp.wdb.ht_pattern[1] = 0;
+ if (s->dev->sensedat.model == PCIN500)
+ wp.wdb.ht_pattern[1] = s->halftone;
+ wp.wdb.rif_padding = (s->reverse * 128) + 0;
+
+ if (s->dev->sensedat.model == PCIN500)
+ {
+ wp.wdbx500.data_length = 0x07;
+ wp.wdbx500.control = 0 | (s->or << 7) | (s->edge << 6) | (s->nr << 5) |
+ (s->lightcolor << 2);
+ wp.wdbx500.format = 0;
+ wp.wdbx500.gamma = s->gamma;
+ wp.wdbx500.tint = s->tint;
+ wp.wdbx500.color = s->color;
+ wp.wdbx500.reserved1 = 0;
+ wp.wdbx500.reserved2 = 0;
+ }
+
+ DBG (5, "wdl=%d\n", (wp.wpdh.wdl[0] << 8) + wp.wpdh.wdl[1]);
+ DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
+ DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
+ DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
+ (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
+ DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
+ (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
+ DBG (5, "width=%d\n", (wp.wdb.width[0] << 8) + (wp.wdb.width[1] << 16) +
+ (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
+ DBG (5, "length=%d\n", (wp.wdb.length[0] << 16) + (wp.wdb.length[1] << 16) +
+ (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
+
+ DBG (5, "threshold=%d\n", wp.wdb.threshold);
+ DBG (5, "image_composition=%d\n", wp.wdb.image_composition);
+ DBG (5, "bpp=%d\n", wp.wdb.bpp);
+ DBG (5, "rif_padding=%d\n", wp.wdb.rif_padding);
+
+#ifdef DEBUG_NEC
+ {
+ window_param foo;
+ size_t len = buf_size;
+ len += sizeof(WPDH);
+ DBG (5, "start: GET WINDOW\n");
+ status = get_window (s->fd, &foo, &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#if 1
+ {
+ unsigned char *p = (unsigned char*) &foo;
+ int i;
+ DBG(11, "get window(debug):\n");
+ for (i = 0; i < len; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif
+ foo.wpdh.wpdh[1] = 0;
+ status = set_window (s->fd, &foo, len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#if 1
+ {
+ unsigned char *p = (unsigned char*) &foo;
+ int i;
+ DBG(11, "set window(debug):\n");
+ for (i = 0; i < len; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif
+ }
+#endif /* debug_nec */
+
+#ifdef DEBUG_NEC
+ {
+ unsigned char *p = (unsigned char*) &wp;
+ int i;
+ DBG(11, "set window(debug):\n");
+ for (i = 0; i < sizeof(wp.wdb) + sizeof(wp.wdbx500); i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif /* debug_nec */
+
+ buf_size += sizeof(WPDH);
+ DBG (5, "start: SET WINDOW\n");
+ status = set_window (s->fd, &wp, buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+
+ memset (&wp, 0, buf_size);
+ DBG (5, "start: GET WINDOW\n");
+ status = get_window (s->fd, &wp, &buf_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#ifdef DEBUG_NEC
+ {
+ unsigned char *p = (unsigned char*) &wp;
+ int i;
+ DBG(11, "get window(debug):\n");
+ for (i = 0; i < buf_size; i += 16)
+ {
+ DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
+ p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
+ }
+ }
+#endif
+ DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
+ DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
+ DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
+ (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
+ DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
+ (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
+ DBG (5, "width=%d\n", (wp.wdb.width[0] << 24) + (wp.wdb.width[1] << 16) +
+ (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
+ DBG (5, "length=%d\n", (wp.wdb.length[0] << 24) + (wp.wdb.length[1] << 16) +
+ (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
+ DBG (5, "start: SCAN\n");
+ s->scanning = SANE_TRUE;
+ s->busy = SANE_TRUE;
+ s->cancel = SANE_FALSE;
+ s->get_params_called = 0;
+
+ status = scan (s->fd);
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: scan started %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ s->busy = SANE_FALSE;
+ s->cancel = SANE_FALSE;
+ return (status);
+ }
+
+ /* ask the scanner for the scan size */
+ /* wait_ready(s->fd); */
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: wait_ready ok %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ sane_get_parameters(s, 0);
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: get_params ok %li.%06li\n", t.tv_sec, t.tv_usec);
+ }
+#endif
+ if (strcmp (mode, M_LINEART_COLOR) != 0)
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ else
+ {
+ s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
+ }
+
+#ifdef USE_FORK
+ {
+ size_t i;
+ for (i = 0; i < s->dev->info.buffers; i++)
+ s->rdr_ctl->buf_ctl[i].shm_status = SHM_EMPTY;
+ s->read_buff = 0;
+ s->rdr_ctl->cancel = 0;
+ s->rdr_ctl->running = 0;
+ s->rdr_ctl->status = SANE_STATUS_GOOD;
+ }
+ s->reader_pid = fork();
+#ifdef DEBUG
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ DBG(2, "rd: forked %li.%06li %i\n", t.tv_sec, t.tv_usec,
+ s->reader_pid);
+ }
+#endif
+ if (s->reader_pid == 0)
+ {
+ sigset_t ignore_set;
+ struct SIGACTION act;
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+
+ /* don't use exit() since that would run the atexit() handlers... */
+ _exit (reader_process (s));
+ }
+ else if (s->reader_pid == -1)
+ {
+ s->busy = SANE_FALSE;
+ do_cancel(s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+#endif /* USE_FORK */
+
+
+ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
+ "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
+ s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_RESOLUTION].w);
+
+ s->busy = SANE_FALSE;
+ s->buf_used = 0;
+ s->buf_pos = 0;
+
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ DBG (10, ">>\n");
+ return(SANE_STATUS_CANCELLED);
+ }
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+
+}
+
+static SANE_Status
+sane_read_direct (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+ DBG (10, "<< sane_read_direct ");
+
+#if 0
+ {
+ buffer_status bs;
+ size_t len = sizeof (buffer_status);
+ get_data_buffer_status (s->fd, &bs, &len);
+ DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]);
+ }
+#endif
+ DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read);
+ *len = 0;
+
+ if (s->bytes_to_read == 0)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning)
+ return (do_cancel (s));
+ nread = max_len;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ if (nread > s->dev->info.bufsize)
+ nread = s->dev->info.bufsize;
+
+#ifdef USE_FORK
+ status = read_data(s, dst_buf, &nread);
+#else
+#ifdef NOTUSE_PCIN500
+ wait_ready(s->fd);
+#endif
+ status = read_data (s, dst_buf, &nread);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ return (SANE_STATUS_IO_ERROR);
+ }
+ *len = nread;
+ s->bytes_to_read -= nread;
+ DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read);
+
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static SANE_Status
+sane_read_shuffled (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len, int eight_bit_data)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Byte *dest, *red, *green, *blue, mask;
+ SANE_Int transfer;
+ size_t nread, ntest, pixel, max_pixel, line, max_line;
+ size_t start_input, bytes_per_line_in;
+ DBG (10, "<< sane_read_shuffled ");
+
+#if 0
+ {
+ buffer_status bs;
+ size_t len = sizeof (buffer_status);
+ get_data_buffer_status (s->fd, &bs, &len);
+ DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]);
+ }
+#endif
+ *len = 0;
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ {
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_EOF);
+ }
+
+ if (!s->scanning)
+ {
+ DBG (10, ">>\n");
+ return(do_cancel(s));
+ }
+
+ if (s->buf_pos < s->buf_used)
+ {
+ transfer = s->buf_used - s->buf_pos;
+ if (transfer > max_len)
+ transfer = max_len;
+
+ memcpy(dst_buf, &(s->buffer[s->buf_pos]), transfer);
+ s->buf_pos += transfer;
+ max_len -= transfer;
+ *len = transfer;
+ }
+
+ while (max_len > 0 && s->bytes_to_read > 0)
+ {
+ if (eight_bit_data)
+ {
+ nread = s->dev->info.bufsize / s->params.bytes_per_line - 1;
+ nread *= s->params.bytes_per_line;
+ if (nread > s->bytes_to_read)
+ nread = s->bytes_to_read;
+ max_line = nread / s->params.bytes_per_line;
+ start_input = s->params.bytes_per_line;
+ bytes_per_line_in = s->params.bytes_per_line;
+ }
+ else
+ {
+ bytes_per_line_in = (s->params.pixels_per_line + 7) / 8;
+ bytes_per_line_in *= 3;
+ max_line = s->params.bytes_per_line + bytes_per_line_in;
+ max_line = s->dev->info.bufsize / max_line;
+ nread = max_line * bytes_per_line_in;
+ if (nread > s->bytes_to_read)
+ {
+ nread = s->bytes_to_read;
+ max_line = nread / bytes_per_line_in;
+ }
+ start_input = s->dev->info.bufsize - nread;
+ }
+ ntest = nread;
+
+#ifdef USE_FORK
+ status = read_data (s, &(s->buffer[start_input]), &nread);
+#else
+ status = read_data (s, &(s->buffer[start_input]), &nread);
+#endif
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (nread != ntest)
+ {
+ /* if this happens, something is wrong in the input buffer
+ management...
+ */
+ DBG(1, "Warning: could not read an integral number of scan lines\n");
+ DBG(1, " image will be scrambled\n");
+ }
+
+
+ s->buf_used = max_line * s->params.bytes_per_line;
+ s->buf_pos = 0;
+ s->bytes_to_read -= nread;
+ dest = s->buffer;
+ max_pixel = s->params.pixels_per_line;
+
+ if (eight_bit_data)
+ for (line = 1; line <= max_line; line++)
+ {
+ red = &(s->buffer[line * s->params.bytes_per_line]);
+ green = &(red[max_pixel]);
+ blue = &(green[max_pixel]);
+ for (pixel = 0; pixel < max_pixel; pixel++)
+ {
+ *dest++ = *red++;
+ *dest++ = *green++;
+ *dest++ = *blue++;
+ }
+ }
+ else
+ for (line = 0; line < max_line; line++)
+ {
+ red = &(s->buffer[start_input + line * bytes_per_line_in]);
+ green = &(red[(max_pixel+7)/8]);
+ blue = &(green[(max_pixel+7)/8]);
+ mask = 0x80;
+ for (pixel = 0; pixel < max_pixel; pixel++)
+ {
+ *dest++ = (*red & mask) ? 0xff : 0;
+ *dest++ = (*green & mask) ? 0xff : 0;
+ *dest++ = (*blue & mask) ? 0xff : 0;
+ mask = mask >> 1;
+ if (mask == 0)
+ {
+ mask = 0x80;
+ red++;
+ green++;
+ blue++;
+ }
+ }
+ }
+
+ transfer = max_len;
+ if (transfer > s->buf_used)
+ transfer = s->buf_used;
+ memcpy(&(dst_buf[*len]), s->buffer, transfer);
+
+ max_len -= transfer;
+ s->buf_pos += transfer;
+ *len += transfer;
+ }
+
+ if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
+ do_cancel (s);
+ DBG (10, ">>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ NEC_Scanner *s = handle;
+ SANE_Status status;
+
+ DBG (10, "<< sane_read ");
+ s->busy = SANE_TRUE;
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ *len = 0;
+ return (SANE_STATUS_CANCELLED);
+ }
+
+ if (s->image_composition <= 2)
+ status = sane_read_direct(handle, dst_buf, max_len, len);
+ else if (s->image_composition <= 4)
+ status = sane_read_shuffled(handle, dst_buf, max_len, len, 0);
+ else if (s->dev->sensedat.model == PCIN500)
+ status = sane_read_direct(handle, dst_buf, max_len, len);
+ else
+ status = sane_read_shuffled(handle, dst_buf, max_len, len, 1);
+
+ s->busy = SANE_FALSE;
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ return (SANE_STATUS_CANCELLED);
+ }
+
+ DBG (10, ">> \n");
+ return (status);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ NEC_Scanner *s = handle;
+ DBG (10, "<< sane_cancel ");
+
+ s->cancel = SANE_TRUE;
+ if (s->busy == SANE_FALSE)
+ do_cancel(s);
+
+ DBG (10, ">>\n");
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ handle = handle;
+ non_blocking = non_blocking; /* silence compilation warnings */
+
+ DBG (10, "<< sane_set_io_mode");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ handle = handle;
+ fd = fd; /* silence compilation warnings */
+
+ DBG (10, "<< sane_get_select_fd");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}