summaryrefslogtreecommitdiff
path: root/backend/hs2p-scsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/hs2p-scsi.c')
-rw-r--r--backend/hs2p-scsi.c2198
1 files changed, 2198 insertions, 0 deletions
diff --git a/backend/hs2p-scsi.c b/backend/hs2p-scsi.c
new file mode 100644
index 0000000..82640e9
--- /dev/null
+++ b/backend/hs2p-scsi.c
@@ -0,0 +1,2198 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2007 Jeremy Johnson
+ This file is part of a SANE backend for Ricoh IS450
+ and IS420 family of HS2P Scanners using the SCSI controller.
+
+ 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 <time.h>
+#include "hs2p.h"
+
+
+static SANE_String_Const
+print_devtype (SANE_Byte devtype)
+{
+ int i = devtype;
+ static SANE_String devtypes[] = {
+ "disk",
+ "tape",
+ "printer",
+ "processor",
+ "CD-writer",
+ "CD-drive",
+ "scanner",
+ "optical-drive",
+ "jukebox",
+ "communicator"
+ };
+
+ return (i >= 0 && i < NELEMS (devtypes)) ? devtypes[i] : "unknown-device";
+}
+static void
+print_bytes (const void *buf, size_t bufsize)
+{
+ const SANE_Byte *bp;
+ unsigned i;
+
+ for (i = 0, bp = buf; i < bufsize; i++, bp++)
+ DBG (DBG_error, "%3d: 0x%02x %d\n", i, *bp, *bp);
+}
+
+static void
+ScannerDump (HS2P_Scanner * s)
+{
+ int i;
+ HS2P_Info *info;
+ SANE_Device *sdev;
+
+ info = &s->hw->info;
+ sdev = &s->hw->sane;
+
+ DBG (DBG_info, "\n\n");
+ DBG (DBG_info, ">> ScannerDump:\n");
+ DBG (DBG_info, "SANE Device: '%s' Vendor: '%s' Model: '%s' Type: '%s'\n",
+ sdev->name, sdev->vendor, sdev->model, sdev->type);
+
+ DBG (DBG_info, "Type: '%s' Vendor: '%s' Product: '%s' Revision: '%s'\n",
+ print_devtype (info->devtype), info->vendor, info->product,
+ info->revision);
+
+ DBG (DBG_info, "Automatic Document Feeder: %s%s%s%s\n",
+ info->hasADF ? "Installed " : "Not Installed ",
+ info->hasSimplex ? "simplex" : "",
+ info->hasDuplex ? "duplex" : "",
+ info->hasARDF ? "reverse double-sided" : "");
+ DBG (DBG_info, "Endorser :%s\n",
+ info->hasEndorser ? " <Installed>" : " <Not Installed>");
+ DBG (DBG_info, "Image Processing Unit:%s\n",
+ info->hasIPU ? " <Installed>" : " <Not Installed>");
+ DBG (DBG_info, "Extended Board :%s\n",
+ info->hasXBD ? " <Installed>" : " <Not Installed>");
+
+ DBG (DBG_info, "\n");
+ DBG (DBG_info, "Image Composition Support\n");
+ DBG (DBG_info, "Line Art (B/W) Support : %s\n",
+ info->supports_lineart ? "Yes" : "No");
+ DBG (DBG_info, "Dithering (Halftone) Support: %s\n",
+ info->supports_dithering ? "Yes" : "No");
+ DBG (DBG_info, "Error Diffusion Support : %s\n",
+ info->supports_errordiffusion ? "Yes" : "No");
+ DBG (DBG_info, "Color Support : %s\n",
+ info->supports_color ? "Yes" : "No");
+ DBG (DBG_info, "4 Bit Gray Support : %s\n",
+ info->supports_4bitgray ? "Yes" : "No");
+ DBG (DBG_info, "5-8 Bit Gray Support : %s\n",
+ info->supports_8bitgray ? "Yes" : "No");
+
+ DBG (DBG_info, "Image Data processing:%s%s%s%s%s%s\n",
+ info->supports_whiteframing ? " <White Frame>" : "",
+ info->supports_blackframing ? " <Black Frame>" : "",
+ info->supports_edgeextraction ? " <Edge Extraction>" : "",
+ info->supports_noiseremoval ? " <Noise Filter>" : "",
+ info->supports_smoothing ? " <Smooth>" : "",
+ info->supports_linebolding ? " <Line Bolding>" : "");
+
+ DBG (DBG_info, "Image Compression:%s%s%s%s\n",
+ info->supports_MH ? " <MH support>" : "",
+ info->supports_MR ? " <MR support>" : "",
+ info->supports_MMR ? " <MMR support>" : "",
+ info->supports_MHB ? " <MH byte boundary support>" : "");
+ DBG (DBG_info, "Marker Recognition: %s\n",
+ info->supports_markerrecognition ? "<supported>" : "<not supported>");
+ DBG (DBG_info, "Size Recognition : %s\n",
+ info->supports_sizerecognition ? "<supported>" : "<not supported>");
+ DBG (DBG_info, "X Maximum Output Pixels = %d\n", info->xmaxoutputpixels);
+
+ /*
+ DBG (DBG_info, "Optional Features:%s%s%s%s\n",
+ info->canBorderRecog ? " <Border Recognition>" : "",
+ info->canBarCode ? " <BarCode Decoding>" : "",
+ info->canIcon ? " <Icon Generation>" : "",
+ info->canSection ? " <Section Support>" : "");
+ */
+
+ DBG (DBG_info, "Max bytes per scan-line: %d (%d pixels)\n",
+ info->xmaxoutputpixels / 8, info->xmaxoutputpixels);
+
+ DBG (DBG_info, "Basic resolution (X/Y) : %d/%d\n", info->resBasicX,
+ info->resBasicY);
+ DBG (DBG_info, "Maximum resolution (X/Y) : %d/%d\n", info->resMaxX,
+ info->resMaxY);
+ DBG (DBG_info, "Minimum resolution (X/Y) : %d/%d\n", info->resMinX,
+ info->resMinY);
+ DBG (DBG_info, "Standard Resolutions:\n");
+ for (i = 1; i <= info->resStdList[0]; i++)
+ DBG (DBG_info, " %d\n", info->resStdList[i]);
+
+ DBG (DBG_info,
+ "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 (DBG_info, "Summary:%s%s%s\n",
+ info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
+ info->canACE ? " (ACE capable)" : "",
+ info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
+ */
+
+ DBG (DBG_info, "Buffer Full Ratio = %#02x\n",
+ info->cxn.buffer_full_ratio);
+ DBG (DBG_info, "Buffer Empty Ratio = %#02x\n",
+ info->cxn.buffer_empty_ratio);
+ DBG (DBG_info, "Bus Inactive Limit = %#02x\n",
+ info->cxn.bus_inactive_limit[0] << 8 | info->cxn.
+ bus_inactive_limit[1]);
+ DBG (DBG_info, "Disconnect Time Limit = %#04x\n",
+ info->cxn.disconnect_time_limit[0] << 8 | info->cxn.
+ disconnect_time_limit[1]);
+ DBG (DBG_info, "Connect Time Limit = %#02x\n",
+ info->cxn.connect_time_limit[0] << 8 | info->cxn.
+ connect_time_limit[1]);
+ DBG (DBG_info, "Maximum Burst Size = %#04x\n",
+ info->cxn.maximum_burst_size[0] << 8 | info->cxn.
+ maximum_burst_size[1]);
+ DBG (DBG_info, "DTDC = %#02x\n", info->cxn.dtdc & 0x03);
+
+ DBG (DBG_info, "White Balance is %s\n",
+ info->white_balance == 1 ? "Absolute" : "Relative");
+ DBG (DBG_info, "Medium Wait Timer is <not supported>\n"); /* get_medium_wait_timer(fd) */
+ DBG (DBG_info, "Scan Wait Mode is %s\n",
+ info->scan_wait_mode == 0 ? "OFF" : "ON");
+ DBG (DBG_info, "Service Mode is in Select %s Mode\n",
+ info->service_mode == 0 ? "Self-Diagnostics" : "Optical Adjustment");
+
+ sprintf (info->inquiry_data, "Vendor: %s Product: %s Rev: %s %s%s\n",
+ info->vendor, info->product, info->revision,
+ info->hasADF && info->hasDuplex ? "Duplex Scanner" : "",
+ info->hasADF && info->hasSimplex ? "Simplex Scanner" : "");
+
+ DBG (DBG_info, "duplex_default=%d\n", info->default_duplex);
+ /*
+ DBG (DBG_info, "autoborder_default=%d\n", info->autoborder_default);
+ DBG (DBG_info, "batch_default=%d\n", info->batch_default);
+ DBG (DBG_info, "deskew_default=%d\n", info->deskew_default);
+ DBG (DBG_info, "check_adf_default=%d\n", info->check_adf_default);
+ DBG (DBG_info, "timeout_adf_default=%d\n", info->timeout_adf_default);
+ DBG (DBG_info, "timeout_manual_default=%d\n", info->timeout_manual_default);
+ DBG (DBG_info, "control_panel_default=%d\n", info->control_panel_default);
+ */
+
+ DBG (DBG_info, "bmu = %d\n", info->bmu);
+ DBG (DBG_info, "mud = %d\n", info->mud);
+ DBG (DBG_info, "white balance = %#0x\n", info->white_balance);
+ DBG (DBG_info, "adf control = %#0x\n", info->adf_control);
+ DBG (DBG_info, "adf mode control = %#0x\n", info->adf_mode_control);
+ DBG (DBG_info, "endorser control = %#0x\n", info->endorser_control);
+ DBG (DBG_info, "endorser string = %s\n", info->endorser_string);
+ DBG (DBG_info, "scan wait mode = %#0x\n", info->scan_wait_mode);
+ DBG (DBG_info, "service mode = %#0x\n", info->service_mode);
+
+ DBG (DBG_info, "BasicXRes = %d\n", info->resBasicX);
+ DBG (DBG_info, "BasicYRes = %d\n", info->resBasicY);
+
+ DBG (DBG_info, "XResStep = %d\n", info->resXstep);
+ DBG (DBG_info, "YResStep = %d\n", info->resYstep);
+
+ DBG (DBG_info, "MaxXres = %d\n", info->resMaxX);
+ DBG (DBG_info, "MaxYres = %d\n", info->resMaxY);
+
+ DBG (DBG_info, "MinXres = %d\n", info->resMinX);
+ DBG (DBG_info, "MinYres = %d\n", info->resMinY);
+
+ DBG (DBG_info, "Width = %d\n", info->winWidth);
+ DBG (DBG_info, "Height = %d\n", info->winHeight);
+
+ DBG (DBG_info, "<< ScannerDump\n");
+}
+static void
+print_vpd_info (struct inquiry_vpd_data *vbuf)
+{
+ DBG (DBG_info, "VPD IDENTIFIER C0H\n");
+ DBG (DBG_info, "[00] Peripheral %#02x\n", vbuf->devtype);
+ DBG (DBG_info, "[01] Page Code %#02x\n", vbuf->pagecode);
+ DBG (DBG_info, "[02] reserved %#02x\n", vbuf->byte2);
+ DBG (DBG_info, "[03] Page Length %#02x\n", vbuf->pagelength);
+ DBG (DBG_info, "[04] ADF ID %#02x\n", vbuf->adf_id);
+ DBG (DBG_info, "[05] Endorser ID %#02x\n", vbuf->end_id);
+ DBG (DBG_info, "[06] Image Processing Unit %#02x\n", vbuf->ipu_id);
+ DBG (DBG_info, "[07] Image Composition %#02x\n",
+ vbuf->imagecomposition);
+ DBG (DBG_info, "[08] Image Data Processing %lu\n",
+ _2btol (&vbuf->imagedataprocessing[0]));
+ DBG (DBG_info, "[10] Compression %#02x\n", vbuf->compression);
+ DBG (DBG_info, "[11] Marker Recognition %#02x\n",
+ vbuf->markerrecognition);
+ DBG (DBG_info, "[12] Size Recognition %#02x\n",
+ vbuf->sizerecognition);
+ DBG (DBG_info, "[13] reserved %#02x\n", vbuf->byte13);
+ DBG (DBG_info, "[14] X Maximum Output Pixel %lu\n",
+ _2btol (&vbuf->xmaxoutputpixels[0]));
+}
+static void
+print_jis_info (struct inquiry_jis_data *jbuf)
+{
+ DBG (DBG_info, "JIS IDENTIFIER F0H\n");
+ DBG (DBG_info, "[00] devtype %#02x\n", jbuf->devtype);
+ DBG (DBG_info, "[01] Page Code %#02x\n", jbuf->pagecode);
+ DBG (DBG_info, "[02] JIS Ver %#02x\n", jbuf->jisversion);
+ DBG (DBG_info, "[03] reserved1 %#02x\n", jbuf->reserved1);
+ DBG (DBG_info, "[04] Page Len %#02x\n", jbuf->alloclen);
+ DBG (DBG_info, "[05] BasicXRes %lu\n", _2btol (&jbuf->BasicRes.x[0]));
+ DBG (DBG_info, "[07] BasicYRes %lu\n", _2btol (&jbuf->BasicRes.y[0]));
+ DBG (DBG_info, "[09] Resolution step %#02x\n", jbuf->resolutionstep);
+ DBG (DBG_info, "[10] MaxXRes %lu\n", _2btol (&jbuf->MaxRes.x[0]));
+ DBG (DBG_info, "[12] MaxYRes %lu\n", _2btol (&jbuf->MaxRes.y[0]));
+ DBG (DBG_info, "[14] MinXRes %lu\n", _2btol (&jbuf->MinRes.x[0]));
+ DBG (DBG_info, "[16] MinYRes %lu\n", _2btol (&jbuf->MinRes.y[0]));
+ DBG (DBG_info, "[18] Std Res %#0x\n",
+ (jbuf->standardres[0] << 8) | jbuf->standardres[1]);
+ DBG (DBG_info, "[20] Win Width %lu\n", _4btol (&jbuf->Window.width[0])); /* Manual says 4787/12B3H pixels @400dpi = 12in */
+ DBG (DBG_info, "[24] Win Len %lu\n", _4btol (&jbuf->Window.length[0])); /* Manual says 6803/1A93H pixels @400dpi = 17in) */
+ DBG (DBG_info, "[28] function %#02x\n", jbuf->functions);
+ DBG (DBG_info, "[29] reserved %#02x\n", jbuf->reserved2);
+}
+
+/* 1-3-1 TEST UNIT READY
+ Byte0: | 0x00 |
+ Byte1: | 7-5 Logical Unit Number | Reserved |
+ Byte2: | Reserved |
+ Byte3: | Reserved |
+ Byte4: | Reserved |
+ Byte5: | 7-6 Vendor Unique | 5-2 Reserved | 1 Flag | 0 Link |
+*/
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> test_unit_ready\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = HS2P_SCSI_TEST_UNIT_READY;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< test_unit_ready\n");
+ return (status);
+}
+
+/* 1-3-2 REQUEST SENSE
+ Byte0: | 0x00 |
+ Byte1: | 7-5 Logical Unit Number | Reserved |
+ Byte2: | Reserved |
+ Byte3: | Reserved |
+ Byte4: | Allocation Length |
+ Byte5: | 7-6 Vendor Unique | 5-2 Reserved | 1 Flag | 0 Link |
+*/
+
+#if 0
+static SANE_Status
+get_sense_data (int fd, SENSE_DATA * sense_data)
+{
+ SANE_Status status;
+ DBG (DBG_sane_proc, ">> get_sense_data\n");
+
+ static SANE_Byte cmd[6];
+ size_t len;
+
+ len = sizeof (*sense_data);
+ memset (sense_data, 0, len);
+ memset (cmd, 0, sizeof (cmd));
+
+ cmd[0] = HS2P_SCSI_REQUEST_SENSE;
+ cmd[4] = len;
+
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_data, &len);
+
+ DBG (DBG_proc, "<< get_sense_data\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+print_sense_data (int dbg_level, SENSE_DATA * data)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte *bp, *end;
+ SANE_Int i;
+
+ DBG (DBG_sane_proc, ">> print_sense_data\n");
+
+ bp = (SANE_Byte *) data;
+ end = bp + (SANE_Byte) sizeof (SENSE_DATA);
+ for (i = 0; bp < end; bp++, i++)
+ {
+ DBG (dbg_level, "Byte #%2d is %3d, 0x%02x\n", i, *bp, *bp);
+ }
+
+ DBG (dbg_level, "Valid=%1d, ErrorCode=%#x\n",
+ (data->error_code & 0x80) >> 7, data->error_code & 0x7F);
+ DBG (dbg_level, "Segment number = %d\n", data->segment_number);
+ DBG (dbg_level,
+ "F-mark=%1d, EOM=%1d, ILI=%1d, Reserved=%1d, SenseKey=%#x\n",
+ (data->sense_key & 0x80) >> 7, (data->sense_key & 0x40) >> 6,
+ (data->sense_key & 0x20) >> 5, (data->sense_key & 0x10) >> 4,
+ (data->sense_key & 0x0F));
+ DBG (dbg_level, "Information Byte = %lu\n", _4btol (data->information));
+ DBG (dbg_level, "Additional Sense Length = %d\n", data->sense_length);
+ DBG (dbg_level, "Command Specific Infomation = %lu\n",
+ _4btol (data->command_specific_information));
+ DBG (dbg_level, "Additional Sense Code = %#x\n", data->sense_code);
+ DBG (dbg_level, "Additional Sense Code Qualifier = %#x\n",
+ data->sense_code_qualifier);
+
+ DBG (DBG_proc, "<< print_sense_data\n");
+ return (status);
+}
+
+static struct sense_key *
+lookup_sensekey_errmsg (int code)
+{
+ int i;
+ struct sense_key *k = &sensekey_errmsg[0];
+
+ for (i = 0; i < 16; i++, k++)
+ if (k->key == code)
+ return k;
+ return NULL;
+}
+static struct ASCQ *
+lookup_ascq_errmsg (unsigned int code)
+{
+ unsigned int i;
+ struct ASCQ *k = &ascq_errmsg[0];
+
+ for (i = 0; i < 74; i++, k++)
+ if (k->codequalifier == code)
+ return k;
+ return NULL;
+}
+
+/* a sensible sense handler
+ arg is a pointer to the associated HS2P_Scanner structure
+
+ SENSE DATA FORMAT: 14 bytes bits[7-0]
+ Byte 0: [7]:valid [6-0]:Error Code
+ Byte 1: Segment Number
+ Byte 2: [7]: F-mark; [6]:EOM; [5]:ILI; [4]:reserved; [3-0]:Sense Key
+ Byte 3: Information Byte
+ Byte 4: Information Byte
+ Byte 5: Information Byte
+ Byte 6: Information Byte
+ Byte 7: Additional Sense Length (n-7)
+ Byte 8: Command Specific Information
+ Byte 9: Command Specific Information
+ Byte 10: Command Specific Information
+ Byte 11: Command Specific Information
+ Byte 12: Additional Sense Code
+ Byte 13: Additional Sense Code Qualifier
+*/
+static SANE_Status
+sense_handler (int __sane_unused__ scsi_fd, u_char * sense_buffer, void *sd)
+{
+ u_char sense, asc, ascq, EOM, ILI, ErrorCode, ValidData;
+ u_long MissingBytes;
+ char *sense_str = "";
+
+ struct sense_key *skey;
+ struct ASCQ *ascq_key;
+ SENSE_DATA *sdp = (SENSE_DATA *) sd;
+ SANE_Int i;
+ SANE_Status status = SANE_STATUS_INVAL;
+ SANE_Char print_sense[(16 * 3) + 1];
+
+ DBG (DBG_proc, ">> sense_handler\n");
+ if (DBG_LEVEL >= DBG_info)
+ print_sense_data (DBG_LEVEL, (SENSE_DATA *) sense_buffer);
+
+ /* store sense_buffer */
+ DBG (DBG_info, ">> copying %lu bytes from sense_buffer[] to sense_data\n",
+ (u_long) sizeof (SENSE_DATA));
+ memcpy (sdp, sense_buffer, sizeof (SENSE_DATA));
+ if (DBG_LEVEL >= DBG_info)
+ print_sense_data (DBG_LEVEL, sdp);
+
+ ErrorCode = sense_buffer[0] & 0x7F;
+ ValidData = (sense_buffer[0] & 0x80) != 0;
+ sense = sense_buffer[2] & 0x0f; /* Sense Key */
+ asc = sense_buffer[12]; /* Additional Sense Code */
+ ascq = sense_buffer[13]; /* Additional Sense Code Qualifier */
+ EOM = (sense_buffer[2] & 0x40) != 0; /* End Of Media */
+ ILI = (sense_buffer[2] & 0x20) != 0; /* Invalid Length Indicator */
+ MissingBytes = ValidData ? _4btol (&sense_buffer[3]) : 0;
+
+ DBG (DBG_sense,
+ "sense_handler: sense_buffer=%#x, sense=%#x, asc=%#x, ascq=%#x\n",
+ sense_buffer[0], sense, asc, ascq);
+ DBG (DBG_sense,
+ "sense_handler: ErrorCode %02x ValidData: %d "
+ "EOM: %d ILI: %d MissingBytes: %lu\n", ErrorCode, ValidData, EOM,
+ ILI, MissingBytes);
+
+ memset (print_sense, '\0', sizeof (print_sense));
+ for (i = 0; i < 16; i++)
+ sprintf (print_sense + strlen (print_sense), "%02x ", sense_buffer[i]);
+ DBG (DBG_sense, "sense_handler: sense=%s\n", print_sense);
+
+ if (ErrorCode != 0x70 && ErrorCode != 0x71)
+ {
+ DBG (DBG_error, "sense_handler: error code is invalid.\n");
+ return SANE_STATUS_IO_ERROR; /* error code is invalid */
+ }
+
+ skey = lookup_sensekey_errmsg (sense); /* simple sequential search */
+ DBG (DBG_sense, "sense_handler: sense_key=%#x '%s - %s'\n", skey->key,
+ skey->meaning, skey->description);
+
+ DBG (DBG_sense, "Looking up ascq=(%#x,%#x)=%#x\n", asc, ascq,
+ (asc << 8) | ascq);
+ ascq_key = lookup_ascq_errmsg ((asc << 8) | ascq); /* simple sequential search */
+ DBG (DBG_sense, "sense_handler: ascq=(%#x,%#x): %#x '%s'\n", asc, ascq,
+ ascq_key->codequalifier, ascq_key->description);
+
+ /* handle each sense key: Translate from HS2P message to SANE_STATUS_ message
+ * SANE_STATUS_GOOD, _ACCESS_DEINIED, _NO_MEM, _INVAL, _IO_ERROR, _DEVICE_BUSY,
+ * _EOF, _UNSUPPORTED, _CANCELLED, _JAMMED, _NO_DOCS, _COVER_OPEN
+ */
+ switch (sense)
+ {
+ case 0x00: /* no sense */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x01: /* recovered error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x02: /* not ready */
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0x03: /* medium error */
+ status = SANE_STATUS_JAMMED;
+ break;
+ case 0x04: /* hardware error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x05: /* illegal request */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x06: /* unit attention */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x07: /* data protect */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x08: /* blank check */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x09: /* vendor specific */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0A: /* copy aborted */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ case 0x0B: /* aborted command */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ case 0x0C: /* equal */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0D: /* volume overflow */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0E: /* miscompare */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0F: /* reserved */
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+ if (ErrorCode == 0x70) /* Additional Sense Codes available */
+ switch ((asc << 8) | ascq)
+ {
+ case 0x0000: /* No additional Information */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x0002: /* End of Medium */
+ status = SANE_STATUS_NO_DOCS;
+ break;
+ case 0x0005: /* End of Data */
+ status = SANE_STATUS_EOF;
+ break;
+ case 0x0400: /* LUN not ready */
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0x0401: /* LUN becoming ready */
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case 0x0403: /* LUN not ready. Manual intervention needed */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x0500: /* LUN doesn't respond to selection */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x0700: /* Multiple peripheral devices selected */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x1100: /* Unrecovered read error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x1101: /* Read retries exhausted */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x1501: /* Mechanical positioning error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x1A00: /* Parameter list length error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2000: /* Invalid command operation code */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2400: /* Invalid field in CDB (check field pointer) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2500: /* LUN not supported */
+ status = SANE_STATUS_UNSUPPORTED;
+ break;
+ case 0x2600: /* Invalid field in parameter list (check field pointer) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2900: /* Power on, reset, or BUS DEVICE RESET occurred */
+ status = SANE_STATUS_GOOD;
+ break;
+ case 0x2A01: /* (MODE parameter changed) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2C00: /* Command sequence error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2C01: /* Too many windows specified */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x2C02: /* Invalid combination of windows specified */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x3700: /* (Rounded paramter) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x3900: /* (Saving parameters not supported) */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x3A00: /* Medium not present */
+ status = SANE_STATUS_NO_DOCS;
+ break;
+ case 0x3B09: /* Read past end of medium */
+ status = SANE_STATUS_EOF;
+ break;
+ case 0x3B0B: /* Position past end of medium */
+ status = SANE_STATUS_EOF;
+ break;
+ case 0x3D00: /* Invalid bits in IDENTIFY message */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x4300: /* Message error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x4500: /* Select/Reselect failure */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4700: /* (SCSI parity error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4800: /* Initiator detected error message received */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x4900: /* Invalid message error */
+ status = SANE_STATUS_INVAL;
+ break;
+ case 0x4B00: /* Data phase error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x5300: /* (Media Load/Eject failed) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6000: /* Lamp failure */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6001: /* Shading error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6002: /* White adjustment error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6010: /* Reverse Side Lamp Failure */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6200: /* Scan head positioning error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x6300: /* Document Waiting Cancel */
+ status = SANE_STATUS_CANCELLED;
+ break;
+ case 0x8000: /* (PSU over heate) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8001: /* (PSU 24V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8002: /* (ADF 24V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8003: /* (5V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8004: /* (-12V fuse down) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8100: /* (ADF 24V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8102: /* (Base 12V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8103: /* Lamp cover open (Lamp 24V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8104: /* (-12V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8105: /* (Endorser 6V power off) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8106: /* SCU 3.3V power down error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8107: /* RCU 3.3V power down error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8108: /* OIPU 3.3V power down error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8200: /* Memory Error (Bus error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8210: /* Reverse-side memory error (Bus error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8300: /* (Image data processing LSI error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8301: /* (Interface LSI error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8302: /* (SCSI controller error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8303: /* (Compression unit error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8304: /* (Marker detect unit error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8400: /* Endorser error */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8500: /* (Origin Positioning error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8600: /* Mechanical Time Out error (Pick Up Roller error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8700: /* (Heater error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8800: /* (Thermistor error) */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ case 0x8900: /* ADF cover open */
+ status = SANE_STATUS_COVER_OPEN;
+ break;
+ case 0x8901: /* (ADF lift up) */
+ status = SANE_STATUS_COVER_OPEN;
+ break;
+ case 0x8902: /* Document jam error for ADF */
+ status = SANE_STATUS_JAMMED;
+ break;
+ case 0x8903: /* Document misfeed for ADF */
+ status = SANE_STATUS_JAMMED;
+ break;
+ case 0x8A00: /* (Interlock open) */
+ status = SANE_STATUS_COVER_OPEN;
+ break;
+ case 0x8B00: /* (Not enough memory) */
+ status = SANE_STATUS_NO_MEM;
+ break;
+ case 0x8C00: /* Size Detection failed */
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ default: /* Should never get here */
+ status = SANE_STATUS_INVAL;
+ DBG (DBG_sense,
+ "sense_handler: 'Undocumented code': ascq=(%#x,%#x)\n",
+ asc & 0xFF00, ascq & 0x00FF);
+ break;
+ }
+
+
+ DBG (DBG_proc, "sense_handler %s: '%s'-'%s' '%s' return:%d\n", sense_str,
+ skey->meaning, skey->description, ascq_key->description, status);
+ return status;
+}
+
+/* VPD IDENTIFIER Page Code 0x00
+ * A list of all Page Codes supported by scanner is returned as data
+ * Byte0 => bit7-5: Peripheral Qualifier, bits4-0: Peripheral Device Type
+ * Byte1 => Page Code of CDB is set as Page Code 0
+ * Byte2 => Reserved
+ * Byte3 => Page Length is 2 because scanner supports just two page codes: C0H and F0H
+ * Byte4 => First Support Page Code
+ * Byte5 => Second Support Page Code
+*/
+#if 0
+static SANE_Status
+vpd_indentifier_00H (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> vpd_identifier_00H\n");
+
+ cmd[0] = HS2P_SCSI_REQUEST_SENSE;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< vpd_identifier_00H\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+vpd_identifier_C0H (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> vpd_identifier_C0H\n");
+
+ cmd[0] = HS2P_SCSI_REQUEST_SENSE;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< vpd_identifier_C0H\n");
+ return (status);
+}
+#endif
+
+/* 1-3-3 INQUIRY : 6 bytes:
+ * Byte0 => 0x12
+ * Byte1 => bits7-5: Logical Unit number
+ * bits4-1: Reserved
+ * bit0: EVPD
+ * Byte2 => Page Code
+ * Byte3 => Reserved
+ * Byte4 => Allocation Length
+ * Byte5 => bits7-6: Vendor Unique
+ * bits5-2: Reserved
+ * bit1: Flag
+ * bit0: Link
+*/
+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 (DBG_proc, ">> inquiry\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = HS2P_SCSI_INQUIRY;
+ cmd[1] = evpd;
+ cmd[2] = page_code;
+/*cmd[3] Reserved */
+ cmd[4] = *buf_size;
+/*cmd[5] vendorunique+reserved+flag+link */
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< inquiry\n");
+ return (status);
+}
+
+/* 1-3-6 MODE SELECT -- sets various operation mode parameters for scanner */
+static SANE_Status
+mode_select (int fd, MP * settings)
+{
+ static struct
+ {
+ SELECT cmd; /* Mode page Select command */
+ MP mp; /* Hdr + Parameters */
+ } msc; /* Mode Select Command */
+ SANE_Status status;
+ size_t npages;
+
+ DBG (DBG_proc, ">> mode_select\n");
+
+ memset (&msc, 0, sizeof (msc)); /* Fill struct with zeros */
+ msc.cmd.opcode = HS2P_SCSI_MODE_SELECT; /* choose Mode Select Command */
+ msc.cmd.byte1 &= ~SMS_SP; /* unset bit0 SavePage to 0 */
+ msc.cmd.byte1 |= SMS_PF; /* set bit4 PageFormat to 1 */
+ npages = (settings->page.code == 2) ? 16 : 8;
+ msc.cmd.len = sizeof (msc.mp.hdr) + npages; /* either 4+8 or 4+20 */
+
+ memcpy (&msc.mp, settings, msc.cmd.len); /* Copy hdr+pages from Settings to msc.mp */
+ memset (&msc.mp.hdr, 0, sizeof (msc.mp.hdr)); /* make sure the hdr is all zeros */
+ /*
+ msc.hdr.data_len = 0x00;
+ msc.hdr.medium_type = 0x00;
+ msc.hdr.dev_spec = 0x00;
+ msc.hdr.blk_desc_len = 0x00;
+ */
+
+ /* Now execute the whole command */
+ if ((status =
+ sanei_scsi_cmd (fd, &msc, sizeof (msc.cmd) + msc.cmd.len, 0,
+ 0)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "ERROR: mode_select: %s\n", sane_strstatus (status));
+ DBG (DBG_error, "PRINTING CMD BLOCK:\n");
+ print_bytes (&msc.cmd, sizeof (msc.cmd));
+ DBG (DBG_error, "PRINTING MP HEADER:\n");
+ print_bytes (&msc.mp.hdr, sizeof (msc.mp.hdr));
+ DBG (DBG_error, "PRINTING MP PAGES:\n");
+ print_bytes (&msc.mp.page, msc.cmd.len);
+ }
+
+ DBG (DBG_proc, "<< mode_select\n");
+ return (status);
+}
+
+/* 1-3-7 MODE SENSE -- gets various operation mode parameters from scanner */
+static SANE_Status
+mode_sense (int fd, MP * buf, SANE_Byte page_code)
+{
+ SANE_Status status;
+ SENSE cmd; /* 6byte cmd */
+ MP msp; /* Mode Sense Page
+ * 4byte hdr + {2bytes +14 bytes}
+ * buffer to hold mode sense data gotten from scanner */
+
+ size_t nbytes;
+
+ DBG (DBG_proc, ">>>>> mode_sense: fd=%d, page_code=%#02x\n", fd, page_code);
+ nbytes = sizeof (msp);
+
+ DBG (DBG_info,
+ ">>>>> mode_sense: Zero'ing ModeSenseCommand msc and msp structures\n");
+
+ memset (&cmd, 0, sizeof (cmd)); /* Fill cmd struct with zeros */
+ memset (&msp, 0, sizeof (msp)); /* Fill msp struct with zeros */
+
+ /* set up Mode Sense Command */
+ DBG (DBG_info, ">>>>> mode_sense: Initializing Mode Sense cmd\n");
+ cmd.opcode = HS2P_SCSI_MODE_SENSE;
+ cmd.dbd &= ~(1 << 3); /* Disable Block Description (bit3) is set to 0 */
+ cmd.pc = (page_code & 0x3F); /* bits 5-0 */
+ cmd.pc &= ~(0x03 << 6); /* unset PC Field (bits7-6)
+ * 00 Curent Value is the only effective value
+ * 01 Changeable Value
+ * 10 Default Value
+ * 11 Saved Value */
+ /* cmd.len = ??? Allocation Length */
+
+ /* Now execute the whole command and store results in msc */
+ DBG (DBG_info, ">>>>> mode_sense: sanei_scsi_cmd\n");
+ DBG (DBG_info, ">>>>> cmd.opcode=%#0x cmd.dbd=%#02x, cmd.pc=%#02x\n",
+ cmd.opcode, cmd.dbd, cmd.pc);
+
+ nbytes = (page_code == 2) ? 20 : 12;
+ DBG (DBG_info,
+ ">>>>> sizeof(cmd)=%lu sizeof(msp)=%lu sizeof(hdr)=%lu sizeof(page)=%lu requesting %lu bytes\n",
+ (u_long) sizeof (cmd), (u_long) sizeof (msp),
+ (u_long) sizeof (msp.hdr), (u_long) sizeof (msp.page),
+ (u_long) nbytes);
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &msp, &nbytes);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "ERROR mode_sense: sanei_scsi_cmd error \"%s\"\n",
+ sane_strstatus (status));
+ DBG (DBG_error,
+ ">>>>> mode sense: number of bytes received from scanner: %lu\n",
+ (u_long) nbytes);
+ DBG (DBG_error, "PRINTING CMD BLOCK:\n");
+ print_bytes (&cmd, sizeof (cmd));
+ DBG (DBG_error, "PRINTING MP HEADER:\n");
+ print_bytes (&msp.hdr, sizeof (msp.hdr));
+ DBG (DBG_error, "PRINTING MP PAGES:\n");
+ print_bytes (&msp.page, sizeof (msp.page));
+ }
+ else
+ {
+ /* nbytes = (page_code==2)? 14 : 6; */
+ DBG (DBG_info, ">> >> got %lu bytes from scanner\n", (u_long) nbytes);
+ nbytes -= 4; /* we won't copy 4 byte hdr */
+ DBG (DBG_info, ">>>>> copying from msp to calling function's buf\n"
+ ">>>>> msp.page_size=%lu bytes=%lu buf_size=%lu\n",
+ (u_long) sizeof (msp.page), (u_long) nbytes,
+ (u_long) sizeof (*buf));
+ memcpy (buf, &(msp.page), nbytes);
+ }
+
+ DBG (DBG_proc, "<<<<< mode_sense\n");
+ return (status);
+}
+
+static SANE_Status
+set_window (int fd, SWD * swd)
+{
+ static struct
+ {
+ struct set_window_cmd cmd;
+ struct set_window_data swd;
+ } win;
+ SANE_Status status;
+ static size_t wdl, tl; /*window descriptor length, transfer length */
+ DBG (DBG_proc, ">> set_window\n");
+
+ /* initialize our struct with zeros */
+ memset (&win, 0, sizeof (win));
+
+ /* fill in struct with opcode */
+ win.cmd.opcode = HS2P_SCSI_SET_WINDOW;
+
+ /* bytes 1-5 are reserved */
+
+ /* Transfer length is header + window data */
+ tl = sizeof (*swd);
+ _lto3b (tl, &win.cmd.len[0]); /* 8 + (2*320) = 648 */
+ DBG (DBG_info,
+ "set_window: SET WINDOW COMMAND Transfer Length = %lu (should be 648)\n",
+ (unsigned long) tl);
+
+ /* Copy data from swd (including 8-byte header) to win.swd */
+ DBG (DBG_info,
+ "set_window: COPYING %lu bytes from settings to Set Window Command (%lu)\n",
+ (u_long) sizeof (*swd), (u_long) sizeof (win.swd));
+ if (!memcpy (&(win.swd), swd, sizeof (*swd)))
+ {
+ DBG (DBG_error, "set_window: error with memcpy\n");
+ }
+
+ /* Set Window Data Header: 0-5:reserved; 6-7:Window Descriptor Lenght=640 */
+ wdl = sizeof (win.swd) - sizeof (win.swd.hdr);
+ _lto2b (wdl, &win.swd.hdr.len[0]);
+ DBG (DBG_info,
+ "set_window: SET WINDOW COMMAND Window Descriptor Length = %lu (should be 640)\n",
+ (unsigned long) wdl);
+
+ /* Now execute command */
+ DBG (DBG_info,
+ "set_window: calling sanei_scsi_cmd(%d,&win,%lu, NULL, NULL)\n", fd,
+ (u_long) sizeof (win));
+ status = sanei_scsi_cmd (fd, &win, sizeof (win), NULL, NULL);
+ /*
+ status = sanei_scsi_cmd2 (fd, &win.cmd, sizeof(win.cmd), &win.swd, sizeof(win.swd), NULL, NULL);
+ */
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "*********************\n");
+ DBG (DBG_error, "ERROR: set_window: %s\n", sane_strstatus (status));
+ DBG (DBG_error, "PRINTING SWD CMD BLK:\n");
+ print_bytes (&win.cmd, sizeof (win.cmd));
+ DBG (DBG_error, "PRINTING SWD HEADER:\n");
+ print_bytes (&win.swd.hdr, sizeof (win.swd.hdr));
+ DBG (DBG_error, "PRINTING SWD DATA[0]:\n");
+ print_bytes (&win.swd.data[0], sizeof (win.swd.data[0]));
+ DBG (DBG_error, "PRINTING SWD DATA[1]:\n");
+ print_bytes (&win.swd.data[1], sizeof (win.swd.data[1]));
+ DBG (DBG_error, "*********************\n");
+ }
+
+ DBG (DBG_proc, "<< set_window\n");
+ return (status);
+}
+
+static SANE_Status
+get_window (int fd, GWD * gwd)
+{
+ struct get_window_cmd cmd;
+ SANE_Status status;
+ static size_t gwd_size;
+
+ DBG (DBG_proc, ">> get_window\n");
+
+ gwd_size = sizeof (*gwd);
+ DBG (DBG_info, ">> get_window datalen = %lu\n", (unsigned long) gwd_size);
+
+ /* fill in get_window_cmd */
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR cmd */
+ cmd.opcode = HS2P_SCSI_GET_WINDOW;
+ cmd.byte1 &= ~0x01; /* unset single bit 0 */
+ cmd.win_id = 0x00; /* either 0 or 1 */
+ _lto3b (gwd_size, cmd.len); /* Transfer Length is byte length of DATA to be returned */
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), gwd, &gwd_size);
+
+ DBG (DBG_proc, "<< get_window, datalen = %lu\n", (unsigned long) gwd_size);
+ return (status);
+}
+static void
+print_window_data (SWD * buf)
+{
+ int i, j, k;
+ struct hs2p_window_data *data;
+ struct window_section *ws;
+
+ DBG (DBG_proc, ">> print_window_data\n");
+ DBG (DBG_info, "HEADER\n");
+ for (i = 0; i < 6; i++)
+ DBG (DBG_info, "%#02x\n", buf->hdr.reserved[i]);
+ DBG (DBG_info, "Window Descriptor Length=%lu\n\n", _2btol (buf->hdr.len));
+
+ for (i = 0; i < 2; i++)
+ {
+ data = &buf->data[i];
+ DBG (DBG_info, "Window Identifier = %d\n", data->window_id);
+ DBG (DBG_info, "AutoBit = %#x\n", data->auto_bit);
+ DBG (DBG_info, "X-Axis Resolution = %lu\n", _2btol (data->xres));
+ DBG (DBG_info, "Y-Axis Resolution = %lu\n", _2btol (data->yres));
+ DBG (DBG_info, "X-Axis Upper Left = %lu\n", _4btol (data->ulx));
+ DBG (DBG_info, "Y-Axis Upper Left = %lu\n", _4btol (data->uly));
+ DBG (DBG_info, "Window Width = %lu\n", _4btol (data->width));
+ DBG (DBG_info, "Window Length = %lu\n", _4btol (data->length));
+ DBG (DBG_info, "Brightness = %d\n", data->brightness);
+ DBG (DBG_info, "Threshold = %d\n", data->threshold);
+ DBG (DBG_info, "Contrast = %d\n", data->contrast);
+ DBG (DBG_info, "Image Composition = %#0x\n", data->image_composition);
+ DBG (DBG_info, "Bits per Pixel = %d\n", data->bpp);
+ DBG (DBG_info, "Halftone Code = %#0x\n", data->halftone_code);
+ DBG (DBG_info, "Halftone Id = %#0x\n", data->halftone_id);
+ DBG (DBG_info, "Byte29 = %#0x RIF=%d PaddingType=%d\n", data->byte29,
+ data->byte29 & 0x80, data->byte29 & 0x7);
+ DBG (DBG_info, "Bit Ordering = %lu\n", _2btol (data->bit_ordering));
+ DBG (DBG_info, "Compression Type = %#x\n", data->compression_type);
+ DBG (DBG_info, "Compression Arg = %#x\n", data->compression_arg);
+ for (j = 0; j < 6; j++)
+ DBG (DBG_info, "Reserved=%#x\n", data->reserved2[j]);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored1);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored2);
+ DBG (DBG_info, "Byte42 = %#x MRIF=%d Filtering=%d GammaID=%d\n",
+ data->byte42, data->byte42 & 0x80, data->byte42 & 0x70,
+ data->byte42 & 0x0F);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored3);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored4);
+ DBG (DBG_info, "Binary Filtering = %#x\n", data->binary_filtering);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored5);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored6);
+ DBG (DBG_info, "Automatic Separation = %#x\n",
+ data->automatic_separation);
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored7);
+ DBG (DBG_info, "Automatic Binarization = %#x\n",
+ data->automatic_binarization);
+ for (j = 0; j < 13; j++)
+ DBG (DBG_info, "Ignored = %#x\n", data->ignored8[j]);
+
+ for (k = 0; k < 8; k++)
+ {
+ ws = &data->sec[k];
+ DBG (DBG_info, "\n\n");
+ DBG (DBG_info, "SECTION %d\n", k);
+ DBG (DBG_info, "Section Enable Flat (sef bit) = %#x\n", ws->sef);
+ DBG (DBG_info, "ignored = %d\n", ws->ignored0);
+ DBG (DBG_info, "Upper Left X = %lu\n", _4btol (ws->ulx));
+ DBG (DBG_info, "Upper Left Y = %lu\n", _4btol (ws->uly));
+ DBG (DBG_info, "Width = %lu\n", _4btol (ws->width));
+ DBG (DBG_info, "Length = %lu\n", _4btol (ws->length));
+ DBG (DBG_info, "Binary Filtering = %#x\n", ws->binary_filtering);
+ DBG (DBG_info, "ignored = %d\n", ws->ignored1);
+ DBG (DBG_info, "Threshold = %#x\n", ws->threshold);
+ DBG (DBG_info, "ignored = %d\n", ws->ignored2);
+ DBG (DBG_info, "Image Composition = %#x\n", ws->image_composition);
+ DBG (DBG_info, "Halftone Id = %#x\n", ws->halftone_id);
+ DBG (DBG_info, "Halftone Code = %#x\n", ws->halftone_code);
+ for (j = 0; j < 7; j++)
+ DBG (DBG_info, "ignored = %d\n", ws->ignored3[j]);
+ }
+ }
+ DBG (DBG_proc, "<< print_window_data\n");
+}
+
+static SANE_Status
+read_data (int fd, void *buf, size_t * buf_size, SANE_Byte dtc, u_long dtq)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> read_data buf_size=%lu dtc=0x%2.2x dtq=%lu\n",
+ (unsigned long) *buf_size, (int) dtc, dtq);
+ if (fd < 0)
+ {
+ DBG (DBG_error, "read_data: scanner is closed!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = dtc;
+ _lto2b (dtq, cmd.dtq);
+ _lto3b (*buf_size, cmd.len);
+
+ DBG (DBG_info, "read_data ready to send scsi cmd\n");
+ DBG (DBG_info, "opcode=0x%2.2x, dtc=0x%2.2x, dtq=%lu, transfer len =%d\n",
+ cmd.opcode, cmd.dtc, _2btol (cmd.dtq), _3btol (cmd.len));
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "read_data: %s\n", sane_strstatus (status));
+ DBG (DBG_proc, "<< read_data %lu\n", (unsigned long) *buf_size);
+ return (status);
+}
+
+#if 0
+static SANE_Status
+send_data (int fd, void *buf, size_t * buf_size)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> send_data %lu\n", (unsigned long) *buf_size);
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ memcpy (&cmd, buf, sizeof (*buf)); /* Fill in our struct with set values */
+ cmd.opcode = HS2P_SCSI_SEND_DATA;
+ _lto3b (*buf_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< send_data %lu\n", (unsigned long) *buf_size);
+ return (status);
+}
+#endif
+
+static SANE_Bool
+is_valid_endorser_character (char c)
+{
+ int i = (int) c;
+ /* 44 characters can be printed by endorser */
+
+ if (i >= 0x30 && i <= 0x3A)
+ return SANE_TRUE; /* 0123456789: */
+ if (i == 0x23)
+ return SANE_TRUE; /* # */
+ if (i == 0x27)
+ return SANE_TRUE; /* ` */
+ if (i >= 0x2C && i <= 0x2F)
+ return SANE_TRUE; /* '-./ */
+ if (i == 0x20)
+ return SANE_TRUE; /* space */
+ if (i >= 0x41 && i <= 0x5A)
+ return SANE_TRUE; /* ABCDEFGHIJKLMNOPQRSTUVWXYZ <spaces> */
+ if (i >= 0x61 && i <= 0x7A)
+ return SANE_TRUE; /* abcdefghijklmnopqrstuvwxyz <spaces> */
+
+ return SANE_FALSE;
+}
+
+static SANE_Status
+set_endorser_string (int fd, SANE_String s)
+{
+ struct
+ {
+ struct scsi_rs_scanner_cmd cmd;
+ SANE_Byte endorser[19];
+ } out;
+ char *t;
+ int i, len;
+
+ SANE_Status status;
+ DBG (DBG_proc, ">> set_endorser_string %s\n", s);
+
+ for (i = 0, t = s; *t != '\0' && i < 19; i++)
+ {
+ DBG (DBG_info, "CHAR=%c\n", *t);
+ if (!is_valid_endorser_character (*t++))
+ return SANE_STATUS_INVAL;
+ }
+ len = strlen (s);
+
+ memset (&out, 0, sizeof (out)); /* CLEAR */
+ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */
+ out.cmd.dtc = 0x80; /* Endorser Data */
+ _lto3b (len, &out.cmd.len[0]); /* 19 bytes max */
+ memset (&out.endorser[0], ' ', 19); /* fill with spaces */
+ memcpy (&out.endorser[0], s, len);
+
+ status = sanei_scsi_cmd (fd, &out, sizeof (out), NULL, NULL);
+
+
+ DBG (DBG_proc, "<< set_endorser_string s=\"%s\" len=%d\n", s, len);
+ return (status);
+}
+
+static SANE_Status
+hs2p_send_gamma (HS2P_Scanner * s)
+{
+ SANE_Status status;
+ struct
+ {
+ struct scsi_rs_scanner_cmd cmd;
+ SANE_Byte gamma[2 + GAMMA_LENGTH];
+ } out;
+ int i;
+ size_t len = sizeof (out.gamma);
+
+ DBG (DBG_proc, ">> teco_send_gamma\n");
+
+ memset (&out, 0, sizeof (out)); /* CLEAR */
+ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */
+ out.cmd.dtc = 0x03; /* Gamma Function Data */
+ _lto3b (len, &out.cmd.len[0]); /* 19 bytes max */
+ out.gamma[0] = 0x08; /* Gamma ID for Download table */
+ out.gamma[1] = 0x08; /* The Number of gray scale (M) = 8 */
+ for (i = 2; i < 2 + GAMMA_LENGTH; i++)
+ {
+ out.gamma[i] = s->gamma_table[i];
+ }
+ status = sanei_scsi_cmd (s->fd, &out, sizeof (out), NULL, NULL);
+
+ DBG (DBG_proc, "<< teco_send_gamma\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+clear_maintenance_data (int fd, int code, char XorY, int number)
+{
+ struct
+ {
+ struct scsi_rs_scanner_cmd cmd;
+ char string[20];
+ } out;
+
+ SANE_Status status;
+ DBG (DBG_proc, ">> set_maintenance data\n");
+
+ memset (&out, 0, sizeof (out)); /* CLEAR */
+ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */
+ out.cmd.dtc = 0x85; /* Maintenance Data */
+ _lto3b (20, out.cmd.len); /* 20 bytes */
+ switch (code)
+ {
+ case 1:
+ strcpy (out.string, "EEPROM ALL ALL RESET");
+ break;
+ case 2:
+ strcpy (out.string, "EEPROM ALL RESET");
+ break;
+ case 3:
+ strcpy (out.string, "ADF RESET");
+ break;
+ case 4:
+ strcpy (out.string, "FLATBED RESET");
+ break;
+ case 5:
+ strcpy (out.string, "LAMP RESET");
+ break;
+ case 6:
+ sprintf (out.string, "EEPROM ADF %c %+4.1d", XorY, number);
+ break;
+ case 7:
+ sprintf (out.string, "EEPROM BOOK %c %4.1d", XorY, number);
+ break;
+ case 8:
+ sprintf (out.string, "WHITE ADJUST DATA %3d", number);
+ break;
+ case 9:
+ strcpy (out.string, "EEPROM FIRST WHITE ODD");
+ break;
+ case 10:
+ strcpy (out.string, "EEPROM FIRST WHITE EVEN");
+ break;
+ case 11:
+ strcpy (out.string, "R ADF RESET");
+ break;
+ case 12:
+ strcpy (out.string, "R LAMP RESET");
+ break;
+ case 13:
+ sprintf (out.string, "EEPROM R ADF %c %4.1d", XorY, number);
+ break;
+ case 14:
+ strcpy (out.string, "ENDORSER RESET");
+ break;
+ }
+ status = sanei_scsi_cmd (fd, &out, sizeof (out), NULL, NULL);
+
+ DBG (DBG_proc, "<< set_maintenance data\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+read_halftone_mask (int fd, SANE_Byte halftone_id, void *buf,
+ size_t * buf_size)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ SANE_Int len;
+ DBG (DBG_proc, ">> read_halftone_mask\n");
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = DATA_TYPE_HALFTONE;
+ _lto2b (halftone_id, cmd.dtq);
+
+ /* Each cell of an NxM dither pattern is 1 byte from the set {2,3,4,6,8,16} */
+ switch (halftone_id)
+ {
+ case 0x01:
+ len = 32;
+ break; /* 8x4, 45 degree */
+ case 0x02:
+ len = 36;
+ break; /* 6x6, spiral */
+ case 0x03:
+ len = 16;
+ break; /* 4x4, spiral */
+ case 0x04:
+ len = 64;
+ break; /* 8x8, 90 degree */
+ case 0x05:
+ len = 70;
+ break; /* 70 lines */
+ case 0x06:
+ len = 95;
+ break; /* 95 lines */
+ case 0x07:
+ len = 180;
+ break; /* 180 lines */
+ case 0x08:
+ len = 128;
+ break; /* 16x8, 45 degree */
+ case 0x09:
+ len = 256;
+ break; /* 16x16, 90 degree */
+ case 0x0A:
+ len = 64;
+ break; /* 8x8, Bayer */
+ default:
+ return SANE_STATUS_INVAL; /* Reserved */
+ }
+
+ _lto3b (len, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< read_halftone_mask\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+set_halftone_mask (int fd, SANE_Byte halftone_id, void *buf,
+ size_t * buf_size)
+{
+ static struct scsi_rs_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> set_halftone_mask\n");
+
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR */
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = DATA_TYPE_HALFTONE;
+ _lto2b (halftone_id, cmd.dtq);
+
+ /* Each cell of an NxM dither pattern is 1 byte from the set {2,3,4,6,8,16}
+ * 0x80, 0x81 are User definable custom dither patterns
+ */
+ if (halftone_id != 0x80 && halftone_id != 0x81)
+ return SANE_STATUS_INVAL;
+
+ _lto3b (*buf_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (DBG_proc, "<< set_halftone_mask\n");
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+read_gamma_function (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+
+static SANE_Status
+read_endorser_data (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+
+static SANE_Status
+read_size_data (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+#endif
+
+#if 0
+static SANE_Status
+read_maintenance_data (int fd)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ return (status);
+}
+#endif
+
+/* Bit0: is 0 if document on ADF; else 1
+ * Bit1: is 0 if ADF cover is closed; else 1
+ * Bit2: reserved
+ * Bits7-3: reserved
+*/
+
+
+#if 0
+static SANE_Status
+read_adf_status (int fd, SANE_Byte * adf_status_byte)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct scsi_rs_scanner_cmd cmd;
+ static size_t len = 1;
+
+ DBG (DBG_proc, ">> read_adf_status\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = HS2P_SCSI_READ_DATA;
+ cmd.dtc = DATA_TYPE_ADF_STATUS;
+ _lto3b (0x01, cmd.len); /* convert 0x01 into 3-byte Transfer Length */
+ if ((status =
+ sanei_scsi_cmd (fd, &cmd, sizeof (cmd), adf_status_byte,
+ &len)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "read_adf_status ERROR: %s\n", sane_strstatus (status));
+ }
+ DBG (DBG_proc, "<< read_adf_status\n");
+ return (status);
+}
+#endif
+
+/*
+ * read_ipu_photoletter_parameters
+ * read_ipu_threshold_parameters
+ * read_sensor_data (WHAT DATA TYPE CODE?)
+*/
+/* SEND CMD */
+/*
+ * send_halftone_mask
+ * send_gamma_function
+ * send_endorser_data
+ * send_maintenance_data
+ * EPROM All Clear
+ * EPROM Counter Clear
+ * ADF Counter Clear
+ * Flatbed Counter Clear
+ * Lamp Counter Clear
+ * ADF Register Data
+ * Flatbed Register Data
+ * White Adjustment Data
+ * White level first Data (ODD)
+ * White level first Data (EVEN)
+ * Reverse side ADF Counter Clear
+ * Reverse side Lamp Counter Clear
+ * Reverse side ADF Register Data
+ * Endorser Character Counter Clear
+ * send_ipu_parameters
+*/
+
+/* OBJECT POSITION */
+/* GET DATA BUFFER STATUS */
+
+/* 1-3-4 MODE SELECT */
+
+/* 1-3-5 Reserve Unit: 0x16
+ * 1-3-6 Release Unit: 0x17
+*/
+static SANE_Status
+unit_cmd (int fd, SANE_Byte opcode)
+{
+ static struct
+ {
+ SANE_Byte opcode; /* 16H: Reserve Unit 17H: Release Unit */
+ SANE_Byte byte1; /* 7-5: LUN; 4: 3rd Party; 3-1: 3rd Party Device; 0: Reserved */
+ SANE_Byte reserved[3];
+ SANE_Byte control; /* 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link */
+ } cmd;
+
+ SANE_Byte LUN = (0x00 & 0x07) << 5;
+ SANE_Status status;
+ DBG (DBG_proc, ">> unit_cmd\n");
+
+ cmd.opcode = opcode;
+ cmd.byte1 = LUN & 0xE1; /* Mask=11100001 3rd Party and 3rd Party Device must be 0 */
+ memset (&cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< unit_cmd\n");
+ return (status);
+
+}
+
+/* The OBJECT POSITION command is used for carriage control or
+ * document feed and eject with ADF
+ *
+ * Position Function: Byte1 bits2-0
+ * 000 Unload instructs document eject
+ * 001 Load instructs document feed to scan start position
+ * 010 Absolute Positioning - instructs carriage to move to carriage lock position
+ * The carriage moves in the Y-axis direction as the amount set in Count when
+ * count>0
+ * (Not supported in IS420)
+ *
+*/
+static SANE_Status
+object_position (int fd, int load)
+{
+ static struct scsi_object_position_cmd cmd;
+ SANE_Status status;
+ DBG (DBG_proc, ">> object_position\n");
+
+ /* byte 0 opcode
+ * byte 1 position function
+ * bytes 2-4 reserved
+ * bytes 5-8 reserved
+ * byte 9 control
+ */
+
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = HS2P_SCSI_OBJECT_POSITION;
+ if (load)
+ cmd.position_func = OBJECT_POSITION_LOAD;
+ else
+ cmd.position_func = OBJECT_POSITION_UNLOAD;
+
+
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< object_position\n");
+ return (status);
+}
+
+static SANE_Status
+get_data_status (int fd, STATUS_DATA * dbs)
+{
+ static GET_DBS_CMD cmd;
+ static STATUS_BUFFER buf; /* hdr + data */
+ size_t bufsize = sizeof (buf);
+ SANE_Status status;
+ DBG (DBG_proc, ">> get_data_status %lu\n", (unsigned long) bufsize);
+
+ /* Set up GET DATA BUFFER STATUS cmd */
+ memset (&cmd, 0, sizeof (cmd)); /* CLEAR cmd */
+ cmd.opcode = HS2P_SCSI_GET_BUFFER_STATUS;
+ cmd.wait &= ~0x01; /* unset Wait bit0 */
+ _lto2b (bufsize, cmd.len);
+
+ /* Now execute cmd, and put returned results in buf */
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &buf, &bufsize);
+
+ /* Now copy from buf.data to dbs */
+ memcpy (dbs, &buf.data, sizeof (*dbs));
+
+ if (status == SANE_STATUS_GOOD &&
+ ((unsigned int) _3btol (buf.hdr.len) <= sizeof (*dbs)
+ || _3btol (buf.data.filled) == 0))
+ {
+ DBG (DBG_info, "get_data_status: busy\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ DBG (DBG_proc, "<< get_data_status %lu\n", (unsigned long) bufsize);
+ return (status);
+}
+
+/* 1-3-7 MODE SENSE */
+/* 1-3-8 SCAN */
+
+/* 1-3-9 Receive Diagnostic
+ * Byte0: 1CH
+ * Byte1: 7-5 LUN; 4-0: reserved
+ * Byte2: Reserved
+ * Byte3-4: Allocation Length
+ * Byte5: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link
+ *
+ * This command is treated as a dummy command
+ * Return GOOD unless there is an error in command in which case it returns CHECK
+*/
+
+/*
+* The IS450 performs 7 self-diagnostics tests
+* 1) Home position error check
+* 2) Exposure lamp error check
+* 3) White level error check
+* 4) Document table error check
+* 5) SCU error check
+* 6) RCU error check
+* 7) Memory error check
+*
+* and uses the lights on the scanner to indicate the result
+*
+* PowerOn MachineBusy DocumentInPlace Error
+* (green) (green) (green) (red)
+*
+* SCU error check Blinking Blinking
+* RCU error check Blinking On Blinking
+* Home position error check Blinking Blinking Blinking On
+* Exposure lamp error check Blinking Blinking On On
+* White level error check Blinking Blinking
+* Memory Error (Simplex) Blinking
+* Memory Error (Duplex) Blinking
+*
+*/
+#if 0
+static SANE_Status
+receive_diagnostic (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> receive_diagnostic\n");
+
+ cmd[0] = HS2P_SCSI_RECEIVE_DIAGNOSTICS;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< receive_diagnostic\n");
+ return (status);
+}
+#endif
+
+/* 1-3-10 Send Diagnostic
+ * Byte0: 1DH
+ * Byte1: 7-5 LUN; 4: PF; 3: Reserved; 2: S-Test; 1: DevOfl; 0: U-Ofl
+ * Byte2: Reserved
+ * Byte3-4: Parameter List Length
+ * Byte5: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link
+ * This command executes self-diagnostic and optical-adjustment
+ * PF, DevOfl, and Parameter List Length must be 0 or CHECK condition is returned.
+*/
+#if 0
+static SANE_Status
+send_diagnostic (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (DBG_proc, ">> send_diagnostic\n");
+
+ cmd[0] = HS2P_SCSI_SEND_DIAGNOSTICS;
+ cmd[1] = 0x00 & (1 << 2) & 0xED; /* Set Self-Test bit and clear PF, DevOfl bits */
+ cmd[3] = 0x00;
+ cmd[4] = 0x00; /* Parameter list (bytes3-4) must be 0x00 */
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (DBG_proc, "<< send_diagnostic\n");
+ return (status);
+}
+#endif
+
+
+/* 1-3-8 SCAN command is used to instruct scanner to start scanning */
+static SANE_Status
+trigger_scan (HS2P_Scanner * s)
+{
+ static struct
+ {
+ START_SCAN cmd;
+ SANE_Byte wid[2]; /* scanner supports up to 2 windows */
+ } scan;
+ SANE_Status status;
+ DBG (DBG_proc, ">> trigger scan\n");
+
+ memset (&scan, 0, sizeof (scan)); /* CLEAR scan */
+ scan.cmd.opcode = HS2P_SCSI_START_SCAN;
+ /* Transfer length is the byte length of Window List transferred
+ * Window List is a list of Window Identifier created by SET WINDOW command
+ * Since only 1 Window is supported by SCAN command, 0 or 1 is used for Window Identifier
+ * and 1 or 2 for length
+ status = sanei_scsi_cmd (s->fd, &trigger, sizeof (trigger), &window_id_list[0], &wl_size);
+ */
+ scan.cmd.len = (s->val[OPT_DUPLEX].w == SANE_TRUE) ? 2 : 1;
+
+ DBG (DBG_info, "trigger_scan: sending %d Window Id to scanner\n",
+ scan.cmd.len);
+ status =
+ sanei_scsi_cmd (s->fd, &scan, sizeof (scan.cmd) + scan.cmd.len, NULL,
+ NULL);
+
+ DBG (DBG_proc, "<< trigger scan\n");
+ return (status);
+}
+
+#define MAX_WAITING_TIME 15
+static SANE_Status
+hs2p_wait_ready (HS2P_Scanner * s)
+{
+ STATUS_DATA dbs; /* Status Buffer Status DATA */
+ time_t now, start;
+ SANE_Status status;
+
+ start = time (NULL);
+
+ while (1)
+ {
+ status = get_data_status (s->fd, &dbs);
+
+ switch (status)
+ {
+ default:
+ /* Ignore errors while waiting for scanner to become ready.
+ Some SCSI drivers return EIO while the scanner is
+ returning to the home position. */
+ DBG (DBG_error, "scsi_wait_ready: get datat status failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ now = time (NULL);
+ if (now - start >= MAX_WAITING_TIME)
+ {
+ DBG (DBG_error,
+ "hs2p_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now - start));
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_STATUS_GOOD:
+ DBG (DBG_proc, "hs2p_wait_ready: %d bytes ready\n",
+ _3btol (dbs.filled));
+ return status;
+ break;
+ }
+ usleep (1000000); /* retry after 100ms */
+ }
+ return SANE_STATUS_INVAL;
+}
+
+
+/* MODE PAGES GET/SET */
+static SANE_Status
+connection_parameters (int fd, MP_CXN * settings, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_CXN buf;
+ size_t nbytes;
+ DBG (DBG_proc, ">> connection_parameters\n");
+ nbytes = sizeof (buf);
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET connection_parameters >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_CONNECTION);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_connection_parameters: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ memcpy (settings, &buf, nbytes);
+ }
+ else
+ { /* SET */
+ DBG (DBG_info, ">> SET connection_parameters >> calling mode_select\n");
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ memcpy (&buf, settings, nbytes);
+ /* Make sure calling function didn't change these bytes */
+ memset (&buf.hdr, 0, sizeof (buf.hdr)); /* Make sure 4bytes are 0 */
+ buf.code = PAGE_CODE_CONNECTION; /* bits5-0: Page Code 02H */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x0E; /* This is the only page with 14 bytes */
+
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_connection_parameters: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ DBG (DBG_proc, "<< connection_parameters\n");
+ return (status);
+}
+
+static SANE_Status
+get_basic_measurement_unit (int fd, SANE_Int * bmu, SANE_Int * mud)
+{
+ SANE_Status status;
+ MP_SMU buf;
+
+ DBG (DBG_proc, ">> get_basic_measurement_unit: fd=\"%d\"\n", fd);
+
+ status =
+ mode_sense (fd, (MP *) & buf,
+ (SANE_Byte) PAGE_CODE_SCANNING_MEASUREMENTS);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_basic_measurement_unit: MODE_SELECT failed with status=%d\n",
+ status);
+ return (SANE_STATUS_INVAL);
+ }
+ *bmu = buf.bmu;
+ *mud = ((buf.mud[0] << 8) | buf.mud[1]);
+
+ DBG (DBG_proc, "<< get_basic_measurement_unit: bmu=%d mud=%d\n", *bmu,
+ *mud);
+ return (status);
+}
+
+static SANE_Status
+set_basic_measurement_unit (int fd, SANE_Byte bmu)
+{
+ MP_SMU buf; /* Mode Page Scanning Measurements Page Code */
+ SANE_Status status;
+ SANE_Int mud;
+ size_t bufsize = sizeof (buf);
+
+ DBG (DBG_proc, ">> set_basic_measurement_unit: %d\n", bmu);
+
+ /* Set up buf */
+ memset (&buf, 0, bufsize); /* CLEAR buf */
+ buf.code = PAGE_CODE_SCANNING_MEASUREMENTS; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+
+ buf.bmu = bmu; /* Power on default is POINTS */
+ mud = (bmu == INCHES) ? DEFAULT_MUD : 1;
+ DBG (DBG_info, "SET_BASIC_MEASUREMENT_UNIT: bmu=%d mud=%d\n", bmu, mud);
+ _lto2b (mud, &buf.mud[0]); /* buf.mud[0] = (mud >> 8) & 0xff; buf.mud[1] = (mud & 0xff); */
+
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_basic_measurement_unit: MODE_SELECT failed with status=%d\n",
+ status);
+ status = SANE_STATUS_INVAL;
+ }
+
+ DBG (DBG_proc,
+ "<< set_basic_measurement_unit: opcode=%d len=%d bmu=%d mud=%ld\n",
+ buf.code, buf.len, buf.bmu, _2btol (&buf.mud[0]));
+ return (status);
+}
+
+static SANE_Status
+adf_control (int fd, SANE_Bool flag, SANE_Byte * adf_control,
+ SANE_Byte * adf_mode, SANE_Byte * mwt)
+{
+ SANE_Status status;
+ MP_ADF buf;
+ size_t bufsize = sizeof (buf);
+
+ DBG (DBG_proc, ">> adf_control\n");
+
+ memset (&buf, 0, bufsize); /* Fill struct with zeros */
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET ADF_control>> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_ADF_CONTROL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_adf_control: MODE_SELECT failed\n");
+ return (status);
+ }
+ *adf_control = buf.adf_control;
+ *adf_mode = buf.adf_mode_control;
+ *mwt = buf.medium_wait_timer;
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ buf.code = PAGE_CODE_ADF_CONTROL; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ /* Byte2: adf_control: 7-2:reserved; 1-0:adf_control: Default 00H Flatbed, 01H Simplex, 02H Duplex */
+ /* Byte3: adf_mode_control: 7-3:reserved; 2: Prefeed Mode: 0 invalid, 1 valid; 1-0: ignored */
+ /* Byte4: medium_wait_timer: timeout period. Not supported */
+ buf.adf_control = (*adf_control & 0x03);
+ buf.adf_mode_control = (*adf_mode & 0x04);
+ buf.medium_wait_timer = *mwt;
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_adf_control: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ }
+ DBG (DBG_proc, ">> adf_control\n");
+ return (status);
+}
+
+static SANE_Status
+white_balance (int fd, int *val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_WhiteBal buf; /* White Balance Page Code */
+ size_t bufsize = sizeof (buf);
+
+ memset (&buf, 0, bufsize);
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_proc, ">> GET white_balance>> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_WHITE_BALANCE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_white_balance: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ *val = buf.white_balance;
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_WHITE_BALANCE; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ buf.white_balance = *val; /* Power on default is RELATIVE_WHITE */
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_white_balance: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ }
+ DBG (DBG_proc, "<< white balance: buf.white_balance=%#02x\n",
+ buf.white_balance);
+ return (status);
+}
+
+#if 0
+static SANE_Int
+lamp_timer (int fd, int val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_LampTimer buf; /* Lamp Timer Page Code */
+
+ DBG (DBG_proc, ">> lamp timer\n");
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET lamp_timer>> calling mode_sense\n");
+ if ((status =
+ mode_sense (fd, (MP *) & buf,
+ (SANE_Byte) PAGE_CODE_LAMP_TIMER_SET)) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "get_lamp_timer: MODE_SELECT failed\n");
+ return (-1);
+ }
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_LAMP_TIMER_SET; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ buf.time_on = val; /* time lamp has been on */
+ if ((status = mode_select (fd, (MP *) & buf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_lamp_timer: MODE_SELECT failed with status=%d\n", status);
+ return (-1);
+ }
+ }
+ DBG (DBG_proc, "<< lamp timer\n");
+ return (buf.time_on);
+}
+#endif
+
+static SANE_Status
+endorser_control (int fd, int *val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_EndCtrl buf; /* MPHdr (4bytes) + MPP (8bytes) */
+ SANE_Byte mask = 0x7; /* 7-3:reserved; 2-0: Endorser Control */
+ size_t bufsize = sizeof (buf);
+
+ DBG (DBG_proc, ">> endorser_control: fd=%d val=%d flag=%d\n", fd, *val,
+ flag);
+
+ memset (&buf, 0, bufsize); /* Fill struct with zeros */
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET endorser control >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_ENDORSER_CONTROL);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_endorser_control: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ *val = buf.endorser_control & mask;
+ }
+ else
+ { /* SET */
+ DBG (DBG_info, ">> SET endorser control >> calling mode_select\n");
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_ENDORSER_CONTROL; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+
+ buf.endorser_control = *val & mask; /* Power on default is OFF */
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_endorser_control: MODE_SELECT failed with status=%d\n",
+ status);
+ return (status);
+ }
+ }
+ DBG (DBG_proc, "<< endorser_control: endorser_control=%#02x\n",
+ buf.endorser_control);
+ return (status);
+}
+
+/* When SCAN, READ, or LOAD (in ADF mode) is issued, scanner waits until operator panel start button is pressed */
+static SANE_Status
+scan_wait_mode (int fd, int val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_SWM buf; /* Scan Wait Mode Page Code */
+ DBG (DBG_proc, ">> scan_wait_mode\n");
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET scan_wait_mode >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_SCAN_WAIT_MODE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_scan_wait_mode: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_SCAN_WAIT_MODE; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ buf.swm = 0x00;
+ if (val == 1)
+ buf.swm |= 1; /* set bit 1 if scan_wait_mode ON */
+ else
+ buf.swm &= ~1; /* unset bit 1 if scan_wait_mode OFF */
+
+ DBG (DBG_info, ">> SET scan_wait_mode >> calling mode_sense\n");
+ if ((status = mode_select (fd, (MP *) & buf)) != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "mode_select ERROR %s\n", sane_strstatus (status));
+ }
+ }
+ DBG (DBG_proc, "<< scan_wait_mode: buf.swm=%#02x\n", buf.swm);
+ return (status);
+}
+
+/* Selectable when Send Diagnostics command is performed */
+static SANE_Int
+service_mode (int fd, int val, SANE_Bool flag)
+{
+ SANE_Status status;
+ MP_SRV buf; /* Service Mode Page Code */
+ DBG (DBG_proc, ">> service_mode\n");
+
+ if (flag)
+ { /* GET */
+ DBG (DBG_info, ">> GET service_mode >> calling mode_sense\n");
+ status =
+ mode_sense (fd, (MP *) & buf,
+ (SANE_Byte) PAGE_CODE_SERVICE_MODE_SELECT);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "get_service_mode: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ else
+ { /* SET */
+ /* Fill in struct then hand off to mode_select */
+ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */
+ buf.code = PAGE_CODE_SERVICE_MODE_SELECT; /* bits5-0: Page Code */
+ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */
+ buf.len = 0x06;
+ /* 0H: Self-Diagnostics Mode, 1H: Optical Adjustment Mode */
+ buf.service = val & 0x01;
+ status = mode_select (fd, (MP *) & buf);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "set_service_mode: MODE_SELECT failed with status=%d\n",
+ status);
+ return (-1);
+ }
+ }
+ DBG (DBG_proc, "<< service_mode\n");
+ return (buf.service & 0x01);
+}