kopia lustrzana https://gitlab.com/sane-project/backends
6408 wiersze
173 KiB
C
6408 wiersze
173 KiB
C
/*
|
|
epson.c - SANE library for Epson flatbed scanners.
|
|
|
|
Based on Kazuhiro Sasayama previous
|
|
Work on epson.[ch] file from the SANE package.
|
|
|
|
Original code taken from sane-0.71
|
|
Copyright (C) 1997 Hypercore Software Design, Ltd.
|
|
|
|
modifications
|
|
Copyright (C) 1998-1999 Christian Bucher <bucher@vernetzt.at>
|
|
Copyright (C) 1998-1999 Kling & Hautzinger GmbH
|
|
Copyright (C) 1999 Norihiko Sawa <sawa@yb3.so-net.ne.jp>
|
|
Copyright (C) 2000 Mike Porter <mike@udel.edu> (mjp)
|
|
Copyright (C) 2003 EPSON KOWA Corporation
|
|
Copyright (C) 1999-2005 Karl Heinz Kremer <khk@khk.net>
|
|
Copyright (C) 2006 Claus Boje <claus@egehuset.dk>
|
|
*/
|
|
|
|
#define SANE_EPSON_VERSION "SANE Epson Backend v0.2.47 - 2006-08-21"
|
|
#define SANE_EPSON_BUILD 247
|
|
|
|
/*
|
|
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. */
|
|
|
|
/*
|
|
2006-08-21 Fix buffer overflow error (submitted by Johannes Meixner)
|
|
2006-06-11 Applied patch from Henning. Fixed a number of compiler warnings
|
|
2006-03-12 Added support for perfetion 4990 photo 4800 dpi
|
|
2005-01-09 "flaming hack to get USB scanners working without timeouts under linux"
|
|
submitted by "Steve" (in comment to bug #300830)
|
|
2004-12-18 Added USB IDs for CX-4600 and CX-3650
|
|
2004-10-16 Added USB ID for Expression 10000XL
|
|
2004-05-08 Disable feed() for Perfection1640
|
|
2004-02-08 Reformat all source code with "indent -bli0"
|
|
2004-02-01 Added D7 function level as copy of D1 for CX-6400
|
|
Added IDs for CX-6400 and Perfection 4870
|
|
2003-10-27 Replaced DBG(0, ... with DBG(1, ...
|
|
2003-09-12 Increment only once in loop to find USB scanners
|
|
Fix rounding problem when determining number of lines to scan
|
|
2003-08-21 Removed '//' comments - again ...
|
|
Added EPSON Kowa copyright message
|
|
2003-08-15 Added support for GT-30000, with support for the ADF in simplex mode
|
|
Borrowed some code from the EPSON Kowa IScan version of the backend
|
|
Use sanei_scsi_cmd2() to send commands. This makes this backend
|
|
useable for SBP-2 under FreeBSD
|
|
2003-05-11 Initialize OPT_LIMIT_RESOLUTION before first call to filter_resolution_list()
|
|
Fix memory problem in get_identity_information(). Both problems were
|
|
reported to the Debian bug database.
|
|
2003-03-26 Fixed two warnings reported by der Mouse
|
|
2003-02-16 Code cleanup, use more descriptive variable names.
|
|
2003-02-15 Move sanei_usb_init() to sane_init(). Thanks to Ron Cemer
|
|
for providing the patch.
|
|
2003-02-15 Fix problem with "usb <vendor> <product> syntax in config file
|
|
2002-12-28 Added advanced option to display only short resolution list for
|
|
displays that can not show the complete list.
|
|
2002-11-23 Fixed problem with dropout color.
|
|
2002-11-03 Full libusb support.
|
|
2002-10-05 Fixed problem with incorrect response to sane_get_parameters()
|
|
in certain situations.
|
|
2002-09-01 USB scanners are now using libsane-usb funtions
|
|
2002-08-17 Fixed typo in variable name.
|
|
Fixed IEEE-1394 problem with Perfection-2450.
|
|
Fixed problem with older B3 level SCSI scanners that do
|
|
not support the extended status request.
|
|
2002-04-22 Declare close_scanner() and open_scanner() before they
|
|
are used.
|
|
2002-04-13 Check if scanner needs to be opened for the reset call.
|
|
(Thanks to Thomas Wenrich for pointing this out)
|
|
Added product IDs for Perfection 1650 and 2450
|
|
2002-01-18 Recognize GT-xxxx type scanners also when using the SCSI
|
|
or IEEE-1394 interface
|
|
2002-01-06 Disable TEST_IOCTL again, which was enabled by accident. Also
|
|
protect the ioctl portion with an #ifdef __linux__
|
|
2002-01-05 Version 0.2.17
|
|
Check for and set s->fd to -1 when device is closed.
|
|
Removed black gamma table - only use RGB even for grayscale
|
|
2002-01-01 Do not call access() for OS/2 systems
|
|
2001-11-13 Version 0.2.16
|
|
Do not call access() for parallel port scanners.
|
|
2001-11-11 Version 0.2.15
|
|
Fixed "wait-for-button" functionality, accidentially merged back wrong
|
|
version after code freeze.
|
|
Corrected "need-strange-reorder" recognition.
|
|
Added IOCTL support to header file.
|
|
2001-11-10 Version 0.2.14
|
|
Added "wait-for-button" functionality
|
|
2001-10-30 I18N patches (Stefan Roellin)
|
|
2001-10-28 Fixed bug with 1650 recognition
|
|
2001-06-09 Version 0.2.09
|
|
Changed debug level for sense handler from 0 to 2
|
|
2001-05-25 Version 0.2.07
|
|
Allow more than 8 bit color depth even for preview mode
|
|
since Xsane can handle this. Some code cleanup.
|
|
2001-05-24 Removed ancient code that was used to determine the resolution
|
|
back when the backend still had a slider for the resolution
|
|
selection.
|
|
2001-05-22 Version 0.2.06
|
|
Added sense_handler to support the GT-8000 scanner. Thanks to Matthias Trute
|
|
for figuring out the details.
|
|
Also added experimental code to use USB scanner probing. Need kernel patch
|
|
for this.
|
|
2001-05-19 Version 0.2.05
|
|
fixed the year in the recent change log entries - I now that it's
|
|
2001...
|
|
Finally fixed the TPU problem with B4 level scanners
|
|
2001-05-13 Version 0.2.04
|
|
Removed check for '\n' before end of line
|
|
Free memory malloced in sane_get_devices() in sane_exit() again
|
|
2001-04-22 Version 0.2.03
|
|
Check first if the scanner does support the set film type
|
|
and set focus position before the GUI elements are displayed.
|
|
This caused problems with older (B4 level) scanners when a TPU
|
|
was connected.
|
|
2001-03-31 Version 0.2.02
|
|
2001-03-17 Next attempt to get the reported number of lines correct
|
|
for the "color shuffling" part.
|
|
Added more comments.
|
|
2000-12-25 Version 0.2.01
|
|
Fixed problem with bilevel scanning with Perfection610: The
|
|
line count has to be an even number with this scanner.
|
|
Several initialization fixes regarding bit depth selection.
|
|
This version goes back into the CVS repository, the 1.0.4
|
|
release is out and therefore the code freeze is over.
|
|
Some general cleanup, added more comments.
|
|
2000-12-09 Version 0.2.00
|
|
Cleaned up printing of gamma table data. 16 elements
|
|
are now printed in one line without the [epson] in
|
|
between the values. Values are only printed for
|
|
Debug levels >= 10.
|
|
2000-12-04 We've introduced the concept of inverting images
|
|
when scanning from a TPU. This is fine, but
|
|
the user supplied gamma tables no longer work.
|
|
This is because the data a frontend is going
|
|
to compute a gamma table for is not what the
|
|
scanner actually sent. So, we have to back into
|
|
the proper gamma table. I think this works. See
|
|
set_gamma_table. (mjp)
|
|
2000-12-03 added the 12/14/16 bit support again.
|
|
2000-12-03 Version 0.1.38
|
|
removed changes regarding 12/14 bit support because
|
|
of SANE feature freeze for 1.0.4. The D1 fix for
|
|
reading the values from the scanner instead of using
|
|
hardcoded values and the fix for the off-by-one error
|
|
in the reorder routine are still in the code base.
|
|
Also force reload after change of scan mode.
|
|
The full backend can be downloaded from my web site at
|
|
http://www.freecolormanagement.com/sane
|
|
2000-12-03 Fixed off-by-one error in color reordering function.
|
|
2000-12-02 Read information about optical resolution and line
|
|
distance from scanner instead of hardcoded values.
|
|
Add support for color depth > 8 bits per channel.
|
|
2000-11-23 Display "Set Focus" control only for scanners that
|
|
can actually handle the command.
|
|
2000-11-19 Added support for the "set focus position" command,
|
|
this is necessary for the Expression1600.
|
|
2000-07-28 Changed #include <...> to #include "..." for the
|
|
sane/... include files.
|
|
2000-07-26 Fixed problem with Perfection610: The variable
|
|
s->color_shuffle_line was never correctly initialized
|
|
2000-06-28 When closing the scanner device the data that's
|
|
still in the scanner, waiting to be transferred
|
|
is flushed. This fixes the problem with scanimage -T
|
|
2000-06-13 Invert image when scanning negative with TPU,
|
|
Show film type only when TPU is selected
|
|
2000-06-13 Initialize optical_res to 0 (Dave Hill)
|
|
2000-06-07 Fix in sane_close() - found by Henning Meier-Geinitz
|
|
2000-06-01 Threshhold should only be active when scan depth
|
|
is 1 and halftoning is off. (mjp)
|
|
2000-05-28 Turned on scanner based color correction.
|
|
Dependancies between many options are now
|
|
being enforced. For instance, auto area seg
|
|
(AAS) should only be on when scan depth == 1.
|
|
Added some routines to active and deactivate
|
|
options. Routines report if option changed.
|
|
Help prevent extraneous option reloads. Split
|
|
sane_control_option in getvalue and setvalue.
|
|
Further split up setvalue into several different
|
|
routines. (mjp)
|
|
2000-05-21 In sane_close use close_scanner instead of just the
|
|
SCSI close function.
|
|
2000-05-20 ... finally fixed the problem with the 610
|
|
Added resolution_list to Epson_Device structure in
|
|
epson.h - this fixes a bug that caused problems when
|
|
more than one EPSON scanner was connected.
|
|
2000-05-13 Fixed the color problem with the Perfection 610. The few
|
|
lines with "garbage" at the beginning of the scan are not
|
|
yet removed.
|
|
2000-05-06 Added support for multiple EPSON scanners. At this time
|
|
this may not be bug free, but it's a start and it seems
|
|
to work well with just one scanner.
|
|
2000-04-06 Did some cleanup on the gamma correction part. The user
|
|
defined table is now initialized to gamma=1, the gamma
|
|
handling is also no longer depending on platform specific
|
|
tables (handled instead by pointers to the actual tables)
|
|
2000-03-27 Disable request for push button status
|
|
2000-03-22 Removed free() calls to static strings to remove
|
|
compile warnings. These were introduced to apparently
|
|
fix an OS/2 bug. It now turned out that they are not
|
|
necessary. The real fix was in the repository for a
|
|
long time (2000-01-25).
|
|
2000-03-19 Fixed problem with A4 level devices - they use the
|
|
line mode instead of the block mode. The routine to
|
|
handle this was screwed up pretty bad. Now I have
|
|
a solid version that handles all variations of line
|
|
mode (automatically deals with the order the color
|
|
lines are sent).
|
|
2000-03-06 Fixed occasional crash after warm up when the "in warmup
|
|
state" went away in between doing ESC G and getting the
|
|
extended status message.
|
|
2000-03-02 Code cleanup, disabled ZOOM until I have time to
|
|
deal with all the side effects.
|
|
2000-03-01 More D1 fixes. In the future I have to come up with
|
|
a more elegant solution to destinguish between different
|
|
function levels. The level > n does not work anymore with
|
|
D1.
|
|
Added support for "set threshold" and "set zoom".
|
|
2000-02-23 First stab at level D1 support, also added a test
|
|
for valid "set halftone" command to enable OPT_HALFTONE
|
|
2000-02-21 Check for "warming up" in after sane_start. This is
|
|
IMHO a horrible hack, but that's the only way without
|
|
a major redesign that will work. (KHK)
|
|
2000-02-20 Added some cleanup on error conditions in attach()
|
|
Use new sanei_config_read() instead of fgets() for
|
|
compatibility with OS/2 (Yuri Dario)
|
|
2000-02-19 Changed some "int" to "size_t" types
|
|
Removed "Preview Resolution"
|
|
Implemented resolution list as WORD_LIST instead of
|
|
a RANGE (KHK)
|
|
2000-02-11 Default scan source is always "Flatbed", regardless
|
|
of installed options. Corrected some typos. (KHK)
|
|
2000-02-03 Gamma curves now coupled with gamma correction menu.
|
|
Only when "User defined" is selected are the curves
|
|
selected. (Dave Hill)
|
|
Renamed "Contrast" to "Gamma Correction" (KHK)
|
|
2000-02-02 "Brown Paper Bag Release" Put the USB fix finally
|
|
into the CVS repository.
|
|
2000-02-01 Fixed problem with USB scanner not being recognized
|
|
because of hte changes to attach a few days ago. (KHK)
|
|
2000-01-29 fixed core dump with xscanimage by moving the gamma
|
|
curves to the standard interface (no longer advanced)
|
|
Removed pragma pack() from source code to make it
|
|
easier to compile on non-gcc compilers (KHK)
|
|
2000-01-26 fixed problem with resolution selection when using the
|
|
resolution list in xsane (KHK)
|
|
2000-01-25 moved the section where the device name is assigned
|
|
in attach. This avoids the core dump of frontend
|
|
applications when no scanner is found (Dave Hill)
|
|
2000-01-24 reorganization of SCSI related "helper" functions
|
|
started support for user defined color correction -
|
|
this is not yet available via the UI (Christian Bucher)
|
|
2000-01-24 Removed C++ style comments '//' (KHK)
|
|
*/
|
|
|
|
|
|
/* #define TEST_IOCTL */
|
|
|
|
/* DON'T CHANGE THE NEXT LINE ! */
|
|
/* #undef FORCE_COLOR_SHUFFLE */
|
|
|
|
|
|
#ifdef _AIX
|
|
#include <lalloca.h> /* MUST come first for AIX! */
|
|
#endif
|
|
|
|
/* --------------------- SANE INTERNATIONALISATION ------------------ */
|
|
|
|
#ifndef SANE_I18N
|
|
#define SANE_I18N(text) (text)
|
|
#endif
|
|
|
|
#include "sane/config.h"
|
|
|
|
#include <lalloca.h>
|
|
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include "sane/sane.h"
|
|
#include "sane/saneopts.h"
|
|
#include "sane/sanei_scsi.h"
|
|
|
|
#include "sane/sanei_usb.h"
|
|
|
|
#include <sane/sanei_pio.h>
|
|
|
|
#define BACKEND_NAME epson
|
|
#include <sane/sanei_backend.h>
|
|
|
|
#include <sane/sanei_config.h>
|
|
|
|
#include "epson.h"
|
|
#include "epson_scsi.h"
|
|
#include "epson_usb.h"
|
|
|
|
#define EPSON_CONFIG_FILE "epson.conf"
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX (1024)
|
|
#endif
|
|
|
|
#ifndef MM_PER_INCH
|
|
#define MM_PER_INCH 25.4
|
|
#endif
|
|
|
|
#define walloc(x) (x *)malloc(sizeof(x))
|
|
#define walloca(x) (x *)alloca(sizeof(x))
|
|
|
|
#ifndef XtNumber
|
|
#define XtNumber(x) ( sizeof(x)/ sizeof(x[0]) )
|
|
#define XtOffset(p_type,field) ((size_t)&(((p_type)NULL)->field))
|
|
#define XtOffsetOf(s_type,field) XtOffset(s_type*,field)
|
|
#endif
|
|
|
|
#define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */
|
|
#define DEVICE_NAME_LEN (16) /* length of device name in extended status */
|
|
|
|
/* NOTE: you can find these codes with "man ascii". */
|
|
#define STX 0x02
|
|
#define ACK 0x06
|
|
#define NAK 0x15
|
|
#define CAN 0x18
|
|
#define ESC 0x1B
|
|
#define PF 0x19
|
|
|
|
#define S_ACK "\006"
|
|
#define S_CAN "\030"
|
|
|
|
#define STATUS_FER 0x80 /* fatal error */
|
|
#define STATUS_AREA_END 0x20 /* area end */
|
|
#define STATUS_OPTION 0x10 /* option installed */
|
|
|
|
#define EXT_STATUS_FER 0x80 /* fatal error */
|
|
#define EXT_STATUS_FBF 0x40 /* flat bed scanner */
|
|
#define EXT_STATUS_WU 0x02 /* warming up */
|
|
#define EXT_STATUS_PB 0x01 /* scanner has a push button */
|
|
|
|
#define EXT_STATUS_IST 0x80 /* option detected */
|
|
#define EXT_STATUS_EN 0x40 /* option enabled */
|
|
#define EXT_STATUS_ERR 0x20 /* other error */
|
|
#define EXT_STATUS_PE 0x08 /* no paper */
|
|
#define EXT_STATUS_PJ 0x04 /* paper jam */
|
|
#define EXT_STATUS_OPN 0x02 /* cover open */
|
|
|
|
#define EPSON_LEVEL_A1 0
|
|
#define EPSON_LEVEL_A2 1
|
|
#define EPSON_LEVEL_B1 2
|
|
#define EPSON_LEVEL_B2 3
|
|
#define EPSON_LEVEL_B3 4
|
|
#define EPSON_LEVEL_B4 5
|
|
#define EPSON_LEVEL_B5 6
|
|
#define EPSON_LEVEL_B6 7
|
|
#define EPSON_LEVEL_B7 8
|
|
#define EPSON_LEVEL_B8 9
|
|
#define EPSON_LEVEL_F5 10
|
|
#define EPSON_LEVEL_D1 11
|
|
#define EPSON_LEVEL_D7 12
|
|
#define EPSON_LEVEL_D8 13
|
|
|
|
/* there is also a function level "A5", which I'm igoring here until somebody can
|
|
convince me that this is still needed. The A5 level was for the GT-300, which
|
|
was (is) a monochrome only scanner. So if somebody really wants to use this
|
|
scanner with SANE get in touch with me and we can work something out - khk */
|
|
|
|
#define EPSON_LEVEL_DEFAULT EPSON_LEVEL_B3
|
|
|
|
static EpsonCmdRec epson_cmd[] = {
|
|
/*
|
|
* request identity
|
|
* | request identity2
|
|
* | | request status
|
|
* | | | request condition
|
|
* | | | | set color mode
|
|
* | | | | | start scanning
|
|
* | | | | | | set data format
|
|
* | | | | | | | set resolution
|
|
* | | | | | | | | set zoom
|
|
* | | | | | | | | | set scan area
|
|
* | | | | | | | | | | set brightness
|
|
* | | | | | | | | | | | set gamma
|
|
* | | | | | | | | | | | | set halftoning
|
|
* | | | | | | | | | | | | | set color correction
|
|
* | | | | | | | | | | | | | | initialize scanner
|
|
* | | | | | | | | | | | | | | | set speed
|
|
* | | | | | | | | | | | | | | | | set lcount
|
|
* | | | | | | | | | | | | | | | | | mirror image
|
|
* | | | | | | | | | | | | | | | | | | set gamma table
|
|
* | | | | | | | | | | | | | | | | | | | set outline emphasis
|
|
* | | | | | | | | | | | | | | | | | | | | set dither
|
|
* | | | | | | | | | | | | | | | | | | | | | set color correction coefficients
|
|
* | | | | | | | | | | | | | | | | | | | | | | request extension status
|
|
* | | | | | | | | | | | | | | | | | | | | | | | control an extension
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | forward feed / eject
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | feed
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | request push button status
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | control auto area segmentation
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | set film type
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set exposure time
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set bay
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set threshold
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set focus position
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request focus position
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
*/
|
|
{"A1", 'I', 0, 'F','S', 0, 'G', 0, 'R', 0, 'A', 0, {0,0,0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"A2", 'I', 0, 'F','S', 0, 'G','D','R','H','A','L',{-3,3,0}, 'Z','B', 0, '@', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"B1", 'I', 0, 'F','S','C','G','D','R', 0, 'A', 0, {0,0,0}, 0, 'B', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"B2", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B', 0, '@', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"B3", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@', 0, 0, 0, 0, 0, 0, 'm','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"B4", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d', 0, 'z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"B5", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"B6", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{"B7", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-4,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0, '!','s','N', 0, 0, 't', 0, 0},
|
|
{"B8", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-4,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0x19,'!','s','N', 0, 0, 0, 'p','q'},
|
|
{"F5", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z', 0, 'M','@','g','d','K','z','Q', 0, 'm','f','e','\f', 0, 0, 0, 'N','T','P', 0, 0, 0},
|
|
{"D1", 'I','i','F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f', 0, 0, 0, '!', 0, 0, 0, 0, 0, 0, 0},
|
|
{"D7", 'I', 0, 'F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f', 0, 0, 0, '!', 0, 0, 0, 0, 0, 0, 0},
|
|
{"D8", 'I','i','F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f','e', 0, 0, '!', 0, 0, 0, 0, 0, 0, 0},
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* Definition of the mode_param struct, that is used to
|
|
* specify the valid parameters for the different scan modes.
|
|
*
|
|
* The depth variable gets updated when the bit depth is modified.
|
|
*/
|
|
|
|
struct mode_param
|
|
{
|
|
int color;
|
|
int mode_flags;
|
|
int dropout_mask;
|
|
int depth;
|
|
};
|
|
|
|
static struct mode_param mode_params[] = {
|
|
{0, 0x00, 0x30, 1},
|
|
{0, 0x00, 0x30, 8},
|
|
{1, 0x02, 0x00, 8}
|
|
};
|
|
|
|
static const SANE_String_Const mode_list[] = {
|
|
SANE_I18N ("Binary"),
|
|
SANE_I18N ("Gray"),
|
|
SANE_I18N ("Color"),
|
|
NULL
|
|
};
|
|
|
|
static const SANE_String_Const adf_mode_list[] = {
|
|
SANE_I18N ("Simplex"),
|
|
SANE_I18N ("Duplex"),
|
|
NULL
|
|
};
|
|
|
|
|
|
/*
|
|
* Define the different scan sources:
|
|
*/
|
|
|
|
#define FBF_STR SANE_I18N("Flatbed")
|
|
#define TPU_STR SANE_I18N("Transparency Unit")
|
|
#define ADF_STR SANE_I18N("Automatic Document Feeder")
|
|
|
|
/*
|
|
* source list need one dummy entry (save device settings is crashing).
|
|
* NOTE: no const - this list gets created while exploring the capabilities
|
|
* of the scanner.
|
|
*/
|
|
|
|
static SANE_String_Const source_list[] = {
|
|
FBF_STR,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
/* some defines to make handling the TPU easier: */
|
|
#define FILM_TYPE_POSITIVE (0)
|
|
#define FILM_TYPE_NEGATIVE (1)
|
|
|
|
static const SANE_String_Const film_list[] = {
|
|
SANE_I18N ("Positive Film"),
|
|
SANE_I18N ("Negative Film"),
|
|
NULL
|
|
};
|
|
|
|
static const SANE_String_Const focus_list[] = {
|
|
SANE_I18N ("Focus on glass"),
|
|
SANE_I18N ("Focus 2.5mm above glass"),
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* TODO: add some missing const.
|
|
*/
|
|
|
|
#define HALFTONE_NONE 0x01
|
|
#define HALFTONE_TET 0x03
|
|
|
|
static int halftone_params[] = {
|
|
HALFTONE_NONE,
|
|
0x00,
|
|
0x10,
|
|
0x20,
|
|
0x80,
|
|
0x90,
|
|
0xa0,
|
|
0xb0,
|
|
HALFTONE_TET,
|
|
0xc0,
|
|
0xd0
|
|
};
|
|
|
|
static const SANE_String_Const halftone_list[] = {
|
|
SANE_I18N ("None"),
|
|
SANE_I18N ("Halftone A (Hard Tone)"),
|
|
SANE_I18N ("Halftone B (Soft Tone)"),
|
|
SANE_I18N ("Halftone C (Net Screen)"),
|
|
NULL
|
|
};
|
|
|
|
static const SANE_String_Const halftone_list_4[] = {
|
|
SANE_I18N ("None"),
|
|
SANE_I18N ("Halftone A (Hard Tone)"),
|
|
SANE_I18N ("Halftone B (Soft Tone)"),
|
|
SANE_I18N ("Halftone C (Net Screen)"),
|
|
SANE_I18N ("Dither A (4x4 Bayer)"),
|
|
SANE_I18N ("Dither B (4x4 Spiral)"),
|
|
SANE_I18N ("Dither C (4x4 Net Screen)"),
|
|
SANE_I18N ("Dither D (8x4 Net Screen)"),
|
|
NULL
|
|
};
|
|
|
|
static const SANE_String_Const halftone_list_7[] = {
|
|
SANE_I18N ("None"),
|
|
SANE_I18N ("Halftone A (Hard Tone)"),
|
|
SANE_I18N ("Halftone B (Soft Tone)"),
|
|
SANE_I18N ("Halftone C (Net Screen)"),
|
|
SANE_I18N ("Dither A (4x4 Bayer)"),
|
|
SANE_I18N ("Dither B (4x4 Spiral)"),
|
|
SANE_I18N ("Dither C (4x4 Net Screen)"),
|
|
SANE_I18N ("Dither D (8x4 Net Screen)"),
|
|
SANE_I18N ("Text Enhanced Technology"),
|
|
SANE_I18N ("Download pattern A"),
|
|
SANE_I18N ("Download pattern B"),
|
|
NULL
|
|
};
|
|
|
|
static int dropout_params[] = {
|
|
0x00, /* none */
|
|
0x10, /* red */
|
|
0x20, /* green */
|
|
0x30 /* blue */
|
|
};
|
|
|
|
static const SANE_String_Const dropout_list[] = {
|
|
SANE_I18N ("None"),
|
|
SANE_I18N ("Red"),
|
|
SANE_I18N ("Green"),
|
|
SANE_I18N ("Blue"),
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* Color correction:
|
|
* One array for the actual parameters that get sent to the scanner (color_params[]),
|
|
* one array for the strings that get displayed in the user interface (color_list[])
|
|
* and one array to mark the user defined color correction (dolor_userdefined[]).
|
|
*/
|
|
|
|
static int color_params[] = {
|
|
0x00,
|
|
0x01,
|
|
0x10,
|
|
0x20,
|
|
0x40,
|
|
0x80
|
|
};
|
|
|
|
static SANE_Bool color_userdefined[] = {
|
|
SANE_FALSE,
|
|
SANE_TRUE,
|
|
SANE_FALSE,
|
|
SANE_FALSE,
|
|
SANE_FALSE,
|
|
SANE_FALSE
|
|
};
|
|
|
|
static const SANE_String_Const color_list[] = {
|
|
SANE_I18N ("No Correction"),
|
|
SANE_I18N ("User defined"),
|
|
SANE_I18N ("Impact-dot printers"),
|
|
SANE_I18N ("Thermal printers"),
|
|
SANE_I18N ("Ink-jet printers"),
|
|
SANE_I18N ("CRT monitors"),
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* Gamma correction:
|
|
* The A and B level scanners work differently than the D level scanners, therefore
|
|
* I define two different sets of arrays, plus one set of variables that get set to
|
|
* the actally used params and list arrays at runtime.
|
|
*/
|
|
|
|
static int gamma_params_ab[] = {
|
|
0x01,
|
|
0x03,
|
|
0x00,
|
|
0x10,
|
|
0x20
|
|
};
|
|
|
|
static const SANE_String_Const gamma_list_ab[] = {
|
|
SANE_I18N ("Default"),
|
|
SANE_I18N ("User defined"),
|
|
SANE_I18N ("High density printing"),
|
|
SANE_I18N ("Low density printing"),
|
|
SANE_I18N ("High contrast printing"),
|
|
NULL
|
|
};
|
|
|
|
static SANE_Bool gamma_userdefined_ab[] = {
|
|
SANE_FALSE,
|
|
SANE_TRUE,
|
|
SANE_FALSE,
|
|
SANE_FALSE,
|
|
SANE_FALSE,
|
|
};
|
|
|
|
static int gamma_params_d[] = {
|
|
0x03,
|
|
0x04
|
|
};
|
|
|
|
static const SANE_String_Const gamma_list_d[] = {
|
|
SANE_I18N ("User defined (Gamma=1.0)"),
|
|
SANE_I18N ("User defined (Gamma=1.8)"),
|
|
NULL
|
|
};
|
|
|
|
static SANE_Bool gamma_userdefined_d[] = {
|
|
SANE_TRUE,
|
|
SANE_TRUE
|
|
};
|
|
|
|
static SANE_Bool *gamma_userdefined;
|
|
static int *gamma_params;
|
|
|
|
/* flaming hack to get USB scanners
|
|
working without timeouts under linux */
|
|
/* (cribbed from fujitsu.c) */
|
|
static unsigned int r_cmd_count = 0;
|
|
static unsigned int w_cmd_count = 0;
|
|
|
|
|
|
|
|
|
|
/* Bay list:
|
|
* this is used for the FilmScan
|
|
*/
|
|
|
|
static const SANE_String_Const bay_list[] = {
|
|
" 1 ",
|
|
" 2 ",
|
|
" 3 ",
|
|
" 4 ",
|
|
" 5 ",
|
|
" 6 ",
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* minimum, maximum, quantization.
|
|
*/
|
|
|
|
static const SANE_Range u8_range = { 0, 255, 0 };
|
|
static const SANE_Range s8_range = { -127, 127, 0 };
|
|
static const SANE_Range zoom_range = { 50, 200, 0 };
|
|
|
|
/*
|
|
* The "switch_params" are used for several boolean choices
|
|
*/
|
|
static int switch_params[] = {
|
|
0,
|
|
1
|
|
};
|
|
|
|
#define mirror_params switch_params
|
|
#define speed_params switch_params
|
|
#define film_params switch_params
|
|
|
|
static const SANE_Range outline_emphasis_range = { -2, 2, 0 };
|
|
|
|
/* static const SANE_Range gamma_range = { -2, 2, 0 }; */
|
|
|
|
struct qf_param
|
|
{
|
|
SANE_Word tl_x;
|
|
SANE_Word tl_y;
|
|
SANE_Word br_x;
|
|
SANE_Word br_y;
|
|
};
|
|
|
|
/* gcc don't like to overwrite const field */
|
|
static /*const */ struct qf_param qf_params[] = {
|
|
{0, 0, SANE_FIX (120.0), SANE_FIX (120.0)},
|
|
{0, 0, SANE_FIX (148.5), SANE_FIX (210.0)},
|
|
{0, 0, SANE_FIX (210.0), SANE_FIX (148.5)},
|
|
{0, 0, SANE_FIX (215.9), SANE_FIX (279.4)}, /* 8.5" x 11" */
|
|
{0, 0, SANE_FIX (210.0), SANE_FIX (297.0)},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
static const SANE_String_Const qf_list[] = {
|
|
SANE_I18N ("CD"),
|
|
SANE_I18N ("A5 portrait"),
|
|
SANE_I18N ("A5 landscape"),
|
|
SANE_I18N ("Letter"),
|
|
SANE_I18N ("A4"),
|
|
SANE_I18N ("Max"),
|
|
NULL
|
|
};
|
|
|
|
|
|
static SANE_Word *bitDepthList = NULL;
|
|
|
|
|
|
|
|
/*
|
|
* List of pointers to devices - will be dynamically allocated depending
|
|
* on the number of devices found.
|
|
*/
|
|
static const SANE_Device **devlist = 0;
|
|
|
|
|
|
/*
|
|
* Some utility functions
|
|
*/
|
|
|
|
static size_t
|
|
max_string_size (const SANE_String_Const strings[])
|
|
{
|
|
size_t size, max_size = 0;
|
|
int i;
|
|
|
|
for (i = 0; strings[i]; i++)
|
|
{
|
|
size = strlen (strings[i]) + 1;
|
|
if (size > max_size)
|
|
max_size = size;
|
|
}
|
|
return max_size;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
u_char code;
|
|
u_char status;
|
|
u_char count1;
|
|
u_char count2;
|
|
u_char buf[1];
|
|
|
|
} EpsonHdrRec, *EpsonHdr;
|
|
|
|
typedef struct
|
|
{
|
|
u_char code;
|
|
u_char status;
|
|
u_char count1;
|
|
u_char count2;
|
|
|
|
u_char type;
|
|
u_char level;
|
|
|
|
u_char buf[1];
|
|
|
|
} EpsonIdentRec, *EpsonIdent;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
u_char code;
|
|
u_char status;
|
|
u_short count;
|
|
|
|
u_char buf[1];
|
|
|
|
} EpsonParameterRec, *EpsonParameter;
|
|
|
|
typedef struct
|
|
{
|
|
u_char code;
|
|
u_char status;
|
|
|
|
u_char buf[4];
|
|
|
|
} EpsonDataRec, *EpsonData;
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static EpsonHdr command (Epson_Scanner * s, u_char * cmd, size_t cmd_size,
|
|
SANE_Status * status);
|
|
static SANE_Status get_identity_information (SANE_Handle handle);
|
|
static SANE_Status get_identity2_information (SANE_Handle handle);
|
|
static int send (Epson_Scanner * s, void *buf, size_t buf_size,
|
|
SANE_Status * status);
|
|
static ssize_t receive (Epson_Scanner * s, void *buf, ssize_t buf_size,
|
|
SANE_Status * status);
|
|
static SANE_Status color_shuffle (SANE_Handle handle, int *new_length);
|
|
static SANE_Status request_focus_position (SANE_Handle handle,
|
|
u_char * position);
|
|
static SANE_Bool request_push_button_status (SANE_Handle handle,
|
|
SANE_Bool * theButtonStatus);
|
|
static void activateOption (Epson_Scanner * s, SANE_Int option,
|
|
SANE_Bool * change);
|
|
static void deactivateOption (Epson_Scanner * s, SANE_Int option,
|
|
SANE_Bool * change);
|
|
static void setOptionState (Epson_Scanner * s, SANE_Bool state,
|
|
SANE_Int option, SANE_Bool * change);
|
|
static void close_scanner (Epson_Scanner * s);
|
|
static SANE_Status open_scanner (Epson_Scanner * s);
|
|
SANE_Status sane_auto_eject (Epson_Scanner * s);
|
|
static SANE_Status attach_one_usb (SANE_String_Const devname);
|
|
static void filter_resolution_list (Epson_Scanner * s);
|
|
static void get_size (char c1, char c2, double *w, double *h);
|
|
static void scan_finish (Epson_Scanner * s);
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static int
|
|
send (Epson_Scanner * s, void *buf, size_t buf_size, SANE_Status * status)
|
|
{
|
|
DBG (3, "send buf, size = %lu\n", (u_long) buf_size);
|
|
|
|
#if 1
|
|
{
|
|
unsigned int k;
|
|
const u_char *s = buf;
|
|
|
|
for (k = 0; k < buf_size; k++)
|
|
{
|
|
DBG (125, "buf[%d] %02x %c\n", k, s[k], isprint (s[k]) ? s[k] : '.');
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (s->hw->connection == SANE_EPSON_SCSI)
|
|
{
|
|
return sanei_epson_scsi_write (s->fd, buf, buf_size, status);
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_PIO)
|
|
{
|
|
size_t n;
|
|
|
|
if (buf_size == (n = sanei_pio_write (s->fd, buf, buf_size)))
|
|
*status = SANE_STATUS_GOOD;
|
|
else
|
|
*status = SANE_STATUS_INVAL;
|
|
|
|
return n;
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_USB)
|
|
{
|
|
size_t n;
|
|
n = buf_size;
|
|
*status = sanei_usb_write_bulk (s->fd, buf, &n);
|
|
w_cmd_count++;
|
|
DBG (5, "w_cmd_count = %d\n",w_cmd_count);
|
|
DBG (5, "r_cmd_count = %d\n",r_cmd_count);
|
|
|
|
return n;
|
|
}
|
|
|
|
return SANE_STATUS_INVAL;
|
|
/* never reached */
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static ssize_t
|
|
receive (Epson_Scanner * s, void *buf, ssize_t buf_size, SANE_Status * status)
|
|
{
|
|
ssize_t n = 0;
|
|
|
|
if (s->hw->connection == SANE_EPSON_SCSI)
|
|
{
|
|
n = sanei_epson_scsi_read (s->fd, buf, buf_size, status);
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_PIO)
|
|
{
|
|
if (buf_size == (n = sanei_pio_read (s->fd, buf, (size_t) buf_size)))
|
|
*status = SANE_STATUS_GOOD;
|
|
else
|
|
*status = SANE_STATUS_INVAL;
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_USB)
|
|
{
|
|
/* !!! only report an error if we don't read anything */
|
|
n = buf_size; /* buf_size gets overwritten */
|
|
*status = sanei_usb_read_bulk (s->fd, (SANE_Byte *) buf, (size_t *) & n);
|
|
r_cmd_count += (n+63)/64; /* add # of packets, rounding up */
|
|
DBG (5, "w_cmd_count = %d\n",w_cmd_count);
|
|
DBG (5, "r_cmd_count = %d\n",r_cmd_count);
|
|
|
|
if (n > 0)
|
|
*status = SANE_STATUS_GOOD;
|
|
}
|
|
|
|
DBG (7, "receive buf, expected = %lu, got = %ld\n", (u_long) buf_size, (long) n);
|
|
|
|
#if 1
|
|
if (n > 0)
|
|
{
|
|
int k;
|
|
const u_char *s = buf;
|
|
|
|
for (k = 0; k < n; k++)
|
|
{
|
|
DBG (127, "buf[%d] %02x %c\n", k, s[k], isprint (s[k]) ? s[k] : '.');
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
int i;
|
|
ssize_t k;
|
|
ssize_t hex_start = 0;
|
|
const u_char *s = buf;
|
|
char hex_str[NUM_OF_HEX_ELEMENTS * 3 + 1];
|
|
char tmp_str[NUM_OF_HEX_ELEMENTS * 3 + 1];
|
|
char ascii_str[NUM_OF_HEX_ELEMENTS * 2 + 1];
|
|
|
|
hex_str[0] = '\0';
|
|
ascii_str[0] = '\0';
|
|
|
|
for (k = 0; k < buf_size; k++)
|
|
{
|
|
/* write out the data in lines of 16 bytes */
|
|
/* add the next hex value to the hex string */
|
|
sprintf (tmp_str, "%s %02x", hex_str, s[k]);
|
|
strcpy (hex_str, tmp_str);
|
|
|
|
/* add the character to the ascii string */
|
|
sprintf (tmp_str, "%s %c", ascii_str, isprint (s[k]) ? s[k] : '.');
|
|
strcpy (ascii_str, tmp_str);
|
|
|
|
if ((k % (NUM_OF_HEX_ELEMENTS)) == 0)
|
|
{
|
|
if (k != 0) /* don't do this the first time */
|
|
{
|
|
for (i = strlen (hex_str); i < (NUM_OF_HEX_ELEMENTS * 3); i++)
|
|
{
|
|
hex_str[i] = ' ';
|
|
}
|
|
hex_str[NUM_OF_HEX_ELEMENTS + 1] = '\0';
|
|
|
|
DBG (125, "recv buf[%05d]: %s %s\n", hex_start, hex_str,
|
|
ascii_str);
|
|
hex_start = k;
|
|
hex_str[0] = '\0';
|
|
ascii_str[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = strlen (hex_str); i < NUM_OF_HEX_ELEMENTS * 3; i++)
|
|
{
|
|
hex_str[i] = ' ';
|
|
}
|
|
hex_str[NUM_OF_HEX_ELEMENTS + 1] = '\0';
|
|
|
|
DBG (125, "recv buf[%05d]: %s %s\n", hex_start, hex_str, ascii_str);
|
|
}
|
|
#endif
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static SANE_Status
|
|
expect_ack (Epson_Scanner * s)
|
|
{
|
|
u_char result[1];
|
|
size_t len;
|
|
SANE_Status status;
|
|
|
|
len = sizeof (result);
|
|
|
|
receive (s, result, len, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
if (ACK != result[0])
|
|
return SANE_STATUS_INVAL;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static SANE_Status
|
|
set_cmd (Epson_Scanner * s, u_char cmd, int val)
|
|
{
|
|
SANE_Status status;
|
|
u_char params[2];
|
|
|
|
if (!cmd)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
params[0] = ESC;
|
|
params[1] = cmd;
|
|
|
|
send (s, params, 2, &status);
|
|
if (SANE_STATUS_GOOD != (status = expect_ack (s)))
|
|
return status;
|
|
|
|
params[0] = val;
|
|
send (s, params, 1, &status);
|
|
status = expect_ack (s);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* A little helper function to correct the extended status reply
|
|
gotten from scanners with known buggy firmware.
|
|
*/
|
|
static void
|
|
fix_up_extended_status_reply (const char *model, u_char * buf)
|
|
{
|
|
|
|
if (0 == strncmp (model, "ES-9000H", strlen ("ES-9000H"))
|
|
|| 0 == strncmp (model, "GT-30000", strlen ("GT-30000")))
|
|
{
|
|
DBG (1, "Fixing up buggy ADF max scan dimensions.\n");
|
|
buf[2] = 0xB0;
|
|
buf[3] = 0x6D;
|
|
buf[4] = 0x60;
|
|
buf[5] = 0x9F;
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_params (const SANE_Parameters params)
|
|
{
|
|
DBG (5, "params.format = %d\n", params.format);
|
|
DBG (5, "params.last_frame = %d\n", params.last_frame);
|
|
DBG (5, "params.bytes_per_line = %d\n", params.bytes_per_line);
|
|
DBG (5, "params.pixels_per_line = %d\n", params.pixels_per_line);
|
|
DBG (5, "params.lines = %d\n", params.lines);
|
|
DBG (5, "params.depth = %d\n", params.depth);
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
#define set_focus_position(s,v) set_cmd( s,(s)->hw->cmd->set_focus_position,v)
|
|
#define set_color_mode(s,v) set_cmd( s,(s)->hw->cmd->set_color_mode,v)
|
|
#define set_data_format(s,v) set_cmd( s,(s)->hw->cmd->set_data_format, v)
|
|
#define set_halftoning(s,v) set_cmd( s,(s)->hw->cmd->set_halftoning, v)
|
|
#define set_gamma(s,v) set_cmd( s,(s)->hw->cmd->set_gamma, v)
|
|
#define set_color_correction(s,v) set_cmd( s,(s)->hw->cmd->set_color_correction, v)
|
|
#define set_lcount(s,v) set_cmd( s,(s)->hw->cmd->set_lcount, v)
|
|
#define set_bright(s,v) set_cmd( s,(s)->hw->cmd->set_bright, v)
|
|
#define mirror_image(s,v) set_cmd( s,(s)->hw->cmd->mirror_image, v)
|
|
#define set_speed(s,v) set_cmd( s,(s)->hw->cmd->set_speed, v)
|
|
#define set_outline_emphasis(s,v) set_cmd( s,(s)->hw->cmd->set_outline_emphasis, v)
|
|
#define control_auto_area_segmentation(s,v) set_cmd( s,(s)->hw->cmd->control_auto_area_segmentation, v)
|
|
#define set_film_type(s,v) set_cmd( s,(s)->hw->cmd->set_film_type, v)
|
|
#define set_exposure_time(s,v) set_cmd( s,(s)->hw->cmd->set_exposure_time, v)
|
|
#define set_bay(s,v) set_cmd( s,(s)->hw->cmd->set_bay, v)
|
|
#define set_threshold(s,v) set_cmd( s,(s)->hw->cmd->set_threshold, v)
|
|
#define control_extension(s,v) set_cmd( s,(s)->hw->cmd->control_an_extension, v)
|
|
|
|
/*#define (s,v) set_cmd( s,(s)->hw->cmd->, v) */
|
|
|
|
static SANE_Status
|
|
set_zoom (Epson_Scanner * s, int x_zoom, int y_zoom)
|
|
{
|
|
SANE_Status status;
|
|
u_char cmd[2];
|
|
u_char params[2];
|
|
|
|
if (!s->hw->cmd->set_zoom)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
cmd[0] = ESC;
|
|
cmd[1] = s->hw->cmd->set_zoom;
|
|
|
|
send (s, cmd, 2, &status);
|
|
status = expect_ack (s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
params[0] = x_zoom;
|
|
params[1] = y_zoom;
|
|
|
|
send (s, params, 2, &status);
|
|
status = expect_ack (s);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
static SANE_Status
|
|
set_resolution (Epson_Scanner * s, int xres, int yres)
|
|
{
|
|
SANE_Status status;
|
|
u_char params[4];
|
|
|
|
if (!s->hw->cmd->set_resolution)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
params[0] = ESC;
|
|
params[1] = s->hw->cmd->set_resolution;
|
|
|
|
send (s, params, 2, &status);
|
|
status = expect_ack (s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
params[0] = xres;
|
|
params[1] = xres >> 8;
|
|
params[2] = yres;
|
|
params[3] = yres >> 8;
|
|
|
|
send (s, params, 4, &status);
|
|
status = expect_ack (s);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* set_scan_area()
|
|
*
|
|
* Sends the "set scan area" command to the scanner with the currently selected
|
|
* scan area. This scan area is already corrected for "color shuffling" if
|
|
* necessary.
|
|
*/
|
|
static SANE_Status
|
|
set_scan_area (Epson_Scanner * s, int x, int y, int width, int height)
|
|
{
|
|
SANE_Status status;
|
|
u_char params[8];
|
|
|
|
DBG (1, "set_scan_area: %p %d %d %d %d\n", (void *) s, x, y, width, height);
|
|
|
|
if (!s->hw->cmd->set_scan_area)
|
|
{
|
|
DBG (1, "set_scan_area not supported\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* verify the scan area */
|
|
if (x < 0 || y < 0 || width <= 0 || height <= 0)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
params[0] = ESC;
|
|
params[1] = s->hw->cmd->set_scan_area;
|
|
|
|
send (s, params, 2, &status);
|
|
status = expect_ack (s);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
params[0] = x;
|
|
params[1] = x >> 8;
|
|
params[2] = y;
|
|
params[3] = y >> 8;
|
|
params[4] = width;
|
|
params[5] = width >> 8;
|
|
params[6] = height;
|
|
params[7] = height >> 8;
|
|
|
|
send (s, params, 8, &status);
|
|
status = expect_ack (s);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* set_color_correction_coefficients()
|
|
*
|
|
* Sends the "set color correction coefficients" command with the currently selected
|
|
* parameters to the scanner.
|
|
*/
|
|
|
|
static SANE_Status
|
|
set_color_correction_coefficients (Epson_Scanner * s)
|
|
{
|
|
SANE_Status status;
|
|
u_char cmd = s->hw->cmd->set_color_correction_coefficients;
|
|
u_char params[2];
|
|
const int length = 9;
|
|
signed char cct[9];
|
|
|
|
DBG (1, "set_color_correction_coefficients: starting.\n");
|
|
if (!cmd)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
params[0] = ESC;
|
|
params[1] = cmd;
|
|
|
|
send (s, params, 2, &status);
|
|
if (SANE_STATUS_GOOD != (status = expect_ack (s)))
|
|
return status;
|
|
|
|
cct[0] = s->val[OPT_CCT_1].w;
|
|
cct[1] = s->val[OPT_CCT_2].w;
|
|
cct[2] = s->val[OPT_CCT_3].w;
|
|
cct[3] = s->val[OPT_CCT_4].w;
|
|
cct[4] = s->val[OPT_CCT_5].w;
|
|
cct[5] = s->val[OPT_CCT_6].w;
|
|
cct[6] = s->val[OPT_CCT_7].w;
|
|
cct[7] = s->val[OPT_CCT_8].w;
|
|
cct[8] = s->val[OPT_CCT_9].w;
|
|
|
|
DBG (1, "set_color_correction_coefficients: %d,%d,%d %d,%d,%d %d,%d,%d.\n",
|
|
cct[0], cct[1], cct[2], cct[3],
|
|
cct[4], cct[5], cct[6], cct[7], cct[8]);
|
|
|
|
send (s, cct, length, &status);
|
|
status = expect_ack (s);
|
|
DBG (1, "set_color_correction_coefficients: ending=%d.\n", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static SANE_Status
|
|
set_gamma_table (Epson_Scanner * s)
|
|
{
|
|
|
|
SANE_Status status;
|
|
u_char cmd = s->hw->cmd->set_gamma_table;
|
|
u_char params[2];
|
|
const int length = 257;
|
|
u_char gamma[257];
|
|
int n;
|
|
int table;
|
|
/* static const char gamma_cmds[] = { 'M', 'R', 'G', 'B' }; */
|
|
static const char gamma_cmds[] = { 'R', 'G', 'B' };
|
|
|
|
|
|
DBG (1, "set_gamma_table: starting.\n");
|
|
if (!cmd)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
params[0] = ESC;
|
|
params[1] = cmd;
|
|
|
|
/*
|
|
* Print the gamma tables before sending them to the scanner.
|
|
*/
|
|
|
|
if (DBG_LEVEL > 0)
|
|
{
|
|
int c, i, j;
|
|
|
|
DBG (1, "set_gamma_table()\n");
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
for (i = 0; i < 256; i += 16)
|
|
{
|
|
char gammaValues[16 * 3 + 1], newValue[4];
|
|
|
|
gammaValues[0] = '\0';
|
|
|
|
for (j = 0; j < 16; j++)
|
|
{
|
|
sprintf (newValue, " %02x", s->gamma_table[c][i + j]);
|
|
strcat (gammaValues, newValue);
|
|
}
|
|
|
|
DBG (10, "Gamma Table[%d][%d] %s\n", c, i, gammaValues);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* TODO: &status in send makes no sense like that.
|
|
*/
|
|
|
|
/*
|
|
* When handling inverted images, we must also invert the user
|
|
* supplied gamma function. This is *not* just 255-gamma -
|
|
* this gives a negative image.
|
|
*/
|
|
|
|
for (table = 0; table < 3; table++)
|
|
{
|
|
gamma[0] = gamma_cmds[table];
|
|
if (s->invert_image)
|
|
{
|
|
for (n = 0; n < 256; ++n)
|
|
{
|
|
gamma[n + 1] = 255 - s->gamma_table[table][255 - n];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (n = 0; n < 256; ++n)
|
|
{
|
|
gamma[n + 1] = s->gamma_table[table][n];
|
|
}
|
|
}
|
|
|
|
send (s, params, 2, &status);
|
|
if (SANE_STATUS_GOOD != (status = expect_ack (s)))
|
|
return status;
|
|
|
|
send (s, gamma, length, &status);
|
|
if (SANE_STATUS_GOOD != (status = expect_ack (s)))
|
|
return status;
|
|
|
|
}
|
|
|
|
DBG (1, "set_gamma_table: complete = %d.\n", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
get_size (char c1, char c2, double *w, double *h)
|
|
{
|
|
int ind;
|
|
unsigned char flag;
|
|
|
|
double wsizetbl[] = {
|
|
11.60, /* A3V */
|
|
11.00, /* WLT */
|
|
10.12, /* B4V */
|
|
8.50, /* LGV */
|
|
8.27, /* A4V */
|
|
11.69, /* A4H */
|
|
8.50, /* LTV */
|
|
11.00, /* LTH */
|
|
7.17, /* B5V */
|
|
10.12, /* B5H */
|
|
5.83, /* A5V */
|
|
8.27, /* A5H */
|
|
7.25, /* EXV */
|
|
10.50, /* EXH */
|
|
11.69, /* unknown */
|
|
11.69, /* unknown */
|
|
11.69, /* unknown */
|
|
};
|
|
double hsizetbl[] = {
|
|
16.54, /* A3V */
|
|
17.00, /* WLT */
|
|
14.33, /* B4V */
|
|
14.00, /* LGV */
|
|
11.69, /* A4V */
|
|
8.27, /* A4H */
|
|
11.00, /* LTV */
|
|
8.50, /* LTH */
|
|
10.12, /* B5V */
|
|
7.17, /* B5H */
|
|
8.27, /* A5V */
|
|
5.83, /* A5H */
|
|
10.50, /* EXV */
|
|
7.25, /* EXH */
|
|
17.00, /* unknown */
|
|
17.00, /* unknown */
|
|
17.00, /* unknown */
|
|
};
|
|
|
|
flag = c1;
|
|
for (ind = 0; ind < 8; ind++)
|
|
{
|
|
if (flag & 0x80)
|
|
goto DetectSize;
|
|
flag = flag << 1;
|
|
}
|
|
flag = c2;
|
|
for (; ind < 16; ind++)
|
|
{
|
|
if (flag & 0x80)
|
|
goto DetectSize;
|
|
flag = flag << 1;
|
|
}
|
|
|
|
DetectSize:
|
|
|
|
*w = wsizetbl[ind];
|
|
*h = hsizetbl[ind];
|
|
|
|
DBG (10, "detected width: %f\n", *w);
|
|
DBG (10, "detected height: %f\n", *h);
|
|
}
|
|
|
|
|
|
/*
|
|
* check_ext_status()
|
|
*
|
|
* Requests the extended status flag from the scanner. The "warming up" condition
|
|
* is reported as a warning (only visible if debug level is set to 10 or greater) -
|
|
* every other condition is reported as an error.
|
|
*
|
|
* This function only gets called when we are dealing with a scanner that supports the
|
|
* "warming up" code, so it's not a problem for B3 level scanners, that don't handle
|
|
* request extended status commands.
|
|
*/
|
|
|
|
static SANE_Status
|
|
check_ext_status (Epson_Scanner * s, int *max_x, int *max_y)
|
|
{
|
|
SANE_Status status;
|
|
u_char cmd = s->hw->cmd->request_extended_status;
|
|
u_char params[2];
|
|
u_char *buf;
|
|
EpsonHdr head;
|
|
|
|
*max_x = 0;
|
|
*max_y = 0;
|
|
|
|
if (cmd == 0)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
params[0] = ESC;
|
|
params[1] = cmd;
|
|
|
|
head = (EpsonHdr) command (s, params, 2, &status);
|
|
if (NULL == head)
|
|
{
|
|
DBG (1, "Extended status flag request failed\n");
|
|
return status;
|
|
}
|
|
|
|
buf = &head->buf[0];
|
|
|
|
if (buf[0] & EXT_STATUS_WU)
|
|
{
|
|
DBG (10, "option: warming up\n");
|
|
status = SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
if (buf[0] & EXT_STATUS_FER)
|
|
{
|
|
DBG (1, "option: fatal error\n");
|
|
status = SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (s->hw->ADF && s->hw->use_extension && s->hw->cmd->feed)
|
|
{
|
|
fix_up_extended_status_reply (s->hw->sane.model, buf);
|
|
|
|
*max_x = buf[3] << 8 | buf[2];
|
|
*max_y = buf[5] << 8 | buf[4];
|
|
|
|
if (0 == strcmp ("ES-9000H", s->hw->sane.model)
|
|
|| 0 == strcmp ("GT-30000", s->hw->sane.model))
|
|
{
|
|
/* set size of current sheet, but don't clobber zoom
|
|
settings (which should always be smaller than the
|
|
detected sheet size) */
|
|
double w, h;
|
|
get_size (buf[16], buf[17], &w, &h);
|
|
w = SANE_FIX (w * MM_PER_INCH);
|
|
h = SANE_FIX (h * MM_PER_INCH);
|
|
if (w < s->val[OPT_BR_X].w)
|
|
s->val[OPT_BR_X].w = w;
|
|
if (h < s->val[OPT_BR_Y].w)
|
|
s->val[OPT_BR_Y].w = h;
|
|
}
|
|
}
|
|
|
|
|
|
if (buf[1] & EXT_STATUS_ERR)
|
|
{
|
|
DBG (1, "ADF: other error\n");
|
|
status = SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (buf[1] & EXT_STATUS_PE)
|
|
{
|
|
DBG (1, "ADF: no paper\n");
|
|
status = SANE_STATUS_NO_DOCS;
|
|
return status;
|
|
}
|
|
|
|
if (buf[1] & EXT_STATUS_PJ)
|
|
{
|
|
DBG (1, "ADF: paper jam\n");
|
|
status = SANE_STATUS_JAMMED;
|
|
}
|
|
|
|
if (buf[1] & EXT_STATUS_OPN)
|
|
{
|
|
DBG (1, "ADF: cover open\n");
|
|
status = SANE_STATUS_COVER_OPEN;
|
|
}
|
|
|
|
if (buf[6] & EXT_STATUS_ERR)
|
|
{
|
|
DBG (1, "TPU: other error\n");
|
|
status = SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* return the max. scan area for the ADF */
|
|
if (buf[6] & EXT_STATUS_IST)
|
|
{
|
|
*max_x = buf[8] << 8 | buf[7];
|
|
*max_y = buf[10] << 8 | buf[9];
|
|
}
|
|
|
|
/* return the max. scan area for the flatbed */
|
|
if (s->hw->devtype == 3 && s->hw->use_extension == 0)
|
|
{
|
|
double w, h;
|
|
get_size (buf[18], buf[19], &w, &h);
|
|
*max_x = (int) (w * s->hw->dpi_range.max);
|
|
*max_y = (int) (h * s->hw->dpi_range.max);
|
|
}
|
|
|
|
free (head);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* reset()
|
|
*
|
|
* Send the "initialize scanner" command to the device and reset it.
|
|
*
|
|
*/
|
|
|
|
static SANE_Status
|
|
reset (Epson_Scanner * s)
|
|
{
|
|
SANE_Status status;
|
|
u_char param[2];
|
|
SANE_Bool needToClose = SANE_FALSE;
|
|
|
|
DBG (5, "reset()\n");
|
|
|
|
if (!s->hw->cmd->initialize_scanner)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
param[0] = ESC;
|
|
param[1] = s->hw->cmd->initialize_scanner;
|
|
|
|
if (s->fd == -1)
|
|
{
|
|
needToClose = SANE_TRUE;
|
|
DBG (5, "reset calling open_scanner\n");
|
|
if ((status = open_scanner (s)) != SANE_STATUS_GOOD)
|
|
return status;
|
|
}
|
|
|
|
send (s, param, 2, &status);
|
|
status = expect_ack (s);
|
|
|
|
if (needToClose)
|
|
{
|
|
close_scanner (s);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* close_scanner()
|
|
*
|
|
* Close the open scanner. Depending on the connection method, a different
|
|
* close function is called.
|
|
*/
|
|
|
|
static void
|
|
close_scanner (Epson_Scanner * s)
|
|
{
|
|
DBG (5, "close_scanner(fd = %d)\n", s->fd);
|
|
|
|
if (s->fd == -1)
|
|
return;
|
|
|
|
if (r_cmd_count % 2)
|
|
{
|
|
/* send a request_status. This toggles w_cmd_count and r_cmd_count */
|
|
u_char param[3];
|
|
u_char result[5];
|
|
SANE_Status status;
|
|
|
|
param[0] = ESC;
|
|
param[1] = s->hw->cmd->request_status;
|
|
param[2]='\0';
|
|
send(s,param,2,&status);
|
|
receive(s,result,4,&status);
|
|
}
|
|
|
|
|
|
DBG (5, "w_cmd_count = %d\n",w_cmd_count);
|
|
DBG (5, "r_cmd_count = %d\n",r_cmd_count);
|
|
|
|
if (w_cmd_count % 2)
|
|
{
|
|
int junk1,junk2;
|
|
|
|
/* check extended status. This toggles w_cmd_count%2 only */
|
|
check_ext_status (s,&junk1,&junk2);
|
|
}
|
|
|
|
DBG (5, "w_cmd_count = %d\n",w_cmd_count);
|
|
DBG (5, "r_cmd_count = %d\n",r_cmd_count);
|
|
|
|
|
|
if (s->hw->connection == SANE_EPSON_SCSI)
|
|
{
|
|
sanei_scsi_close (s->fd);
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_PIO)
|
|
{
|
|
sanei_pio_close (s->fd);
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_USB)
|
|
{
|
|
sanei_usb_close (s->fd);
|
|
}
|
|
|
|
s->fd = -1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* open_scanner()
|
|
*
|
|
* Open the scanner device. Depending on the connection method,
|
|
* different open functions are called.
|
|
*/
|
|
|
|
static SANE_Status
|
|
open_scanner (Epson_Scanner * s)
|
|
{
|
|
SANE_Status status = 0;
|
|
|
|
DBG (5, "open_scanner()\n");
|
|
|
|
if (s->fd != -1)
|
|
{
|
|
DBG (5, "scanner is already open: fd = %d\n", s->fd);
|
|
return SANE_STATUS_GOOD; /* no need to open the scanner */
|
|
}
|
|
|
|
/* don't do this for OS2: */
|
|
#ifndef HAVE_OS2_H
|
|
#if 0
|
|
/* test the device name */
|
|
if ((s->hw->connection != SANE_EPSON_PIO)
|
|
&& (access (s->hw->sane.name, R_OK | W_OK) != 0))
|
|
{
|
|
DBG (1, "sane_start: access(%s, R_OK | W_OK) failed\n", s->hw->sane.name);
|
|
return SANE_STATUS_ACCESS_DENIED;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
if (s->hw->connection == SANE_EPSON_SCSI)
|
|
{
|
|
status = sanei_scsi_open (s->hw->sane.name, &s->fd,
|
|
sanei_epson_scsi_sense_handler, NULL);
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: %s open failed: %s\n", s->hw->sane.name,
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_PIO)
|
|
{
|
|
status = sanei_pio_open (s->hw->sane.name, &s->fd);
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: %s open failed: %s\n", s->hw->sane.name,
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_USB)
|
|
{
|
|
status = sanei_usb_open (s->hw->sane.name, &s->fd);
|
|
|
|
return status;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
/*
|
|
* feed ( )
|
|
*/
|
|
|
|
static SANE_Status
|
|
feed (Epson_Scanner * s)
|
|
{
|
|
SANE_Status status;
|
|
u_char params[2];
|
|
u_char cmd = s->hw->cmd->feed;
|
|
|
|
DBG (5, "feed()\n");
|
|
|
|
if (!cmd)
|
|
{
|
|
DBG (5, "feed() is not supported\n");
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
params[0] = cmd;
|
|
|
|
send (s, params, 1, &status);
|
|
|
|
if (SANE_STATUS_GOOD != (status = expect_ack (s)))
|
|
{
|
|
close_scanner (s);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* eject()
|
|
*
|
|
* Eject the current page from the ADF. The scanner is opened prior to
|
|
* sending the command and closed afterwards.
|
|
*
|
|
*/
|
|
|
|
static SANE_Status
|
|
eject (Epson_Scanner * s)
|
|
{
|
|
SANE_Status status;
|
|
u_char params[2];
|
|
u_char cmd = s->hw->cmd->eject;
|
|
SANE_Bool needToClose = SANE_FALSE;
|
|
|
|
DBG (5, "eject()\n");
|
|
|
|
if (!cmd)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
if (s->fd == -1)
|
|
{
|
|
needToClose = SANE_TRUE;
|
|
if (SANE_STATUS_GOOD != (status = open_scanner (s)))
|
|
return status;
|
|
}
|
|
|
|
params[0] = cmd;
|
|
|
|
send (s, params, 1, &status);
|
|
|
|
if (SANE_STATUS_GOOD != (status = expect_ack (s)))
|
|
{
|
|
close_scanner (s);
|
|
return status;
|
|
}
|
|
|
|
if (needToClose)
|
|
close_scanner (s);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static int num_devices = 0; /* number of EPSON scanners attached to backend */
|
|
static Epson_Device *first_dev = NULL; /* first EPSON scanner in list */
|
|
static Epson_Scanner *first_handle = NULL;
|
|
|
|
|
|
static EpsonHdr
|
|
command (Epson_Scanner * s, u_char * cmd, size_t cmd_size,
|
|
SANE_Status * status)
|
|
{
|
|
EpsonHdr head;
|
|
u_char *buf;
|
|
int count;
|
|
|
|
if (NULL == (head = walloc (EpsonHdrRec)))
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
*status = SANE_STATUS_NO_MEM;
|
|
return (EpsonHdr) 0;
|
|
}
|
|
|
|
send (s, cmd, cmd_size, status);
|
|
|
|
if (SANE_STATUS_GOOD != *status)
|
|
{
|
|
/* this is necessary for the GT-8000. I don't know why, but
|
|
it seems to fix the problem. It should not have any
|
|
ill effects on other scanners. */
|
|
*status = SANE_STATUS_GOOD;
|
|
send (s, cmd, cmd_size, status);
|
|
if (SANE_STATUS_GOOD != *status)
|
|
return (EpsonHdr) 0;
|
|
}
|
|
|
|
buf = (u_char *) head;
|
|
|
|
if (s->hw->connection == SANE_EPSON_SCSI)
|
|
{
|
|
receive (s, buf, 4, status);
|
|
buf += 4;
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_USB)
|
|
{
|
|
int bytes_read;
|
|
bytes_read = receive (s, buf, 4, status);
|
|
buf += bytes_read;
|
|
}
|
|
else
|
|
{
|
|
receive (s, buf, 1, status);
|
|
buf += 1;
|
|
}
|
|
|
|
if (SANE_STATUS_GOOD != *status)
|
|
return (EpsonHdr) 0;
|
|
|
|
DBG (4, "code %02x\n", (int) head->code);
|
|
|
|
switch (head->code)
|
|
{
|
|
|
|
case NAK:
|
|
/* fall through */
|
|
/* !!! is this really sufficient to report an error ? */
|
|
case ACK:
|
|
break; /* no need to read any more data after ACK or NAK */
|
|
|
|
case STX:
|
|
if (s->hw->connection == SANE_EPSON_SCSI)
|
|
{
|
|
/* nope */
|
|
}
|
|
else if (s->hw->connection == SANE_EPSON_USB)
|
|
{
|
|
/* we've already read the complete data */
|
|
}
|
|
else
|
|
{
|
|
receive (s, buf, 3, status);
|
|
/* buf += 3; */
|
|
}
|
|
|
|
if (SANE_STATUS_GOOD != *status)
|
|
return (EpsonHdr) 0;
|
|
|
|
DBG (4, "status %02x\n", (int) head->status);
|
|
|
|
count = head->count2 * 255 + head->count1;
|
|
DBG (4, "count %d\n", count);
|
|
|
|
if (NULL == (head = realloc (head, sizeof (EpsonHdrRec) + count)))
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
*status = SANE_STATUS_NO_MEM;
|
|
return (EpsonHdr) 0;
|
|
}
|
|
|
|
buf = head->buf;
|
|
receive (s, buf, count, status);
|
|
|
|
if (SANE_STATUS_GOOD != *status)
|
|
return (EpsonHdr) 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
if (0 == head->code)
|
|
DBG (1, "Incompatible printer port (probably bi/directional)\n");
|
|
else if (cmd[cmd_size - 1] == head->code)
|
|
DBG (1, "Incompatible printer port (probably not bi/directional)\n");
|
|
|
|
DBG (2, "Illegal response of scanner for command: %02x\n", head->code);
|
|
break;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
|
|
/*
|
|
* static SANE_Status attach()
|
|
*
|
|
* Attach one device with name *dev_name to the backend.
|
|
*/
|
|
|
|
static SANE_Status
|
|
attach (const char *dev_name, Epson_Device * *devp, int type)
|
|
{
|
|
SANE_Status status;
|
|
Epson_Scanner *s = walloca (Epson_Scanner);
|
|
char *str;
|
|
struct Epson_Device *dev;
|
|
SANE_String_Const *source_list_add = source_list;
|
|
int port;
|
|
|
|
DBG (1, "%s\n", SANE_EPSON_VERSION);
|
|
|
|
DBG (5, "attach(%s, %d)\n", dev_name, type);
|
|
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
{
|
|
if (strcmp (dev->sane.name, dev_name) == 0)
|
|
{
|
|
if (devp)
|
|
{
|
|
*devp = dev;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
|
|
dev = malloc (sizeof (*dev));
|
|
if (!dev)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
/* check for PIO devices */
|
|
/* can we convert the device name to an integer? This is only possible
|
|
with PIO devices */
|
|
port = atoi (dev_name);
|
|
if (port != 0)
|
|
{
|
|
type = SANE_EPSON_PIO;
|
|
}
|
|
|
|
if (strncmp
|
|
(dev_name, SANE_EPSON_CONFIG_PIO, strlen (SANE_EPSON_CONFIG_PIO)) == 0)
|
|
{
|
|
/* we have a match for the PIO string and adjust the device name */
|
|
dev_name += strlen (SANE_EPSON_CONFIG_PIO);
|
|
dev_name = sanei_config_skip_whitespace (dev_name);
|
|
type = SANE_EPSON_PIO;
|
|
}
|
|
|
|
|
|
/*
|
|
* set dummy values.
|
|
*/
|
|
|
|
s->hw = dev;
|
|
s->hw->sane.name = NULL;
|
|
s->hw->sane.type = "flatbed scanner";
|
|
s->hw->sane.vendor = "Epson";
|
|
s->hw->sane.model = NULL;
|
|
s->hw->optical_res = 0; /* just to have it initialized */
|
|
s->hw->color_shuffle = SANE_FALSE;
|
|
s->hw->extension = SANE_FALSE;
|
|
s->hw->use_extension = SANE_FALSE;
|
|
|
|
s->hw->need_color_reorder = SANE_FALSE;
|
|
s->hw->need_double_vertical = SANE_FALSE;
|
|
|
|
s->hw->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT]; /* default function level */
|
|
s->hw->connection = type;
|
|
|
|
DBG (3, "attach: opening %s\n", dev_name);
|
|
|
|
s->hw->last_res = 0;
|
|
s->hw->last_res_preview = 0; /* set resolution to safe values */
|
|
|
|
/*
|
|
* decide if interface is USB, SCSI or parallel.
|
|
*/
|
|
|
|
/*
|
|
* if interface is SCSI do an inquiry.
|
|
*/
|
|
|
|
if (s->hw->connection == SANE_EPSON_SCSI)
|
|
{
|
|
u_char buf[INQUIRY_BUF_SIZE + 1];
|
|
size_t buf_size = INQUIRY_BUF_SIZE;
|
|
|
|
status =
|
|
sanei_scsi_open (dev_name, &s->fd, sanei_epson_scsi_sense_handler,
|
|
NULL);
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
|
|
return status;
|
|
}
|
|
DBG (3, "attach: sending INQUIRY\n");
|
|
|
|
status = sanei_epson_scsi_inquiry (s->fd, 0, buf, &buf_size);
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
|
|
close_scanner (s);
|
|
return status;
|
|
}
|
|
|
|
buf[INQUIRY_BUF_SIZE] = 0;
|
|
DBG (1, ">%s<\n", buf + 8);
|
|
|
|
/*
|
|
* For USB and PIO scanners this will be done later, once
|
|
* we have communication established with the device.
|
|
*/
|
|
|
|
if (buf[0] != TYPE_PROCESSOR
|
|
|| strncmp ((char *) (buf + 8), "EPSON", 5) != 0
|
|
|| (strncmp ((char *) buf + 16, "SCANNER ", 8) != 0
|
|
&& strncmp ((char *) buf + 14, "SCANNER ", 8) != 0
|
|
&& strncmp ((char *) buf + 14, "Perfection", 10) != 0
|
|
&& strncmp ((char *) buf + 16, "Perfection", 10) != 0
|
|
&& strncmp ((char *) buf + 16, "Expression", 10) != 0
|
|
&& strncmp ((char *) buf + 16, "GT", 2) != 0))
|
|
{
|
|
DBG (1, "attach: device doesn't look like an EPSON scanner\n");
|
|
close_scanner (s);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
/* use the SANEI functions to handle a PIO device */
|
|
else if (s->hw->connection == SANE_EPSON_PIO)
|
|
{
|
|
if (SANE_STATUS_GOOD != (status = sanei_pio_open (dev_name, &s->fd)))
|
|
{
|
|
DBG (1, "Cannot open %s as a parallel-port device: %s\n",
|
|
dev_name, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
/* use the SANEI functions to handle a USB device */
|
|
else if (s->hw->connection == SANE_EPSON_USB)
|
|
{
|
|
SANE_Word vendor;
|
|
SANE_Word product;
|
|
SANE_Bool isLibUSB;
|
|
|
|
isLibUSB = (strncmp (dev_name, "libusb:", strlen ("libusb:")) == 0);
|
|
|
|
if ((!isLibUSB) && (strlen (dev_name) == 0))
|
|
{
|
|
int i;
|
|
int numIds;
|
|
|
|
numIds = sanei_epson_getNumberOfUSBProductIds ();
|
|
|
|
for (i = 0; i < numIds; i++)
|
|
{
|
|
product = sanei_epson_usb_product_ids[i];
|
|
vendor = 0x4b8;
|
|
|
|
status = sanei_usb_find_devices (vendor, product, attach_one_usb);
|
|
}
|
|
return SANE_STATUS_INVAL; /* return - the attach_one_usb()
|
|
will take care of this */
|
|
}
|
|
|
|
status = sanei_usb_open (dev_name, &s->fd);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* if the sanei_usb_get_vendor_product call is not supported,
|
|
then we just ignore this and rely on the user to config
|
|
the correct device.
|
|
*/
|
|
|
|
if (sanei_usb_get_vendor_product (s->fd, &vendor, &product) ==
|
|
SANE_STATUS_GOOD)
|
|
{
|
|
int i; /* loop variable */
|
|
int numIds;
|
|
SANE_Bool is_valid;
|
|
|
|
/* check the vendor ID to see if we are dealing with an EPSON device */
|
|
if (vendor != SANE_EPSON_VENDOR_ID)
|
|
{
|
|
/* this is not a supported vendor ID */
|
|
DBG (1,
|
|
"The device at %s is not manufactured by EPSON (vendor id=0x%x)\n",
|
|
dev_name, vendor);
|
|
sanei_usb_close (s->fd);
|
|
s->fd = -1;
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
numIds = sanei_epson_getNumberOfUSBProductIds ();
|
|
is_valid = SANE_FALSE;
|
|
i = 0;
|
|
|
|
/* check all known product IDs to verify that we know
|
|
about the device */
|
|
while (i != numIds && !is_valid)
|
|
{
|
|
if (product == sanei_epson_usb_product_ids[i])
|
|
is_valid = SANE_TRUE;
|
|
|
|
i++;
|
|
}
|
|
|
|
if (is_valid == SANE_FALSE)
|
|
{
|
|
DBG (1,
|
|
"The device at %s is not a supported EPSON scanner (product id=0x%x)\n",
|
|
dev_name, product);
|
|
sanei_usb_close (s->fd);
|
|
s->fd = -1;
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
DBG (1, "Found valid EPSON scanner: 0x%x/0x%x (vendorID/productID)\n",
|
|
vendor, product);
|
|
}
|
|
else
|
|
{
|
|
DBG (1,
|
|
"Cannot use IOCTL interface to verify that device is a scanner - will continue\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the scanner (ESC @).
|
|
*/
|
|
reset (s);
|
|
|
|
|
|
|
|
/*
|
|
* Identification Request (ESC I).
|
|
*/
|
|
if (s->hw->cmd->request_identity != 0)
|
|
{
|
|
status = get_identity_information (s);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
} /* request identity */
|
|
|
|
|
|
/*
|
|
* Check for "Request Identity 2" command. If this command is available
|
|
* get the information from the scanner and store it in dev
|
|
*/
|
|
|
|
if (s->hw->cmd->request_identity2 != 0)
|
|
{
|
|
status = get_identity2_information (s);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
} /* request identity 2 */
|
|
|
|
|
|
/*
|
|
* Check for the max. supported color depth and assign
|
|
* the values to the bitDepthList.
|
|
*/
|
|
|
|
bitDepthList = malloc (sizeof (SANE_Word) * 4);
|
|
if (bitDepthList == NULL)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
bitDepthList[0] = 1; /* we start with one element in the list */
|
|
bitDepthList[1] = 8; /* 8bit is the default */
|
|
|
|
if (set_data_format (s, 16) == SANE_STATUS_GOOD)
|
|
{
|
|
s->hw->maxDepth = 16;
|
|
|
|
bitDepthList[0]++;
|
|
bitDepthList[bitDepthList[0]] = 16;
|
|
|
|
}
|
|
else if (set_data_format (s, 14) == SANE_STATUS_GOOD)
|
|
{
|
|
s->hw->maxDepth = 14;
|
|
|
|
bitDepthList[0]++;
|
|
bitDepthList[bitDepthList[0]] = 14;
|
|
}
|
|
else if (set_data_format (s, 12) == SANE_STATUS_GOOD)
|
|
{
|
|
s->hw->maxDepth = 12;
|
|
|
|
bitDepthList[0]++;
|
|
bitDepthList[bitDepthList[0]] = 12;
|
|
}
|
|
else
|
|
{
|
|
s->hw->maxDepth = 8;
|
|
|
|
/* the default depth is already in the list */
|
|
}
|
|
|
|
DBG (1, "Max. supported color depth = %d\n", s->hw->maxDepth);
|
|
|
|
|
|
/*
|
|
* Check for "request focus position" command. If this command is
|
|
* supported, then the scanner does also support the "set focus
|
|
* position" command.
|
|
*/
|
|
|
|
if (request_focus_position (s, &s->currentFocusPosition) ==
|
|
SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "Enabling 'Set Focus' support\n");
|
|
s->hw->focusSupport = SANE_TRUE;
|
|
s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
/* reflect the current focus position in the GUI */
|
|
if (s->currentFocusPosition < 0x4C)
|
|
{
|
|
/* focus on glass */
|
|
s->val[OPT_FOCUS].w = 0;
|
|
}
|
|
else
|
|
{
|
|
/* focus 2.5mm above glass */
|
|
s->val[OPT_FOCUS].w = 1;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "Disabling 'Set Focus' support\n");
|
|
s->hw->focusSupport = SANE_FALSE;
|
|
s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
|
|
s->val[OPT_FOCUS].w = 0; /* on glass - just in case */
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Set defaults for no extension.
|
|
*/
|
|
|
|
dev->x_range = &dev->fbf_x_range;
|
|
dev->y_range = &dev->fbf_y_range;
|
|
|
|
/*
|
|
* Correct for a firmware bug in some Perfection 1650 scanners:
|
|
* Firmware version 1.08 reports only half the vertical scan area, we have
|
|
* to double the number. To find out if we have to do this, we just compare
|
|
* is the vertical range is smaller than the horizontal range.
|
|
*/
|
|
|
|
if ((dev->x_range->max - dev->x_range->min) >
|
|
(dev->y_range->max - dev->y_range->min))
|
|
{
|
|
dev->y_range->max += (dev->y_range->max - dev->y_range->min);
|
|
dev->need_double_vertical = SANE_TRUE;
|
|
dev->need_color_reorder = SANE_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Extended status flag request (ESC f).
|
|
* this also requests the scanner device name from the the scanner
|
|
*/
|
|
/*
|
|
* because we are also using the device name from this command,
|
|
* we have to run this block even if the scanner does not report
|
|
* an extension. The extensions are only reported if the ADF or
|
|
* the TPU are actually detected.
|
|
*/
|
|
if (s->hw->cmd->request_extended_status != 0)
|
|
{
|
|
u_char *buf;
|
|
u_char params[2];
|
|
EpsonHdr head;
|
|
|
|
params[0] = ESC;
|
|
params[1] = s->hw->cmd->request_extended_status;
|
|
|
|
if (NULL == (head = (EpsonHdr) command (s, params, 2, &status)))
|
|
{
|
|
DBG (1, "Extended status flag request failed\n");
|
|
dev->sane.model = strdup ("Unknown model");
|
|
*source_list_add++ = FBF_STR;
|
|
}
|
|
else
|
|
{
|
|
buf = &head->buf[0];
|
|
|
|
/*
|
|
* Add the flatbed option to the source list
|
|
*/
|
|
|
|
*source_list_add++ = FBF_STR;
|
|
|
|
s->hw->devtype = buf[11] >> 6;
|
|
|
|
/*
|
|
* Get the device name and copy it to dev->sane.model.
|
|
* The device name starts at buf[0x1A] and is up to 16 bytes long
|
|
* We are overwriting whatever was set previously!
|
|
*/
|
|
{
|
|
char device_name[DEVICE_NAME_LEN + 1];
|
|
char *end_ptr;
|
|
int len;
|
|
|
|
/* make sure that the end of string is marked */
|
|
device_name[DEVICE_NAME_LEN] = '\0';
|
|
|
|
/* copy the string to an area where we can work with it */
|
|
memcpy (device_name, buf + 0x1A, DEVICE_NAME_LEN);
|
|
end_ptr = strchr (device_name, ' ');
|
|
if (end_ptr != NULL)
|
|
{
|
|
*end_ptr = '\0';
|
|
}
|
|
|
|
len = strlen (device_name);
|
|
|
|
str = malloc (len + 1);
|
|
str[len] = '\0';
|
|
|
|
dev->sane.model = (char *) memcpy (str, device_name, len);
|
|
}
|
|
/*
|
|
* ADF
|
|
*/
|
|
|
|
if (dev->extension && (buf[1] & EXT_STATUS_IST))
|
|
{
|
|
DBG (1, "ADF detected\n");
|
|
|
|
/* the GT-30000 does not report the ADF scan area */
|
|
if ((strcmp (dev->sane.model, "GT-30000") == 0) ||
|
|
(strcmp (dev->sane.model, "ES-9000H") == 0))
|
|
{
|
|
fix_up_extended_status_reply ((const char *) buf + 26, buf);
|
|
|
|
dev->duplexSupport = (buf[0] & 0x10) != 0;
|
|
if (dev->duplexSupport)
|
|
{
|
|
DBG (1, "Found DUPLEX ADF\n");
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
if (buf[1] & EXT_STATUS_EN)
|
|
{
|
|
DBG (1, "ADF is enabled\n");
|
|
dev->x_range = &dev->adf_x_range;
|
|
dev->y_range = &dev->adf_y_range;
|
|
}
|
|
|
|
dev->adf_x_range.min = 0;
|
|
dev->adf_x_range.max =
|
|
SANE_FIX ((buf[3] << 8 | buf[2]) * 25.4 / dev->dpi_range.max);
|
|
dev->adf_x_range.quant = 0;
|
|
|
|
dev->adf_max_x = buf[3] << 8 | buf[2];
|
|
|
|
dev->adf_y_range.min = 0;
|
|
dev->adf_y_range.max =
|
|
SANE_FIX ((buf[5] << 8 | buf[4]) * 25.4 / dev->dpi_range.max);
|
|
dev->adf_y_range.quant = 0;
|
|
|
|
dev->adf_max_y = buf[5] << 8 | buf[4];
|
|
|
|
DBG (5, "adf tlx %f tly %f brx %f bry %f [mm]\n",
|
|
SANE_UNFIX (dev->adf_x_range.min),
|
|
SANE_UNFIX (dev->adf_y_range.min),
|
|
SANE_UNFIX (dev->adf_x_range.max),
|
|
SANE_UNFIX (dev->adf_y_range.max));
|
|
|
|
*source_list_add++ = ADF_STR;
|
|
|
|
dev->ADF = SANE_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* TPU
|
|
*/
|
|
|
|
if (dev->extension && (buf[6] & EXT_STATUS_IST))
|
|
{
|
|
DBG (1, "TPU detected\n");
|
|
|
|
if (buf[6] & EXT_STATUS_EN)
|
|
{
|
|
DBG (1, "TPU is enabled\n");
|
|
dev->x_range = &dev->tpu_x_range;
|
|
dev->y_range = &dev->tpu_y_range;
|
|
}
|
|
|
|
dev->tpu_x_range.min = 0;
|
|
dev->tpu_x_range.max =
|
|
SANE_FIX ((buf[8] << 8 | buf[7]) * 25.4 / dev->dpi_range.max);
|
|
dev->tpu_x_range.quant = 0;
|
|
|
|
dev->tpu_y_range.min = 0;
|
|
dev->tpu_y_range.max =
|
|
SANE_FIX ((buf[10] << 8 | buf[9]) * 25.4 / dev->dpi_range.max);
|
|
dev->tpu_y_range.quant = 0;
|
|
|
|
/*
|
|
* Check for Perfection 4990 photo/GT-X800 scanner.
|
|
* This scanner only report 3200 dpi back.
|
|
* The scanner fysical supports 4800 dpi.
|
|
* This is simulated here...
|
|
* Futher details read:
|
|
* EPSON Programming guide for EPSON Color Image Scanner Perfection 4990
|
|
*/
|
|
if (strncmp((char *) buf + 0x1A,"GT-X800",7) == 0)
|
|
{
|
|
dev->tpu_x_range.max = (dev->tpu_x_range.max/32)*48;
|
|
dev->tpu_y_range.max = (dev->tpu_y_range.max/32)*48;
|
|
DBG (5, "dpi_range.max %x \n", dev->dpi_range.max);
|
|
}
|
|
|
|
DBG (5, "tpu tlx %f tly %f brx %f bry %f [mm]\n",
|
|
SANE_UNFIX (dev->tpu_x_range.min),
|
|
SANE_UNFIX (dev->tpu_y_range.min),
|
|
SANE_UNFIX (dev->tpu_x_range.max),
|
|
SANE_UNFIX (dev->tpu_y_range.max));
|
|
|
|
*source_list_add++ = TPU_STR;
|
|
|
|
dev->TPU = SANE_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Get the device name and copy it to dev->sane.model.
|
|
* The device name starts at buf[0x1A] and is up to 16 bytes long
|
|
* We are overwriting whatever was set previously!
|
|
*/
|
|
{
|
|
char device_name[DEVICE_NAME_LEN + 1];
|
|
char *end_ptr;
|
|
int len;
|
|
|
|
/* make sure that the end of string is marked */
|
|
device_name[DEVICE_NAME_LEN] = '\0';
|
|
|
|
/* copy the string to an area where we can work with it */
|
|
memcpy (device_name, buf + 0x1A, DEVICE_NAME_LEN);
|
|
end_ptr = strchr (device_name, ' ');
|
|
if (end_ptr != NULL)
|
|
{
|
|
*end_ptr = '\0';
|
|
}
|
|
|
|
len = strlen (device_name);
|
|
|
|
str = malloc (len + 1);
|
|
str[len] = '\0';
|
|
|
|
/* finally copy the device name to the structure */
|
|
dev->sane.model = (char *) memcpy (str, device_name, len);
|
|
}
|
|
}
|
|
}
|
|
else /* command is not known */
|
|
{
|
|
dev->sane.model = strdup ("EPSON Scanner");
|
|
}
|
|
|
|
*source_list_add = NULL; /* add end marker to source list */
|
|
|
|
DBG (1, "scanner model: %s\n", dev->sane.model);
|
|
|
|
/* establish defaults */
|
|
s->hw->need_reset_on_source_change = SANE_FALSE;
|
|
|
|
if (strcmp ("ES-9000H", dev->sane.model) == 0 ||
|
|
strcmp ("GT-30000", dev->sane.model) == 0)
|
|
{
|
|
s->hw->cmd->set_focus_position = 0;
|
|
s->hw->cmd->feed = 0x19;
|
|
}
|
|
else if (strcmp ("GT-8200", dev->sane.model) == 0 ||
|
|
strcmp ("Perfection1650", dev->sane.model) == 0 ||
|
|
strcmp ("Perfection1640", dev->sane.model) == 0 ||
|
|
strcmp ("GT-8700", dev->sane.model) == 0)
|
|
{
|
|
s->hw->cmd->feed = 0;
|
|
s->hw->cmd->set_focus_position = 0;
|
|
s->hw->need_reset_on_source_change = SANE_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set values for quick format "max" entry.
|
|
*/
|
|
|
|
qf_params[XtNumber (qf_params) - 1].tl_x = dev->x_range->min;
|
|
qf_params[XtNumber (qf_params) - 1].tl_y = dev->y_range->min;
|
|
qf_params[XtNumber (qf_params) - 1].br_x = dev->x_range->max;
|
|
qf_params[XtNumber (qf_params) - 1].br_y = dev->y_range->max;
|
|
|
|
|
|
/*
|
|
* Now we can finally set the device name:
|
|
*/
|
|
str = malloc (strlen (dev_name) + 1);
|
|
dev->sane.name = strcpy (str, dev_name);
|
|
|
|
close_scanner (s);
|
|
|
|
/*
|
|
* we are done with this one, prepare for the next scanner:
|
|
*/
|
|
|
|
++num_devices;
|
|
dev->next = first_dev;
|
|
first_dev = dev;
|
|
|
|
if (devp)
|
|
{
|
|
*devp = dev;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* attach_one()
|
|
*
|
|
* Part of the SANE API: Attaches the scanner with the device name in *dev.
|
|
*/
|
|
|
|
static SANE_Status
|
|
attach_one (const char *dev)
|
|
{
|
|
DBG (5, "attach_one(%s)\n", dev);
|
|
|
|
return attach (dev, 0, SANE_EPSON_SCSI);
|
|
}
|
|
|
|
SANE_Status
|
|
attach_one_usb (SANE_String_Const devname)
|
|
{
|
|
int len = strlen (devname);
|
|
char *attach_string;
|
|
|
|
DBG (5, "attach_one_usb(%s)\n", devname);
|
|
|
|
attach_string = alloca (len + 5);
|
|
if (attach_string == NULL)
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
return attach (devname, 0, SANE_EPSON_USB);
|
|
}
|
|
|
|
/*
|
|
* sane_init()
|
|
*
|
|
*
|
|
*/
|
|
SANE_Status
|
|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
|
|
{
|
|
size_t len;
|
|
FILE *fp;
|
|
|
|
authorize = authorize; /* get rid of compiler warning */
|
|
|
|
/* sanei_authorization(devicename, STRINGIFY(BACKEND_NAME), auth_callback); */
|
|
|
|
DBG_INIT ();
|
|
#if defined PACKAGE && defined VERSION
|
|
DBG (2, "sane_init: " PACKAGE " " VERSION "\n");
|
|
#endif
|
|
|
|
if (version_code != NULL)
|
|
*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, SANE_EPSON_BUILD);
|
|
|
|
sanei_usb_init ();
|
|
|
|
/* default to /dev/scanner instead of insisting on config file */
|
|
if ((fp = sanei_config_open (EPSON_CONFIG_FILE)))
|
|
{
|
|
char line[PATH_MAX];
|
|
|
|
while (sanei_config_read (line, sizeof (line), fp))
|
|
{
|
|
int vendor, product;
|
|
|
|
DBG (4, "sane_init, >%s<\n", line);
|
|
if (line[0] == '#') /* ignore line comments */
|
|
continue;
|
|
len = strlen (line);
|
|
if (!len)
|
|
continue; /* ignore empty lines */
|
|
|
|
if (sscanf (line, "usb %i %i", &vendor, &product) == 2)
|
|
{
|
|
int numIds;
|
|
|
|
/* add the vendor and product IDs to the list of
|
|
known devices before we call the attach function */
|
|
numIds = sanei_epson_getNumberOfUSBProductIds ();
|
|
if (vendor != 0x4b8)
|
|
continue; /* this is not an EPSON device */
|
|
|
|
sanei_epson_usb_product_ids[numIds - 1] = product;
|
|
sanei_usb_attach_matching_devices (line, attach_one_usb);
|
|
}
|
|
else if (strncmp (line, "usb", 3) == 0)
|
|
{
|
|
const char *dev_name;
|
|
/* remove the "usb" sub string */
|
|
dev_name = sanei_config_skip_whitespace (line + 3);
|
|
attach_one_usb (dev_name);
|
|
}
|
|
else
|
|
{
|
|
sanei_config_attach_matching_devices (line, attach_one);
|
|
}
|
|
}
|
|
fclose (fp);
|
|
}
|
|
|
|
/* read the option section and assign the connection type to the
|
|
scanner structure - which we don't have at this time. So I have
|
|
to come up with something :-) */
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
* void sane_exit(void)
|
|
*
|
|
* Clean up the list of attached scanners.
|
|
*/
|
|
|
|
void
|
|
sane_exit (void)
|
|
{
|
|
Epson_Device *dev, *next;
|
|
|
|
for (dev = first_dev; dev; dev = next)
|
|
{
|
|
next = dev->next;
|
|
free ((void *) dev->sane.name);
|
|
free ((void *) dev->sane.model);
|
|
free (dev);
|
|
}
|
|
|
|
free (devlist);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
SANE_Status
|
|
sane_get_devices (const SANE_Device * **device_list, SANE_Bool local_only)
|
|
{
|
|
Epson_Device *dev;
|
|
int i;
|
|
|
|
DBG (5, "sane_get_devices()\n");
|
|
|
|
local_only = local_only; /* just to get rid of the compiler warning */
|
|
|
|
if (devlist)
|
|
{
|
|
free (devlist);
|
|
}
|
|
|
|
devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
|
|
if (!devlist)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
i = 0;
|
|
|
|
for (dev = first_dev; i < num_devices; dev = dev->next)
|
|
{
|
|
devlist[i++] = &dev->sane;
|
|
}
|
|
|
|
devlist[i++] = 0;
|
|
|
|
*device_list = devlist;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static SANE_Status
|
|
init_options (Epson_Scanner * s)
|
|
{
|
|
int i;
|
|
SANE_Bool dummy;
|
|
|
|
DBG (5, "init_options()\n");
|
|
|
|
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->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;
|
|
|
|
/* "Scan Mode" group: */
|
|
|
|
s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
|
|
s->opt[OPT_MODE_GROUP].desc = "";
|
|
s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
|
|
s->opt[OPT_MODE_GROUP].cap = 0;
|
|
|
|
/* scan mode */
|
|
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].w = 0; /* Binary */
|
|
|
|
/* bit depth */
|
|
s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
|
|
s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
|
|
s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
|
|
s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
|
|
s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
s->opt[OPT_BIT_DEPTH].constraint.word_list = bitDepthList;
|
|
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
|
|
s->val[OPT_BIT_DEPTH].w = bitDepthList[1]; /* the first "real" element is the default */
|
|
|
|
if (bitDepthList[0] == 1) /* only one element in the list -> hide the option */
|
|
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
|
|
|
|
/* halftone */
|
|
s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
|
|
s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
|
|
s->opt[OPT_HALFTONE].desc = SANE_I18N ("Selects the halftone.");
|
|
|
|
s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_HALFTONE].size = max_string_size (halftone_list_7);
|
|
s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
if (s->hw->level >= 7)
|
|
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7;
|
|
else if (s->hw->level >= 4)
|
|
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4;
|
|
else
|
|
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
|
|
|
|
s->val[OPT_HALFTONE].w = 1; /* Halftone A */
|
|
|
|
if (!s->hw->cmd->set_halftoning)
|
|
{
|
|
s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* dropout */
|
|
s->opt[OPT_DROPOUT].name = "dropout";
|
|
s->opt[OPT_DROPOUT].title = SANE_I18N ("Dropout");
|
|
s->opt[OPT_DROPOUT].desc = SANE_I18N ("Selects the dropout.");
|
|
|
|
s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_DROPOUT].size = max_string_size (dropout_list);
|
|
s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_DROPOUT].constraint.string_list = dropout_list;
|
|
s->val[OPT_DROPOUT].w = 0; /* None */
|
|
|
|
/* brightness */
|
|
s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
|
|
s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
|
|
s->opt[OPT_BRIGHTNESS].desc = SANE_I18N ("Selects the brightness.");
|
|
|
|
s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
|
|
s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range;
|
|
s->val[OPT_BRIGHTNESS].w = 0; /* Normal */
|
|
|
|
if (!s->hw->cmd->set_bright)
|
|
{
|
|
s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* sharpness */
|
|
s->opt[OPT_SHARPNESS].name = "sharpness";
|
|
s->opt[OPT_SHARPNESS].title = SANE_I18N ("Sharpness");
|
|
s->opt[OPT_SHARPNESS].desc = "";
|
|
|
|
s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT;
|
|
s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range;
|
|
s->val[OPT_SHARPNESS].w = 0; /* Normal */
|
|
|
|
if (!s->hw->cmd->set_outline_emphasis)
|
|
{
|
|
s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
/* gamma */
|
|
s->opt[OPT_GAMMA_CORRECTION].name = SANE_NAME_GAMMA_CORRECTION;
|
|
s->opt[OPT_GAMMA_CORRECTION].title = SANE_TITLE_GAMMA_CORRECTION;
|
|
s->opt[OPT_GAMMA_CORRECTION].desc = SANE_DESC_GAMMA_CORRECTION;
|
|
|
|
s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_GAMMA_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
/*
|
|
* special handling for D1 function level - at this time I'm not
|
|
* testing for D1, I'm just assuming that all D level scanners will
|
|
* behave the same way. This has to be confirmed with the next D-level
|
|
* scanner
|
|
*/
|
|
if (s->hw->cmd->level[0] == 'D')
|
|
{
|
|
s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_d);
|
|
s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_d;
|
|
s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */
|
|
gamma_userdefined = gamma_userdefined_d;
|
|
gamma_params = gamma_params_d;
|
|
}
|
|
else
|
|
{
|
|
s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_ab);
|
|
s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_ab;
|
|
s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */
|
|
gamma_userdefined = gamma_userdefined_ab;
|
|
gamma_params = gamma_params_ab;
|
|
}
|
|
|
|
if (!s->hw->cmd->set_gamma)
|
|
{
|
|
s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
/* 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;
|
|
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;
|
|
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[0][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;
|
|
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[1][0];
|
|
|
|
|
|
/* red 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;
|
|
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[2][0];
|
|
|
|
if (s->hw->cmd->set_gamma_table &&
|
|
gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w] == SANE_TRUE)
|
|
{
|
|
/* 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_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;
|
|
}
|
|
|
|
/* initialize the Gamma tables */
|
|
memset (&s->gamma_table[0], 0, 256 * sizeof (SANE_Word));
|
|
memset (&s->gamma_table[1], 0, 256 * sizeof (SANE_Word));
|
|
memset (&s->gamma_table[2], 0, 256 * sizeof (SANE_Word));
|
|
/* memset(&s->gamma_table[3], 0, 256 * sizeof(SANE_Word)); */
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
s->gamma_table[0][i] = i;
|
|
s->gamma_table[1][i] = i;
|
|
s->gamma_table[2][i] = i;
|
|
/* s->gamma_table[3][i] = i; */
|
|
}
|
|
|
|
|
|
/* color correction */
|
|
s->opt[OPT_COLOR_CORRECTION].name = "color-correction";
|
|
s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N ("Color correction");
|
|
s->opt[OPT_COLOR_CORRECTION].desc =
|
|
SANE_I18N
|
|
("Sets the color correction table for the selected output device.");
|
|
|
|
s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_COLOR_CORRECTION].size = 32;
|
|
s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_COLOR_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list;
|
|
s->val[OPT_COLOR_CORRECTION].w = 5; /* scanner default: CRT monitors */
|
|
|
|
if (!s->hw->cmd->set_color_correction)
|
|
{
|
|
s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* resolution */
|
|
s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
|
|
s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
|
|
s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
|
|
|
|
s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
|
|
s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
|
|
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->resolution_list;
|
|
s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
|
|
|
|
/* 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 = &u8_range;
|
|
s->val[OPT_THRESHOLD].w = 0x80;
|
|
|
|
if (!s->hw->cmd->set_threshold)
|
|
{
|
|
s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
s->opt[OPT_CCT_GROUP].title = SANE_I18N ("Color correction coefficients");
|
|
s->opt[OPT_CCT_GROUP].desc = SANE_I18N ("Matrix multiplication of RGB");
|
|
s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP;
|
|
s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
|
|
/* color correction coefficients */
|
|
s->opt[OPT_CCT_1].name = "cct-1";
|
|
s->opt[OPT_CCT_2].name = "cct-2";
|
|
s->opt[OPT_CCT_3].name = "cct-3";
|
|
s->opt[OPT_CCT_4].name = "cct-4";
|
|
s->opt[OPT_CCT_5].name = "cct-5";
|
|
s->opt[OPT_CCT_6].name = "cct-6";
|
|
s->opt[OPT_CCT_7].name = "cct-7";
|
|
s->opt[OPT_CCT_8].name = "cct-8";
|
|
s->opt[OPT_CCT_9].name = "cct-9";
|
|
|
|
s->opt[OPT_CCT_1].title = SANE_I18N ("Green");
|
|
s->opt[OPT_CCT_2].title = SANE_I18N ("Shift green to red");
|
|
s->opt[OPT_CCT_3].title = SANE_I18N ("Shift green to blue");
|
|
s->opt[OPT_CCT_4].title = SANE_I18N ("Shift red to green");
|
|
s->opt[OPT_CCT_5].title = SANE_I18N ("Red");
|
|
s->opt[OPT_CCT_6].title = SANE_I18N ("Shift red to blue");
|
|
s->opt[OPT_CCT_7].title = SANE_I18N ("Shift blue to green");
|
|
s->opt[OPT_CCT_8].title = SANE_I18N ("Shift blue to red");
|
|
s->opt[OPT_CCT_9].title = SANE_I18N ("Blue");
|
|
|
|
s->opt[OPT_CCT_1].desc = SANE_I18N ("Controls green level");
|
|
s->opt[OPT_CCT_2].desc = SANE_I18N ("Adds to red based on green level");
|
|
s->opt[OPT_CCT_3].desc = SANE_I18N ("Adds to blue based on green level");
|
|
s->opt[OPT_CCT_4].desc = SANE_I18N ("Adds to green based on red level");
|
|
s->opt[OPT_CCT_5].desc = SANE_I18N ("Controls red level");
|
|
s->opt[OPT_CCT_6].desc = SANE_I18N ("Adds to blue based on red level");
|
|
s->opt[OPT_CCT_7].desc = SANE_I18N ("Adds to green based on blue level");
|
|
s->opt[OPT_CCT_8].desc = SANE_I18N ("Adds to red based on blue level");
|
|
s->opt[OPT_CCT_9].desc = SANE_I18N ("Controls blue level");
|
|
|
|
s->opt[OPT_CCT_1].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_2].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_3].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_4].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_5].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_6].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_7].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_8].type = SANE_TYPE_INT;
|
|
s->opt[OPT_CCT_9].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_CCT_1].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_2].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_3].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_4].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_5].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_6].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_7].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_8].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_CCT_9].cap |= SANE_CAP_ADVANCED;
|
|
|
|
s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_CCT_1].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_2].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_3].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_4].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_5].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_6].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_7].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_8].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_CCT_9].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_CCT_1].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_2].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_3].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_4].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_5].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_6].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_7].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_8].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_CCT_9].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_CCT_1].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_2].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_3].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_4].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_5].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_6].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_7].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_8].constraint.range = &s8_range;
|
|
s->opt[OPT_CCT_9].constraint.range = &s8_range;
|
|
|
|
s->val[OPT_CCT_1].w = 32;
|
|
s->val[OPT_CCT_2].w = 0;
|
|
s->val[OPT_CCT_3].w = 0;
|
|
s->val[OPT_CCT_4].w = 0;
|
|
s->val[OPT_CCT_5].w = 32;
|
|
s->val[OPT_CCT_6].w = 0;
|
|
s->val[OPT_CCT_7].w = 0;
|
|
s->val[OPT_CCT_8].w = 0;
|
|
s->val[OPT_CCT_9].w = 32;
|
|
|
|
if (!s->hw->cmd->set_color_correction_coefficients)
|
|
{
|
|
s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE;
|
|
s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
/* "Advanced" group: */
|
|
s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N ("Advanced");
|
|
s->opt[OPT_ADVANCED_GROUP].desc = "";
|
|
s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
|
|
s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
|
|
/* mirror */
|
|
s->opt[OPT_MIRROR].name = "mirror";
|
|
s->opt[OPT_MIRROR].title = SANE_I18N ("Mirror image");
|
|
s->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror the image.");
|
|
|
|
s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
|
|
s->val[OPT_MIRROR].w = SANE_FALSE;
|
|
|
|
if (!s->hw->cmd->mirror_image)
|
|
{
|
|
s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
/* speed */
|
|
s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
|
|
s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
|
|
s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
|
|
|
|
s->opt[OPT_SPEED].type = SANE_TYPE_BOOL;
|
|
s->val[OPT_SPEED].w = SANE_FALSE;
|
|
|
|
if (!s->hw->cmd->set_speed)
|
|
{
|
|
s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* preview speed */
|
|
s->opt[OPT_PREVIEW_SPEED].name = "preview-speed";
|
|
s->opt[OPT_PREVIEW_SPEED].title = SANE_I18N ("Fast preview");
|
|
s->opt[OPT_PREVIEW_SPEED].desc = "";
|
|
|
|
s->opt[OPT_PREVIEW_SPEED].type = SANE_TYPE_BOOL;
|
|
s->val[OPT_PREVIEW_SPEED].w = SANE_FALSE;
|
|
|
|
if (!s->hw->cmd->set_speed)
|
|
{
|
|
s->opt[OPT_PREVIEW_SPEED].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* auto area segmentation */
|
|
s->opt[OPT_AAS].name = "auto-area-segmentation";
|
|
s->opt[OPT_AAS].title = SANE_I18N ("Auto area segmentation");
|
|
s->opt[OPT_AAS].desc = "";
|
|
|
|
s->opt[OPT_AAS].type = SANE_TYPE_BOOL;
|
|
s->val[OPT_AAS].w = SANE_TRUE;
|
|
|
|
if (!s->hw->cmd->control_auto_area_segmentation)
|
|
{
|
|
s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* limit resolution list */
|
|
s->opt[OPT_LIMIT_RESOLUTION].name = "short-resolution";
|
|
s->opt[OPT_LIMIT_RESOLUTION].title = SANE_I18N ("Short resolution list");
|
|
s->opt[OPT_LIMIT_RESOLUTION].desc =
|
|
SANE_I18N ("Display short resolution list");
|
|
s->opt[OPT_LIMIT_RESOLUTION].type = SANE_TYPE_BOOL;
|
|
s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE;
|
|
|
|
|
|
/* zoom */
|
|
s->opt[OPT_ZOOM].name = "zoom";
|
|
s->opt[OPT_ZOOM].title = SANE_I18N ("Zoom");
|
|
s->opt[OPT_ZOOM].desc =
|
|
SANE_I18N ("Defines the zoom factor the scanner will use");
|
|
|
|
s->opt[OPT_ZOOM].type = SANE_TYPE_INT;
|
|
s->opt[OPT_ZOOM].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_ZOOM].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
s->opt[OPT_ZOOM].constraint.range = &zoom_range;
|
|
s->val[OPT_ZOOM].w = 100;
|
|
|
|
/* if( ! s->hw->cmd->set_zoom) */
|
|
{
|
|
s->opt[OPT_ZOOM].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
/* "Preview settings" group: */
|
|
s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW;
|
|
s->opt[OPT_PREVIEW_GROUP].desc = "";
|
|
s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP;
|
|
s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
/* preview */
|
|
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->val[OPT_PREVIEW].w = SANE_FALSE;
|
|
|
|
/* "Geometry" group: */
|
|
s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("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;
|
|
|
|
/* 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->hw->x_range;
|
|
s->val[OPT_TL_X].w = 0;
|
|
|
|
/* 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->hw->y_range;
|
|
s->val[OPT_TL_Y].w = 0;
|
|
|
|
/* 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->hw->x_range;
|
|
s->val[OPT_BR_X].w = s->hw->x_range->max;
|
|
|
|
/* 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->hw->y_range;
|
|
s->val[OPT_BR_Y].w = s->hw->y_range->max;
|
|
|
|
/* Quick format */
|
|
s->opt[OPT_QUICK_FORMAT].name = "quick-format";
|
|
s->opt[OPT_QUICK_FORMAT].title = SANE_I18N ("Quick format");
|
|
s->opt[OPT_QUICK_FORMAT].desc = "";
|
|
|
|
s->opt[OPT_QUICK_FORMAT].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_QUICK_FORMAT].size = max_string_size (qf_list);
|
|
s->opt[OPT_QUICK_FORMAT].cap |= SANE_CAP_ADVANCED;
|
|
s->opt[OPT_QUICK_FORMAT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_QUICK_FORMAT].constraint.string_list = qf_list;
|
|
s->val[OPT_QUICK_FORMAT].w = XtNumber (qf_params) - 1; /* max */
|
|
|
|
/* "Optional equipment" group: */
|
|
s->opt[OPT_EQU_GROUP].title = SANE_I18N ("Optional equipment");
|
|
s->opt[OPT_EQU_GROUP].desc = "";
|
|
s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP;
|
|
s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
|
|
/* source */
|
|
s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
|
|
s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
|
|
s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
|
|
|
|
s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_SOURCE].size = max_string_size (source_list);
|
|
|
|
s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_SOURCE].constraint.string_list = source_list;
|
|
|
|
if (!s->hw->extension)
|
|
{
|
|
s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */
|
|
|
|
|
|
/* film type */
|
|
s->opt[OPT_FILM_TYPE].name = "film-type";
|
|
s->opt[OPT_FILM_TYPE].title = SANE_I18N ("Film type");
|
|
s->opt[OPT_FILM_TYPE].desc = "";
|
|
|
|
s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_FILM_TYPE].size = max_string_size (film_list);
|
|
|
|
s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_FILM_TYPE].constraint.string_list = film_list;
|
|
|
|
s->val[OPT_FILM_TYPE].w = 0;
|
|
|
|
deactivateOption (s, OPT_FILM_TYPE, &dummy); /* default is inactive */
|
|
|
|
/* focus position */
|
|
s->opt[OPT_FOCUS].name = SANE_EPSON_FOCUS_NAME;
|
|
s->opt[OPT_FOCUS].title = SANE_EPSON_FOCUS_TITLE;
|
|
s->opt[OPT_FOCUS].desc = SANE_EPSON_FOCUS_DESC;
|
|
s->opt[OPT_FOCUS].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_FOCUS].size = max_string_size (focus_list);
|
|
s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_FOCUS].constraint.string_list = focus_list;
|
|
s->val[OPT_FOCUS].w = 0;
|
|
|
|
s->opt[OPT_FOCUS].cap |= SANE_CAP_ADVANCED;
|
|
if (s->hw->focusSupport == SANE_TRUE)
|
|
{
|
|
s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
|
|
}
|
|
else
|
|
{
|
|
s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
#if 0
|
|
if ((!s->hw->TPU) && (!s->hw->cmd->set_bay))
|
|
{ /* Hack: Using set_bay to indicate. */
|
|
SANE_Bool dummy;
|
|
deactivateOption (s, OPT_FILM_TYPE, &dummy);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
/* forward feed / eject */
|
|
s->opt[OPT_EJECT].name = "eject";
|
|
s->opt[OPT_EJECT].title = SANE_I18N ("Eject");
|
|
s->opt[OPT_EJECT].desc = SANE_I18N ("Eject the sheet in the ADF");
|
|
|
|
s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON;
|
|
|
|
if ((!s->hw->ADF) && (!s->hw->cmd->set_bay))
|
|
{ /* Hack: Using set_bay to indicate. */
|
|
s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
/* auto forward feed / eject */
|
|
s->opt[OPT_AUTO_EJECT].name = "auto-eject";
|
|
s->opt[OPT_AUTO_EJECT].title = SANE_I18N ("Auto eject");
|
|
s->opt[OPT_AUTO_EJECT].desc = SANE_I18N ("Eject document after scanning");
|
|
|
|
s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL;
|
|
s->val[OPT_AUTO_EJECT].w = SANE_FALSE;
|
|
|
|
if (!s->hw->ADF)
|
|
{
|
|
s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
s->opt[OPT_ADF_MODE].name = "adf_mode";
|
|
s->opt[OPT_ADF_MODE].title = SANE_I18N ("ADF Mode");
|
|
s->opt[OPT_ADF_MODE].desc =
|
|
SANE_I18N ("Selects the ADF mode (simplex/duplex)");
|
|
s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_ADF_MODE].size = max_string_size (adf_mode_list);
|
|
s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
|
|
s->val[OPT_ADF_MODE].w = 0; /* simplex */
|
|
|
|
if ((!s->hw->ADF) || (s->hw->duplexSupport == SANE_FALSE))
|
|
{
|
|
s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* select bay */
|
|
s->opt[OPT_BAY].name = "bay";
|
|
s->opt[OPT_BAY].title = SANE_I18N ("Bay");
|
|
s->opt[OPT_BAY].desc = SANE_I18N ("Select bay to scan");
|
|
|
|
s->opt[OPT_BAY].type = SANE_TYPE_STRING;
|
|
s->opt[OPT_BAY].size = max_string_size (bay_list);
|
|
s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
s->opt[OPT_BAY].constraint.string_list = bay_list;
|
|
s->val[OPT_BAY].w = 0; /* Bay 1 */
|
|
|
|
if (!s->hw->cmd->set_bay)
|
|
{
|
|
s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
s->opt[OPT_WAIT_FOR_BUTTON].name = SANE_EPSON_WAIT_FOR_BUTTON_NAME;
|
|
s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_EPSON_WAIT_FOR_BUTTON_TITLE;
|
|
s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_EPSON_WAIT_FOR_BUTTON_DESC;
|
|
|
|
s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL;
|
|
s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE;
|
|
s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE;
|
|
s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL;
|
|
s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED;
|
|
|
|
if (!s->hw->cmd->request_push_button_status)
|
|
{
|
|
s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
SANE_Status
|
|
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
|
|
{
|
|
Epson_Device *dev;
|
|
Epson_Scanner *s;
|
|
|
|
DBG (5, "sane_open(%s)\n", devicename);
|
|
|
|
/* search for device */
|
|
if (devicename[0])
|
|
{
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
{
|
|
if (strcmp (dev->sane.name, devicename) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!dev)
|
|
{
|
|
#if 0
|
|
status = attach (devicename, &dev, SANE_EPSON_);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
return status;
|
|
}
|
|
#endif
|
|
DBG (1, "Error opening the device");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dev = first_dev;
|
|
}
|
|
|
|
if (!dev)
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
s = calloc (sizeof (Epson_Scanner), 1);
|
|
if (!s)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
s->fd = -1;
|
|
s->hw = dev;
|
|
|
|
init_options (s);
|
|
|
|
/* insert newly opened handle into list of open handles */
|
|
s->next = first_handle;
|
|
first_handle = s;
|
|
|
|
*handle = (SANE_Handle) s;
|
|
|
|
open_scanner (s);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
void
|
|
sane_close (SANE_Handle handle)
|
|
{
|
|
Epson_Scanner *s, *prev;
|
|
|
|
/*
|
|
* Test if there is still data pending from
|
|
* the scanner. If so, then do a cancel
|
|
*/
|
|
|
|
s = (Epson_Scanner *) handle;
|
|
|
|
/* remove handle from list of open handles */
|
|
prev = 0;
|
|
for (s = first_handle; s; s = s->next)
|
|
{
|
|
if (s == handle)
|
|
break;
|
|
prev = s;
|
|
}
|
|
|
|
if (!s)
|
|
{
|
|
DBG (1, "close: invalid handle (0x%p)\n", handle);
|
|
return;
|
|
}
|
|
|
|
if (prev)
|
|
prev->next = s->next;
|
|
else
|
|
first_handle = s->next;
|
|
|
|
if (s->fd != -1)
|
|
close_scanner (s);
|
|
|
|
free (s);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
const SANE_Option_Descriptor *
|
|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
|
|
if (option < 0 || option >= NUM_OPTIONS)
|
|
return NULL;
|
|
|
|
return (s->opt + option);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static const SANE_String_Const *
|
|
search_string_list (const SANE_String_Const * list, SANE_String value)
|
|
{
|
|
while (*list != NULL && strcmp (value, *list) != 0)
|
|
{
|
|
++list;
|
|
}
|
|
|
|
return ((*list == NULL) ? NULL : list);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
/*
|
|
Activate, deactivate an option. Subroutines so we can add
|
|
debugging info if we want. The change flag is set to TRUE
|
|
if we changed an option. If we did not change an option,
|
|
then the value of the changed flag is not modified.
|
|
*/
|
|
|
|
static void
|
|
activateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change)
|
|
{
|
|
if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
|
|
{
|
|
s->opt[option].cap &= ~SANE_CAP_INACTIVE;
|
|
*change = SANE_TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
deactivateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change)
|
|
{
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
|
|
{
|
|
s->opt[option].cap |= SANE_CAP_INACTIVE;
|
|
*change = SANE_TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
setOptionState (Epson_Scanner * s, SANE_Bool state,
|
|
SANE_Int option, SANE_Bool * change)
|
|
{
|
|
if (state)
|
|
{
|
|
activateOption (s, option, change);
|
|
}
|
|
else
|
|
{
|
|
deactivateOption (s, option, change);
|
|
}
|
|
}
|
|
|
|
/**
|
|
End of activateOption, deactivateOption, setOptionState.
|
|
**/
|
|
|
|
static SANE_Status
|
|
getvalue (SANE_Handle handle, SANE_Int option, void *value)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Option_Descriptor *sopt = &(s->opt[option]);
|
|
Option_Value *sval = &(s->val[option]);
|
|
|
|
switch (option)
|
|
{
|
|
/* case OPT_GAMMA_VECTOR: */
|
|
case OPT_GAMMA_VECTOR_R:
|
|
case OPT_GAMMA_VECTOR_G:
|
|
case OPT_GAMMA_VECTOR_B:
|
|
memcpy (value, sval->wa, sopt->size);
|
|
break;
|
|
|
|
case OPT_NUM_OPTS:
|
|
case OPT_RESOLUTION:
|
|
case OPT_TL_X:
|
|
case OPT_TL_Y:
|
|
case OPT_BR_X:
|
|
case OPT_BR_Y:
|
|
case OPT_MIRROR:
|
|
case OPT_SPEED:
|
|
case OPT_PREVIEW_SPEED:
|
|
case OPT_AAS:
|
|
case OPT_PREVIEW:
|
|
case OPT_BRIGHTNESS:
|
|
case OPT_SHARPNESS:
|
|
case OPT_AUTO_EJECT:
|
|
case OPT_CCT_1:
|
|
case OPT_CCT_2:
|
|
case OPT_CCT_3:
|
|
case OPT_CCT_4:
|
|
case OPT_CCT_5:
|
|
case OPT_CCT_6:
|
|
case OPT_CCT_7:
|
|
case OPT_CCT_8:
|
|
case OPT_CCT_9:
|
|
case OPT_THRESHOLD:
|
|
case OPT_ZOOM:
|
|
case OPT_BIT_DEPTH:
|
|
case OPT_WAIT_FOR_BUTTON:
|
|
case OPT_LIMIT_RESOLUTION:
|
|
*((SANE_Word *) value) = sval->w;
|
|
break;
|
|
case OPT_MODE:
|
|
case OPT_ADF_MODE:
|
|
case OPT_HALFTONE:
|
|
case OPT_DROPOUT:
|
|
case OPT_QUICK_FORMAT:
|
|
case OPT_SOURCE:
|
|
case OPT_FILM_TYPE:
|
|
case OPT_GAMMA_CORRECTION:
|
|
case OPT_COLOR_CORRECTION:
|
|
case OPT_BAY:
|
|
case OPT_FOCUS:
|
|
strcpy ((char *) value, sopt->constraint.string_list[sval->w]);
|
|
break;
|
|
#if 0
|
|
case OPT_MODEL:
|
|
strcpy (value, sval->s);
|
|
break;
|
|
#endif
|
|
|
|
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
/**
|
|
End of getvalue.
|
|
**/
|
|
|
|
|
|
static void
|
|
handle_depth_halftone (Epson_Scanner * s, SANE_Bool * reload)
|
|
/*
|
|
This routine handles common options between OPT_MODE and
|
|
OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS
|
|
- auto area segmentation, and threshold. Apparently AAS
|
|
is some method to differentiate between text and photos.
|
|
Or something like that.
|
|
|
|
AAS is available when the scan color depth is 1 and the
|
|
halftone method is not TET.
|
|
|
|
Threshold is available when halftone is NONE, and depth is 1.
|
|
*/
|
|
{
|
|
int hti = s->val[OPT_HALFTONE].w;
|
|
int mdi = s->val[OPT_MODE].w;
|
|
SANE_Bool aas = SANE_FALSE;
|
|
SANE_Bool thresh = SANE_FALSE;
|
|
|
|
if (!s->hw->cmd->control_auto_area_segmentation)
|
|
return;
|
|
|
|
if (mode_params[mdi].depth == 1)
|
|
{
|
|
if (halftone_params[hti] != HALFTONE_TET)
|
|
{
|
|
aas = SANE_TRUE;
|
|
}
|
|
if (halftone_params[hti] == HALFTONE_NONE)
|
|
{
|
|
thresh = SANE_TRUE;
|
|
}
|
|
}
|
|
setOptionState (s, aas, OPT_AAS, reload);
|
|
setOptionState (s, thresh, OPT_THRESHOLD, reload);
|
|
}
|
|
|
|
/**
|
|
End of handle_depth_halftone.
|
|
**/
|
|
|
|
|
|
static void
|
|
handle_source (Epson_Scanner * s, SANE_Int optindex, char *value)
|
|
/*
|
|
Handles setting the source (flatbed, transparency adapter (TPU),
|
|
or auto document feeder (ADF)).
|
|
|
|
For newer scanners it also sets the focus according to the
|
|
glass / TPU settings.
|
|
*/
|
|
{
|
|
int force_max = SANE_FALSE;
|
|
SANE_Bool dummy;
|
|
|
|
/* reset the scanner when we are changing the source setting -
|
|
this is necessary for the Perfection 1650 */
|
|
if (s->hw->need_reset_on_source_change)
|
|
reset (s);
|
|
|
|
s->focusOnGlass = SANE_TRUE; /* this is the default */
|
|
|
|
if (s->val[OPT_SOURCE].w == optindex)
|
|
return;
|
|
|
|
s->val[OPT_SOURCE].w = optindex;
|
|
|
|
if (s->val[OPT_TL_X].w == s->hw->x_range->min
|
|
&& s->val[OPT_TL_Y].w == s->hw->y_range->min
|
|
&& s->val[OPT_BR_X].w == s->hw->x_range->max
|
|
&& s->val[OPT_BR_Y].w == s->hw->y_range->max)
|
|
{
|
|
force_max = SANE_TRUE;
|
|
}
|
|
if (strcmp (ADF_STR, value) == 0)
|
|
{
|
|
s->hw->x_range = &s->hw->adf_x_range;
|
|
s->hw->y_range = &s->hw->adf_y_range;
|
|
s->hw->use_extension = SANE_TRUE;
|
|
/* disable film type option */
|
|
deactivateOption (s, OPT_FILM_TYPE, &dummy);
|
|
s->val[OPT_FOCUS].w = 0;
|
|
if (s->hw->duplexSupport)
|
|
{
|
|
activateOption (s, OPT_ADF_MODE, &dummy);
|
|
}
|
|
else
|
|
{
|
|
deactivateOption (s, OPT_ADF_MODE, &dummy);
|
|
s->val[OPT_ADF_MODE].w = 0;
|
|
}
|
|
}
|
|
else if (strcmp (TPU_STR, value) == 0)
|
|
{
|
|
s->hw->x_range = &s->hw->tpu_x_range;
|
|
s->hw->y_range = &s->hw->tpu_y_range;
|
|
s->hw->use_extension = SANE_TRUE;
|
|
/* enable film type option only if the scanner supports it */
|
|
if (s->hw->cmd->set_film_type != 0)
|
|
{
|
|
activateOption (s, OPT_FILM_TYPE, &dummy);
|
|
}
|
|
else
|
|
{
|
|
deactivateOption (s, OPT_FILM_TYPE, &dummy);
|
|
}
|
|
/* enable focus position if the scanner supports it */
|
|
if (s->hw->cmd->set_focus_position != 0)
|
|
{
|
|
s->val[OPT_FOCUS].w = 1;
|
|
s->focusOnGlass = SANE_FALSE;
|
|
}
|
|
deactivateOption (s, OPT_ADF_MODE, &dummy);
|
|
deactivateOption (s, OPT_EJECT, &dummy);
|
|
deactivateOption (s, OPT_AUTO_EJECT, &dummy);
|
|
}
|
|
else /* neither ADF nor TPU active */
|
|
{
|
|
s->hw->x_range = &s->hw->fbf_x_range;
|
|
s->hw->y_range = &s->hw->fbf_y_range;
|
|
s->hw->use_extension = SANE_FALSE;
|
|
/* disable film type option */
|
|
deactivateOption (s, OPT_FILM_TYPE, &dummy);
|
|
s->val[OPT_FOCUS].w = 0;
|
|
deactivateOption (s, OPT_ADF_MODE, &dummy);
|
|
}
|
|
|
|
qf_params[XtNumber (qf_params) - 1].tl_x = s->hw->x_range->min;
|
|
qf_params[XtNumber (qf_params) - 1].tl_y = s->hw->y_range->min;
|
|
qf_params[XtNumber (qf_params) - 1].br_x = s->hw->x_range->max;
|
|
qf_params[XtNumber (qf_params) - 1].br_y = s->hw->y_range->max;
|
|
|
|
s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
|
|
s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
|
|
|
|
if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max)
|
|
s->val[OPT_TL_X].w = s->hw->x_range->min;
|
|
|
|
if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max)
|
|
s->val[OPT_TL_Y].w = s->hw->y_range->min;
|
|
|
|
if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max)
|
|
s->val[OPT_BR_X].w = s->hw->x_range->max;
|
|
|
|
if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max)
|
|
s->val[OPT_BR_Y].w = s->hw->y_range->max;
|
|
|
|
setOptionState (s, s->hw->ADF && s->hw->use_extension,
|
|
OPT_AUTO_EJECT, &dummy);
|
|
setOptionState (s, s->hw->ADF && s->hw->use_extension, OPT_EJECT, &dummy);
|
|
|
|
#if 0
|
|
BAY is part of the filmscan device.We are not sure
|
|
if we are really going to support this device in this
|
|
code.Is there an online manual for it ?
|
|
setOptionState (s, s->hw->ADF && s->hw->use_extension, OPT_BAY, &reload);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
End of handle_source.
|
|
**/
|
|
|
|
static SANE_Status
|
|
setvalue (SANE_Handle handle, SANE_Int option, void *value, SANE_Int * info)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Option_Descriptor *sopt = &(s->opt[option]);
|
|
Option_Value *sval = &(s->val[option]);
|
|
|
|
SANE_Status status;
|
|
const SANE_String_Const *optval;
|
|
int optindex;
|
|
SANE_Bool reload = SANE_FALSE;
|
|
|
|
DBG (5, "setvalue(option = %d, value = %p)\n", option, value);
|
|
|
|
status = sanei_constrain_value (sopt, value, info);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
s->option_has_changed = SANE_TRUE;
|
|
|
|
optval = NULL;
|
|
optindex = 0;
|
|
|
|
if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST)
|
|
{
|
|
optval = search_string_list (sopt->constraint.string_list,
|
|
(char *) value);
|
|
|
|
if (optval == NULL)
|
|
return SANE_STATUS_INVAL;
|
|
optindex = optval - sopt->constraint.string_list;
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
/* case OPT_GAMMA_VECTOR: */
|
|
case OPT_GAMMA_VECTOR_R:
|
|
case OPT_GAMMA_VECTOR_G:
|
|
case OPT_GAMMA_VECTOR_B:
|
|
memcpy (sval->wa, value, sopt->size); /* Word arrays */
|
|
break;
|
|
|
|
case OPT_CCT_1:
|
|
case OPT_CCT_2:
|
|
case OPT_CCT_3:
|
|
case OPT_CCT_4:
|
|
case OPT_CCT_5:
|
|
case OPT_CCT_6:
|
|
case OPT_CCT_7:
|
|
case OPT_CCT_8:
|
|
case OPT_CCT_9:
|
|
sval->w = *((SANE_Word *) value); /* Simple values */
|
|
break;
|
|
|
|
case OPT_DROPOUT:
|
|
case OPT_FILM_TYPE:
|
|
case OPT_BAY:
|
|
case OPT_FOCUS:
|
|
sval->w = optindex; /* Simple lists */
|
|
break;
|
|
|
|
case OPT_EJECT:
|
|
/* return eject( s ); */
|
|
eject (s);
|
|
break;
|
|
|
|
case OPT_RESOLUTION:
|
|
sval->w = *((SANE_Word *) value);
|
|
reload = SANE_TRUE;
|
|
break;
|
|
|
|
case OPT_TL_X:
|
|
case OPT_TL_Y:
|
|
case OPT_BR_X:
|
|
case OPT_BR_Y:
|
|
sval->w = *((SANE_Word *) value);
|
|
DBG (1, "set = %f\n", SANE_UNFIX (sval->w));
|
|
if (NULL != info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
break;
|
|
|
|
case OPT_SOURCE:
|
|
handle_source (s, optindex, (char *) value);
|
|
reload = SANE_TRUE;
|
|
break;
|
|
|
|
case OPT_MODE:
|
|
{
|
|
SANE_Bool isColor = mode_params[optindex].color;
|
|
SANE_Bool userDefined =
|
|
color_userdefined[s->val[OPT_COLOR_CORRECTION].w];
|
|
|
|
sval->w = optindex;
|
|
|
|
if (s->hw->cmd->set_halftoning != 0)
|
|
{
|
|
setOptionState (s, mode_params[optindex].depth == 1,
|
|
OPT_HALFTONE, &reload);
|
|
}
|
|
|
|
setOptionState (s, !isColor, OPT_DROPOUT, &reload);
|
|
if (s->hw->cmd->set_color_correction)
|
|
{
|
|
setOptionState (s, isColor, OPT_COLOR_CORRECTION, &reload);
|
|
}
|
|
if (s->hw->cmd->set_color_correction_coefficients)
|
|
{
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_1, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_2, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_3, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_4, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_5, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_6, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_7, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_8, &reload);
|
|
setOptionState (s, isColor && userDefined, OPT_CCT_9, &reload);
|
|
}
|
|
|
|
/* if binary, then disable the bit depth selection */
|
|
if (optindex == 0)
|
|
{
|
|
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
else
|
|
{
|
|
if (bitDepthList[0] == 1)
|
|
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
|
|
else
|
|
{
|
|
s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
|
|
s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth;
|
|
}
|
|
}
|
|
|
|
handle_depth_halftone (s, &reload);
|
|
reload = SANE_TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
case OPT_ADF_MODE:
|
|
sval->w = optindex;
|
|
break;
|
|
|
|
case OPT_BIT_DEPTH:
|
|
sval->w = *((SANE_Word *) value);
|
|
mode_params[s->val[OPT_MODE].w].depth = sval->w;
|
|
reload = SANE_TRUE;
|
|
break;
|
|
|
|
case OPT_HALFTONE:
|
|
sval->w = optindex;
|
|
handle_depth_halftone (s, &reload);
|
|
break;
|
|
|
|
case OPT_COLOR_CORRECTION:
|
|
{
|
|
SANE_Bool f = color_userdefined[optindex];
|
|
|
|
sval->w = optindex;
|
|
setOptionState (s, f, OPT_CCT_1, &reload);
|
|
setOptionState (s, f, OPT_CCT_2, &reload);
|
|
setOptionState (s, f, OPT_CCT_3, &reload);
|
|
setOptionState (s, f, OPT_CCT_4, &reload);
|
|
setOptionState (s, f, OPT_CCT_5, &reload);
|
|
setOptionState (s, f, OPT_CCT_6, &reload);
|
|
setOptionState (s, f, OPT_CCT_7, &reload);
|
|
setOptionState (s, f, OPT_CCT_8, &reload);
|
|
setOptionState (s, f, OPT_CCT_9, &reload);
|
|
|
|
break;
|
|
}
|
|
|
|
case OPT_GAMMA_CORRECTION:
|
|
{
|
|
SANE_Bool f = gamma_userdefined[optindex];
|
|
|
|
sval->w = optindex;
|
|
/* setOptionState(s, f, OPT_GAMMA_VECTOR, &reload ); */
|
|
setOptionState (s, f, OPT_GAMMA_VECTOR_R, &reload);
|
|
setOptionState (s, f, OPT_GAMMA_VECTOR_G, &reload);
|
|
setOptionState (s, f, OPT_GAMMA_VECTOR_B, &reload);
|
|
setOptionState (s, !f, OPT_BRIGHTNESS, &reload); /* Note... */
|
|
|
|
break;
|
|
}
|
|
|
|
case OPT_MIRROR:
|
|
case OPT_SPEED:
|
|
case OPT_PREVIEW_SPEED:
|
|
case OPT_AAS:
|
|
case OPT_PREVIEW: /* needed? */
|
|
case OPT_BRIGHTNESS:
|
|
case OPT_SHARPNESS:
|
|
case OPT_AUTO_EJECT:
|
|
case OPT_THRESHOLD:
|
|
case OPT_ZOOM:
|
|
case OPT_WAIT_FOR_BUTTON:
|
|
sval->w = *((SANE_Word *) value);
|
|
break;
|
|
|
|
case OPT_LIMIT_RESOLUTION:
|
|
sval->w = *((SANE_Word *) value);
|
|
filter_resolution_list (s);
|
|
reload = SANE_TRUE;
|
|
break;
|
|
|
|
case OPT_QUICK_FORMAT:
|
|
sval->w = optindex;
|
|
|
|
s->val[OPT_TL_X].w = qf_params[sval->w].tl_x;
|
|
s->val[OPT_TL_Y].w = qf_params[sval->w].tl_y;
|
|
s->val[OPT_BR_X].w = qf_params[sval->w].br_x;
|
|
s->val[OPT_BR_Y].w = qf_params[sval->w].br_y;
|
|
|
|
if (s->val[OPT_TL_X].w < s->hw->x_range->min)
|
|
s->val[OPT_TL_X].w = s->hw->x_range->min;
|
|
|
|
if (s->val[OPT_TL_Y].w < s->hw->y_range->min)
|
|
s->val[OPT_TL_Y].w = s->hw->y_range->min;
|
|
|
|
if (s->val[OPT_BR_X].w > s->hw->x_range->max)
|
|
s->val[OPT_BR_X].w = s->hw->x_range->max;
|
|
|
|
if (s->val[OPT_BR_Y].w > s->hw->y_range->max)
|
|
s->val[OPT_BR_Y].w = s->hw->y_range->max;
|
|
|
|
reload = SANE_TRUE;
|
|
break;
|
|
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (reload && info != NULL)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**
|
|
End of setvalue.
|
|
**/
|
|
|
|
SANE_Status
|
|
sane_control_option (SANE_Handle handle,
|
|
SANE_Int option,
|
|
SANE_Action action, void *value, SANE_Int * info)
|
|
{
|
|
if (option < 0 || option >= NUM_OPTIONS)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
if (info != NULL)
|
|
*info = 0;
|
|
|
|
switch (action)
|
|
{
|
|
case SANE_ACTION_GET_VALUE:
|
|
return (getvalue (handle, option, value));
|
|
|
|
case SANE_ACTION_SET_VALUE:
|
|
return (setvalue (handle, option, value, info));
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
* sane_get_parameters()
|
|
*
|
|
* This function is part of the SANE API and gets called when the front end
|
|
* requests information aobut the scan configuration (e.g. color depth, mode,
|
|
* bytes and pixels per line, number of lines. This information is returned
|
|
* in the SANE_Parameters structure.
|
|
*
|
|
* Once a scan was started, this routine has to report the correct values, if
|
|
* it is called before the scan is actually started, the values are based on
|
|
* the current settings.
|
|
*
|
|
*/
|
|
SANE_Status
|
|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
int ndpi, max_x, max_y;
|
|
int bytes_per_pixel;
|
|
|
|
DBG (5, "sane_get_parameters()\n");
|
|
|
|
/*
|
|
* If sane_start was already called, then just retrieve the parameters
|
|
* from the scanner data structure
|
|
*/
|
|
|
|
if (!s->eof && s->ptr != NULL)
|
|
{
|
|
DBG (5, "Returning saved params structure\n");
|
|
if (params != NULL)
|
|
{
|
|
DBG(1, "Restoring parameters from saved parameters\n");
|
|
*params = s->params;
|
|
}
|
|
|
|
DBG (3, "Preview = %d\n", s->val[OPT_PREVIEW].w);
|
|
DBG (3, "Resolution = %d\n", s->val[OPT_RESOLUTION].w);
|
|
|
|
DBG (1, "get para %p %p tlx %f tly %f brx %f bry %f [mm]\n", (void *) s,
|
|
(void *) s->val, SANE_UNFIX (s->val[OPT_TL_X].w),
|
|
SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w),
|
|
SANE_UNFIX (s->val[OPT_BR_Y].w));
|
|
|
|
print_params (s->params);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* otherwise initialize the params structure and gather the data */
|
|
|
|
memset (&s->params, 0, sizeof (SANE_Parameters));
|
|
|
|
ndpi = s->val[OPT_RESOLUTION].w;
|
|
|
|
max_x = max_y = 0;
|
|
|
|
s->params.pixels_per_line =
|
|
SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) / 25.4 * ndpi + 0.5;
|
|
s->params.lines =
|
|
SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) / 25.4 * ndpi + 0.5;
|
|
|
|
/*
|
|
* Make sure that the number of lines is correct for color shuffling:
|
|
* The shuffling alghorithm produces 2xline_distance lines at the
|
|
* beginning and the same amount at the end of the scan that are not
|
|
* useable. If s->params.lines gets negative, 0 lines are reported
|
|
* back to the frontend.
|
|
*/
|
|
if (s->hw->color_shuffle)
|
|
{
|
|
s->params.lines -= 4 * s->line_distance;
|
|
if (s->params.lines < 0)
|
|
{
|
|
s->params.lines = 0;
|
|
}
|
|
DBG (1, "Adjusted params.lines for color_shuffle by %d to %d\n",
|
|
4 * s->line_distance, s->params.lines);
|
|
}
|
|
|
|
DBG (3, "Preview = %d\n", s->val[OPT_PREVIEW].w);
|
|
DBG (3, "Resolution = %d\n", s->val[OPT_RESOLUTION].w);
|
|
|
|
DBG (1, "get para %p %p tlx %f tly %f brx %f bry %f [mm]\n", (void *) s,
|
|
(void *) s->val, SANE_UNFIX (s->val[OPT_TL_X].w),
|
|
SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w),
|
|
SANE_UNFIX (s->val[OPT_BR_Y].w));
|
|
|
|
|
|
/*
|
|
* Calculate bytes_per_pixel and bytes_per_line for
|
|
* any color depths.
|
|
*
|
|
* The default color depth is stored in mode_params.depth:
|
|
*/
|
|
|
|
if (mode_params[s->val[OPT_MODE].w].depth == 1)
|
|
{
|
|
s->params.depth = 1;
|
|
}
|
|
else
|
|
{
|
|
s->params.depth = s->val[OPT_BIT_DEPTH].w;
|
|
}
|
|
|
|
if (s->params.depth > 8)
|
|
{
|
|
s->params.depth = 16; /*
|
|
* The frontends can only handle 8 or 16 bits
|
|
* for gray or color - so if it's more than 8,
|
|
* it gets automatically set to 16. This works
|
|
* as long as EPSON does not come out with a
|
|
* scanner that can handle more than 16 bits
|
|
* per color channel.
|
|
*/
|
|
|
|
}
|
|
|
|
bytes_per_pixel = s->params.depth / 8; /* this works because it can only be set to 1, 8 or 16 */
|
|
if (s->params.depth % 8) /* just in case ... */
|
|
{
|
|
bytes_per_pixel++;
|
|
}
|
|
|
|
/* pixels_per_line is rounded to the next 8bit boundary */
|
|
s->params.pixels_per_line = s->params.pixels_per_line & ~7;
|
|
|
|
s->params.last_frame = SANE_TRUE;
|
|
|
|
if (mode_params[s->val[OPT_MODE].w].color)
|
|
{
|
|
s->params.format = SANE_FRAME_RGB;
|
|
s->params.bytes_per_line =
|
|
3 * s->params.pixels_per_line * bytes_per_pixel;
|
|
}
|
|
else
|
|
{
|
|
s->params.format = SANE_FRAME_GRAY;
|
|
s->params.bytes_per_line =
|
|
s->params.pixels_per_line * s->params.depth / 8;
|
|
}
|
|
|
|
if (NULL != params)
|
|
*params = s->params;
|
|
|
|
print_params (s->params);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
* sane_start()
|
|
*
|
|
* This function is part of the SANE API and gets called from the front end to
|
|
* start the scan process.
|
|
*
|
|
*/
|
|
|
|
SANE_Status
|
|
sane_start (SANE_Handle handle)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Status status;
|
|
SANE_Bool button_status;
|
|
const struct mode_param *mparam;
|
|
u_char params[4];
|
|
int ndpi;
|
|
int left, top;
|
|
int lcount;
|
|
int i, j; /* loop counter */
|
|
|
|
DBG (5, "sane_start()\n");
|
|
|
|
open_scanner (s);
|
|
|
|
/*
|
|
* There is some undocumented special behavior with the TPU enable/disable.
|
|
* TPU power ESC e status
|
|
* on 0 NAK
|
|
* on 1 ACK
|
|
* off 0 ACK
|
|
* off 1 NAK
|
|
*
|
|
* It makes no sense to scan with TPU powered on and source flatbed, because
|
|
* light will come from both sides.
|
|
*/
|
|
|
|
if (s->hw->extension)
|
|
{
|
|
int max_x, max_y;
|
|
|
|
int extensionCtrl;
|
|
extensionCtrl = (s->hw->use_extension ? 1 : 0);
|
|
if (s->hw->use_extension && (s->val[OPT_ADF_MODE].w == 1))
|
|
extensionCtrl = 2;
|
|
|
|
status = control_extension (s, extensionCtrl);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "You may have to power %s your TPU\n",
|
|
s->hw->use_extension ? "on" : "off");
|
|
|
|
DBG (1, "Also you may have to restart the Sane frontend.\n");
|
|
close_scanner (s);
|
|
return status;
|
|
}
|
|
|
|
if (s->hw->cmd->request_extended_status != 0)
|
|
{
|
|
status = check_ext_status (s, &max_x, &max_y);
|
|
|
|
if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
|
|
{
|
|
close_scanner (s);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (s->hw->ADF && s->hw->use_extension && s->hw->cmd->feed)
|
|
{
|
|
status = feed (s);
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
close_scanner (s);
|
|
return status;
|
|
}
|
|
|
|
check_ext_status (s, &max_x, &max_y);
|
|
s->hw->adf_max_x = max_x;
|
|
s->hw->adf_max_y = max_y;
|
|
}
|
|
|
|
|
|
/*
|
|
* set the focus position according to the extension used:
|
|
* if the TPU is selected, then focus 2.5mm above the glass,
|
|
* otherwise focus on the glass. Scanners that don't support
|
|
* this feature, will just ignore these calls.
|
|
*/
|
|
|
|
if (s->hw->focusSupport == SANE_TRUE)
|
|
{
|
|
if (s->val[OPT_FOCUS].w == 0)
|
|
{
|
|
DBG (1, "Setting focus to glass surface\n");
|
|
set_focus_position (s, 0x40);
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "Setting focus to 2.5mm above glass\n");
|
|
set_focus_position (s, 0x59);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* use the flatbed size for the max. scansize for the GT-30000
|
|
and similar scanners if the ADF is not enabled */
|
|
if (s->hw->devtype == 3 && s->hw->use_extension == 0)
|
|
{
|
|
int max_x, max_y;
|
|
|
|
status = check_ext_status (s, &max_x, &max_y);
|
|
if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status)
|
|
{
|
|
close_scanner (s);
|
|
return status;
|
|
}
|
|
|
|
s->hw->fbf_max_x = max_x;
|
|
s->hw->fbf_max_y = max_y;
|
|
}
|
|
|
|
|
|
mparam = mode_params + s->val[OPT_MODE].w;
|
|
DBG (1, "sane_start: Setting data format to %d bits\n", mparam->depth);
|
|
status = set_data_format (s, mparam->depth);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_data_format failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* The byte sequence mode was introduced in B5, for B[34] we need line sequence mode
|
|
*/
|
|
|
|
if ((s->hw->cmd->level[0] == 'D' ||
|
|
(s->hw->cmd->level[0] == 'B' && s->hw->level >= 5)) &&
|
|
mparam->mode_flags == 0x02)
|
|
{
|
|
status = set_color_mode (s, 0x13);
|
|
}
|
|
else
|
|
{
|
|
status = set_color_mode (s, mparam->mode_flags | (mparam->dropout_mask
|
|
& dropout_params[s->
|
|
val
|
|
[OPT_DROPOUT].
|
|
w]));
|
|
}
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_color_mode failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
if (s->hw->cmd->set_halftoning &&
|
|
SANE_OPTION_IS_ACTIVE (s->opt[OPT_HALFTONE].cap))
|
|
{
|
|
status = set_halftoning (s, halftone_params[s->val[OPT_HALFTONE].w]);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_halftoning failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BRIGHTNESS].cap))
|
|
{
|
|
status = set_bright (s, s->val[OPT_BRIGHTNESS].w);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_bright failed: %s\n", sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_MIRROR].cap))
|
|
{
|
|
status = mirror_image (s, mirror_params[s->val[OPT_MIRROR].w]);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: mirror_image failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
}
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SPEED].cap))
|
|
{
|
|
|
|
if (s->val[OPT_PREVIEW].w)
|
|
status = set_speed (s, speed_params[s->val[OPT_PREVIEW_SPEED].w]);
|
|
else
|
|
status = set_speed (s, speed_params[s->val[OPT_SPEED].w]);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_speed failed: %s\n", sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* use of speed_params is ok here since they are false and true.
|
|
* NOTE: I think I should throw that "params" stuff as long w is already the value.
|
|
*/
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_AAS].cap))
|
|
{
|
|
status = control_auto_area_segmentation (s,
|
|
speed_params[s->val[OPT_AAS].w]);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: control_auto_area_segmentation failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
s->invert_image = SANE_FALSE; /* default: to not inverting the image */
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_FILM_TYPE].cap))
|
|
{
|
|
s->invert_image = (s->val[OPT_FILM_TYPE].w == FILM_TYPE_NEGATIVE);
|
|
status = set_film_type (s, film_params[s->val[OPT_FILM_TYPE].w]);
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_film_type failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BAY].cap))
|
|
{
|
|
status = set_bay (s, s->val[OPT_BAY].w);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_bay: %s\n", sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SHARPNESS].cap))
|
|
{
|
|
|
|
status = set_outline_emphasis (s, s->val[OPT_SHARPNESS].w);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_outline_emphasis failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (s->hw->cmd->set_gamma &&
|
|
SANE_OPTION_IS_ACTIVE (s->opt[OPT_GAMMA_CORRECTION].cap))
|
|
{
|
|
int val;
|
|
if (s->hw->cmd->level[0] == 'D')
|
|
{
|
|
/*
|
|
* The D1 level has only the two user defined gamma
|
|
* settings.
|
|
*/
|
|
val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
|
|
}
|
|
else
|
|
{
|
|
val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w];
|
|
|
|
/*
|
|
* If "Default" is selected then determine the actual value
|
|
* to send to the scanner: If bilevel mode, just send the
|
|
* value from the table (0x01), for grayscale or color mode
|
|
* add one and send 0x02.
|
|
*/
|
|
/* if( s->val[ OPT_GAMMA_CORRECTION].w <= 1) { */
|
|
if (s->val[OPT_GAMMA_CORRECTION].w == 0)
|
|
{
|
|
val += mparam->depth == 1 ? 0 : 1;
|
|
}
|
|
}
|
|
|
|
DBG (1, "sane_start: set_gamma( s, 0x%x ).\n", val);
|
|
status = set_gamma (s, val);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_gamma failed: %s\n", sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (s->hw->cmd->set_gamma_table &&
|
|
gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w])
|
|
{ /* user defined. */
|
|
status = set_gamma_table (s);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_gamma_table failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* TODO: think about if SANE_OPTION_IS_ACTIVE is a good criteria to send commands.
|
|
*/
|
|
|
|
if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_COLOR_CORRECTION].cap))
|
|
{
|
|
int val = color_params[s->val[OPT_COLOR_CORRECTION].w];
|
|
|
|
DBG (1, "sane_start: set_color_correction( s, 0x%x )\n", val);
|
|
status = set_color_correction (s, val);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_color_correction failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
if (1 == s->val[OPT_COLOR_CORRECTION].w)
|
|
{ /* user defined. */
|
|
status = set_color_correction_coefficients (s);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_color_correction_coefficients failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
if (s->hw->cmd->set_threshold != 0
|
|
&& SANE_OPTION_IS_ACTIVE (s->opt[OPT_THRESHOLD].cap))
|
|
{
|
|
status = set_threshold (s, s->val[OPT_THRESHOLD].w);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_threshold(%d) failed: %s\n",
|
|
s->val[OPT_THRESHOLD].w, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
ndpi = s->val[OPT_RESOLUTION].w;
|
|
|
|
status = set_resolution (s, ndpi, ndpi);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_resolution(%d, %d) failed: %s\n",
|
|
ndpi, ndpi, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = sane_get_parameters (handle, NULL);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
/* set the zoom */
|
|
if (s->hw->cmd->set_zoom != 0
|
|
&& SANE_OPTION_IS_ACTIVE (s->opt[OPT_ZOOM].cap))
|
|
{
|
|
status = set_zoom (s, s->val[OPT_ZOOM].w, s->val[OPT_ZOOM].w);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "sane_start: set_zoom(%d) failed: %s\n",
|
|
s->val[OPT_ZOOM].w, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Now s->params is initialized.
|
|
*/
|
|
|
|
|
|
/*
|
|
* If WAIT_FOR_BUTTON is active, then do just that: Wait until the button is
|
|
* pressed. If the button was already pressed, then we will get the button
|
|
* Pressed event right away.
|
|
*/
|
|
|
|
if (s->val[OPT_WAIT_FOR_BUTTON].w == SANE_TRUE)
|
|
{
|
|
s->hw->wait_for_button = SANE_TRUE;
|
|
|
|
while (s->hw->wait_for_button == SANE_TRUE)
|
|
{
|
|
if (s->canceling == SANE_TRUE)
|
|
{
|
|
s->hw->wait_for_button = SANE_FALSE;
|
|
}
|
|
/* get the button status from the scanner */
|
|
else if (request_push_button_status (s, &button_status) ==
|
|
SANE_STATUS_GOOD)
|
|
{
|
|
if (button_status == SANE_TRUE)
|
|
{
|
|
s->hw->wait_for_button = SANE_FALSE;
|
|
}
|
|
else
|
|
{
|
|
sleep (1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* we run into an eror condition, just continue */
|
|
s->hw->wait_for_button = SANE_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* in file:frontend/preview.c
|
|
*
|
|
* The preview strategy is as follows:
|
|
*
|
|
* 1) A preview always acquires an image that covers the entire
|
|
* scan surface. This is necessary so the user can see not
|
|
* only what is, but also what isn't selected.
|
|
*/
|
|
|
|
left = SANE_UNFIX (s->val[OPT_TL_X].w) / 25.4 * ndpi + 0.5;
|
|
top = SANE_UNFIX (s->val[OPT_TL_Y].w) / 25.4 * ndpi + 0.5;
|
|
|
|
/*
|
|
* Calculate correction for line_distance in D1 scanner:
|
|
* Start line_distance lines earlier and add line_distance lines at the end
|
|
*
|
|
* Because the actual line_distance is not yet calculated we have to do this
|
|
* first.
|
|
*/
|
|
|
|
s->hw->color_shuffle = SANE_FALSE;
|
|
s->current_output_line = 0;
|
|
s->lines_written = 0;
|
|
s->color_shuffle_line = 0;
|
|
|
|
if ((s->hw->optical_res != 0) && (mparam->depth == 8)
|
|
&& (mparam->mode_flags != 0))
|
|
{
|
|
s->line_distance = s->hw->max_line_distance * ndpi / s->hw->optical_res;
|
|
if (s->line_distance != 0)
|
|
{
|
|
s->hw->color_shuffle = SANE_TRUE;
|
|
}
|
|
else
|
|
s->hw->color_shuffle = SANE_FALSE;
|
|
}
|
|
|
|
/*
|
|
* for debugging purposes:
|
|
*/
|
|
#ifdef FORCE_COLOR_SHUFFLE
|
|
DBG (1, "Test mode: FORCE_COLOR_SHUFFLE = TRUE\n");
|
|
s->hw->color_shuffle = SANE_TRUE;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Modify the scan area: If the scanner requires color shuffling, then we try to
|
|
* scan more lines to compensate for the lines that will be removed from the scan
|
|
* due to the color shuffling alghorithm.
|
|
* At this time we add two times the line distance to the number of scan lines if
|
|
* this is possible - if not, then we try to calculate the number of additional
|
|
* lines according to the selected scan area.
|
|
*/
|
|
if (s->hw->color_shuffle == SANE_TRUE)
|
|
{
|
|
|
|
/* start the scan 2*line_distance earlier */
|
|
top -= 2 * s->line_distance;
|
|
if (top < 0)
|
|
{
|
|
top = 0;
|
|
}
|
|
|
|
/* scan 4*line_distance lines more */
|
|
s->params.lines += 4 * s->line_distance;
|
|
}
|
|
|
|
/*
|
|
* If (top + s->params.lines) is larger than the max scan area, reset
|
|
* the number of scan lines:
|
|
*/
|
|
if (SANE_UNFIX (s->val[OPT_BR_Y].w) / 25.4 * ndpi < (s->params.lines + top))
|
|
{
|
|
s->params.lines = ((int) SANE_UNFIX (s->val[OPT_BR_Y].w) /
|
|
25.4 * ndpi + 0.5) - top;
|
|
}
|
|
|
|
|
|
status =
|
|
set_scan_area (s, left, top, s->params.pixels_per_line, s->params.lines);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_scan_area failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
s->block = SANE_FALSE;
|
|
lcount = 1;
|
|
|
|
/*
|
|
* The set line count commands needs to be sent for certain scanners in
|
|
* color mode. The D1 level requires it, we are however only testing for
|
|
* 'D' and not for the actual numeric level.
|
|
*/
|
|
|
|
if (((s->hw->cmd->level[0] == 'B') &&
|
|
((s->hw->level >= 5) || ((s->hw->level >= 4) &&
|
|
(!mode_params[s->val[OPT_MODE].w].color))))
|
|
|| (s->hw->cmd->level[0] == 'D'))
|
|
{
|
|
s->block = SANE_TRUE;
|
|
lcount = sanei_scsi_max_request_size / s->params.bytes_per_line;
|
|
|
|
if (lcount >= 255)
|
|
{
|
|
lcount = 255;
|
|
}
|
|
|
|
if (s->hw->TPU && s->hw->use_extension && lcount > 32)
|
|
{
|
|
lcount = 32;
|
|
}
|
|
|
|
|
|
/*
|
|
* The D1 series of scanners only allow an even line number
|
|
* for bi-level scanning. If a bit depth of 1 is selected, then
|
|
* make sure the next lower even number is selected.
|
|
*/
|
|
if (s->hw->cmd->level[0] == 'D')
|
|
{
|
|
if (lcount % 2)
|
|
{
|
|
lcount -= 1;
|
|
}
|
|
}
|
|
|
|
if (lcount == 0)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
status = set_lcount (s, lcount);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: set_lcount(%d) failed: %s\n",
|
|
lcount, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (s->hw->cmd->request_extended_status != 0
|
|
&& SANE_TRUE == s->hw->extension)
|
|
{
|
|
u_char result[4];
|
|
u_char *buf;
|
|
size_t len;
|
|
|
|
params[0] = ESC;
|
|
params[1] = s->hw->cmd->request_extended_status;
|
|
|
|
send (s, params, 2, &status); /* send ESC f (request extended status) */
|
|
|
|
if (SANE_STATUS_GOOD == status)
|
|
{
|
|
len = 4; /* receive header */
|
|
|
|
receive (s, result, len, &status);
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = result[3] << 8 | result[2];
|
|
buf = alloca (len);
|
|
|
|
receive (s, buf, len, &status); /* receive actual status data */
|
|
|
|
if (buf[0] & 0x80)
|
|
{
|
|
close_scanner (s);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "Extended status flag request failed\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* for debug purpose
|
|
* check scanner conditions
|
|
*/
|
|
#if 1
|
|
if (s->hw->cmd->request_condition != 0)
|
|
{
|
|
u_char result[4];
|
|
u_char *buf;
|
|
size_t len;
|
|
|
|
params[0] = ESC;
|
|
params[1] = s->hw->cmd->request_condition;
|
|
|
|
send (s, params, 2, &status); /* send request condition */
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = 4;
|
|
receive (s, result, len, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = result[3] << 8 | result[2];
|
|
buf = alloca (len);
|
|
receive (s, buf, len, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
#if 0
|
|
DBG (10, "SANE_START: length=%d\n", len);
|
|
for (i = 1; i <= len; i++)
|
|
{
|
|
DBG (10, "SANE_START: %d: %c\n", i, buf[i - 1]);
|
|
}
|
|
#endif
|
|
|
|
DBG (5, "SANE_START: Color: %d\n", (int) buf[1]);
|
|
DBG (5, "SANE_START: Resolution (x, y): (%d, %d)\n",
|
|
(int) (buf[4] << 8 | buf[3]), (int) (buf[6] << 8 | buf[5]));
|
|
DBG (5,
|
|
"SANE_START: Scan area(pixels) (x0, y0), (x1, y1): (%d, %d), (%d, %d)\n",
|
|
(int) (buf[9] << 8 | buf[8]), (int) (buf[11] << 8 | buf[10]),
|
|
(int) (buf[13] << 8 | buf[12]), (int) (buf[15] << 8 | buf[14]));
|
|
DBG (5, "SANE_START: Data format: %d\n", (int) buf[17]);
|
|
DBG (5, "SANE_START: Halftone: %d\n", (int) buf[19]);
|
|
DBG (5, "SANE_START: Brightness: %d\n", (int) buf[21]);
|
|
DBG (5, "SANE_START: Gamma: %d\n", (int) buf[23]);
|
|
DBG (5, "SANE_START: Zoom (x, y): (%d, %d)\n", (int) buf[26],
|
|
(int) buf[25]);
|
|
DBG (5, "SANE_START: Color correction: %d\n", (int) buf[28]);
|
|
DBG (5, "SANE_START: Sharpness control: %d\n", (int) buf[30]);
|
|
DBG (5, "SANE_START: Scanning mode: %d\n", (int) buf[32]);
|
|
DBG (5, "SANE_START: Mirroring: %d\n", (int) buf[34]);
|
|
DBG (5, "SANE_START: Auto area segmentation: %d\n", (int) buf[36]);
|
|
DBG (5, "SANE_START: Threshold: %d\n", (int) buf[38]);
|
|
DBG (5, "SANE_START: Line counter: %d\n", (int) buf[40]);
|
|
DBG (5, "SANE_START: Option unit control: %d\n", (int) buf[42]);
|
|
DBG (5, "SANE_START: Film type: %d\n", (int) buf[44]);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* set the retry count to 0 */
|
|
s->retry_count = 0;
|
|
|
|
if (s->hw->color_shuffle == SANE_TRUE)
|
|
{
|
|
|
|
/* initialize the line buffers */
|
|
for (i = 0; i < s->line_distance * 2 + 1; i++)
|
|
{
|
|
if (s->line_buffer[i] != NULL)
|
|
free (s->line_buffer[i]);
|
|
|
|
s->line_buffer[i] = malloc (s->params.bytes_per_line);
|
|
if (s->line_buffer[i] == NULL)
|
|
{
|
|
/* free the memory we've malloced so far */
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
free (s->line_buffer[j]);
|
|
s->line_buffer[j] = NULL;
|
|
}
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
}
|
|
}
|
|
|
|
params[0] = ESC;
|
|
params[1] = s->hw->cmd->start_scanning;
|
|
|
|
send (s, params, 2, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "sane_start: start failed: %s\n", sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
s->eof = SANE_FALSE;
|
|
s->buf = realloc (s->buf, lcount * s->params.bytes_per_line);
|
|
s->ptr = s->end = s->buf;
|
|
s->canceling = SANE_FALSE;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
} /* sane_start */
|
|
|
|
/*
|
|
*
|
|
* TODO: clean up the eject and direct cmd mess.
|
|
*/
|
|
|
|
SANE_Status
|
|
sane_auto_eject (Epson_Scanner * s)
|
|
{
|
|
|
|
DBG (5, "sane_auto_eject()\n");
|
|
|
|
if (s->hw->ADF && s->hw->use_extension && s->val[OPT_AUTO_EJECT].w)
|
|
{ /* sequence! */
|
|
SANE_Status status;
|
|
|
|
u_char params[1];
|
|
u_char cmd = s->hw->cmd->eject;
|
|
|
|
if (!cmd)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
params[0] = cmd;
|
|
|
|
send (s, params, 1, &status);
|
|
|
|
if (SANE_STATUS_GOOD != (status = expect_ack (s)))
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static SANE_Status
|
|
read_data_block (Epson_Scanner * s, EpsonDataRec * result)
|
|
{
|
|
SANE_Status status;
|
|
u_char param[3];
|
|
|
|
receive (s, result, s->block ? 6 : 4, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
if (STX != result->code)
|
|
{
|
|
DBG (1, "code %02x\n", (int) result->code);
|
|
DBG (1, "error, expected STX\n");
|
|
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (result->status & STATUS_FER)
|
|
{
|
|
int dummy_x, dummy_y;
|
|
|
|
DBG (1, "fatal error - Status = %02x\n", result->status);
|
|
|
|
status = check_ext_status (s, &dummy_x, &dummy_y);
|
|
|
|
/*
|
|
* Hack Alert!!!
|
|
* If the status is SANE_STATUS_DEVICE_BUSY then we need to
|
|
* re-issue the command again. We can assume that the command that
|
|
* caused this problem was ESC G, so in a loop with a sleep 1 we
|
|
* are testing this over and over and over again, until the lamp
|
|
* "thinks" it is ready.
|
|
*
|
|
* TODO: Store the last command and execute what was actually used
|
|
* as the last command. For all situations this error may occur
|
|
* ESC G is very very likely to be the command in question, but
|
|
* we better make sure that this is the case.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* let's safe some stack space: If this is not the first go around,
|
|
* then just return the status and let the loop handle this - otherwise
|
|
* we would run this function recursively.
|
|
*/
|
|
|
|
if ((status == SANE_STATUS_DEVICE_BUSY && s->retry_count > 0) ||
|
|
(status == SANE_STATUS_GOOD && s->retry_count > 0))
|
|
{
|
|
return SANE_STATUS_DEVICE_BUSY; /* return busy even if we just read OK
|
|
so that the following loop can end
|
|
gracefully */
|
|
}
|
|
|
|
while (status == SANE_STATUS_DEVICE_BUSY)
|
|
{
|
|
if (s->retry_count > SANE_EPSON_MAX_RETRIES)
|
|
{
|
|
DBG (1, "Max retry count exceeded (%d)\n", s->retry_count);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
sleep (1); /* wait one second for the next attempt */
|
|
|
|
DBG (1, "retrying ESC G - %d\n", ++(s->retry_count));
|
|
|
|
param[0] = ESC;
|
|
param[1] = s->hw->cmd->start_scanning;
|
|
|
|
send (s, param, 2, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "read_data_block: start failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = read_data_block (s, result);
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
void
|
|
scan_finish (Epson_Scanner * s)
|
|
{
|
|
SANE_Status status;
|
|
int i, x, y;
|
|
|
|
DBG (5, "scan_finish()\n");
|
|
|
|
free (s->buf);
|
|
s->buf = NULL;
|
|
|
|
status = check_ext_status (s, &x, &y);
|
|
|
|
if (SANE_STATUS_NO_DOCS == status && s->hw->ADF && s->hw->use_extension)
|
|
sane_auto_eject (s);
|
|
|
|
for (i = 0; i < s->line_distance; i++)
|
|
{
|
|
if (s->line_buffer[i] != NULL)
|
|
{
|
|
free (s->line_buffer[i]);
|
|
s->line_buffer[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define GET_COLOR(x) ((x.status>>2) & 0x03)
|
|
|
|
SANE_Status
|
|
sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length,
|
|
SANE_Int * length)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Status status;
|
|
int index = 0;
|
|
SANE_Bool reorder = SANE_FALSE;
|
|
SANE_Bool needStrangeReorder = SANE_FALSE;
|
|
int bytes_to_process = 0;
|
|
|
|
START_READ:
|
|
DBG (5, "sane_read: begin\n");
|
|
|
|
if (s->ptr == s->end)
|
|
{
|
|
EpsonDataRec result;
|
|
size_t buf_len;
|
|
|
|
if ((s->fd != -1) && s->eof)
|
|
{
|
|
if (s->hw->color_shuffle)
|
|
{
|
|
DBG (1, "Written %d lines after color shuffle\n", s->lines_written);
|
|
DBG (1, "Lines requested: %d\n", s->params.lines);
|
|
}
|
|
|
|
*length = 0;
|
|
scan_finish (s);
|
|
|
|
return SANE_STATUS_EOF;
|
|
}
|
|
|
|
|
|
DBG (5, "sane_read: begin scan1\n");
|
|
|
|
if (SANE_STATUS_GOOD != (status = read_data_block (s, &result)))
|
|
{
|
|
*length = 0;
|
|
scan_finish (s);
|
|
return status;
|
|
}
|
|
|
|
buf_len = result.buf[1] << 8 | result.buf[0];
|
|
|
|
DBG (5, "sane_read: buf len = %lu\n", (u_long) buf_len);
|
|
|
|
if (s->block)
|
|
{
|
|
buf_len *= (result.buf[3] << 8 | result.buf[2]);
|
|
DBG (5, "sane_read: buf len (adjusted) = %lu\n", (u_long) buf_len);
|
|
}
|
|
|
|
if (!s->block && SANE_FRAME_RGB == s->params.format)
|
|
{
|
|
/*
|
|
* Read color data in line mode
|
|
*/
|
|
|
|
|
|
/*
|
|
* read the first color line - the number of bytes to read
|
|
* is already known (from last call to read_data_block()
|
|
* We determine where to write the line from the color information
|
|
* in the data block. At the end we want the order RGB, but the
|
|
* way the data is delivered does not guarantee this - actually it's
|
|
* most likely that the order is GRB if it's not RGB!
|
|
*/
|
|
switch (GET_COLOR (result))
|
|
{
|
|
case 1:
|
|
index = 1;
|
|
break;
|
|
case 2:
|
|
index = 0;
|
|
break;
|
|
case 3:
|
|
index = 2;
|
|
break;
|
|
}
|
|
|
|
receive (s, s->buf + index * s->params.pixels_per_line, buf_len,
|
|
&status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
/*
|
|
* send the ACK signal to the scanner in order to make
|
|
* it ready for the next data block.
|
|
*/
|
|
send (s, S_ACK, 1, &status);
|
|
|
|
/*
|
|
* ... and request the next data block
|
|
*/
|
|
if (SANE_STATUS_GOOD != (status = read_data_block (s, &result)))
|
|
return status;
|
|
|
|
buf_len = result.buf[1] << 8 | result.buf[0];
|
|
/*
|
|
* this should never happen, because we are already in
|
|
* line mode, but it does not hurt to check ...
|
|
*/
|
|
if (s->block)
|
|
buf_len *= (result.buf[3] << 8 | result.buf[2]);
|
|
|
|
DBG (5, "sane_read: buf len2 = %lu\n", (u_long) buf_len);
|
|
|
|
switch (GET_COLOR (result))
|
|
{
|
|
case 1:
|
|
index = 1;
|
|
break;
|
|
case 2:
|
|
index = 0;
|
|
break;
|
|
case 3:
|
|
index = 2;
|
|
break;
|
|
}
|
|
|
|
receive (s, s->buf + index * s->params.pixels_per_line, buf_len,
|
|
&status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
scan_finish (s);
|
|
*length = 0;
|
|
return status;
|
|
}
|
|
|
|
send (s, S_ACK, 1, &status);
|
|
|
|
/*
|
|
* ... and the last data block
|
|
*/
|
|
if (SANE_STATUS_GOOD != (status = read_data_block (s, &result)))
|
|
{
|
|
*length = 0;
|
|
scan_finish (s);
|
|
return status;
|
|
}
|
|
|
|
buf_len = result.buf[1] << 8 | result.buf[0];
|
|
|
|
if (s->block)
|
|
buf_len *= (result.buf[3] << 8 | result.buf[2]);
|
|
|
|
DBG (5, "sane_read: buf len3 = %lu\n", (u_long) buf_len);
|
|
|
|
switch (GET_COLOR (result))
|
|
{
|
|
case 1:
|
|
index = 1;
|
|
break;
|
|
case 2:
|
|
index = 0;
|
|
break;
|
|
case 3:
|
|
index = 2;
|
|
break;
|
|
}
|
|
|
|
receive (s, s->buf + index * s->params.pixels_per_line, buf_len,
|
|
&status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
*length = 0;
|
|
scan_finish (s);
|
|
return status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Read data in block mode
|
|
*/
|
|
|
|
/* do we have to reorder the data ? */
|
|
if (GET_COLOR (result) == 0x01)
|
|
{
|
|
reorder = SANE_TRUE;
|
|
}
|
|
|
|
bytes_to_process = receive (s, s->buf, buf_len, &status);
|
|
|
|
/* bytes_to_process = buf_len; */
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
*length = 0;
|
|
scan_finish (s);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (result.status & STATUS_AREA_END)
|
|
{
|
|
s->eof = SANE_TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (s->canceling)
|
|
{
|
|
send (s, S_CAN, 1, &status);
|
|
expect_ack (s);
|
|
|
|
*length = 0;
|
|
|
|
scan_finish (s);
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
}
|
|
else
|
|
send (s, S_ACK, 1, &status);
|
|
}
|
|
|
|
s->end = s->buf + buf_len;
|
|
s->ptr = s->buf;
|
|
|
|
/*
|
|
* if we have to re-order the color components (GRB->RGB) we
|
|
* are doing this here:
|
|
*/
|
|
|
|
/*
|
|
* Some scaners (e.g. the Perfection 1640 and GT-2200) seem
|
|
* to have the R and G channels swapped.
|
|
* The GT-8700 is the Asian version of the Perfection1640.
|
|
* If the scanner name is one of these, and the scan mode is
|
|
* RGB then swap the colors.
|
|
*/
|
|
|
|
needStrangeReorder =
|
|
(strstr (s->hw->sane.model, "GT-2200") ||
|
|
((strstr (s->hw->sane.model, "1640") &&
|
|
strstr (s->hw->sane.model, "Perfection")) ||
|
|
strstr (s->hw->sane.model, "GT-8700"))) &&
|
|
s->params.format == SANE_FRAME_RGB;
|
|
|
|
/*
|
|
* Certain Perfection 1650 also need this re-ordering of the two
|
|
* color channels. These scanners are identified by the problem
|
|
* with the half vertical scanning area. When we corrected this,
|
|
* we also set the variable s->hw->need_color_reorder
|
|
*/
|
|
if (s->hw->need_color_reorder)
|
|
{
|
|
needStrangeReorder = SANE_TRUE;
|
|
}
|
|
|
|
if (needStrangeReorder)
|
|
reorder = SANE_FALSE; /* reordering once is enough */
|
|
|
|
if (s->params.format != SANE_FRAME_RGB)
|
|
reorder = SANE_FALSE; /* don't reorder for BW or gray */
|
|
|
|
if (reorder)
|
|
{
|
|
SANE_Byte *ptr;
|
|
|
|
ptr = s->buf;
|
|
while (ptr < s->end)
|
|
{
|
|
if (s->params.depth > 8)
|
|
{
|
|
SANE_Byte tmp;
|
|
|
|
/* R->G G->R */
|
|
tmp = ptr[0];
|
|
ptr[0] = ptr[2]; /* first Byte G */
|
|
ptr[2] = tmp; /* first Byte R */
|
|
|
|
tmp = ptr[1];
|
|
ptr[1] = ptr[3]; /* second Byte G */
|
|
ptr[3] = tmp; /* second Byte R */
|
|
|
|
ptr += 6; /* go to next pixel */
|
|
}
|
|
else
|
|
{
|
|
/* R->G G->R */
|
|
SANE_Byte tmp;
|
|
|
|
tmp = ptr[0];
|
|
ptr[0] = ptr[1]; /* G */
|
|
ptr[1] = tmp; /* R */
|
|
/* B stays the same */
|
|
ptr += 3; /* go to next pixel */
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do the color_shuffle if everything else is correct - at this time
|
|
* most of the stuff is hardcoded for the Perfection 610
|
|
*/
|
|
|
|
if (s->hw->color_shuffle)
|
|
{
|
|
int new_length = 0;
|
|
|
|
status = color_shuffle (s, &new_length);
|
|
|
|
/*
|
|
* If no bytes are returned, check if the scanner is already done, if so,
|
|
* we'll probably just return, but if there is more data to process get
|
|
* the next batch.
|
|
*/
|
|
|
|
if (new_length == 0 && s->end != s->ptr)
|
|
{
|
|
goto START_READ;
|
|
}
|
|
|
|
s->end = s->buf + new_length;
|
|
s->ptr = s->buf;
|
|
|
|
}
|
|
|
|
|
|
DBG (5, "sane_read: begin scan2\n");
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* copy the image data to the data memory area
|
|
*/
|
|
|
|
if (!s->block && SANE_FRAME_RGB == s->params.format)
|
|
{
|
|
|
|
max_length /= 3;
|
|
|
|
if (max_length > s->end - s->ptr)
|
|
max_length = s->end - s->ptr;
|
|
|
|
*length = 3 * max_length;
|
|
|
|
if (s->invert_image == SANE_TRUE)
|
|
{
|
|
while (max_length-- != 0)
|
|
{
|
|
/* invert the three values */
|
|
*data++ = (u_char) ~ (s->ptr[0]);
|
|
*data++ = (u_char) ~ (s->ptr[s->params.pixels_per_line]);
|
|
*data++ = (u_char) ~ (s->ptr[2 * s->params.pixels_per_line]);
|
|
++s->ptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (max_length-- != 0)
|
|
{
|
|
*data++ = s->ptr[0];
|
|
*data++ = s->ptr[s->params.pixels_per_line];
|
|
*data++ = s->ptr[2 * s->params.pixels_per_line];
|
|
++s->ptr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (max_length > s->end - s->ptr)
|
|
max_length = s->end - s->ptr;
|
|
|
|
*length = max_length;
|
|
|
|
if (1 == s->params.depth)
|
|
{
|
|
if (s->invert_image == SANE_TRUE)
|
|
{
|
|
while (max_length-- != 0)
|
|
*data++ = *s->ptr++;
|
|
}
|
|
else
|
|
{
|
|
while (max_length-- != 0)
|
|
*data++ = ~*s->ptr++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
if (s->invert_image == SANE_TRUE)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < max_length; i++)
|
|
{
|
|
data[i] = (u_char) ~ (s->ptr[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy (data, s->ptr, max_length);
|
|
}
|
|
s->ptr += max_length;
|
|
}
|
|
}
|
|
|
|
DBG (5, "sane_read: end\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
static SANE_Status
|
|
color_shuffle (SANE_Handle handle, int *new_length)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Byte *buf = s->buf;
|
|
int length = s->end - s->buf;
|
|
|
|
if (s->hw->color_shuffle == SANE_TRUE)
|
|
{
|
|
SANE_Byte *data_ptr; /* ptr to data to process */
|
|
SANE_Byte *data_end; /* ptr to end of processed data */
|
|
SANE_Byte *out_data_ptr; /* ptr to memory when writing data */
|
|
int i; /* loop counter */
|
|
|
|
/*
|
|
* It looks like we are dealing with a scanner that has an odd way
|
|
* of dealing with colors... The red and blue scan lines are shifted
|
|
* up or down by a certain number of lines relative to the green line.
|
|
*/
|
|
DBG (5, "sane_read: color_shuffle\n");
|
|
|
|
|
|
/*
|
|
* Initialize the variables we are going to use for the
|
|
* copying of the data. data_ptr is the pointer to
|
|
* the currently worked on scan line. data_end is the
|
|
* end of the data area as calculated from adding *length
|
|
* to the start of data.
|
|
* out_data_ptr is used when writing out the processed data
|
|
* and always points to the beginning of the next line to
|
|
* write.
|
|
*/
|
|
|
|
data_ptr = out_data_ptr = buf;
|
|
data_end = data_ptr + length;
|
|
|
|
/*
|
|
* The image data is in *buf, we know that the buffer contains s->end - s->buf ( = length)
|
|
* bytes of data. The width of one line is in s->params.bytes_per_line
|
|
*/
|
|
|
|
/*
|
|
* The buffer area is supposed to have a number of full scan
|
|
* lines, let's test if this is the case.
|
|
*/
|
|
|
|
if (length % s->params.bytes_per_line != 0)
|
|
{
|
|
DBG (1, "ERROR in size of buffer: %d / %d\n",
|
|
length, s->params.bytes_per_line);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
while (data_ptr < data_end)
|
|
{
|
|
SANE_Byte *source_ptr, *dest_ptr;
|
|
int loop;
|
|
|
|
/* copy the green information into the current line */
|
|
|
|
source_ptr = data_ptr + 1;
|
|
dest_ptr = s->line_buffer[s->color_shuffle_line] + 1;
|
|
|
|
for (i = 0; i < s->params.bytes_per_line / 3; i++)
|
|
{
|
|
*dest_ptr = *source_ptr;
|
|
dest_ptr += 3;
|
|
source_ptr += 3;
|
|
}
|
|
|
|
/* copy the red information n lines back */
|
|
|
|
if (s->color_shuffle_line >= s->line_distance)
|
|
{
|
|
source_ptr = data_ptr + 2;
|
|
dest_ptr =
|
|
s->line_buffer[s->color_shuffle_line - s->line_distance] + 2;
|
|
|
|
/* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */
|
|
for (loop = 0; loop < s->params.bytes_per_line / 3; loop++)
|
|
|
|
{
|
|
*dest_ptr = *source_ptr;
|
|
dest_ptr += 3;
|
|
source_ptr += 3;
|
|
}
|
|
}
|
|
|
|
/* copy the blue information n lines forward */
|
|
|
|
source_ptr = data_ptr;
|
|
dest_ptr = s->line_buffer[s->color_shuffle_line + s->line_distance];
|
|
|
|
/* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */
|
|
for (loop = 0; loop < s->params.bytes_per_line / 3; loop++)
|
|
{
|
|
*dest_ptr = *source_ptr;
|
|
dest_ptr += 3;
|
|
source_ptr += 3;
|
|
}
|
|
|
|
data_ptr += s->params.bytes_per_line;
|
|
|
|
if (s->color_shuffle_line == s->line_distance)
|
|
{
|
|
/*
|
|
* we just finished the line in line_buffer[0] - write it to the
|
|
* output buffer and continue.
|
|
*/
|
|
|
|
|
|
/*
|
|
* The ouput buffer ist still "buf", but because we are
|
|
* only overwriting from the beginning of the memory area
|
|
* we are not interfering with the "still to shuffle" data
|
|
* in the same area.
|
|
*/
|
|
|
|
/*
|
|
* Strip the first and last n lines and limit to
|
|
*/
|
|
if ((s->current_output_line >= s->line_distance) &&
|
|
(s->current_output_line < s->params.lines + s->line_distance))
|
|
{
|
|
memcpy (out_data_ptr, s->line_buffer[0], s->params.bytes_per_line);
|
|
out_data_ptr += s->params.bytes_per_line;
|
|
|
|
s->lines_written++;
|
|
}
|
|
|
|
s->current_output_line++;
|
|
|
|
|
|
/*
|
|
* Now remove the 0-entry and move all other
|
|
* lines up by one. There are 2*line_distance + 1
|
|
* buffers, * therefore the loop has to run from 0
|
|
* to * 2*line_distance, and because we want to
|
|
* copy every n+1st entry to n the loop runs
|
|
* from - to 2*line_distance-1!
|
|
*/
|
|
|
|
free (s->line_buffer[0]);
|
|
|
|
for (i = 0; i < s->line_distance * 2; i++)
|
|
{
|
|
s->line_buffer[i] = s->line_buffer[i + 1];
|
|
}
|
|
|
|
/*
|
|
* and create one new buffer at the end
|
|
*/
|
|
|
|
s->line_buffer[s->line_distance * 2] =
|
|
malloc (s->params.bytes_per_line);
|
|
if (s->line_buffer[s->line_distance * 2] == NULL)
|
|
{
|
|
int i;
|
|
for (i = 0; i < s->line_distance * 2; i++)
|
|
{
|
|
free (s->line_buffer[i]);
|
|
s->line_buffer[i] = NULL;
|
|
}
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s->color_shuffle_line++; /* increase the buffer number */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* At this time we've used up all the new data from the scanner, some of
|
|
* it is still in the line_buffers, but we are ready to return some of it
|
|
* to the front end software. To do so we have to adjust the size of the
|
|
* data area and the *new_length variable.
|
|
*/
|
|
|
|
*new_length = out_data_ptr - buf;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* static SANE_Status get_identity_information ( SANE_Handle handle)
|
|
*
|
|
* Request Identity information from scanner and fill in information
|
|
* into dev and/or scanner structures.
|
|
*/
|
|
static SANE_Status
|
|
get_identity_information (SANE_Handle handle)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
Epson_Device *dev = s->hw;
|
|
EpsonIdent ident;
|
|
u_char param[3];
|
|
SANE_Status status;
|
|
u_char *buf;
|
|
|
|
DBG (5, "get_identity_information()\n");
|
|
|
|
if (!s->hw->cmd->request_identity)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
|
|
param[0] = ESC;
|
|
param[1] = s->hw->cmd->request_identity;
|
|
param[2] = '\0';
|
|
|
|
if (NULL == (ident = (EpsonIdent) command (s, param, 2, &status)))
|
|
{
|
|
DBG (1, "ident failed\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
DBG (1, "type %3c 0x%02x\n", ident->type, ident->type);
|
|
DBG (1, "level %3c 0x%02x\n", ident->level, ident->level);
|
|
|
|
{
|
|
char *force = getenv ("SANE_EPSON_CMD_LVL");
|
|
|
|
if (force)
|
|
{
|
|
ident->type = force[0];
|
|
ident->level = force[1];
|
|
|
|
DBG (1, "type %3c 0x%02x\n", ident->type, ident->type);
|
|
DBG (1, "level %3c 0x%02x\n", ident->level, ident->level);
|
|
|
|
DBG (1, "forced\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* check if option equipment is installed.
|
|
*/
|
|
|
|
if (ident->status & STATUS_OPTION)
|
|
{
|
|
DBG (1, "option equipment is installed\n");
|
|
dev->extension = SANE_TRUE;
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "no option equipment installed\n");
|
|
dev->extension = SANE_FALSE;
|
|
}
|
|
|
|
dev->TPU = SANE_FALSE;
|
|
dev->ADF = SANE_FALSE;
|
|
|
|
/*
|
|
* set command type and level.
|
|
*/
|
|
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < NELEMS (epson_cmd); n++)
|
|
{
|
|
char type_level[3];
|
|
sprintf(type_level, "%c%c", ident->type, ident->level);
|
|
if (!strncmp (type_level, epson_cmd[n].level, 2))
|
|
break;
|
|
}
|
|
|
|
if (n < NELEMS (epson_cmd))
|
|
{
|
|
dev->cmd = &epson_cmd[n];
|
|
}
|
|
else
|
|
{
|
|
dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT];
|
|
DBG (1, "Unknown type %c or level %c, using %s\n",
|
|
ident->type, ident->level, dev->cmd->level);
|
|
}
|
|
|
|
s->hw->level = dev->cmd->level[1] - '0';
|
|
} /* set comand type and level */
|
|
|
|
/*
|
|
* Setting available resolutions and xy ranges for sane frontend.
|
|
*/
|
|
|
|
s->hw->res_list_size = 0;
|
|
s->hw->res_list =
|
|
(SANE_Int *) calloc (s->hw->res_list_size, sizeof (SANE_Int));
|
|
|
|
if (NULL == s->hw->res_list)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
{
|
|
int n, k;
|
|
int x = 0, y = 0;
|
|
int count = ident->count2 * 255 + ident->count1;
|
|
|
|
/* we need to correct for the difference in size between
|
|
the EpsonIdentRec and the EpsonHdrRec */
|
|
int correction = sizeof (EpsonIdentRec) - sizeof (EpsonHdrRec);
|
|
for (n = (count - correction), buf = ident->buf; n; n -= k, buf += k)
|
|
{
|
|
switch (*buf)
|
|
{
|
|
case 'R':
|
|
{
|
|
int val = buf[2] << 8 | buf[1];
|
|
|
|
s->hw->res_list_size++;
|
|
s->hw->res_list =
|
|
(SANE_Int *) realloc (s->hw->res_list,
|
|
s->hw->res_list_size * sizeof (SANE_Int));
|
|
|
|
if (NULL == s->hw->res_list)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
s->hw->res_list[s->hw->res_list_size - 1] = (SANE_Int) val;
|
|
|
|
DBG (1, "resolution (dpi): %d\n", val);
|
|
k = 3;
|
|
continue;
|
|
}
|
|
case 'A':
|
|
{
|
|
x = buf[2] << 8 | buf[1];
|
|
y = buf[4] << 8 | buf[3];
|
|
|
|
DBG (1, "maximum scan area: x %d y %d\n", x, y);
|
|
k = 5;
|
|
|
|
/*
|
|
* Check for Perfection 4990 photo/GT-X800 scanner.
|
|
* This scanner only report 3200 dpi back.
|
|
* The scanner fysical supports 4800 dpi.
|
|
* This is simulated here...
|
|
* Futher details read:
|
|
* EPSON Programming guide for EPSON Color Image Scanner Perfection 4990
|
|
*/
|
|
if (s->hw->cmd->request_extended_status != 0)
|
|
{
|
|
u_char *buf;
|
|
u_char params[2];
|
|
EpsonHdr head;
|
|
|
|
params[0] = ESC;
|
|
params[1] = s->hw->cmd->request_extended_status;
|
|
|
|
if (NULL != (head = (EpsonHdr) command (s, params, 2, &status)))
|
|
{
|
|
buf = &head->buf[0x1A];
|
|
DBG (1, "product name %x %x %x %x %x %x %x %x \n", buf[0], buf[1],buf[2],buf[3],buf[4], buf[5],buf[6], buf[7] );
|
|
if (strncmp((char *) buf,"GT-X800",7) == 0)
|
|
{
|
|
int val = 0x12 << 8 | 0xC0;
|
|
|
|
s->hw->res_list_size++;
|
|
s->hw->res_list =
|
|
(SANE_Int *) realloc (s->hw->res_list,
|
|
s->hw->res_list_size * sizeof (SANE_Int));
|
|
|
|
if (NULL == s->hw->res_list)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
s->hw->res_list[s->hw->res_list_size - 1] = (SANE_Int) val;
|
|
x = (x/32)*48;
|
|
y = (y/32)*48;
|
|
|
|
DBG (1, "resolution (dpi): %d\n", val);
|
|
DBG (1, "maximum scan area GT-X800: x %d y %d\n", x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
default:
|
|
break;
|
|
} /* case */
|
|
|
|
break;
|
|
} /* for */
|
|
|
|
dev->dpi_range.min = s->hw->res_list[0];
|
|
dev->dpi_range.max = s->hw->res_list[s->hw->res_list_size - 1];
|
|
dev->dpi_range.quant = 0;
|
|
|
|
dev->fbf_x_range.min = 0;
|
|
dev->fbf_x_range.max = SANE_FIX (x * 25.4 / dev->dpi_range.max);
|
|
dev->fbf_x_range.quant = 0;
|
|
|
|
dev->fbf_y_range.min = 0;
|
|
dev->fbf_y_range.max = SANE_FIX (y * 25.4 / dev->dpi_range.max);
|
|
dev->fbf_y_range.quant = 0;
|
|
|
|
DBG (5, "fbf tlx %f tly %f brx %f bry %f [mm]\n",
|
|
SANE_UNFIX (dev->fbf_x_range.min), SANE_UNFIX (dev->fbf_y_range.min),
|
|
SANE_UNFIX (dev->fbf_x_range.max),
|
|
SANE_UNFIX (dev->fbf_y_range.max));
|
|
|
|
}
|
|
|
|
/*
|
|
* Copy the resolution list to the resolution_list array so that the frontend can
|
|
* display the correct values
|
|
*/
|
|
|
|
s->hw->resolution_list =
|
|
malloc ((s->hw->res_list_size + 1) * sizeof (SANE_Word));
|
|
|
|
if (s->hw->resolution_list == NULL)
|
|
{
|
|
DBG (1, "out of memory (line %d)\n", __LINE__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
*(s->hw->resolution_list) = s->hw->res_list_size;
|
|
memcpy (&(s->hw->resolution_list[1]), s->hw->res_list,
|
|
s->hw->res_list_size * sizeof (SANE_Word));
|
|
|
|
/* filter the resolution list */
|
|
/* the option is not yet initialized, for now just set it to false */
|
|
s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE;
|
|
filter_resolution_list (s);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
} /* request identity */
|
|
|
|
|
|
/*
|
|
* static SANE_Status get_identity2_information ( SANE_Handle handle)
|
|
*
|
|
* Request Identity2 information from scanner and fill in information
|
|
* into dev and/or scanner structures.
|
|
*/
|
|
static SANE_Status
|
|
get_identity2_information (SANE_Handle handle)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Status status;
|
|
int len;
|
|
u_char param[3];
|
|
u_char result[4];
|
|
u_char *buf;
|
|
|
|
DBG (5, "get_identity2_information()\n");
|
|
|
|
if (s->hw->cmd->request_identity2 == 0)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
param[0] = ESC;
|
|
param[1] = s->hw->cmd->request_identity2;
|
|
param[2] = '\0';
|
|
|
|
send (s, param, 2, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = 4; /* receive header */
|
|
|
|
receive (s, result, len, &status);
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = result[3] << 8 | result[2];
|
|
buf = alloca (len);
|
|
|
|
receive (s, buf, len, &status); /* reveive actual status data */
|
|
|
|
/* the first two bytes of the buffer contain the optical resolution */
|
|
s->hw->optical_res = buf[1] << 8 | buf[0];
|
|
|
|
/*
|
|
* the 4th and 5th byte contain the line distance. Both values have to
|
|
* be identical, otherwise this software can not handle this scanner.
|
|
*/
|
|
if (buf[4] != buf[5])
|
|
{
|
|
close_scanner (s);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
s->hw->max_line_distance = buf[4];
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
* void sane_cancel(SANE_Handle handle)
|
|
*
|
|
* Set the cancel flag to true. The next time the backend requests data
|
|
* from the scanner the CAN message will be sent.
|
|
*/
|
|
|
|
void
|
|
sane_cancel (SANE_Handle handle)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
|
|
/*
|
|
* If the s->ptr pointer is not NULL, then a scan operation
|
|
* was started and if s->eof is FALSE, it was not finished.
|
|
*/
|
|
|
|
if (s->buf != NULL)
|
|
{
|
|
u_char *dummy;
|
|
int len;
|
|
|
|
/* malloc one line */
|
|
dummy = malloc (s->params.bytes_per_line);
|
|
if (dummy == NULL)
|
|
{
|
|
DBG (1, "Out of memory\n");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* there is still data to read from the scanner */
|
|
|
|
s->canceling = SANE_TRUE;
|
|
|
|
while (!s->eof &&
|
|
SANE_STATUS_CANCELLED != sane_read (s, dummy,
|
|
s->params.bytes_per_line,
|
|
&len))
|
|
{
|
|
/* empty body, the while condition does the processing */
|
|
}
|
|
free (dummy);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static SANE_Status
|
|
request_focus_position (SANE_Handle handle, u_char * position)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Status status;
|
|
int len;
|
|
u_char param[3];
|
|
u_char result[4];
|
|
u_char *buf;
|
|
|
|
DBG (5, "request_focus_position()\n");
|
|
|
|
if (s->hw->cmd->request_focus_position == 0)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
param[0] = ESC;
|
|
param[1] = s->hw->cmd->request_focus_position;
|
|
param[2] = '\0';
|
|
|
|
send (s, param, 2, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = 4; /* receive header */
|
|
|
|
receive (s, result, len, &status);
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = result[3] << 8 | result[2];
|
|
buf = alloca (len);
|
|
|
|
receive (s, buf, len, &status); /* reveive actual status data */
|
|
|
|
*position = buf[1];
|
|
DBG (1, "Focus position = 0x%x\n", buf[1]);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
/*
|
|
* Request the push button status
|
|
* returns SANE_TRUE if the button was pressed
|
|
* and SANE_FALSE if the button was not pressed
|
|
* it also returns SANE_TRUE in case of an error.
|
|
* This is necessary so that a process that waits for
|
|
* the button does not block indefinitely.
|
|
*/
|
|
static SANE_Bool
|
|
request_push_button_status (SANE_Handle handle, SANE_Bool * theButtonStatus)
|
|
{
|
|
Epson_Scanner *s = (Epson_Scanner *) handle;
|
|
SANE_Status status;
|
|
int len;
|
|
u_char param[3];
|
|
u_char result[4];
|
|
u_char *buf;
|
|
|
|
DBG (5, "request_push_button_status()\n");
|
|
|
|
if (s->hw->cmd->request_push_button_status == 0)
|
|
{
|
|
DBG (1, "push button status unsupported\n");
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
param[0] = ESC;
|
|
param[1] = s->hw->cmd->request_push_button_status;
|
|
param[2] = '\0';
|
|
|
|
send (s, param, 2, &status);
|
|
|
|
if (SANE_STATUS_GOOD != status)
|
|
{
|
|
DBG (1, "error sending command\n");
|
|
return status;
|
|
}
|
|
|
|
len = 4; /* receive header */
|
|
|
|
receive (s, result, len, &status);
|
|
if (SANE_STATUS_GOOD != status)
|
|
return status;
|
|
|
|
len = result[3] << 8 | result[2]; /* this should be 1 for scanners with one button */
|
|
buf = alloca (len);
|
|
|
|
receive (s, buf, len, &status); /* reveive actual status data */
|
|
|
|
DBG (1, "Push button status = %d\n", buf[0] & 0x01);
|
|
*theButtonStatus = ((buf[0] & 0x01) != 0);
|
|
|
|
return (SANE_STATUS_GOOD);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
filter_resolution_list (Epson_Scanner * s)
|
|
{
|
|
/* re-create the list */
|
|
|
|
if (s->val[OPT_LIMIT_RESOLUTION].w == SANE_TRUE)
|
|
{
|
|
/* copy the short list */
|
|
|
|
/* filter out all values that are not 300 or 400 dpi based */
|
|
int i;
|
|
|
|
int new_size = 0;
|
|
SANE_Bool is_correct_resolution = SANE_FALSE;
|
|
|
|
for (i = 1; i <= s->hw->res_list_size; i++)
|
|
{
|
|
SANE_Word res;
|
|
res = s->hw->res_list[i];
|
|
if ((res < 100) || (0 == (res % 300)) || (0 == (res % 400)))
|
|
{
|
|
/* add the value */
|
|
new_size++;
|
|
|
|
s->hw->resolution_list[new_size] = s->hw->res_list[i];
|
|
|
|
/* check for a valid current resolution */
|
|
if (res == s->val[OPT_RESOLUTION].w)
|
|
{
|
|
is_correct_resolution = SANE_TRUE;
|
|
}
|
|
}
|
|
}
|
|
s->hw->resolution_list[0] = new_size;
|
|
|
|
if (is_correct_resolution == SANE_FALSE)
|
|
{
|
|
for (i = 1; i <= new_size; i++)
|
|
{
|
|
if (s->val[OPT_RESOLUTION].w < s->hw->resolution_list[i])
|
|
{
|
|
s->val[OPT_RESOLUTION].w = s->hw->resolution_list[i];
|
|
i = new_size + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* copy the full list */
|
|
s->hw->resolution_list[0] = s->hw->res_list_size;
|
|
memcpy (&(s->hw->resolution_list[1]), s->hw->res_list,
|
|
s->hw->res_list_size * sizeof (SANE_Word));
|
|
}
|
|
}
|
|
|
|
/**********************************************************************************/
|
|
|
|
/*
|
|
* SANE_Status sane_set_io_mode()
|
|
*
|
|
* not supported - for asynchronous I/O
|
|
*/
|
|
|
|
SANE_Status
|
|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
|
|
{
|
|
/* get rid of compiler warning */
|
|
handle = handle;
|
|
non_blocking = non_blocking;
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
/*
|
|
* SANE_Status sane_get_select_fd()
|
|
*
|
|
* not supported - for asynchronous I/O
|
|
*/
|
|
|
|
SANE_Status
|
|
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
|
|
{
|
|
/* get rid of compiler warnings */
|
|
handle = handle;
|
|
fd = fd;
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
/*
|
|
vim:ts=2:sw=2:cindent:
|
|
*/
|