kopia lustrzana https://gitlab.com/sane-project/backends
4214 wiersze
126 KiB
C
4214 wiersze
126 KiB
C
/* 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) {
|
|
free(s);
|
|
sanei_scsi_close(s->fd);
|
|
s->fd = -1;
|
|
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;
|
|
}
|