2001-01-20 23:42:12 +00:00
|
|
|
/* sane - Scanner Access Now Easy.
|
|
|
|
Copyright (C) 1999,2000 Tom Martone
|
|
|
|
This file is part of a SANE backend for Bell and Howell Copiscan II
|
|
|
|
Scanners using the Remote SCSI Controller(RSC).
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include "sane/config.h"
|
|
|
|
#include <limits.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include "sane/sane.h"
|
|
|
|
#include "sane/saneopts.h"
|
|
|
|
#include "sane/sanei_scsi.h"
|
|
|
|
#include "sane/sanei_config.h"
|
|
|
|
#include "bh.h"
|
|
|
|
|
|
|
|
#define BACKEND_NAME bh
|
|
|
|
#include "sane/sanei_backend.h"
|
|
|
|
#define BUILD 4
|
|
|
|
|
|
|
|
#define MIN(x,y) ((x)<(y) ? (x) : (y))
|
|
|
|
#define MAX(x,y) ((x)>(y) ? (x) : (y))
|
|
|
|
|
|
|
|
static int num_devices = 0;
|
|
|
|
static BH_Device *first_dev = NULL;
|
|
|
|
static BH_Scanner *first_handle = NULL;
|
|
|
|
static SANE_Char inquiry_data[255] = "Bell+Howell scanner";
|
|
|
|
static SANE_Int disable_optional_frames = 0;
|
|
|
|
static SANE_Int fake_inquiry = 0;
|
|
|
|
|
|
|
|
static int allblank(const char *s)
|
|
|
|
{
|
|
|
|
while (s && *s)
|
|
|
|
if (!isspace(*s++))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trim_spaces(char *s, size_t n)
|
|
|
|
{
|
|
|
|
for (s += (n-1); n > 0; n--, s--)
|
|
|
|
{
|
|
|
|
if (*s && !isspace(*s))
|
|
|
|
break;
|
|
|
|
*s = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_String_Const
|
|
|
|
print_devtype (SANE_Byte devtype)
|
|
|
|
{
|
|
|
|
static SANE_String devtypes[] =
|
|
|
|
{
|
|
|
|
"disk",
|
|
|
|
"tape",
|
|
|
|
"printer",
|
|
|
|
"processor",
|
|
|
|
"CD-writer",
|
|
|
|
"CD-drive",
|
|
|
|
"scanner",
|
|
|
|
"optical-drive",
|
|
|
|
"jukebox",
|
|
|
|
"communicator"
|
|
|
|
};
|
|
|
|
|
|
|
|
return (devtype > 0 && devtype < NELEMS(devtypes)) ?
|
|
|
|
devtypes[devtype] :
|
|
|
|
"unknown-device";
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_String_Const
|
|
|
|
print_barcodetype (SANE_Int i)
|
|
|
|
{
|
|
|
|
return (i > 0 && i < NELEMS(barcode_search_bar_list)) ?
|
|
|
|
barcode_search_bar_list[i] :
|
2003-01-24 13:52:52 +00:00
|
|
|
(SANE_String_Const) "unknown";
|
2001-01-20 23:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_String_Const
|
|
|
|
print_orientation (SANE_Int i)
|
|
|
|
{
|
|
|
|
switch(i)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
case 7:
|
|
|
|
return "vertical upwards";
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
return "horizontal right";
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
return "vertical downwards";
|
|
|
|
case 5:
|
|
|
|
case 6:
|
|
|
|
return "horizontal left";
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_String_Const
|
|
|
|
print_read_type (SANE_Int i)
|
|
|
|
{
|
|
|
|
static char buf[32];
|
|
|
|
SANE_Int n;
|
|
|
|
|
|
|
|
/* translate BH_SCSI_READ_TYPE_ codes to a human-readable string */
|
|
|
|
if (i == BH_SCSI_READ_TYPE_FRONT)
|
|
|
|
{
|
|
|
|
strcpy(buf, "front page");
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_BACK)
|
|
|
|
{
|
|
|
|
strcpy(buf, "back page");
|
|
|
|
}
|
|
|
|
else if (i > BH_SCSI_READ_TYPE_FRONT &&
|
|
|
|
i <= BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS)
|
|
|
|
{
|
|
|
|
n = i - BH_SCSI_READ_TYPE_FRONT;
|
|
|
|
sprintf(buf, "front section %d", n);
|
|
|
|
}
|
|
|
|
else if (i > BH_SCSI_READ_TYPE_BACK &&
|
|
|
|
i <= BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS)
|
|
|
|
{
|
|
|
|
n = i - BH_SCSI_READ_TYPE_BACK;
|
|
|
|
sprintf(buf, "back section %d", n);
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_FRONT_BARCODE)
|
|
|
|
{
|
|
|
|
strcpy(buf, "front page barcode");
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_BACK_BARCODE)
|
|
|
|
{
|
|
|
|
strcpy(buf, "back page barcode");
|
|
|
|
}
|
|
|
|
else if (i > BH_SCSI_READ_TYPE_FRONT_BARCODE &&
|
|
|
|
i <= BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS)
|
|
|
|
{
|
|
|
|
n = i - BH_SCSI_READ_TYPE_FRONT_BARCODE;
|
|
|
|
sprintf(buf, "front barcode section %d", n);
|
|
|
|
}
|
|
|
|
else if (i > BH_SCSI_READ_TYPE_BACK_BARCODE &&
|
|
|
|
i <= BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS)
|
|
|
|
{
|
|
|
|
n = i - BH_SCSI_READ_TYPE_BACK_BARCODE;
|
|
|
|
sprintf(buf, "back barcode section %d", n);
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_FRONT_PATCHCODE)
|
|
|
|
{
|
|
|
|
strcpy(buf, "front page patchcode");
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_BACK_PATCHCODE)
|
|
|
|
{
|
|
|
|
strcpy(buf, "back page patchcode");
|
|
|
|
}
|
|
|
|
else if (i > BH_SCSI_READ_TYPE_FRONT_PATCHCODE &&
|
|
|
|
i <= BH_SCSI_READ_TYPE_FRONT_PATCHCODE + NUM_SECTIONS)
|
|
|
|
{
|
|
|
|
n = i - BH_SCSI_READ_TYPE_FRONT_PATCHCODE;
|
|
|
|
sprintf(buf, "front patchcode section %d", n);
|
|
|
|
}
|
|
|
|
else if (i > BH_SCSI_READ_TYPE_BACK_PATCHCODE &&
|
|
|
|
i <= BH_SCSI_READ_TYPE_BACK_PATCHCODE + NUM_SECTIONS)
|
|
|
|
{
|
|
|
|
n = i - BH_SCSI_READ_TYPE_BACK_PATCHCODE;
|
|
|
|
sprintf(buf, "back patchcode section %d", n);
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_FRONT_ICON)
|
|
|
|
{
|
|
|
|
strcpy(buf, "front page icon");
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_BACK_ICON)
|
|
|
|
{
|
|
|
|
strcpy(buf, "back page icon");
|
|
|
|
}
|
|
|
|
else if (i == BH_SCSI_READ_TYPE_SENDBARFILE)
|
|
|
|
{
|
|
|
|
strcpy(buf, "transmit bar/patch codes");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
strcpy(buf, "unknown");
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2001-02-10 18:23:28 +00:00
|
|
|
static SANE_Int
|
|
|
|
get_rotation_id(char *s)
|
|
|
|
{
|
|
|
|
SANE_Int i;
|
|
|
|
|
|
|
|
for (i = 0; rotation_list[i]; i++)
|
|
|
|
if (strcmp(s, rotation_list[i]) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* unknown strings are treated as '0' */
|
|
|
|
return rotation_list[i] ? i : 0;
|
|
|
|
}
|
|
|
|
|
2001-01-20 23:42:12 +00:00
|
|
|
static SANE_Int
|
|
|
|
get_compression_id(char *s)
|
|
|
|
{
|
|
|
|
SANE_Int i;
|
|
|
|
|
|
|
|
for (i = 0; compression_list[i]; i++)
|
|
|
|
if (strcmp(s, compression_list[i]) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* unknown strings are treated as 'none' */
|
|
|
|
return compression_list[i] ? i : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Int
|
|
|
|
get_barcode_id(char *s)
|
|
|
|
{
|
|
|
|
SANE_Int i;
|
|
|
|
|
|
|
|
for (i = 0; barcode_search_bar_list[i]; i++)
|
|
|
|
if (strcmp(s, barcode_search_bar_list[i]) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* unknown strings are treated as 'none' */
|
|
|
|
return barcode_search_bar_list[i] ? i : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Int
|
|
|
|
get_scan_mode_id(char *s)
|
|
|
|
{
|
|
|
|
SANE_Int i;
|
|
|
|
|
|
|
|
for (i = 0; scan_mode_list[i]; i++)
|
|
|
|
if (strcmp(s, scan_mode_list[i]) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* unknown strings are treated as 'lineart' */
|
|
|
|
return scan_mode_list[i] ? i : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Int
|
|
|
|
get_paper_id(char *s)
|
|
|
|
{
|
|
|
|
SANE_Int i;
|
|
|
|
|
|
|
|
for (i = 0; paper_list[i]; i++)
|
|
|
|
if (strcmp(s, paper_list[i]) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* unknown strings are treated as 'custom' */
|
|
|
|
return paper_list[i] ? i : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Int
|
|
|
|
get_barcode_search_mode(char *s)
|
|
|
|
{
|
|
|
|
SANE_Int i;
|
|
|
|
|
|
|
|
if (strcmp(s, "horizontal") == 0)
|
|
|
|
{
|
|
|
|
i = 1;
|
|
|
|
}
|
|
|
|
else if (strcmp(s, "vertical") == 0)
|
|
|
|
{
|
|
|
|
i = 2;
|
|
|
|
}
|
|
|
|
else if (strcmp(s, "vert-horiz") == 0)
|
|
|
|
{
|
|
|
|
i = 6;
|
|
|
|
}
|
|
|
|
else if (strcmp(s, "horiz-vert") == 0)
|
|
|
|
{
|
|
|
|
i = 9;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* unknown strings are treated as 'horiz-vert' */
|
|
|
|
DBG(1, "get_barcode_search_mode: unrecognized string `%s'\n", s);
|
|
|
|
i = 9;
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
appendStdList(BH_Info *sc, SANE_Int res)
|
|
|
|
{
|
|
|
|
/* append entry to resolution list - a SANE_WORD_LIST */
|
|
|
|
sc->resStdList[sc->resStdList[0]+1] = res;
|
|
|
|
sc->resStdList[0]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ScannerDump(BH_Scanner *s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
BH_Info *info;
|
|
|
|
SANE_Device *sdev;
|
|
|
|
|
|
|
|
info = &s->hw->info;
|
|
|
|
sdev = &s->hw->sane;
|
|
|
|
|
|
|
|
DBG (1, "SANE Device: '%s' Vendor: '%s' Model: '%s' Type: '%s'\n",
|
|
|
|
sdev->name,
|
|
|
|
sdev->vendor,
|
|
|
|
sdev->model,
|
|
|
|
sdev->type);
|
|
|
|
|
|
|
|
DBG (1, "Type: '%s' Vendor: '%s' Product: '%s' Revision: '%s'\n",
|
|
|
|
print_devtype(info->devtype),
|
|
|
|
info->vendor,
|
|
|
|
info->product,
|
|
|
|
info->revision);
|
|
|
|
|
|
|
|
DBG (1, "Automatic Document Feeder:%s\n",
|
|
|
|
info->canADF ? " <Installed>" : " <Not Installed>");
|
|
|
|
|
|
|
|
DBG (1, "Colors:%s%s\n", info->colorBandW ? " <Black and White>" : "",
|
|
|
|
info->colorHalftone ? " <Halftone>" : "");
|
|
|
|
|
|
|
|
DBG (1, "Data processing:%s%s%s%s%s%s\n",
|
|
|
|
info->canWhiteFrame ? " <White Frame>" : "",
|
|
|
|
info->canBlackFrame ? " <Black Frame>" : "",
|
|
|
|
info->canEdgeExtract ? " <Edge Extraction>" : "",
|
|
|
|
info->canNoiseFilter ? " <Noise Filter>" : "",
|
|
|
|
info->canSmooth ? " <Smooth>" : "",
|
|
|
|
info->canLineBold ? " <Line Bolding>" : "");
|
|
|
|
|
|
|
|
DBG (1, "Compression:%s%s%s\n",
|
|
|
|
info->comprG3_1D ? " <Group 3, 1D>" : "",
|
|
|
|
info->comprG3_2D ? " <Group 3, 2D>" : "",
|
|
|
|
info->comprG4 ? " <Group 4>" : "");
|
|
|
|
|
|
|
|
DBG (1, "Optional Features:%s%s%s%s\n",
|
|
|
|
info->canBorderRecog ? " <Border Recognition>" : "",
|
|
|
|
info->canBarCode ? " <BarCode Decoding>" : "",
|
|
|
|
info->canIcon ? " <Icon Generation>" : "",
|
|
|
|
info->canSection ? " <Section Support>" : "");
|
|
|
|
|
|
|
|
DBG (1, "Max bytes per scan-line: %d (%d pixels)\n",
|
|
|
|
info->lineMaxBytes,
|
|
|
|
info->lineMaxBytes * 8);
|
|
|
|
|
|
|
|
DBG (1, "Basic resolution (X/Y): %d/%d\n",
|
|
|
|
info->resBasicX,
|
|
|
|
info->resBasicY);
|
|
|
|
|
|
|
|
DBG (1, "Maximum resolution (X/Y): %d/%d\n",
|
|
|
|
info->resMaxX,
|
|
|
|
info->resMaxY);
|
|
|
|
|
|
|
|
DBG (1, "Minimum resolution (X/Y): %d/%d\n",
|
|
|
|
info->resMinX,
|
|
|
|
info->resMinY);
|
|
|
|
|
|
|
|
DBG (1, "Standard Resolutions:\n");
|
|
|
|
for (i = 0; i < info->resStdList[0]; i++)
|
|
|
|
DBG (1, " %d\n", info->resStdList[i+1]);
|
|
|
|
|
|
|
|
DBG (1, "Window Width/Height (in basic res) %d/%d (%.2f/%.2f inches)\n",
|
|
|
|
info->winWidth,
|
|
|
|
info->winHeight,
|
|
|
|
(info->resBasicX != 0) ? ((float) info->winWidth) / info->resBasicX : 0.0,
|
|
|
|
(info->resBasicY) ? ((float) info->winHeight) / info->resBasicY : 0.0);
|
|
|
|
|
|
|
|
DBG (1, "Summary:%s%s%s\n",
|
|
|
|
info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
|
|
|
|
info->canACE ? " (ACE capable)" : "",
|
|
|
|
info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
|
|
|
|
|
|
|
|
sprintf(inquiry_data, "Vendor: %s Product: %s Rev: %s %s%s%s\n",
|
|
|
|
info->vendor,
|
|
|
|
info->product,
|
|
|
|
info->revision,
|
|
|
|
info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
|
|
|
|
info->canACE ? " (ACE capable)" : "",
|
|
|
|
info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
|
|
|
|
|
|
|
|
DBG (5, "autoborder_default=%d\n", info->autoborder_default);
|
|
|
|
DBG (5, "batch_default=%d\n", info->batch_default);
|
2001-02-10 18:23:28 +00:00
|
|
|
DBG (5, "deskew_default=%d\n", info->deskew_default);
|
2001-01-20 23:42:12 +00:00
|
|
|
DBG (5, "check_adf_default=%d\n", info->check_adf_default);
|
|
|
|
DBG (5, "duplex_default=%d\n", info->duplex_default);
|
|
|
|
DBG (5, "timeout_adf_default=%d\n", info->timeout_adf_default);
|
|
|
|
DBG (5, "timeout_manual_default=%d\n", info->timeout_manual_default);
|
|
|
|
DBG (5, "control_panel_default=%d\n", info->control_panel_default);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
test_unit_ready (int fd)
|
|
|
|
{
|
|
|
|
static SANE_Byte cmd[6];
|
|
|
|
SANE_Status status;
|
|
|
|
DBG (3, "test_unit_ready called\n");
|
|
|
|
|
|
|
|
cmd[0] = BH_SCSI_TEST_UNIT_READY;
|
|
|
|
memset (cmd, 0, sizeof (cmd));
|
|
|
|
status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
object_position (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static SANE_Byte cmd[10];
|
|
|
|
SANE_Status status;
|
|
|
|
DBG (3, "object_position called\n");
|
|
|
|
|
|
|
|
memset (cmd, 0, sizeof (cmd));
|
|
|
|
cmd[0] = BH_SCSI_OBJECT_POSITION;
|
|
|
|
cmd[1] = 0x01;
|
|
|
|
status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
read_barcode_data (BH_Scanner *s, FILE *fp)
|
|
|
|
{
|
|
|
|
static SANE_Byte cmd[10];
|
|
|
|
SANE_Status status;
|
|
|
|
SANE_Int num_found = 0;
|
|
|
|
double w, l, x, y, res;
|
|
|
|
struct barcode_data buf;
|
|
|
|
size_t buf_size = sizeof(buf);
|
|
|
|
DBG (3, "read_barcode_data called\n");
|
|
|
|
|
|
|
|
memset (&cmd, 0, sizeof (cmd));
|
|
|
|
cmd[0] = BH_SCSI_READ_SCANNED_DATA;
|
|
|
|
cmd[2] = s->readlist[s->readptr];
|
|
|
|
_lto3b(buf_size, &cmd[6]); /* transfer length */
|
|
|
|
|
|
|
|
s->barcode_not_found = SANE_FALSE;
|
|
|
|
do {
|
|
|
|
memset (&buf, 0, sizeof(buf));
|
|
|
|
status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
break;
|
|
|
|
if (s->barcode_not_found == SANE_TRUE)
|
|
|
|
break;
|
|
|
|
|
|
|
|
num_found++;
|
|
|
|
|
|
|
|
buf.barcodedata[sizeof(buf.barcodedata)-1] = '\0';
|
|
|
|
|
|
|
|
/* calculate the bounding rectangle */
|
|
|
|
x = MIN((int) _2btol(buf.posxb), (int) _2btol(buf.posxa));
|
|
|
|
y = MIN((int) _2btol(buf.posyb), (int) _2btol(buf.posyd));
|
|
|
|
w = MAX((int) _2btol(buf.posxd), (int) _2btol(buf.posxd)) - x;
|
|
|
|
l = MAX((int) _2btol(buf.posya), (int) _2btol(buf.posyc)) - y;
|
|
|
|
/* convert from pixels to mm */
|
|
|
|
res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
|
|
|
|
if (res <= 0.0)
|
|
|
|
{
|
|
|
|
/* avoid divide by zero */
|
|
|
|
DBG(1, "read_barcode_data: warning: "
|
|
|
|
"encountered bad resolution value '%f', replacing with '%f'\n",
|
|
|
|
res, 200.0);
|
|
|
|
res = 200.0;
|
|
|
|
}
|
|
|
|
x = x * MM_PER_INCH / res;
|
|
|
|
y = y * MM_PER_INCH / res;
|
|
|
|
w = w * MM_PER_INCH / res;
|
|
|
|
l = l * MM_PER_INCH / res;
|
|
|
|
/* add a bit of a border around the edges */
|
|
|
|
x = MAX(0.0, x - BH_DECODE_FUDGE);
|
|
|
|
y = MAX(0.0, y - BH_DECODE_FUDGE);
|
|
|
|
w += (BH_DECODE_FUDGE * 4);
|
|
|
|
l += (BH_DECODE_FUDGE * 4);
|
|
|
|
|
|
|
|
/* write the decoded barcode data into the file */
|
|
|
|
fprintf(fp, "<barcode>\n <section>%s</section>\n",
|
|
|
|
print_read_type((int) s->readlist[s->readptr]));
|
|
|
|
fprintf(fp, " <type>%s</type>\n <status-flag>%d</status-flag>\n",
|
|
|
|
print_barcodetype((int) _2btol(buf.barcodetype)),
|
|
|
|
(int) _2btol(buf.statusflag));
|
|
|
|
fprintf(fp, " <orientation>%s</orientation>\n",
|
|
|
|
print_orientation((int) _2btol(buf.barcodeorientation)));
|
|
|
|
fprintf(fp, " <location>\n <tl><x>%d</x><y>%d</y></tl>\n",
|
|
|
|
(int) _2btol(buf.posxb), (int) _2btol(buf.posyb));
|
|
|
|
fprintf(fp, " <tr><x>%d</x><y>%d</y></tr>\n",
|
|
|
|
(int) _2btol(buf.posxd), (int) _2btol(buf.posyd));
|
|
|
|
fprintf(fp, " <bl><x>%d</x><y>%d</y></bl>\n",
|
|
|
|
(int) _2btol(buf.posxa), (int) _2btol(buf.posya));
|
|
|
|
fprintf(fp, " <br><x>%d</x><y>%d</y></br>\n </location>\n",
|
|
|
|
(int) _2btol(buf.posxc), (int) _2btol(buf.posyc));
|
|
|
|
fprintf(fp, " <rectangle>%.2fx%.2f+%.2f+%.2f</rectangle>\n",
|
|
|
|
w, l, x, y);
|
|
|
|
fprintf(fp, " <search-time>%d</search-time>\n <length>%d</length>\n",
|
|
|
|
(int) _2btol(buf.barcodesearchtime),
|
|
|
|
(int) buf.barcodelen);
|
|
|
|
fprintf(fp, " <data>%s</data>\n</barcode>\n",
|
|
|
|
buf.barcodedata);
|
|
|
|
} while (num_found <= BH_DECODE_TRIES);
|
|
|
|
|
|
|
|
DBG (3, "read_barcode_data: found %d barcodes, returning %s\n",
|
|
|
|
num_found, sane_strstatus(status));
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
read_icon_data (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static SANE_Byte cmd[10];
|
|
|
|
SANE_Status status;
|
|
|
|
struct icon_data buf;
|
|
|
|
size_t buf_size = sizeof(buf);
|
|
|
|
DBG (3, "read_icon_data called\n");
|
|
|
|
|
|
|
|
memset (&cmd, 0, sizeof (cmd));
|
|
|
|
cmd[0] = BH_SCSI_READ_SCANNED_DATA;
|
|
|
|
cmd[2] = s->readlist[s->readptr];
|
|
|
|
_lto3b(buf_size, &cmd[6]); /* transfer length */
|
|
|
|
|
|
|
|
memset (&buf, 0, sizeof(buf));
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size);
|
|
|
|
|
|
|
|
/* set the fields in the scanner handle for later reference */
|
|
|
|
s->iconwidth = _4btol(buf.iconwidth);
|
|
|
|
s->iconlength = _4btol(buf.iconlength);
|
|
|
|
|
|
|
|
DBG(3, "read_icon_data: windowwidth:%lu, windowlength:%lu\n",
|
|
|
|
_4btol(buf.windowwidth),
|
|
|
|
_4btol(buf.windowlength));
|
|
|
|
DBG(3, "read_icon_data: iconwidth:%lu, iconlength:%lu, iconwidth(bytes):%lu\n",
|
|
|
|
_4btol(buf.iconwidth),
|
|
|
|
_4btol(buf.iconlength),
|
|
|
|
_4btol(buf.iconwidthbytes));
|
|
|
|
DBG(3, "read_icon_data: bitordering:%02x, icondatalen:%lu\n",
|
|
|
|
buf.bitordering,
|
|
|
|
_4btol(buf.icondatalen));
|
|
|
|
|
|
|
|
DBG (3, "read_icon_data returning %d\n", status);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
read_barfile (BH_Scanner *s, void *buf, size_t *buf_size)
|
|
|
|
{
|
|
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
|
|
size_t nread;
|
|
|
|
DBG (3, "read_barfile called (%lu bytes)\n", (u_long) *buf_size);
|
|
|
|
|
|
|
|
if (s->barf != NULL)
|
|
|
|
{
|
|
|
|
/* this function needs to set InvalidBytes so it looks
|
|
|
|
* like a B&H scsi EOF
|
|
|
|
*/
|
|
|
|
if ((nread = fread(buf, 1, *buf_size, s->barf)) < *buf_size)
|
|
|
|
{
|
|
|
|
/* set InvalidBytes */
|
|
|
|
s->InvalidBytes = *buf_size - nread;
|
|
|
|
|
|
|
|
if (ferror(s->barf))
|
|
|
|
{
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
fclose(s->barf);
|
|
|
|
s->barf = NULL;
|
|
|
|
unlink(s->barfname);
|
|
|
|
}
|
|
|
|
else if (feof(s->barf))
|
|
|
|
{
|
|
|
|
/* it also needs to close the file and delete it when EOF is
|
|
|
|
* reached.
|
|
|
|
*/
|
|
|
|
fclose(s->barf);
|
|
|
|
s->barf = NULL;
|
|
|
|
unlink(s->barfname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* set InvalidBytes */
|
|
|
|
s->InvalidBytes = *buf_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
read_data (BH_Scanner *s, void *buf, size_t *buf_size)
|
|
|
|
{
|
|
|
|
static SANE_Byte cmd[10];
|
|
|
|
SANE_Status status;
|
|
|
|
DBG (3, "read_data called (%lu bytes)\n", (u_long) *buf_size);
|
|
|
|
|
|
|
|
if (s->readlist[s->readptr] == BH_SCSI_READ_TYPE_SENDBARFILE)
|
|
|
|
{
|
|
|
|
/* call special barcode data read function. */
|
|
|
|
status = read_barfile(s, buf, buf_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memset (&cmd, 0, sizeof (cmd));
|
|
|
|
cmd[0] = BH_SCSI_READ_SCANNED_DATA;
|
|
|
|
cmd[2] = s->readlist[s->readptr];
|
|
|
|
_lto3b(*buf_size, &cmd[6]); /* transfer length */
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, buf_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
mode_select_measurement (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[6];
|
|
|
|
struct mode_page_03 mp;
|
|
|
|
} select_cmd;
|
|
|
|
SANE_Status status;
|
|
|
|
|
|
|
|
DBG (3, "mode_select_measurement called (bmu:%d mud:%d)\n",
|
|
|
|
s->bmu, s->mud);
|
|
|
|
|
|
|
|
memset (&select_cmd, 0, sizeof (select_cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
|
|
|
|
select_cmd.cmd[1] = 0x10;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
select_cmd.mp.pagecode = BH_MODE_MEASUREMENT_PAGE_CODE;
|
|
|
|
select_cmd.mp.paramlen = 0x06;
|
|
|
|
select_cmd.mp.bmu = s->bmu;
|
|
|
|
_lto2b(s->mud, select_cmd.mp.mud);
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
mode_select_timeout (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[6];
|
|
|
|
struct mode_page_20 mp;
|
|
|
|
} select_cmd;
|
|
|
|
SANE_Status status;
|
|
|
|
|
|
|
|
DBG (3, "mode_select_timeout called\n");
|
|
|
|
|
|
|
|
memset (&select_cmd, 0, sizeof (select_cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
|
|
|
|
select_cmd.cmd[1] = 0x10;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
select_cmd.mp.pagecode = BH_MODE_TIMEOUT_PAGE_CODE;
|
|
|
|
select_cmd.mp.paramlen = 0x06;
|
|
|
|
select_cmd.mp.timeoutmanual = _OPT_VAL_WORD(s, OPT_TIMEOUT_MANUAL);
|
|
|
|
select_cmd.mp.timeoutadf = _OPT_VAL_WORD(s, OPT_TIMEOUT_ADF);
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
mode_select_icon (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[6];
|
|
|
|
struct mode_page_21 mp;
|
|
|
|
} select_cmd;
|
|
|
|
SANE_Status status;
|
|
|
|
|
|
|
|
DBG (3, "mode_select_icon called\n");
|
|
|
|
|
|
|
|
memset (&select_cmd, 0, sizeof (select_cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
|
|
|
|
select_cmd.cmd[1] = 0x10;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
select_cmd.mp.pagecode = BH_MODE_ICON_PAGE_CODE;
|
|
|
|
select_cmd.mp.paramlen = 0x06;
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_ICON_WIDTH), select_cmd.mp.iconwidth);
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_ICON_LENGTH), select_cmd.mp.iconlength);
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
mode_select_barcode_priority (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[6];
|
|
|
|
struct mode_page_30 mp;
|
|
|
|
} select_cmd;
|
|
|
|
SANE_Status status;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DBG (3, "mode_select_barcode_priority called\n");
|
|
|
|
|
|
|
|
memset (&select_cmd, 0, sizeof (select_cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
|
|
|
|
select_cmd.cmd[1] = 0x10;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
select_cmd.mp.pagecode = BH_MODE_BARCODE_PRIORITY_PAGE_CODE;
|
|
|
|
select_cmd.mp.paramlen = 0x06;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_SEARCH_BARS; i++)
|
|
|
|
{
|
|
|
|
/* anything after a 'none' is ignored */
|
|
|
|
if ((select_cmd.mp.priority[i] = s->search_bars[i]) == 0) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
mode_select_barcode_param1 (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[6];
|
|
|
|
struct mode_page_31 mp;
|
|
|
|
} select_cmd;
|
|
|
|
SANE_Status status;
|
|
|
|
|
|
|
|
DBG (3, "mode_select_barcode_param1 called\n");
|
|
|
|
|
|
|
|
memset (&select_cmd, 0, sizeof (select_cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
|
|
|
|
select_cmd.cmd[1] = 0x10;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM1_PAGE_CODE;
|
|
|
|
select_cmd.mp.paramlen = 0x06;
|
|
|
|
|
|
|
|
_lto2b((SANE_Int)_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BARCODE_HMIN), select_cmd.mp.minbarheight);
|
|
|
|
select_cmd.mp.searchcount = _OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_COUNT);
|
|
|
|
select_cmd.mp.searchmode =
|
|
|
|
get_barcode_search_mode(_OPT_VAL_STRING(s, OPT_BARCODE_SEARCH_MODE));
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_TIMEOUT), select_cmd.mp.searchtimeout);
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
mode_select_barcode_param2 (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[6];
|
|
|
|
struct mode_page_32 mp;
|
|
|
|
} select_cmd;
|
|
|
|
SANE_Status status;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
DBG (3, "mode_select_barcode_param2 called\n");
|
|
|
|
|
|
|
|
/* first we'll do a mode sense, then we'll overwrite with
|
|
|
|
* our new values, and then do a mode select
|
|
|
|
*/
|
|
|
|
memset (&select_cmd, 0, sizeof (select_cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SENSE;
|
|
|
|
select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM2_PAGE_CODE;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
len = sizeof(select_cmd.mp);
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd),
|
|
|
|
&select_cmd.mp, &len);
|
|
|
|
|
|
|
|
if (status == SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(8, "mode_select_barcode_param2: sensed values: relmax:%d barmin:%d barmax:%d\n",
|
|
|
|
(int) _2btol(select_cmd.mp.relmax),
|
|
|
|
(int) _2btol(select_cmd.mp.barmin),
|
|
|
|
(int) _2btol(select_cmd.mp.barmax));
|
|
|
|
|
|
|
|
memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
|
|
|
|
select_cmd.cmd[1] = 0x10;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
select_cmd.mp.modedatalen = 0x00;
|
|
|
|
select_cmd.mp.mediumtype = 0x00;
|
|
|
|
select_cmd.mp.devicespecificparam = 0x00;
|
|
|
|
select_cmd.mp.blockdescriptorlen = 0x00;
|
|
|
|
|
|
|
|
select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM2_PAGE_CODE;
|
|
|
|
select_cmd.mp.paramlen = 0x06;
|
|
|
|
|
|
|
|
/* only overwrite the default values if the option is non-zero */
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX) != 0)
|
|
|
|
{
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX), select_cmd.mp.relmax);
|
|
|
|
}
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN) != 0)
|
|
|
|
{
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN), select_cmd.mp.barmin);
|
|
|
|
}
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX) != 0)
|
|
|
|
{
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX), select_cmd.mp.barmax);
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(8, "mode_select_barcode_param2: param values: relmax:%d barmin:%d barmax:%d\n",
|
|
|
|
(int) _OPT_VAL_WORD(s, OPT_BARCODE_RELMAX),
|
|
|
|
(int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMIN),
|
|
|
|
(int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMAX));
|
|
|
|
|
|
|
|
DBG(8, "mode_select_barcode_param2: select values: relmax:%d barmin:%d barmax:%d\n",
|
|
|
|
(int) _2btol(select_cmd.mp.relmax),
|
|
|
|
(int) _2btol(select_cmd.mp.barmin),
|
|
|
|
(int) _2btol(select_cmd.mp.barmax));
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
mode_select_barcode_param3 (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[6];
|
|
|
|
struct mode_page_33 mp;
|
|
|
|
} select_cmd;
|
|
|
|
SANE_Status status;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
DBG (3, "mode_select_barcode_param3 called\n");
|
|
|
|
|
|
|
|
/* first we'll do a mode sense, then we'll overwrite with
|
|
|
|
* our new values, and then do a mode select
|
|
|
|
*/
|
|
|
|
memset (&select_cmd, 0, sizeof (select_cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SENSE;
|
|
|
|
select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM3_PAGE_CODE;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
len = sizeof(select_cmd.mp);
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd),
|
|
|
|
&select_cmd.mp, &len);
|
|
|
|
|
|
|
|
if (status == SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(8, "mode_select_barcode_param3: sensed values: contrast:%d patchmode:%d\n",
|
|
|
|
(int) _2btol(select_cmd.mp.barcodecontrast),
|
|
|
|
(int) _2btol(select_cmd.mp.patchmode));
|
|
|
|
|
|
|
|
memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd));
|
|
|
|
select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
|
|
|
|
select_cmd.cmd[1] = 0x10;
|
|
|
|
select_cmd.cmd[4] = sizeof(select_cmd.mp);
|
|
|
|
|
|
|
|
select_cmd.mp.modedatalen = 0x00;
|
|
|
|
select_cmd.mp.mediumtype = 0x00;
|
|
|
|
select_cmd.mp.devicespecificparam = 0x00;
|
|
|
|
select_cmd.mp.blockdescriptorlen = 0x00;
|
|
|
|
|
|
|
|
select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM3_PAGE_CODE;
|
|
|
|
select_cmd.mp.paramlen = 0x06;
|
|
|
|
|
|
|
|
/* only overwrite the default values if the option is non-zero */
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST) != 0)
|
|
|
|
{
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST), select_cmd.mp.barcodecontrast);
|
|
|
|
}
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE) != 0)
|
|
|
|
{
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE), select_cmd.mp.patchmode);
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(8, "mode_select_barcode_param3: param values: contrast:%d patchmode:%d\n",
|
|
|
|
(int) _OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST),
|
|
|
|
(int) _OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE));
|
|
|
|
|
|
|
|
DBG(8, "mode_select_barcode_param3: select values: contrast:%d patchmode:%d\n",
|
|
|
|
(int) _2btol(select_cmd.mp.barcodecontrast),
|
|
|
|
(int) _2btol(select_cmd.mp.patchmode));
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
inquiry (int fd, void *buf, size_t *buf_size, SANE_Byte evpd, SANE_Byte page_code)
|
|
|
|
{
|
|
|
|
static SANE_Byte cmd[6];
|
|
|
|
SANE_Status status;
|
|
|
|
DBG (3, "inquiry called\n");
|
|
|
|
|
|
|
|
memset (cmd, 0, sizeof (cmd));
|
|
|
|
cmd[0] = BH_SCSI_INQUIRY;
|
|
|
|
cmd[1] = evpd;
|
|
|
|
cmd[2] = page_code;
|
|
|
|
cmd[4] = *buf_size;
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
set_window (BH_Scanner *s, SANE_Byte batchmode)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
SANE_Byte cmd[10];
|
|
|
|
SANE_Byte hdr[8];
|
|
|
|
struct window_data window;
|
|
|
|
} set_window_cmd;
|
|
|
|
SANE_Status status;
|
2001-02-10 18:23:28 +00:00
|
|
|
SANE_Int width, length, i, format, rotation, deskew ;
|
2001-01-20 23:42:12 +00:00
|
|
|
|
|
|
|
DBG (3, "set_window called\n");
|
|
|
|
|
|
|
|
/* set to thousandths for set_window */
|
|
|
|
s->bmu = BH_UNIT_INCH;
|
|
|
|
s->mud = 1000;
|
|
|
|
status = mode_select_measurement(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
memset (&set_window_cmd, 0, sizeof (set_window_cmd));
|
|
|
|
set_window_cmd.cmd[0] = BH_SCSI_SET_WINDOW;
|
|
|
|
DBG(3, "set_window: sizeof(hdr) %d, sizeof(window): %d\n",
|
|
|
|
(int)sizeof(set_window_cmd.hdr), (int)sizeof(set_window_cmd.window));
|
|
|
|
|
|
|
|
_lto3b(sizeof(set_window_cmd.hdr) + sizeof(set_window_cmd.window),
|
|
|
|
&set_window_cmd.cmd[6]);
|
|
|
|
|
|
|
|
_lto2b(256, &set_window_cmd.hdr[6]);
|
|
|
|
|
|
|
|
set_window_cmd.window.windowid = 0;
|
|
|
|
set_window_cmd.window.autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER);
|
2001-02-10 18:23:28 +00:00
|
|
|
DBG (5, "autoborder set to=%d\n", set_window_cmd.window.autoborder);
|
2001-01-20 23:42:12 +00:00
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.xres);
|
|
|
|
_lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.yres);
|
|
|
|
_lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X), set_window_cmd.window.ulx);
|
|
|
|
_lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y), set_window_cmd.window.uly);
|
|
|
|
|
|
|
|
width = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X) -
|
|
|
|
_OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X));
|
|
|
|
length = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y) -
|
|
|
|
_OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y));
|
|
|
|
|
|
|
|
_lto4b(width, set_window_cmd.window.windowwidth);
|
|
|
|
_lto4b(length, set_window_cmd.window.windowlength);
|
|
|
|
|
|
|
|
/* brightness (1-255) 0 is default, aka 128. Ignored with ACE scanners */
|
|
|
|
set_window_cmd.window.brightness = _OPT_VAL_WORD(s, OPT_BRIGHTNESS);
|
|
|
|
/* threshold (1-255) 0 is default, aka 128. Ignored with ACE scanners */
|
|
|
|
set_window_cmd.window.threshold = _OPT_VAL_WORD(s, OPT_THRESHOLD);
|
|
|
|
/*!!! contrast (not used) */
|
|
|
|
/*!!! set_window_cmd.window.contrast = _OPT_VAL_WORD(s, OPT_CONTRAST); */
|
|
|
|
/* imagecomposition 0x00 lineart, 0x01 dithered/halftone, 0x02 grayscale*/
|
|
|
|
set_window_cmd.window.imagecomposition =
|
|
|
|
get_scan_mode_id(_OPT_VAL_STRING(s, OPT_SCAN_MODE));
|
|
|
|
|
|
|
|
set_window_cmd.window.bitsperpixel = 0x01;
|
|
|
|
/*!!! halftone code (not used) */
|
|
|
|
/*!!! halftone id (not used) */
|
|
|
|
|
|
|
|
set_window_cmd.window.paddingtype = 0x03; /* truncate byte */
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_NEGATIVE) == SANE_TRUE) {
|
|
|
|
/* reverse image format (valid when bitsperpixel=1)
|
|
|
|
* 0x00 normal, 0x01 reversed. This is bit 7 of paddingtype.
|
|
|
|
*/
|
|
|
|
set_window_cmd.window.paddingtype |= 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_window_cmd.window.bitordering[0] = 0x00;
|
|
|
|
|
|
|
|
/* we must always sent plain gray data in preview mode */
|
|
|
|
format = (_OPT_VAL_WORD(s, OPT_PREVIEW)) ?
|
|
|
|
BH_COMP_NONE :
|
|
|
|
get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
|
|
|
|
|
|
|
|
switch (format)
|
|
|
|
{
|
|
|
|
case BH_COMP_G31D:
|
|
|
|
set_window_cmd.window.compressiontype = 0x01;
|
|
|
|
set_window_cmd.window.compressionarg = 0x00;
|
|
|
|
set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
|
|
|
|
break;
|
|
|
|
case BH_COMP_G32D:
|
|
|
|
set_window_cmd.window.compressiontype = 0x02;
|
|
|
|
set_window_cmd.window.compressionarg = 0x04;
|
|
|
|
set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
|
|
|
|
break;
|
|
|
|
case BH_COMP_G42D:
|
|
|
|
set_window_cmd.window.compressiontype = 0x03;
|
|
|
|
set_window_cmd.window.compressionarg = 0x00;
|
|
|
|
set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
|
|
|
|
break;
|
|
|
|
case BH_COMP_NONE:
|
|
|
|
default:
|
|
|
|
set_window_cmd.window.compressiontype = 0x00;
|
|
|
|
set_window_cmd.window.compressionarg = 0x00;
|
|
|
|
set_window_cmd.window.bitordering[1] = 0x00; /* n/a */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2001-02-10 18:23:28 +00:00
|
|
|
/* rotation and deskew settings, if autoborder is turned on */
|
|
|
|
if(set_window_cmd.window.autoborder){ /*--- setting byte 46 of the window descriptor block only works with autoborder */
|
|
|
|
rotation = get_rotation_id(_OPT_VAL_STRING(s, OPT_ROTATION));
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_DESKEW) == SANE_TRUE) deskew = BH_DESKEW_ENABLE;
|
|
|
|
else deskew = BH_DESKEW_DISABLE;
|
|
|
|
set_window_cmd.window.border_rotation = ( rotation | deskew ); /*--- deskew assumes autoborder */
|
|
|
|
}
|
|
|
|
|
2001-01-20 23:42:12 +00:00
|
|
|
/* remote - 0x00 ACE set in window; 0x01 ACE set by control panel */
|
|
|
|
set_window_cmd.window.remote = _OPT_VAL_WORD(s, OPT_CONTROL_PANEL);
|
|
|
|
if (set_window_cmd.window.remote == 0x00) {
|
|
|
|
/* acefunction (ignored on non-ACE scanners) */
|
|
|
|
set_window_cmd.window.acefunction = _OPT_VAL_WORD(s, OPT_ACE_FUNCTION);
|
|
|
|
/* acesensitivity (ignored on non-ACE scanners) */
|
|
|
|
set_window_cmd.window.acesensitivity = _OPT_VAL_WORD(s, OPT_ACE_SENSITIVITY);
|
|
|
|
}
|
|
|
|
|
|
|
|
set_window_cmd.window.batchmode = batchmode;
|
|
|
|
|
|
|
|
/* fill in the section descriptor blocks */
|
|
|
|
for (i = 0; i < s->num_sections; i++)
|
|
|
|
{
|
|
|
|
BH_SectionBlock *b;
|
|
|
|
|
|
|
|
b = &set_window_cmd.window.sectionblock[i];
|
|
|
|
|
|
|
|
_lto4b(s->sections[i].left, b->ul_x);
|
|
|
|
_lto4b(s->sections[i].top, b->ul_y);
|
|
|
|
_lto4b(s->sections[i].width, b->width);
|
|
|
|
_lto4b(s->sections[i].length, b->length);
|
|
|
|
b->compressiontype = s->sections[i].compressiontype;
|
|
|
|
b->compressionarg = s->sections[i].compressionarg;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &set_window_cmd, sizeof (set_window_cmd), 0, 0);
|
2001-02-10 18:23:28 +00:00
|
|
|
DBG (5, "sanei_scsi_cmd executed, status=%d\n", status );
|
2001-01-20 23:42:12 +00:00
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
/* set to points for reading */
|
|
|
|
s->bmu = BH_UNIT_POINT;
|
|
|
|
s->mud = 1;
|
|
|
|
status = mode_select_measurement(s);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
get_window (BH_Scanner *s, SANE_Int *w, SANE_Int *h, SANE_Bool backpage)
|
|
|
|
{
|
|
|
|
SANE_Byte cmd[10];
|
|
|
|
static struct {
|
|
|
|
SANE_Byte hdr[8];
|
|
|
|
struct window_data window;
|
|
|
|
} get_window_data;
|
|
|
|
SANE_Status status;
|
2001-02-10 18:23:28 +00:00
|
|
|
SANE_Int x, y, i = 0, get_window_delay = 1;
|
2001-01-20 23:42:12 +00:00
|
|
|
SANE_Bool autoborder;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
DBG (3, "get_window called\n");
|
|
|
|
|
|
|
|
autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER) == 1;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
memset (&cmd, 0, sizeof (cmd));
|
|
|
|
memset (&get_window_data, 0, sizeof (get_window_data));
|
|
|
|
|
|
|
|
cmd[0] = BH_SCSI_GET_WINDOW;
|
|
|
|
_lto3b(sizeof(get_window_data), &cmd[6]);
|
|
|
|
|
|
|
|
_lto2b(256, &get_window_data.hdr[6]);
|
|
|
|
|
|
|
|
get_window_data.window.windowid = (backpage == SANE_TRUE) ? 1 : 0;
|
|
|
|
|
|
|
|
len = sizeof(get_window_data);
|
|
|
|
status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd),
|
|
|
|
&get_window_data, &len);
|
|
|
|
if (status == SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
x =_4btol(get_window_data.window.ulx);
|
|
|
|
y =_4btol(get_window_data.window.uly);
|
|
|
|
*w =_4btol(get_window_data.window.windowwidth);
|
|
|
|
*h =_4btol(get_window_data.window.windowlength);
|
|
|
|
|
|
|
|
if (autoborder)
|
|
|
|
{
|
|
|
|
/* we try repeatedly until we get the autoborder bit set */
|
|
|
|
if (get_window_data.window.autoborder != 1 &&
|
|
|
|
i < BH_AUTOBORDER_TRIES)
|
|
|
|
{
|
2001-02-10 18:23:28 +00:00
|
|
|
DBG (5, "waiting %d second[s], try: %d\n",get_window_delay,i);
|
|
|
|
sleep(get_window_delay); /*--- page 4-5 of B&H Copiscan 8000 ESC OEM Tech Manual */
|
|
|
|
/*--- requires at least 50ms wait between each GET WINDOW command */
|
|
|
|
/*--- experience shows that this can take 3 to 4 seconds */
|
2001-01-20 23:42:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (get_window_data.window.autoborder != 1)
|
|
|
|
{
|
|
|
|
DBG(1, "Automatic Border Detection not done within %d tries\n",
|
|
|
|
BH_AUTOBORDER_TRIES);
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
2001-02-10 18:23:28 +00:00
|
|
|
DBG (0, "page dimension: wide:%d high:%d \n",*w,*h);
|
2001-01-20 23:42:12 +00:00
|
|
|
}
|
|
|
|
DBG (3, "*** Window size: %dx%d+%d+%d\n", *w, *h, x, y);
|
2001-02-10 18:23:28 +00:00
|
|
|
DBG (5, "*** get_window found autoborder=%02xh\n", get_window_data.window.autoborder);
|
|
|
|
DBG (5, "*** get_window found border_rotation=%02xh\n", get_window_data.window.border_rotation);
|
2001-01-20 23:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* we are 'outta here' */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
get_parameters (SANE_Handle handle, SANE_Parameters *params)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
SANE_Int width, length, res, comp;
|
|
|
|
double br_x, tl_x, br_y, tl_y;
|
|
|
|
SANE_Frame format;
|
|
|
|
|
|
|
|
DBG(3, "get_parameters called\n");
|
|
|
|
|
|
|
|
memset (&s->params, 0, sizeof (s->params));
|
|
|
|
|
|
|
|
res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
|
|
|
|
|
|
|
|
/* make best-effort guess at what parameters will look like once
|
|
|
|
the scan starts. */
|
|
|
|
|
|
|
|
br_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X);
|
|
|
|
br_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y);
|
|
|
|
tl_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X);
|
|
|
|
tl_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y);
|
|
|
|
|
|
|
|
width = (br_x - tl_x + 1) * res / 1000.0;
|
|
|
|
length = (br_y - tl_y + 1) * res / 1000.0;
|
|
|
|
|
|
|
|
/* figure out the default image format for front/back pages */
|
|
|
|
comp = get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
|
|
|
|
switch (comp)
|
|
|
|
{
|
|
|
|
case BH_COMP_G31D:
|
|
|
|
format = SANE_FRAME_G31D;
|
|
|
|
break;
|
|
|
|
case BH_COMP_G32D:
|
|
|
|
format = SANE_FRAME_G32D;
|
|
|
|
break;
|
|
|
|
case BH_COMP_G42D:
|
|
|
|
format = SANE_FRAME_G42D;
|
|
|
|
break;
|
|
|
|
case BH_COMP_NONE:
|
|
|
|
default:
|
|
|
|
format = SANE_FRAME_GRAY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->scanning)
|
|
|
|
{
|
|
|
|
SANE_Int w, l, status;
|
|
|
|
SANE_Byte itemtype;
|
|
|
|
|
|
|
|
itemtype = s->readlist[s->readptr];
|
|
|
|
/* update parameters based on the current item */
|
|
|
|
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
|
|
if (itemtype == BH_SCSI_READ_TYPE_FRONT)
|
|
|
|
{
|
|
|
|
DBG (3, "get_parameters: sending GET WINDOW (front)\n");
|
|
|
|
status = get_window (s, &w, &l, SANE_FALSE);
|
|
|
|
if (status == SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
width = w;
|
|
|
|
length = l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (itemtype == BH_SCSI_READ_TYPE_BACK)
|
|
|
|
{
|
|
|
|
DBG (3, "get_parameters: sending GET WINDOW (back)\n");
|
|
|
|
status = get_window (s, &w, &l, SANE_TRUE);
|
|
|
|
if (status == SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
width = w;
|
|
|
|
length = l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON ||
|
|
|
|
itemtype == BH_SCSI_READ_TYPE_BACK_ICON)
|
|
|
|
{
|
|
|
|
/* the icon is never compressed */
|
|
|
|
format = SANE_FRAME_GRAY;
|
|
|
|
width = s->iconwidth;
|
|
|
|
length = s->iconlength;
|
|
|
|
}
|
|
|
|
else if (itemtype > BH_SCSI_READ_TYPE_FRONT &&
|
|
|
|
itemtype <= (BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS))
|
|
|
|
{
|
|
|
|
/* a front section */
|
|
|
|
SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_FRONT;
|
|
|
|
|
|
|
|
format = s->sections[sectnum - 1].format;
|
|
|
|
/* convert from thousandths to pixels */
|
|
|
|
width = s->sections[sectnum - 1].width * res / 1000.0;
|
|
|
|
length = s->sections[sectnum - 1].length * res / 1000.0;
|
|
|
|
}
|
|
|
|
else if (itemtype > BH_SCSI_READ_TYPE_BACK &&
|
|
|
|
itemtype <= (BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS))
|
|
|
|
{
|
|
|
|
/* a back section */
|
|
|
|
SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_BACK;
|
|
|
|
|
|
|
|
format = s->sections[sectnum - 1].format;
|
|
|
|
/* convert from thousandths to pixels */
|
|
|
|
width = s->sections[sectnum - 1].width * res / 1000.0;
|
|
|
|
length = s->sections[sectnum - 1].length * res / 1000.0;
|
|
|
|
}
|
|
|
|
else if ( (itemtype >= BH_SCSI_READ_TYPE_BACK_BARCODE &&
|
|
|
|
itemtype <= (BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS)) ||
|
|
|
|
(itemtype >= BH_SCSI_READ_TYPE_FRONT_BARCODE &&
|
|
|
|
itemtype <= (BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS)) )
|
|
|
|
{
|
|
|
|
/* decoded barcode data */
|
|
|
|
format = SANE_FRAME_TEXT;
|
|
|
|
width = 8;
|
|
|
|
length = -1;
|
|
|
|
}
|
|
|
|
else if (itemtype == BH_SCSI_READ_TYPE_SENDBARFILE)
|
|
|
|
{
|
|
|
|
/* decoded barcode data file */
|
|
|
|
format = SANE_FRAME_TEXT;
|
|
|
|
width = 8;
|
|
|
|
length = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
format = SANE_FRAME_GRAY;
|
|
|
|
width = 8;
|
|
|
|
length = -1;
|
|
|
|
DBG(1, "get_parameters: unrecognized read itemtype: %d\n",
|
|
|
|
itemtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "get_parameters: failed\n");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res <= 0 || width <= 0)
|
|
|
|
{
|
|
|
|
DBG(1, "get_parameters:illegal parameters res=%d, width=%d, length=%d\n",
|
|
|
|
res, width, length);
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we disable our compression/barcode formats in preview as well
|
|
|
|
* as with the disable_optional_frames configuration option. NOTE:
|
|
|
|
* we may still be delivering 'wierd' data and lying about it being _GRAY!
|
|
|
|
*/
|
|
|
|
if (format != SANE_FRAME_GRAY &&
|
|
|
|
(_OPT_VAL_WORD(s, OPT_PREVIEW) || disable_optional_frames))
|
|
|
|
{
|
|
|
|
DBG(1, "get_parameters: warning: delivering %s data as gray",
|
|
|
|
sane_strframe(format));
|
|
|
|
format = SANE_FRAME_GRAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->params.format = format;
|
|
|
|
s->params.depth = 1;
|
|
|
|
s->params.last_frame = SANE_TRUE;
|
|
|
|
s->params.pixels_per_line = width;
|
|
|
|
s->params.lines = length;
|
|
|
|
s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
|
|
|
|
/* The Bell and Howell truncates to the byte */
|
|
|
|
s->params.pixels_per_line = s->params.bytes_per_line * 8;
|
|
|
|
|
|
|
|
if (params)
|
|
|
|
*params = s->params;
|
|
|
|
|
|
|
|
DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, "
|
|
|
|
"lines=%d, dpi=%d\n",
|
|
|
|
(int) s->params.format,
|
|
|
|
s->params.pixels_per_line,
|
|
|
|
s->params.bytes_per_line,
|
|
|
|
s->params.lines,
|
|
|
|
res);
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
section_parse(const char *val, BH_Section *sect, SANE_Int res, SANE_Int comp)
|
|
|
|
{
|
|
|
|
SANE_Status status = SANE_STATUS_INVAL;
|
|
|
|
char buf[255+1], *x, *y, *w, *l, *f, *ep;
|
|
|
|
const char *seps = "x+:";
|
|
|
|
double mm, fpixels;
|
|
|
|
u_long pixels;
|
|
|
|
|
|
|
|
DBG(3, "section_parse called\n");
|
|
|
|
|
|
|
|
/* a section option looks something like this:
|
|
|
|
* <width>x<length>+<tl-x>+<tl-y>:<functioncodes>
|
|
|
|
* Example:
|
|
|
|
* 76.2x25.4+50.8+0:frontbar:back:front
|
|
|
|
* the width, length, tl-x, and tl-y are in mm.
|
|
|
|
* the function codes are one or more of:
|
|
|
|
* front, back, frontbar, backbar, frontpatch, backpatch
|
|
|
|
*/
|
|
|
|
if (strlen(val) > sizeof(buf) - 1)
|
|
|
|
{
|
|
|
|
DBG(1, "section_parse: option string too long\n");
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
strcpy(buf, val);
|
|
|
|
|
|
|
|
x = y = w = l = f = NULL;
|
|
|
|
w = strtok(buf, seps);
|
|
|
|
if (w) l = strtok(NULL, seps);
|
|
|
|
if (l) x = strtok(NULL, seps);
|
|
|
|
if (x) y = strtok(NULL, seps);
|
|
|
|
if (y) f = strtok(NULL, seps);
|
|
|
|
if (!x || !y || !w || !l) break;
|
|
|
|
|
|
|
|
mm = strtod(x, &ep);
|
|
|
|
if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
|
|
|
|
sect->left = mm * 1000.0 / MM_PER_INCH;
|
|
|
|
|
|
|
|
mm = strtod(y, &ep);
|
|
|
|
if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
|
|
|
|
sect->top = mm * 1000.0 / MM_PER_INCH;
|
|
|
|
|
|
|
|
mm = strtod(w, &ep);
|
|
|
|
if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
|
|
|
|
sect->width = mm * 1000.0 / MM_PER_INCH;
|
|
|
|
/* the window width must be truncated to 16 bit points */
|
|
|
|
fpixels = sect->width * res / 1000.0;
|
|
|
|
pixels = fpixels / 16;
|
|
|
|
sect->width = pixels * 16 * 1000 / res;
|
|
|
|
|
|
|
|
mm = strtod(l, &ep);
|
|
|
|
if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
|
|
|
|
sect->length = mm * 1000.0 / MM_PER_INCH;
|
|
|
|
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
|
|
while (f)
|
|
|
|
{
|
|
|
|
/* parse the function modifiers and set flags */
|
|
|
|
if (strcmp(f, "front") == 0)
|
|
|
|
sect->flags |= BH_SECTION_FRONT_IMAGE;
|
|
|
|
else if (strcmp(f, "frontbar") == 0)
|
|
|
|
sect->flags |= BH_SECTION_FRONT_BAR;
|
|
|
|
else if (strcmp(f, "frontpatch") == 0)
|
|
|
|
sect->flags |= BH_SECTION_FRONT_PATCH;
|
|
|
|
else if (strcmp(f, "back") == 0)
|
|
|
|
sect->flags |= BH_SECTION_BACK_IMAGE;
|
|
|
|
else if (strcmp(f, "backbar") == 0)
|
|
|
|
sect->flags |= BH_SECTION_BACK_BAR;
|
|
|
|
else if (strcmp(f, "backpatch") == 0)
|
|
|
|
sect->flags |= BH_SECTION_BACK_PATCH;
|
|
|
|
else if (strcmp(f, "g42d") == 0)
|
|
|
|
comp = BH_COMP_G42D;
|
|
|
|
else if (strcmp(f, "g32d") == 0)
|
|
|
|
comp = BH_COMP_G32D;
|
|
|
|
else if (strcmp(f, "g31d") == 0)
|
|
|
|
comp = BH_COMP_G31D;
|
|
|
|
else if (strcmp(f, "none") == 0)
|
|
|
|
comp = BH_COMP_NONE;
|
|
|
|
else
|
|
|
|
DBG(1, "section_parse: ignoring unrecognized function "
|
|
|
|
"code '%s'\n", f);
|
|
|
|
|
|
|
|
f = strtok(NULL, seps);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (comp)
|
|
|
|
{
|
|
|
|
case BH_COMP_G31D:
|
|
|
|
sect->compressiontype = 0x01;
|
|
|
|
sect->compressionarg = 0x00;
|
|
|
|
sect->format = SANE_FRAME_G31D;
|
|
|
|
break;
|
|
|
|
case BH_COMP_G32D:
|
|
|
|
sect->compressiontype = 0x02;
|
|
|
|
sect->compressionarg = 0x04;
|
|
|
|
sect->format = SANE_FRAME_G32D;
|
|
|
|
break;
|
|
|
|
case BH_COMP_G42D:
|
|
|
|
sect->compressiontype = 0x03;
|
|
|
|
sect->compressionarg = 0x00;
|
|
|
|
sect->format = SANE_FRAME_G42D;
|
|
|
|
break;
|
|
|
|
case BH_COMP_NONE:
|
|
|
|
default:
|
|
|
|
sect->compressiontype = 0x00;
|
|
|
|
sect->compressionarg = 0x00;
|
|
|
|
sect->format = SANE_FRAME_GRAY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(3, "section_parse: converted '%s' (mm) to "
|
|
|
|
"%ldx%ld+%ld+%ld (thousandths) "
|
|
|
|
"flags=%02x compression=[%d,%d] frame=%s\n",
|
|
|
|
val,
|
|
|
|
sect->width, sect->length, sect->left, sect->top,
|
|
|
|
sect->flags,
|
|
|
|
sect->compressiontype, sect->compressionarg,
|
|
|
|
sane_strframe(sect->format));
|
|
|
|
|
|
|
|
} while (0); /* perform 'loop' once */
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
setup_sections (BH_Scanner *s, const char *val)
|
|
|
|
{
|
|
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
|
|
SANE_Int sectnum = 0;
|
|
|
|
char buf[255+1], *section;
|
|
|
|
|
|
|
|
DBG(3, "setup_sections called\n");
|
|
|
|
|
|
|
|
memset(s->sections, '\0', sizeof(s->sections));
|
|
|
|
if (strlen(val) > sizeof(buf) - 1)
|
|
|
|
{
|
|
|
|
DBG(1, "setup_sections: option string too long\n");
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
strcpy(buf, val);
|
|
|
|
|
|
|
|
section = strtok(buf, ",");
|
|
|
|
while (section != NULL && sectnum < NUM_SECTIONS)
|
|
|
|
{
|
|
|
|
if (!allblank(section))
|
|
|
|
{
|
|
|
|
SANE_Int res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
|
|
|
|
SANE_Int format =
|
|
|
|
get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
|
|
|
|
|
|
|
|
status = section_parse(section, &s->sections[sectnum],
|
|
|
|
res, format);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1,
|
|
|
|
"setup_sections: error parsing section `%s'\n",
|
|
|
|
section);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sectnum++;
|
|
|
|
}
|
|
|
|
section += strlen(section) + 1;
|
|
|
|
if (section > buf + strlen(val)) break;
|
|
|
|
|
|
|
|
section = strtok(section, ",");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s->num_sections = sectnum;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
start_setup (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
SANE_Status status;
|
|
|
|
SANE_Bool duplex;
|
|
|
|
SANE_Int i, imagecnt;
|
|
|
|
SANE_Byte batchmode;
|
|
|
|
|
|
|
|
DBG(3, "start_setup called\n");
|
|
|
|
|
|
|
|
duplex = _OPT_VAL_WORD(s, OPT_DUPLEX);
|
|
|
|
|
|
|
|
/* get the _SECTION option, parse it and fill in the sections */
|
|
|
|
status = setup_sections(s, _OPT_VAL_STRING(s, OPT_SECTION));
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: setup_sections failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* see whether we'll be decoding barcodes and
|
|
|
|
* set the barcodes flag appropriately
|
|
|
|
*/
|
|
|
|
if (s->search_bars[0] == 0)
|
|
|
|
{
|
|
|
|
s->barcodes = SANE_FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s->barcodes = SANE_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* see whether we'll be handling icons (thumbnails)
|
|
|
|
* set the icons flag appropriately
|
|
|
|
*/
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_ICON_WIDTH) >= 8 &&
|
|
|
|
_OPT_VAL_WORD(s, OPT_ICON_LENGTH) >= 8)
|
|
|
|
{
|
|
|
|
s->icons = SANE_TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s->icons = SANE_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* calculate a new readlist for this 'batch' */
|
|
|
|
s->readptr = s->readcnt = 0;
|
|
|
|
|
|
|
|
/* always read the front image */
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT;
|
|
|
|
|
|
|
|
/* read back page only if duplex is true */
|
|
|
|
if (duplex == SANE_TRUE)
|
|
|
|
{
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add image section reads to the readlist */
|
|
|
|
for (i = 0; i < s->num_sections; i++)
|
|
|
|
{
|
|
|
|
SANE_Word flags = s->sections[i].flags;
|
|
|
|
|
|
|
|
if (flags & BH_SECTION_FRONT_IMAGE)
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT + i + 1;
|
|
|
|
if (flags & BH_SECTION_BACK_IMAGE)
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK + i + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* icons (thumbnails) */
|
|
|
|
if (s->icons)
|
|
|
|
{
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_ICON;
|
|
|
|
/* read back icon only if duplex is true */
|
|
|
|
if (duplex == SANE_TRUE)
|
|
|
|
{
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_ICON;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NOTE: It is important that all of the image data comes before
|
|
|
|
* the barcode/patchcode data.
|
|
|
|
*/
|
|
|
|
/* barcodes */
|
|
|
|
imagecnt = s->readcnt;
|
|
|
|
if (s->barcodes)
|
|
|
|
{
|
|
|
|
if (s->num_sections == 0)
|
|
|
|
{
|
|
|
|
/* we only decode the entire page(s) if there are no
|
|
|
|
* sections defined
|
|
|
|
*/
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_BARCODE;
|
|
|
|
/* read back barcode only if duplex is true */
|
|
|
|
if (duplex == SANE_TRUE)
|
|
|
|
{
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_BARCODE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* add barcode section reads to the readlist */
|
|
|
|
for (i = 0; i < s->num_sections; i++)
|
|
|
|
{
|
|
|
|
SANE_Word flags = s->sections[i].flags;
|
|
|
|
|
|
|
|
if (flags & BH_SECTION_FRONT_BAR)
|
|
|
|
s->readlist[s->readcnt++] =
|
|
|
|
BH_SCSI_READ_TYPE_FRONT_BARCODE + i + 1;
|
|
|
|
if (flags & BH_SECTION_BACK_BAR)
|
|
|
|
s->readlist[s->readcnt++] =
|
|
|
|
BH_SCSI_READ_TYPE_BACK_BARCODE + i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* patchcodes */
|
|
|
|
if (s->patchcodes)
|
|
|
|
{
|
|
|
|
if (s->num_sections == 0)
|
|
|
|
{
|
|
|
|
/* we only decode the entire page(s) if there are no
|
|
|
|
* sections defined
|
|
|
|
*/
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_PATCHCODE;
|
|
|
|
/* read back patchcode only if duplex is true */
|
|
|
|
if (duplex == SANE_TRUE)
|
|
|
|
{
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_PATCHCODE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* add patchcode section reads to the readlist */
|
|
|
|
for (i = 0; i < s->num_sections; i++)
|
|
|
|
{
|
|
|
|
SANE_Word flags = s->sections[i].flags;
|
|
|
|
|
|
|
|
if (flags & BH_SECTION_FRONT_PATCH)
|
|
|
|
s->readlist[s->readcnt++] =
|
|
|
|
BH_SCSI_READ_TYPE_FRONT_PATCHCODE + i + 1;
|
|
|
|
if (flags & BH_SECTION_BACK_PATCH)
|
|
|
|
s->readlist[s->readcnt++] =
|
|
|
|
BH_SCSI_READ_TYPE_BACK_PATCHCODE + i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add the special item to the read list which transfers the barcode
|
|
|
|
* file that's built as a result of processing barcode and patchcode
|
|
|
|
* readitems. NOTE: this one must be last!
|
|
|
|
*/
|
|
|
|
if (s->readcnt > imagecnt)
|
|
|
|
{
|
|
|
|
s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_SENDBARFILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE)
|
|
|
|
{
|
|
|
|
/* if batchmode is enabled, then call set_window to
|
|
|
|
* abort the batch (even though there might not (and probably
|
|
|
|
* isn't) a batch in progress). This avoids a batch start error
|
|
|
|
* in the case where a previous batch was not aborted.
|
|
|
|
*/
|
|
|
|
DBG(5, "start_setup: calling set_window to abort batch\n");
|
|
|
|
set_window(s, BH_BATCH_ABORT);
|
|
|
|
|
|
|
|
batchmode = BH_BATCH_ENABLE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
batchmode = BH_BATCH_DISABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(5, "start_setup: duplex=%s, barcodes=%s, patchcodes=%s, "
|
|
|
|
"icons=%s, batch=%s\n",
|
|
|
|
(duplex == SANE_TRUE) ? "yes" : "no",
|
|
|
|
(s->barcodes == SANE_TRUE) ? "yes" : "no",
|
|
|
|
(s->patchcodes == SANE_TRUE) ? "yes" : "no",
|
|
|
|
(s->icons == SANE_TRUE) ? "yes" : "no",
|
|
|
|
(batchmode == BH_BATCH_ENABLE) ? "yes" : "no");
|
|
|
|
DBG(5, "start_setup: sections=%d\n", s->num_sections);
|
|
|
|
for (i = 0; i < s->num_sections; i++)
|
|
|
|
{
|
|
|
|
DBG(5, "start_setup: "
|
|
|
|
"[%d] %lux%lu+%lu+%lu flags=%02x compression=[%d,%d]\n",
|
|
|
|
i+1,
|
|
|
|
s->sections[i].width, s->sections[i].length,
|
|
|
|
s->sections[i].left, s->sections[i].top,
|
|
|
|
s->sections[i].flags,
|
|
|
|
s->sections[i].compressiontype, s->sections[i].compressionarg);
|
|
|
|
}
|
|
|
|
DBG(5, "start_setup: read list length=%d\n", s->readcnt);
|
|
|
|
for (i = 0; i < s->readcnt; i++)
|
|
|
|
{
|
|
|
|
DBG(5, "start_setup: [%d] %s\n", i+1, print_read_type(s->readlist[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(5, "start_setup: sending SET WINDOW\n");
|
|
|
|
status = set_window(s, batchmode);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: SET WINDOW failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(5, "start_setup: sending mode_select_timeout\n");
|
|
|
|
status = mode_select_timeout(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: mode_select_timeout failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->icons == SANE_TRUE)
|
|
|
|
{
|
|
|
|
DBG(5, "start_setup: sending mode_select_icon\n");
|
|
|
|
status = mode_select_icon(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: mode_select_icon failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->barcodes == SANE_TRUE)
|
|
|
|
{
|
|
|
|
DBG(5, "start_setup: sending mode_select_barcode_priority\n");
|
|
|
|
status = mode_select_barcode_priority(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: mode_select_barcode_priority failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(5, "start_setup: sending mode_select_barcode_param1\n");
|
|
|
|
status = mode_select_barcode_param1(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: mode_select_barcode_param1 failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(5, "start_setup: sending mode_select_barcode_param2\n");
|
|
|
|
status = mode_select_barcode_param2(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: mode_select_barcode_param2 failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(5, "start_setup: sending mode_select_barcode_param3\n");
|
|
|
|
status = mode_select_barcode_param3(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(1, "start_setup: mode_select_barcode_param3 failed: %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
start_scan (BH_Scanner *s)
|
|
|
|
{
|
|
|
|
static SANE_Byte cmd[8];
|
|
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
|
|
SANE_Bool check_adf, duplex;
|
|
|
|
DBG (3, "start_scan called\n");
|
|
|
|
|
|
|
|
/* SANE front ends will call this function between 'FRAMES'.
|
|
|
|
* A single scan on the B&H may result in up to 56 different
|
|
|
|
* things to read (20 are SANE image frames, 36 are non-SANE
|
|
|
|
* data - decoded bar/patch codes).
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (s->readcnt > 1 && s->scanning == SANE_TRUE)
|
|
|
|
{
|
|
|
|
DBG(3, "start_scan: any more items in the readlist?\n");
|
|
|
|
/* we've been reading data from this scan, so we just
|
|
|
|
* move on to the next item in the readlist without
|
|
|
|
* starting a new scan.
|
|
|
|
*/
|
|
|
|
s->readptr++;
|
|
|
|
if (s->readptr < s->readcnt)
|
|
|
|
{
|
|
|
|
SANE_Byte itemtype;
|
|
|
|
|
|
|
|
for (; s->readptr < s->readcnt; s->readptr++)
|
|
|
|
{
|
|
|
|
|
|
|
|
itemtype = s->readlist[s->readptr];
|
|
|
|
|
|
|
|
DBG(3, "start_scan: advance readlist(%d, %d)\n",
|
|
|
|
s->readptr,
|
|
|
|
(int) itemtype);
|
|
|
|
|
|
|
|
/* 'dance' by the non-SANE data streams
|
|
|
|
* like bar/patch code data
|
|
|
|
*/
|
|
|
|
if (!BH_HAS_IMAGE_DATA(itemtype))
|
|
|
|
{
|
2002-01-11 02:49:36 +00:00
|
|
|
int fd;
|
2001-01-20 23:42:12 +00:00
|
|
|
FILE *fp;
|
|
|
|
|
2002-01-11 02:49:36 +00:00
|
|
|
strncpy(s->barfname, "/tmp/bhXXXXXX", sizeof(s->barfname));
|
|
|
|
s->barfname[sizeof(s->barfname)-1] = '\0';
|
|
|
|
|
|
|
|
if ((mktemp(s->barfname) == NULL) &&
|
|
|
|
((fd = open(s->barfname, O_CREAT | O_EXCL | O_WRONLY, 0600)) != -1) &&
|
|
|
|
((fp = fdopen(fd, "w")) != NULL))
|
2001-01-20 23:42:12 +00:00
|
|
|
{
|
|
|
|
fprintf(fp, "<xml-stream>\n");
|
|
|
|
|
|
|
|
for (;
|
|
|
|
s->readptr < s->readcnt &&
|
|
|
|
status == SANE_STATUS_GOOD;
|
|
|
|
s->readptr++)
|
|
|
|
{
|
|
|
|
if (s->readlist[s->readptr] ==
|
|
|
|
BH_SCSI_READ_TYPE_SENDBARFILE) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
status = read_barcode_data(s, fp);
|
|
|
|
if (status != SANE_STATUS_GOOD) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(fp, "</xml-stream>\n");
|
|
|
|
|
|
|
|
/* close file; re-open for read(setting s->barfd) */
|
|
|
|
fclose(fp);
|
|
|
|
if ((s->barf = fopen(s->barfname, "r")) == NULL)
|
|
|
|
{
|
|
|
|
DBG(1, "sane_start: error opening barfile `%s'\n",
|
|
|
|
s->barfname);
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBG(1, "sane_start: error opening barfile `%s'\n",
|
|
|
|
s->barfname);
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON ||
|
|
|
|
itemtype == BH_SCSI_READ_TYPE_BACK_ICON)
|
|
|
|
{
|
|
|
|
/* read the icon header setting the iconwidth and iconlength
|
|
|
|
* to the actual values so get_parameters will have them.
|
|
|
|
* Subsequent calls to sane_read will get pure image data
|
|
|
|
* since the icon header has been consumed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
status = read_icon_data(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status == SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
/* update our parameters to reflect the new item */
|
|
|
|
status = get_parameters (s, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status != SANE_STATUS_GOOD) s->scanning = SANE_FALSE;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
/* if we reach here, we're finished with the readlist and
|
|
|
|
* will drop through to start a new scan
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s->readptr = 0;
|
|
|
|
|
|
|
|
check_adf = _OPT_VAL_WORD(s, OPT_CHECK_ADF);
|
|
|
|
duplex = _OPT_VAL_WORD(s, OPT_DUPLEX);
|
|
|
|
|
|
|
|
memset (&cmd, 0, sizeof (cmd));
|
|
|
|
cmd[0] = BH_SCSI_START_SCAN;
|
|
|
|
cmd[4] = (duplex == SANE_TRUE) ? 2 : 1;
|
|
|
|
|
|
|
|
cmd[6] = 0;
|
|
|
|
cmd[7] = 1;
|
|
|
|
|
|
|
|
if (check_adf)
|
|
|
|
{
|
|
|
|
status = object_position(s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG(3, "object_position: returned %d\n", status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0);
|
|
|
|
if (status == SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
s->scanning = SANE_TRUE;
|
|
|
|
|
|
|
|
/* update our parameters,
|
|
|
|
* now that we're scanning we'll do a GET_WINDOW
|
|
|
|
*/
|
|
|
|
status = get_parameters (s, 0);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
s->scanning = SANE_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* a sensible sense handler, courtesy of Franck;
|
|
|
|
arg is a pointer to the associated BH_Scanner structure */
|
|
|
|
static SANE_Status
|
|
|
|
sense_handler (int scsi_fd, u_char *result, void *arg)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = (BH_Scanner *) arg;
|
|
|
|
u_char sense, asc, ascq, EOM, ILI, ErrorCode, ValidData;
|
|
|
|
u_long InvalidBytes;
|
|
|
|
char *sense_str = "", *as_str = "";
|
|
|
|
SANE_Int i;
|
|
|
|
SANE_Status status = SANE_STATUS_INVAL;
|
|
|
|
SANE_Char print_sense[(16 * 3) + 1];
|
|
|
|
|
|
|
|
scsi_fd = scsi_fd; /* get rid of compiler warning */
|
|
|
|
ErrorCode = result[0] & 0x7F;
|
|
|
|
ValidData = (result[0] & 0x80) != 0;
|
|
|
|
sense = result[2] & 0x0f; /* Key */
|
|
|
|
asc = result[12]; /* Code */
|
|
|
|
ascq = result[13]; /* Qual */
|
|
|
|
EOM = (result[2] & 0x40) != 0; /* End Of Media */
|
|
|
|
ILI = (result[2] & 0x20) != 0; /* Invalid Length Indicator */
|
|
|
|
InvalidBytes = ValidData ? _4btol(&result[3]) : 0;
|
|
|
|
|
|
|
|
DBG(3, "sense_handler: result=%x, sense=%x, asc=%x, ascq=%x\n",
|
|
|
|
result[0], sense, asc, ascq);
|
|
|
|
DBG(3, "sense_handler: ErrorCode %02x ValidData: %d "
|
|
|
|
"EOM: %d ILI: %d InvalidBytes: %lu\n",
|
|
|
|
ErrorCode, ValidData, EOM, ILI, InvalidBytes);
|
|
|
|
|
|
|
|
memset(print_sense, '\0', sizeof(print_sense));
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
{
|
|
|
|
sprintf(print_sense + strlen(print_sense), "%02x ", result[i]);
|
|
|
|
}
|
|
|
|
DBG(5, "sense_handler: sense=%s\n", print_sense);
|
|
|
|
|
|
|
|
if (ErrorCode != 0x70 && ErrorCode != 0x71)
|
|
|
|
{
|
|
|
|
DBG (3, "sense_handler: error code is invalid.\n");
|
|
|
|
return SANE_STATUS_IO_ERROR; /* error code is invalid */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle each sense key;
|
|
|
|
* RSC supports 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B
|
|
|
|
*/
|
|
|
|
switch (sense)
|
|
|
|
{
|
|
|
|
case 0x00:
|
|
|
|
/* no sense */
|
|
|
|
sense_str = "No sense.";
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
|
|
if (ILI && asc == 0x00 && ascq == 0x05)
|
|
|
|
{
|
|
|
|
/* from read_data function */
|
|
|
|
as_str = "ILI bit is set.";
|
|
|
|
if (s != NULL)
|
|
|
|
{
|
|
|
|
s->InvalidBytes = InvalidBytes;
|
|
|
|
}
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
else if (EOM && asc == 0x00 && ascq == 0x02)
|
|
|
|
{
|
|
|
|
/* from adfStatus or startScan function */
|
|
|
|
as_str = "Out of paper in the hopper.";
|
|
|
|
status = SANE_STATUS_NO_DOCS;
|
|
|
|
}
|
|
|
|
else if (EOM)
|
|
|
|
{
|
|
|
|
/* from adfStatus or startScan function */
|
|
|
|
as_str = "Out of paper in the hopper.";
|
|
|
|
status = SANE_STATUS_NO_DOCS;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
/* recovered error */
|
|
|
|
sense_str = "Recovered error.";
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
/* not ready */
|
|
|
|
sense_str = "Not ready.";
|
|
|
|
status = SANE_STATUS_DEVICE_BUSY;
|
|
|
|
if (asc == 0x40 && ascq == 0x01)
|
|
|
|
{
|
|
|
|
as_str = "P.O.D. error: Scanner not found.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x40 && ascq == 0x02)
|
|
|
|
{
|
|
|
|
as_str = "P.O.D. error: Scanner not ready(paper in transport).";
|
|
|
|
status = SANE_STATUS_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
else if (asc == 0x40 && ascq == 0x03)
|
|
|
|
{
|
|
|
|
as_str = "P.O.D. error: Unknown scanner.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x03:
|
|
|
|
/* medium error */
|
|
|
|
sense_str = "Medium error.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
if (asc == 0x00 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: paper jam detected.";
|
|
|
|
status = SANE_STATUS_JAMMED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
/* hardware error */
|
|
|
|
sense_str = "Hardware error.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
if (asc == 0x60 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: illumination lamps failure.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x03)
|
|
|
|
{
|
|
|
|
as_str = "Communication error between RSC and scanner.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x06)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: page detected but lamps are off.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x07)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: camera white level problem.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x08)
|
|
|
|
{
|
|
|
|
/* could be caught from start_scan or read_data */
|
|
|
|
/* stop button pressed */
|
|
|
|
as_str = "Scanner error: operator pressed the Stop key.";
|
|
|
|
status = SANE_STATUS_NO_DOCS;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x12)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: transport motor failure.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x15)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: device / page sensor(s) bouncing.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x16)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: feeder is not attached.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x18)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: logic system general failure.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x34)
|
|
|
|
{
|
|
|
|
as_str = "Scanner error: no dual logic communication.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x05:
|
|
|
|
/* illegal request */
|
|
|
|
sense_str = "Illegal request.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
if (asc == 0x1a && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Parameter list length error.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x20 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Invalid command operation code.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x24 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
/* caught from object_position (via reverse engineering) */
|
|
|
|
/* Not supported? */
|
|
|
|
as_str = "Invalid field in CDB.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x25 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Unsupported LUN.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x26 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
/* caught from mode_select (as well as others) */
|
|
|
|
/* Bar/Patch code detection support not installed */
|
|
|
|
/* See Appendix A, Section A.5 */
|
|
|
|
as_str = "Invalid field in parameter list.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x2c && ascq == 0x00)
|
|
|
|
{
|
|
|
|
/* we were getting this in read_data during the time
|
|
|
|
that the ADF was misbehaving. Hopefully we will
|
|
|
|
not see it anymore.
|
|
|
|
*/
|
|
|
|
as_str = "Command out of sequence.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x2c && ascq == 0x01)
|
|
|
|
{
|
|
|
|
as_str = "Too many windows defined.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x2c && ascq == 0x02)
|
|
|
|
{
|
|
|
|
as_str = "Batch start error.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x2c && ascq == 0x03)
|
|
|
|
{
|
|
|
|
as_str = "Batch abort error.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x3d && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Invalid bits in IDENTIFY message.";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x06:
|
|
|
|
/* unit attention */
|
|
|
|
sense_str = "Unit attention.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
if (asc == 0x04 && ascq == 0x01)
|
|
|
|
{
|
|
|
|
as_str = "Reset detected, LUN is becoming ready.";
|
|
|
|
status = SANE_STATUS_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x07:
|
|
|
|
/* data protect */
|
|
|
|
sense_str = "Data protect.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x08:
|
|
|
|
/* blank check */
|
|
|
|
sense_str = "Blank check.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x09:
|
|
|
|
/* vendor specific */
|
|
|
|
sense_str = "Vendor specific.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x0A:
|
|
|
|
/* copy aborted */
|
|
|
|
sense_str = "Copy aborted.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x0B:
|
|
|
|
/* aborted command */
|
|
|
|
sense_str = "Aborted command.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
if (asc == 0x00 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Aborted command (unspecified error).";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x08 && ascq == 0x01)
|
|
|
|
{
|
|
|
|
/* caught from start_scan */
|
|
|
|
/* manual feed timeout */
|
|
|
|
as_str = "SCSI Time-out, paper Time-out (SCAN command).";
|
|
|
|
status = SANE_STATUS_NO_DOCS;
|
|
|
|
}
|
|
|
|
else if (asc == 0x47 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "SCSI parity error.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x00)
|
|
|
|
{
|
|
|
|
as_str = "Aborted command due to memory error.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x01)
|
|
|
|
{
|
|
|
|
/* caught from read_data */
|
|
|
|
/* section border error; border is outside the main window */
|
|
|
|
/* See Appendix A, Section A.4 */
|
|
|
|
as_str = "Section Read error (out of border).";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x02)
|
|
|
|
{
|
|
|
|
/* caught from read_data */
|
|
|
|
/* No code found; no barcode data is found */
|
|
|
|
/* See Appendix A, Section A.5 */
|
|
|
|
s->barcode_not_found = SANE_TRUE;
|
|
|
|
as_str = "No Bar/Patch Code found.";
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
else if (asc == 0x80 && ascq == 0x03)
|
|
|
|
{
|
|
|
|
as_str = "Icon Read error (out of border).";
|
|
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x0C:
|
|
|
|
/* equal */
|
|
|
|
sense_str = "Equal.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x0D:
|
|
|
|
/* volume overflow */
|
|
|
|
sense_str = "Volume overflow.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x0E:
|
|
|
|
/* miscompare */
|
|
|
|
sense_str = "Miscompare.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x0F:
|
|
|
|
/* reserved */
|
|
|
|
sense_str = "Reserved.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sense_str = "Unhandled case.";
|
|
|
|
status = SANE_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(3, "sense_handler: '%s' '%s' return:%d\n",
|
|
|
|
sense_str, as_str, status);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
init_options (BH_Scanner * s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
DBG (3, "init_options called\n");
|
|
|
|
|
|
|
|
memset (s->opt, 0, sizeof (s->opt));
|
|
|
|
memset (s->val, 0, sizeof (s->val));
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_OPTIONS; ++i)
|
|
|
|
{
|
|
|
|
s->opt[i].size = sizeof (SANE_Word);
|
|
|
|
s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
|
|
|
|
s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
|
|
|
|
s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
|
|
|
|
s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
|
|
|
|
|
|
|
|
/* "Scan Mode" group: */
|
|
|
|
s->opt[OPT_MODE_GROUP].name = "";
|
|
|
|
s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE_GROUP;
|
|
|
|
s->opt[OPT_MODE_GROUP].desc = "";
|
|
|
|
s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
|
|
|
|
s->opt[OPT_MODE_GROUP].cap = 0;
|
|
|
|
s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
|
|
|
|
/* Preview: */
|
|
|
|
s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
|
|
|
|
s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
|
|
|
|
s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
|
|
|
|
s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_PREVIEW].w = 0;
|
|
|
|
|
|
|
|
/* Inquiry */
|
|
|
|
s->opt[OPT_INQUIRY].name = SANE_NAME_INQUIRY;
|
|
|
|
s->opt[OPT_INQUIRY].title = SANE_TITLE_INQUIRY;
|
|
|
|
s->opt[OPT_INQUIRY].desc = SANE_DESC_INQUIRY;
|
|
|
|
s->opt[OPT_INQUIRY].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_INQUIRY].size = sizeof(inquiry_data);
|
|
|
|
s->opt[OPT_INQUIRY].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_INQUIRY].s = strdup(inquiry_data);
|
|
|
|
s->opt[OPT_INQUIRY].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
|
|
|
|
/* scan mode */
|
|
|
|
s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
|
|
|
|
s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
|
|
|
|
s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
|
|
|
|
s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_list);
|
|
|
|
s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_list;
|
|
|
|
s->val[OPT_SCAN_MODE].s = strdup (scan_mode_list[0]);
|
|
|
|
|
|
|
|
/* Standard resolutions */
|
|
|
|
s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
|
|
|
|
s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
|
|
|
|
s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
|
|
|
|
s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
|
|
|
|
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
|
|
s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->info.resStdList;
|
|
|
|
s->val[OPT_RESOLUTION].w = s->hw->info.res_default;
|
|
|
|
|
|
|
|
/* compression */
|
|
|
|
s->opt[OPT_COMPRESSION].name = SANE_NAME_COMPRESSION;
|
|
|
|
s->opt[OPT_COMPRESSION].title = SANE_TITLE_COMPRESSION;
|
|
|
|
s->opt[OPT_COMPRESSION].desc = SANE_DESC_COMPRESSION;
|
|
|
|
s->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_COMPRESSION].size = max_string_size (compression_list);
|
|
|
|
s->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
s->opt[OPT_COMPRESSION].constraint.string_list = compression_list;
|
|
|
|
s->val[OPT_COMPRESSION].s = strdup (compression_list[0]);
|
|
|
|
|
|
|
|
if (s->hw->info.colorHalftone == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_min_list);
|
|
|
|
s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_min_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->hw->info.comprG3_1D == SANE_FALSE ||
|
|
|
|
s->hw->info.comprG3_2D == SANE_FALSE ||
|
|
|
|
s->hw->info.comprG4 == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "Geometry" group: */
|
|
|
|
s->opt[OPT_GEOMETRY_GROUP].name = "";
|
|
|
|
s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY_GROUP;
|
|
|
|
s->opt[OPT_GEOMETRY_GROUP].desc = "";
|
|
|
|
s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
|
|
|
|
s->opt[OPT_GEOMETRY_GROUP].cap = 0;
|
|
|
|
s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
|
|
|
|
/* Autoborder: */
|
|
|
|
s->opt[OPT_AUTOBORDER].name = SANE_NAME_AUTOBORDER;
|
|
|
|
s->opt[OPT_AUTOBORDER].title = SANE_TITLE_AUTOBORDER;
|
|
|
|
s->opt[OPT_AUTOBORDER].desc = SANE_DESC_AUTOBORDER;
|
|
|
|
s->opt[OPT_AUTOBORDER].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_AUTOBORDER].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_AUTOBORDER].w = s->hw->info.autoborder_default;
|
|
|
|
|
|
|
|
/* Paper Size */
|
|
|
|
s->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
|
|
|
|
s->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
|
|
|
|
s->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
|
|
|
|
s->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_PAPER_SIZE].size = max_string_size (paper_list);
|
|
|
|
s->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
s->opt[OPT_PAPER_SIZE].constraint.string_list = paper_list;
|
|
|
|
s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]);
|
|
|
|
|
2001-02-10 18:23:28 +00:00
|
|
|
/* rotation */
|
|
|
|
s->opt[OPT_ROTATION].name = SANE_NAME_ROTATION;
|
|
|
|
s->opt[OPT_ROTATION].title = SANE_TITLE_ROTATION;
|
|
|
|
s->opt[OPT_ROTATION].desc = SANE_DESC_ROTATION;
|
|
|
|
s->opt[OPT_ROTATION].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_ROTATION].size = max_string_size (rotation_list);
|
|
|
|
s->opt[OPT_ROTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
s->opt[OPT_ROTATION].constraint.string_list = rotation_list;
|
|
|
|
s->val[OPT_ROTATION].s = strdup (rotation_list[0]);
|
|
|
|
|
|
|
|
/* Deskew: */
|
|
|
|
s->opt[OPT_DESKEW].name = SANE_NAME_DESKEW;
|
|
|
|
s->opt[OPT_DESKEW].title = SANE_TITLE_DESKEW;
|
|
|
|
s->opt[OPT_DESKEW].desc = SANE_DESC_DESKEW;
|
|
|
|
s->opt[OPT_DESKEW].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_DESKEW].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_DESKEW].w = s->hw->info.deskew_default;
|
|
|
|
|
2001-01-20 23:42:12 +00:00
|
|
|
/* top-left x */
|
|
|
|
s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
|
|
|
|
s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
|
|
|
|
s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
|
|
|
|
s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
|
|
|
|
s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
|
|
|
|
s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_TL_X].constraint.range = &(s->hw->info.x_range);
|
|
|
|
s->val[OPT_TL_X].w = SANE_FIX(0.0);
|
|
|
|
|
|
|
|
/* top-left y */
|
|
|
|
s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
|
|
|
|
s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
|
|
|
|
s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
|
|
|
|
s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
|
|
|
|
s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
|
|
|
|
s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_TL_Y].constraint.range = &(s->hw->info.y_range);
|
|
|
|
s->val[OPT_TL_Y].w = SANE_FIX(0.0);
|
|
|
|
|
|
|
|
/* bottom-right x */
|
|
|
|
s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
|
|
|
|
s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
|
|
|
|
s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
|
|
|
|
s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
|
|
|
|
s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
|
|
|
|
s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BR_X].constraint.range = &(s->hw->info.x_range);
|
|
|
|
s->val[OPT_BR_X].w = s->hw->info.x_range.max;
|
|
|
|
|
|
|
|
/* bottom-right y */
|
|
|
|
s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
|
|
|
|
s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
|
|
|
|
s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
|
|
|
|
s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
|
|
|
|
s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
|
|
|
|
s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BR_Y].constraint.range = &(s->hw->info.y_range);
|
|
|
|
s->val[OPT_BR_Y].w = s->hw->info.y_range.max;
|
|
|
|
|
|
|
|
if (s->hw->info.canBorderRecog == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_AUTOBORDER].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "Feeder" group: */
|
|
|
|
s->opt[OPT_FEEDER_GROUP].name = "";
|
|
|
|
s->opt[OPT_FEEDER_GROUP].title = SANE_TITLE_FEEDER_GROUP;
|
|
|
|
s->opt[OPT_FEEDER_GROUP].desc = "";
|
|
|
|
s->opt[OPT_FEEDER_GROUP].type = SANE_TYPE_GROUP;
|
|
|
|
s->opt[OPT_FEEDER_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
s->opt[OPT_FEEDER_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
|
|
|
|
/* scan source */
|
|
|
|
s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
|
|
|
|
s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
|
|
|
|
s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
|
|
|
|
s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_SCAN_SOURCE].size = max_string_size (scan_source_list);
|
|
|
|
s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
s->opt[OPT_SCAN_SOURCE].constraint.string_list = scan_source_list;
|
|
|
|
s->val[OPT_SCAN_SOURCE].s = strdup (scan_source_list[0]);
|
|
|
|
|
|
|
|
/* Batch: */
|
|
|
|
s->opt[OPT_BATCH].name = SANE_NAME_BATCH;
|
|
|
|
s->opt[OPT_BATCH].title = SANE_TITLE_BATCH;
|
|
|
|
s->opt[OPT_BATCH].desc = SANE_DESC_BATCH;
|
|
|
|
s->opt[OPT_BATCH].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_BATCH].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_BATCH].w = s->hw->info.batch_default;
|
|
|
|
|
|
|
|
/* Check ADF: */
|
|
|
|
s->opt[OPT_CHECK_ADF].name = SANE_NAME_CHECK_ADF;
|
|
|
|
s->opt[OPT_CHECK_ADF].title = SANE_TITLE_CHECK_ADF;
|
|
|
|
s->opt[OPT_CHECK_ADF].desc = SANE_DESC_CHECK_ADF;
|
|
|
|
s->opt[OPT_CHECK_ADF].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_CHECK_ADF].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_CHECK_ADF].w = s->hw->info.check_adf_default;
|
|
|
|
|
|
|
|
/* Duplex: */
|
|
|
|
s->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
|
|
|
|
s->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
|
|
|
|
s->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
|
|
|
|
s->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_DUPLEX].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_DUPLEX].w = s->hw->info.duplex_default;
|
|
|
|
|
|
|
|
/* timeout adf */
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].name = SANE_NAME_TIMEOUT_ADF;
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].title = SANE_TITLE_TIMEOUT_ADF;
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].desc = SANE_DESC_TIMEOUT_ADF;
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_TIMEOUT_ADF].w = s->hw->info.timeout_adf_default;
|
|
|
|
|
|
|
|
/* timeout manual */
|
|
|
|
s->opt[OPT_TIMEOUT_MANUAL].name = SANE_NAME_TIMEOUT_MANUAL;
|
|
|
|
s->opt[OPT_TIMEOUT_MANUAL].title = SANE_TITLE_TIMEOUT_MANUAL;
|
|
|
|
s->opt[OPT_TIMEOUT_MANUAL].desc = SANE_DESC_TIMEOUT_MANUAL;
|
|
|
|
s->opt[OPT_TIMEOUT_MANUAL].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_TIMEOUT_MANUAL].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_TIMEOUT_MANUAL].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_TIMEOUT_MANUAL].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_TIMEOUT_MANUAL].w = s->hw->info.timeout_manual_default;
|
|
|
|
|
|
|
|
if (s->hw->info.canCheckADF == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_CHECK_ADF].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->hw->info.canDuplex == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->hw->info.canADF == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_TIMEOUT_ADF].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "Enhancement" group: */
|
|
|
|
s->opt[OPT_ENHANCEMENT_GROUP].name = "";
|
|
|
|
s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT_GROUP;
|
|
|
|
s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
|
|
|
|
s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
|
|
|
|
s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
|
|
|
|
/* Control Panel: */
|
|
|
|
s->opt[OPT_CONTROL_PANEL].name = SANE_NAME_CONTROL_PANEL;
|
|
|
|
s->opt[OPT_CONTROL_PANEL].title = SANE_TITLE_CONTROL_PANEL;
|
|
|
|
s->opt[OPT_CONTROL_PANEL].desc = SANE_DESC_CONTROL_PANEL;
|
|
|
|
s->opt[OPT_CONTROL_PANEL].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_CONTROL_PANEL].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_CONTROL_PANEL].w = s->hw->info.control_panel_default;
|
|
|
|
|
|
|
|
/* Ace_Function */
|
|
|
|
s->opt[OPT_ACE_FUNCTION].name = SANE_NAME_ACE_FUNCTION;
|
|
|
|
s->opt[OPT_ACE_FUNCTION].title = SANE_TITLE_ACE_FUNCTION;
|
|
|
|
s->opt[OPT_ACE_FUNCTION].desc = SANE_DESC_ACE_FUNCTION;
|
|
|
|
s->opt[OPT_ACE_FUNCTION].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_ACE_FUNCTION].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_ACE_FUNCTION].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_ACE_FUNCTION].constraint.range = &ace_function_range;
|
|
|
|
s->val[OPT_ACE_FUNCTION].w = 0;
|
|
|
|
|
|
|
|
/* Ace_Sensitivity */
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].name = SANE_NAME_ACE_SENSITIVITY;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].title = SANE_TITLE_ACE_SENSITIVITY;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].desc = SANE_DESC_ACE_SENSITIVITY;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].constraint.range = &ace_sensitivity_range;
|
|
|
|
s->val[OPT_ACE_SENSITIVITY].w = 4;
|
|
|
|
|
|
|
|
/* Brightness */
|
|
|
|
s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
|
|
|
|
s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
|
|
|
|
s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
|
|
|
|
s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_BRIGHTNESS].w = 0;
|
|
|
|
|
|
|
|
/* Threshold */
|
|
|
|
s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
|
|
|
|
s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
|
|
|
|
s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
|
|
|
|
s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_THRESHOLD].w = 0;
|
|
|
|
|
|
|
|
/* Contrast */
|
|
|
|
s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
|
|
|
|
s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
|
|
|
|
s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
|
|
|
|
s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_CONTRAST].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_CONTRAST].w = 0;
|
|
|
|
|
|
|
|
/* Negative: */
|
|
|
|
s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
|
|
|
|
s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
|
|
|
|
s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
|
|
|
|
s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
|
|
|
|
s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->val[OPT_NEGATIVE].w = SANE_FALSE;
|
|
|
|
|
|
|
|
/* Contrast is not used in any case; why did we add it? */
|
|
|
|
s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
|
|
if (s->hw->info.control_panel_default == SANE_TRUE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
else if (s->hw->info.canACE == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "ICON" group: */
|
|
|
|
s->opt[OPT_ICON_GROUP].name = "";
|
|
|
|
s->opt[OPT_ICON_GROUP].title = SANE_TITLE_ICON_GROUP;
|
|
|
|
s->opt[OPT_ICON_GROUP].desc = "";
|
|
|
|
s->opt[OPT_ICON_GROUP].type = SANE_TYPE_GROUP;
|
|
|
|
s->opt[OPT_ICON_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
s->opt[OPT_ICON_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
|
|
|
|
/* Icon_Width */
|
|
|
|
s->opt[OPT_ICON_WIDTH].name = SANE_NAME_ICON_WIDTH;
|
|
|
|
s->opt[OPT_ICON_WIDTH].title = SANE_TITLE_ICON_WIDTH;
|
|
|
|
s->opt[OPT_ICON_WIDTH].desc = SANE_DESC_ICON_WIDTH;
|
|
|
|
s->opt[OPT_ICON_WIDTH].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_ICON_WIDTH].unit = SANE_UNIT_PIXEL;
|
|
|
|
s->opt[OPT_ICON_WIDTH].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_ICON_WIDTH].constraint.range = &icon_range;
|
|
|
|
s->val[OPT_ICON_WIDTH].w = 0;
|
|
|
|
|
|
|
|
/* Icon_Length */
|
|
|
|
s->opt[OPT_ICON_LENGTH].name = SANE_NAME_ICON_LENGTH;
|
|
|
|
s->opt[OPT_ICON_LENGTH].title = SANE_TITLE_ICON_LENGTH;
|
|
|
|
s->opt[OPT_ICON_LENGTH].desc = SANE_DESC_ICON_LENGTH;
|
|
|
|
s->opt[OPT_ICON_LENGTH].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_ICON_LENGTH].unit = SANE_UNIT_PIXEL;
|
|
|
|
s->opt[OPT_ICON_LENGTH].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_ICON_LENGTH].constraint.range = &icon_range;
|
|
|
|
s->val[OPT_ICON_LENGTH].w = 0;
|
|
|
|
|
|
|
|
if (s->hw->info.canIcon == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_ICON_GROUP].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_ICON_WIDTH].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_ICON_LENGTH].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "Barcode" group: */
|
|
|
|
s->opt[OPT_BARCODE_GROUP].name = "";
|
|
|
|
s->opt[OPT_BARCODE_GROUP].title = SANE_TITLE_BARCODE_GROUP;
|
|
|
|
s->opt[OPT_BARCODE_GROUP].desc = "";
|
|
|
|
s->opt[OPT_BARCODE_GROUP].type = SANE_TYPE_GROUP;
|
|
|
|
s->opt[OPT_BARCODE_GROUP].cap = SANE_CAP_ADVANCED;
|
|
|
|
s->opt[OPT_BARCODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
|
|
|
|
/* Add <name> to barcode search priority. */
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].name = SANE_NAME_BARCODE_SEARCH_BAR;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].title = SANE_TITLE_BARCODE_SEARCH_BAR;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].desc = SANE_DESC_BARCODE_SEARCH_BAR;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].constraint.string_list = barcode_search_bar_list;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].size = max_string_size (barcode_search_bar_list);
|
|
|
|
s->val[OPT_BARCODE_SEARCH_BAR].s = strdup (barcode_search_bar_list[0]);
|
|
|
|
|
|
|
|
/* Barcode search count (1-7, default 1). */
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].name = SANE_NAME_BARCODE_SEARCH_COUNT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].title = SANE_TITLE_BARCODE_SEARCH_COUNT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].desc = SANE_DESC_BARCODE_SEARCH_COUNT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].constraint.range = &barcode_search_count_range;
|
|
|
|
s->val[OPT_BARCODE_SEARCH_COUNT].w = 3;
|
|
|
|
|
|
|
|
/* Barcode search mode. horiz-vert, horizontal, vertical, vert-horiz */
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].name = SANE_NAME_BARCODE_SEARCH_MODE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].title = SANE_TITLE_BARCODE_SEARCH_MODE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].desc = SANE_DESC_BARCODE_SEARCH_MODE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].size = max_string_size (barcode_search_mode_list);
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].constraint.string_list = barcode_search_mode_list;
|
|
|
|
s->val[OPT_BARCODE_SEARCH_MODE].s = strdup(barcode_search_mode_list[0]);
|
|
|
|
|
|
|
|
/* Patch code min height (def=5mm) */
|
|
|
|
s->opt[OPT_BARCODE_HMIN].name = SANE_NAME_BARCODE_HMIN;
|
|
|
|
s->opt[OPT_BARCODE_HMIN].title = SANE_TITLE_BARCODE_HMIN;
|
|
|
|
s->opt[OPT_BARCODE_HMIN].desc = SANE_DESC_BARCODE_HMIN;
|
|
|
|
s->opt[OPT_BARCODE_HMIN].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_HMIN].unit = SANE_UNIT_MM;
|
|
|
|
s->opt[OPT_BARCODE_HMIN].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_HMIN].constraint.range = &barcode_hmin_range;
|
|
|
|
s->val[OPT_BARCODE_HMIN].w = 5;
|
|
|
|
|
|
|
|
/* Barcode search timeout in ms (20-65535,default is 10000). */
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].name = SANE_NAME_BARCODE_SEARCH_TIMEOUT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].title = SANE_TITLE_BARCODE_SEARCH_TIMEOUT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].desc = SANE_DESC_BARCODE_SEARCH_TIMEOUT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].unit = SANE_UNIT_MICROSECOND;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint.range = &barcode_search_timeout_range;
|
|
|
|
s->val[OPT_BARCODE_SEARCH_TIMEOUT].w = 10000;
|
|
|
|
|
|
|
|
/* Specify image sections and functions */
|
|
|
|
s->opt[OPT_SECTION].name = SANE_NAME_SECTION;
|
|
|
|
s->opt[OPT_SECTION].title = SANE_TITLE_SECTION;
|
|
|
|
s->opt[OPT_SECTION].desc = SANE_DESC_SECTION;
|
|
|
|
s->opt[OPT_SECTION].type = SANE_TYPE_STRING;
|
|
|
|
s->opt[OPT_SECTION].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_SECTION].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
s->opt[OPT_SECTION].size = 255;
|
|
|
|
s->val[OPT_SECTION].s = strdup ("");
|
|
|
|
|
|
|
|
/* Barcode_Relmax */
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].name = SANE_NAME_BARCODE_RELMAX;
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].title = SANE_TITLE_BARCODE_RELMAX;
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].desc = SANE_DESC_BARCODE_RELMAX;
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_BARCODE_RELMAX].w = 0;
|
|
|
|
|
|
|
|
/* Barcode_Barmin */
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].name = SANE_NAME_BARCODE_BARMIN;
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].title = SANE_TITLE_BARCODE_BARMIN;
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].desc = SANE_DESC_BARCODE_BARMIN;
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_BARCODE_BARMIN].w = 0;
|
|
|
|
|
|
|
|
/* Barcode_Barmax */
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].name = SANE_NAME_BARCODE_BARMAX;
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].title = SANE_TITLE_BARCODE_BARMAX;
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].desc = SANE_DESC_BARCODE_BARMAX;
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].constraint.range = &u8_range;
|
|
|
|
s->val[OPT_BARCODE_BARMAX].w = 0;
|
|
|
|
|
|
|
|
/* Barcode_Contrast */
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].name = SANE_NAME_BARCODE_CONTRAST;
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].title = SANE_TITLE_BARCODE_CONTRAST;
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].desc = SANE_DESC_BARCODE_CONTRAST;
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].constraint.range = &barcode_contrast_range;
|
|
|
|
s->val[OPT_BARCODE_CONTRAST].w = 3;
|
|
|
|
|
|
|
|
/* Barcode_Patchmode */
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].name = SANE_NAME_BARCODE_PATCHMODE;
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].title = SANE_TITLE_BARCODE_PATCHMODE;
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].desc = SANE_DESC_BARCODE_PATCHMODE;
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].type = SANE_TYPE_INT;
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].unit = SANE_UNIT_NONE;
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].constraint.range = &barcode_patchmode_range;
|
|
|
|
s->val[OPT_BARCODE_PATCHMODE].w = 0;
|
|
|
|
|
|
|
|
if (s->hw->info.canSection == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_SECTION].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->hw->info.canBarCode == SANE_FALSE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_BARCODE_GROUP].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_BAR].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_COUNT].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_MODE].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_HMIN].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_SEARCH_TIMEOUT].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_RELMAX].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_BARMIN].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_BARMAX].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_BARCODE_PATCHMODE].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
attach (const char *devnam, BH_Device ** devp)
|
|
|
|
{
|
|
|
|
SANE_Status status;
|
|
|
|
BH_Device *dev;
|
|
|
|
struct inquiry_standard_data ibuf;
|
|
|
|
struct inquiry_vpd_data vbuf;
|
|
|
|
struct inquiry_jis_data jbuf;
|
|
|
|
size_t buf_size;
|
|
|
|
int fd = -1;
|
|
|
|
double mm;
|
|
|
|
|
|
|
|
DBG (3, "attach called\n");
|
|
|
|
|
|
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
|
|
{
|
|
|
|
if (strcmp (dev->sane.name, devnam) == 0)
|
|
|
|
{
|
|
|
|
if (devp)
|
|
|
|
*devp = dev;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FAKE_INQUIRY
|
|
|
|
if (fake_inquiry)
|
|
|
|
{
|
|
|
|
DBG (3, "attach: faking inquiry of %s\n", devnam);
|
|
|
|
|
|
|
|
memset (&ibuf, 0, sizeof (ibuf));
|
|
|
|
ibuf.devtype = 6;
|
|
|
|
memcpy(ibuf.vendor, "**FAKE**", 8);
|
|
|
|
memcpy(ibuf.product, "COPISCAN II 6338", 16);
|
|
|
|
memcpy(ibuf.revision, "0016", 4);
|
|
|
|
|
|
|
|
DBG (1, "attach: reported devtype='%d', vendor='%.8s', "
|
|
|
|
"product='%.16s', revision='%.4s'\n",
|
|
|
|
ibuf.devtype, ibuf.vendor,
|
|
|
|
ibuf.product, ibuf.revision);
|
|
|
|
|
|
|
|
memset (&vbuf, 0, sizeof (vbuf));
|
|
|
|
memset (&jbuf, 0, sizeof (jbuf));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
DBG (3, "attach: opening %s\n", devnam);
|
|
|
|
status = sanei_scsi_open (devnam, &fd, sense_handler, NULL);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG (3, "attach: sending TEST_UNIT_READY\n");
|
|
|
|
status = test_unit_ready (fd);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "attach: test unit ready failed (%s)\n",
|
|
|
|
sane_strstatus (status));
|
|
|
|
sanei_scsi_close (fd);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG (3, "attach: sending INQUIRY (standard data)\n");
|
|
|
|
memset (&ibuf, 0, sizeof (ibuf));
|
|
|
|
buf_size = sizeof(ibuf);
|
|
|
|
status = inquiry (fd, &ibuf, &buf_size, 0,
|
|
|
|
BH_INQUIRY_STANDARD_PAGE_CODE);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "attach: inquiry (standard data) failed: %s\n",
|
|
|
|
sane_strstatus (status));
|
|
|
|
sanei_scsi_close (fd);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG (1, "attach: reported devtype='%d', vendor='%.8s', "
|
|
|
|
"product='%.16s', revision='%.4s'\n",
|
|
|
|
ibuf.devtype, ibuf.vendor,
|
|
|
|
ibuf.product, ibuf.revision);
|
|
|
|
|
|
|
|
if (ibuf.devtype != 6
|
|
|
|
|| strncmp ((char *)ibuf.vendor, "B&H SCSI", 8) != 0
|
2001-02-10 18:23:28 +00:00
|
|
|
|| strncmp ((char *)ibuf.product, "COPISCAN ", 9) != 0)
|
2001-01-20 23:42:12 +00:00
|
|
|
{
|
|
|
|
DBG (1,
|
|
|
|
"attach: device is not a recognized Bell and Howell scanner\n");
|
|
|
|
sanei_scsi_close (fd);
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG (3, "attach: sending INQUIRY (vpd data)\n");
|
|
|
|
memset (&vbuf, 0, sizeof (vbuf));
|
|
|
|
buf_size = sizeof(vbuf);
|
|
|
|
status = inquiry (fd, &vbuf, &buf_size, 1,
|
|
|
|
BH_INQUIRY_VPD_PAGE_CODE);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "attach: inquiry (vpd data) failed: %s\n",
|
|
|
|
sane_strstatus (status));
|
|
|
|
sanei_scsi_close (fd);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG (3, "attach: sending INQUIRY (jis data)\n");
|
|
|
|
memset (&jbuf, 0, sizeof (jbuf));
|
|
|
|
buf_size = sizeof(jbuf);
|
|
|
|
status = inquiry (fd, &jbuf, &buf_size, 1,
|
|
|
|
BH_INQUIRY_JIS_PAGE_CODE);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "attach: inquiry (jis data) failed: %s\n",
|
|
|
|
sane_strstatus (status));
|
|
|
|
sanei_scsi_close (fd);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
sanei_scsi_close (fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = malloc (sizeof (*dev));
|
|
|
|
if (!dev)
|
|
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
memset (dev, 0, sizeof (*dev));
|
|
|
|
|
|
|
|
|
|
|
|
dev->info.devtype = ibuf.devtype;
|
|
|
|
sprintf(dev->info.vendor, "%.8s", ibuf.vendor);
|
|
|
|
trim_spaces(dev->info.vendor, sizeof(dev->info.vendor));
|
|
|
|
sprintf(dev->info.product, "%.16s", ibuf.product);
|
|
|
|
trim_spaces(dev->info.product, sizeof(dev->info.product));
|
|
|
|
sprintf(dev->info.revision, "%.4s", ibuf.revision);
|
|
|
|
trim_spaces(dev->info.revision, sizeof(dev->info.revision));
|
|
|
|
|
|
|
|
dev->sane.name = strdup (devnam);
|
|
|
|
dev->sane.vendor = strdup(dev->info.vendor);
|
|
|
|
dev->sane.model = strdup(dev->info.product);;
|
|
|
|
dev->sane.type = strdup(print_devtype(dev->info.devtype));
|
|
|
|
|
|
|
|
/* set capabilities from vpd */
|
|
|
|
dev->info.canADF = vbuf.adf & 0x01;
|
|
|
|
dev->info.colorBandW = vbuf.imagecomposition & 0x01;
|
|
|
|
dev->info.colorHalftone = vbuf.imagecomposition & 0x02;
|
|
|
|
dev->info.canWhiteFrame = vbuf.imagedataprocessing[1] & 0x01;
|
|
|
|
dev->info.canBlackFrame = vbuf.imagedataprocessing[1] & 0x02;
|
|
|
|
dev->info.canEdgeExtract = vbuf.imagedataprocessing[1] & 0x04;
|
|
|
|
dev->info.canNoiseFilter = vbuf.imagedataprocessing[1] & 0x08;
|
|
|
|
dev->info.canSmooth = vbuf.imagedataprocessing[1] & 0x10;
|
|
|
|
dev->info.canLineBold = vbuf.imagedataprocessing[1] & 0x20;
|
|
|
|
dev->info.comprG3_1D = vbuf.compression & 0x01;
|
|
|
|
dev->info.comprG3_2D = vbuf.compression & 0x02;
|
|
|
|
dev->info.comprG4 = vbuf.compression & 0x04;
|
|
|
|
dev->info.canBorderRecog = vbuf.sizerecognition & 0x01;
|
|
|
|
dev->info.canBarCode = vbuf.optionalfeatures & 0x01;
|
|
|
|
dev->info.canIcon = vbuf.optionalfeatures & 0x02;
|
|
|
|
dev->info.canSection = vbuf.optionalfeatures & 0x04;
|
|
|
|
dev->info.lineMaxBytes = _2btol(vbuf.xmaxoutputbytes);
|
|
|
|
|
|
|
|
#ifdef FAKE_INQUIRY
|
|
|
|
if (fake_inquiry)
|
|
|
|
{
|
|
|
|
dev->info.canADF = SANE_FALSE;
|
|
|
|
dev->info.colorBandW = SANE_TRUE;
|
|
|
|
dev->info.colorHalftone = SANE_TRUE;
|
|
|
|
dev->info.canWhiteFrame = SANE_TRUE;
|
|
|
|
dev->info.canBlackFrame = SANE_TRUE;
|
|
|
|
dev->info.canEdgeExtract = SANE_TRUE;
|
|
|
|
dev->info.canNoiseFilter = SANE_TRUE;
|
|
|
|
dev->info.canSmooth = SANE_TRUE;
|
|
|
|
dev->info.canLineBold = SANE_TRUE;
|
|
|
|
dev->info.comprG3_1D = SANE_TRUE;
|
|
|
|
dev->info.comprG3_2D = SANE_TRUE;
|
|
|
|
dev->info.comprG4 = SANE_TRUE;
|
|
|
|
dev->info.canBorderRecog = SANE_TRUE;
|
|
|
|
dev->info.canBarCode = SANE_TRUE;
|
|
|
|
dev->info.canIcon = SANE_TRUE;
|
|
|
|
dev->info.canSection = SANE_TRUE;
|
|
|
|
dev->info.lineMaxBytes = 450;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* set capabilities from jis */
|
|
|
|
dev->info.resBasicX = _2btol(jbuf.basicxres);
|
|
|
|
dev->info.resBasicY = _2btol(jbuf.basicyres);
|
|
|
|
dev->info.resMaxX = _2btol(jbuf.maxxres);
|
|
|
|
dev->info.resMaxY = _2btol(jbuf.maxyres);
|
|
|
|
dev->info.resMinX = _2btol(jbuf.minxres);
|
|
|
|
dev->info.resMinY = _2btol(jbuf.minyres);
|
|
|
|
|
|
|
|
/* set the length of the list to zero first, then append standard resolutions */
|
|
|
|
dev->info.resStdList[0] = 0;
|
|
|
|
if (jbuf.standardres[0] & 0x80) appendStdList(&dev->info, 60);
|
|
|
|
if (jbuf.standardres[0] & 0x40) appendStdList(&dev->info, 75);
|
|
|
|
if (jbuf.standardres[0] & 0x20) appendStdList(&dev->info, 100);
|
|
|
|
if (jbuf.standardres[0] & 0x10) appendStdList(&dev->info, 120);
|
|
|
|
if (jbuf.standardres[0] & 0x08) appendStdList(&dev->info, 150);
|
|
|
|
if (jbuf.standardres[0] & 0x04) appendStdList(&dev->info, 160);
|
|
|
|
if (jbuf.standardres[0] & 0x02) appendStdList(&dev->info, 180);
|
|
|
|
if (jbuf.standardres[0] & 0x01) appendStdList(&dev->info, 200);
|
|
|
|
if (jbuf.standardres[1] & 0x80) appendStdList(&dev->info, 240);
|
|
|
|
if (jbuf.standardres[1] & 0x40) appendStdList(&dev->info, 300);
|
|
|
|
if (jbuf.standardres[1] & 0x20) appendStdList(&dev->info, 320);
|
|
|
|
if (jbuf.standardres[1] & 0x10) appendStdList(&dev->info, 400);
|
|
|
|
if (jbuf.standardres[1] & 0x08) appendStdList(&dev->info, 480);
|
|
|
|
if (jbuf.standardres[1] & 0x04) appendStdList(&dev->info, 600);
|
|
|
|
if (jbuf.standardres[1] & 0x02) appendStdList(&dev->info, 800);
|
|
|
|
if (jbuf.standardres[1] & 0x01) appendStdList(&dev->info, 1200);
|
|
|
|
if (dev->info.resStdList[0] == 0)
|
|
|
|
{
|
|
|
|
/* make a default standard resolutions for 200 and 300dpi */
|
|
|
|
DBG(1, "attach: no standard resolutions reported\n");
|
|
|
|
dev->info.resStdList[0] = 2;
|
|
|
|
dev->info.resStdList[1] = 200;
|
|
|
|
dev->info.resStdList[2] = 300;
|
|
|
|
dev->info.resBasicX = dev->info.resBasicY = 300;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->info.winWidth = _4btol(jbuf.windowwidth);
|
|
|
|
dev->info.winHeight = _4btol(jbuf.windowlength);
|
|
|
|
|
|
|
|
if (dev->info.winWidth <= 0)
|
|
|
|
{
|
|
|
|
dev->info.winWidth = (SANE_Int) (dev->info.resBasicX * 8.5);
|
|
|
|
DBG(1, "attach: invalid window width reported, using %d\n", dev->info.winWidth);
|
|
|
|
}
|
|
|
|
if (dev->info.winHeight <= 0)
|
|
|
|
{
|
|
|
|
dev->info.winHeight = dev->info.resBasicY * 14;
|
|
|
|
DBG(1, "attach: invalid window height reported, using %d\n", dev->info.winHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
mm = (dev->info.resBasicX > 0) ?
|
|
|
|
((double) dev->info.winWidth / (double) dev->info.resBasicX * MM_PER_INCH) :
|
|
|
|
0.0;
|
|
|
|
dev->info.x_range.min = SANE_FIX(0.0);
|
|
|
|
dev->info.x_range.max = SANE_FIX(mm);
|
|
|
|
dev->info.x_range.quant = SANE_FIX(0.0);
|
|
|
|
|
|
|
|
mm = (dev->info.resBasicY > 0) ?
|
|
|
|
((double) dev->info.winHeight / (double) dev->info.resBasicY * MM_PER_INCH) :
|
|
|
|
0.0;
|
|
|
|
dev->info.y_range.min = SANE_FIX(0.0);
|
|
|
|
dev->info.y_range.max = SANE_FIX(mm);
|
|
|
|
dev->info.y_range.quant = SANE_FIX(0.0);
|
|
|
|
|
|
|
|
/* set additional discovered/guessed capabilities */
|
|
|
|
|
|
|
|
/* if all of the ACE capabilities are present, declare it ACE capable */
|
|
|
|
dev->info.canACE = dev->info.canEdgeExtract &&
|
|
|
|
dev->info.canNoiseFilter &&
|
|
|
|
dev->info.canSmooth &&
|
|
|
|
dev->info.canLineBold;
|
|
|
|
|
|
|
|
/* if the model is known to be a duplex, declare it duplex capable */
|
|
|
|
if (strcmp(dev->info.product, "COPISCAN II 6338") == 0)
|
|
|
|
{
|
|
|
|
dev->info.canDuplex = SANE_TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dev->info.canDuplex = SANE_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the paper sensor requires RSC revision 1.4 or higher and an
|
|
|
|
* installed feeder. NOTE: It also requires SW-4 on and the
|
|
|
|
* AccufeedPlus feeder, but we cannot discover that.
|
|
|
|
*/
|
|
|
|
if (strcmp(dev->info.revision, "0014") >= 0)
|
|
|
|
{
|
|
|
|
dev->info.canCheckADF = dev->info.canADF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dev->info.canCheckADF = SANE_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set option defaults based on inquiry information */
|
|
|
|
dev->info.res_default = dev->info.resBasicX;
|
|
|
|
dev->info.autoborder_default = dev->info.canBorderRecog;
|
|
|
|
dev->info.batch_default = SANE_FALSE;
|
2001-02-10 18:23:28 +00:00
|
|
|
dev->info.deskew_default = SANE_FALSE;
|
2001-01-20 23:42:12 +00:00
|
|
|
dev->info.check_adf_default = SANE_FALSE;
|
|
|
|
dev->info.duplex_default = SANE_FALSE;
|
|
|
|
dev->info.timeout_adf_default = 0;
|
|
|
|
dev->info.timeout_manual_default = 0;
|
|
|
|
dev->info.control_panel_default = dev->info.canACE;
|
|
|
|
|
|
|
|
++num_devices;
|
|
|
|
dev->next = first_dev;
|
|
|
|
first_dev = dev;
|
|
|
|
|
|
|
|
if (devp)
|
|
|
|
*devp = dev;
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
attach_one(const char *devnam)
|
|
|
|
{
|
|
|
|
attach (devnam, NULL);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
|
|
|
|
{
|
|
|
|
char devnam[PATH_MAX] = "/dev/scanner";
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
authorize = authorize; /* get rid of compiler warning */
|
|
|
|
|
|
|
|
DBG_INIT();
|
|
|
|
DBG(3, "sane_init called\n");
|
|
|
|
DBG(1, "Bell+Howell SANE backend %d.%d build %d %s endian\n",
|
|
|
|
V_MAJOR, V_MINOR, BUILD,
|
|
|
|
_is_host_little_endian() ? "little" : "big");
|
|
|
|
|
|
|
|
if (version_code)
|
|
|
|
*version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
|
|
|
|
|
|
|
|
fp = sanei_config_open(BH_CONFIG_FILE);
|
|
|
|
if (fp)
|
|
|
|
{
|
|
|
|
char line[PATH_MAX];
|
|
|
|
const char *lp;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
/* read config file */
|
|
|
|
while (sanei_config_read (line, sizeof (line), fp))
|
|
|
|
{
|
|
|
|
if (line[0] == '#') /* ignore line comments */
|
|
|
|
continue;
|
|
|
|
len = strlen (line);
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
continue; /* ignore empty lines */
|
|
|
|
|
|
|
|
lp = sanei_config_skip_whitespace (line);
|
|
|
|
|
|
|
|
DBG(16,
|
|
|
|
"sane_init: processing config file line '%s'\n",
|
|
|
|
line);
|
|
|
|
if (strncmp(lp, "option", 6) == 0 &&
|
|
|
|
(isspace (lp[6]) || lp[6] == '\0'))
|
|
|
|
{
|
|
|
|
lp += 6;
|
|
|
|
lp = sanei_config_skip_whitespace (lp);
|
|
|
|
|
|
|
|
if (strncmp(lp, "disable-optional-frames", 23) == 0)
|
|
|
|
{
|
|
|
|
DBG(1, "sane_init: configuration option "
|
|
|
|
"'disable-optional-frames' set\n");
|
|
|
|
disable_optional_frames = 1;
|
|
|
|
}
|
|
|
|
else if (strncmp(lp, "fake-inquiry", 12) == 0)
|
|
|
|
{
|
|
|
|
DBG(1, "sane_init: configuration option "
|
|
|
|
"'fake-inquiry' set\n");
|
|
|
|
fake_inquiry = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBG(1, "sane_init: ignoring unknown "
|
|
|
|
"configuration option '%s'\n",
|
|
|
|
lp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBG(16,
|
|
|
|
"sane_init: found a device: line '%s'\n",
|
|
|
|
lp);
|
|
|
|
strncpy (devnam, lp, sizeof(devnam));
|
|
|
|
devnam[sizeof(devnam)-1] = '\0';
|
|
|
|
|
|
|
|
sanei_config_attach_matching_devices(devnam,
|
|
|
|
attach_one);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose (fp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* configure the /dev/scanner device in the absence of config file */
|
|
|
|
sanei_config_attach_matching_devices ("/dev/scanner", attach_one);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_get_devices (const SANE_Device ***device_list, SANE_Bool local)
|
|
|
|
{
|
|
|
|
static const SANE_Device **devlist = 0;
|
|
|
|
BH_Device *dev;
|
|
|
|
int i;
|
|
|
|
DBG(3, "sane_get_devices called\n");
|
|
|
|
|
|
|
|
local = local; /* get rid of compiler warning */
|
|
|
|
if (devlist)
|
|
|
|
free (devlist);
|
|
|
|
devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
|
|
|
|
if (!devlist)
|
|
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
|
|
devlist[i++] = &dev->sane;
|
|
|
|
devlist[i++] = 0;
|
|
|
|
|
|
|
|
*device_list = devlist;
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_open (SANE_String_Const devnam, SANE_Handle *handle)
|
|
|
|
{
|
|
|
|
SANE_Status status;
|
|
|
|
BH_Device *dev;
|
|
|
|
BH_Scanner *s;
|
|
|
|
DBG(3, "sane_open called\n");
|
|
|
|
|
|
|
|
if (devnam[0] != '\0')
|
|
|
|
{
|
|
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
|
|
{
|
|
|
|
if (strcmp (dev->sane.name, devnam) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
{
|
|
|
|
status = attach (devnam, &dev);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dev = first_dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
|
|
|
|
s = malloc (sizeof (*s));
|
|
|
|
if (!s)
|
|
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
memset (s, 0, sizeof (*s));
|
|
|
|
|
|
|
|
s->fd = -1;
|
|
|
|
s->hw = dev;
|
|
|
|
|
|
|
|
s->bmu = BH_UNIT_POINT;
|
|
|
|
s->mud = 1;
|
|
|
|
|
|
|
|
ScannerDump(s);
|
|
|
|
|
|
|
|
init_options (s);
|
|
|
|
|
|
|
|
s->next = first_handle;
|
|
|
|
first_handle = s;
|
|
|
|
|
|
|
|
/* initialize our parameters */
|
|
|
|
get_parameters(s, 0);
|
|
|
|
|
|
|
|
*handle = s;
|
|
|
|
|
|
|
|
#ifdef FAKE_INQUIRY
|
|
|
|
if (fake_inquiry)
|
|
|
|
{
|
|
|
|
DBG (1, "sane_open: faking open of %s\n",
|
|
|
|
s->hw->sane.name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "sane_open: open of %s failed: %s\n",
|
|
|
|
s->hw->sane.name, sane_strstatus (status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
const SANE_Option_Descriptor *
|
|
|
|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
DBG(3, "sane_get_option_descriptor called (option:%d)\n", option);
|
|
|
|
|
|
|
|
if ((unsigned) option >= NUM_OPTIONS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return (s->opt + option);
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
|
|
|
|
void *val, SANE_Word *info)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
SANE_Status status;
|
|
|
|
SANE_Word cap;
|
|
|
|
SANE_String_Const name;
|
|
|
|
|
|
|
|
DBG(3, "sane_control_option called\n");
|
|
|
|
|
|
|
|
name = s->opt[option].name ? s->opt[option].name : "(nil)";
|
|
|
|
|
|
|
|
if (info)
|
|
|
|
*info = 0;
|
|
|
|
|
|
|
|
if (s->scanning && action == SANE_ACTION_SET_VALUE)
|
|
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
if (option >= NUM_OPTIONS)
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
|
|
|
|
cap = s->opt[option].cap;
|
|
|
|
if (!SANE_OPTION_IS_ACTIVE (cap))
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
|
|
|
|
if (action == SANE_ACTION_GET_VALUE)
|
|
|
|
{
|
|
|
|
DBG(16, "sane_control_option: get_value %s [#%d]\n", name, option);
|
|
|
|
switch (option)
|
|
|
|
{
|
|
|
|
/* word options: */
|
|
|
|
case OPT_RESOLUTION:
|
|
|
|
case OPT_TL_X:
|
|
|
|
case OPT_TL_Y:
|
|
|
|
case OPT_BR_X:
|
|
|
|
case OPT_BR_Y:
|
|
|
|
case OPT_TIMEOUT_ADF:
|
|
|
|
case OPT_TIMEOUT_MANUAL:
|
|
|
|
case OPT_ACE_FUNCTION:
|
|
|
|
case OPT_ACE_SENSITIVITY:
|
|
|
|
case OPT_BRIGHTNESS:
|
|
|
|
case OPT_THRESHOLD:
|
|
|
|
case OPT_CONTRAST:
|
|
|
|
case OPT_ICON_WIDTH:
|
|
|
|
case OPT_ICON_LENGTH:
|
|
|
|
case OPT_BARCODE_SEARCH_COUNT:
|
|
|
|
case OPT_BARCODE_HMIN:
|
|
|
|
case OPT_BARCODE_SEARCH_TIMEOUT:
|
|
|
|
case OPT_BARCODE_RELMAX:
|
|
|
|
case OPT_BARCODE_BARMIN:
|
|
|
|
case OPT_BARCODE_BARMAX:
|
|
|
|
case OPT_BARCODE_CONTRAST:
|
|
|
|
case OPT_BARCODE_PATCHMODE:
|
|
|
|
case OPT_NUM_OPTS:
|
|
|
|
*(SANE_Word *) val = s->val[option].w;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
/* string options: */
|
|
|
|
case OPT_INQUIRY:
|
|
|
|
case OPT_SCAN_SOURCE:
|
|
|
|
case OPT_SCAN_MODE:
|
|
|
|
case OPT_COMPRESSION:
|
|
|
|
case OPT_PAPER_SIZE:
|
2001-02-10 18:23:28 +00:00
|
|
|
case OPT_ROTATION:
|
2001-01-20 23:42:12 +00:00
|
|
|
case OPT_BARCODE_SEARCH_BAR:
|
|
|
|
case OPT_BARCODE_SEARCH_MODE:
|
|
|
|
case OPT_SECTION:
|
|
|
|
strcpy (val, s->val[option].s);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
/* boolean options: */
|
|
|
|
case OPT_PREVIEW:
|
|
|
|
case OPT_AUTOBORDER:
|
2001-02-10 18:23:28 +00:00
|
|
|
case OPT_DESKEW:
|
2001-01-20 23:42:12 +00:00
|
|
|
case OPT_BATCH:
|
|
|
|
case OPT_CHECK_ADF:
|
|
|
|
case OPT_DUPLEX:
|
|
|
|
case OPT_CONTROL_PANEL:
|
|
|
|
case OPT_NEGATIVE:
|
|
|
|
*(SANE_Word *) val = s->val[option].w;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DBG(1, "sane_control_option:invalid option number %d\n", option);
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (action == SANE_ACTION_SET_VALUE)
|
|
|
|
{
|
|
|
|
switch (s->opt[option].type)
|
|
|
|
{
|
|
|
|
case SANE_TYPE_BOOL:
|
|
|
|
case SANE_TYPE_INT:
|
|
|
|
DBG(16, "sane_control_option: set_value %s [#%d] to %d\n",
|
|
|
|
name, option, *(SANE_Word *) val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SANE_TYPE_FIXED:
|
|
|
|
DBG(16, "sane_control_option: set_value %s [#%d] to %f\n",
|
|
|
|
name, option, SANE_UNFIX(*(SANE_Word *) val));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SANE_TYPE_STRING:
|
|
|
|
DBG(16, "sane_control_option: set_value %s [#%d] to %s\n",
|
|
|
|
name, option, (char *) val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DBG(16, "sane_control_option: set_value %s [#%d]\n",
|
|
|
|
name, option);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SANE_OPTION_IS_SETTABLE (cap))
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
|
|
|
|
status = sanei_constrain_value (s->opt + option, val, info);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
switch (option)
|
|
|
|
{
|
|
|
|
/* (mostly) side-effect-free word options: */
|
|
|
|
case OPT_TL_X:
|
|
|
|
case OPT_TL_Y:
|
|
|
|
case OPT_BR_X:
|
|
|
|
case OPT_BR_Y:
|
|
|
|
/* make sure that paper-size is set to custom */
|
|
|
|
if (s->val[option].w != *(SANE_Word *) val)
|
|
|
|
{
|
|
|
|
if (info) *info |= SANE_INFO_RELOAD_PARAMS;
|
|
|
|
|
|
|
|
if (get_paper_id(_OPT_VAL_STRING(s, OPT_PAPER_SIZE)) != 0)
|
|
|
|
{
|
|
|
|
if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
|
|
|
|
/* set paper size to 'custom' */
|
|
|
|
free (s->val[OPT_PAPER_SIZE].s);
|
|
|
|
s->val[OPT_PAPER_SIZE].s = strdup(paper_list[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
case OPT_RESOLUTION:
|
|
|
|
if (info && s->val[option].w != *(SANE_Word *) val)
|
|
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
|
|
/* fall through */
|
|
|
|
case OPT_TIMEOUT_ADF:
|
|
|
|
case OPT_TIMEOUT_MANUAL:
|
|
|
|
case OPT_ACE_FUNCTION:
|
|
|
|
case OPT_ACE_SENSITIVITY:
|
|
|
|
case OPT_BRIGHTNESS:
|
|
|
|
case OPT_THRESHOLD:
|
|
|
|
case OPT_CONTRAST:
|
|
|
|
case OPT_ICON_WIDTH:
|
|
|
|
case OPT_ICON_LENGTH:
|
|
|
|
case OPT_BARCODE_SEARCH_COUNT:
|
|
|
|
case OPT_BARCODE_HMIN:
|
|
|
|
case OPT_BARCODE_SEARCH_TIMEOUT:
|
|
|
|
case OPT_BARCODE_RELMAX:
|
|
|
|
case OPT_BARCODE_BARMIN:
|
|
|
|
case OPT_BARCODE_BARMAX:
|
|
|
|
case OPT_BARCODE_CONTRAST:
|
|
|
|
case OPT_BARCODE_PATCHMODE:
|
|
|
|
case OPT_NUM_OPTS:
|
|
|
|
s->val[option].w = *(SANE_Word *) val;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
/* string options */
|
|
|
|
case OPT_BARCODE_SEARCH_BAR:
|
|
|
|
/*!!! we're supporting only a single barcode type via the option */
|
|
|
|
s->search_bars[0] = get_barcode_id(val);
|
|
|
|
/* fall through */
|
|
|
|
case OPT_SCAN_SOURCE:
|
|
|
|
case OPT_COMPRESSION:
|
2001-02-10 18:23:28 +00:00
|
|
|
case OPT_ROTATION:
|
2001-01-20 23:42:12 +00:00
|
|
|
case OPT_BARCODE_SEARCH_MODE:
|
|
|
|
case OPT_SECTION:
|
|
|
|
if (s->val[option].s)
|
|
|
|
free (s->val[option].s);
|
|
|
|
s->val[option].s = strdup (val);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
/* boolean options: */
|
|
|
|
case OPT_AUTOBORDER:
|
|
|
|
/*!!! autoborder true disables geometry controls
|
|
|
|
* and sets them to defaults?
|
|
|
|
*/
|
|
|
|
/* fall through */
|
|
|
|
case OPT_PREVIEW:
|
|
|
|
case OPT_BATCH:
|
2001-02-10 18:23:28 +00:00
|
|
|
case OPT_DESKEW:
|
2001-01-20 23:42:12 +00:00
|
|
|
case OPT_CHECK_ADF:
|
|
|
|
case OPT_DUPLEX:
|
|
|
|
case OPT_NEGATIVE:
|
|
|
|
s->val[option].w = *(SANE_Word *) val;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
/* options with side effects */
|
|
|
|
case OPT_CONTROL_PANEL:
|
|
|
|
/* a boolean option */
|
|
|
|
/* control-panel true enables/disables some enhancement controls */
|
|
|
|
if (s->val[option].w != *(SANE_Word *) val)
|
|
|
|
{
|
|
|
|
if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
|
|
|
|
s->val[option].w = *(SANE_Word *) val;
|
|
|
|
|
|
|
|
if (*(SANE_Word *) val == SANE_TRUE)
|
|
|
|
{
|
|
|
|
if (s->hw->info.canACE == SANE_TRUE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (s->hw->info.canACE == SANE_TRUE)
|
|
|
|
{
|
|
|
|
s->opt[OPT_ACE_FUNCTION].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_ACE_SENSITIVITY].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
case OPT_SCAN_MODE:
|
|
|
|
/* a string option */
|
|
|
|
/* scan mode != lineart disables compression, setting it to
|
|
|
|
* 'none'
|
|
|
|
*/
|
|
|
|
if (strcmp (s->val[option].s, (SANE_String) val))
|
|
|
|
{
|
|
|
|
if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
if (get_scan_mode_id((SANE_String) val) != 0)
|
|
|
|
{
|
|
|
|
/* scan mode is not lineart, disable compression
|
|
|
|
* and set compression to 'none'
|
|
|
|
*/
|
|
|
|
s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
|
|
|
|
if (s->val[OPT_COMPRESSION].s &&
|
|
|
|
get_compression_id(s->val[OPT_COMPRESSION].s) != 0)
|
|
|
|
{
|
|
|
|
free (s->val[OPT_COMPRESSION].s);
|
|
|
|
s->val[OPT_COMPRESSION].s = strdup(compression_list[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* scan mode is lineart, enable compression */
|
|
|
|
s->opt[OPT_COMPRESSION].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
free (s->val[option].s);
|
|
|
|
s->val[option].s = strdup (val);
|
|
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
case OPT_PAPER_SIZE:
|
|
|
|
/* a string option */
|
|
|
|
/* changes geometry options, therefore _RELOAD_PARAMS and _RELOAD_OPTIONS */
|
|
|
|
if (strcmp (s->val[option].s, (SANE_String) val))
|
|
|
|
{
|
|
|
|
SANE_Int paper_id = get_paper_id((SANE_String) val);
|
|
|
|
|
|
|
|
/* paper_id 0 is a special case (custom) that
|
|
|
|
* disables the paper size control of geometry
|
|
|
|
*/
|
|
|
|
if (paper_id != 0)
|
|
|
|
{
|
|
|
|
double left, x_max, y_max, x, y;
|
|
|
|
|
|
|
|
x_max = SANE_UNFIX(s->hw->info.x_range.max);
|
|
|
|
y_max = SANE_UNFIX(s->hw->info.y_range.max);
|
|
|
|
/* a dimension of 0.0 (or less) is replaced with the max value */
|
|
|
|
x = (paper_sizes[paper_id].width <= 0.0) ? x_max :
|
|
|
|
paper_sizes[paper_id].width;
|
|
|
|
y = (paper_sizes[paper_id].length <= 0.0) ? y_max :
|
|
|
|
paper_sizes[paper_id].length;
|
|
|
|
|
|
|
|
if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
|
|
|
|
/* set geometry options based on paper size */
|
|
|
|
/* set geometry options based on paper size */
|
|
|
|
if (s->hw->info.canADF)
|
|
|
|
{
|
|
|
|
/* when the feeder is used the paper is centered in the
|
|
|
|
* hopper; with the manual feed it is aligned left.
|
|
|
|
*/
|
|
|
|
left = (x_max - x) / 2.0;
|
|
|
|
if (left < 0.0) left = 0.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
left = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->val[OPT_TL_X].w = SANE_FIX(left);
|
|
|
|
s->val[OPT_TL_Y].w = SANE_FIX(0.0);
|
|
|
|
s->val[OPT_BR_X].w = SANE_FIX(MIN(x + left, x_max));
|
|
|
|
s->val[OPT_BR_Y].w = SANE_FIX(MIN(y, y_max));
|
|
|
|
}
|
|
|
|
free (s->val[option].s);
|
|
|
|
s->val[option].s = strdup (val);
|
|
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DBG(1, "sane_control_option:invalid option number %d\n", option);
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
SANE_Int status = SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
DBG(3, "sane_get_parameters called\n");
|
|
|
|
|
|
|
|
if (params)
|
|
|
|
{
|
|
|
|
SANE_Int res;
|
|
|
|
|
|
|
|
if (!s->scanning)
|
|
|
|
{
|
|
|
|
/* update our parameters ONLY if we're not scanning */
|
|
|
|
status = get_parameters(s, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
*params = s->params;
|
|
|
|
|
|
|
|
res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
|
|
|
|
|
|
|
|
DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, "
|
|
|
|
"lines=%d, dpi=%d\n",
|
|
|
|
(int) s->params.format,
|
|
|
|
s->params.pixels_per_line,
|
|
|
|
s->params.bytes_per_line,
|
|
|
|
s->params.lines,
|
|
|
|
res);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_start (SANE_Handle handle)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
SANE_Status status;
|
|
|
|
|
|
|
|
DBG(3, "sane_start called\n");
|
|
|
|
s->cancelled = SANE_FALSE;
|
|
|
|
|
|
|
|
if (s->scanning == SANE_FALSE)
|
|
|
|
{
|
|
|
|
/* get preliminary parameters */
|
|
|
|
status = get_parameters (s, 0);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "sane_start: get_parameters failed: %s\n",
|
|
|
|
sane_strstatus (status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do the setup once per 'batch'. The SANE standard requires the
|
|
|
|
* frontend to call sane_cancel once all desired frames have been
|
|
|
|
* acquired. That is when scanning is set back to SANE_FALSE and
|
|
|
|
* the 'batch' is considered done.
|
|
|
|
*/
|
|
|
|
status = start_setup (s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "sane_start: start_setup failed: %s\n",
|
|
|
|
sane_strstatus (status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
status = start_scan (s);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "sane_start: start_scan failed: %s\n",
|
|
|
|
sane_strstatus (status));
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
SANE_Status status;
|
|
|
|
size_t nread;
|
|
|
|
|
|
|
|
DBG(3, "sane_read called\n");
|
|
|
|
|
|
|
|
*len = 0;
|
|
|
|
|
|
|
|
if (s->cancelled) {
|
|
|
|
DBG (3, "sane_read: cancelled!\n");
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!s->scanning) {
|
|
|
|
DBG (3, "sane_read: scanning is false!\n");
|
|
|
|
sane_cancel(s);
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
nread = maxlen;
|
|
|
|
|
|
|
|
DBG (3, "sane_read: request %lu bytes\n", (u_long) nread);
|
|
|
|
/* set InvalidBytes to 0 before read; sense_handler will set it
|
|
|
|
* to non-zero if we do the last partial read.
|
|
|
|
*/
|
|
|
|
s->InvalidBytes = 0;
|
|
|
|
status = read_data (s, buf, &nread);
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
|
|
{
|
|
|
|
DBG (1, "sane_read: read_data failed %s\n",
|
|
|
|
sane_strstatus(status));
|
|
|
|
sane_cancel (s);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
nread = maxlen - s->InvalidBytes;
|
|
|
|
DBG (3, "sane_read: got %lu bytes\n", (u_long) nread);
|
|
|
|
*len = nread;
|
|
|
|
|
|
|
|
return (maxlen != 0 && nread == 0) ? SANE_STATUS_EOF : SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
|
|
|
|
{
|
|
|
|
#ifdef NONBLOCKSUPPORTED
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
#endif
|
|
|
|
DBG(3, "sane_set_io_mode called: non_blocking=%d\n", non_blocking);
|
|
|
|
|
|
|
|
#ifdef NONBLOCKSUPPORTED
|
|
|
|
if (s->fd < 0)
|
|
|
|
{
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fcntl (s->fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
|
|
|
|
{
|
|
|
|
DBG(1, "sane_set_io_mode: error setting io mode\n");
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
#else
|
|
|
|
handle = handle; /* get rid of compiler warning */
|
|
|
|
return (non_blocking == 1) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
|
|
|
|
{
|
|
|
|
#ifdef NONBLOCKSUPPORTED
|
|
|
|
BH_Scanner *s = handle;
|
|
|
|
#endif
|
|
|
|
DBG(3, "sane_get_select_fd called\n");
|
|
|
|
|
|
|
|
#ifdef NONBLOCKSUPPORTED
|
|
|
|
if (s->fd < 0)
|
|
|
|
{
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
*fd = s->fd;
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
#else
|
|
|
|
handle = handle; fd = fd; /* get rid of compiler warning */
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sane_cancel (SANE_Handle handle)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = (BH_Scanner *) handle;
|
|
|
|
DBG(3, "sane_cancel called\n");
|
|
|
|
if (s->scanning)
|
|
|
|
{
|
|
|
|
/* if batchmode is enabled, then call set_window to
|
|
|
|
* abort the batch
|
|
|
|
*/
|
|
|
|
if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE)
|
|
|
|
{
|
|
|
|
DBG(5, "sane_cancel: calling set_window to abort batch\n");
|
|
|
|
set_window(s, BH_BATCH_ABORT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s->scanning = SANE_FALSE;
|
|
|
|
s->cancelled = SANE_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sane_close (SANE_Handle handle)
|
|
|
|
{
|
|
|
|
BH_Scanner *s = (BH_Scanner *) handle;
|
|
|
|
DBG(3, "sane_close called\n");
|
|
|
|
|
|
|
|
if (s->fd != -1)
|
|
|
|
sanei_scsi_close (s->fd);
|
|
|
|
s->fd = -1;
|
|
|
|
free (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sane_exit (void)
|
|
|
|
{
|
|
|
|
BH_Device *dev, *next;
|
|
|
|
DBG(3, "sane_exit called\n");
|
|
|
|
|
|
|
|
for (dev = first_dev; dev; dev = next)
|
|
|
|
{
|
|
|
|
next = dev->next;
|
|
|
|
free (dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|