kopia lustrzana https://gitlab.com/sane-project/backends
3787 wiersze
115 KiB
C
3787 wiersze
115 KiB
C
static const char RCSid[] = "$Header$";
|
|
/* sane - Scanner Access Now Easy.
|
|
|
|
This file is part of the SANE package.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
MA 02111-1307, USA.
|
|
|
|
As a special exception, the authors of SANE give permission for
|
|
additional uses of the libraries contained in this release of SANE.
|
|
|
|
The exception is that, if you link a SANE library with other files
|
|
to produce an executable, this does not by itself cause the
|
|
resulting executable to be covered by the GNU General Public
|
|
License. Your use of that executable is in no way restricted on
|
|
account of linking the SANE library code into it.
|
|
|
|
This exception does not, however, invalidate any other reasons why
|
|
the executable file might be covered by the GNU General Public
|
|
License.
|
|
|
|
If you submit changes to SANE to the maintainers to be included in
|
|
a subsequent release, you agree by submitting the changes that
|
|
those changes may be distributed with this exception intact.
|
|
|
|
If you write modifications of your own for SANE, it is your choice
|
|
whether to permit this exception to apply to your modifications.
|
|
If you do not wish that, delete this exception notice.
|
|
|
|
This file implements a SANE backend for Fujitsu M3096G
|
|
flatbed/ADF scanners. It was derived from the COOLSCAN driver.
|
|
Written by Randolph Bentson <bentson@holmsjoen.com> */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*
|
|
* $Log$
|
|
* Revision 1.5 2001/10/10 21:50:22 hmg
|
|
* Update (from Oliver Schirrmeister <oschirr@abm.de>). Added: Support for ipc2/3
|
|
* and cmp2 options; support for duplex-scanners m3093DG, m4097DG; constraint checking
|
|
* for m3093; support EVPD (virtual product data); support ADF paper size spezification.
|
|
* Henning Meier-Geinitz <henning@meier-geinitz.de>
|
|
*
|
|
* Revision 1.4 2001/05/31 18:01:39 hmg
|
|
* Fixed config_line[len-1] bug which could generate an access
|
|
* violation if len==0.
|
|
* Henning Meier-Geinitz <henning@meier-geinitz.de>
|
|
*
|
|
* Revision 1.3 2000/08/12 15:09:17 pere
|
|
* Merge devel (v1.0.3) into head branch.
|
|
*
|
|
* Revision 1.1.2.6 2000/07/30 11:16:01 hmg
|
|
* 2000-07-30 Henning Meier-Geinitz <hmg@gmx.de>
|
|
*
|
|
* * backend/mustek.*: Update to Mustek backend 1.0-95. Changed from
|
|
* wait() to waitpid() and removed unused code.
|
|
* * configure configure.in backend/m3096g.c backend/sp15c.c: Reverted
|
|
* the V_REV patch. V_REV should not be used in backends.
|
|
*
|
|
* Revision 1.1.2.5 2000/07/29 21:38:12 hmg
|
|
* 2000-07-29 Henning Meier-Geinitz <hmg@gmx.de>
|
|
*
|
|
* * backend/sp15.c backend/m3096g.c: Replace fgets with
|
|
* sanei_config_read, return V_REV as part of version_code string
|
|
* (patch from Randolph Bentson).
|
|
*
|
|
* Revision 1.1.2.4 2000/07/25 21:47:33 hmg
|
|
* 2000-07-25 Henning Meier-Geinitz <hmg@gmx.de>
|
|
*
|
|
* * backend/snapscan.c: Use DBG(0, ...) instead of fprintf (stderr, ...).
|
|
* * backend/abaton.c backend/agfafocus.c backend/apple.c backend/dc210.c
|
|
* backend/dll.c backend/dmc.c backend/microtek2.c backend/pint.c
|
|
* backend/qcam.c backend/ricoh.c backend/s9036.c backend/snapscan.c
|
|
* backend/tamarack.c: Use sanei_config_read instead of fgets.
|
|
* * backend/dc210.c backend/microtek.c backend/pnm.c: Added
|
|
* #include <sane/config.h>.
|
|
* * backend/dc25.c backend/m3096.c backend/sp15.c
|
|
* backend/st400.c: Moved #include <sane/config.h> to the beginning.
|
|
* * AUTHORS: Changed agfa to agfafocus.
|
|
*
|
|
* Revision 1.1.2.3 2000/03/14 17:47:07 abel
|
|
* new version of the Sharp backend added.
|
|
*
|
|
* Revision 1.1.2.2 2000/01/26 03:51:46 pere
|
|
* Updated backends sp15c (v1.12) and m3096g (v1.11).
|
|
*
|
|
* Revision 1.11 2000/01/25 16:24:15 bentson
|
|
* expand tabs; add debug message; clean-up compiler warnings
|
|
*
|
|
* Revision 1.10 2000/01/05 05:25:19 bentson
|
|
* indent to barfin' GNU style
|
|
*
|
|
* Revision 1.9 2000/01/05 05:24:06 bentson
|
|
* fixin' boundary conditions on paper size
|
|
*
|
|
* Revision 1.8.1.1 1999/12/20 20:25:05 bentson
|
|
* hack for preview resolution
|
|
*
|
|
* Revision 1.8 1999/12/16 16:08:56 bentson
|
|
* fix problem with landscape ADF operation
|
|
*
|
|
* Revision 1.7 1999/12/04 00:48:36 bentson
|
|
* cosmetic changes only
|
|
*
|
|
* Revision 1.6 1999/11/24 20:05:10 bentson
|
|
* minor fix to size parameter controls
|
|
*
|
|
* Revision 1.5 1999/11/23 18:47:27 bentson
|
|
* add some constraint checking
|
|
*
|
|
* Revision 1.4 1999/11/19 17:29:15 bentson
|
|
* enhance control of device (works with xscanimage)
|
|
*
|
|
* Revision 1.3 1999/11/18 18:13:36 bentson
|
|
* basic grayscale scanning works
|
|
*
|
|
* Revision 1.2 1999/11/17 00:36:19 bentson
|
|
* basic lineart scanning works
|
|
*
|
|
* Revision 1.1 1999/11/12 05:41:07 bentson
|
|
* can move paper, but not yet scan
|
|
*
|
|
*/
|
|
|
|
/* SANE-FLOW-DIAGRAMM
|
|
|
|
- sane_init() : initialize backend, attach scanners
|
|
. - sane_get_devices() : query list of scanner-devices
|
|
. - sane_open() : open a particular scanner-device
|
|
. . - sane_set_io_mode : set blocking-mode
|
|
. . - sane_get_select_fd : get scanner-fd
|
|
. . - sane_get_option_descriptor() : get option informations
|
|
. . - sane_control_option() : change option values
|
|
. .
|
|
. . - sane_start() : start image aquisition
|
|
. . - sane_get_parameters() : returns actual scan-parameters
|
|
. . - sane_read() : read image-data (from pipe)
|
|
. .
|
|
. . - sane_cancel() : cancel operation
|
|
. - sane_close() : close opened scanner-device
|
|
- sane_exit() : terminate use of backend
|
|
*/
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#include "sane/config.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include "sane/sanei_backend.h"
|
|
#include "sane/sanei_scsi.h"
|
|
#include "sane/saneopts.h"
|
|
#include "sane/sanei_config.h"
|
|
|
|
|
|
#include "m3096g-scsi.h"
|
|
#include "m3096g.h"
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static const char negativeStr[] = "Negative";
|
|
static const char positiveStr[] = "Positive";
|
|
static SANE_String_Const type_list[] =
|
|
{positiveStr, negativeStr, 0};
|
|
|
|
static SANE_String_Const source_list[] =
|
|
{"ADF", "FB", NULL};
|
|
|
|
#ifdef no_preview_res
|
|
static const SANE_Int resolution_list[] =
|
|
{5, 0, 200, 240, 300, 400};
|
|
#endif
|
|
|
|
|
|
static SANE_Int res_list0[] =
|
|
{4, 0, 200, 300, 400}; /* 3096GX gray */
|
|
|
|
static SANE_Int res_list1[] =
|
|
{5, 0, 200, 240, 300, 400}; /* 3096GX binary */
|
|
|
|
static SANE_Int res_list2[] =
|
|
{7, 0, 100, 150, 200, 240, 300, 400}; /* 3093DG gray */
|
|
|
|
static SANE_Int res_list3[] =
|
|
{8, 0, 100, 150, 200, 240, 300, 400, 600}; /* 3093DG binary */
|
|
|
|
|
|
static const char lineStr[] = "Lineart";
|
|
static const char halfStr[] = "Halftone";
|
|
static const char grayStr[] = "Gray";
|
|
static SANE_String_Const scan_mode_list[] =
|
|
{lineStr, halfStr, grayStr, NULL};
|
|
|
|
|
|
/* how do the following work? */
|
|
static const SANE_Range brightness_range =
|
|
{0, 255, 32};
|
|
static const SANE_Range threshold_range =
|
|
{0, 255, 4};
|
|
static const SANE_Range contrast_range =
|
|
{0, 255, 1};
|
|
|
|
|
|
static const char cmp_none[] = "None";
|
|
static const char cmp_mh[] = "MH"; /* Fax Group 3 */
|
|
static const char cmp_mr[] = "MR"; /* what's that??? */
|
|
static const char cmp_mmr[] = "MMR"; /* Fax Groupp 4 */
|
|
static SANE_String_Const compression_mode_list[] =
|
|
{cmp_none, cmp_mh, cmp_mr, cmp_mmr, NULL};
|
|
|
|
static const char gamma_default[] = "Default";
|
|
static const char gamma_normal[] = "Normal";
|
|
static const char gamma_soft[] = "Soft";
|
|
static const char gamma_sharp[] = "Sharp";
|
|
static SANE_String_Const gamma_mode_list[] =
|
|
{gamma_default, gamma_normal, gamma_soft, gamma_sharp, NULL};
|
|
|
|
static const char emphasis_none[] = "None";
|
|
static const char emphasis_low[] = "Low";
|
|
static const char emphasis_medium[] = "Medium";
|
|
static const char emphasis_high[] = "High";
|
|
static const char emphasis_smooth[] = "Smooth";
|
|
static SANE_String_Const emphasis_mode_list[] =
|
|
{emphasis_none, emphasis_low, emphasis_medium,
|
|
emphasis_high, emphasis_smooth, NULL};
|
|
|
|
static const SANE_Range variance_rate_range =
|
|
{0, 255, 1};
|
|
|
|
static const SANE_Range threshold_curve_range =
|
|
{0, 7, 1};
|
|
|
|
static const char gradiation_ordinary[] = "Ordinary";
|
|
static const char gradiation_high[] = "High";
|
|
static SANE_String_Const gradiation_mode_list[] =
|
|
{gradiation_ordinary, gradiation_high, NULL};
|
|
|
|
static const char smoothing_mode_ocr[] = "OCR";
|
|
static const char smoothing_mode_image[] = "Image";
|
|
static SANE_String_Const smoothing_mode_list[] =
|
|
{smoothing_mode_ocr, smoothing_mode_image, NULL};
|
|
|
|
static const char filtering_ballpoint[] = "Ballpoint";
|
|
static const char filtering_ordinary[] = "Ordinary";
|
|
static SANE_String_Const filtering_mode_list[] =
|
|
{filtering_ballpoint, filtering_ordinary, NULL};
|
|
|
|
static const char background_white[] = "White";
|
|
static const char background_black[] = "Black";
|
|
static SANE_String_Const background_mode_list[] =
|
|
{background_white, background_black, NULL};
|
|
|
|
static const char white_level_follow_default[] = "Default";
|
|
static const char white_level_follow_enabled[] = "Enabled";
|
|
static const char white_level_follow_disabled[] = "Disabled";
|
|
static SANE_String_Const white_level_follow_mode_list[] =
|
|
{white_level_follow_default, white_level_follow_enabled,
|
|
white_level_follow_disabled, NULL};
|
|
|
|
static const char dtc_selection_default[] = "Default";
|
|
static const char dtc_selection_simplified[] = "Simplified";
|
|
static const char dtc_selection_dynamic[] = "Dynamic";
|
|
static SANE_String_Const dtc_selection_mode_list[] =
|
|
{dtc_selection_default, dtc_selection_simplified, dtc_selection_dynamic, NULL};
|
|
|
|
static const char paper_size_a3[] = "A3";
|
|
static const char paper_size_a4[] = "A4";
|
|
static const char paper_size_a5[] = "A5";
|
|
static const char paper_size_double[] = "Double";
|
|
static const char paper_size_letter[] = "Letter";
|
|
static const char paper_size_b4[] = "B4";
|
|
static const char paper_size_b5[] = "B5";
|
|
static const char paper_size_legal[] = "Legal";
|
|
static const char paper_size_custom[] = "Custom";
|
|
static const char paper_size_detect[] = "Autodetect";
|
|
|
|
static SANE_String_Const paper_size_mode_list[] =
|
|
{paper_size_a3, paper_size_a4, paper_size_a5,
|
|
paper_size_double, paper_size_letter, paper_size_b4, paper_size_b5,
|
|
paper_size_legal, paper_size_custom, paper_size_detect, NULL};
|
|
|
|
static const char paper_orientation_portrait[] = "Portrait";
|
|
static const char paper_orientation_landscape[] = "Landscape";
|
|
static SANE_String_Const paper_orientation_mode_list[] =
|
|
{paper_orientation_portrait, paper_orientation_landscape, NULL};
|
|
|
|
|
|
#if 0
|
|
static void
|
|
wabbit (void)
|
|
{
|
|
int i;
|
|
DBG (10, "%s\n", "\twait a bit before quitting\n");
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
sleep (1);
|
|
DBG (10, "\ttick\n");
|
|
}
|
|
} /* wabbit */
|
|
#endif
|
|
|
|
static struct m3096g *current_scanner;
|
|
|
|
/* ################# externally visible routines ################{ */
|
|
|
|
SANE_Status /* looks like frontend ignores results */
|
|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
|
|
{
|
|
char dev_name[PATH_MAX];
|
|
size_t len;
|
|
FILE *fp;
|
|
DBG_INIT ();
|
|
DBG (10, "sane_init %d\n", authorize);
|
|
|
|
if (version_code)
|
|
*version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0);
|
|
fp = sanei_config_open (M3096G_CONFIG_FILE);
|
|
if (!fp)
|
|
{
|
|
attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
while (sanei_config_read (dev_name, sizeof (dev_name), fp))
|
|
{
|
|
if (dev_name[0] == '#')
|
|
continue;
|
|
len = strlen (dev_name);
|
|
if (!len)
|
|
continue;
|
|
sanei_config_attach_matching_devices (dev_name, attach_one);
|
|
}
|
|
|
|
fclose (fp);
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
} /* sane_init */
|
|
|
|
|
|
SANE_Status
|
|
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
|
|
{
|
|
static const SANE_Device **devlist = 0;
|
|
struct m3096g *dev;
|
|
int i;
|
|
|
|
DBG (10, "sane_get_devices %d\n", local_only);
|
|
|
|
if (devlist)
|
|
free (devlist);
|
|
devlist = calloc (num_devices + 1, sizeof (devlist[0]));
|
|
if (!devlist)
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
for (dev = first_dev, i = 0; i < num_devices; dev = dev->next)
|
|
devlist[i++] = &dev->sane;
|
|
devlist[i++] = 0;
|
|
|
|
*device_list = devlist;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
} /* sane_get_devices */
|
|
|
|
|
|
SANE_Status
|
|
sane_open (SANE_String_Const name, SANE_Handle * handle)
|
|
{
|
|
struct m3096g *dev = first_dev;
|
|
|
|
DBG (10, "sane_open %s\n", name);
|
|
|
|
if (!dev)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
init_options (dev);
|
|
*handle = dev;
|
|
|
|
if (dev->autofeeder)
|
|
dev->use_adf = SANE_TRUE;
|
|
else
|
|
dev->use_adf = SANE_FALSE;
|
|
|
|
dev->x_res = 200;
|
|
dev->y_res = 200;
|
|
dev->tl_x = 0;
|
|
dev->tl_y = 0;
|
|
dev->br_x = 1200 * 17 / 2;
|
|
dev->br_y = 1200 * 11;
|
|
dev->brightness = 128;
|
|
dev->threshold = 0;
|
|
dev->contrast = 0;
|
|
dev->composition = WD_comp_LA;
|
|
dev->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
dev->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
dev->bitsperpixel = 1;
|
|
dev->halftone = 0;
|
|
dev->rif = 0;
|
|
dev->bitorder = 0;
|
|
dev->compress_type = 0;
|
|
dev->compress_arg = 0;
|
|
dev->vendor_id_code = 0;
|
|
dev->gamma = WD_gamma_DEFAULT;
|
|
dev->outline = 0;
|
|
dev->emphasis = WD_emphasis_NONE;
|
|
dev->auto_sep = 0;
|
|
dev->mirroring = 0;
|
|
dev->var_rate_dyn_thresh = 0;
|
|
|
|
dev->dtc_threshold_curve = 0;
|
|
dev->gradiation = WD_gradiation_ORDINARY;
|
|
dev->smoothing_mode = WD_smoothing_IMAGE;
|
|
dev->filtering = WD_filtering_ORDINARY;
|
|
dev->background = WD_background_WHITE;
|
|
dev->matrix2x2 = 0;
|
|
dev->matrix3x3 = 0;
|
|
dev->matrix4x4 = 0;
|
|
dev->matrix5x5 = 0;
|
|
dev->noise_removal = 0;
|
|
dev->opt[OPT_MATRIX2X2].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_MATRIX3X3].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_MATRIX4X4].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_MATRIX5X5].cap = SANE_CAP_INACTIVE;
|
|
|
|
dev->white_level_follow = WD_white_level_follow_DEFAULT;
|
|
|
|
dev->subwindow_list = 0;
|
|
|
|
dev->paper_size = WD_paper_A4;
|
|
dev->paper_orientation = WD_paper_PORTRAIT;
|
|
dev->paper_selection = WD_paper_SEL_STANDARD;
|
|
|
|
dev->paper_width_X = 0;
|
|
dev->paper_length_Y = 0;
|
|
|
|
dev->duplex = 0;
|
|
|
|
dev->dtc_selection = WD_dtc_selection_DEFAULT;
|
|
dev->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_BACKGROUND].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_FILTERING].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_GRADIATION].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_INACTIVE;
|
|
dev->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_INACTIVE;
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
} /* sane_open */
|
|
|
|
|
|
SANE_Status
|
|
sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
|
|
{
|
|
DBG (10, "sane_set_io_mode \n");
|
|
DBG (99, "%d %d\n", non_blocking, h); /* avoid compiler warning */
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
} /* sane_set_io_mode */
|
|
|
|
|
|
SANE_Status
|
|
sane_get_select_fd (SANE_Handle h, SANE_Int * fdp)
|
|
{
|
|
DBG (10, "sane_get_select_fd\n");
|
|
DBG (99, "%d %d\n", fdp, h); /* avoid compiler warning */
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
} /* sane_get_select_fd */
|
|
|
|
|
|
const SANE_Option_Descriptor *
|
|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|
{
|
|
struct m3096g *scanner = handle;
|
|
|
|
DBG (10, "sane_get_option_descriptor: \"%s\"\n",
|
|
scanner->opt[option].name);
|
|
|
|
if ((unsigned) option >= NUM_OPTIONS)
|
|
return 0;
|
|
return &scanner->opt[option];
|
|
} /* sane_get_option_descriptor */
|
|
|
|
|
|
SANE_Status
|
|
sane_control_option (SANE_Handle handle, SANE_Int option,
|
|
SANE_Action action, void *val,
|
|
SANE_Int * info)
|
|
{
|
|
struct m3096g *scanner = handle;
|
|
SANE_Status status;
|
|
SANE_Word cap;
|
|
|
|
if (info)
|
|
*info = 0;
|
|
|
|
if (scanner->scanning == SANE_TRUE)
|
|
{
|
|
DBG (5, "sane_control_option: device busy\n");
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
if (option >= NUM_OPTIONS)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
cap = scanner->opt[option].cap;
|
|
|
|
if (action == SANE_ACTION_GET_VALUE)
|
|
{
|
|
DBG (10, "sane_control_option: get value \"%s\"\n",
|
|
scanner->opt[option].name);
|
|
DBG (11, "\tcap = %d\n", cap);
|
|
|
|
if (!SANE_OPTION_IS_ACTIVE (cap))
|
|
{
|
|
DBG (10, "\tinactive\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
|
|
case OPT_NUM_OPTS:
|
|
*(SANE_Word *) val = NUM_OPTIONS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_SOURCE:
|
|
if (scanner->use_adf == SANE_TRUE)
|
|
{
|
|
strcpy (val, "ADF");
|
|
}
|
|
else
|
|
{
|
|
strcpy (val, "FB");
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MODE:
|
|
switch (scanner->composition)
|
|
{
|
|
case WD_comp_LA:
|
|
strcpy (val, lineStr);
|
|
break;
|
|
case WD_comp_HT:
|
|
strcpy (val, halfStr);
|
|
break;
|
|
case WD_comp_GS:
|
|
strcpy (val, grayStr);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TYPE:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
case OPT_PRESCAN:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
case OPT_X_RES:
|
|
*(SANE_Word *) val = scanner->x_res;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_Y_RES:
|
|
*(SANE_Word *) val = scanner->y_res;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
#ifdef no_preview_res
|
|
case OPT_PREVIEW_RES:
|
|
return SANE_STATUS_INVAL;
|
|
#endif
|
|
|
|
case OPT_TL_X:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_x));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TL_Y:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_y));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_X:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_x));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_Y:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_y));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AVERAGING:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
case OPT_BRIGHTNESS:
|
|
*(SANE_Word *) val = scanner->brightness;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_THRESHOLD:
|
|
*(SANE_Word *) val = scanner->threshold;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_CONTRAST:
|
|
*(SANE_Word *) val = scanner->contrast;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_RIF:
|
|
*(SANE_Bool *) val = scanner->rif;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_COMPRESSION:
|
|
switch (scanner->compress_type)
|
|
{
|
|
case WD_cmp_NONE:
|
|
strcpy (val, cmp_none);
|
|
break;
|
|
case WD_cmp_MH:
|
|
strcpy (val, cmp_mh);
|
|
break;
|
|
case WD_cmp_MR:
|
|
strcpy (val, cmp_mr);
|
|
break;
|
|
case WD_cmp_MMR:
|
|
strcpy (val, cmp_mmr);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA:
|
|
switch (scanner->gamma)
|
|
{
|
|
case WD_gamma_DEFAULT:
|
|
strcpy (val, gamma_default);
|
|
break;
|
|
case WD_gamma_NORMAL:
|
|
strcpy (val, gamma_normal);
|
|
break;
|
|
case WD_gamma_SOFT:
|
|
strcpy (val, gamma_soft);
|
|
break;
|
|
case WD_gamma_SHARP:
|
|
strcpy (val, gamma_sharp);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_OUTLINE_EXTRACTION:
|
|
*(SANE_Bool *) val = scanner->outline;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_EMPHASIS:
|
|
switch (scanner->emphasis)
|
|
{
|
|
case WD_emphasis_NONE:
|
|
strcpy (val, emphasis_none);
|
|
break;
|
|
case WD_emphasis_LOW:
|
|
strcpy (val, emphasis_low);
|
|
break;
|
|
case WD_emphasis_MEDIUM:
|
|
strcpy (val, emphasis_medium);
|
|
break;
|
|
case WD_emphasis_HIGH:
|
|
strcpy (val, emphasis_high);
|
|
break;
|
|
case WD_emphasis_SMOOTH:
|
|
strcpy (val, emphasis_smooth);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AUTOSEP:
|
|
*(SANE_Bool *) val = scanner->auto_sep;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MIRROR_IMAGE:
|
|
*(SANE_Bool *) val = scanner->mirroring;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_VARIANCE_RATE:
|
|
*(SANE_Word *) val = scanner->var_rate_dyn_thresh;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_THRESHOLD_CURVE:
|
|
*(SANE_Word *) val = scanner->dtc_threshold_curve;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GRADIATION:
|
|
switch (scanner->gradiation)
|
|
{
|
|
case WD_gradiation_ORDINARY:
|
|
strcpy (val, gradiation_ordinary);
|
|
break;
|
|
case WD_gradiation_HIGH:
|
|
strcpy (val, gradiation_high);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_SMOOTHING_MODE:
|
|
switch (scanner->smoothing_mode)
|
|
{
|
|
case WD_smoothing_OCR:
|
|
strcpy (val, smoothing_mode_ocr);
|
|
break;
|
|
case WD_smoothing_IMAGE:
|
|
strcpy (val, smoothing_mode_image);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_FILTERING:
|
|
switch (scanner->filtering)
|
|
{
|
|
case WD_filtering_BALLPOINT:
|
|
strcpy (val, filtering_ballpoint);
|
|
break;
|
|
case WD_filtering_ORDINARY:
|
|
strcpy (val, filtering_ordinary);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BACKGROUND:
|
|
switch (scanner->background)
|
|
{
|
|
case WD_background_WHITE:
|
|
strcpy (val, background_white);
|
|
break;
|
|
case WD_background_BLACK:
|
|
strcpy (val, background_black);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX2X2:
|
|
*(SANE_Bool *) val = scanner->matrix2x2;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX3X3:
|
|
*(SANE_Bool *) val = scanner->matrix3x3;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX4X4:
|
|
*(SANE_Bool *) val = scanner->matrix4x4;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX5X5:
|
|
*(SANE_Bool *) val = scanner->matrix5x5;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_NOISE_REMOVAL:
|
|
*(SANE_Bool *) val = scanner->noise_removal;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_WHITE_LEVEL_FOLLOW:
|
|
switch (scanner->white_level_follow)
|
|
{
|
|
case WD_white_level_follow_DEFAULT:
|
|
strcpy (val, white_level_follow_default);
|
|
break;
|
|
case WD_white_level_follow_ENABLED:
|
|
strcpy (val, white_level_follow_enabled);
|
|
break;
|
|
case WD_white_level_follow_DISABLED:
|
|
strcpy (val, white_level_follow_disabled);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_DTC_SELECTION:
|
|
switch (scanner->dtc_selection)
|
|
{
|
|
case WD_dtc_selection_DEFAULT:
|
|
strcpy (val, dtc_selection_default);
|
|
break;
|
|
case WD_dtc_selection_DYNAMIC:
|
|
strcpy (val, dtc_selection_dynamic);
|
|
break;
|
|
case WD_dtc_selection_SIMPLIFIED:
|
|
strcpy (val, dtc_selection_simplified);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PREVIEW:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
case OPT_DUPLEX:
|
|
*(SANE_Bool *) val = scanner->duplex;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PAPER_SIZE:
|
|
switch (scanner->paper_size)
|
|
{
|
|
case WD_paper_A3:
|
|
strcpy (val, paper_size_a3);
|
|
break;
|
|
case WD_paper_A4:
|
|
strcpy (val, paper_size_a4);
|
|
break;
|
|
case WD_paper_A5:
|
|
strcpy (val, paper_size_a5);
|
|
break;
|
|
case WD_paper_DOUBLE:
|
|
strcpy (val, paper_size_double);
|
|
break;
|
|
case WD_paper_LETTER:
|
|
strcpy (val, paper_size_letter);
|
|
break;
|
|
case WD_paper_B4:
|
|
strcpy (val, paper_size_b4);
|
|
break;
|
|
case WD_paper_B5:
|
|
strcpy (val, paper_size_b5);
|
|
break;
|
|
case WD_paper_LEGAL:
|
|
strcpy (val, paper_size_legal);
|
|
break;
|
|
case WD_paper_UNDEFINED:
|
|
if (scanner->paper_selection == WD_paper_UNDEFINED)
|
|
{
|
|
strcpy(val, paper_size_detect);
|
|
}
|
|
else
|
|
{
|
|
strcpy(val, paper_size_custom);
|
|
}
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PAPER_ORIENTATION:
|
|
switch (scanner->paper_orientation)
|
|
{
|
|
case WD_paper_PORTRAIT:
|
|
strcpy (val, paper_orientation_portrait);
|
|
break;
|
|
case WD_paper_LANDSCAPE:
|
|
strcpy (val, paper_orientation_landscape);
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PAPER_WIDTH:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->paper_width_X));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PAPER_HEIGHT:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->paper_length_Y));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_START_BUTTON:
|
|
if ( SANE_STATUS_GOOD == m3096g_get_hardware_status(scanner) )
|
|
{
|
|
*(SANE_Bool *) val = get_HW_start_button(scanner->buffer);
|
|
}
|
|
else
|
|
{
|
|
*(SANE_Bool *) val = SANE_FALSE;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
else if (action == SANE_ACTION_SET_VALUE)
|
|
{
|
|
DBG (10, "sane_control_option: set value \"%s\"\n",
|
|
scanner->opt[option].name);
|
|
|
|
if (!SANE_OPTION_IS_ACTIVE (cap))
|
|
{
|
|
DBG (10, "\tinactive\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (!SANE_OPTION_IS_SETTABLE (cap))
|
|
{
|
|
DBG (10, "\tnot settable\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
status = sanei_constrain_value (scanner->opt + option, val, info);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (10, "\tbad value\n");
|
|
return status;
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
|
|
case OPT_NUM_OPTS:
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_SOURCE:
|
|
if (strcmp (val, "ADF") == 0)
|
|
{
|
|
if (scanner->use_adf == SANE_TRUE)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->use_adf = SANE_TRUE;
|
|
if (scanner->is_duplex)
|
|
scanner->opt[OPT_DUPLEX].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_PAPER_SIZE].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
}
|
|
else if (strcmp (val, "FB") == 0)
|
|
{
|
|
if (scanner->use_adf == SANE_FALSE)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->use_adf = SANE_FALSE;
|
|
scanner->opt[OPT_DUPLEX].cap = SANE_CAP_INACTIVE;
|
|
scanner->duplex = 0;
|
|
scanner->opt[OPT_PAPER_SIZE].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_INACTIVE;
|
|
}
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_PARAMS |SANE_INFO_RELOAD_OPTIONS;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MODE:
|
|
if (strcmp (val, lineStr) == 0)
|
|
{
|
|
if (scanner->composition == WD_comp_LA)
|
|
return SANE_STATUS_GOOD;
|
|
scanner->composition = WD_comp_LA;
|
|
scanner->bitsperpixel = 1;
|
|
scanner->threshold = 0;
|
|
scanner->opt[OPT_X_RES].constraint.word_list = scanner->x_res_list;
|
|
scanner->opt[OPT_Y_RES].constraint.word_list = scanner->y_res_list;
|
|
scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
if (scanner->cmp2)
|
|
scanner->opt[OPT_COMPRESSION].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;;
|
|
scanner->opt[OPT_GAMMA].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;;
|
|
|
|
if (!m3096g_valid_number (scanner->x_res,
|
|
scanner->opt[OPT_X_RES].constraint.word_list))
|
|
{
|
|
scanner->x_res = 240;
|
|
}
|
|
if (!m3096g_valid_number (scanner->y_res,
|
|
scanner->opt[OPT_Y_RES].constraint.word_list))
|
|
{
|
|
scanner->y_res = 240;
|
|
}
|
|
|
|
scanner->rif = 0;
|
|
}
|
|
else if (strcmp (val, halfStr) == 0)
|
|
{
|
|
if (scanner->composition == WD_comp_HT)
|
|
return SANE_STATUS_GOOD;
|
|
scanner->composition = WD_comp_HT;
|
|
scanner->bitsperpixel = 1;
|
|
scanner->opt[OPT_X_RES].constraint.word_list = scanner->x_res_list;
|
|
scanner->opt[OPT_Y_RES].constraint.word_list = scanner->y_res_list;
|
|
scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
if (scanner->cmp2)
|
|
scanner->opt[OPT_COMPRESSION].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;;
|
|
scanner->opt[OPT_GAMMA].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;;
|
|
if (!m3096g_valid_number (scanner->x_res,
|
|
scanner->opt[OPT_X_RES].constraint.word_list))
|
|
{
|
|
scanner->x_res = 200;
|
|
}
|
|
if (!m3096g_valid_number (scanner->y_res,
|
|
scanner->opt[OPT_Y_RES].constraint.word_list))
|
|
{
|
|
scanner->y_res = 200;
|
|
}
|
|
scanner->rif = 0;
|
|
}
|
|
else if (strcmp (val, grayStr) == 0)
|
|
{
|
|
if (scanner->composition == WD_comp_GS)
|
|
return SANE_STATUS_GOOD;
|
|
scanner->composition = WD_comp_GS;
|
|
scanner->bitsperpixel = 8;
|
|
scanner->compress_type = WD_cmp_NONE;
|
|
scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_X_RES].constraint.word_list = scanner->x_res_list_grey;
|
|
scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_Y_RES].constraint.word_list = scanner->y_res_list_grey;
|
|
scanner->opt[OPT_COMPRESSION].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_CONTRAST].cap = SANE_CAP_INACTIVE;
|
|
if (!m3096g_valid_number (scanner->x_res,
|
|
scanner->opt[OPT_X_RES].constraint.word_list))
|
|
{
|
|
scanner->x_res = 400;
|
|
}
|
|
if (!m3096g_valid_number (scanner->y_res,
|
|
scanner->opt[OPT_Y_RES].constraint.word_list))
|
|
{
|
|
scanner->y_res = 400;
|
|
}
|
|
scanner->rif = 1;
|
|
}
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TYPE:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
case OPT_PRESCAN:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
case OPT_X_RES:
|
|
scanner->x_res = (*(SANE_Word *) val);
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_Y_RES:
|
|
scanner->y_res = (*(SANE_Word *) val);
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
#ifdef no_preview_res
|
|
case OPT_PREVIEW_RES:
|
|
return SANE_STATUS_INVAL;
|
|
#endif
|
|
|
|
case OPT_TL_X:
|
|
scanner->tl_x = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_x));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TL_Y:
|
|
scanner->tl_y = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tl_y));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_X:
|
|
scanner->br_x = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_x));
|
|
/*scanner->paper_width_X = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));*/
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_Y:
|
|
scanner->br_y = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->br_y));
|
|
/*scanner->paper_length_Y = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));*/
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AVERAGING:
|
|
return SANE_STATUS_INVAL;
|
|
|
|
case OPT_MIRROR_IMAGE:
|
|
scanner->mirroring = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_NOISE_REMOVAL:
|
|
|
|
if (scanner->noise_removal == (*(SANE_Bool *) val) )
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
scanner->noise_removal = *(SANE_Bool *) val;
|
|
|
|
if (!scanner->noise_removal)
|
|
{
|
|
scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_INACTIVE;
|
|
}
|
|
else
|
|
{
|
|
|
|
scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
}
|
|
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX2X2:
|
|
scanner->matrix2x2 = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX3X3:
|
|
scanner->matrix3x3 = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX4X4:
|
|
scanner->matrix4x4 = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MATRIX5X5:
|
|
scanner->matrix5x5 = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AUTOSEP:
|
|
scanner->auto_sep = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_RIF:
|
|
scanner->rif = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_OUTLINE_EXTRACTION:
|
|
scanner->outline = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_COMPRESSION:
|
|
if (strcmp (val, cmp_none) == 0)
|
|
scanner->compress_type = WD_cmp_NONE;
|
|
else if (strcmp (val, cmp_mh) == 0)
|
|
scanner->compress_type = WD_cmp_MH;
|
|
else if (strcmp (val, cmp_mr) == 0)
|
|
scanner->compress_type = WD_cmp_MR;
|
|
else if (strcmp (val, cmp_mmr) == 0)
|
|
scanner->compress_type = WD_cmp_MMR;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_EMPHASIS:
|
|
if (strcmp (val, emphasis_none) == 0)
|
|
scanner->emphasis = WD_emphasis_NONE;
|
|
else if (strcmp (val, emphasis_low) == 0)
|
|
scanner->emphasis = WD_emphasis_LOW;
|
|
else if (strcmp (val, emphasis_medium) == 0)
|
|
scanner->emphasis = WD_emphasis_MEDIUM;
|
|
else if (strcmp (val, emphasis_high) == 0)
|
|
scanner->emphasis = WD_emphasis_HIGH;
|
|
else if (strcmp (val, emphasis_smooth) == 0)
|
|
scanner->emphasis = WD_emphasis_SMOOTH;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA:
|
|
if (strcmp (val, gamma_default) == 0)
|
|
scanner->gamma = WD_gamma_DEFAULT;
|
|
else if (strcmp (val, gamma_normal) == 0)
|
|
scanner->gamma = WD_gamma_NORMAL;
|
|
else if (strcmp (val, gamma_soft) == 0)
|
|
scanner->gamma = WD_gamma_SOFT;
|
|
else if (strcmp (val, gamma_sharp) == 0)
|
|
scanner->gamma = WD_gamma_SHARP;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_THRESHOLD_CURVE:
|
|
scanner->dtc_threshold_curve = *(SANE_Word *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GRADIATION:
|
|
if (strcmp (val, gradiation_ordinary) == 0)
|
|
scanner->gradiation = WD_gradiation_ORDINARY;
|
|
else if (strcmp (val, gradiation_high) == 0)
|
|
scanner->gradiation = WD_gradiation_HIGH;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_SMOOTHING_MODE:
|
|
if (strcmp (val, smoothing_mode_ocr) == 0)
|
|
scanner->smoothing_mode = WD_smoothing_OCR;
|
|
else if (strcmp (val, smoothing_mode_image) == 0)
|
|
scanner->smoothing_mode = WD_smoothing_IMAGE;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_FILTERING:
|
|
if (strcmp (val, filtering_ballpoint) == 0)
|
|
scanner->filtering = WD_filtering_BALLPOINT;
|
|
else if (strcmp (val, filtering_ordinary) == 0)
|
|
scanner->filtering = WD_filtering_ORDINARY;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BACKGROUND:
|
|
if (strcmp (val, background_white) == 0)
|
|
scanner->background = WD_background_WHITE;
|
|
else if (strcmp (val, background_black) == 0)
|
|
scanner->background = WD_background_BLACK;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_WHITE_LEVEL_FOLLOW:
|
|
if (strcmp (val, white_level_follow_default) == 0)
|
|
scanner->white_level_follow = WD_white_level_follow_DEFAULT;
|
|
else if (strcmp (val, white_level_follow_enabled) == 0)
|
|
scanner->white_level_follow = WD_white_level_follow_ENABLED;
|
|
else if (strcmp (val, white_level_follow_disabled) == 0)
|
|
scanner->white_level_follow = WD_white_level_follow_DISABLED;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_DTC_SELECTION:
|
|
if (strcmp (val, dtc_selection_default) == 0)
|
|
{
|
|
if (scanner->dtc_selection == WD_dtc_selection_DEFAULT)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->dtc_selection = WD_dtc_selection_DEFAULT;
|
|
}
|
|
else if (strcmp (val, dtc_selection_dynamic) == 0)
|
|
{
|
|
if (scanner->dtc_selection == WD_dtc_selection_DYNAMIC)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->dtc_selection = WD_dtc_selection_DYNAMIC;
|
|
}
|
|
else if (strcmp (val, dtc_selection_simplified) == 0)
|
|
{
|
|
if (scanner->dtc_selection == WD_dtc_selection_SIMPLIFIED )
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->dtc_selection = WD_dtc_selection_SIMPLIFIED;
|
|
}
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (scanner->dtc_selection != WD_dtc_selection_DYNAMIC)
|
|
{
|
|
scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_BACKGROUND].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_FILTERING].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GRADIATION].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_INACTIVE;
|
|
|
|
scanner->noise_removal = 0;
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
else
|
|
{
|
|
scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_BACKGROUND].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_FILTERING].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_GRADIATION].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
}
|
|
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PAPER_SIZE:
|
|
|
|
|
|
if (strcmp (val, paper_size_a3) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_A3)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_A3;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_a4) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_A4)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_A4;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_a5) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_A5)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_A5;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_double) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_DOUBLE)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_DOUBLE;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_letter) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_LETTER)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_LETTER;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_b4) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_B4)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_B4;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_b5) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_B5)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_B5;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_legal) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_LEGAL)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_LEGAL;
|
|
m3096g_set_standard_size(scanner);
|
|
}
|
|
else if (strcmp (val, paper_size_custom) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_UNDEFINED &&
|
|
scanner->paper_selection == WD_paper_SEL_NON_STANDARD)
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
scanner->paper_size = WD_paper_UNDEFINED;
|
|
scanner->paper_selection = WD_paper_SEL_NON_STANDARD;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_PAPER_WIDTH].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
scanner->opt[OPT_PAPER_HEIGHT].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
else if (strcmp (val, paper_size_detect) == 0)
|
|
{
|
|
if (scanner->paper_size == WD_paper_UNDEFINED &&
|
|
scanner->paper_selection == WD_paper_SEL_UNDEFINED)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
scanner->paper_size = WD_paper_UNDEFINED;
|
|
scanner->paper_orientation = WD_paper_PORTRAIT;
|
|
scanner->paper_selection = WD_paper_SEL_UNDEFINED;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_PAPER_WIDTH].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_PAPER_HEIGHT].cap = SANE_CAP_INACTIVE;
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
case OPT_PAPER_ORIENTATION:
|
|
if (strcmp (val, paper_orientation_portrait) == 0)
|
|
scanner->paper_orientation = WD_paper_PORTRAIT;
|
|
else if (strcmp (val, paper_orientation_landscape) == 0)
|
|
scanner->paper_orientation = WD_paper_LANDSCAPE;
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PAPER_WIDTH:
|
|
scanner->paper_width_X = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->paper_width_X));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PAPER_HEIGHT:
|
|
scanner->paper_length_Y = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->paper_length_Y));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BRIGHTNESS:
|
|
scanner->brightness = *(SANE_Word *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_CONTRAST:
|
|
scanner->contrast = *(SANE_Word *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_VARIANCE_RATE:
|
|
scanner->var_rate_dyn_thresh = *(SANE_Word *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_THRESHOLD:
|
|
scanner->threshold = *(SANE_Word *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_DUPLEX:
|
|
scanner->duplex = *(SANE_Bool *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
} /* switch */
|
|
} /* else */
|
|
return SANE_STATUS_INVAL;
|
|
} /* sane_control_option */
|
|
|
|
|
|
#if 0
|
|
static int m3096g_valid_x_resolution(SANE_Handle handle)
|
|
{
|
|
struct m3096g *s = handle;
|
|
|
|
DBG (5, "m3096g_valid_x_resolution\n");
|
|
if (s->x_res_range.quant>0 && 0)
|
|
{
|
|
if (s->x_res >= s->x_res_range.min &&
|
|
s->x_res <= s->x_res_range.max)
|
|
{
|
|
if ((s->x_res - s->x_res_range.min) % s->x_res_range.quant == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return m3096g_valid_number(s->x_res, s->x_res_list);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int m3096g_valid_y_resolution(SANE_Handle handle)
|
|
{
|
|
struct m3096g *s = handle;
|
|
|
|
if (s->y_res_range.quant>0 && 0)
|
|
{
|
|
if (s->y_res >= s->y_res_range.min &&
|
|
s->y_res <= s->y_res_range.max)
|
|
{
|
|
if ((s->y_res - s->y_res_range.min) % s->y_res_range.quant == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return m3096g_valid_number(s->y_res, s->y_res_list);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void m3096g_set_binary_res(SANE_Handle handle)
|
|
{
|
|
struct m3096g *scanner = handle;
|
|
|
|
if (scanner->x_res_range.quant>0)
|
|
{
|
|
scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_X_RES].constraint.range = &scanner->x_res_range;
|
|
}
|
|
else
|
|
{
|
|
scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_X_RES].constraint.word_list = scanner->x_res_list;
|
|
}
|
|
|
|
if (scanner->y_res_range.quant>0)
|
|
{
|
|
scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_Y_RES].constraint.range = &scanner->y_res_range;
|
|
}
|
|
else
|
|
{
|
|
scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_Y_RES].constraint.word_list = scanner->y_res_list;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static void m3096g_set_standard_size(SANE_Handle handle)
|
|
{
|
|
struct m3096g *scanner = handle;
|
|
|
|
scanner->paper_selection = WD_paper_SEL_STANDARD;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_SOFT_DETECT
|
|
| SANE_CAP_SOFT_SELECT;
|
|
scanner->opt[OPT_PAPER_WIDTH].cap = SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_PAPER_HEIGHT].cap = SANE_CAP_INACTIVE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_start (SANE_Handle handle)
|
|
{
|
|
struct m3096g *scanner = handle;
|
|
int fds[2];
|
|
int ret;
|
|
int i_window_id;
|
|
|
|
DBG (10, "sane_start\n");
|
|
|
|
/*
|
|
if (scanner->scanning == SANE_TRUE)
|
|
{
|
|
DBG (5, "sane_start: device busy\n");
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
*/
|
|
|
|
if (scanner->sfd < 0)
|
|
{ /* first call */
|
|
if (sanei_scsi_open (scanner->sane.name, &(scanner->sfd),
|
|
sense_handler, 0) != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "sane_start: open of %s failed:\n",
|
|
scanner->sane.name);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
scanner->scanning = SANE_TRUE;
|
|
|
|
|
|
if ((ret = m3096g_check_values (scanner)) != 0)
|
|
{ /* Verify values */
|
|
DBG (1, "sane_start: ERROR: invalid scan-values\n");
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->scanning = SANE_FALSE;
|
|
scanner->sfd = -1;
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if ((ret = m3096g_grab_scanner (scanner)))
|
|
{
|
|
DBG (5, "sane_start: unable to reserve scanner\n");
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->scanning = SANE_FALSE;
|
|
scanner->sfd = -1;
|
|
return ret;
|
|
}
|
|
|
|
if (scanner->use_adf == SANE_TRUE
|
|
&& (ret = m3096g_object_position (scanner)))
|
|
{
|
|
DBG (5, "sane_start: WARNING: ADF empty\n");
|
|
m3096g_free_scanner (scanner);
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->scanning = SANE_FALSE;
|
|
scanner->sfd = -1;
|
|
return ret;
|
|
}
|
|
|
|
swap_res (scanner);
|
|
|
|
if ((ret = m3096g_set_window_param (scanner, 0)))
|
|
{
|
|
DBG (5, "sane_start: ERROR: failed to set window\n");
|
|
m3096g_free_scanner (scanner);
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->scanning = SANE_FALSE;
|
|
scanner->sfd = -1;
|
|
return ret;
|
|
}
|
|
|
|
|
|
DBG (10, "\tbytes per line = %d\n", bytes_per_line (scanner));
|
|
DBG (10, "\tpixels_per_line = %d\n", pixels_per_line (scanner));
|
|
DBG (10, "\tlines = %d\n", lines_per_scan (scanner));
|
|
DBG (10, "\tbrightness (halftone) = %d\n", scanner->brightness);
|
|
DBG (10, "\tthreshold (line art) = %d\n", scanner->threshold);
|
|
|
|
/*
|
|
if (( ret = m3096g_read_pixel_size(scanner, 28, 0)))
|
|
{
|
|
DBG (5, "sane_start: unable to read pixel size\n");
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->scanning = SANE_FALSE;
|
|
scanner->sfd = -1;
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
DBG(1, "numx: %d\n", get_PSIZE_num_x(scanner->buffer));
|
|
DBG(1, "numy: %d\n", get_PSIZE_num_y(scanner->buffer));
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
if ((ret = m3096g_start_scan (scanner)))
|
|
{
|
|
DBG (5, "sane_start: unable to set reading method\n");
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->scanning = SANE_FALSE;
|
|
scanner->sfd = -1;
|
|
return ret;
|
|
}
|
|
|
|
|
|
i_window_id = 0x00;
|
|
}
|
|
else
|
|
{
|
|
i_window_id = 0x80;
|
|
}
|
|
|
|
/* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
|
|
if (pipe (fds) < 0)
|
|
{
|
|
DBG (1, "ERROR: could not create pipe\n");
|
|
swap_res (scanner);
|
|
scanner->scanning = SANE_FALSE;
|
|
m3096g_free_scanner (scanner);
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
scanner->reader_pid = fork ();
|
|
if (scanner->reader_pid == 0)
|
|
{
|
|
/* reader_pid = 0 ===> child process */
|
|
sigset_t ignore_set;
|
|
struct SIGACTION act;
|
|
|
|
close (fds[0]);
|
|
|
|
sigfillset (&ignore_set);
|
|
sigdelset (&ignore_set, SIGTERM);
|
|
sigprocmask (SIG_SETMASK, &ignore_set, 0);
|
|
|
|
memset (&act, 0, sizeof (act));
|
|
sigaction (SIGTERM, &act, 0);
|
|
|
|
/* don't use exit() since that would run the atexit() handlers... */
|
|
_exit (reader_process (scanner, fds[1], i_window_id));
|
|
}
|
|
close (fds[1]);
|
|
scanner->pipe = fds[0];
|
|
|
|
DBG (10, "sane_start: ok\n");
|
|
return SANE_STATUS_GOOD;
|
|
} /* sane_start */
|
|
|
|
|
|
SANE_Status
|
|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
|
|
{
|
|
struct m3096g *scanner = handle;
|
|
|
|
DBG (10, "sane_get_parameters\n");
|
|
params->format = SANE_FRAME_GRAY;
|
|
|
|
params->depth = scanner->bitsperpixel;
|
|
params->pixels_per_line = pixels_per_line (scanner);
|
|
params->lines = lines_per_scan (scanner);
|
|
params->bytes_per_line = bytes_per_line (scanner);
|
|
if ( scanner->i_num_frames > 1 )
|
|
{
|
|
params->last_frame = 0;
|
|
}
|
|
else
|
|
{
|
|
params->last_frame = 1;
|
|
}
|
|
DBG (10, "\tdepth %d\n", params->depth);
|
|
DBG (10, "\tlines %d\n", params->lines);
|
|
DBG (10, "\tpixels_per_line %d\n", params->pixels_per_line);
|
|
DBG (10, "\tbytes_per_line %d\n", params->bytes_per_line);
|
|
return SANE_STATUS_GOOD;
|
|
} /* sane_get_parameters */
|
|
|
|
|
|
SANE_Status
|
|
sane_read (SANE_Handle handle, SANE_Byte * buf,
|
|
SANE_Int max_len, SANE_Int * len)
|
|
{
|
|
struct m3096g *scanner = handle;
|
|
ssize_t nread;
|
|
|
|
DBG (10, "sane_read\n");
|
|
*len = 0;
|
|
|
|
scanner->i_num_frames = scanner->i_num_frames - 1;
|
|
|
|
nread = read (scanner->pipe, buf, max_len);
|
|
DBG (10, "sane_read: read %ld bytes of %ld\n",
|
|
(long) nread, (long) max_len);
|
|
|
|
if (scanner->scanning == SANE_FALSE)
|
|
{
|
|
/* PREDICATE WAS (!(scanner->scanning)) */
|
|
return do_cancel (scanner);
|
|
}
|
|
|
|
if (nread < 0)
|
|
{
|
|
if (errno == EAGAIN)
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
else
|
|
{
|
|
do_cancel (scanner);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
}
|
|
|
|
*len = nread;
|
|
|
|
if (nread == 0)
|
|
return do_eof (scanner); /* close pipe */
|
|
|
|
return SANE_STATUS_GOOD;
|
|
} /* sane_read */
|
|
|
|
|
|
void
|
|
sane_cancel (SANE_Handle h)
|
|
{
|
|
DBG (10, "sane_cancel\n");
|
|
do_cancel ((struct m3096g *) h);
|
|
} /* sane_cancel */
|
|
|
|
|
|
void
|
|
sane_close (SANE_Handle handle)
|
|
{
|
|
DBG (10, "sane_close\n");
|
|
if (((struct m3096g *) handle)->scanning == SANE_TRUE)
|
|
do_cancel (handle);
|
|
} /* sane_close */
|
|
|
|
|
|
void
|
|
sane_exit (void)
|
|
{
|
|
struct m3096g *dev, *next;
|
|
|
|
DBG (10, "sane_exit\n");
|
|
|
|
for (dev = first_dev; dev; dev = next)
|
|
{
|
|
next = dev->next;
|
|
free (dev->devicename);
|
|
free (dev->buffer);
|
|
free (dev);
|
|
}
|
|
} /* sane_exit */
|
|
|
|
/* }################ internal (support) routines ################{ */
|
|
|
|
static SANE_Status
|
|
attach_scanner (const char *devicename, struct m3096g **devp)
|
|
{
|
|
struct m3096g *dev;
|
|
int sfd;
|
|
|
|
DBG (15, "attach_scanner: %s\n", devicename);
|
|
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
{
|
|
if (strcmp (dev->sane.name, devicename) == 0)
|
|
{
|
|
if (devp)
|
|
{
|
|
*devp = dev;
|
|
}
|
|
DBG (5, "attach_scanner: scanner already attached (is ok)!\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
|
|
DBG (15, "attach_scanner: opening %s\n", devicename);
|
|
if (sanei_scsi_open (devicename, &sfd, sense_handler, 0) != 0)
|
|
{
|
|
DBG (5, "attach_scanner: open failed\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (NULL == (dev = malloc (sizeof (*dev))))
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
dev->row_bufsize = (sanei_scsi_max_request_size < (64 * 1024))
|
|
? sanei_scsi_max_request_size
|
|
: 64 * 1024;
|
|
|
|
if ((dev->buffer = malloc (dev->row_bufsize)) == NULL)
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
dev->devicename = strdup (devicename);
|
|
dev->sfd = sfd;
|
|
|
|
if (m3096g_identify_scanner (dev) != 0)
|
|
{
|
|
DBG (5, "attach_scanner: scanner-identification failed\n");
|
|
sanei_scsi_close (dev->sfd);
|
|
free (dev->buffer);
|
|
free (dev);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
#if 0
|
|
/* Get MUD (via mode_sense), internal info (via get_internal_info), and
|
|
* initialize values */
|
|
coolscan_initialize_values (dev);
|
|
#endif
|
|
|
|
/* Why? */
|
|
sanei_scsi_close (dev->sfd);
|
|
dev->sfd = -1;
|
|
|
|
dev->sane.name = dev->devicename;
|
|
dev->sane.vendor = dev->vendor;
|
|
dev->sane.model = dev->product;
|
|
dev->sane.type = "scanner";
|
|
|
|
#if 0
|
|
dev->x_range.min = SANE_FIX (0);
|
|
dev->x_range.quant = SANE_FIX (length_quant);
|
|
dev->x_range.max = SANE_FIX ((double) ((dev->xmaxpix) * length_quant));
|
|
|
|
dev->y_range.min = SANE_FIX (0.0);
|
|
dev->y_range.quant = SANE_FIX (length_quant);
|
|
dev->y_range.max = SANE_FIX ((double) ((dev->ymaxpix) * length_quant));
|
|
|
|
/* ...and this?? */
|
|
dev->dpi_range.min = SANE_FIX (108);
|
|
dev->dpi_range.quant = SANE_FIX (0);
|
|
dev->dpi_range.max = SANE_FIX (dev->maxres);
|
|
DBG (15, "attach: dev->dpi_range.max = %f\n",
|
|
SANE_UNFIX (dev->dpi_range.max));
|
|
#endif
|
|
|
|
++num_devices;
|
|
dev->next = first_dev;
|
|
first_dev = dev;
|
|
|
|
if (devp)
|
|
{
|
|
*devp = dev;
|
|
}
|
|
|
|
DBG (15, "attach_scanner: done\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
} /* attach_scanner */
|
|
|
|
static SANE_Status
|
|
attach_one (const char *name)
|
|
{
|
|
return attach_scanner (name, 0);
|
|
} /* attach_one */
|
|
|
|
static SANE_Status
|
|
sense_handler (int scsi_fd, u_char * result, void *arg)
|
|
{
|
|
DBG (99, "%d %s\n", scsi_fd, arg); /* avoid compiler warning */
|
|
|
|
return request_sense_parse (result);
|
|
} /* sense_handler */
|
|
|
|
static int
|
|
request_sense_parse (u_char * sensed_data)
|
|
{
|
|
unsigned int ret, sense, asc, ascq;
|
|
|
|
sense = get_RS_sense_key (sensed_data);
|
|
asc = get_RS_ASC (sensed_data);
|
|
ascq = get_RS_ASCQ (sensed_data);
|
|
|
|
ret = SANE_STATUS_IO_ERROR;
|
|
|
|
switch (sense)
|
|
{
|
|
case 0x0: /* No Sense */
|
|
DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq);
|
|
if ( get_RS_EOM(sensed_data) ) {
|
|
|
|
current_scanner->i_transfer_length = get_RS_information(sensed_data);
|
|
|
|
return SANE_STATUS_EOF;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case 0x2: /* Not Ready */
|
|
if ((0x00 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Not Ready \n", sense, asc, ascq);
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
}
|
|
break;
|
|
|
|
case 0x3: /* Medium Error */
|
|
if ((0x80 == asc) && (0x01 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Jam \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_JAMMED;
|
|
}
|
|
else if ((0x80 == asc) && (0x02 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: ADF cover open \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_COVER_OPEN;
|
|
}
|
|
else if ((0x80 == asc) && (0x03 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: ADF empty \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_NO_DOCS;
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
}
|
|
break;
|
|
|
|
case 0x4: /* Hardware Error */
|
|
if ((0x80 == asc) && (0x01 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: FB motor fuse \n", sense, asc, ascq);
|
|
}
|
|
else if ((0x80 == asc) && (0x02 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: heater fuse \n", sense, asc, ascq);
|
|
}
|
|
else if ((0x80 == asc) && (0x04 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: ADF motor fuse \n", sense, asc, ascq);
|
|
}
|
|
else if ((0x80 == asc) && (0x05 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: mechanical alarm \n", sense, asc, ascq);
|
|
}
|
|
else if ((0x80 == asc) && (0x06 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: optical alarm \n", sense, asc, ascq);
|
|
}
|
|
else if ((0x44 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: abnormal internal target \n", sense, asc, ascq);
|
|
}
|
|
else if ((0x47 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: SCSI parity error \n", sense, asc, ascq);
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
}
|
|
break;
|
|
|
|
case 0x5: /* Illegal Request */
|
|
if ((0x20 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Invalid command \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_INVAL;
|
|
}
|
|
else if ((0x24 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Invalid field in CDB \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_INVAL;
|
|
}
|
|
else if ((0x25 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Unsupported logical unit \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
else if ((0x26 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Invalid field in parm list \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_INVAL;
|
|
}
|
|
else if ((0x2C == asc) && (0x02 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: wrong window combination \n", sense, asc, ascq);
|
|
ret = SANE_STATUS_INVAL;
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
}
|
|
break;
|
|
|
|
case 0x6: /* Unit Attention */
|
|
if ((0x00 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: UNIT ATTENTION \n", sense, asc, ascq);
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
}
|
|
break;
|
|
|
|
case 0xb: /* Aborted Command */
|
|
if ((0x43 == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Message error \n", sense, asc, ascq);
|
|
}
|
|
else if ((0x80 == asc) && (0x01 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: Image transfer error \n", sense, asc, ascq);
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
}
|
|
return ret;
|
|
} /* request_sense_parse */
|
|
|
|
static int
|
|
m3096g_identify_scanner (struct m3096g *s)
|
|
{
|
|
char vendor[9];
|
|
char product[0x11];
|
|
char version[5];
|
|
char *pp;
|
|
int i;
|
|
|
|
DBG (10, "identify_scanner\n");
|
|
|
|
vendor[8] = product[0x10] = version[4] = 0;
|
|
|
|
|
|
m3096g_do_inquiry (s); /* get inquiry */
|
|
if (get_IN_periph_devtype (s->buffer) != IN_periph_devtype_scanner)
|
|
{
|
|
DBG (5, "identify_scanner: not a scanner\n");
|
|
return 1;
|
|
}
|
|
|
|
get_IN_vendor (s->buffer, vendor);
|
|
get_IN_product (s->buffer, product);
|
|
get_IN_version (s->buffer, version);
|
|
|
|
if (strncmp ("FUJITSU ", vendor, 8))
|
|
{
|
|
DBG (5, "identify_scanner: \"%s\" isn't a Fujitsu product\n", vendor);
|
|
return 1;
|
|
}
|
|
|
|
pp = &vendor[8];
|
|
vendor[8] = ' ';
|
|
while (*pp == ' ')
|
|
{
|
|
*pp-- = '\0';
|
|
}
|
|
|
|
pp = &product[0x10];
|
|
product[0x10] = ' ';
|
|
while (*(pp - 1) == ' ')
|
|
{
|
|
*pp-- = '\0';
|
|
} /* leave one blank at the end! */
|
|
|
|
pp = &version[4];
|
|
version[4] = ' ';
|
|
while (*pp == ' ')
|
|
{
|
|
*pp-- = '\0';
|
|
}
|
|
|
|
DBG (10, "Found %s scanner %s version %s on device %s\n",
|
|
vendor, product, version, s->devicename);
|
|
|
|
vendor[8] = '\0';
|
|
product[16] = '\0';
|
|
version[4] = '\0';
|
|
|
|
strncpy (s->vendor, vendor, 9);
|
|
strncpy (s->product, product, 17);
|
|
strncpy (s->version, version, 5);
|
|
|
|
|
|
s->autofeeder = 1; /* inferred by product title M3096G */
|
|
s->flatbed = 1;
|
|
s->is_duplex = 0;
|
|
|
|
s->has_hw_status = 0;
|
|
s->has_outline = 0;
|
|
s->has_emphasis = 0;
|
|
s->has_autosep = 0;
|
|
s->has_mirroring = 0;
|
|
s->has_white_level_follow = 1;
|
|
s->has_subwindow = 1;
|
|
|
|
s->ipc = 0;
|
|
s->cmp2 = 0;
|
|
s->x_res_list[0] = 0;
|
|
s->y_res_list[0] = 0;
|
|
|
|
s->x_res_range.min = s->y_res_range.min = 50;
|
|
s->x_res_range.quant = s->y_res_range.quant = 0;
|
|
s->x_grey_res_range.min = s->y_grey_res_range.min = 50;
|
|
s->x_grey_res_range.quant = s->y_grey_res_range.quant = 0;
|
|
|
|
|
|
/* examples of product names: 3093GXim, 3097Gim, 3093GXm, ...
|
|
* i == ipc present
|
|
* m == cmp2 present
|
|
*/
|
|
for ( i = 5; i < 16; i++ )
|
|
{
|
|
if (product[i]=='i')
|
|
{
|
|
s->ipc = 1;
|
|
}
|
|
else if (product[i]=='m')
|
|
{
|
|
s->cmp2 = 1;
|
|
}
|
|
else if (product[i]=='d')
|
|
{
|
|
s->is_duplex = 1;
|
|
}
|
|
|
|
}
|
|
|
|
if (s->ipc)
|
|
{
|
|
s->has_outline = 1;
|
|
s->has_emphasis = 1;
|
|
s->has_autosep = 1;
|
|
s->has_mirroring = 1;
|
|
}
|
|
|
|
/* initialize dpi-lists */
|
|
if (!strncmp(product, "M3093DG", 6) ||
|
|
!strncmp(product, "M4097D", 5) )
|
|
{
|
|
for ( i = 0; i <= res_list3[0]; i++ )
|
|
{
|
|
s->y_res_list[i] = s->x_res_list[i] = res_list3[i];
|
|
}
|
|
for ( i = 0; i <= res_list2[0]; i++ )
|
|
{
|
|
s->x_res_list_grey[i] = s->y_res_list_grey[i] = res_list2[i];
|
|
}
|
|
|
|
if (s->cmp2)
|
|
{
|
|
s->x_res_range.max = s->y_res_range.max = 800;
|
|
s->x_res_range.quant = s->y_res_range.quant = 1;
|
|
s->x_grey_res_range.max = s->y_grey_res_range.max = 400;
|
|
s->x_grey_res_range.quant = s->y_grey_res_range.quant = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* M3093GX/M3096GX */
|
|
|
|
for ( i = 0; i <= res_list1[0]; i++ )
|
|
{
|
|
s->y_res_list[i] = s->x_res_list[i] = res_list1[i];
|
|
}
|
|
for ( i = 0; i <= res_list0[0]; i++ )
|
|
{
|
|
s->x_res_list_grey[i] = s->y_res_list_grey[i] = res_list0[i];
|
|
}
|
|
|
|
|
|
if (s->cmp2)
|
|
{
|
|
s->x_res_range.max = s->y_res_range.max = 400;
|
|
s->x_res_range.quant = s->y_res_range.quant = 1;
|
|
}
|
|
}
|
|
|
|
/* initialize scanning- and adf paper size*/
|
|
|
|
s->x_range.min = SANE_FIX(0);
|
|
s->x_range.quant = SANE_FIX(length_quant);
|
|
|
|
s->y_range.min = SANE_FIX(0);
|
|
s->y_range.quant = SANE_FIX(length_quant);
|
|
|
|
s->adf_width_range.min = SANE_FIX(0);
|
|
s->adf_width_range.quant = SANE_FIX(length_quant);
|
|
|
|
s->adf_height_range.min = SANE_FIX(0);
|
|
s->adf_height_range.quant = SANE_FIX(length_quant);
|
|
|
|
if ( 0 == strncmp(product, "M3093", 5) )
|
|
{
|
|
/* A4 scanner */
|
|
if ( 0 == strncmp(product, "M3093DG", 7) )
|
|
{
|
|
s->x_range.max = SANE_FIX(219);
|
|
s->y_range.max = SANE_FIX(308);
|
|
s->adf_width_range.max = SANE_FIX(219);
|
|
s->adf_height_range.max = SANE_FIX(308);
|
|
}
|
|
else
|
|
{
|
|
s->x_range.max = SANE_FIX(215);
|
|
s->y_range.max = SANE_FIX(308);
|
|
s->adf_width_range.max = SANE_FIX(215);
|
|
s->adf_height_range.max = SANE_FIX(308);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* A3 scanner */
|
|
s->x_range.max = SANE_FIX(308);
|
|
s->y_range.max = SANE_FIX(438);
|
|
s->adf_width_range.max = SANE_FIX(308);
|
|
s->adf_height_range.max = SANE_FIX(438);
|
|
}
|
|
|
|
|
|
/* Here's where to add code to fetch the "vital product data"!!! */
|
|
if ( SANE_STATUS_GOOD == m3096g_get_vital_product_data(s) )
|
|
{
|
|
/* This scanner supports vital product data.
|
|
* Use this data to set dpi-lists etc.
|
|
*/
|
|
int i_num_res;
|
|
|
|
if (get_IN_page_length(s->buffer) > 0x19)
|
|
{
|
|
/* VPD extended format present */
|
|
s->autofeeder = get_IN_adf(s->buffer);
|
|
s->is_duplex = get_IN_duplex(s->buffer);
|
|
s->flatbed = get_IN_flatbed(s->buffer);
|
|
s->has_hw_status = get_IN_has_hw_status(s->buffer);
|
|
s->has_outline = get_IN_ipc_outline_extraction(s->buffer);
|
|
s->has_emphasis = get_IN_ipc_image_emphasis(s->buffer);
|
|
s->has_autosep = get_IN_ipc_auto_separation(s->buffer);
|
|
s->has_mirroring = get_IN_ipc_mirroring(s->buffer);
|
|
s->has_white_level_follow = get_IN_ipc_white_level_follow(s->buffer);
|
|
s->has_subwindow = get_IN_ipc_subwindow(s->buffer);
|
|
}
|
|
|
|
|
|
s->x_res_range.quant = get_IN_step_x_res(s->buffer);
|
|
s->y_res_range.quant = get_IN_step_y_res(s->buffer);
|
|
s->x_res_range.max = get_IN_max_x_res(s->buffer);
|
|
s->y_res_range.max = get_IN_max_y_res(s->buffer);
|
|
s->x_res_range.min = get_IN_min_x_res(s->buffer);
|
|
s->y_res_range.min = get_IN_min_y_res(s->buffer);
|
|
|
|
i_num_res = 0;
|
|
if (get_IN_std_res_60(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 60;
|
|
}
|
|
if (get_IN_std_res_75(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 75;
|
|
}
|
|
if (get_IN_std_res_100(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 100;
|
|
}
|
|
if (get_IN_std_res_120(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 120;
|
|
}
|
|
if (get_IN_std_res_150(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 150;
|
|
}
|
|
if (get_IN_std_res_160(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 160;
|
|
}
|
|
if (get_IN_std_res_180(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 180;
|
|
}
|
|
if (get_IN_std_res_200(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 200;
|
|
}
|
|
|
|
if (get_IN_std_res_240(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 240;
|
|
}
|
|
if (get_IN_std_res_300(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 300;
|
|
}
|
|
if (get_IN_std_res_320(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 320;
|
|
}
|
|
if (get_IN_std_res_400(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 400;
|
|
}
|
|
if (get_IN_std_res_480(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 480;
|
|
}
|
|
if (get_IN_std_res_600(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 600;
|
|
}
|
|
if (get_IN_std_res_800(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 800;
|
|
}
|
|
if (get_IN_std_res_1200(s->buffer))
|
|
{
|
|
i_num_res ++;
|
|
s->x_res_list[i_num_res] = 1200;
|
|
}
|
|
|
|
s->x_res_list[0] = i_num_res;
|
|
|
|
for ( i = 0; i <= i_num_res; i++ )
|
|
{
|
|
s->y_res_list[i] = s->x_res_list[i];
|
|
/* can't get resolutions for grey scale reading ????*/
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} /* m3096g_identify_scanner */
|
|
|
|
static void
|
|
m3096g_do_inquiry (struct m3096g *s)
|
|
{
|
|
DBG (10, "do_inquiry\n");
|
|
|
|
memset (s->buffer, '\0', 256); /* clear buffer */
|
|
set_IN_return_size (inquiryB.cmd, 96);
|
|
set_IN_evpd(inquiryB.cmd, 0);
|
|
set_IN_page_code(inquiryB.cmd, 0);
|
|
|
|
do_scsi_cmd (s->sfd, inquiryB.cmd, inquiryB.size, s->buffer, 96);
|
|
} /* m3096g_do_inquiry */
|
|
|
|
|
|
static SANE_Status
|
|
m3096g_get_hardware_status(struct m3096g *s)
|
|
{
|
|
SANE_Status ret;
|
|
DBG (10, "get_hardware_status\n");
|
|
|
|
memset (s->buffer, '\0', 256); /* clear buffer */
|
|
set_HW_allocation_length (hw_statusB.cmd, 10);
|
|
|
|
if (s->sfd < 0)
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
else
|
|
{
|
|
ret = do_scsi_cmd (s->sfd, hw_statusB.cmd, hw_statusB.size, s->buffer, 10);
|
|
}
|
|
|
|
if (ret == SANE_STATUS_GOOD)
|
|
{
|
|
DBG(1, "B5 %d\n", get_HW_B5_present(s->buffer));
|
|
DBG(1, "A4 %d \n", get_HW_A4_present(s->buffer));
|
|
DBG(1, "B4 %d \n", get_HW_B4_present(s->buffer));
|
|
DBG(1, "A3 %d \n", get_HW_A3_present(s->buffer));
|
|
DBG(1, "HE %d\n", get_HW_adf_empty(s->buffer));
|
|
DBG(1, "OMR %d\n", get_HW_omr(s->buffer));
|
|
DBG(1, "ADFC %d\n", get_HW_adfc_open(s->buffer));
|
|
DBG(1, "SLEEP %d\n", get_HW_sleep(s->buffer));
|
|
DBG(1, "MF %d\n", get_HW_manual_feed(s->buffer));
|
|
DBG(1, "Start %d\n", get_HW_start_button(s->buffer));
|
|
DBG(1, "Ink empty %d\n", get_HW_ink_empty(s->buffer));
|
|
DBG(1, "DFEED %d\n", get_HW_dfeed_detected(s->buffer));
|
|
DBG(1, "SKEW %d\n", get_HW_skew_angle(s->buffer));
|
|
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static SANE_Status
|
|
m3096g_get_vital_product_data(struct m3096g *s)
|
|
{
|
|
SANE_Status ret;
|
|
|
|
DBG (10, "get_vital_product_data\n");
|
|
|
|
|
|
memset (s->buffer, '\0', 256); /* clear buffer */
|
|
set_IN_return_size (inquiryB.cmd, 0x64);
|
|
set_IN_evpd(inquiryB.cmd, 1);
|
|
set_IN_page_code(inquiryB.cmd, 0xf0);
|
|
|
|
ret = do_scsi_cmd (s->sfd, inquiryB.cmd, inquiryB.size, s->buffer, 0x64);
|
|
if (ret == SANE_STATUS_GOOD)
|
|
{
|
|
|
|
DBG(1, "basic x res: %d\n",get_IN_basic_x_res(s->buffer));
|
|
DBG(1, "basic y res %d\n", get_IN_basic_y_res(s->buffer));
|
|
DBG(1, "step x res %d\n", get_IN_step_x_res(s->buffer));
|
|
DBG(1, "step y res %d\n", get_IN_step_y_res(s->buffer));
|
|
DBG(1, "max x res %d\n", get_IN_max_x_res(s->buffer));
|
|
DBG(1, "max y res %d\n", get_IN_max_y_res(s->buffer));
|
|
DBG(1, "min x res %d\n", get_IN_min_x_res(s->buffer));
|
|
DBG(1, "max y res %d\n", get_IN_min_y_res(s->buffer));
|
|
DBG(1, "window width %d\n", get_IN_window_width(s->buffer));
|
|
DBG(1, "window length %d\n", get_IN_window_length(s->buffer));
|
|
DBG(1, "has operator panel %d\n", get_IN_operator_panel(s->buffer));
|
|
DBG(1, "has barcode %d\n", get_IN_barcode(s->buffer));
|
|
DBG(1, "has endorser %d\n", get_IN_endorser(s->buffer));
|
|
DBG(1, "is duplex %d\n", get_IN_duplex(s->buffer));
|
|
DBG(1, "has flatbed %d\n", get_IN_flatbed(s->buffer));
|
|
DBG(1, "has adf %d\n", get_IN_adf(s->buffer));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static SANE_Status
|
|
do_scsi_cmd (int fd, char *cmd, int cmd_len, char *out, size_t out_len)
|
|
{
|
|
SANE_Status ret;
|
|
size_t ol = out_len;
|
|
|
|
hexdump (20, "<cmd<", cmd, cmd_len);
|
|
|
|
ret = sanei_scsi_cmd (fd, cmd, cmd_len, out, &ol);
|
|
if ((out_len != 0) && (out_len != ol))
|
|
{
|
|
DBG (1, "sanei_scsi_cmd: asked %lu bytes, got %lu\n",
|
|
(u_long) out_len, (u_long) ol);
|
|
}
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "sanei_scsi_cmd: returning 0x%08x\n", ret);
|
|
}
|
|
DBG (10, "sanei_scsi_cmd: returning %lu bytes:\n", (u_long) ol);
|
|
if (out != NULL && out_len != 0)
|
|
hexdump (15, ">rslt>", out, (out_len > 0x60) ? 0x60 : out_len);
|
|
|
|
return ret;
|
|
} /* do_scsi_cmd */
|
|
|
|
static void
|
|
hexdump (int level, char *comment, unsigned char *p, int l)
|
|
{
|
|
int i;
|
|
char line[128];
|
|
char *ptr;
|
|
|
|
DBG (level, "%s\n", comment);
|
|
ptr = line;
|
|
for (i = 0; i < l; i++, p++)
|
|
{
|
|
if ((i % 16) == 0)
|
|
{
|
|
if (ptr != line)
|
|
{
|
|
*ptr = '\0';
|
|
DBG (level, "%s\n", line);
|
|
ptr = line;
|
|
}
|
|
sprintf (ptr, "%3.3d:", i);
|
|
ptr += 4;
|
|
}
|
|
sprintf (ptr, " %2.2x", *p);
|
|
ptr += 3;
|
|
}
|
|
*ptr = '\0';
|
|
DBG (level, "%s\n", line);
|
|
} /* hexdump */
|
|
|
|
static SANE_Status
|
|
init_options (struct m3096g *scanner)
|
|
{
|
|
int i;
|
|
|
|
DBG (10, "init_options\n");
|
|
|
|
memset (scanner->opt, 0, sizeof (scanner->opt));
|
|
|
|
for (i = 0; i < NUM_OPTIONS; ++i)
|
|
{
|
|
scanner->opt[i].name = "filler";
|
|
scanner->opt[i].size = sizeof (SANE_Word);
|
|
scanner->opt[i].cap = SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
|
|
scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
|
|
scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
/************** "Mode" group: **************/
|
|
scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
|
|
scanner->opt[OPT_MODE_GROUP].desc = "";
|
|
scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
/* source */
|
|
scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
|
|
scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
|
|
scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
|
|
scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_SOURCE].size = max_string_size (source_list);
|
|
scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
|
|
if (scanner->autofeeder && scanner->flatbed)
|
|
{
|
|
scanner->opt[OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT
|
|
| SANE_CAP_SOFT_DETECT;
|
|
}
|
|
|
|
/* scan mode */
|
|
scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
|
|
scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
|
|
scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
|
|
scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list);
|
|
scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list;
|
|
scanner->opt[OPT_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
/* negative */
|
|
scanner->opt[OPT_TYPE].name = "type";
|
|
scanner->opt[OPT_TYPE].title = "Film type";
|
|
scanner->opt[OPT_TYPE].desc = "positive or negative image";
|
|
scanner->opt[OPT_TYPE].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_TYPE].size = max_string_size (type_list);
|
|
scanner->opt[OPT_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_TYPE].constraint.string_list = type_list;
|
|
|
|
scanner->opt[OPT_PRESCAN].name = "prescan";
|
|
scanner->opt[OPT_PRESCAN].title = "Prescan";
|
|
scanner->opt[OPT_PRESCAN].desc = "Perform a prescan during preview";
|
|
scanner->opt[OPT_PRESCAN].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_PRESCAN].unit = SANE_UNIT_NONE;
|
|
|
|
/* resolution */
|
|
scanner->opt[OPT_X_RES].name = SANE_NAME_SCAN_X_RESOLUTION;
|
|
scanner->opt[OPT_X_RES].title = SANE_TITLE_SCAN_X_RESOLUTION;
|
|
scanner->opt[OPT_X_RES].desc = SANE_DESC_SCAN_X_RESOLUTION;
|
|
scanner->opt[OPT_X_RES].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_X_RES].unit = SANE_UNIT_DPI;
|
|
scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_X_RES].constraint.word_list = scanner->x_res_list;
|
|
scanner->opt[OPT_X_RES].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_Y_RES].name = SANE_NAME_SCAN_Y_RESOLUTION;
|
|
scanner->opt[OPT_Y_RES].title = SANE_TITLE_SCAN_Y_RESOLUTION;
|
|
scanner->opt[OPT_Y_RES].desc = SANE_DESC_SCAN_Y_RESOLUTION;
|
|
scanner->opt[OPT_Y_RES].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_Y_RES].unit = SANE_UNIT_DPI;
|
|
scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_Y_RES].constraint.word_list = scanner->y_res_list;
|
|
scanner->opt[OPT_Y_RES].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
#ifdef no_preview_res
|
|
scanner->opt[OPT_PREVIEW_RES].name = "preview-resolution";
|
|
scanner->opt[OPT_PREVIEW_RES].title = "Preview resolution";
|
|
scanner->opt[OPT_PREVIEW_RES].desc = SANE_DESC_SCAN_RESOLUTION;
|
|
scanner->opt[OPT_PREVIEW_RES].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_PREVIEW_RES].unit = SANE_UNIT_DPI;
|
|
scanner->opt[OPT_PREVIEW_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_PREVIEW_RES].constraint.word_list = resolution_list;
|
|
#endif
|
|
|
|
/************** "Geometry" group: **************/
|
|
scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
|
|
scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
|
|
scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
/* top-left x */
|
|
scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
|
|
scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
|
|
scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
|
|
scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_TL_X].constraint.range = &scanner->x_range;
|
|
scanner->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
/* top-left y */
|
|
scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
|
|
scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
|
|
scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
|
|
scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_TL_Y].constraint.range = &scanner->y_range;
|
|
scanner->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
/* bottom-right x */
|
|
scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
|
|
scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
|
|
scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
|
|
scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_BR_X].constraint.range = &scanner->x_range;
|
|
scanner->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
/* bottom-right y */
|
|
scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
|
|
scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
|
|
scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
|
|
scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_BR_Y].constraint.range = &scanner->y_range;
|
|
scanner->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
/* ------------------------------ */
|
|
|
|
/************** "Enhancement" group: **************/
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
scanner->opt[OPT_AVERAGING].name = "averaging";
|
|
scanner->opt[OPT_AVERAGING].title = "Averaging";
|
|
scanner->opt[OPT_AVERAGING].desc = "Averaging";
|
|
scanner->opt[OPT_AVERAGING].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_AVERAGING].unit = SANE_UNIT_NONE;
|
|
|
|
scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
|
|
scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
|
|
scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
|
|
scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
|
|
scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
|
|
scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
|
|
scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
|
|
scanner->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_CONTRAST].constraint.range = &threshold_range;
|
|
scanner->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
|
|
scanner->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
|
|
scanner->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
|
|
scanner->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_THRESHOLD].constraint.range = &contrast_range;
|
|
scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_MIRROR_IMAGE].name = "mirroring";
|
|
scanner->opt[OPT_MIRROR_IMAGE].title = "mirror image";
|
|
scanner->opt[OPT_MIRROR_IMAGE].desc = "mirror image";
|
|
scanner->opt[OPT_MIRROR_IMAGE].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_MIRROR_IMAGE].unit = SANE_UNIT_NONE;
|
|
if (scanner->has_mirroring)
|
|
scanner->opt[OPT_MIRROR_IMAGE].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
scanner->opt[OPT_RIF].name = "rif";
|
|
scanner->opt[OPT_RIF].title = "rif";
|
|
scanner->opt[OPT_RIF].desc = "reverse image format";
|
|
scanner->opt[OPT_RIF].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_RIF].unit = SANE_UNIT_NONE;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_RIF].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_AUTOSEP].name = "autoseparation";
|
|
scanner->opt[OPT_AUTOSEP].title = "automatic separation";
|
|
scanner->opt[OPT_AUTOSEP].desc = "automatic separation";
|
|
scanner->opt[OPT_AUTOSEP].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_AUTOSEP].unit = SANE_UNIT_NONE;
|
|
if (scanner->has_autosep)
|
|
scanner->opt[OPT_AUTOSEP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_OUTLINE_EXTRACTION].name = "outline";
|
|
scanner->opt[OPT_OUTLINE_EXTRACTION].title = "outline extraction";
|
|
scanner->opt[OPT_OUTLINE_EXTRACTION].desc = "enable outline extraction";
|
|
scanner->opt[OPT_OUTLINE_EXTRACTION].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_OUTLINE_EXTRACTION].unit = SANE_UNIT_NONE;
|
|
if (scanner->has_outline)
|
|
scanner->opt[OPT_OUTLINE_EXTRACTION].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_COMPRESSION].name = "compression";
|
|
scanner->opt[OPT_COMPRESSION].title = "compress image";
|
|
scanner->opt[OPT_COMPRESSION].desc = "use hardware compression of scanner";
|
|
scanner->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_COMPRESSION].size = max_string_size (compression_mode_list);
|
|
scanner->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_COMPRESSION].constraint.string_list = compression_mode_list;
|
|
if (scanner->cmp2)
|
|
scanner->opt[OPT_COMPRESSION].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_GAMMA].name = "gamma";
|
|
scanner->opt[OPT_GAMMA].title = "gamma";
|
|
scanner->opt[OPT_GAMMA].desc = "specifies the gamma pattern number for the line art or the halftone";
|
|
scanner->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_GAMMA].size = max_string_size (scan_mode_list);
|
|
scanner->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_GAMMA].constraint.string_list = gamma_mode_list;
|
|
scanner->opt[OPT_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_EMPHASIS].name = "emphasis";
|
|
scanner->opt[OPT_EMPHASIS].title = "emphasis";
|
|
scanner->opt[OPT_EMPHASIS].desc = "image emphasis";
|
|
scanner->opt[OPT_EMPHASIS].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_EMPHASIS].size = max_string_size (scan_mode_list);
|
|
scanner->opt[OPT_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_EMPHASIS].constraint.string_list = emphasis_mode_list;
|
|
if (scanner->has_emphasis)
|
|
scanner->opt[OPT_EMPHASIS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
scanner->opt[OPT_VARIANCE_RATE].name = "variance_rate";
|
|
scanner->opt[OPT_VARIANCE_RATE].title = "variance rate";
|
|
scanner->opt[OPT_VARIANCE_RATE].desc = "variance rate for simplified dynamic threshold";
|
|
scanner->opt[OPT_VARIANCE_RATE].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_VARIANCE_RATE].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_VARIANCE_RATE].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_VARIANCE_RATE].constraint.range = &variance_rate_range;
|
|
scanner->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_THRESHOLD_CURVE].name = "threshold_curve";
|
|
scanner->opt[OPT_THRESHOLD_CURVE].title = "threshold curve";
|
|
scanner->opt[OPT_THRESHOLD_CURVE].desc = "DTC threshold_curve";
|
|
scanner->opt[OPT_THRESHOLD_CURVE].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_THRESHOLD_CURVE].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_THRESHOLD_CURVE].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_GRADIATION].name = "gradiation";
|
|
scanner->opt[OPT_GRADIATION].title = "gradiation";
|
|
scanner->opt[OPT_GRADIATION].desc = "gradiation";
|
|
scanner->opt[OPT_GRADIATION].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_GRADIATION].size = max_string_size (gradiation_mode_list);
|
|
scanner->opt[OPT_GRADIATION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_GRADIATION].constraint.string_list = gradiation_mode_list;
|
|
scanner->opt[OPT_GRADIATION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_SMOOTHING_MODE].name = "smoothing_mode";
|
|
scanner->opt[OPT_SMOOTHING_MODE].title = "smoothing mode";
|
|
scanner->opt[OPT_SMOOTHING_MODE].desc = "smoothing mode";
|
|
scanner->opt[OPT_SMOOTHING_MODE].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_SMOOTHING_MODE].size = max_string_size (smoothing_mode_list);
|
|
scanner->opt[OPT_SMOOTHING_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_SMOOTHING_MODE].constraint.string_list = smoothing_mode_list;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_FILTERING].name = "filtering";
|
|
scanner->opt[OPT_FILTERING].title = "filtering";
|
|
scanner->opt[OPT_FILTERING].desc = "filtering";
|
|
scanner->opt[OPT_FILTERING].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_FILTERING].size = max_string_size (filtering_mode_list);
|
|
scanner->opt[OPT_FILTERING].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_FILTERING].constraint.string_list = filtering_mode_list;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_FILTERING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_BACKGROUND].name = "background";
|
|
scanner->opt[OPT_BACKGROUND].title = "background";
|
|
scanner->opt[OPT_BACKGROUND].desc = "output if gradiation of the video data is equal to or larger than threshold";
|
|
scanner->opt[OPT_BACKGROUND].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_BACKGROUND].size = max_string_size (background_mode_list);
|
|
scanner->opt[OPT_BACKGROUND].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_BACKGROUND].constraint.string_list = background_mode_list;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_BACKGROUND].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
scanner->opt[OPT_NOISE_REMOVAL].name = "noise_removal";
|
|
scanner->opt[OPT_NOISE_REMOVAL].title = "noise removal";
|
|
scanner->opt[OPT_NOISE_REMOVAL].desc = "enables the noise removal matrixes";
|
|
scanner->opt[OPT_NOISE_REMOVAL].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_NOISE_REMOVAL].unit = SANE_UNIT_NONE;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_MATRIX2X2].name = "matrix_2x2";
|
|
scanner->opt[OPT_MATRIX2X2].title = "2x2 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX2X2].desc = "2x2 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX2X2].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_MATRIX2X2].unit = SANE_UNIT_NONE;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_MATRIX3X3].name = "matrix_3x3";
|
|
scanner->opt[OPT_MATRIX3X3].title = "3x3 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX3X3].desc = "3x3 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX3X3].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_MATRIX3X3].unit = SANE_UNIT_NONE;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_MATRIX4X4].name = "matrix_4x4";
|
|
scanner->opt[OPT_MATRIX4X4].title = "4x4 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX4X4].desc = "4x4 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX4X4].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_MATRIX4X4].unit = SANE_UNIT_NONE;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_MATRIX5X5].name = "matrix_5x5";
|
|
scanner->opt[OPT_MATRIX5X5].title = "5x5 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX5X5].desc = "5x5 noise removal matrix";
|
|
scanner->opt[OPT_MATRIX5X5].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_MATRIX5X5].unit = SANE_UNIT_NONE;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].name = "white_level_follow";
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].title = "white level follow";
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].desc = "white level follow";
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].size =
|
|
max_string_size (white_level_follow_mode_list);
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].constraint_type =
|
|
SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].constraint.string_list =
|
|
white_level_follow_mode_list;
|
|
if (scanner->has_white_level_follow)
|
|
scanner->opt[OPT_WHITE_LEVEL_FOLLOW].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_DTC_SELECTION].name = "dtc_selection";
|
|
scanner->opt[OPT_DTC_SELECTION].title = "DTC selection";
|
|
scanner->opt[OPT_DTC_SELECTION].desc = "DTC selection";
|
|
scanner->opt[OPT_DTC_SELECTION].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_DTC_SELECTION].size = max_string_size (dtc_selection_mode_list);
|
|
scanner->opt[OPT_DTC_SELECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_DTC_SELECTION].constraint.string_list = dtc_selection_mode_list;
|
|
if (scanner->ipc)
|
|
scanner->opt[OPT_DTC_SELECTION].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
scanner->opt[OPT_DUPLEX].name = "duplex";
|
|
scanner->opt[OPT_DUPLEX].title = "duplex";
|
|
scanner->opt[OPT_DUPLEX].desc = "Scan front and back side";
|
|
scanner->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_DUPLEX].unit = SANE_UNIT_NONE;
|
|
if (scanner->is_duplex)
|
|
scanner->opt[OPT_DUPLEX].cap = SANE_CAP_SOFT_SELECT |
|
|
SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
|
|
scanner->opt[OPT_PAPER_SIZE].name = "paper_size";
|
|
scanner->opt[OPT_PAPER_SIZE].title = "paper size";
|
|
scanner->opt[OPT_PAPER_SIZE].desc = "Physical size of the paper in the ADF";
|
|
scanner->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_PAPER_SIZE].size = max_string_size (paper_size_mode_list);
|
|
scanner->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_PAPER_SIZE].constraint.string_list = paper_size_mode_list;
|
|
scanner->opt[OPT_PAPER_SIZE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_PAPER_ORIENTATION].name = "orientation";
|
|
scanner->opt[OPT_PAPER_ORIENTATION].title = "orientation";
|
|
scanner->opt[OPT_PAPER_ORIENTATION].desc = "Orientation of the paper in the ADF";
|
|
scanner->opt[OPT_PAPER_ORIENTATION].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].size = max_string_size (paper_orientation_mode_list);
|
|
scanner->opt[OPT_PAPER_ORIENTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].constraint.string_list = paper_orientation_mode_list;
|
|
scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
scanner->opt[OPT_PAPER_WIDTH].name = "paper_width";
|
|
scanner->opt[OPT_PAPER_WIDTH].title = "paper width";
|
|
scanner->opt[OPT_PAPER_WIDTH].desc = "Physical width of the paper in the ADF";
|
|
scanner->opt[OPT_PAPER_WIDTH].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_PAPER_WIDTH].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_PAPER_WIDTH].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_PAPER_WIDTH].constraint.range = &scanner->adf_width_range;
|
|
scanner->opt[OPT_PAPER_WIDTH].cap = SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_PAPER_HEIGHT].name = "paper_height";
|
|
scanner->opt[OPT_PAPER_HEIGHT].title = "paper height";
|
|
scanner->opt[OPT_PAPER_HEIGHT].desc = "Physical height of the paper in the ADF";
|
|
scanner->opt[OPT_PAPER_HEIGHT].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_PAPER_HEIGHT].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_PAPER_HEIGHT].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_PAPER_HEIGHT].constraint.range = &scanner->adf_height_range;
|
|
scanner->opt[OPT_PAPER_HEIGHT].cap = SANE_CAP_INACTIVE;
|
|
|
|
/* ------------------------------ */
|
|
|
|
if (scanner->has_hw_status)
|
|
{
|
|
scanner->opt[OPT_START_BUTTON].name = "start_button";
|
|
scanner->opt[OPT_START_BUTTON].title = "start button";
|
|
scanner->opt[OPT_START_BUTTON].desc = "is the start button of the scanner pressed";
|
|
scanner->opt[OPT_START_BUTTON].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_START_BUTTON].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_START_BUTTON].cap = SANE_CAP_SOFT_DETECT;
|
|
}
|
|
|
|
|
|
/************** "Advanced" group: **************/
|
|
scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
|
|
scanner->opt[OPT_ADVANCED_GROUP].desc = "";
|
|
scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
/* preview */
|
|
scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
|
|
scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
|
|
scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
|
|
scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
|
|
|
|
DBG (10, "init_options:ok\n");
|
|
return SANE_STATUS_GOOD;
|
|
} /* init_options */
|
|
|
|
static int
|
|
m3096g_check_values (struct m3096g *s)
|
|
{
|
|
if (s->use_adf == SANE_TRUE && s->autofeeder == 0)
|
|
{
|
|
DBG (1, "m3096g_check_values: %s\n",
|
|
"ERROR: ADF-MODE NOT SUPPORTED BY SCANNER, ABORTING");
|
|
return (1);
|
|
}
|
|
return (0);
|
|
} /* m3096g_check_values */
|
|
|
|
/* m3096g_free_scanner should go through the following sequence:
|
|
* OBJECT POSITION DISCHARGE
|
|
* GOOD
|
|
* RELEASE UNIT
|
|
* GOOD
|
|
*/
|
|
static int
|
|
m3096g_free_scanner (struct m3096g *s)
|
|
{
|
|
int ret;
|
|
DBG (10, "m3096g_free_scanner\n");
|
|
ret = m3096g_object_discharge (s);
|
|
if (ret)
|
|
return ret;
|
|
|
|
wait_scanner (s);
|
|
|
|
ret = do_scsi_cmd (s->sfd, release_unitB.cmd, release_unitB.size, NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
DBG (10, "m3096g_free_scanner: ok\n");
|
|
return ret;
|
|
} /* m3096g_free_scanner */
|
|
|
|
/* m3096g_grab_scanner should go through the following command sequence:
|
|
* TEST UNIT READY
|
|
* CHECK CONDITION \
|
|
* REQUEST SENSE > These should be handled automagically by
|
|
* UNIT ATTENTION / the kernel if they happen (powerup/reset)
|
|
* TEST UNIT READY
|
|
* GOOD
|
|
* RESERVE UNIT
|
|
* GOOD
|
|
*
|
|
* It is then responsible for installing appropriate signal handlers
|
|
* to call emergency_give_scanner() if user aborts.
|
|
*/
|
|
|
|
static int
|
|
m3096g_grab_scanner (struct m3096g *s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (10, "m3096g_grab_scanner\n");
|
|
wait_scanner (s);
|
|
|
|
ret = do_scsi_cmd (s->sfd, reserve_unitB.cmd, reserve_unitB.size, NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
DBG (10, "m3096g_grab_scanner: ok\n");
|
|
return 0;
|
|
} /* m3096g_grab_scanner */
|
|
|
|
/*
|
|
* wait_scanner spins until TEST_UNIT_READY returns 0 (GOOD)
|
|
* returns 0 on success,
|
|
* returns -1 on error or timeout
|
|
*/
|
|
static int
|
|
wait_scanner (struct m3096g *s)
|
|
{
|
|
int ret = -1;
|
|
int cnt = 0;
|
|
|
|
DBG (10, "wait_scanner\n");
|
|
|
|
while (ret != 0)
|
|
{
|
|
ret = do_scsi_cmd (s->sfd, test_unit_readyB.cmd,
|
|
test_unit_readyB.size, 0, 0);
|
|
if (ret == SANE_STATUS_DEVICE_BUSY)
|
|
{
|
|
usleep (500000); /* wait 0.5 seconds */
|
|
/* 20 sec. max (prescan takes up to 15 sec. */
|
|
if (cnt++ > 40)
|
|
{
|
|
DBG (1, "wait_scanner: scanner does NOT get ready\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else if (ret == SANE_STATUS_GOOD)
|
|
{
|
|
DBG (10, "wait_scanner: ok\n");
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "wait_scanner: unit ready failed (%s)\n",
|
|
sane_strstatus (ret));
|
|
}
|
|
}
|
|
DBG (10, "wait_scanner: ok\n");
|
|
return 0;
|
|
} /* wait_scanner */
|
|
|
|
static int
|
|
m3096g_object_position (struct m3096g *s)
|
|
{
|
|
int ret;
|
|
DBG (10, "m3096g_object_position\n");
|
|
if (s->use_adf != SANE_TRUE)
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
if (s->autofeeder == 0)
|
|
{
|
|
DBG (10, "m3096g_object_position: Autofeeder not present.\n");
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
memcpy (s->buffer, object_positionB.cmd, object_positionB.size);
|
|
set_OP_autofeed (s->buffer, OP_Feed);
|
|
ret = do_scsi_cmd (s->sfd, s->buffer,
|
|
object_positionB.size, NULL, 0);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
return ret;
|
|
}
|
|
wait_scanner (s);
|
|
DBG (10, "m3096g_object_position: ok\n");
|
|
return ret;
|
|
} /* m3096g_object_position */
|
|
|
|
|
|
|
|
static SANE_Status
|
|
do_cancel (struct m3096g *scanner)
|
|
{
|
|
DBG (10, "do_cancel\n");
|
|
swap_res (scanner);
|
|
scanner->scanning = SANE_FALSE;
|
|
|
|
do_eof (scanner); /* close pipe and reposition scanner */
|
|
|
|
if (scanner->reader_pid > 0)
|
|
{
|
|
int exit_status;
|
|
DBG (10, "do_cancel: kill reader_process\n");
|
|
/* ensure child knows it's time to stop: */
|
|
kill (scanner->reader_pid, SIGTERM);
|
|
while (wait (&exit_status) != scanner->reader_pid)
|
|
DBG (50, "wait for scanner to stop\n");
|
|
;
|
|
scanner->reader_pid = 0;
|
|
}
|
|
|
|
if (scanner->sfd >= 0)
|
|
{
|
|
m3096g_free_scanner (scanner);
|
|
DBG (10, "do_cancel: close filedescriptor\n");
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
}
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
} /* do_cancel */
|
|
|
|
static void
|
|
swap_res (struct m3096g *s)
|
|
{ /* for the time being, do nothing */
|
|
DBG (99, "%d\n", s);
|
|
} /* swap_res */
|
|
|
|
static int
|
|
m3096g_object_discharge (struct m3096g *s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (10, "m3096g_object_discharge\n");
|
|
if (s->use_adf != SANE_TRUE)
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
memcpy (s->buffer, object_positionB.cmd, object_positionB.size);
|
|
set_OP_autofeed (s->buffer, OP_Discharge);
|
|
ret = do_scsi_cmd (s->sfd, s->buffer,
|
|
object_positionB.size, NULL, 0);
|
|
wait_scanner (s);
|
|
DBG (10, "m3096g_object_discharge: ok\n");
|
|
return ret;
|
|
} /* m3096g_object_discharge */
|
|
|
|
static int
|
|
m3096g_set_window_param (struct m3096g *s, int prescan)
|
|
{
|
|
unsigned char buffer_r[max_WDB_size];
|
|
unsigned char *cp_buffer;
|
|
int width, length, pixels;
|
|
int ret;
|
|
int b_finished, b_front;
|
|
int i_cmd_len, i_xfer_len;
|
|
|
|
wait_scanner (s);
|
|
DBG (10, "set_window_param\n");
|
|
DBG (99, "%d\n", prescan); /* avoid compiler warning */
|
|
|
|
memset (buffer_r, '\0', max_WDB_size); /* clear buffer */
|
|
memcpy (buffer_r, window_descriptor_blockB.cmd,
|
|
window_descriptor_blockB.size); /* copy preset data */
|
|
|
|
b_front = 1;
|
|
cp_buffer = buffer_r;
|
|
|
|
do
|
|
{
|
|
|
|
if ( b_front )
|
|
{
|
|
set_WD_wid (cp_buffer, WD_wid_front); /* window identifier */
|
|
}
|
|
else
|
|
{
|
|
set_WD_wid (cp_buffer, WD_wid_back); /* window identifier */
|
|
}
|
|
|
|
set_WD_Xres (cp_buffer, s->x_res); /* x resolution in dpi */
|
|
set_WD_Yres (cp_buffer, s->y_res); /* y resolution in dpi */
|
|
|
|
set_WD_ULX (cp_buffer, s->tl_x); /* top left x */
|
|
set_WD_ULY (cp_buffer, s->tl_y); /* top left y */
|
|
|
|
width = s->br_x - s->tl_x;
|
|
/* increase initial width until we've a full number of bytes in line */
|
|
if (s->x_res == 0)
|
|
{
|
|
while (pixels = 400 * width / 1200,
|
|
(s->bitsperpixel * pixels) % 8)
|
|
{
|
|
width++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (pixels = s->x_res * width / 1200,
|
|
(s->bitsperpixel * pixels) % 8)
|
|
{
|
|
width++;
|
|
}
|
|
}
|
|
length = s->br_y - s->tl_y;
|
|
|
|
if (13200 < width
|
|
&& width <= 14592)
|
|
{
|
|
if (length > 19842
|
|
&& length < (19842 + 600))
|
|
{
|
|
length = 19840;
|
|
}
|
|
}
|
|
|
|
/* todo: constraint checking for M3093GX DG GXm DGm */
|
|
|
|
set_WD_width (cp_buffer, width);
|
|
set_WD_length (cp_buffer, length);
|
|
|
|
set_WD_brightness (cp_buffer, s->brightness);
|
|
set_WD_threshold (cp_buffer, s->threshold);
|
|
set_WD_contrast (cp_buffer, s->contrast);
|
|
set_WD_composition (cp_buffer, s->composition);
|
|
set_WD_bitsperpixel (cp_buffer, s->bitsperpixel);
|
|
set_WD_halftone (cp_buffer, s->halftone);
|
|
set_WD_rif (cp_buffer, s->rif);
|
|
set_WD_bitorder (cp_buffer, s->bitorder);
|
|
set_WD_compress_type (cp_buffer, s->compress_type);
|
|
set_WD_compress_arg (cp_buffer, s->compress_arg);
|
|
set_WD_vendor_id_code (cp_buffer, s->vendor_id_code);
|
|
set_WD_gamma (cp_buffer, s->gamma);
|
|
set_WD_outline (cp_buffer, s->outline);
|
|
set_WD_emphasis (cp_buffer, s->emphasis);
|
|
set_WD_auto_sep (cp_buffer, s->auto_sep);
|
|
set_WD_mirroring (cp_buffer, s->mirroring);
|
|
set_WD_var_rate_dyn_thresh (cp_buffer, s->var_rate_dyn_thresh);
|
|
set_WD_dtc_threshold_curve (cp_buffer, s->dtc_threshold_curve);
|
|
set_WD_gradiation(cp_buffer, s->gradiation);
|
|
set_WD_smoothing_mode(cp_buffer, s->smoothing_mode);
|
|
set_WD_filtering(cp_buffer, s->filtering);
|
|
set_WD_background(cp_buffer, s->background);
|
|
set_WD_matrix2x2(cp_buffer, s->matrix2x2);
|
|
set_WD_matrix3x3(cp_buffer, s->matrix3x3);
|
|
set_WD_matrix4x4(cp_buffer, s->matrix4x4);
|
|
set_WD_matrix5x5(cp_buffer, s->matrix5x5);
|
|
set_WD_noise_removal(cp_buffer, s->noise_removal);
|
|
set_WD_white_level_follow (cp_buffer, s->white_level_follow);
|
|
set_WD_subwindow_list (cp_buffer, s->subwindow_list);
|
|
|
|
if ( b_front )
|
|
{
|
|
set_WD_paper_size (cp_buffer, s->paper_size);
|
|
set_WD_paper_orientation(cp_buffer, s->paper_orientation);
|
|
set_WD_paper_selection(cp_buffer, s->paper_selection);
|
|
}
|
|
else
|
|
{
|
|
/* bytes 35 - 3D has to be 0 for back-side */
|
|
set_WD_paper_size (cp_buffer, WD_paper_UNDEFINED);
|
|
set_WD_paper_orientation(cp_buffer, WD_paper_PORTRAIT);
|
|
set_WD_paper_selection(cp_buffer, WD_paper_SEL_UNDEFINED);
|
|
set_WD_paper_width_X (cp_buffer, 0);
|
|
set_WD_paper_length_Y (cp_buffer, 0);
|
|
}
|
|
|
|
set_WD_paper_width_X (cp_buffer, s->paper_width_X);
|
|
set_WD_paper_length_Y (cp_buffer, s->paper_length_Y);
|
|
|
|
set_WD_dtc_selection (cp_buffer, s->dtc_selection);
|
|
|
|
DBG (10, "\tx_res=%d, y_res=%d\n",
|
|
s->x_res, s->y_res);
|
|
DBG (10, "\tupper left-x=%d, upper left-y=%d\n",
|
|
s->tl_x, s->tl_y);
|
|
DBG (10, "\twindow width=%d, length=%d\n",
|
|
width, s->br_y - s->tl_y);
|
|
|
|
DBG (10, "paper_size %d\n", s->paper_size);
|
|
DBG (10, "paper_orient %d\n", s->paper_orientation);
|
|
DBG (10, "paper_select %d\n", s->paper_selection);
|
|
|
|
if (s->duplex && b_front)
|
|
{
|
|
/* if we are scanning duplex and just set the front side
|
|
* switch to back side and repeat the steps above.
|
|
*/
|
|
b_front = 0;
|
|
b_finished = 0;
|
|
cp_buffer = cp_buffer + used_WDB_size;
|
|
}
|
|
else
|
|
{
|
|
b_finished = 1;
|
|
}
|
|
}
|
|
while (!b_finished);
|
|
|
|
|
|
if (s->duplex)
|
|
{
|
|
s->i_num_frames = 2;
|
|
}
|
|
else
|
|
{
|
|
s->i_num_frames = 1;
|
|
}
|
|
|
|
/* prepare SCSI-BUFFER */
|
|
memcpy (s->buffer, set_windowB.cmd, set_windowB.size); /* SET-WINDOW cmd */
|
|
memcpy ((s->buffer + set_windowB.size), /* add WPDB */
|
|
window_parameter_data_blockB.cmd,
|
|
window_parameter_data_blockB.size);
|
|
|
|
/* set size of one window descriptor block */
|
|
set_WPDB_wdblen ((s->buffer + set_windowB.size), used_WDB_size);
|
|
|
|
|
|
if ( s->duplex )
|
|
{
|
|
memcpy (s->buffer + set_windowB.size + window_parameter_data_blockB.size,
|
|
buffer_r, used_WDB_size * 2);
|
|
|
|
i_xfer_len = window_parameter_data_blockB.size + used_WDB_size * 2;
|
|
set_SW_xferlen (s->buffer, i_xfer_len);
|
|
i_cmd_len = set_windowB.size + i_xfer_len;
|
|
}
|
|
else
|
|
{
|
|
memcpy (s->buffer + set_windowB.size + window_parameter_data_blockB.size,
|
|
buffer_r, used_WDB_size);
|
|
|
|
i_xfer_len = window_parameter_data_blockB.size + used_WDB_size;
|
|
set_SW_xferlen (s->buffer, i_xfer_len);
|
|
i_cmd_len = set_windowB.size + i_xfer_len;
|
|
}
|
|
|
|
hexdump (15, "Window front", buffer_r, used_WDB_size);
|
|
if ( s->duplex )
|
|
hexdump (15, "Window back", buffer_r + used_WDB_size, used_WDB_size);
|
|
|
|
|
|
ret = do_scsi_cmd (s->sfd, s->buffer, i_cmd_len, NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
DBG (10, "set_window_param: ok\n");
|
|
return ret;
|
|
} /* m3096g_set_window_param */
|
|
|
|
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;
|
|
} /* max_string_size */
|
|
|
|
static int
|
|
m3096g_start_scan (struct m3096g *s)
|
|
{
|
|
unsigned char cp_buffer[2];
|
|
int ret;
|
|
int i_xfer_len;
|
|
|
|
DBG (10, "m3096g_start_scan\n");
|
|
|
|
cp_buffer[0] = 0x00;
|
|
cp_buffer[1] = 0x80;
|
|
|
|
memcpy (s->buffer, scanB.cmd, scanB.size);
|
|
|
|
if (s->duplex)
|
|
{
|
|
i_xfer_len = 2;
|
|
}
|
|
else
|
|
{
|
|
i_xfer_len = 1;
|
|
}
|
|
|
|
memcpy (s->buffer + scanB.size, cp_buffer, i_xfer_len);
|
|
set_SC_xfer_length(s->buffer, i_xfer_len);
|
|
|
|
DBG(1, "cmd_len = %d\n",scanB.size + i_xfer_len);
|
|
ret = do_scsi_cmd (s->sfd, s->buffer, scanB.size + i_xfer_len, NULL, 0);
|
|
|
|
if (ret)
|
|
return ret;
|
|
DBG (10, "m3096g_start_scan:ok\n");
|
|
|
|
return ret;
|
|
} /* m3096g_start_scan */
|
|
|
|
static void
|
|
sigterm_handler (int signal)
|
|
{
|
|
|
|
DBG (99, "%d\n", signal); /* avoid compiler warning */
|
|
|
|
sanei_scsi_req_flush_all (); /* flush SCSI queue */
|
|
_exit (SANE_STATUS_GOOD);
|
|
} /* sigterm_handler */
|
|
|
|
/* This function is executed as a child process. */
|
|
static int
|
|
reader_process (struct m3096g *scanner, int pipe_fd, int i_window_id)
|
|
{
|
|
SANE_Status status;
|
|
unsigned int data_left;
|
|
unsigned int data_to_read;
|
|
FILE *fp;
|
|
sigset_t sigterm_set;
|
|
struct SIGACTION act;
|
|
|
|
DBG (10, "reader_process started\n");
|
|
|
|
sigemptyset (&sigterm_set);
|
|
sigaddset (&sigterm_set, SIGTERM);
|
|
|
|
fp = fdopen (pipe_fd, "w");
|
|
if (!fp)
|
|
{
|
|
DBG (1, "reader_process: couldn't open pipe!\n");
|
|
return 1;
|
|
}
|
|
|
|
DBG (10, "reader_process: starting to READ data\n");
|
|
|
|
data_left = bytes_per_line (scanner) *
|
|
lines_per_scan (scanner);
|
|
|
|
m3096g_trim_rowbufsize (scanner); /* trim bufsize */
|
|
|
|
DBG (10, "reader_process: reading %u bytes in blocks of %u bytes\n",
|
|
data_left, scanner->row_bufsize);
|
|
|
|
memset (&act, 0, sizeof (act));
|
|
|
|
act.sa_handler = sigterm_handler;
|
|
sigaction (SIGTERM, &act, 0);
|
|
/* wait_scanner(scanner); */
|
|
do
|
|
{
|
|
data_to_read = (data_left < scanner->row_bufsize)
|
|
? data_left
|
|
: scanner->row_bufsize;
|
|
|
|
current_scanner = scanner;
|
|
|
|
status = m3096g_read_data_block (scanner, data_to_read, i_window_id);
|
|
|
|
switch (status)
|
|
{
|
|
case SANE_STATUS_GOOD:
|
|
|
|
break;
|
|
case SANE_STATUS_EOF:
|
|
|
|
DBG (5, "reader_process: EOM (no more data) length = %d\n",
|
|
current_scanner->i_transfer_length);
|
|
|
|
data_to_read = data_to_read - current_scanner->i_transfer_length;
|
|
data_left = data_to_read;
|
|
|
|
break;
|
|
default:
|
|
DBG (1, "reader_process: unable to get image data from scanner!\n");
|
|
DBG (1, "status = %s\n",sane_strstatus(status));
|
|
fclose (fp);
|
|
return (-1);
|
|
break;
|
|
}
|
|
|
|
fwrite (scanner->buffer, 1, data_to_read, fp);
|
|
fflush (fp);
|
|
|
|
data_left -= data_to_read;
|
|
DBG (10, "reader_process: buffer of %d bytes read; %d bytes to go\n",
|
|
data_to_read, data_left);
|
|
}
|
|
while (data_left);
|
|
|
|
fclose (fp);
|
|
|
|
DBG (10, "reader_process: finished\n");
|
|
|
|
return 0;
|
|
} /* reader_process */
|
|
|
|
static SANE_Status
|
|
do_eof (struct m3096g *scanner)
|
|
{
|
|
DBG (10, "do_eof\n");
|
|
|
|
if (scanner->pipe >= 0)
|
|
{
|
|
close (scanner->pipe);
|
|
scanner->pipe = -1;
|
|
}
|
|
return SANE_STATUS_EOF;
|
|
} /* do_eof */
|
|
|
|
static int
|
|
pixels_per_line (struct m3096g *s)
|
|
{
|
|
int dots;
|
|
if (s->x_res == 0)
|
|
{
|
|
dots = 400 * (s->br_x - s->tl_x) / 1200;
|
|
}
|
|
else
|
|
{
|
|
dots = s->x_res * (s->br_x - s->tl_x) / 1200;
|
|
}
|
|
|
|
if (s->compress_type != 0)
|
|
{
|
|
dots = ((dots * s->bitsperpixel + 7) / 8) * 8;
|
|
}
|
|
|
|
return dots;
|
|
} /* pixels_per_line */
|
|
|
|
static int
|
|
lines_per_scan (struct m3096g *s)
|
|
{
|
|
int lines;
|
|
lines = s->y_res * (s->br_y - s->tl_y) / 1200;
|
|
return lines;
|
|
} /* lines_per_scan */
|
|
|
|
static int
|
|
bytes_per_line (struct m3096g *s)
|
|
{
|
|
return (pixels_per_line (s) * s->bitsperpixel + 7) / 8;
|
|
} /* bytes_per_line */
|
|
|
|
static void
|
|
m3096g_trim_rowbufsize (struct m3096g *s)
|
|
{
|
|
unsigned int row_len;
|
|
row_len = (unsigned int) bytes_per_line (s);
|
|
if (s->row_bufsize >= row_len)
|
|
{
|
|
s->row_bufsize = s->row_bufsize - (s->row_bufsize % row_len);
|
|
DBG (10, "trim_rowbufsize to %d (%d lines)\n",
|
|
s->row_bufsize, s->row_bufsize / row_len);
|
|
}
|
|
} /* m3096g_trim_rowbufsize */
|
|
|
|
|
|
static SANE_Status
|
|
m3096g_read_pixel_size (struct m3096g *s, unsigned int length, int i_window_id)
|
|
{
|
|
SANE_Status r;
|
|
|
|
DBG (10, "m3096g_read_pixel_size (length = %d)\n", length);
|
|
|
|
set_R_datatype_code (readB.cmd, R_pixel_size);
|
|
set_R_window_id(readB.cmd, i_window_id);
|
|
set_R_xfer_length (readB.cmd, length);
|
|
|
|
r = do_scsi_cmd (s->sfd, readB.cmd, readB.size, s->buffer, length);
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
m3096g_read_data_block (struct m3096g *s, unsigned int length, int i_window_id)
|
|
{
|
|
SANE_Status r;
|
|
|
|
DBG (10, "m3096g_read_data_block (length = %d)\n", length);
|
|
/*wait_scanner(s); */
|
|
|
|
set_R_datatype_code (readB.cmd, R_datatype_imagedata);
|
|
set_R_window_id(readB.cmd, i_window_id);
|
|
set_R_xfer_length (readB.cmd, length);
|
|
|
|
r = do_scsi_cmd (s->sfd, readB.cmd, readB.size, s->buffer, length);
|
|
|
|
return r;
|
|
#if 0
|
|
#if 0
|
|
return ((r != 0) ? -1 : length);
|
|
#else
|
|
if (r)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return length;
|
|
}
|
|
#endif
|
|
#endif
|
|
} /* m3096g_read_data_block */
|
|
|
|
static int
|
|
m3096g_valid_number (int value, const int *acceptable)
|
|
{
|
|
int index, max = acceptable[0];
|
|
|
|
for (index = 1; index < max + 1; index++)
|
|
{
|
|
if (value == acceptable[index])
|
|
return 1;
|
|
}
|
|
return 0;
|
|
} /* m3096g_valid_number */
|
|
|
|
/******************************************************************************
|
|
}#############################################################################
|
|
******************************************************************************/
|