summaryrefslogtreecommitdiff
path: root/backend/pieusb_scancmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/pieusb_scancmd.c')
-rw-r--r--backend/pieusb_scancmd.c1329
1 files changed, 1329 insertions, 0 deletions
diff --git a/backend/pieusb_scancmd.c b/backend/pieusb_scancmd.c
new file mode 100644
index 0000000..a86f8fe
--- /dev/null
+++ b/backend/pieusb_scancmd.c
@@ -0,0 +1,1329 @@
+/* sane - Scanner Access Now Easy.
+
+ pieusb_scancmd.c
+
+ Copyright (C) 2012-2015 Jan Vleeshouwers, Michael Rickmann, Klaus Kaempf
+
+ 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. */
+
+/* =========================================================================
+ *
+ * Pieusb scanner commands
+ *
+ * Each scanner command has its own function.
+ * See the sort description preceeding each function.
+ *
+ * ========================================================================= */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "pieusb.h"
+#include "pieusb_scancmd.h"
+#include "pieusb_usb.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <time.h> /* for time() */
+
+static void _prep_scsi_cmd(SANE_Byte* command_bytes, SANE_Byte command, SANE_Word size);
+
+/* =========================================================================
+ *
+ * Pieusb utility functions
+ *
+ * ========================================================================= */
+
+/*
+ * Get the unsigned char value in the array at given offset
+ */
+static SANE_Byte
+_get_byte(SANE_Byte* array, SANE_Byte offset) {
+ return *(array+offset);
+}
+
+/*
+ * Set the array at given offset to the given unsigned char value
+ */
+static void
+_set_byte(SANE_Byte val, SANE_Byte* array, SANE_Byte offset) {
+ *(array+offset) = val;
+}
+
+
+/*
+ * Get the unsigned short value in the array at given offset.
+ * All data in structures is little-endian, so the LSB comes first
+ * SANE_Int is 4 bytes, but that is not a problem.
+ */
+static SANE_Int
+_get_short(SANE_Byte* array, SANE_Byte offset) {
+ SANE_Int i = *(array+offset+1);
+ i <<= 8;
+ i += *(array+offset);
+ return i;
+}
+
+
+/*
+ * Put the bytes of a short int value into an unsigned char array
+ * All data in structures is little-endian, so start with LSB
+ */
+static void
+_set_short(SANE_Word val, SANE_Byte* array, SANE_Byte offset) {
+ *(array+offset) = val & 0xFF;
+ *(array+offset+1) = (val>>8) & 0xFF;
+}
+
+
+/*
+ * Get the signed int value in the array at given offset.
+ * All data in structures is little-endian, so the LSB comes first
+ */
+static SANE_Int
+_get_int(SANE_Byte* array, SANE_Byte offset) {
+ SANE_Int i = *(array+offset+3);
+ i <<= 8;
+ i += *(array+offset+2);
+ i <<= 8;
+ i += *(array+offset+1);
+ i <<= 8;
+ i += *(array+offset);
+ return i;
+}
+
+#if 0 /* unused */
+/*
+ * Put the bytes of a signed int value into an unsigned char array
+ * All data in structures is little-endian, so start with LSB
+ */
+static void
+_set_int(SANE_Word val, SANE_Byte* array, SANE_Byte offset) {
+ *(array+offset) = val & 0xFF;
+ *(array+offset+1) = (val>>8) & 0xFF;
+ *(array+offset+2) = (val>>16) & 0xFF;
+ *(array+offset+3) = (val>>24) & 0xFF;
+}
+#endif
+
+/*
+ * Copy count unsigned char values from src to dst
+ */
+static void
+_copy_bytes(SANE_Byte* dst, SANE_Byte* src, SANE_Byte count) {
+ SANE_Byte k;
+ for (k=0; k<count; k++) {
+ *dst++ = *src++;
+ }
+}
+
+
+/*
+ * Get count unsigned short values in the array at given offset.
+ * All data in structures is little-endian, so the MSB comes first.
+ * SANE_Word is 4 bytes, but that is not a problem.
+ */
+static void
+_get_shorts(SANE_Word* dst, SANE_Byte* src, SANE_Byte count) {
+ SANE_Byte k;
+ for (k=0; k<count; k++) {
+ *dst = *(src+2*k+1);
+ *dst <<= 8;
+ *dst++ += *(src+2*k);
+ }
+}
+
+/*
+ * Copy an unsigned short array of given size
+ * All data in structures is little-endian, so start with MSB of each short.
+ * SANE_Word is 4 bytes, but that is not a problem. All MSB 2 bytes are ignored.
+ */
+static void
+_set_shorts(SANE_Word* src, SANE_Byte* dst, SANE_Byte count) {
+ SANE_Byte k;
+ for (k=0; k<count; k++) {
+ *(dst+2*k) = *src & 0xFF;
+ *(dst+2*k+1) = ((*src++)>>8) & 0xFF;
+ }
+}
+
+
+/**
+ * Perform a TEST UNIT READY (SCSI command code 0x00)
+ * Returns status->pieusb_status:
+ * - PIEUSB_STATUS_GOOD if device is ready
+ * - PIEUSB_STATUS_DEVICE_BUSY if device is still busy after timeout
+ * - other SANE status code if TEST UNIT READY failed or if it returned
+ * CHECK CONDITION and REQUEST SENSE failed
+ *
+ * @param device_number Device number
+ * @return Pieusb_Command_Status SANE_STATUS_GOOD if ready, SANE_STATUS_DEVICE_BUSY if not
+ */
+void
+sanei_pieusb_cmd_test_unit_ready(SANE_Int device_number, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_test_unit_ready()\n");
+
+ _prep_scsi_cmd (command, SCSI_TEST_UNIT_READY, 0);
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, NULL, 0);
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_test_unit_ready() return status = %s\n", sane_strstatus(status->pieusb_status));
+}
+
+/**
+ * slide action
+ * @param action SLIDE_NEXT, SLIDE_PREV, SLIDE_LAMP_ON, SLIDE_RELOAD
+ * @return Pieusb_Command_Status
+ */
+
+void
+sanei_pieusb_cmd_slide(SANE_Int device_number, slide_action action, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define SLIDE_DATA_SIZE 4
+ SANE_Byte data[SLIDE_DATA_SIZE];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_slide(0x%02x)\n", action);
+
+ _prep_scsi_cmd(command, SCSI_SLIDE, SLIDE_DATA_SIZE);
+ memset(data, '\0', SLIDE_DATA_SIZE);
+ data[0] = action;
+ data[1] = 0x01;
+
+ status->pieusb_status = sanei_pieusb_command(device_number, command, data, SLIDE_DATA_SIZE);
+#undef SLIDE_DATA_SIZE
+}
+
+/**
+ * Perform a REQUEST SENSE (SCSI command code 0x03)
+ * Returns status->pieusb_status:
+ * - PIEUSB_STATUS_GOOD is the command executes OK
+ * - other SANE status code if REQUEST SENSE fails
+ * The sense fields in status are always 0. A REQUEST SENSE is not repeated if
+ * the device returns PIEUSB_STATUS_DEVICE_BUSY.
+ *
+ * @param device_number Device number
+ * @param sense Sense data
+ * @param status Command result status
+ * @see struc Pieusb_Sense
+ */
+void
+sanei_pieusb_cmd_get_sense(SANE_Int device_number, struct Pieusb_Sense* sense, struct Pieusb_Command_Status *status, PIEUSB_Status *ret)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define DATA_SIZE 14
+ SANE_Int size = DATA_SIZE;
+ SANE_Byte data[DATA_SIZE];
+ PIEUSB_Status st;
+ SANE_Char* sd;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_sense()\n");
+
+ _prep_scsi_cmd(command, SCSI_REQUEST_SENSE, size);
+
+ memset(data, '\0', size);
+ st = sanei_pieusb_command(device_number, command, data, size);
+ if (st != PIEUSB_STATUS_GOOD) {
+ status->pieusb_status = st;
+ /*FIXME*/
+ return;
+ }
+
+ /* Decode data recieved */
+ sense->errorCode = _get_byte (data, 0);
+ sense->segment = _get_byte (data, 1);
+ sense->senseKey = _get_byte (data, 2);
+ _copy_bytes (sense->info, data+3, 4);
+ sense->addLength = _get_byte (data, 7);
+ _copy_bytes (sense->cmdInfo, data+8, 4);
+ sense->senseCode = _get_byte (data, 12);
+ sense->senseQualifier = _get_byte (data, 13);
+ status->pieusb_status = PIEUSB_STATUS_GOOD;
+#undef DATA_SIZE
+ DBG (DBG_info_scan, "\tsense details:\n");
+ DBG (DBG_info_scan, "\t\terror......... : 0x%02x\n", sense->errorCode);
+ DBG (DBG_info_scan, "\t\tsegment....... : %d\n", sense->segment);
+ DBG (DBG_info_scan, "\t\tsenseKey...... : 0x%02x\n", sense->senseKey);
+ DBG (DBG_info_scan, "\t\tinfo.......... : %02x %02x %02x %02x\n", sense->info[0], sense->info[1], sense->info[2], sense->info[3]);
+ DBG (DBG_info_scan, "\t\taddLength..... : %d\n", sense->addLength);
+ DBG (DBG_info_scan, "\t\tcmdInfo....... : %02x %02x %02x %02x\n", sense->cmdInfo[0], sense->cmdInfo[1], sense->cmdInfo[2], sense->cmdInfo[3]);
+ DBG (DBG_info_scan, "\t\tsenseCode..... : 0x%02x\n", sense->senseCode);
+ DBG (DBG_info_scan, "\t\tsenseQualifier : 0x%02x\n", sense->senseQualifier);
+ sd = sanei_pieusb_decode_sense (sense, ret?ret:&st);
+ DBG (DBG_info_scan, "\tsense: %s\n", sd);
+ free(sd);
+}
+
+/**
+ * Read the halftone pattern with the specified index. This requires two
+ * commands, one to ask the device to prepare the pattern, and one to read it.
+ *
+ * @param device_number Device number
+ * @param index index of halftone pattern
+ * @param pattern Halftone pattern (not implemented)
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Halftone_Pattern
+ */
+void
+sanei_pieusb_cmd_get_halftone_pattern(SANE_Int device_number, SANE_Int index, struct Pieusb_Halftone_Pattern* pattern, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define PATTERN_SIZE 256 /* Assumed maximum pattern size */
+ SANE_Int size = PATTERN_SIZE;
+ SANE_Byte data[PATTERN_SIZE];
+ int psize;
+ SANE_Char* desc;
+ PIEUSB_Status st;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_halftone_pattern()\n");
+
+ /* Ask scanner to prepare the pattern with the given index. Only SCSI_COMMAND_LEN bytes of data. */
+ _prep_scsi_cmd(command, SCSI_WRITE, SCSI_COMMAND_LEN);
+ memset(data, '\0', SCSI_COMMAND_LEN);
+ data[0] = SCSI_HALFTONE_PATTERN | 0x80; /* set bit 7 means prepare read */
+ data[4] = index;
+
+ st = sanei_pieusb_command(device_number, command, data, SCSI_COMMAND_LEN);
+ if (st != PIEUSB_STATUS_GOOD) {
+ status->pieusb_status = st;
+ /* FIXME */
+ return;
+ }
+
+ /* Read pattern */
+ _prep_scsi_cmd(command, SCSI_READ, size);
+
+ memset(data, '\0', size);
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+
+ /*FIXME: analyse */
+ fprintf(stderr, "Halftone pattern %d:\n", index);
+ psize = (data[3]<<8) + data[2];
+ desc = (SANE_Char*)(data + 4 + psize);
+ data[4 + psize + 16] = '\0';
+ fprintf(stderr,"Descr. offset from byte 4 = %d, %16s, index = %d, size = %dx%d\n", psize, desc, data[4]&0x7F, data[6], data[7]);
+#undef PATTERN_SIZE
+}
+
+/**
+ * Read the scan frame with the specified index. This requires two
+ * commands, one to ask the device to prepare the pattern, and one to read it.
+ *
+ * @param device_number Device number
+ * @param frame Scan frame
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Scan_Frame
+ */
+void
+sanei_pieusb_cmd_get_scan_frame(SANE_Int device_number, SANE_Int index, struct Pieusb_Scan_Frame* frame, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define FRAME_SIZE 256 /* Assumed maximum frame size */
+ SANE_Int size = FRAME_SIZE;
+ SANE_Byte data[FRAME_SIZE];
+ PIEUSB_Status st;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_scan_frame()\n");
+
+ /* Ask scanner to prepare the scan frame with the given index. Only SCSI_COMMAND_LEN bytes of data. */
+ _prep_scsi_cmd (command, SCSI_WRITE, SCSI_COMMAND_LEN);
+ memset (data, '\0', SCSI_COMMAND_LEN);
+ data[0] = SCSI_SCAN_FRAME | 0x80; /* set bit 7 means prepare read */
+ data[4] = index;
+
+ st = sanei_pieusb_command (device_number, command, data, SCSI_COMMAND_LEN);
+ if (st != PIEUSB_STATUS_GOOD) {
+ status->pieusb_status = st;
+ /* FIXME */
+ return;
+ }
+
+ /* Read scan frame */
+ _prep_scsi_cmd (command, SCSI_READ, size);
+
+ memset(data, '\0', size);
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+
+ /* Decode data */
+ frame->index = _get_byte (data, 4);
+ frame->x0 = _get_short (data, 6);
+ frame->y0 = _get_short (data, 8);
+ frame->x1 = _get_short (data, 10);
+ frame->y1 = _get_short (data, 12);
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_scan_frame() set:\n");
+ DBG (DBG_info_scan, " x0,y0 = %d,%d\n", frame->x0, frame->y0);
+ DBG (DBG_info_scan, " x1,y1 = %d,%d\n", frame->x1, frame->y1);
+ DBG (DBG_info_scan, " index = %d\n", frame->index);
+#undef FRAME_SIZE
+}
+
+/**
+ * command 17 - unknown
+ */
+
+void
+sanei_pieusb_cmd_17(SANE_Int device_number, SANE_Int value, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define CMD_17_SIZE 6
+ SANE_Byte data[CMD_17_SIZE];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_17(%d)\n", value);
+
+ _prep_scsi_cmd (command, SCSI_WRITE, CMD_17_SIZE);
+ memset (data, '\0', CMD_17_SIZE);
+ _set_short (SCSI_CMD_17, data, 0);
+ _set_short (2, data, 2);
+ _set_short (value, data, 4);
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, CMD_17_SIZE);
+#undef CMD_17_SIZE
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD) {
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_17 failed: 0x%02x\n", status->pieusb_status);
+ return;
+ }
+}
+
+/**
+ * Read the relative exposure time for the specified colorbits. This requires two
+ * commands, one to ask the device to prepare the value, and one to read it.
+ *
+ * @param device_number Device number
+ * @param time Relative exposure time(s)
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Exposure_Time
+ */
+void
+sanei_pieusb_cmd_get_exposure_time(SANE_Int device_number, SANE_Int colorbits, struct Pieusb_Exposure_Time* time, struct Pieusb_Command_Status *status)
+{
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_exposure_time(): not implemented\n");
+ status->pieusb_status = PIEUSB_STATUS_INVAL;
+}
+
+/**
+ * Read the highlight and shadow levels with the specified colorbits. This requires two
+ * commands, one to ask the device to prepare the value, and one to read it.
+ *
+ * @param device_number Device number
+ * @param hgltshdw Highlight and shadow level(s)
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Highlight_Shadow
+ */
+void
+sanei_pieusb_cmd_get_highlight_shadow(SANE_Int device_number, SANE_Int colorbits, struct Pieusb_Highlight_Shadow* hgltshdw, struct Pieusb_Command_Status *status)
+{
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_highlight_shadow(): not implemented\n");
+ status->pieusb_status = PIEUSB_STATUS_INVAL;
+}
+
+/**
+ * Read the shading data parameters. This requires two
+ * commands, one to ask the device to prepare the value, and one to read it.
+ *
+ * @param device_number Device number
+ * @param shading Shading data parameters
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Shading_Parameters
+ */
+void
+sanei_pieusb_cmd_get_shading_parms(SANE_Int device_number, struct Pieusb_Shading_Parameters_Info* shading, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define SHADING_SIZE 32
+#define PREP_READ_SIZE 6
+ SANE_Int size = SHADING_SIZE;
+ SANE_Byte data[SHADING_SIZE];
+ int k;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_shading_parms()\n");
+
+ /* Ask scanner to prepare the scan frame with the given index. Only SCSI_COMMAND_LEN bytes of data. */
+ _prep_scsi_cmd (command, SCSI_WRITE, SCSI_COMMAND_LEN);
+ memset (data, '\0', PREP_READ_SIZE);
+ data[0] = SCSI_CALIBRATION_INFO | 0x80; /* set bit 7 means prepare read */
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, PREP_READ_SIZE);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD) {
+ return;
+ }
+
+ /* Read shading parameters */
+ _prep_scsi_cmd(command, SCSI_READ, size);
+
+ memset (data, '\0', size);
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD) {
+ return;
+ }
+
+ /* Decode data [32 bytes]
+ 0: 95 00 type
+ 2: 1c 00 payload len
+ 4: 04 entries
+ 5: 06 entry size
+ 6: 04 00 ?
+ 8: 00 10 10 14 1a 1d type send recv nlines pixPerLine(2bytes)
+ 14: 08 10 10 14 1a 1d
+ 20: 10 10 10 14 1a 1d
+ 26: 20 10 10 14 1a 1d
+ */
+ for (k = 0; k < data[4]; k++) {
+ shading[k].type = _get_byte (data, 8 + data[5]*k);
+ shading[k].sendBits = _get_byte (data, 9 + data[5]*k);
+ shading[k].recieveBits = _get_byte (data, 10 + data[5]*k);
+ shading[k].nLines = _get_byte (data, 11 + data[5]*k);
+ shading[k].pixelsPerLine = _get_short (data, 12 + data[5]*k);
+ }
+#undef PREP_READ_SIZE
+#undef SHADING_SIZE
+}
+
+/**
+ * Read scanned data from the scanner memory into a byte array. The lines
+ * argument specifies how many lines will be read, the size argument specifies
+ * the total amount of bytes in these lines. Use sanei_pieusb_cmd_get_parameters() to
+ * determine the current line size and the number of available lines.\n
+ * If there is scanned data available, it should be read. Waiting too long
+ * causes the scan to stop, probably because a buffer is filled to its limits
+ * (if so, it is approximately 2Mb in size). I haven't tried what happens if you
+ * start reading after a stop. Reading to fast causes the scanner to return
+ * a busy status, which is not a problem.
+ * This is a SCSI READ command (code 0x08). It is distinguished from the other
+ * READ commands by the context in which it is issued: see sanei_pieusb_cmd_start_scan().
+ *
+ * @param device_number
+ * @param data
+ * @param lines
+ * @param size
+ * @return Pieusb_Command_Status
+ */
+void
+sanei_pieusb_cmd_get_scanned_lines(SANE_Int device_number, SANE_Byte* data, SANE_Int lines, SANE_Int size, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_scanned_lines(): %d lines (%d bytes)\n", lines, size);
+
+ _prep_scsi_cmd (command, SCSI_READ, lines);
+ memset (data, '\0', size);
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+}
+
+/**
+ * Set the halftone pattern with the given index to the specified pattern. The
+ * command is a SCSI WRITE command (code 0x0A, write code 0x11).
+ *
+ * @param device_number Device number
+ * @param index Pattern index (0-7)
+ * @param pattern Halftone pattern (not implemented)
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Halftone_Pattern
+ */
+void
+sanei_pieusb_cmd_set_halftone_pattern(SANE_Int device_number, SANE_Int index, struct Pieusb_Halftone_Pattern* pattern, struct Pieusb_Command_Status *status)
+{
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_halftone_pattern(): not implemented\n");
+ status->pieusb_status = PIEUSB_STATUS_INVAL;
+}
+
+/**
+ * Set the scan frame with the given index to the frame. The command is a SCSI
+ * WRITE command (code SCSI_WRITE, write code SCSI_SCAN_FRAME).
+ *
+ * @param device_number Device number
+ * @param index Frame index (0-7)
+ * @param frame Scan frame
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Scan_Frame
+ */
+void
+sanei_pieusb_cmd_set_scan_frame(SANE_Int device_number, SANE_Int index, struct Pieusb_Scan_Frame* frame, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define FRAME_SIZE 14
+ SANE_Int size = FRAME_SIZE;
+ SANE_Byte data[FRAME_SIZE];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_scan_frame()\n");
+
+ _prep_scsi_cmd(command, SCSI_WRITE, size);
+
+ DBG (DBG_info_scan, " x0,y0 = %d,%d\n", frame->x0, frame->y0);
+ DBG (DBG_info_scan, " x1,y1 = %d,%d\n", frame->x1, frame->y1);
+ DBG (DBG_info_scan, " index = %d\n", index);
+
+ /* Code data */
+ memset (data, '\0', size);
+ _set_short (SCSI_SCAN_FRAME, data, 0);
+ _set_short (size-4, data, 2); /* size: one frame, 5 shorts */
+ _set_short (index, data, 4);
+ _set_short (frame->x0, data, 6);
+ _set_short (frame->y0, data, 8);
+ _set_short (frame->x1, data, 10);
+ _set_short (frame->y1, data, 12);
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+#undef FRAME_SIZE
+}
+
+/**
+ * Set the relative exposure time to the given values. Only the first
+ * Pieusb_Exposure_Time_Color is used. The command is a SCSI
+ * WRITE command (code SCSI_WRITE, write code SCSI_EXPOSURE).
+ *
+ * @param device_number Device number
+ * @param time Relative exposure time
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Exposure_Time
+ */
+void
+sanei_pieusb_cmd_set_exposure_time(SANE_Int device_number, struct Pieusb_Exposure_Time* time, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define EXPOSURE_DATA_SIZE 8
+ SANE_Byte data[EXPOSURE_DATA_SIZE];
+ struct Pieusb_Exposure_Time_Color *exptime;
+ int i;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_exposure_time()\n");
+
+ for (i = 0; i < 3; ++i) { /* R, G, B */
+ _prep_scsi_cmd (command, SCSI_WRITE, EXPOSURE_DATA_SIZE);
+ memset (data, '\0', EXPOSURE_DATA_SIZE);
+ exptime = &(time->color[i]);
+ _set_short (SCSI_EXPOSURE, data, 0);
+ _set_short (EXPOSURE_DATA_SIZE-4, data, 2); /* short: RGB, short: value */
+ _set_short (exptime->filter, data, 4); /* 1: neutral, 2: R, 4: G, 8: B */
+ _set_short (exptime->value, data, 6);
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, EXPOSURE_DATA_SIZE);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD)
+ break;
+ }
+
+#undef EXPOSURE_DATA_SIZE
+}
+
+/**
+ * Set the highlight and shadow levels to the given values. Only the first
+ * Pieusb_Highlight_Shadow_Color is used. The command is a SCSI
+ * WRITE command (code SCSI_WRITE, write code SCSI_HIGHLIGHT_SHADOW).
+ *
+ * @param device_number Device number
+ * @param hgltshdw highlight and shadow level
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Highlight_Shadow
+ */
+void
+sanei_pieusb_cmd_set_highlight_shadow(SANE_Int device_number, struct Pieusb_Highlight_Shadow* hgltshdw, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define HIGHLIGHT_SHADOW_SIZE 8
+ SANE_Byte data[HIGHLIGHT_SHADOW_SIZE];
+ struct Pieusb_Highlight_Shadow_Color *color;
+ int i;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_highlight_shadow()\n");
+
+ for (i = 0; i < 3; ++i) { /* R, G, B */
+ _prep_scsi_cmd (command, SCSI_WRITE, HIGHLIGHT_SHADOW_SIZE);
+ memset (data, '\0', HIGHLIGHT_SHADOW_SIZE);
+ color = &(hgltshdw->color[i]);
+ _set_short (SCSI_HIGHLIGHT_SHADOW, data, 0);
+ _set_short (HIGHLIGHT_SHADOW_SIZE-4, data, 2); /* short: RGB, short: value */
+ _set_short (color->filter, data, 4); /* 1: neutral, 2: R, 4: G, 8: B */
+ _set_short (color->value, data, 6);
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, HIGHLIGHT_SHADOW_SIZE);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD)
+ break;
+ }
+
+#undef HIGHLIGHT_SHADOW_SIZE
+}
+
+/**
+ * Set the CCD-mask for the colors set in the given color bit mask. The mask
+ * array must contain mask_size. The command is a SCSI WRITE command
+ * (code 0x0A, write code 0x16).
+ * (The command is able to handle more masks at once, but that is not implemented.)
+ *
+ * @param device_number Device number
+ * @param colorbits 0000RGB0 color bit mask; at least one color bit must be set
+ * @param mask CCD mask to use
+ * @return Pieusb_Command_Status
+ */
+void
+sanei_pieusb_cmd_set_ccd_mask(SANE_Int device_number, SANE_Byte colorbits, SANE_Byte* mask, SANE_Int mask_size, struct Pieusb_Command_Status *status)
+{
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_ccd_mask(): not implemented\n");
+ status->pieusb_status = PIEUSB_STATUS_INVAL;
+}
+
+/* SCSI PARAM, code 0x0F */
+/**
+ * Get the parameters of an executed scan, such as width, lines and bytes, which
+ * are needed to calculate the parameters of the READ-commands which read the
+ * actual scan data.
+ *
+ * @param device_number Device number
+ * @param parameters Scan parameters
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Scan_Parameters
+ */
+void
+sanei_pieusb_cmd_get_parameters(SANE_Int device_number, struct Pieusb_Scan_Parameters* parameters, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define PARAMETER_SIZE 18
+ SANE_Int size = PARAMETER_SIZE;
+ SANE_Byte data[PARAMETER_SIZE];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_parameters()\n");
+
+ _prep_scsi_cmd (command, SCSI_PARAM, size);
+ memset (data, '\0', size);
+
+ status->pieusb_status = sanei_pieusb_command(device_number, command, data, size);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD) {
+ return;
+ }
+
+ /* cyberview:
+ * 0: e6 02 width 0x2e6 - 742
+ * 2: e0 02 lines 0x2e0 - 736
+ * 4: e6 02 bytes 0x2e6 - 742
+ * 6: 08 filterOffeset1 8
+ * 7: 08 filterOffset2 8
+ * 8: c9 1c 00 00 period 7369
+ * c: 00 00 scsi transfer rate
+ * e: d7 00 available lines 215
+ * 10:00 00
+ */
+ /* Decode data recieved */
+ parameters->width = _get_short(data, 0);
+ parameters->lines = _get_short(data, 2);
+ parameters->bytes = _get_short(data, 4);
+ parameters->filterOffset1 = _get_byte(data, 6);
+ parameters->filterOffset2 = _get_byte(data, 7);
+ parameters->period = _get_int(data, 8); /* unused */
+ parameters->scsiTransferRate = _get_short(data, 12); /* unused */
+ parameters->availableLines = _get_short(data, 14);
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_parameters() read:\n");
+ DBG (DBG_info_scan, " width = %d\n", parameters->width);
+ DBG (DBG_info_scan, " lines = %d\n", parameters->lines);
+ DBG (DBG_info_scan, " bytes = %d\n", parameters->bytes);
+ DBG (DBG_info_scan, " offset1 = %d\n", parameters->filterOffset1);
+ DBG (DBG_info_scan, " offset2 = %d\n", parameters->filterOffset2);
+ DBG (DBG_info_scan, " available lines = %d\n", parameters->availableLines);
+#undef PARAMETER_SIZE
+}
+
+/**
+ * Read INQUIRY block from device (SCSI command code 0x12). This block contains
+ * information about the properties of the scanner.
+ * Returns status->pieusb_status:
+ * - PIEUSB_STATUS_GOOD if the INQUIRY command succeeded
+ * - PIEUSB_STATUS_DEVICE_BUSY if device is busy after repeat retries
+ * - other SANE status code if INQUIRY failed or if it returned CHECK CONDITION
+ * and REQUEST SENSE failed
+ *
+ * @param device_number Device number
+ * @param data Input or output data buffer
+ * @param size Size of the data buffer
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Scanner_Properties
+ */
+void
+sanei_pieusb_cmd_inquiry(SANE_Int device_number, struct Pieusb_Scanner_Properties* inq, SANE_Byte size, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define INQUIRY_SIZE 256
+ SANE_Byte data[INQUIRY_SIZE];
+ int k;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_inquiry()\n");
+
+ _prep_scsi_cmd (command, SCSI_INQUIRY, size);
+ memset (data, '\0', INQUIRY_SIZE); /* size may be less than INQUIRY_SIZE, so prevent returning noise */
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD) {
+ return;
+ }
+
+ /* Decode data recieved */
+ inq->deviceType = _get_byte(data, 0);
+ inq->additionalLength = _get_byte(data, 4);
+ _copy_bytes((SANE_Byte*)(inq->vendor), data+8, 8); /* Note: not 0-terminated */
+ _copy_bytes((SANE_Byte*)(inq->product), data+16, 16); /* Note: not 0-terminated */
+ _copy_bytes((SANE_Byte*)(inq->productRevision), data+32, 4); /* Note: not 0-terminated */
+ /* 1st Vendor-specific block, 20 bytes, see pie_get_inquiry_values(), partially: */
+ inq->maxResolutionX = _get_short(data, 36);
+ inq->maxResolutionY = _get_short(data, 38);
+ inq->maxScanWidth = _get_short(data, 40);
+ inq->maxScanHeight = _get_short(data, 42);
+ inq->filters = _get_byte(data, 44);
+ inq->colorDepths = _get_byte(data, 45);
+ inq->colorFormat = _get_byte(data, 46);
+ inq->imageFormat = _get_byte(data, 48);
+ inq->scanCapability = _get_byte(data, 49);
+ inq->optionalDevices = _get_byte(data, 50);
+ inq->enhancements = _get_byte(data, 51);
+ inq->gammaBits = _get_byte(data, 52);
+ inq->lastFilter = _get_byte(data, 53);
+ inq->previewScanResolution = _get_short(data, 54);
+ /* 2nd vendor specific block (36 bytes at offset 96) */
+ _copy_bytes((SANE_Byte*)(inq->firmwareVersion), data+96, 4); inq->firmwareVersion[4]=0x00;
+ inq->halftones = _get_byte(data, 100);
+ inq->minumumHighlight = _get_byte(data, 101);
+ inq->maximumShadow = _get_byte(data, 102);
+ inq->calibrationEquation = _get_byte(data, 103);
+ inq->maximumExposure = _get_short(data ,104);
+ inq->minimumExposure = _get_short(data ,106);
+ inq->x0 = _get_short(data, 108);
+ inq->y0 = _get_short(data, 110);
+ inq->x1 = _get_short(data, 112);
+ inq->y1 = _get_short(data, 114);
+ inq->model = _get_short(data, 116);
+ _copy_bytes((SANE_Byte*)(inq->production), data+120, 4);
+ _copy_bytes((SANE_Byte*)(inq->timestamp), data+124, 20);
+ _copy_bytes((SANE_Byte*)(inq->signature), data+144, 40);
+ /* remove newline in signature */
+ for (k=0; k<40; k++) if (inq->signature[k]==0x0a || inq->signature[k]==0x0d) inq->signature[k]=' ';
+#undef INQUIRY_SIZE
+}
+
+/**
+ * Set scan mode parameters, such as resolution, colors to scan, color depth,
+ * color format, and a couple of scan quality settings (sharpen, skip
+ * calibration, fast infrared). It performs the SCSI-command MODE SELECT,
+ * code 0x15.
+ *
+ * @param device_number Device number
+ * @param mode Mode parameters
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Mode
+ */
+void
+sanei_pieusb_cmd_set_mode(SANE_Int device_number, struct Pieusb_Mode* mode, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define MODE_SIZE 16
+ SANE_Int size = MODE_SIZE;
+ SANE_Byte data[MODE_SIZE];
+ SANE_Byte quality;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_mode()\n");
+
+ _prep_scsi_cmd(command, SCSI_MODE_SELECT, size);
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_mode() set:\n");
+ DBG (DBG_info_scan, " resolution = %d\n", mode->resolution);
+ DBG (DBG_info_scan, " passes = %02x\n", mode->passes);
+ DBG (DBG_info_scan, " depth = %02x\n", mode->colorDepth);
+ DBG (DBG_info_scan, " color format = %02x\n", mode->colorFormat);
+ DBG (DBG_info_scan, " sharpen = %d\n", mode->sharpen);
+ DBG (DBG_info_scan, " skip calibration = %d\n", mode->skipShadingAnalysis);
+ DBG (DBG_info_scan, " fast infrared = %d\n", mode->fastInfrared);
+ DBG (DBG_info_scan, " halftone pattern = %d\n", mode->halftonePattern);
+ DBG (DBG_info_scan, " line threshold = %d\n", mode->lineThreshold);
+
+ /* Code data */
+ /* cyberview
+ * 00 0f entries
+ * f4 01 resolution 500
+ * 80 RGB (90: RGBI)
+ * 04 color depth (4: 8 bit, 20: 16 bit)
+ * 04 color format
+ * 00
+ * 01 byte order
+ * 08 quality bitmask: 80=fast infrared, 08=skip shading analysis, 02=sharpen
+ * 00 00
+ * 00 halftone pattern
+ * 80 line threshold
+ * 10 00
+ *
+ * pieusb
+ * 0: 00 0f
+ * 2: e8 03 resolution 1000
+ * 4: 80 passes
+ * 5: 04 color depth
+ * 6: 04 color format
+ * 7: 00
+ * 8: 01 byte order
+ * 9: 02 quality bitmask: sharpen
+ * a: 00 00
+ * c: 00 halftone pattern
+ * d: 7f line threshold
+ * e: 00 00
+ */
+ memset (data, '\0', size);
+ _set_byte (size-1, data, 1);
+ _set_short (mode->resolution, data, 2);
+ _set_byte (mode->passes, data, 4);
+ _set_byte (mode->colorDepth, data, 5);
+ _set_byte (mode->colorFormat, data, 6);
+ _set_byte (mode->byteOrder, data, 8);
+ quality = 0x00;
+ if (mode->sharpen) quality |= 0x02;
+ if (mode->skipShadingAnalysis) quality |= 0x08;
+ if (mode->fastInfrared) quality |= 0x80;
+ _set_byte (quality, data, 9);
+ _set_byte (mode->halftonePattern, data, 12);
+ _set_byte (mode->lineThreshold, data, 13);
+ _set_byte (0x10, data, 14); /* ? */
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+#undef MODE_SIZE
+}
+
+/* SCSI COPY, code 0x18 */
+/**
+ * Get the currently used CCD-mask, which defines which pixels have been used in
+ * the scan, and which allows to relate scan data to shading data. A mask is a
+ * 5340 byte array which consists only contains the values 0x00 and 0x70. A
+ * value of 0x00 indicates the pixel is used, a value of 0x70 that it is not.\n
+ * The number of 0x00 bytes equals the number of pixels on a line.\n
+ * The mask begins with a number of 0x70 bytes equal to the scan frame x0-value
+ * divided by 2.\n
+ * The SCSI-command COPY (code 0x18) is used for function.
+ *
+ * @param device_number Device number
+ * @param mask
+ * @return Pieusb_Command_Status
+ */
+void
+sanei_pieusb_cmd_get_ccd_mask(SANE_Int device_number, SANE_Byte* mask, SANE_Int mask_size, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_ccd_mask()\n");
+
+ _prep_scsi_cmd (command, SCSI_COPY, mask_size);
+
+ memset (mask, '\0', mask_size);
+ status->pieusb_status = sanei_pieusb_command (device_number, command, mask, mask_size);
+}
+
+/**
+ * Get scan mode parameters, such as resolution, colors to scan, color depth,
+ * color format, and a couple of scan quality settings (sharpen, skip
+ * calibration, fast infrared). It performs the SCSI-command MODE SELECT,
+ * code 0x1A.
+ *
+ * @param device_number Device number
+ * @param mode Mode parameters
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Mode
+ */
+void
+sanei_pieusb_cmd_get_mode(SANE_Int device_number, struct Pieusb_Mode* mode, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define MODE_SIZE 16
+ SANE_Int size = MODE_SIZE;
+ SANE_Byte data[MODE_SIZE];
+ SANE_Byte quality;
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_mode()\n");
+
+ _prep_scsi_cmd (command, SCSI_MODE_SENSE, size);
+ memset (data, '\0', size);
+
+ status->pieusb_status = sanei_pieusb_command(device_number, command, data, size);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD) {
+ return;
+ }
+
+ /* Decode data recieved */
+ mode->resolution = _get_short (data, 2);
+ mode->passes = _get_byte (data, 4);
+ mode->colorDepth = _get_byte (data, 5);
+ mode->colorFormat = _get_byte (data, 6);
+ mode->byteOrder = _get_byte (data, 8);
+ quality = _get_byte (data, 9);
+ mode->sharpen = (quality |= 0x02) ? SANE_TRUE : SANE_FALSE;
+ mode->skipShadingAnalysis = (quality |= 0x08) ? SANE_TRUE : SANE_FALSE;
+ mode->fastInfrared = (quality |= 0x80) ? SANE_TRUE : SANE_FALSE;
+ mode->halftonePattern = _get_byte (data, 12);
+ mode->lineThreshold = _get_byte (data, 13);
+
+ DBG (DBG_info_scan, "cmdGetMode():\n");
+ DBG (DBG_info_scan, " resolution = %d\n", mode->resolution);
+ DBG (DBG_info_scan, " passes = %02x\n", mode->passes);
+ DBG (DBG_info_scan, " depth = %02x\n", mode->colorDepth);
+ DBG (DBG_info_scan, " color format = %02x\n", mode->colorFormat);
+ DBG (DBG_info_scan, " sharpen = %d\n", mode->sharpen);
+ DBG (DBG_info_scan, " skip calibration = %d\n", mode->skipShadingAnalysis);
+ DBG (DBG_info_scan, " fast infrared = %d\n", mode->fastInfrared);
+ DBG (DBG_info_scan, " halftone pattern = %d\n", mode->halftonePattern);
+ DBG (DBG_info_scan, " line threshold = %d\n", mode->lineThreshold);
+#undef MODE_SIZE
+}
+
+/**
+ * Start a scan (SCSI SCAN command, code 0x1B, size byte = 0x01).\n
+ * There are four phases in a scan process. During each phase a limited number of
+ * commands is available. The phases are:\n
+ * 1. Calibration phase: make previously collected shading correction data available\n
+ * 2. Line-by-line scan & read phase\n
+ * 3. Output CCD-mask phase\n
+ * 4. Scan and output scan data phase\n
+ *
+ * The calibration phase is skipped if Pieusb_Mode.skipCalibration is set. If
+ * the scanner determines a calibration is necessary, a CHECK CONDIDITION response
+ * is returned. Available command during this phase:\n
+ * 1. sanei_pieusb_cmd_test_unit_ready()\n
+ * 2. sanei_pieusb_cmd_get_scanned_lines(): read shading correction lines\n
+ * 3. sanei_pieusb_cmd_stop_scan: abort scanning process\n
+ * 4. sanei_pieusb_cmd_get_gain_offset() : the settings are generated during the initialisation of this phase, so they are current\n
+ * 5. cmdSetSettings(): settings take effect in the next scan phase\n\n
+ * The line-by-line phase is only entered if Pieusb_Mode.div_10[0] bit 5 is
+ * set. It is not implemented.\n\n
+ * In the CCD-mask output phase the CCD-mask is read. Available command during this phase:\n
+ * 1. sanei_pieusb_cmd_test_unit_ready()\n
+ * 2. sanei_pieusb_cmd_get_ccd_mask()\n
+ * 3. sanei_pieusb_cmd_stop_scan: abort scanning process\n\n
+ * In the 'scan and output scan data' phase, the slide is scanned while data is
+ * read in the mean time. Available command during this phase:\n
+ * 1. sanei_pieusb_cmd_test_unit_ready()\n
+ * 2. sanei_pieusb_cmd_get_scanned_lines()\n
+ * 2. sanei_pieusb_cmd_get_parameters()\n
+ * 4. sanei_pieusb_cmd_stop_scan: abort scanning process\n
+ *
+ * @param device_number Device number
+ * @return Pieusb_Command_Status
+ */
+void
+sanei_pieusb_cmd_start_scan(SANE_Int device_number, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_start_scan()\n");
+
+ _prep_scsi_cmd (command, SCSI_SCAN, 1);
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, NULL, 0);
+}
+
+/**
+ * Stop a scan started with sanei_pieusb_cmd_start_scan(). It issues a SCSI SCAN command,
+ * code 0x1B, with size byte = 0x00.
+ *
+ * @param device_number Device number
+ * @return Pieusb_Command_Status
+ */
+void
+sanei_pieusb_cmd_stop_scan(SANE_Int device_number, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_stop_scan()\n");
+
+ _prep_scsi_cmd (command, SCSI_SCAN, 0);
+
+ status->pieusb_status = sanei_pieusb_command (device_number, command, NULL, 0);
+}
+
+/**
+ * Set scan head to a specific position, depending on the value for mode:\n
+ * mode = 1: Returns the scan head to the resting position, after a short move
+ * forward. If this command is left out between two scans, the second scan is
+ * up-down-mirrored, and scanning starts where the proevious scan stopped.\n
+ * mode = 2: Resets the scan head an then moves it forward depending on 'size',
+ * but it is a bit unpredictable to what position. The scanner may attempt to
+ * move the head past its physical end position. The mode is not implemented.\n
+ * mode = 3: This command positions the scan head to the start of the slide.\n
+ * mode = 4 or 5: The command forwards (4) or retreats (5) the scan head the
+ * given amount of steps (in size).\n
+ * The SCSI code is 0xD2, there is no related command name.
+ *
+ * @param device_number Device number
+ * @param mode
+ * @param size
+ * @return Pieusb_Command_Status
+ */
+void
+sanei_pieusb_cmd_set_scan_head(SANE_Int device_number, SANE_Int mode, SANE_Int steps, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define SCAN_HEAD_SIZE 4
+ SANE_Int size = SCAN_HEAD_SIZE;
+ SANE_Byte data[SCAN_HEAD_SIZE];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_scan_head()\n");
+
+ _prep_scsi_cmd (command, SCSI_SET_SCAN_HEAD, size);
+
+ /* Code data */
+ memset (data, '\0', size);
+ switch (mode) {
+ case 1:
+ data[0] = 2;
+ break;
+ case 2:
+ DBG (DBG_error, "sanei_pieusb_cmd_set_scan_head() mode 2 unreliable, possibly dangerous\n");
+ status->pieusb_status = PIEUSB_STATUS_INVAL;
+ return;
+ case 3:
+ data[0] = 8;
+ break;
+ case 4:
+ data[0] = 0; /* forward */
+ data[2] = (steps>>8) & 0xFF;
+ data[3] = steps & 0xFF;
+ break;
+ case 5:
+ data[0] = 1; /* backward */
+ data[2] = (steps>>8) & 0xFF;
+ data[3] = steps & 0xFF;
+ break;
+ }
+
+ status->pieusb_status = sanei_pieusb_command(device_number, command, data, size);
+#undef SCAN_HEAD_SIZE
+}
+
+/**
+ * Get internal scanner settings which have resulted from an auto-calibration
+ * procedure. This procedure only runs when calibrating (Scan phase 1), so the
+ * data returned are relatively static.\n
+ * The SCSI code is 0xD7, there is no related command name.
+ *
+ * @param device_number Device number
+ * @param settings Settings for gain and offset for the four colors RGBI
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Settings
+ */
+void
+sanei_pieusb_cmd_get_gain_offset(SANE_Int device_number, struct Pieusb_Settings* settings, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define GAIN_OFFSET_SIZE 103
+ SANE_Int size = GAIN_OFFSET_SIZE;
+ SANE_Byte data[GAIN_OFFSET_SIZE];
+ int k;
+ SANE_Byte val[3];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_get_gain_offset()\n");
+
+ _prep_scsi_cmd (command, SCSI_READ_GAIN_OFFSET, size);
+
+ memset (data, '\0', size);
+ status->pieusb_status = sanei_pieusb_command(device_number, command, data, size);
+ if (status->pieusb_status != PIEUSB_STATUS_GOOD) {
+ return;
+ }
+
+ /* Decode data received */
+ _get_shorts (settings->saturationLevel, data+54, 3);
+ _get_shorts (settings->exposureTime, data+60, 3);
+ _copy_bytes (val, data+66, 3);
+ for (k = 0; k < 3; k++) settings->offset[k] = val[k];
+ _copy_bytes (val, data+72, 3);
+ for (k = 0; k < 3; k++) settings->gain[k] = val[k];
+ settings->light = _get_byte (data, 75);
+ settings->exposureTime[3] = _get_short (data, 98);
+ settings->offset[3] = _get_byte (data, 100);
+ settings->gain[3] = _get_byte (data, 102);
+
+ DBG (DBG_info, "sanei_pieusb_cmd_get_gain_offset() set:\n");
+ DBG (DBG_info, " saturationlevels = %d-%d-%d\n", settings->saturationLevel[0], settings->saturationLevel[1], settings->saturationLevel[2]);
+ DBG (DBG_info, " ---\n");
+ DBG (DBG_info, " exposure times = %d-%d-%d-%d\n", settings->exposureTime[0], settings->exposureTime[1], settings->exposureTime[2], settings->exposureTime[3]);
+ DBG (DBG_info, " gain = %d-%d-%d-%d\n", settings->gain[0], settings->gain[1], settings->gain[2], settings->gain[3]);
+ DBG (DBG_info, " offset = %d-%d-%d-%d\n", settings->offset[0], settings->offset[1], settings->offset[2], settings->offset[3]);
+ DBG (DBG_info, " light = %02x\n", settings->light);
+ DBG (DBG_info, " double times = %02x\n", settings->doubleTimes);
+ DBG (DBG_info, " extra entries = %02x\n", settings->extraEntries);
+#undef GAIN_OFFSET_SIZE
+}
+
+
+/**
+ * Set internal scanner settings such as gain and offset.\n
+ * There are two effective moments for this command:\n
+ * 1. For a scan without calibration phase: before the sanei_pieusb_cmd_start_scan() command;
+ * 2. For a sccan with calibration phase: before (or during) reading the shading reference data.
+ * The SCSI code is 0xDC, there is no related command name.
+ *
+ * @param device_number Device number
+ * @param settings Settings for gain and offset for the four colors RGBI
+ * @return Pieusb_Command_Status
+ * @see Pieusb_Settings
+ */
+void
+sanei_pieusb_cmd_set_gain_offset(SANE_Int device_number, struct Pieusb_Settings* settings, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define GAIN_OFFSET_SIZE 29
+ SANE_Int size = GAIN_OFFSET_SIZE;
+ SANE_Byte data[GAIN_OFFSET_SIZE];
+ int k;
+ SANE_Byte val[3];
+
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_set_gain_offset()\n");
+
+ _prep_scsi_cmd (command, SCSI_WRITE_GAIN_OFFSET, size);
+
+ DBG (DBG_info, "sanei_pieusb_cmd_set_gain_offset() set:\n");
+ DBG (DBG_info, " exposure times = %d-%d-%d-%d\n", settings->exposureTime[0], settings->exposureTime[1], settings->exposureTime[2], settings->exposureTime[3]);
+ DBG (DBG_info, " gain = %d-%d-%d-%d\n", settings->gain[0], settings->gain[1], settings->gain[2], settings->gain[3]);
+ DBG (DBG_info, " offset = %d-%d-%d-%d\n", settings->offset[0], settings->offset[1], settings->offset[2], settings->offset[3]);
+ DBG (DBG_info, " light = %02x\n", settings->light);
+ DBG (DBG_info, " double times = %02x\n", settings->doubleTimes);
+ DBG (DBG_info, " extra entries = %02x\n", settings->extraEntries);
+
+ /* Code data */
+ memset (data, '\0', size);
+ _set_shorts (settings->exposureTime, data, 3);
+ for (k = 0; k < 3; k++) {
+ val[k] = settings->offset[k];
+ }
+ _copy_bytes (data + 6, val, 3);
+ for (k = 0; k < 3; k++) {
+ val[k] = settings->gain[k];
+ }
+ _copy_bytes (data + 12, val, 3);
+ _set_byte (settings->light, data, 15);
+ _set_byte (settings->extraEntries, data, 16);
+ _set_byte (settings->doubleTimes, data, 17);
+ _set_short (settings->exposureTime[3], data, 18);
+ _set_byte (settings->offset[3], data, 20);
+ _set_byte (settings->gain[3], data, 22);
+ /*
+ * pieusb-get_gain_offset:
+ * 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ * 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ * 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ * 00000030: a9 00 88 00 b1 00 00 00 00 00 00 00 04 10 04 10 )...1...........
+ * 00000040: 04 10 53 4f 6e 00 00 00 2e 21 21 05 04 10 df 2d ..SOn....!!..._-
+ * 00000050: a3 5c e7 f2 a1 2c b3 c4 42 df 32 42 eb 82 8e e0 #\gr!,3DB_2Bk..`
+ * 00000060: 87 be 04 10 4f 00 2c .>..O.,
+ *
+ * cyberview:
+ * 00000000: 65 22 57 18 19 19 - exposure time RGB
+ * 00000006: 51 4e 6a - offset RGB
+ * 00000009: 00 00 00
+ * 0000000c: 21 21 21 - gain RGB
+ * 0000000f: 05 - light
+ * 00000010: 01 - extra entries
+ * 00000011: 00 - double times
+ * 00000012: 04 10 - exposure time I
+ * 00000014: 4e 00 - offset I
+ * 00000016: 2a - gain I
+ * 00000017: 00 00 00 00 00 00
+ *
+ * pieusb:
+ * 00000000: 04 10 04 10 04 10 - exposure time RGB
+ * 00000006: 53 4f 6e - offset RGB
+ * 00000009: 00 00 00
+ * 0000000c: 2e 21 21 - gain RGB
+ * 0000000f: 05 - light
+ * 00000010: 00 - extra entries
+ * 00000011: 00 - double times
+ * 00000012: 04 10 - exposure time I
+ * 00000014: 4f 00 - offset I
+ * 00000016: 2c - gain I
+ * 00000017: 00 00 00 00 00 00
+ */
+
+ status->pieusb_status = sanei_pieusb_command(device_number, command, data, size);
+#undef GAIN_OFFSET_SIZE
+}
+
+/**
+ * Get scanner state information: button pushed,
+ * warming up, scanning.
+ *
+ * @param device_number Device number
+ * @param state State information
+ * @return Pieusb_Command_Status
+ */
+void
+sanei_pieusb_cmd_read_state(SANE_Int device_number, struct Pieusb_Scanner_State* state, struct Pieusb_Command_Status *status)
+{
+ SANE_Byte command[SCSI_COMMAND_LEN];
+#define GET_STATE_SIZE 12
+ SANE_Byte data[GET_STATE_SIZE];
+ SANE_Int size = GET_STATE_SIZE;
+
+ /* Execute READ STATUS command */
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_read_state()\n");
+
+ _prep_scsi_cmd (command, SCSI_READ_STATE, size);
+
+ memset (data, '\0', size);
+ status->pieusb_status = sanei_pieusb_command (device_number, command, data, size);
+
+ if (status->pieusb_status == PIEUSB_STATUS_WARMING_UP
+ || status->pieusb_status == PIEUSB_STATUS_DEVICE_BUSY) {
+ data[5] = 1;
+ status->pieusb_status = PIEUSB_STATUS_GOOD;
+ }
+ /* Decode data recieved */
+ state->buttonPushed = _get_byte(data, 0);
+ state->warmingUp = _get_byte(data, 5);
+ state->scanning = _get_byte(data, 6);
+/* state->busy = _get_byte(data, 8); */
+ DBG (DBG_info_scan, "sanei_pieusb_cmd_read_state(): button %d, warmingUp %d, scanning %d, busy? %d\n", state->buttonPushed, state->warmingUp, state->scanning, _get_byte(data, 8));
+#undef GET_STATE_SIZE
+}
+
+/**
+ * Prepare SCSI_COMMAND_LEN-byte command array with command code and size value
+ *
+ * @param command
+ * @param code
+ * @param size
+ */
+static void
+_prep_scsi_cmd(SANE_Byte* command, SANE_Byte code, SANE_Word size)
+{
+ memset(command, '\0', SCSI_COMMAND_LEN);
+ command[0] = code;
+ command[3] = (size>>8) & 0xFF; /* lsb first */
+ command[4] = size & 0xFF;
+}