diff --git a/backend/bh.c b/backend/bh.c new file mode 100644 index 000000000..92f791e44 --- /dev/null +++ b/backend/bh.c @@ -0,0 +1,3826 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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] : + (const SANE_String) "unknown"; +} + +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; +} + +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 ? " " : " "); + + DBG (1, "Colors:%s%s\n", info->colorBandW ? " " : "", + info->colorHalftone ? " " : ""); + + DBG (1, "Data processing:%s%s%s%s%s%s\n", + info->canWhiteFrame ? " " : "", + info->canBlackFrame ? " " : "", + info->canEdgeExtract ? " " : "", + info->canNoiseFilter ? " " : "", + info->canSmooth ? " " : "", + info->canLineBold ? " " : ""); + + DBG (1, "Compression:%s%s%s\n", + info->comprG3_1D ? " " : "", + info->comprG3_2D ? " " : "", + info->comprG4 ? " " : ""); + + DBG (1, "Optional Features:%s%s%s%s\n", + info->canBorderRecog ? " " : "", + info->canBarCode ? " " : "", + info->canIcon ? " " : "", + info->canSection ? "
" : ""); + + 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); + 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, "\n
%s
\n", + print_read_type((int) s->readlist[s->readptr])); + fprintf(fp, " %s\n %d\n", + print_barcodetype((int) _2btol(buf.barcodetype)), + (int) _2btol(buf.statusflag)); + fprintf(fp, " %s\n", + print_orientation((int) _2btol(buf.barcodeorientation))); + fprintf(fp, " \n %d%d\n", + (int) _2btol(buf.posxb), (int) _2btol(buf.posyb)); + fprintf(fp, " %d%d\n", + (int) _2btol(buf.posxd), (int) _2btol(buf.posyd)); + fprintf(fp, " %d%d\n", + (int) _2btol(buf.posxa), (int) _2btol(buf.posya)); + fprintf(fp, "
%d%d
\n
\n", + (int) _2btol(buf.posxc), (int) _2btol(buf.posyc)); + fprintf(fp, " %.2fx%.2f+%.2f+%.2f\n", + w, l, x, y); + fprintf(fp, " %d\n %d\n", + (int) _2btol(buf.barcodesearchtime), + (int) buf.barcodelen); + fprintf(fp, " %s\n
\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; + SANE_Int width, length, i, format; + + 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); + _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; + } + + /* 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); + 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; + SANE_Int x, y, i = 0; + 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) + { + 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; + } + } + DBG (3, "*** Window size: %dx%d+%d+%d\n", *w, *h, x, y); + } + + /* 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: + * x++: + * 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)) + { + FILE *fp; + /*!!!need a real tmpname*/ + strcpy(s->barfname, "/tmp/sane.bh.000001"); + + if ((fp = fopen(s->barfname, "w")) != NULL) + { + fprintf(fp, "\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, "\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]); + + /* 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 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 + || strncmp ((char *)ibuf.product, "COPISCAN II", 11) != 0) + { + 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; + 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 (line[len - 1] == '\n') + line[--len] = '\0'; + + 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: + 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: + 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: + 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: + 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); + } +} + diff --git a/backend/bh.conf b/backend/bh.conf new file mode 100644 index 000000000..bca80ec45 --- /dev/null +++ b/backend/bh.conf @@ -0,0 +1,2 @@ +scsi "B&H SCSI" +/dev/scanner diff --git a/backend/bh.desc b/backend/bh.desc index d3d9ff805..4a260270d 100644 --- a/backend/bh.desc +++ b/backend/bh.desc @@ -10,9 +10,9 @@ ; :backend "bh" ; name of backend -:version "(0.1)" ; version of backend +:version "1.0-4" ; version of backend :status :new ; :alpha, :beta, :stable, :new -;:manpage "sane-bh" ; name of manpage (if it exists) +:manpage "sane-bh" ; name of manpage (if it exists) :url "http://www.martoneconsulting.com/sane-bh.html" ; backend's web page :devicetype :scanner ; start of a list of devices.... diff --git a/backend/bh.h b/backend/bh.h new file mode 100644 index 000000000..160a33b2c --- /dev/null +++ b/backend/bh.h @@ -0,0 +1,1007 @@ +/* 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. */ + +#ifndef BH_H +#define BH_H 1 + +#ifndef PATH_MAX +#define PATH_MAX (1024) +#endif + +#define BH_CONFIG_FILE "bh.conf" + +/* number of barcode types that can be searched at one time */ +#define NUM_SEARCH_BARS 6 +/* number of additional scanning/decoding sections supported */ +#define NUM_SECTIONS 8 +/* number of possible reads per scan plus the extra one for + * the barcode file + */ +#define NUM_READS 56 + 1 + +/* specify sanity limits for autoborder detection and barcode decoding */ +#define BH_AUTOBORDER_TRIES 1000 +#define BH_DECODE_TRIES 100 +/* specify a fudge factor in mm for border around decoded barcodes */ +#define BH_DECODE_FUDGE 1.0 + +/* section flags - what operation(s) to perform on section */ +#define BH_SECTION_FRONT_IMAGE (1 << 0) +#define BH_SECTION_BACK_IMAGE (1 << 1) +#define BH_SECTION_FRONT_BAR (1 << 2) +#define BH_SECTION_BACK_BAR (1 << 3) +#define BH_SECTION_FRONT_PATCH (1 << 4) +#define BH_SECTION_BACK_PATCH (1 << 5) + +typedef enum +{ + BH_UNIT_INCH, + BH_UNIT_MM, + BH_UNIT_POINT +} bh_measureUnit; + +typedef enum +{ + BH_COMP_NONE, + BH_COMP_G31D, + BH_COMP_G32D, + BH_COMP_G42D +} bh_compress; + +typedef enum +{ + OPT_NUM_OPTS = 0, + + OPT_MODE_GROUP, + /* inquiry string */ + OPT_INQUIRY, + /* preview mode */ + OPT_PREVIEW, + /* scan mode */ + OPT_SCAN_MODE, + /* resolution */ + OPT_RESOLUTION, + /* hardware compression */ + OPT_COMPRESSION, + + OPT_GEOMETRY_GROUP, + /* automatic border detection */ + OPT_AUTOBORDER, + /* paper size */ + OPT_PAPER_SIZE, + /* top-left x */ + OPT_TL_X, + /* top-left y */ + OPT_TL_Y, + /* bottom-right x */ + OPT_BR_X, + /* bottom-right y */ + OPT_BR_Y, + + OPT_FEEDER_GROUP, + /* scan source (eg. ADF) */ + OPT_SCAN_SOURCE, + /* scan in batch mode */ + OPT_BATCH, + /* scan both sides of the page */ + OPT_DUPLEX, + /* timeout in seconds with manual feed */ + OPT_TIMEOUT_MANUAL, + /* timeout in seconds with ADF */ + OPT_TIMEOUT_ADF, + /* check for page in ADF before scanning */ + OPT_CHECK_ADF, + + OPT_ENHANCEMENT_GROUP, + /* Enables the scanner's control panel */ + OPT_CONTROL_PANEL, + /* ACE Function */ + OPT_ACE_FUNCTION, + /* ACE Sensitivity */ + OPT_ACE_SENSITIVITY, + /* Brightness */ + OPT_BRIGHTNESS, + /* Threshold */ + OPT_THRESHOLD, + /* Contrast */ + OPT_CONTRAST, + /* Negative (reverse image) */ + OPT_NEGATIVE, + + OPT_ICON_GROUP, + /* Width of icon (thumbnail) image in pixels */ + OPT_ICON_WIDTH, + /* Length of icon (thumbnail) image in pixels */ + OPT_ICON_LENGTH, + + OPT_BARCODE_GROUP, + /* Add to barcode search priority. */ + OPT_BARCODE_SEARCH_BAR, + /* Barcode search count (1-7, default 3). */ + OPT_BARCODE_SEARCH_COUNT, + /* Barcode search mode. + * (1 = horizontal,2 = vertical, 6 = v then h, 9 = h then v). + */ + OPT_BARCODE_SEARCH_MODE, + /* Patch code min height (def=127 (5mm)) */ + OPT_BARCODE_HMIN, + /* Barcode search timeout in ms + * (20-65535,default is disabled). + */ + OPT_BARCODE_SEARCH_TIMEOUT, + /* Specify image sections and functions + */ + OPT_SECTION, + /* Specifies the maximum relation from the widest to + * the smallest bar + */ + OPT_BARCODE_RELMAX, + /* Specifies the minimum number of bars in Bar/Patch code */ + OPT_BARCODE_BARMIN, + /* Specifies the maximum number of bars in a Bar/Patch code */ + OPT_BARCODE_BARMAX, + /* Specifies the image contrast used in decoding. + * Use higher values when there are more white pixels + * in the code + */ + OPT_BARCODE_CONTRAST, + /* Controls Patch Code detection. */ + OPT_BARCODE_PATCHMODE, + + /* must come last: */ + NUM_OPTIONS + +} BH_Option; + +typedef union +{ + SANE_Word w; /* word */ + SANE_Word *wa; /* word array */ + SANE_String s; /* string */ +} Option_Value; + +/* macros for accessing the value for an option within a scanning context */ +#define MM_PER_INCH 25.4 +#define _OPT_VAL_WORD(s, o) ((s)->val[(o)].w) +#define _OPT_VAL_WORD_THOUSANDTHS(s, o) \ + (SANE_UNFIX(_OPT_VAL_WORD((s), (o))) * 1000.0 / MM_PER_INCH) +#define _OPT_VAL_STRING(s, o) ((s)->val[(o)].s) +#define _OPT_VAL_WORD_ARRAY(s, o) ((s)->val[(o)].wa) + + +typedef struct _BH_Paper +{ + SANE_String name; + + /* paper dimensions in mm */ + double width, length; +} BH_Paper; + +typedef struct _BH_Section +{ + /* section dimensions - in millimeters */ + u_long top, left, width, length; + + /* compression type/arg/frameformat */ + SANE_Byte compressiontype; + SANE_Byte compressionarg; + SANE_Frame format; + + /* Flags (see BH_SECTION_...) indicating operation(s) to perform + * on the section. If zero, the section is completely disabled + * and will not even be defined in set_window. + */ + SANE_Word flags; + +} BH_Section; + +typedef struct _BH_Info +{ + SANE_Range x_range; + SANE_Range y_range; + + SANE_Int res_default; + SANE_Bool autoborder_default; + SANE_Bool batch_default; + SANE_Bool check_adf_default; + SANE_Bool duplex_default; + SANE_Int timeout_adf_default; + SANE_Int timeout_manual_default; + SANE_Bool control_panel_default; + + /* additional discovered/guessed capabilities */ + SANE_Bool canACE; + SANE_Bool canDuplex; + SANE_Bool canCheckADF; + + /* standard information */ + SANE_Byte devtype; + SANE_Char vendor[9]; /* model name */ + SANE_Char product[17]; /* product name */ + SANE_Char revision[5]; /* revision */ + + /* VPD information */ + SANE_Bool canADF; /* is there an ADF available */ + SANE_Bool colorBandW; /* can scanner do black and white */ + SANE_Bool colorHalftone; /* can scanner do Halftone */ + SANE_Bool canWhiteFrame; /* data processing: White Framing */ + SANE_Bool canBlackFrame; /* data processing: Black Framing */ + SANE_Bool canEdgeExtract; /* data processing: ACE: Edge Extraction */ + SANE_Bool canNoiseFilter; /* data processing: ACE: Noise Filtering */ + SANE_Bool canSmooth; /* data processing: ACE: Smoothing */ + SANE_Bool canLineBold; /* data processing: ACE: LineBolding */ + SANE_Bool comprG3_1D; /* compression: Group 3, 1 dimensional */ + SANE_Bool comprG3_2D; /* compression: Group 3, 2 dimensional */ + SANE_Bool comprG4; /* compression: Group 4 */ + SANE_Bool canBorderRecog; /* can do border recognition */ + SANE_Bool canBarCode; /* bar code support available */ + SANE_Bool canIcon; /* icon support available */ + SANE_Bool canSection; /* section support available */ + SANE_Int lineMaxBytes; /* maximum bytes per scan-line */ + + /* jis information */ + SANE_Int resBasicX; /* basic X resolution */ + SANE_Int resBasicY; /* basic Y resolution */ + SANE_Int resMaxX; /* maximum X resolution */ + SANE_Int resMaxY; /* maximum Y resolution */ + SANE_Int resMinX; /* minimum X resolution */ + SANE_Int resMinY; /* minimum Y resolution */ + SANE_Int resStdList[16+1]; /* list of available standard resolutions + * (first slot is the length) + */ + SANE_Int winWidth; /* length of window (in BasicX res DPI) */ + SANE_Int winHeight; /* height of window (in BasicY res DPI) */ +} BH_Info; + +typedef struct _BH_Device BH_Device; + +struct _BH_Device +{ + BH_Device *next; + SANE_Device sane; + BH_Info info; +}; + + +typedef struct _BH_Scanner BH_Scanner; +struct _BH_Scanner +{ + /* all the state needed to define a scan request: */ + + /* linked list for housekeeping */ + BH_Scanner *next; + + /* scanner dependent/low-level state: */ + BH_Device *hw; + + /* SCSI filedescriptor */ + int fd; + + /* tempfile which is used to send decoded barcode data */ + FILE *barf; + char barfname[PATH_MAX+1]; + + /* SANE option descriptors and values */ + SANE_Option_Descriptor opt[NUM_OPTIONS]; + Option_Value val[NUM_OPTIONS]; + + /* additional values that don't fit into Option_Value representation */ + SANE_Byte search_bars[NUM_SEARCH_BARS]; + BH_Section sections[NUM_SECTIONS]; + SANE_Int num_sections; + + /* SANE image parameters */ + SANE_Parameters params; + + /* state information - not options */ + + /* Basic Measurement Unit */ + SANE_Int bmu; + /* Measurement Unit Divisor */ + SANE_Int mud; + + /* track data to be read. ReadList contains the codes of the read types + * (see BH_READ_TYPE...) to perform, readcnt is the total number of reads + * for this scan and readptr points to the current read operation. + */ + SANE_Byte readlist[NUM_READS]; + SANE_Int readcnt, readptr; + + u_long InvalidBytes; + SANE_Bool scanning; + SANE_Bool cancelled; + SANE_Bool backpage; + SANE_Bool barcodes; + SANE_Bool patchcodes; + SANE_Bool icons; + u_long iconwidth, iconlength; + SANE_Bool barcode_not_found; +}; + +static const SANE_Range u8_range = +{ + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range u16_range = +{ + 0, /* minimum */ + 65535, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range icon_range = +{ + 0, /* minimum */ + 3600, /* maximum */ + 8 /* quantization */ +}; + +static const SANE_Range barcode_search_timeout_range = +{ + 20, /* minimum */ + 65535, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range barcode_hmin_range = +{ + 1, /* minimum */ + 1660, /* maximum (when converted from mm + * to thousandths will still be less + * than 65536) + */ + 0 /* quantization */ +}; + +static const SANE_Range barcode_search_count_range = +{ + 1, /* minimum */ + 7, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range barcode_relmax_range = +{ + 0, /* minimum */ + 6, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range barcode_contrast_range = +{ + 0, /* minimum */ + 6, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range barcode_patchmode_range = +{ + 0, /* minimum */ + 1, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range ace_function_range = +{ + -4, /* minimum */ + 4, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range ace_sensitivity_range = +{ + 0, /* minimum */ + 9, /* maximum */ + 0 /* quantization */ +}; + +static SANE_String_Const scan_mode_list[] = +{ + "lineart", + "halftone", + 0 +}; + +static SANE_String_Const scan_mode_min_list[] = +{ + "lineart", + 0 +}; + +static SANE_String_Const barcode_search_mode_list[] = +{ + "horiz-vert", /* 9 */ + "horizontal", /* 1 */ + "vertical", /* 2 */ + "vert-horiz", /* 6 */ + 0 +}; + +static SANE_String_Const scan_source_list[] = +{ + "Automatic Document Feeder", + "Manual Feed Tray", + 0 +}; + +static SANE_String_Const compression_list[] = +{ + "none", + "g31d", + "g32d", + "g42d", + 0 +}; + +/* list of supported bar/patch codes */ +static SANE_String_Const barcode_search_bar_list[] = +{ + "none", + "ean-8", + "ean-13", + "reserved-ean-add", + "code39", + "code2-5-interleaved", + "code2-5-3lines-matrix", + "code2-5-3lines-datalogic", + "code2-5-5lines-industrial", + "patchcode", + "codabar", + "codabar-with-start-stop", + "code39ascii", + "code128", + "code2-5-5lines-iata", + 0 +}; + +/* list of support paper sizes */ +/* 'custom' MUST be item 0; otherwise a width or length of 0 indicates + * the maximum value supported by the scanner + */ +static const BH_Paper paper_sizes[] = +{ + {"Custom", 0.0, 0.0}, + {"Letter", 215.9, 279.4}, + {"Legal", 215.9, 355.6}, + {"A3", 297, 420}, + {"A4", 210, 297}, + {"A5", 148.5, 210}, + {"A6", 105, 148.5}, + {"B4", 250, 353}, + {"B5", 182, 257}, + {"Full", 0.0, 0.0}, +}; + +/* MUST be kept in sync with paper_sizes */ +static SANE_String_Const paper_list[] = +{ + "Custom", + "Letter", + "Legal", + "A3", + "A4", + "A5", + "A6", + "B4", + "B5", + "Full", + 0 +}; + +static /* inline */ int _is_host_little_endian(void); +static /* inline */ int +_is_host_little_endian() +{ + SANE_Int val = 255; + unsigned char *firstbyte = (unsigned char *) &val; + + return (*firstbyte == 255) ? SANE_TRUE : SANE_FALSE; +} + +static /* inline */ void +_lto2b(u_long val, SANE_Byte *bytes) +{ + bytes[0] = (val >> 8) & 0xff; + bytes[1] = val & 0xff; +} + +static /* inline */ void +_lto3b(u_long val, SANE_Byte *bytes) +{ + bytes[0] = (val >> 16) & 0xff; + bytes[1] = (val >> 8) & 0xff; + bytes[2] = val & 0xff; +} + +static /* inline */ void +_lto4b(u_long val, SANE_Byte *bytes) +{ + bytes[0] = (val >> 24) & 0xff; + bytes[1] = (val >> 16) & 0xff; + bytes[2] = (val >> 8) & 0xff; + bytes[3] = val & 0xff; +} + +static /* inline */ u_long +_2btol(SANE_Byte *bytes) +{ + u_long rv; + + rv = (bytes[0] << 8) | bytes[1]; + + return rv; +} + +static /* inline */ u_long +_4btol(SANE_Byte *bytes) +{ + u_long rv; + + rv = (bytes[0] << 24) | + (bytes[1] << 16) | + (bytes[2] << 8) | + bytes[3]; + + return rv; +} + +#define SANE_TITLE_SCAN_MODE_GROUP "Scan Mode" +#define SANE_TITLE_GEOMETRY_GROUP "Geometry" +#define SANE_TITLE_FEEDER_GROUP "Feeder" +#define SANE_TITLE_ENHANCEMENT_GROUP "Enhancement" +#define SANE_TITLE_ICON_GROUP "Icon" +#define SANE_TITLE_BARCODE_GROUP "Barcode" + +#define SANE_NAME_AUTOBORDER "autoborder" +#define SANE_TITLE_AUTOBORDER "Autoborder" +#define SANE_DESC_AUTOBORDER "Enable Automatic Border Detection" + +#define SANE_NAME_COMPRESSION "compression" +#define SANE_TITLE_COMPRESSION "Data Compression" +#define SANE_DESC_COMPRESSION "Sets the compression mode of the scanner" + +#define SANE_NAME_TIMEOUT_ADF "timeout-adf" +#define SANE_TITLE_TIMEOUT_ADF "ADF Timeout" +#define SANE_DESC_TIMEOUT_ADF "Sets the timeout in seconds for the ADF" + +#define SANE_NAME_TIMEOUT_MANUAL "timeout-manual" +#define SANE_TITLE_TIMEOUT_MANUAL "Manual Timeout" +#define SANE_DESC_TIMEOUT_MANUAL "Sets the timeout in seconds for manual feeder" + +#define SANE_NAME_BATCH "batch" +#define SANE_TITLE_BATCH "Batch" +#define SANE_DESC_BATCH "Enable Batch Mode" + +#define SANE_NAME_CHECK_ADF "check-adf" +#define SANE_TITLE_CHECK_ADF "Check ADF" +#define SANE_DESC_CHECK_ADF "Check ADF Status prior to starting scan" + +#define SANE_NAME_DUPLEX "duplex" +#define SANE_TITLE_DUPLEX "Duplex" +#define SANE_DESC_DUPLEX "Enable Duplex (Dual-Sided) Scanning" + +#define SANE_NAME_BARCODE_SEARCH_COUNT "barcode-search-count" +#define SANE_TITLE_BARCODE_SEARCH_COUNT "Barcode Search Count" +#define SANE_DESC_BARCODE_SEARCH_COUNT "Number of barcodes to search for in the scanned image" + +#define SANE_NAME_BARCODE_HMIN "barcode-hmin" +#define SANE_TITLE_BARCODE_HMIN "Barcode Minimum Height" +#define SANE_DESC_BARCODE_HMIN "Sets the Barcode Minimun Height (larger values increase recognition speed)" + +#define SANE_NAME_BARCODE_SEARCH_MODE "barcode-search-mode" +#define SANE_TITLE_BARCODE_SEARCH_MODE "Barcode Search Mode" +#define SANE_DESC_BARCODE_SEARCH_MODE "Chooses the orientation of barcodes to be searched" + +#define SANE_NAME_BARCODE_SEARCH_TIMEOUT "barcode-search-timeout" +#define SANE_TITLE_BARCODE_SEARCH_TIMEOUT "Barcode Search Timeout" +#define SANE_DESC_BARCODE_SEARCH_TIMEOUT "Sets the timeout for barcode searching" + +#define SANE_NAME_BARCODE_SEARCH_BAR "barcode-search-bar" +#define SANE_TITLE_BARCODE_SEARCH_BAR "Barcode Search Bar" +#define SANE_DESC_BARCODE_SEARCH_BAR "Specifies the barcode type to search for" + +#define SANE_NAME_SECTION "section" +#define SANE_TITLE_SECTION "Image/Barcode Search Sections" +#define SANE_DESC_SECTION "Specifies an image section and/or a barcode search region" + +#define SANE_NAME_BARCODE_RELMAX "barcode-relmax" +#define SANE_TITLE_BARCODE_RELMAX "Barcode RelMax" +#define SANE_DESC_BARCODE_RELMAX "Specifies the maximum relation from the widest to the smallest bar" + +#define SANE_NAME_BARCODE_BARMIN "barcode-barmin" +#define SANE_TITLE_BARCODE_BARMIN "Barcode Bar Minimum" +#define SANE_DESC_BARCODE_BARMIN "Specifies the minimum number of bars in Bar/Patch code" + +#define SANE_NAME_BARCODE_BARMAX "barcode-barmax" +#define SANE_TITLE_BARCODE_BARMAX "Barcode Bar Maximum" +#define SANE_DESC_BARCODE_BARMAX "Specifies the maximum number of bars in a Bar/Patch code" + +#define SANE_NAME_BARCODE_CONTRAST "barcode-contrast" +#define SANE_TITLE_BARCODE_CONTRAST "Barcode Contrast" +#define SANE_DESC_BARCODE_CONTRAST "Specifies the image contrast used in decoding. Use higher values when " \ +"there are more white pixels in the code" + +#define SANE_NAME_BARCODE_PATCHMODE "barcode-patchmode" +#define SANE_TITLE_BARCODE_PATCHMODE "Barcode Patch Mode" +#define SANE_DESC_BARCODE_PATCHMODE "Controls Patch Code detection." + +#define SANE_NAME_CONTROL_PANEL "control-panel" +#define SANE_TITLE_CONTROL_PANEL "Control Panel " +#define SANE_DESC_CONTROL_PANEL "Enables the scanner's control panel" + +#define SANE_NAME_ACE_FUNCTION "ace-function" +#define SANE_TITLE_ACE_FUNCTION "ACE Function" +#define SANE_DESC_ACE_FUNCTION "ACE Function" + +#define SANE_NAME_ACE_SENSITIVITY "ace-sensitivity" +#define SANE_TITLE_ACE_SENSITIVITY "ACE Sensitivity" +#define SANE_DESC_ACE_SENSITIVITY "ACE Sensitivity" + +#define SANE_NAME_ICON_WIDTH "icon-width" +#define SANE_TITLE_ICON_WIDTH "Icon Width" +#define SANE_DESC_ICON_WIDTH "Width of icon (thumbnail) image in pixels" + +#define SANE_NAME_ICON_LENGTH "icon-length" +#define SANE_TITLE_ICON_LENGTH "Icon Length" +#define SANE_DESC_ICON_LENGTH "Length of icon (thumbnail) image in pixels" + +#define SANE_NAME_PAPER_SIZE "paper-size" +#define SANE_TITLE_PAPER_SIZE "Paper Size" +#define SANE_DESC_PAPER_SIZE "Specify the scan window geometry by specifying the paper size " \ +"of the documents to be scanned" + +#define SANE_NAME_INQUIRY "inquiry" +#define SANE_TITLE_INQUIRY "Inquiry Data" +#define SANE_DESC_INQUIRY "Displays scanner inquiry data" + +/* low level SCSI commands and buffers */ + +/* SCSI commands */ +#define BH_SCSI_TEST_UNIT_READY 0x00 +#define BH_SCSI_SET_WINDOW 0x24 +#define BH_SCSI_GET_WINDOW 0x25 +#define BH_SCSI_READ_SCANNED_DATA 0x28 +#define BH_SCSI_INQUIRY 0x12 +#define BH_SCSI_MODE_SELECT 0x15 +#define BH_SCSI_START_SCAN 0x1b +#define BH_SCSI_MODE_SENSE 0x1a +#define BH_SCSI_GET_BUFFER_STATUS 0x34 +#define BH_SCSI_OBJECT_POSITION 0x31 + +/* page codes used with BH_SCSI_INQUIRY */ +#define BH_INQUIRY_STANDARD_PAGE_CODE 0x00 +#define BH_INQUIRY_VPD_PAGE_CODE 0xC0 +#define BH_INQUIRY_JIS_PAGE_CODE 0xF0 + +/* page codes used with BH_SCSI_MODE_SELECT and BH_SCSI_MODE_SENSE */ +#define BH_MODE_MEASUREMENT_PAGE_CODE 0x03 +#define BH_MODE_TIMEOUT_PAGE_CODE 0x20 +#define BH_MODE_ICON_PAGE_CODE 0x21 +#define BH_MODE_BARCODE_PRIORITY_PAGE_CODE 0x30 +#define BH_MODE_BARCODE_PARAM1_PAGE_CODE 0x31 +#define BH_MODE_BARCODE_PARAM2_PAGE_CODE 0x32 +#define BH_MODE_BARCODE_PARAM3_PAGE_CODE 0x32 + +/* data type codes used with BH_SCSI_READ_SCANNED_DATA */ +#define BH_SCSI_READ_TYPE_FRONT 0x80 +/* 0x81 thru 0x88 read front page sections 1 thru 8 respectively */ +#define BH_SCSI_READ_TYPE_BACK 0x90 +/* 0x91 thru 0x98 read back page sections 1 thru 8 respectively */ +#define BH_SCSI_READ_TYPE_FRONT_BARCODE 0xA0 +/* 0xA1 thru 0xA8 read front page barcodes in sections 1 thru 8 respectively */ +#define BH_SCSI_READ_TYPE_BACK_BARCODE 0xB0 +/* 0xB1 thru 0xB8 read back page barcodes in sections 1 thru 8 respectively */ +#define BH_SCSI_READ_TYPE_FRONT_PATCHCODE 0xC0 +/* 0xC1 thru 0xC8 read front page patchcodes in sections 1 thru 8 respectively */ +#define BH_SCSI_READ_TYPE_BACK_PATCHCODE 0xD0 +/* 0xD1 thru 0xD8 read back page patchcodes in sections 1 thru 8 respectively */ +#define BH_SCSI_READ_TYPE_FRONT_ICON 0x89 +#define BH_SCSI_READ_TYPE_BACK_ICON 0x99 + +/* this one is not a real readtype; it's used to help transfer the barcode file */ +#define BH_SCSI_READ_TYPE_SENDBARFILE 0xBB + +#define BH_HAS_IMAGE_DATA(i) ((i) >= BH_SCSI_READ_TYPE_FRONT && \ + (i) <= BH_SCSI_READ_TYPE_BACK_ICON) + +/* batchmode codes used with BH_SCSI_SET_WINDOW */ +#define BH_BATCH_DISABLE 0x00 +#define BH_BATCH_ENABLE 0x01 +#define BH_BATCH_TERMINATE 0x02 +#define BH_BATCH_ABORT 0x03 + +/* used with BH_SCSI_SET_WINDOW, BH_SCSI_GET_WINDOW */ +typedef struct _BH_SectionBlock { + SANE_Byte ul_x[4]; + SANE_Byte ul_y[4]; + SANE_Byte width[4]; + SANE_Byte length[4]; + SANE_Byte compressiontype; + SANE_Byte compressionarg; + SANE_Byte reserved[6]; +} BH_SectionBlock; + +/* used with BH_SCSI_SET_WINDOW, BH_SCSI_GET_WINDOW */ +struct window_data { + SANE_Byte windowid; + SANE_Byte autoborder; + SANE_Byte xres[2]; + SANE_Byte yres[2]; + SANE_Byte ulx[4]; + SANE_Byte uly[4]; + SANE_Byte windowwidth[4]; + SANE_Byte windowlength[4]; + SANE_Byte brightness; + SANE_Byte threshold; + SANE_Byte contrast; + SANE_Byte imagecomposition; + SANE_Byte bitsperpixel; + SANE_Byte halftonecode; + SANE_Byte halftoneid; + SANE_Byte paddingtype; + SANE_Byte bitordering[2]; + SANE_Byte compressiontype; + SANE_Byte compressionarg; + SANE_Byte reserved2[6]; + SANE_Byte remote; + SANE_Byte acefunction; + SANE_Byte acesensitivity; + SANE_Byte batchmode; + SANE_Byte reserved3[20]; + BH_SectionBlock sectionblock[NUM_SECTIONS]; +}; + +/* used with BH_SCSI_READ_SCANNED_DATA */ +/* structure for returned decoded barcode data */ +struct barcode_data { + SANE_Byte reserved1[2]; + SANE_Byte barcodetype[2]; + SANE_Byte statusflag[2]; + SANE_Byte barcodeorientation[2]; + SANE_Byte posxa[2]; + SANE_Byte posya[2]; + SANE_Byte posxb[2]; + SANE_Byte posyb[2]; + SANE_Byte posxc[2]; + SANE_Byte posyc[2]; + SANE_Byte posxd[2]; + SANE_Byte posyd[2]; + SANE_Byte barcodesearchtime[2]; + SANE_Byte reserved2[13]; + SANE_Byte barcodelen; + SANE_Byte barcodedata[160]; +}; + +/* structure for returned icon data block */ +struct icon_data { + SANE_Byte windowwidth[4]; + SANE_Byte windowlength[4]; + SANE_Byte iconwidth[4]; + SANE_Byte iconwidthbytes[4]; + SANE_Byte iconlength[4]; + SANE_Byte bitordering; + SANE_Byte reserved[7]; + SANE_Byte icondatalen[4]; +}; + + +/* used with BH_SCSI_INQUIRY */ + +/* Standard Data [EVPD=0] */ +struct inquiry_standard_data { + SANE_Byte devtype; + SANE_Byte reserved[7]; + SANE_Byte vendor[8]; + SANE_Byte product[16]; + SANE_Byte revision[4]; +}; + +/* VPD Information [EVPD=1, PageCode=C0H] */ +struct inquiry_vpd_data { + SANE_Byte devtype; + SANE_Byte pagecode; + SANE_Byte reserved1; + SANE_Byte alloclen; + SANE_Byte adf; + SANE_Byte reserved2[2]; + SANE_Byte imagecomposition; + SANE_Byte imagedataprocessing[2]; + SANE_Byte compression; + SANE_Byte reserved3; + SANE_Byte sizerecognition; + SANE_Byte optionalfeatures; + SANE_Byte xmaxoutputbytes[2]; +}; + + +/* JIS Information [EVPD=1, PageCode=F0H] */ +struct inquiry_jis_data { + SANE_Byte devtype; + SANE_Byte pagecode; + SANE_Byte jisversion; + SANE_Byte reserved1; + SANE_Byte alloclen; + SANE_Byte basicxres[2]; + SANE_Byte basicyres[2]; + SANE_Byte resolutionstep; + SANE_Byte maxxres[2]; + SANE_Byte maxyres[2]; + SANE_Byte minxres[2]; + SANE_Byte minyres[2]; + SANE_Byte standardres[2]; + SANE_Byte windowwidth[4]; + SANE_Byte windowlength[4]; + SANE_Byte functions; + SANE_Byte reserved2; +}; + +/* used with BH_SCSI_MODE_SELECT and BH_SCSI_MODE_SENSE */ + +/* Scanning Measurement Parameters PageCode=03H */ +struct mode_page_03 { + SANE_Byte modedatalen; + SANE_Byte mediumtype; + SANE_Byte devicespecificparam; + SANE_Byte blockdescriptorlen; + + SANE_Byte pagecode; + SANE_Byte paramlen; + SANE_Byte bmu; + SANE_Byte reserved1; + SANE_Byte mud[2]; + SANE_Byte reserved2[2]; +}; + +/* Scan Command Timeout PageCode=20H */ +struct mode_page_20 { + SANE_Byte modedatalen; + SANE_Byte mediumtype; + SANE_Byte devicespecificparam; + SANE_Byte blockdescriptorlen; + + SANE_Byte pagecode; + SANE_Byte paramlen; + SANE_Byte timeoutmanual; + SANE_Byte timeoutadf; + SANE_Byte reserved[4]; +}; + +/* Icon Definition PageCode=21H */ +struct mode_page_21 { + SANE_Byte modedatalen; + SANE_Byte mediumtype; + SANE_Byte devicespecificparam; + SANE_Byte blockdescriptorlen; + + SANE_Byte pagecode; + SANE_Byte paramlen; + SANE_Byte iconwidth[2]; + SANE_Byte iconlength[2]; + SANE_Byte reserved[2]; +}; + +/* Bar/Patch Code search priority order PageCode=30H */ +struct mode_page_30 { + SANE_Byte modedatalen; + SANE_Byte mediumtype; + SANE_Byte devicespecificparam; + SANE_Byte blockdescriptorlen; + + SANE_Byte pagecode; + SANE_Byte paramlen; + SANE_Byte priority[6]; +}; + +/* Bar/Patch Code search parameters 1 of 3 PageCode=31H */ +struct mode_page_31 { + SANE_Byte modedatalen; + SANE_Byte mediumtype; + SANE_Byte devicespecificparam; + SANE_Byte blockdescriptorlen; + + SANE_Byte pagecode; + SANE_Byte paramlen; + SANE_Byte minbarheight[2]; + SANE_Byte searchcount; + SANE_Byte searchmode; + SANE_Byte searchtimeout[2]; +}; + +/* Bar/Patch Code search parameters 2 of 3 PageCode=32H */ +struct mode_page_32 { + SANE_Byte modedatalen; + SANE_Byte mediumtype; + SANE_Byte devicespecificparam; + SANE_Byte blockdescriptorlen; + + SANE_Byte pagecode; + SANE_Byte paramlen; + SANE_Byte relmax[2]; + SANE_Byte barmin[2]; + SANE_Byte barmax[2]; +}; + +/* Bar/Patch Code search parameters 3 of 3 PageCode=33H */ +struct mode_page_33 { + SANE_Byte modedatalen; + SANE_Byte mediumtype; + SANE_Byte devicespecificparam; + SANE_Byte blockdescriptorlen; + + SANE_Byte pagecode; + SANE_Byte paramlen; + SANE_Byte barcodecontrast[2]; + SANE_Byte patchmode[2]; + SANE_Byte reserved[2]; +}; + +#ifndef sane_isbasicframe +#define SANE_FRAME_TEXT 10 +#define SANE_FRAME_JPEG 11 +#define SANE_FRAME_G31D 12 +#define SANE_FRAME_G32D 13 +#define SANE_FRAME_G42D 14 +#define sane_strframe(f) ( (f) == SANE_FRAME_GRAY ? "gray" : \ + (f) == SANE_FRAME_RGB ? "RGB" : \ + (f) == SANE_FRAME_RED ? "red" : \ + (f) == SANE_FRAME_GREEN ? "green" : \ + (f) == SANE_FRAME_BLUE ? "blue" : \ + (f) == SANE_FRAME_TEXT ? "text" : \ + (f) == SANE_FRAME_JPEG ? "jpeg" : \ + (f) == SANE_FRAME_G31D ? "g31d" : \ + (f) == SANE_FRAME_G32D ? "g32d" : \ + (f) == SANE_FRAME_G42D ? "g42d" : \ + "unknown" ) + +#define sane_isbasicframe(f) ( (f) == SANE_FRAME_GRAY || \ + (f) == SANE_FRAME_RGB || \ + (f) == SANE_FRAME_RED || \ + (f) == SANE_FRAME_GREEN || \ + (f) == SANE_FRAME_BLUE ) + +#endif + +#endif /* BH_H */ diff --git a/doc/Makefile.in b/doc/Makefile.in index 29485ecc3..1393e924e 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -38,7 +38,7 @@ SECT5 = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 sane-dll.5 \ sane-artec.5 sane-fujitsu.5 sane-sharp.5 sane-s9036.5 \ sane-tamarack.5 sane-ricoh.5 sane-avision.5 sane-plustek.5 \ sane-st400.5 sane-mustek_pp.5 sane-dc210.5 sane-v4l.5 \ - sane-snapscan.5 sane-canon.5 sane-coolscan.5 + sane-snapscan.5 sane-canon.5 sane-coolscan.5 sane-bh.5 MANPAGES = $(SECT1) $(SECT5) DOCS = sane.ps sane.dvi LATEX = TEXINPUTS=$(srcdir):$$TEXINPUTS latex diff --git a/doc/sane-bh.man b/doc/sane-bh.man new file mode 100644 index 000000000..e151586eb --- /dev/null +++ b/doc/sane-bh.man @@ -0,0 +1,545 @@ +.TH sane-bh 5 "15 Sep 1999" +.IX sane-bh +.SH NAME +sane-bh - SANE backend for Bell+Howell Copiscan II series document +scanners +.SH DESCRIPTION +The +.B sane-bh +library implements a SANE (Scanner Access Now Easy) backend that +provides access to Bell+Howell Copiscan II series document +scanners. The Copiscan II 6338 has been the primary scanner model +used during development and testing, but since the programming interface +for the entire series is consistent the backend should work for the +following scanner models. +.PP +.RS +COPISCAN II 6338 Duplex Scanner with ACE +.br +COPISCAN II 2135 Simplex Scanner +.br +COPISCAN II 2137(A) Simplex Scanner (with ACE) +.br +COPISCAN II 2138A Simplex Scanner with ACE +.br +COPISCAN II 3238 Simplex Scanner +.br +COPISCAN II 3338(A) Simplex Scanner (with ACE) +.br +.RE +.PP +If you have a Bell+Howell scanner and are able to test it with this +backend, please contact +.IR sane\-devel@mostang.com +with the model number and testing results. Additionally, the author is +curious as to the likelihood of using this backend with the newer +4000 and 8000 series scanners. If you have such a beast, please let +me know. +.PP +The Bell+Howell Copiscan II series document scanners are high +volume, high throughput scanners designed for document scanning +applications. As such, they are lineart/grayscale scanners supporting +a fixed number of fairly low resolutions (e.g. 200/240/300dpi). +However, they do have a number of interesting and useful features +suited to needs of document imaging applications. +This backend attempts to support as many of these features as possible. +.PP +The main technical reference used in writing this backend is the +.B Bell and Howell Copiscan II Remote SCSI Controller (RSC) OEM +.B Technical Manual Version 1.5. +The Linux SCSI programming HOWTO, the SANE API documentation, and +SANE source code were also extremely valuable resources. + +.PP +The latest backend release, additional information and helpful hints +are available from the backend homepage: +.br +.RS +.B http://www.martoneconsulting.com/sane-bh.html +.RE +.SH "DEVICE NAMES" +This backend expects device names of the form: +.PP +.RS +.I special +.RE +.PP +Where +.I special +is the path-name for the special device that corresponds to a SCSI +scanner. For SCSI scanners, the special device name must be a generic +SCSI device or a symlink to such a device. Under Linux, such a device +name takes a format such as +.I /dev/sga +or +.IR /dev/sg0 , +for example. See sane-scsi(5) for details. +.SH CONFIGURATION +The contents of the +.I bh.conf +file is a list of device names that correspond to Bell+Howell +scanners. See sane-scsi(5) on details of what constitutes a valid device name. +Additionally, options can be specified; these lines begin with the word "option". +Each option is described in detail below. Empty lines and lines starting +with a hash mark (#) are ignored. + +.SH OPTIONS +The following options can be specified in the +.I bh.conf +file. +.TP +.B disable-optional-frames +This option prevents the backend from sending any optional frames. This +option may be useful when dealing with frontends which do not support these +optional frames. When this option is in effect, the data is sent in a +SANE_FRAME_GRAY frame. The optional frames sent by this backend are: +SANE_FRAME_G31D, SANE_FRAME_G32D, SANE_FRAME_G42D and SANE_FRAME_TEXT. +These frames are generated based on the compression and barcode options. +These frames are never sent in preview mode. +.TP +.B fake-inquiry +This option is used for debugging purposes and its use is not encouraged. +Essentially, it allows the backend to initialize in the absence of +a scanner. This is useful for development and not much else. +This option must be specified earlier in the configuration file than +the devices which are to be "faked". + +.SH FILES +.TP +.I @CONFIGDIR@/bh.conf +The backend configuration file (see also description of +.B SANE_CONFIG_DIR +below). +.TP +.I @LIBDIR@/libsane-bh.a +The static library implementing this backend. +.TP +.I @LIBDIR@/libsane-bh.so +The shared library implementing this backend (present on systems that +support dynamic loading). +.SH ENVIRONMENT +.TP +.B SANE_CONFIG_DIR +This environment variable specifies the list of directories that may +contain the configuration file. Under UNIX, the directories are +separated by a colon (`:'), under OS/2, they are separated by a +semi-colon (`;'). If this variable is not set, the configuration file +is searched in two default directories: first, the current working +directory (".") and then in @CONFIGDIR@. If the value of the +environment variable ends with the directory separator character, then +the default directories are searched after the explicitly specified +directories. For example, setting +.B SANE_CONFIG_DIR +to "/tmp/config:" would result in directories "tmp/config", ".", and +"@CONFIGDIR@" being searched (in this order). +.TP +.B SANE_DEBUG_BH +If the library was compiled with debug support enabled, this +environment variable controls the debug level for this backend. E.g., +a value of 255 requests all debug output to be printed. Smaller +levels reduce verbosity. + +.SH "SUPPORTED FEATURES" +.TP +.B ADF support +With document scanners, automatic document feeder (ADF) support is a key +feature. The backend supports the ADF by default and returns +.B SANE_STATUS_NO_DOCS +when the out-of-paper condition is detected. The SANE frontend +.B scanadf +is a command line frontend that supports multi-page scans. It has been +used successfully with this backend. The SANE frontend +.B xsane +is an improved GUI frontend by Oliver Rauch. Support for multi-page +scans is included in xsane version 0.35 and above. + +.TP +.B Duplex scanning +Some models, such as the COPISCAN II 6338, support duplex scanning. That +is, they scan both sides of the document during a single pass through the +scanner (the scanner has two cameras). This backend supports duplex +scanning (with the +.B --duplex +option). The front and back page images are delivered consecutively +as if they were separately scanned pages. + +.TP +.B Hardware compression +The scanner is capable of compressing the data into several industry +standard formats (CCITT G3, CCITT G3-2D, CCITT G4). This results in +increased performance as less data is passed from the scanner to the +host over the SCSI bus. The backend supports these compression formats +via the +.B --g31d, --g32d, --g42d +options, respectively. Many SANE frontends are not equipped to deal with +these formats, however. The SANE frontend +.B scanadf +supports these optional frame formats. The compressed image data +is written directly to a file and can then be processed by a scan-script +using the +.B --scan-script +option. Examples of this are given on the scanadf homepage. + +.TP +.B Automatic Border Detection +The scanner can automatically detect the paper size and adjust the +scanning window geometry appropriately. The backend supports this +useful feature with the +.B --autoborder +option. It is enabled by default. + +.TP +.B Batch Mode Scanning +The batch scan mode allows for maximum throughput. The Set Window +parameters must remain constant during the entire batch. + +.TP +.B Icon Generation +The Icon function generates a thumbnail of the full page image, that can be +transferred as if it were a separate page. This allows the host to +quickly display a thumbnail representation during the scanning operation. +Perhaps this would be a great way of implementing a preview scan, but +since a normal scan is so quick, it might not be worth the trouble. + +.TP +.B Multiple Sections +Multiple sections (scanning sub-windows) can be defined for the front and +back pages. Each section can have different characteristics (e.g. geometry, +compression). The sections are returned as if they were separately +scanned images. Additionally sections can be used to greatly enhance the +accuracy and efficiency of the barcode/patchcode decoding process by +limiting the search area to a small subset of the page. Most Copiscan II +series scanners support up to 8 user-defined sections. + +.TP +.B Support Barcode/Patchcode Decoding +The RSC unit can recognize Bar and Patch Codes of various types embedded +in the scanned image. The codes are decoded and the data is returned to +the frontend as a text frame. The text is encoded in xml and contains +a great deal of information about the decoded data such as the location +where it was found, its orientation, and the time it took to find. +Further information on the content of this text frame as well as some +barcode decoding examples can be found on the backend homepage. + +.SH LIMITATIONS +.TP +.B Decoding a single barcode type per scan +The RSC unit can search for up to six different barcode types at a time. +While the code generally supports this as well, the +.B --barcode-search-bar +option only allows the user to specify a single barcode type. +Perhaps another option which allows a comma separated list of barcode +type codes could be added to address this. +.TP +.B Scanning a fixed number of pages in batch mode +The separation of front and back end functionality in SANE presents a +problem in supporting the 'cancel batch' functionality in the scanner. +In batch mode, the scanner is always a page ahead of the host. The host, +knowing ahead of time which page will be the last, can cancel batch mode +prior to initiating the last scan command. Currently, there is no mechanism +available for the frontend to pass this knowledge to the backend. +If batch mode is enabled and the --end-count terminates a scanadf session, +an extra page will be pulled through the scanner, but is niether read +nor delivered to the frontend. The issue can be avoided by specifying +--batch=no when scanning a fixed number of pages. +.TP +.B Revision 1.2 Patch detector +There is an enhanced patchcode detection algorithm available in the RSC +with revision 1.2 or higher that is faster and more reliable than the +standard Bar/Patch code decoder. This is not currently supported. + +.SH OPTIONS +.TP +.B Scan Mode Options: +.TP +.B --preview[=(yes|no)] [no] +Request a preview-quality scan. When preview is set to yes image +compression is disabled and the image is delivered in a +SANE_FRAME_GRAY frame. +.TP +.B --mode lineart|halftone [lineart] +Selects the scan mode (e.g., lineart,monochrome, or color). +.TP +.B --resolution 200|240|300dpi [200] +Sets the resolution of the scanned image. Each scanner model supports +a list of standard resolutions; only these resolutions can be used. +.TP +.B --compression none|g31d|g32d|g42d [none] +Sets the compression mode of the scanner. Determines the type of data +returned from the scanner. Values are: +.RS +.B none +- uncompressed data - delivered in a SANE_FRAME_GRAY frame +.br +.B g31d +- CCITT G3 1 dimension (MH) - delivered in a SANE_FRAME_G31D frame +.br +.B g32d +- CCITT G3 2 dimensions (MR, K=4) - delivered in a SANE_FRAME_G32D frame +.br +.B g42d +- CCITT G4 (MMR) - delivered in a SANE_FRAME_G42D frame +.br +NOTE: The use of g31d, g32d, and g42d compression values causes the backend +to generate optional frame formats which may not be supported by all SANE +frontends. +.RE + +.TP +.B Geometry Options: +.TP +.B --autoborder[=(yes|no)] [yes] +Enable/Disable automatic image border detection. When enabled, the RSC unit +automatically detects the image area and sets the window geometry to match. +.TP +.B --paper-size Custom|Letter|Legal|A3|A4|A5|A6|B4|B5 [Custom] +Specify the scan window geometry by specifying the paper size of the +documents to be scanned. +.TP +.B --tl-x 0..297.18mm [0] +Top-left x position of scan area. +.TP +.B --tl-y 0..431.8mm [0] +Top-left y position of scan area. +.TP +.B --br-x 0..297.18mm [297.18] +Bottom-right x position of scan area. +.TP +.B --br-y 0..431.8mm [431.8] +Bottom-right y position of scan area. +.TP +.B Feeder Options: +.TP +.B --source Automatic Document Feeder|Manual Feed Tray [Automatic Document Feeder] +Selects the scan source (such as a document feeder). This option is provided +to allow multiple image scans with xsane; it has no other purpose. +.TP +.B --batch[=(yes|no)] [no] +Enable/disable batch mode scanning. Batch mode allows scanning at maximum throughput +by buffering within the RSC unit. This option is recommended when performing multiple +pages scans until the feeder is emptied. +.TP +.B --duplex[=(yes|no)] [no] +Enable duplex (dual-sided) scanning. The scanner takes an image of each side +of the document during a single pass through the scanner. The front page is +delivered followed by the back page. Most options, such as compression, +affect both the front and back pages. +.TP +.B --timeout-adf 0..255 [0] +Sets the timeout in seconds for the automatic document feeder (ADF). +The value 0 specifies the hardware default value which varies based +on the scanner model. +.TP +.B --timeout-manual 0..255 [0] +Sets the timeout in seconds for semi-automatic feeder. The value 0 specifies +the hardware default value which varies based on the scanner model. +.TP +.B --check-adf[=(yes|no)] [no] +Check ADF Status prior to starting scan using the OBJECT POSITION command. +Note that this feature requires RSC firmware level 1.5 or higher and dip +switch 4 must be in the on position. NOTE: This option has not been tested +extensively and may produce undesireable results. +.TP +.B Enhancement: +.TP +.B --control-panel[=(yes|no)] [yes] +Enables the scanner's control panel for selecting image enhancement +parameters. When the option is set to no the following options are +used to control image enhancement. See the Bell+Howell scanner users' +guide for complete information on ACE functionality. +.TP +.B --ace-function -4..4 [3] +Specify the Automatic Contrast Enhancement (ACE) Function. +.TP +.B --ace-sensitivity 0..9 [5] +Specify the Automatic Contrast Enhancement (ACE) Sensitivity. +.TP +.B --brightness 0..255 [0] +Controls the brightness of the acquired image. Ignored for ACE +capable scanners. +.TP +.B --threshold 0..255 [0] +Select minimum-brightness to get a white point. Ignored for ACE +capable scanners. +.TP +.B --contrast 0..255 [inactive] +Controls the contrast of the acquired image. This option is not +currently used by the scanner (and perhaps never will be). +.TP +.B --negative[=(yes|no)] [no] +Swap black and white, yielding a reverse-video image. +.TP +.B Icon: +.TP +.B --icon-width 0..3600pel (in steps of 8) [0] +Width of icon (thumbnail) image in pixels. +.TP +.B --icon-length 0..3600pel (in steps of 8) [0] +Length of icon (thumbnail) image in pixels. +.TP +.B Barcode Options: +.TP +.B --barcode-search-bar [none] +Specifies the barcode type to search for. If this option is +not specified, or specified with a value of none, then the barcode decoding +feature is completely disabled. The valid barcode type are: +.RS +.br +.B none +.br +.B ean-8 +.br +.B ean-13 +.br +.B reserved-ean-add +.br +.B code39 +.br +.B code2-5-interleaved +.br +.B code2-5-3lines-matrix +.br +.B code2-5-3lines-datalogic +.br +.B code2-5-5lines-industrial +.br +.B patchcode +.br +.B codabar +.br +.B codabar-with-start-stop +.br +.B code39ascii +.br +.B code128 +.br +.B code2-5-5lines-iata +.br +.RE +.TP +.B --barcode-search-count 1..7 [3] +Number of times that the RSC performs the decoding algorithm. Specify +the smallest number possible to increase performance. If you are having +trouble recognizing barcodes, it is suggested that you increase this option +to its maximum value (7). +.TP +.B --barcode-search-mode [horiz-vert] +Chooses the orientation of barcodes to be searched. The valid orientations +are: +.RS +.br +.B horiz-vert +.br +.B horizontal +.br +.B vertical +.br +.B vert-horiz +.RE +.TP +.B --barcode-hmin 0..1660mm [5] +Sets the barcode minimum height in millimeters (larger values increase +recognition speed). Of course the actual barcodes in the document must be +of sufficient size. +.TP +.B --barcode-search-timeout 20..65535us [10000] +Sets the timeout for barcode searching in milliseconds. When the timeout +expires, the decoder will stop trying to decode barcodes. +.TP +.B --section [] +Specifies a series of image sections. A section can be used to gather +a subset image or to provide a small area for barcode decoding. +Each section is specified in the following format (units are in millimeters): +.PP +.B x++[:functioncode...] +.PP +Multiple sections can be specified by separating them with commas. +.PP +For example +.B 76.2x25.4+50.8+0:frontbar +identifies an area 3 inches wide and 1 inch high with a top left corner +at the top of the page two inches from the left hand edge of the page. +This section will be used for barcode decoding on the front page only. +.PP +For example +.B 50.8x25.4+25.4+0:frontbar:front:g42d +identifies an area 2 inches wide and 1 inch high with a top left corner +at the top of the page one inch from the left hand edge of the page. +This section will be used for barcode decoding on the front page as well +as generating an image compressed in g42d format. +.PP +Ordinarily barcodes are searched in the entire image. However, when you +specify sections all barcode searching is done within the specific sections +identified. This can significantly speed up the decoding process. + +The following functioncodes are available: +.RS +.br +.B front +- generate an image for the front page section +.br +.B back +- generate an image for the back page section +.br +.B frontbar +- perform barcode search in front page section +.br +.B backbar +- perform barcode search in back page section +.br +.B frontpatch +- perform patchcode search in front page section +.br +.B backpatch +- perform patchcode search in back page section +.br +.B none +- use no image compression +.br +.B g31d +- use Group 3 1 dimension image compression +.br +.B g32d +- use Group 3 2 dimensions image compression +.br +.B g42d +- use Group 4 2 dimensions image compression +.br +.RE +.PP +If you omit a compression functioncode, the full page compression setting +is used. If you specify multiple compression functioncodes, only the +last one is used. + +.TP +.B --barcode-relmax 0..255 [0] +Specifies the maximum relation from the widest to the smallest bar. +.TP +.B --barcode-barmin 0..255 [0] +Specifies the minimum number of bars in Bar/Patch code. +.TP +.B --barcode-barmax 0..255 [0] +Specifies the maximum number of bars in a Bar/Patch code. +.TP +.B --barcode-contrast 0..6 [3] +Specifies the image contrast used in decoding. Use higher values when +there are more white pixels in the code. +.TP +.B --barcode-patchmode 0..1 [0] +Controls Patch Code detection. + +.SH BUGS +This is a new backend; detailed bug reports are welcome -- and expected ;) +.PP +If you have found something that you think is a bug, please attempt to +recreate it with the SANE_DEBUG_BH environment variable set to +255, and send a report detailing the conditions surrounding the bug to +.IR sane\-devel@mostang.com . + +.SH "SEE ALSO" +sane\-scsi(5), scanimage(1), scanadf(1) + +.SH AUTHOR +The sane-bh backend was written by Tom Martone, based on the sane-ricoh +backend by Feico W. Dillema and the bnhscan program by Sean Reifschneider +of tummy.com ltd.