diff options
Diffstat (limited to 'backend/hs2p-scsi.c')
-rw-r--r-- | backend/hs2p-scsi.c | 2198 |
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); +} |