summaryrefslogtreecommitdiff
path: root/backend/mustek_scsi_pp.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/mustek_scsi_pp.c')
-rw-r--r--backend/mustek_scsi_pp.c1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/backend/mustek_scsi_pp.c b/backend/mustek_scsi_pp.c
new file mode 100644
index 0000000..3dab544
--- /dev/null
+++ b/backend/mustek_scsi_pp.c
@@ -0,0 +1,1086 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2003 James Perry
+ 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.
+
+ This file implements the Mustek SCSI-over-parallel port protocol
+ used by, for example, the Paragon 600 II EP
+*/
+
+
+/**************************************************************************/
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <time.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_pa4s2.h"
+
+/*
+ * Number of times to retry sending a SCSI command before giving up
+ */
+#define MUSTEK_SCSI_PP_NUM_RETRIES 4
+
+/*
+ * Internal middle-level API functionality
+ */
+static int mustek_scsi_pp_timeout = 5000;
+
+/* FIXME: use same method as mustek.c ? */
+static int
+mustek_scsi_pp_get_time ()
+{
+ struct timeval tv;
+ int retval;
+
+ gettimeofday (&tv, 0);
+
+ retval = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+ return retval;
+}
+
+static u_char mustek_scsi_pp_register = 0;
+
+
+static SANE_Status
+mustek_scsi_pp_select_register (int fd, u_char reg)
+{
+ DBG (5, "mustek_scsi_pp_select_register: selecting register %d on fd %d\n",
+ reg, fd);
+
+ mustek_scsi_pp_register = reg;
+
+ return sanei_pa4s2_scsi_pp_reg_select (fd, reg);
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_valid_status (int fd)
+{
+ int start_time;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_valid_status: entering\n");
+
+ start_time = mustek_scsi_pp_get_time ();
+
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_wait_for_valid_status: I/O error while getting status\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status &= 0xf0;
+
+ if ((status != 0xf0) && (!(status & 0x40)) && (status & 0x20))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_valid_status: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - start_time) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_valid_status: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_5_set (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_set: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x20)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_5_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_set: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_5_clear (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_clear: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (!(status & 0x20))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_5_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_clear: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_7_set (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_set: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x80)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_7_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_set: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_7_clear (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_clear: entering\n");
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (!(status & 0x80))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_7_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_clear: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_4_set (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_set: entering\n");
+
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (status & 0x10)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x40)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: bit 6 set\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x10)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_set: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_4_clear (int fd)
+{
+ int t;
+ u_char status;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_clear: entering\n");
+
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (!(status & 0x10))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ t = mustek_scsi_pp_get_time ();
+ do
+ {
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: I/O error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (status & 0x40)
+ {
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: bit 6 set\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (!(status & 0x10))
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_clear: returning success\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout);
+
+ DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: timed out\n");
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+static u_char mustek_scsi_pp_bit_4_state = 0;
+
+static SANE_Status
+mustek_scsi_pp_wait_for_status_bit_4_toggle (int fd)
+{
+ SANE_Status result;
+
+ DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_toggle: entering\n");
+
+ mustek_scsi_pp_bit_4_state ^= 0xff;
+ if (mustek_scsi_pp_bit_4_state)
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_toggle: waiting for set\n");
+ result = mustek_scsi_pp_wait_for_status_bit_4_set (fd);
+ mustek_scsi_pp_timeout = 5000;
+ }
+ else
+ {
+ DBG (5,
+ "mustek_scsi_pp_wait_for_status_bit_4_toggle: waiting for clear\n");
+ result = mustek_scsi_pp_wait_for_status_bit_4_clear (fd);
+ }
+
+ return result;
+}
+
+static SANE_Status
+mustek_scsi_pp_send_command_byte (int fd, u_char cmd)
+{
+ DBG (5, "mustek_scsi_pp_send_command byte: sending 0x%02X\n", cmd);
+
+ mustek_scsi_pp_select_register (fd, 0);
+
+ if (mustek_scsi_pp_wait_for_status_bit_7_clear (fd) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_select_register (fd, 0);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (sanei_pa4s2_writebyte (fd, mustek_scsi_pp_register, cmd) !=
+ SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ mustek_scsi_pp_select_register (fd, 1);
+
+ if (mustek_scsi_pp_wait_for_status_bit_7_set (fd) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_select_register (fd, 0);
+ return SANE_STATUS_IO_ERROR;
+ }
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (5, "mustek_scsi_pp_send_command_byte: returning success\n");
+ return SANE_STATUS_GOOD;
+}
+
+static u_char
+mustek_scsi_pp_read_response (int fd)
+{
+ u_char result;
+
+ DBG (5, "mustek_scsi_pp_read_response: entering\n");
+
+ if (mustek_scsi_pp_wait_for_status_bit_7_set (fd) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_select_register (fd, 0);
+ return 0xff;
+ }
+
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD)
+ {
+ return 0xff;
+ }
+ if (sanei_pa4s2_readbyte (fd, &result) != SANE_STATUS_GOOD)
+ {
+ return 0xff;
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ return 0xff;
+ }
+
+ mustek_scsi_pp_select_register (fd, 1);
+ if (mustek_scsi_pp_wait_for_status_bit_7_clear (fd) != SANE_STATUS_GOOD)
+ {
+ result = 0xff;
+ }
+ mustek_scsi_pp_select_register (fd, 0);
+
+ DBG (5, "mustek_scsi_pp_read_response: returning 0x%02X\n", result);
+ return result;
+}
+
+static SANE_Status
+mustek_scsi_pp_check_response (int fd)
+{
+ if (mustek_scsi_pp_wait_for_status_bit_5_clear (fd) != SANE_STATUS_GOOD)
+ {
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (mustek_scsi_pp_read_response (fd) != 0xA5)
+ {
+ DBG (2, "mustek_scsi_pp_check_response: response!=0xA5\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "mustek_scsi_pp_check_response: returning success\n");
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+mustek_scsi_pp_send_command (int fd, const u_char * cmd)
+{
+ int i;
+ signed char checksum;
+
+ DBG (5, "mustek_scsi_pp_send_command: sending SCSI command 0x%02X\n",
+ cmd[0]);
+
+ /* Set timeout depending on command type */
+ switch (cmd[0])
+ {
+ case 0xf:
+ case 0x8:
+ mustek_scsi_pp_timeout = 1000;
+ break;
+ case 0x2:
+ mustek_scsi_pp_timeout = 80;
+ break;
+ case 0x12:
+ case 0x3:
+ case 0x11:
+ mustek_scsi_pp_timeout = 500;
+ break;
+ default:
+ mustek_scsi_pp_timeout = 1000;
+ break;
+ }
+
+ if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_command: timed out waiting for bit 5 to set\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ checksum = 0;
+ for (i = 0; i < 6; i++)
+ {
+ if (mustek_scsi_pp_send_command_byte (fd, cmd[i]) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_command: error sending byte %d (0x%02X)\n",
+ i, cmd[i]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ checksum += cmd[i];
+ }
+ if (mustek_scsi_pp_send_command_byte (fd, -checksum) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_command: error sending checksum (0x%02X)\n",
+ -checksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return mustek_scsi_pp_check_response (fd);
+}
+
+static SANE_Status
+mustek_scsi_pp_send_data_block (int fd, const u_char * data, int len)
+{
+ int i;
+ signed char checksum;
+
+ DBG (5, "mustek_scsi_pp_send_data_block: sending block of length %d\n",
+ len);
+
+ if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_data_block: timed out waiting for bit 5 to set\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ checksum = 0;
+ for (i = 0; i < len; i++)
+ {
+ if (mustek_scsi_pp_send_command_byte (fd, data[i]) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_data_block: error sending byte %d (0x%02X)\n",
+ i, data[i]);
+ return SANE_STATUS_IO_ERROR;
+ }
+ checksum += data[i];
+ }
+ if (mustek_scsi_pp_send_command_byte (fd, -checksum) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_send_data_block: error sending checksum (0x%02X)\n",
+ -checksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return mustek_scsi_pp_check_response (fd);
+}
+
+static SANE_Status
+mustek_scsi_pp_read_data_block (int fd, u_char * buffer, int len)
+{
+ int i;
+ signed char checksum;
+
+ DBG (5, "mustek_scsi_pp_read_data_block: reading block of length %d\n",
+ len);
+
+ if (mustek_scsi_pp_wait_for_status_bit_5_clear (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_read_data_block: timed out waiting for bit 5 to clear\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ checksum = 0;
+ for (i = 0; i < len; i++)
+ {
+ buffer[i] = mustek_scsi_pp_read_response (fd);
+ checksum += buffer[i];
+ }
+ if ((signed char) mustek_scsi_pp_read_response (fd) != (-checksum))
+ {
+ mustek_scsi_pp_send_command_byte (fd, 0xff);
+ DBG (2, "mustek_scsi_pp_read_data_block: checksums do not match\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_read_data_block: error waiting for bit 5 to set\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (mustek_scsi_pp_send_command_byte (fd, 0) != SANE_STATUS_GOOD)
+ {
+ mustek_scsi_pp_send_command_byte (fd, 0xff);
+ DBG (2, "mustek_scsi_pp_read_data_block: error sending final 0 byte\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "mustek_scsi_pp_read_data_block: returning success\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/*
+ * Externally visible functions
+ */
+SANE_Status
+mustek_scsi_pp_open (const char *dev, int *fd)
+{
+ SANE_Status status;
+
+ status = sanei_pa4s2_scsi_pp_open (dev, fd);
+ if (status == SANE_STATUS_GOOD)
+ {
+ DBG (5, "mustek_scsi_pp_open: device %s opened as fd %d\n", dev, *fd);
+ }
+ else
+ {
+ DBG (2, "mustek_scsi_pp_open: error opening device %s\n", dev);
+ }
+ return status;
+}
+
+static void
+mustek_scsi_pp_close (int fd)
+{
+ DBG (5, "mustek_scsi_pp_close: closing fd %d\n", fd);
+ sanei_pa4s2_close (fd);
+}
+
+static void
+mustek_scsi_pp_exit (void)
+{
+ DBG (5, "mustek_scsi_pp_exit: entering\n");
+}
+
+static SANE_Status
+mustek_scsi_pp_test_ready (int fd)
+{
+ u_char status;
+ SANE_Status retval;
+
+ DBG (5, "mustek_scsi_pp_test_ready: entering with fd=%d\n", fd);
+
+ if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_test_ready: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_test_ready: error getting status\n");
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ return SANE_STATUS_INVAL;
+ }
+
+ retval = SANE_STATUS_GOOD;
+
+ status &= 0xf0;
+
+ if (status == 0xf0)
+ {
+ retval = SANE_STATUS_DEVICE_BUSY;
+ }
+ if (status & 0x40)
+ {
+ retval = SANE_STATUS_DEVICE_BUSY;
+ }
+ if (!(status & 0x20))
+ {
+ retval = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_test_ready: error disabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (retval == SANE_STATUS_GOOD)
+ {
+ DBG (5, "mustek_scsi_pp_test_ready: returning SANE_STATUS_GOOD\n");
+ }
+ else
+ {
+ DBG (5,
+ "mustek_scsi_pp_test_ready: returning SANE_STATUS_DEVICE_BUSY\n");
+ }
+
+ return retval;
+}
+
+static SANE_Status
+mustek_scsi_pp_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ SANE_Status stat;
+ int num_tries = 0;
+ static u_char scan_options = 0;
+ const u_char *cmd;
+ u_char stop_cmd[6] = { 0x1b, 0, 0, 0, 0, 0 };
+ int max_tries;
+
+ max_tries = MUSTEK_SCSI_PP_NUM_RETRIES;
+
+ cmd = (const u_char *) src;
+
+ DBG (5, "mustek_scsi_pp_cmd: sending command 0x%02X to device %d\n",
+ cmd[0], fd);
+
+ if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (cmd[0] == 0x1b)
+ {
+ if (!(cmd[4] & 0x1))
+ {
+ unsigned char c;
+ int i;
+
+ DBG (5, "mustek_scsi_pp_cmd: doing stop-specific stuff\n");
+
+ /*
+ * Remembers what flags were sent with a 'start' command, and
+ * replicate them with a stop command.
+ */
+ stop_cmd[4] = scan_options & 0xfe;
+ cmd = &stop_cmd[0];
+
+ /*
+ * In color mode at least, the scanner doesn't seem to like stopping at
+ * the end. It's a bit of a horrible hack, but reading loads of bytes and
+ * allowing 20 tries for the stop command is the only way I've found that
+ * solves the problem.
+ */
+ max_tries = 20;
+
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error in readbegin for stop\n");
+ }
+
+ for (i = 0; i < 10000; i++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &c) != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_cmd: error reading byte for stop\n");
+ break;
+ }
+ DBG (5, "mustek_scsi_pp_cmd: successfully read byte %d\n", i);
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error in readend for stop\n");
+ }
+ }
+ }
+
+ if (cmd[0] == 0x08)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: doing read-specific stuff\n");
+ mustek_scsi_pp_timeout = 30000;
+ mustek_scsi_pp_bit_4_state = 0xff;
+ }
+
+ /*
+ * Send the command itself in one block, then any extra input data in a second
+ * block. Not sure if that's necessary.
+ */
+ if (src_size < 6)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_cmd: source size is only %lu (<6)\n", (u_long) src_size);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * Retry the command several times, as occasionally it doesn't
+ * work first time.
+ */
+ do
+ {
+ stat = mustek_scsi_pp_send_command (fd, cmd);
+ num_tries++;
+ }
+ while ((stat != SANE_STATUS_GOOD) && (num_tries < max_tries));
+
+ if (stat != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_cmd: sending command failed\n");
+ return stat;
+ }
+
+ if (src_size > 6)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: sending data block of length %lu\n",
+ (u_long) (src_size - 6));
+
+ stat =
+ mustek_scsi_pp_send_data_block (fd, ((const u_char *) src) + 6,
+ src_size - 6);
+ if (stat != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_cmd: sending data block failed\n");
+ return stat;
+ }
+ }
+
+
+ if (dst)
+ {
+ unsigned int length;
+
+ /* check buffer is big enough to receive data */
+ length = (cmd[3] << 8) | cmd[4];
+
+ DBG (5, "mustek_scsi_pp_cmd: reading %d bytes\n", length);
+
+ if (length > *dst_size)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_cmd: buffer (size %lu) not big enough for data (size %d)\n",
+ (u_long) *dst_size, length);
+ return SANE_STATUS_INVAL;
+ }
+
+ stat = mustek_scsi_pp_read_data_block (fd, dst, length);
+ if (stat != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error reading data block\n");
+ }
+ }
+
+ if (cmd[0] == 0x1b)
+ {
+ if (cmd[4] & 0x1)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: doing start-specific stuff\n");
+
+ scan_options = cmd[4];
+
+ /* 'Start' command - wait for valid status */
+ mustek_scsi_pp_timeout = 70000;
+ stat = mustek_scsi_pp_wait_for_valid_status (fd);
+
+ if (stat != SANE_STATUS_GOOD)
+ {
+ DBG (2,
+ "mustek_scsi_pp_cmd: error waiting for valid status after start\n");
+ }
+ }
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_cmd: error disabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (stat == SANE_STATUS_GOOD)
+ {
+ DBG (5, "mustek_scsi_pp_cmd: returning success\n");
+ }
+
+ return stat;
+}
+
+static SANE_Status
+mustek_scsi_pp_rdata (int fd, int planes, SANE_Byte * buf, int lines, int bpl)
+{
+ int i, j;
+
+ DBG (5,
+ "mustek_scsi_pp_rdata: reading %d lines at %d bpl, %d planes from %d\n",
+ lines, bpl, planes, fd);
+
+ if ((planes != 1) && (planes != 3))
+ {
+ DBG (2, "mustek_scsi_pp_rdata: invalid number of planes (%d)\n",
+ planes);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_rdata: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ for (i = 0; i < lines; i++)
+ {
+ if (planes == 3)
+ {
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for red, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readbegin for red, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ for (j = 0; j < (bpl / 3); j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j]) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading red byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readend for red, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for green, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readbegin for green, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ for (j = 0; j < (bpl / 3); j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j + (bpl / 3)]) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading green byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readend for green, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for blue, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readbegin for blue, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ for (j = 0; j < (bpl / 3); j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j + (2 * (bpl / 3))]) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading blue byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error in readend for blue, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else
+ {
+ if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error waiting for bit 4 toggle, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) !=
+ SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_rdata: error in readbegin, line %d\n",
+ i);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ for (j = 0; j < bpl; j++)
+ {
+ if (sanei_pa4s2_readbyte (fd, &buf[j]) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_readend (fd);
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2,
+ "mustek_scsi_pp_rdata: error reading byte, line %d, byte %d\n",
+ i, j);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD)
+ {
+ sanei_pa4s2_enable (fd, SANE_FALSE);
+ DBG (2, "mustek_scsi_pp_rdata: error in readend, line %d\n", i);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ buf += bpl;
+ }
+
+ if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD)
+ {
+ DBG (2, "mustek_scsi_pp_rdata: error enabling scanner\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "mustek_scsi_pp_rdata: returning success\n");
+ return SANE_STATUS_GOOD;
+}