summaryrefslogtreecommitdiff
path: root/backend/sharp.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/sharp.c')
-rw-r--r--backend/sharp.c4213
1 files changed, 4213 insertions, 0 deletions
diff --git a/backend/sharp.c b/backend/sharp.c
new file mode 100644
index 0000000..1225a57
--- /dev/null
+++ b/backend/sharp.c
@@ -0,0 +1,4213 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1998, 1999
+ Kazuya Fukuda, Abel Deuring based on BYTEC GmbH Germany
+ Written by Helmut Koeberle previous Work on canon.c file from the
+ SANE package.
+
+ 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 Sharp flatbed scanners. */
+
+/*
+ Version 0.32
+ changes to version 0.31:
+ - support for JX320 added (Thanks to Isaac Wilcox for providind the
+ patch)
+
+ Version 0.31
+ changes to version 0.30:
+ - support for JX350 added (Thanks to Shuhei Tomita for providind the
+ patch)
+
+ changes to version 0.20
+ - support for the proposed extended open function in sanei_scsi.c added
+ - support for ADF and FSU (transparency adapter) added
+ - simple sense handler added
+ - preview added
+ - added several missing statements "s->fd = -1;" after
+ "sanei_scsi_close(s->fd)" to error returns in sane_start();
+ - maximum scan sizes are read from the scanner, if a JX330 or JX250
+ is used. (this avoids the guessing of scan sizes for the JX330)
+ - gamma table support added
+ - "Fixed gamma selection (1.0/2.2)", available for JX330 and JX610,
+ is now implemented for the JX250 by downloading a gamma table
+ - changed the calls to free() and strdup() in sane_control_option to
+ strcpy.
+ (I don't like too frequent unchecked malloc()s and strdups :) Abel)
+ - cleaned up some quirks in option handling, eg, that "threshold"
+ was initially enabled, while the initial scan mode is "color"
+ - cleaned up setting SANE_INFO_RELOAD_OPTIONS and SANE_INFO_RELOAD_PARAMS
+ bits in sane_control_option
+ - bi-level color scans now give useful (8 bit) output
+ - separate thresholds for red, green, blue (bi-level color scan) added
+*/
+#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
+*/
+#if 0
+#define QUEUEDEBUG
+#define DEBUG
+#ifdef DEBUG
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+#endif
+
+/* USE_FORK: fork a special reader process
+*/
+
+#ifdef HAVE_SYS_SHM_H
+#ifndef HAVE_OS2_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 */
+
+/* xxx I'm not sure, if I understood the JX610 and JX330 manuals right,
+ that the data for the SEND command should be in ASCII format...
+ SEND commands with a data bock are used, if USE_CUSTOM_GAMMA
+ and / or USE_COLOR_THRESHOLD are enabled.
+ Abel
+*/
+#define USE_CUSTOM_GAMMA
+#define USE_COLOR_THRESHOLD
+/* 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 */
+
+/* enable separate specification of resolution in X and Y direction.
+ XSane will show the Y-resolution at a quite different place than
+ the X-resolution
+*/
+/* #define USE_SEPARATE_Y_RESOLUTION */
+
+
+#define BACKEND_NAME sharp
+#include "../include/sane/sanei_backend.h"
+
+#include <sharp.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define DEFAULT_MUD_JX610 25
+#define DEFAULT_MUD_JX320 25
+#define DEFAULT_MUD_JX330 1200
+#define DEFAULT_MUD_JX250 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 SHARP_CONFIG_FILE "sharp.conf"
+
+static int num_devices = 0;
+static SHARP_Device *first_dev = NULL;
+static SHARP_Scanner *first_handle = NULL;
+
+typedef enum
+ {
+ MODES_LINEART = 0,
+ MODES_GRAY,
+ MODES_LINEART_COLOR,
+ MODES_COLOR
+ }
+Modes;
+
+#define M_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define M_GRAY SANE_VALUE_SCAN_MODE_GRAY
+#define M_LINEART_COLOR SANE_VALUE_SCAN_MODE_COLOR_LINEART
+#define M_COLOR SANE_VALUE_SCAN_MODE_COLOR
+static const SANE_String_Const mode_list[] =
+{
+ M_LINEART, M_GRAY, M_LINEART_COLOR, 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"
+
+static const SANE_String_Const halftone_list[] =
+{
+ M_BILEVEL, M_BAYER, M_SPIRAL, M_DISPERSED, M_ERRDIFFUSION,
+ 0
+};
+
+#define LIGHT_GREEN "green"
+#define LIGHT_RED "red"
+#define LIGHT_BLUE "blue"
+#define LIGHT_WHITE "white"
+
+#define MAX_RETRIES 50
+
+static const SANE_String_Const light_color_list[] =
+{
+ LIGHT_GREEN, LIGHT_RED, LIGHT_BLUE, LIGHT_WHITE,
+ 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";
+
+/* auto selection of ADF and FSU, as described in the JX330 manual,
+ is a nice idea -- but I assume that the possible scan window
+ sizes depend not only for the JX250, but also for JX330 on the
+ usage of ADF or FSU. Thus, the user might be able to select scan
+ windows of an "illegal" size, which would have to be automatically
+ corrected, and I don't see, how the user could be informed about
+ this "window clipping". More important, I don't see, how the
+ frontend could be informed that the ADF is automatically enabled.
+
+ Insert a "#define ALLOW_AUTO_SELECT_ADF", if you want to play
+ with this feature.
+*/
+#ifdef ALLOW_AUTO_SELECT_ADF
+static SANE_String_Const use_auto = "AutoSelection";
+#endif
+
+#define HAVE_FSU 1
+#define HAVE_ADF 2
+
+/* The follow #defines are used in SHARP_Scanner.adf_fsu_mode
+ and as indexes for the arrays x_ranges, y_ranges in SHARP_Device
+*/
+#define SCAN_SIMPLE 0
+#define SCAN_WITH_FSU 1
+#define SCAN_WITH_ADF 2
+#ifdef ALLOW_AUTO_SELECT_ADF
+#define SCAN_ADF_FSU_AUTO 3
+#endif
+#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_jx610[] =
+{
+ "A3", "A4", "A5", "A6", "B4", "B5",
+ W_LETTER, "Legal", "Letter", INVOICE,
+ 0
+};
+
+static const SANE_String_Const paper_list_jx330[] =
+{
+ "A4", "A5", "A6", "B5",
+ 0
+};
+
+#define GAMMA10 "1.0"
+#define GAMMA22 "2.2"
+
+static const SANE_String_Const gamma_list[] =
+{
+ GAMMA10, GAMMA22,
+ 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_JX610 8
+static const SANE_String_Const resolution_list_jx610[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "600", "Select",
+ 0
+};
+
+#define RESOLUTION_MAX_JX250 7
+static const SANE_String_Const resolution_list_jx250[] =
+{
+ "50", "75", "100", "150", "200", "300", "400", "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 __sane_unused__ fd, u_char *sense_buffer, void *s)
+{
+ int sense_key;
+ SHARP_Sense_Data *sdat = (SHARP_Sense_Data *) s;
+
+#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[2] & 0x0F;
+ /* do we have additional information ? */
+ if (sense_buffer[7] >= 5)
+ {
+ if (sdat->model == JX610)
+ {
+ /* The JX610 uses somewhat different error codes */
+ switch (add_sense_code)
+ {
+ case 0x04:
+ DBG(5, "error: scanner not ready\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x08:
+ DBG(5, "error: scanner communication failure (time out?)\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x1A:
+ DBG(10, "error: parameter list length error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x20:
+ DBG(10, "error: invalid command code\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x24:
+ DBG(10, "error: invalid field in CDB\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x25:
+ DBG(10, "error: LUN not supported\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x26:
+ DBG(10, "error: invalid field in parameter list\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x29:
+ DBG(10, "note: reset occured\n");
+ return SANE_STATUS_GOOD;
+ case 0x2a:
+ DBG(10, "note: mode parameter change\n");
+ return SANE_STATUS_GOOD;
+ case 0x37:
+ DBG(10, "note: rounded parameter\n");
+ return SANE_STATUS_GOOD;
+ case 0x39:
+ DBG(10, "error: saving parameter not supported\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x47:
+ DBG(10, "SCSI parity error\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x48:
+ DBG(10, "initiator detected error message received\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x60:
+ DBG(1, "error: lamp failure\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x62:
+ DBG(1, "scan head positioning error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ }
+ else if (sdat->model == JX250 || sdat->model == JX330 ||
+ sdat->model == JX350 || sdat->model == JX320)
+ {
+ switch (sense_key)
+ {
+ case 0x02: /* not ready */
+ switch (add_sense_code)
+ {
+ case 0x80:
+ switch (add_sense_qual)
+ {
+ case 0:
+ DBG(1, "Scanner not ready: ADF cover open\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_COVER_OPEN;
+ else
+ return SANE_STATUS_GOOD;
+ case 1:
+ DBG(1, "Scanner not ready: ADF maintenance "
+ "cover open\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_COVER_OPEN;
+ else
+ return SANE_STATUS_GOOD;
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x81:
+ /* NOT TESTED -- I don't have a FSU */
+ switch (add_sense_qual)
+ {
+ case 0:
+ DBG(1, "Scanner not ready: FSU cover open\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_FSU_ERROR)
+ return SANE_STATUS_COVER_OPEN;
+ else
+ return SANE_STATUS_GOOD;
+ case 1:
+ DBG(1, "Scanner not ready: FSU light dispersion "
+ "error\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_FSU_ERROR)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ return SANE_STATUS_GOOD;
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ default:
+ DBG(5, "Scanner not ready: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x03: /* medium error */
+ switch (add_sense_code)
+ {
+ case 0x3a:
+ DBG(1, "ADF is empty\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_NO_DOCS;
+ else
+ return SANE_STATUS_GOOD;
+ case 0x53:
+ DBG(1, "ADF paper jam\n"
+ "Open and close the maintenance cover to clear "
+ "this error\n");
+ if (sdat->complain_on_errors & COMPLAIN_ON_ADF_ERROR)
+ return SANE_STATUS_JAMMED;
+ else
+ return SANE_STATUS_GOOD;
+ default:
+ DBG(5, "medium error: undocumented reason\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x04: /* hardware error */
+ switch (add_sense_code)
+ {
+ case 0x08:
+ DBG(1, "hardware error: scanner communication failed\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x60:
+ DBG(1, "hardware error: lamp failure\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x62:
+ DBG(1, "hardware error: scan head positioning failed\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ 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 */
+ switch (add_sense_code)
+ {
+ case 0x29:
+ DBG(5, "unit attention: reset occured\n");
+ return SANE_STATUS_GOOD;
+ case 0x2a:
+ DBG(5, "unit attention: parameter changed by "
+ "another initiator\n");
+ return SANE_STATUS_IO_ERROR;
+ default:
+ DBG(5, "unit attention: exact reason not documented\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ case 0x09: /* data remains */
+ DBG(5, "error: data remains\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->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);
+}
+
+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;
+#ifdef ALLOW_AUTO_SELECT_ADF
+ case: SCAN_ADF_FSU_AUTO:
+ mp->a_mode = 0;
+ mp->f_mode = 0;
+ break;
+#endif
+ }
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+static SANE_Status wait_ready(int fd);
+
+static SANE_Status
+object_position(int fd, int load)
+{
+ static u_char cmd[] = {OBJECT_POSITION, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< object_position ");
+
+ cmd[1] = load;
+
+ wait_ready(fd);
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+reserve_unit (int fd)
+{
+ static u_char cmd[] = {RESERVE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< reserve_unit ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+release_unit (int fd)
+{
+ static u_char cmd[] = {RELEASE_UNIT, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< release_unit ");
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+mode_sense (int fd, void *modeparam_buf, size_t * modeparam_size,
+ int page)
+{
+ static u_char cmd[6];
+ SANE_Status status;
+ DBG (11, "<< mode_sense ");
+
+ memset (cmd, 0, sizeof (cmd));
+ 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
+send (int fd, SHARP_Send * ss)
+{
+ static u_char cmd[] = {SEND, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ SANE_Status status;
+ DBG (11, "<< send ");
+
+ cmd[2] = ss->dtc;
+ cmd[4] = ss->dtq >> 8;
+ cmd[5] = ss->dtq;
+ cmd[6] = ss->length >> 16;
+ cmd[7] = ss->length >> 8;
+ cmd[8] = ss->length >> 0;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, ">>\n");
+ return (status);
+
+}
+
+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);
+}
+
+#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(SHARP_Scanner *s)
+{
+ return s->rdr_ctl->cancel;
+}
+
+static SANE_Status
+rdr_status(SHARP_Scanner *s)
+{
+ return s->rdr_ctl->status;
+}
+
+static int
+buf_status(SHARP_shmem_ctl *s)
+{
+ return s->shm_status;
+}
+
+static int
+reader_running(SHARP_Scanner *s)
+{
+ return s->rdr_ctl->running;
+}
+
+static int
+reader_process(SHARP_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, retries = MAX_RETRIES;
+ SHARP_shmem_ctl *bc;
+
+ s->rdr_ctl->running = 1;
+ DBG(11, "<< reader_process\n");
+
+ sigemptyset (&sigterm_set);
+
+ bytes_to_queue = s->bytes_to_read;
+
+ /* it seems that some carriage stops can be avoided with the
+ JX-250, if the data of an integral number of scan lines is
+ read with one SCSI command
+ */
+ 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;
+ if (max_queue <= 0)
+ max_queue = 1;
+ 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_DEVICE_BUSY && retries)
+ {
+ bc->used = 0;
+ retries--;
+ DBG(11, "reader: READ command returned BUSY\n");
+ status = SANE_STATUS_GOOD;
+ usleep(10000);
+ }
+ else if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "reader_process: read command failed: %s\n",
+ 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;
+ }
+ else
+ {
+ retries = MAX_RETRIES;
+ }
+#if 1
+ s->bytes_to_read -= bc->used;
+ bytes_to_queue += bc->nreq - bc->used;
+#else
+ /* xxxxxxxxxxxxxxxxxxx TEST xxxxxxxxxxxxxxx */
+ s->bytes_to_read -= bc->nreq;
+ /* memset(bc->buffer + bc->used, 0, bc->nreq - bc->used); */
+ bc->used = bc->nreq;
+ /* bytes_to_queue += bc->nreq - bc->used; */
+ DBG(1, "btr: %i btq: %i nreq: %i nrcv: %i\n",
+ s->bytes_to_read, bytes_to_queue, bc->nreq, bc->used);
+#endif
+ 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 (SHARP_Scanner *s, SANE_Byte *buf, size_t * buf_size)
+{
+ size_t copysize, copied = 0;
+ SHARP_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 (SHARP_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;
+ int retries = MAX_RETRIES;
+ 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_DEVICE_BUSY && retries)
+ {
+ retries--;
+ nread = 0;
+ usleep(10000);
+ }
+ else if (status != SANE_STATUS_GOOD)
+ {
+ DBG(11, ">>\n");
+ return(status);
+ }
+ else
+ {
+ retries = MAX_RETRIES;
+ }
+ 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);
+ if (retry++ > 15){
+ return SANE_STATUS_IO_ERROR;
+ }
+ sleep(3);
+ }
+ return (status);
+
+}
+
+/* ask the scanner for the maximum scan sizes with/without ADF and
+ FSU. The JX330 manual does mention the sizes.
+*/
+static SANE_Status
+get_max_scan_size(int fd, SHARP_Device *dev, int mode)
+{
+ SANE_Status status;
+ mode_sense_subdevice m_subdev;
+ size_t buf_size;
+
+ status = mode_select_adf_fsu(fd, mode);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "get_scan_sizes: MODE_SELECT/subdevice page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ DBG (3, "get_scan_sizes: sending MODE SENSE/subdevice page\n");
+ memset (&m_subdev, 0, sizeof (m_subdev));
+ buf_size = sizeof (m_subdev);
+ status = mode_sense (fd, &m_subdev, &buf_size, 0x20);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "get_scan_sizes: MODE_SENSE/subdevice page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ dev->info.tl_x_ranges[mode].min = 0;
+ dev->info.tl_x_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_x[0] << 24) + (m_subdev.max_x[1] << 16) +
+ (m_subdev.max_x[2] << 8) + m_subdev.max_x[3] - 1, dev->info.mud));
+ dev->info.tl_x_ranges[mode].quant = 0;
+
+ dev->info.br_x_ranges[mode].min = SANE_FIX(PIX_TO_MM(1, dev->info.mud));
+ dev->info.br_x_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_x[0] << 24) + (m_subdev.max_x[1] << 16) +
+ (m_subdev.max_x[2] << 8) + m_subdev.max_x[3], dev->info.mud));
+ dev->info.br_x_ranges[mode].quant = 0;
+
+ dev->info.tl_y_ranges[mode].min = 0;
+ if ((dev->sensedat.model != JX250 && dev->sensedat.model != JX350) ||
+ mode != SCAN_WITH_FSU)
+ dev->info.tl_y_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_y[0] << 24) + (m_subdev.max_y[1] << 16) +
+ (m_subdev.max_y[2] << 8) + m_subdev.max_y[3] - 1, dev->info.mud));
+ else
+ /* The manual for the JX250 states on page 62 that the maximum
+ value for tl_y in FSU mode is 13199, while the max value for
+ br_y is 13900, which is (probably -- I don't have a FSU) returned
+ by mode sense/subdevice page. Therefore, we cannot simply
+ decrement that value and store it as max(tl_y).
+ */
+ dev->info.tl_y_ranges[mode].max = 13199;
+ dev->info.tl_y_ranges[mode].quant = 0;
+
+ dev->info.br_y_ranges[mode].min = SANE_FIX(PIX_TO_MM(1, dev->info.mud));
+ dev->info.br_y_ranges[mode].max = SANE_FIX(PIX_TO_MM(
+ (m_subdev.max_y[0] << 24) + (m_subdev.max_y[1] << 16) +
+ (m_subdev.max_y[2] << 8) + m_subdev.max_y[3], dev->info.mud));
+ dev->info.br_y_ranges[mode].quant = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach (const char *devnam, SHARP_Device ** devp)
+{
+ SANE_Status status;
+ SHARP_Device *dev;
+ SHARP_Sense_Data sensedat;
+
+ int fd;
+ char inquiry_data[INQUIRY_LEN];
+ const char *model_name;
+ mode_sense_param msp;
+ mode_sense_subdevice m_subdev;
+ 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_errors = 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 (inquiry_data + 8, "SHARP", 5) == 0)
+ {
+ if (strncmp (inquiry_data + 16, "JX610", 5) == 0)
+ sensedat.model = JX610;
+ else if (strncmp (inquiry_data + 16, "JX250", 5) == 0)
+ sensedat.model = JX250;
+ else if (strncmp (inquiry_data + 16, "JX350", 5) == 0)
+ sensedat.model = JX350;
+ else if ( strncmp (inquiry_data + 16, "JX320", 5) == 0
+ || strncmp (inquiry_data + 16, "JX325", 5) == 0)
+ sensedat.model = JX320;
+ else if (strncmp (inquiry_data + 16, "JX330", 5) == 0)
+ sensedat.model = JX330;
+ }
+
+ if (sensedat.model == unknown)
+ {
+ DBG (1, "attach: device doesn't look like a Sharp scanner\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");
+ /* JX-610 probably supports only 25 MUD size
+ JX-320 only supports 25 MUD size
+ */
+ if (strncmp (inquiry_data + 16, "JX610", 5) == 0)
+ status = mode_select_mud (fd, DEFAULT_MUD_JX610);
+ else if (strncmp (inquiry_data + 16, "JX320", 5) == 0)
+ status = mode_select_mud (fd, DEFAULT_MUD_JX320);
+ else
+ status = mode_select_mud (fd, DEFAULT_MUD_JX330);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SELECT6 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);
+ }
+
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+
+ dev->sane.name = strdup (devnam);
+ dev->sane.vendor = "SHARP";
+ model_name = (char*) inquiry_data + 16;
+ dev->sane.model = strndup (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);
+
+ dev->info.xres_range.quant = 0;
+ dev->info.yres_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);
+
+ dev->info.xres_default = 150;
+ dev->info.yres_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 == JX610)
+ {
+ dev->info.xres_range.max = 600;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 600;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(303); /* 304.8mm is the real max */
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(304); /* 304.8mm is the real max */
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(430); /* 431.8 is the real max */
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(431); /* 431.8 is the real max */
+ }
+ else if (dev->sensedat.model == JX320)
+ {
+ dev->info.xres_range.max = 600;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 600;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(212);
+ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(213);
+
+ dev->info.y_default = SANE_FIX(297);
+ dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(292);
+ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(293);
+ }
+ else
+ {
+ /* ask the scanner, if ADF or FSU are installed, and ask for
+ the maximum scan sizes with/without ADF and FSU.
+ */
+
+ DBG (3, "attach: sending MODE SENSE/subdevice page\n");
+ memset (&m_subdev, 0, sizeof (m_subdev));
+ buf_size = sizeof (m_subdev);
+ status = mode_sense (fd, &m_subdev, &buf_size, 0x20);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "attach: MODE_SENSE/subdevice page failed\n");
+ sanei_scsi_close (fd);
+ return (SANE_STATUS_INVAL);
+ }
+
+ /* The JX330 manual is not very clear about the ADF- und FSU-Bits
+ returned by a JX320 and JX325 for the mode sense command:
+ Are these bits set to zero or not? To be on the safe side, let's
+ clear them.
+ */
+
+ if ( strncmp(inquiry_data + 16, "JX320", 5) == 0
+ || strncmp(inquiry_data + 16, "JX325", 5) == 0)
+ {
+ m_subdev.f_mode_type = 0;
+ m_subdev.a_mode_type = 0;
+ }
+
+ get_max_scan_size(fd, dev, SCAN_SIMPLE);
+
+ if (m_subdev.a_mode_type & 0x03)
+ {
+ dev->info.adf_fsu_installed = HAVE_ADF;
+ get_max_scan_size(fd, dev, SCAN_WITH_ADF);
+ }
+ if (m_subdev.f_mode_type & 0x07)
+ {
+ dev->info.adf_fsu_installed |= HAVE_FSU;
+ get_max_scan_size(fd, dev, SCAN_WITH_FSU);
+ }
+
+ if ( dev->sensedat.model == JX320
+ || dev->sensedat.model == JX330
+ || dev->sensedat.model == JX350)
+ {
+ dev->info.xres_range.max = 600;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 600;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.y_default = SANE_FIX(297);
+ }
+ else if (dev->sensedat.model == JX250)
+ {
+ dev->info.xres_range.max = 400;
+ dev->info.xres_range.min = 30;
+
+ dev->info.yres_range.max = 400;
+ dev->info.yres_range.min = 30;
+ dev->info.x_default = SANE_FIX(210);
+ dev->info.y_default = SANE_FIX(297);
+ }
+ }
+ sanei_scsi_close (fd);
+
+ dev->info.threshold_range.min = 1;
+ dev->info.threshold_range.max = 255;
+ dev->info.threshold_range.quant = 0;
+
+ DBG (5, "xres_default=%d\n", dev->info.xres_default);
+ DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max);
+ DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min);
+ DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant);
+ DBG (5, "yres_default=%d\n", dev->info.yres_default);
+ DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max);
+ DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min);
+ DBG (5, "xres_range.quant=%d\n", dev->info.xres_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(SHARP_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].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].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;
+ }
+ }
+}
+#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(SHARP_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 (SHARP_Scanner * s)
+{
+ int i, default_source, sourcename_index = 0;
+ 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 */
+#if 0
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup (mode_list[3]); /* color scan */
+#endif
+ init_string_option(s, SANE_NAME_SCAN_MODE, SANE_TITLE_SCAN_MODE,
+ SANE_DESC_SCAN_MODE, mode_list, OPT_MODE, 3);
+
+ /* half tone */
+#if 0
+ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE_PATTERN;
+ s->opt[OPT_HALFTONE].desc = SANE_DESC_HALFTONE " (JX-330 only)";
+ s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ s->opt[OPT_HALFTONE].size = max_string_size (halftone_list);
+ s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
+ s->val[OPT_HALFTONE].s = strdup (halftone_list[0]);
+#endif
+ init_string_option(s, SANE_NAME_HALFTONE_PATTERN, SANE_TITLE_HALFTONE_PATTERN,
+ SANE_DESC_HALFTONE " (JX-330 only)", halftone_list, OPT_HALFTONE, 0);
+
+ if (s->dev->sensedat.model == JX250 || s->dev->sensedat.model == JX350 ||
+ s->dev->sensedat.model == JX610 || s->dev->sensedat.model == JX320)
+ s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+
+ i = 0;
+ default_source = s->dev->info.default_scan_mode;
+
+#ifdef ALLOW_AUTO_SELECT_ADF
+ /* The JX330, but nut not the JX250 supports auto selection of ADF/FSU: */
+ if (s->dev->info.adf_fsu_installed && (s->dev->sensedat.model == JX330))
+ s->dev->info->scansources[i++] = use_auto;
+#endif
+ if (s->dev->info.adf_fsu_installed & HAVE_ADF)
+ {
+ if (default_source == -1)
+ default_source = SCAN_WITH_ADF;
+ if (default_source == SCAN_WITH_ADF)
+ sourcename_index = i;
+ s->dev->info.scansources[i++] = use_adf;
+ }
+ else
+ {
+ if (default_source == SCAN_WITH_ADF)
+ default_source = SCAN_SIMPLE;
+ }
+ if (s->dev->info.adf_fsu_installed & HAVE_FSU)
+ {
+ if (default_source == -1)
+ default_source = SCAN_WITH_FSU;
+ if (default_source == SCAN_WITH_FSU)
+ sourcename_index = i;
+ s->dev->info.scansources[i++] = use_fsu;
+ }
+ else
+ {
+ if (default_source == SCAN_WITH_FSU)
+ default_source = SCAN_SIMPLE;
+ }
+ if (default_source < 0)
+ default_source = SCAN_SIMPLE;
+ if (default_source == SCAN_SIMPLE)
+ sourcename_index = i;
+ s->dev->info.scansources[i++] = use_simple;
+ s->dev->info.scansources[i] = 0;
+
+#if 0
+ s->opt[OPT_SCANSOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SCANSOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SCANSOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SCANSOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCANSOURCE].size = max_string_size (s->dev->info.scansources);
+ s->opt[OPT_SCANSOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCANSOURCE].constraint.string_list = (SANE_String_Const*)s->dev->info.scansources;
+ s->val[OPT_SCANSOURCE].s = strdup (s->dev->info.scansources[0]);
+#endif
+
+ 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, sourcename_index);
+
+ if (i < 2)
+ s->opt[OPT_SCANSOURCE].cap |= SANE_CAP_INACTIVE;
+
+#if 0
+ s->opt[OPT_PAPER].name = "Paper size";
+ s->opt[OPT_PAPER].title = "Paper size";
+ s->opt[OPT_PAPER].desc = "Paper size";
+ s->opt[OPT_PAPER].type = SANE_TYPE_STRING;
+ /* xxx the possible values for the paper size should be changeable,
+ to reflect the different maximum scan sizes with/without ADF and FSU
+ */
+ if (s->dev->sensedat.model == JX610)
+ {
+ s->opt[OPT_PAPER].size = max_string_size (paper_list_jx610);
+ s->opt[OPT_PAPER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER].constraint.string_list = paper_list_jx610;
+ s->val[OPT_PAPER].s = strdup (paper_list_jx610[1]);
+ }
+ else
+ {
+ s->opt[OPT_PAPER].size = max_string_size (paper_list_jx330);
+ s->opt[OPT_PAPER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_PAPER].constraint.string_list = paper_list_jx330;
+ s->val[OPT_PAPER].s = strdup (paper_list_jx330[0]);
+ }
+#endif
+
+ if (s->dev->sensedat.model == JX610)
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_jx610, OPT_PAPER, 1);
+ else
+ init_string_option(s, "Paper size", "Paper size",
+ "Paper size", paper_list_jx330, OPT_PAPER, 0);
+
+ /* gamma */
+#if 0
+ s->opt[OPT_GAMMA].name = "Gamma";
+ s->opt[OPT_GAMMA].title = "Gamma";
+ s->opt[OPT_GAMMA].desc = "Gamma";
+ s->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
+ s->opt[OPT_GAMMA].size = max_string_size (gamma_list);
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_GAMMA].constraint.string_list = gamma_list;
+ s->val[OPT_GAMMA].s = strdup (gamma_list[1]);
+#endif
+
+ init_string_option(s, "Gamma", "Gamma", "Gamma", gamma_list, OPT_GAMMA, 1);
+
+ /* scan speed */
+ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ s->opt[OPT_SPEED].title = "Scan speed [fast]";
+ s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
+ s->opt[OPT_SPEED].type = SANE_TYPE_BOOL;
+ s->val[OPT_SPEED].w = SANE_TRUE;
+
+ /* 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;
+
+ /* select resolution */
+#ifdef USE_RESOLUTION_LIST
+ if (s->dev->sensedat.model == JX610 || s->dev->sensedat.model == JX330 ||
+ s->dev->sensedat.model == JX350 || s->dev->sensedat.model == JX320)
+ init_string_option(s, "ResolutionList", "ResolutionList", "ResolutionList",
+ resolution_list_jx610, OPT_RESOLUTION_LIST, RESOLUTION_MAX_JX610);
+ else
+ init_string_option(s, "ResolutionList", "ResolutionList", "ResolutionList",
+ resolution_list_jx250, OPT_RESOLUTION_LIST, RESOLUTION_MAX_JX250);
+#endif
+ /* x resolution */
+ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_X_RESOLUTION].constraint.range = &s->dev->info.xres_range;
+ s->val[OPT_X_RESOLUTION].w = s->dev->info.xres_default;
+
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ /* y resolution */
+ s->opt[OPT_Y_RESOLUTION].name = "Y" SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].title = "Y " SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_Y_RESOLUTION].constraint.range = &s->dev->info.yres_range;
+ s->val[OPT_Y_RESOLUTION].w = s->dev->info.yres_default;
+#endif
+
+ /* "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];
+ /* The FSU for JX250 allows a maximum scan length of 11.5 inch,
+ which is less than the default value of 297 mm
+ */
+ 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 */
+#if 0
+ s->opt[OPT_EDGE_EMPHASIS].name = "Edge emphasis";
+ s->opt[OPT_EDGE_EMPHASIS].title = "Edge emphasis";
+ s->opt[OPT_EDGE_EMPHASIS].desc = "Edge emphasis";
+ s->opt[OPT_EDGE_EMPHASIS].type = SANE_TYPE_STRING;
+ s->opt[OPT_EDGE_EMPHASIS].size = max_string_size (edge_emphasis_list);
+ s->opt[OPT_EDGE_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_EDGE_EMPHASIS].constraint.string_list = edge_emphasis_list;
+ s->val[OPT_EDGE_EMPHASIS].s = strdup (edge_emphasis_list[0]);
+#endif
+ init_string_option(s, "Edge emphasis", "Edge emphasis",
+ "Edge emphasis", edge_emphasis_list,
+ OPT_EDGE_EMPHASIS, 0);
+
+ if ( s->dev->sensedat.model == JX250 || s->dev->sensedat.model == JX350
+ || s->dev->sensedat.model == JX320)
+ s->opt[OPT_EDGE_EMPHASIS].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) */
+#if 0
+ s->opt[OPT_LIGHTCOLOR].name = "LightColor";
+ s->opt[OPT_LIGHTCOLOR].title = "Light Color";
+ s->opt[OPT_LIGHTCOLOR].desc = "Light Color";
+ s->opt[OPT_LIGHTCOLOR].type = SANE_TYPE_STRING;
+ s->opt[OPT_LIGHTCOLOR].size = max_string_size (light_color_list);
+ s->opt[OPT_LIGHTCOLOR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_LIGHTCOLOR].constraint.string_list = light_color_list;
+ s->val[OPT_LIGHTCOLOR].s = strdup (light_color_list[3]);
+ s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
+#endif
+ 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 (SHARP_Scanner * s)
+{
+ static u_char cmd[] = {READ, 0, 0, 0, 0, 2, 0, 0, 0, 0};
+
+ 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))
+ {
+ /* be brutal...
+ !! The waiting time of 10 seconds might be far too short
+ !! if the resolution limit of the JX 250 is increased to
+ !! to more than 400 dpi: for these (interpolated) resolutions,
+ !! the JX 250 is awfully slow.
+ */
+ kill(s->reader_pid, SIGKILL);
+ }
+ wait(&exit_status);
+ DBG(11, "reader process stopped\n");
+
+ s->reader_pid = 0;
+ }
+
+#endif
+ if (s->scanning == SANE_TRUE)
+ {
+ wait_ready(s->fd);
+ sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
+ /* if (s->adf_scan) */
+ if ( s->dev->sensedat.model != JX610
+ && s->dev->sensedat.model != JX320)
+ object_position(s->fd, UNLOAD_PAPER);
+ }
+
+ 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 SHARP_New_Device *new_devs = 0;
+static SHARP_New_Device *new_dev_pool = 0;
+
+static SANE_Status
+attach_and_list(const char *devnam)
+{
+ SANE_Status res;
+ SHARP_Device *devp;
+ SHARP_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(SHARP_New_Device));
+ if (np == 0)
+ return SANE_STATUS_NO_MEM;
+ }
+ np->next =new_devs;
+ np->dev = devp;
+ new_devs = np;
+ }
+ return res;
+}
+
+static int buffers[2] = {DEFAULT_BUFFERS, DEFAULT_BUFFERS};
+static int bufsize[2] = {DEFAULT_BUFSIZE, DEFAULT_BUFSIZE};
+static int queued_reads[2] = {DEFAULT_QUEUED_READS, DEFAULT_QUEUED_READS};
+static int stop_on_fsu_error[2] = {COMPLAIN_ON_FSU_ERROR | COMPLAIN_ON_ADF_ERROR,
+ COMPLAIN_ON_FSU_ERROR | COMPLAIN_ON_ADF_ERROR};
+static int default_scan_mode[2] = {-1, -1};
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ 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 linecount = 0;
+#if 1
+ SHARP_Device sd;
+ SHARP_Device *dp = &sd;
+#else
+ SHARP_Device *dp;
+#endif
+ SHARP_New_Device *np;
+ int i;
+
+ DBG_INIT ();
+ DBG (10, "<< sane_init ");
+
+#if defined PACKAGE && defined VERSION
+ DBG (2, "sane_init: " PACKAGE " " VERSION "\n");
+#endif
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (SHARP_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;
+ dp->info.complain_on_errors = COMPLAIN_ON_ADF_ERROR | COMPLAIN_ON_FSU_ERROR;
+ dp->info.default_scan_mode = -1;
+ 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 if (strcmp(word, "stop_on_fsu_error") == 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
+ stop_on_fsu_error[opt_index]
+ = i ? COMPLAIN_ON_FSU_ERROR : 0;
+ }
+ else if (strcmp(word, "default_scan_source") == 0)
+ {
+ free(word);
+ word = 0;
+ sanei_config_get_string(lp, &word);
+ if (strcmp(word, "auto") == 0)
+ default_scan_mode[opt_index] = -1;
+ else if (strcmp(word, "fsu") == 0)
+ default_scan_mode[opt_index] = SCAN_WITH_FSU;
+ else if (strcmp(word, "adf") == 0)
+ default_scan_mode[opt_index] = SCAN_WITH_ADF;
+ else if (strcmp(word, "flatbed") == 0)
+ default_scan_mode[opt_index] = SCAN_SIMPLE;
+ else
+ {
+ DBG(1, "error in config file, line %i: number expected:\n",
+ linecount);
+ DBG(1, "%s\n", line);
+ }
+ }
+ 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;
+ new_devs->dev->info.complain_on_errors = stop_on_fsu_error[1];
+ new_devs->dev->info.default_scan_mode = default_scan_mode[1];
+ 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];
+ stop_on_fsu_error[1] = stop_on_fsu_error[0];
+ default_scan_mode[1] = default_scan_mode[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;
+ new_devs->dev->info.complain_on_errors = stop_on_fsu_error[1];
+ new_devs->dev->info.default_scan_mode = default_scan_mode[1];
+ 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, "sane_init >>\n");
+ return (SANE_STATUS_GOOD);
+}
+
+static const SANE_Device **devlist = 0;
+void
+sane_exit (void)
+{
+ SHARP_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);
+ }
+
+ if (devlist)
+ free(devlist);
+ devlist = 0;
+ first_dev = 0;
+
+ DBG (10, ">>\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ SHARP_Device *dev;
+ int i;
+ DBG (10, "<< sane_get_devices ");
+
+ 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;
+ SHARP_Device *dev;
+ SHARP_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);
+ dev->info.buffers = buffers[0];
+ dev->info.wanted_bufsize = bufsize[0];
+ dev->info.queued_reads = queued_reads[0];
+ }
+ }
+ 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)
+{
+ SHARP_Scanner *s = (SHARP_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)
+{
+ SHARP_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)
+{
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+#ifdef USE_CUSTOM_GAMMA
+ SANE_Word w, cap;
+#else
+ SANE_Word cap;
+#endif
+#ifdef USE_RESOLUTION_LIST
+ int i;
+#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_X_RESOLUTION:
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ case OPT_Y_RESOLUTION:
+#endif
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_THRESHOLD:
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_SPEED:
+ 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_X_RESOLUTION:
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ case OPT_Y_RESOLUTION:
+#endif
+ 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.
+ */
+#ifdef USE_COLOR_THRESHOLD
+ case OPT_THRESHOLD_R:
+ case OPT_THRESHOLD_G:
+ case OPT_THRESHOLD_B:
+#endif
+ case OPT_SPEED:
+ 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_THRESHOLD].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 == JX330)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (val, M_LINEART_COLOR) == 0)
+ {
+ s->opt[OPT_THRESHOLD].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 == JX330)
+ s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_THRESHOLD].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 ( 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;
+ }
+
+ 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;
+ 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_X_RESOLUTION].w
+ = atoi(s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]);
+ s->val[OPT_Y_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, xres, yres;
+ const char *mode;
+ SHARP_Scanner *s = handle;
+ DBG (10, "<< sane_get_parameters ");
+
+ xres = s->val[OPT_X_RESOLUTION].w;
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ yres = s->val[OPT_Y_RESOLUTION].w;
+#else
+ yres = xres;
+#endif
+ 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 * xres / s->dev->info.mud;
+ s->params.lines = length * yres / s->dev->info.mud;
+ s->unscanned_lines = s->params.lines;
+ }
+ else
+ {
+ static u_char cmd[] = {READ, 0, 0x81, 0, 0, 0, 0, 0, 4, 0};
+ static u_char buf[4];
+ size_t len = 4;
+ SANE_Status status;
+
+ /* if async reads are used, )ie. if USE_FORK is defined,
+ this command may only be issued immediately after the
+ "start scan" command. Later calls will confuse the
+ read queue.
+ */
+ if (!s->get_params_called)
+ {
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), buf, &len);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ do_cancel(s);
+ return (status);
+ }
+ s->params.pixels_per_line = (buf[1] << 8) + buf[0];
+ s->params.lines = (buf[3] << 8) + buf[2];
+ s->get_params_called = 1;
+ }
+ }
+
+ xres = s->val[OPT_X_RESOLUTION].w;
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ yres = s->val[OPT_Y_RESOLUTION].w;
+#else
+ yres = xres;
+#endif
+
+ 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
+ {
+ 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] > 255 ? 255 : val.wa[0]);
+ /* val.wa[i] is over 255, so val.wa[i] is limitied to 255 */
+ 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 (SHARP_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(11, "%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(SHARP_Scanner *s, SANE_Word *a, int dtq)
+{
+ SANE_Status status;
+ int i;
+
+ DBG(11, "<< send_binary_g_table\n");
+
+ memset(s->buffer, 0, 522);
+
+ s->buffer[0] = SEND;
+ s->buffer[2] = 0x03;
+ s->buffer[5] = dtq;
+ s->buffer[7] = 2;
+ s->buffer[8] = 0;
+
+ for (i = 0; i < 256; i++)
+ {
+ s->buffer[2*i+11] = a[i] > 255 ? 255 : a[i];
+ }
+
+ for (i = 0; i < 256; i += 16)
+ {
+ DBG(11, "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ a[i ], a[i+1], a[i+2], a[i+3],
+ a[i+4], a[i+5], a[i+6], a[i+7],
+ a[i+8], a[i+9], a[i+10], a[i+11],
+ a[i+12], a[i+13], a[i+14], a[i+15]);
+ }
+
+ wait_ready(s->fd);
+ status = sanei_scsi_cmd (s->fd, s->buffer, 2*i+10, 0, 0);
+
+ DBG(11, ">>\n");
+
+ return status;
+}
+
+#ifdef USE_CUSTOM_GAMMA
+static SANE_Status
+send_binary_gamma_tables (SHARP_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;
+
+ 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);
+
+ return status;
+}
+
+static SANE_Status
+send_gamma_tables (SHARP_Scanner *s)
+{
+ if (s->dev->sensedat.model != JX250 && s->dev->sensedat.model != JX350)
+ {
+ return send_ascii_gamma_tables(s);
+ }
+ else
+ {
+ return send_binary_gamma_tables(s);
+ }
+
+}
+#endif
+
+#ifdef USE_COLOR_THRESHOLD
+static SANE_Status
+send_threshold_data(SHARP_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
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ char *mode, *halftone, *paper, *gamma, *edge, *lightcolor, *adf_fsu;
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+ size_t buf_size;
+ SHARP_Send ss;
+ window_param wp;
+ mode_sense_subdevice m_subdev;
+
+ 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_errors
+ = COMPLAIN_ON_ADF_ERROR | s->dev->info.complain_on_errors;
+
+#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.
+
+ NOTE: If you need to decrease this value, remember that s->buffer
+ is used in send_ascii_gamma_tables (JX330/JX610) and in
+ send_binary_g_table (JX250/JX350). send_ascii_gamma_tables needs 4106
+ bytes, and send_binary_g_table needs 522 bytes.
+ */
+ 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(SHARP_rdr_ctl)
+ + s->dev->info.buffers *
+ (sizeof(SHARP_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 = (SHARP_rdr_ctl*) shmat(s->shmid, 0, 0);
+ if (s->rdr_ctl == (void *) -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 = (SHARP_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;
+ s->speed = s->val[OPT_SPEED].w;
+
+ s->xres = s->val[OPT_X_RESOLUTION].w;
+ if (s->val[OPT_PREVIEW].w == SANE_FALSE)
+ {
+#ifdef USE_SEPARATE_Y_RESOLUTION
+ s->yres = s->val[OPT_Y_RESOLUTION].w;
+#else
+ s->yres = s->val[OPT_X_RESOLUTION].w;
+#endif
+ s->speed = s->val[OPT_SPEED].w;
+ }
+ else
+ {
+ s->yres = s->val[OPT_X_RESOLUTION].w;
+ s->speed = SANE_TRUE;
+ }
+
+ 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;
+ s->bpp = s->params.depth;
+
+ s->adf_fsu_mode = SCAN_SIMPLE; /* default: scan without ADF and FSU */
+#ifdef ALLOW_AUTO_SELECT_ADF
+ if (strcmp (adf_fsu, use_auto) == 0)
+ s->adf_fsu_mode = SCAN_ADF_FSU_AUTO;
+ else
+#endif
+ 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;
+
+ if (strcmp (mode, M_LINEART) == 0)
+ {
+ s->reverse = 0;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->halftone = 1;
+ s->image_composition = 0;
+ }
+ else if (strcmp(halftone, M_BAYER) == 0)
+ {
+ s->halftone = 2;
+ s->image_composition = 1;
+ }
+ else if (strcmp(halftone, M_SPIRAL) == 0)
+ {
+ s->halftone = 3;
+ s->image_composition = 1;
+ }
+ else if (strcmp(halftone, M_DISPERSED) == 0)
+ {
+ s->halftone = 4;
+ s->image_composition = 1;
+ }
+ else if (strcmp(halftone, M_ERRDIFFUSION) == 0)
+ {
+ s->halftone = 5;
+ s->image_composition = 1;
+ }
+ }
+ 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;
+ if (strcmp(halftone, M_BILEVEL) == 0)
+ {
+ s->halftone = 1;
+ s->image_composition = 3;
+ }
+ else if (strcmp(halftone, M_BAYER) == 0)
+ {
+ s->halftone = 2;
+ s->image_composition = 4;
+ }
+ else if (strcmp(halftone, M_SPIRAL) == 0)
+ {
+ s->halftone = 3;
+ s->image_composition = 4;
+ }
+ else if (strcmp(halftone, M_DISPERSED) == 0)
+ {
+ s->halftone = 4;
+ s->image_composition = 4;
+ }
+ else if (strcmp(halftone, M_ERRDIFFUSION) == 0)
+ {
+ s->halftone = 5;
+ s->image_composition = 4;
+ }
+ }
+ else if (strcmp (mode, M_COLOR) == 0)
+ {
+ s->image_composition = 5;
+ s->reverse = 1;
+ }
+
+ if (strcmp (edge, EDGE_NONE) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS NONE\n");
+ s->edge = 0;
+ }
+ else if (strcmp (edge, EDGE_MIDDLE) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS MIDDLE\n");
+ s->edge = 1;
+ }
+ else if (strcmp (edge, EDGE_STRONG) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS STRONG\n");
+ s->edge = 2;
+ }
+ else if (strcmp (edge, EDGE_BLUR) == 0)
+ {
+ DBG (11, "EDGE EMPHASIS BLUR\n");
+ 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_WHITE) == 0)
+ s->lightcolor = 3;
+
+ s->adf_scan = 0;
+ if ( s->dev->sensedat.model != JX610
+ && s->dev->sensedat.model != JX320)
+ {
+ status = mode_select_adf_fsu(s->fd, s->adf_fsu_mode);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (10, "sane_start: mode_select_adf_fsu failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ /* if the ADF is selected, check if it is ready */
+ memset (&m_subdev, 0, sizeof (m_subdev));
+ buf_size = sizeof (m_subdev);
+ status = mode_sense (s->fd, &m_subdev, &buf_size, 0x20);
+ DBG(11, "mode sense result a_mode: %x f_mode: %x\n",
+ m_subdev.a_mode_type, m_subdev.f_mode_type);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (10, "sane_start: MODE_SENSE/subdevice page failed\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+ if (s->adf_fsu_mode == SCAN_WITH_ADF)
+ s->adf_scan = 1;
+#ifdef ALLOW_AUTO_SELECT_ADF
+ else if (s->adf_fsu_mode == SCAN_ADF_FSU_AUTO)
+ {
+ if (m_subdev.a_mode_type & 0x80)
+ s->adf_scan = 1;
+ }
+#endif
+ }
+
+
+#ifdef USE_CUSTOM_GAMMA
+ if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
+ {
+#endif
+ if (s->dev->sensedat.model != JX250 && s->dev->sensedat.model != JX350)
+ {
+ 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
+ {
+ /* the JX250 does not support the "fixed gamma selection",
+ therefore, lets calculate & send gamma values
+ */
+ int i;
+ SANE_Word gtbl[256];
+ if (strcmp (gamma, GAMMA10) == 0)
+ for (i = 0; i < 256; i++)
+ gtbl[i] = i;
+ else
+ {
+ 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);
+ }
+#ifdef USE_CUSTOM_GAMMA
+ }
+ else
+ status = send_gamma_tables(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#endif
+
+ if (s->dev->sensedat.model != JX250 && s->dev->sensedat.model != JX350)
+ {
+ ss.dtc = 0x86;
+ ss.dtq = 0x05;
+ 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);
+ }
+
+#ifdef USE_COLOR_THRESHOLD
+ status = send_threshold_data(s);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "send threshold data failed: %s\n", sane_strstatus (status));
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ return (status);
+ }
+#endif
+ }
+
+ memset (&wp, 0, sizeof (wp));
+ /* every Sharp scanner seems to have a different
+ window descriptor block...
+ */
+ if ( s->dev->sensedat.model == JX610
+ || s->dev->sensedat.model == JX320)
+ {
+ buf_size = sizeof(WDB);
+ }
+ else if (s->dev->sensedat.model == JX330)
+ {
+ buf_size = sizeof (WDB) + sizeof(WDBX330);
+ }
+ else
+ {
+ buf_size = sizeof (WDB) + sizeof(WDBX330) + sizeof(WDBX250);
+ }
+
+ wp.wpdh.wdl[0] = buf_size >> 8;
+ wp.wpdh.wdl[1] = buf_size;
+ wp.wdb.x_res[0] = s->xres >> 8;
+ wp.wdb.x_res[1] = s->xres;
+ wp.wdb.y_res[0] = s->yres >> 8;
+ wp.wdb.y_res[1] = s->yres;
+ 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.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;
+ if ( s->dev->sensedat.model == JX610
+ || s->dev->sensedat.model == JX320)
+ {
+ wp.wdb.ht_pattern[1] = 0;
+ }else{
+ wp.wdb.ht_pattern[1] = s->halftone;
+ }
+ wp.wdb.rif_padding = (s->reverse * 128) + 0;
+ wp.wdb.eletu = (!s->speed << 2) + (s->edge << 6) + (s->lightcolor << 4);
+
+ if (s->dev->sensedat.model == JX250 || s->dev->sensedat.model == JX350)
+ {
+ wp.wdbx250.threshold_red = s->val[OPT_THRESHOLD_R].w;
+ wp.wdbx250.threshold_green = s->val[OPT_THRESHOLD_G].w;
+ wp.wdbx250.threshold_blue = s->val[OPT_THRESHOLD_B].w;
+ }
+
+
+ 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);
+ DBG (5, "eletu=%d\n", wp.wdb.eletu);
+
+#if 0
+ {
+ unsigned char *p = (unsigned char*) &wp.wdb;
+ int i;
+ DBG(11, "set window:\n");
+ for (i = 0; i < sizeof(wp.wdb) + + sizeof(wp.wdbx330) + sizeof(wp.wdbx250); 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
+
+ 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);
+ }
+ 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]);
+
+ if (s->adf_scan)
+ {
+ status = object_position(s->fd, LOAD_PAPER);
+ if (status != SANE_STATUS_GOOD)
+ {
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ s->busy = SANE_FALSE;
+ s->cancel = SANE_FALSE;
+ return (status);
+ }
+ }
+
+ DBG (5, "start: SCAN\n");
+ s->scanning = SANE_TRUE;
+ s->busy = SANE_TRUE;
+ s->cancel = SANE_FALSE;
+ s->get_params_called = 0;
+
+ wait_ready(s->fd);
+ 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));
+ do_cancel(s);
+ 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.pixels_per_line+7) / 8;
+ s->bytes_to_read *= 3 * 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_X_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)
+{
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+ size_t nread;
+ DBG (10, "<< sane_read_direct ");
+
+ 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
+ wait_ready(s->fd);
+ 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)
+{
+ SHARP_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 ");
+
+ *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
+ wait_ready(s->fd);
+ 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)
+{
+ SHARP_Scanner *s = handle;
+ SANE_Status status;
+
+ s->busy = SANE_TRUE;
+ if (s->cancel == SANE_TRUE)
+ {
+ do_cancel(s);
+ *len = 0;
+ return (SANE_STATUS_CANCELLED);
+ }
+
+ /* RGB scans with a JX 250 and bi-level color scans
+ must be handled differently: */
+ 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 != JX250 && s->dev->sensedat.model != JX350 )
+ 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);
+ }
+
+ return (status);
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ SHARP_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 __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ DBG (10, "<< sane_set_io_mode");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle,
+ SANE_Int __sane_unused__ * fd)
+{
+ DBG (10, "<< sane_get_select_fd");
+ DBG (10, ">>\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}