summaryrefslogtreecommitdiff
path: root/backend/ibm-scsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/ibm-scsi.c')
-rw-r--r--backend/ibm-scsi.c439
1 files changed, 439 insertions, 0 deletions
diff --git a/backend/ibm-scsi.c b/backend/ibm-scsi.c
new file mode 100644
index 0000000..59b42cb
--- /dev/null
+++ b/backend/ibm-scsi.c
@@ -0,0 +1,439 @@
+/* sane - Scanner Access Now Easy.
+
+ 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>
+
+
+/* SCSI commands that the Ibm scanners understand: */
+#define IBM_SCSI_TEST_UNIT_READY 0x00
+#define IBM_SCSI_SET_WINDOW 0x24
+#define IBM_SCSI_GET_WINDOW 0x25
+#define IBM_SCSI_READ_SCANNED_DATA 0x28
+#define IBM_SCSI_INQUIRY 0x12
+#define IBM_SCSI_MODE_SELECT 0x15
+#define IBM_SCSI_START_SCAN 0x1b
+#define IBM_SCSI_MODE_SENSE 0x1a
+#define IBM_SCSI_GET_BUFFER_STATUS 0x34
+#define IBM_SCSI_OBJECT_POSITION 0x31
+
+/* How long do we wait for scanner to have data for us */
+#define MAX_WAITING_TIME 15
+
+/* for object_position command */
+#define OBJECT_POSITION_UNLOAD 0
+#define OBJECT_POSITION_LOAD 1
+
+struct scsi_window_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte reserved[4];
+ SANE_Byte len[3];
+ SANE_Byte control;
+};
+
+struct scsi_mode_select_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ SANE_Byte page_code; /* for mode_sense, reserved for mode_select */
+ SANE_Byte unused[1];
+ SANE_Byte len;
+ SANE_Byte control;
+};
+
+struct scsi_mode_header {
+ SANE_Byte data_length; /* Sense data length */
+ SANE_Byte medium_type;
+ SANE_Byte dev_spec;
+ SANE_Byte blk_desc_len;
+};
+
+/* next struct introduced by mf */
+struct scsi_object_position_cmd {
+ SANE_Byte opcode;
+ SANE_Byte position_func;
+ SANE_Byte count[3];
+ SANE_Byte res[3];
+ SANE_Byte control;
+ SANE_Byte res2;
+};
+
+struct scsi_get_buffer_status_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte res[5];
+ SANE_Byte len[2];
+ SANE_Byte control;
+};
+
+struct scsi_status_desc {
+ SANE_Byte window_id;
+ SANE_Byte byte2;
+ SANE_Byte available[3];
+ SANE_Byte filled[3];
+};
+
+struct scsi_status_data {
+ SANE_Byte len[3];
+ SANE_Byte byte4;
+ struct scsi_status_desc desc;
+};
+
+struct scsi_start_scan_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte unused[2];
+ SANE_Byte len;
+ SANE_Byte control;
+};
+
+struct scsi_read_scanner_cmd {
+ SANE_Byte opcode;
+ SANE_Byte byte2;
+ SANE_Byte data_type;
+ SANE_Byte byte3;
+ SANE_Byte data_type_qualifier[2];
+ SANE_Byte len[3];
+ SANE_Byte control;
+};
+
+static SANE_Status
+test_unit_ready (int fd)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (11, ">> test_unit_ready\n");
+
+ cmd[0] = IBM_SCSI_TEST_UNIT_READY;
+ memset (cmd, 0, sizeof (cmd));
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< test_unit_ready\n");
+ return (status);
+}
+
+static SANE_Status
+inquiry (int fd, void *buf, size_t * buf_size)
+{
+ static SANE_Byte cmd[6];
+ SANE_Status status;
+ DBG (11, ">> inquiry\n");
+
+ memset (cmd, 0, sizeof (cmd));
+ cmd[0] = IBM_SCSI_INQUIRY;
+ cmd[4] = *buf_size;
+ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, "<< inquiry\n");
+ return (status);
+}
+
+static SANE_Status
+mode_select (int fd, struct mode_pages *mp)
+{
+ static struct {
+ struct scsi_mode_select_cmd cmd;
+ struct scsi_mode_header smh;
+ struct mode_pages mp;
+ } select_cmd;
+ SANE_Status status;
+ DBG (11, ">> mode_select\n");
+
+ memset (&select_cmd, 0, sizeof (select_cmd));
+ select_cmd.cmd.opcode = IBM_SCSI_MODE_SELECT;
+ select_cmd.cmd.byte2 |= SMS_PF;
+ select_cmd.cmd.len = sizeof(select_cmd.smh) + sizeof(select_cmd.mp);
+/* next line by mf */
+/* select_cmd.cmd.page_code= 20; */
+ memcpy (&select_cmd.mp, mp, sizeof(*mp));
+ status = sanei_scsi_cmd (fd, &select_cmd, sizeof (select_cmd), 0, 0);
+
+ DBG (11, "<< mode_select\n");
+ return (status);
+}
+
+#if 0
+static SANE_Status
+mode_sense (int fd, struct mode_pages *mp, SANE_Byte page_code)
+{
+ static struct scsi_mode_select_cmd cmd; /* no type, we can reuse it for sensing */
+ static struct {
+ struct scsi_mode_header smh;
+ struct mode_pages mp;
+ } select_data;
+ static size_t select_size = sizeof(select_data);
+ SANE_Status status;
+ DBG (11, ">> mode_sense\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_MODE_SENSE;
+ cmd.page_code = page_code;
+ cmd.len = sizeof(select_data);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &select_data, &select_size);
+ memcpy (mp, &select_data.mp, sizeof(*mp));
+
+ DBG (11, "<< mode_sense\n");
+ return (status);
+}
+#endif
+
+static SANE_Status
+trigger_scan (int fd)
+{
+ static struct scsi_start_scan_cmd cmd;
+ static char window_id_list[1] = { '\0' }; /* scan start data out */
+ static size_t wl_size = 1;
+ SANE_Status status;
+ DBG (11, ">> trigger scan\n");
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_START_SCAN;
+ cmd.len = wl_size;
+/* next line by mf */
+/* cmd.unused[0] = 1; */
+ if (wl_size)
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &window_id_list, &wl_size);
+ else
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< trigger scan\n");
+ return (status);
+}
+
+static SANE_Status
+set_window (int fd, struct ibm_window_data *iwd)
+{
+
+ static struct {
+ struct scsi_window_cmd cmd;
+ struct ibm_window_data iwd;
+ } win;
+
+ SANE_Status status;
+ DBG (11, ">> set_window\n");
+
+ memset (&win, 0, sizeof (win));
+ win.cmd.opcode = IBM_SCSI_SET_WINDOW;
+ _lto3b(sizeof(*iwd), win.cmd.len);
+ memcpy (&win.iwd, iwd, sizeof(*iwd));
+ status = sanei_scsi_cmd (fd, &win, sizeof (win), 0, 0);
+
+ DBG (11, "<< set_window\n");
+ return (status);
+}
+
+static SANE_Status
+get_window (int fd, struct ibm_window_data *iwd)
+{
+
+ static struct scsi_window_cmd cmd;
+ static size_t iwd_size;
+ SANE_Status status;
+
+ iwd_size = sizeof(*iwd);
+ DBG (11, ">> get_window datalen = %lu\n", (unsigned long) iwd_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_GET_WINDOW;
+#if 1 /* it was if 0 */
+ cmd.byte2 |= (SANE_Byte)0x01; /* set Single bit to get one window desc. */
+#endif
+ _lto3b(iwd_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), iwd, &iwd_size);
+
+ DBG (11, "<< get_window, datalen = %lu\n", (unsigned long) iwd_size);
+ return (status);
+}
+
+static SANE_Status
+read_data (int fd, void *buf, size_t * buf_size)
+{
+ static struct scsi_read_scanner_cmd cmd;
+ SANE_Status status;
+ DBG (11, ">> read_data %lu\n", (unsigned long) *buf_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_READ_SCANNED_DATA;
+ _lto3b(*buf_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size);
+
+ DBG (11, "<< read_data %lu\n", (unsigned long) *buf_size);
+ return (status);
+}
+
+static SANE_Status
+object_position (int fd, int load)
+{
+ static struct scsi_object_position_cmd cmd;
+ SANE_Status status;
+ DBG (11, ">> object_position\n");
+
+#if 0
+ /* At least the Ricoh 420 doesn't like that command */
+ DBG (11, "object_position: ignored\n");
+ return SANE_STATUS_GOOD;
+#endif
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_OBJECT_POSITION;
+ if (load)
+ cmd.position_func = OBJECT_POSITION_LOAD;
+ else
+ cmd.position_func = OBJECT_POSITION_UNLOAD;
+ _lto3b(1, cmd.count);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0);
+
+ DBG (11, "<< object_position\n");
+ return (status);
+}
+
+static SANE_Status
+get_data_status (int fd, struct scsi_status_desc *dbs)
+{
+ static struct scsi_get_buffer_status_cmd cmd;
+ static struct scsi_status_data ssd;
+ size_t ssd_size = sizeof(ssd);
+ SANE_Status status;
+ DBG (11, ">> get_data_status %lu\n", (unsigned long) ssd_size);
+
+ memset (&cmd, 0, sizeof (cmd));
+ cmd.opcode = IBM_SCSI_GET_BUFFER_STATUS;
+ _lto2b(ssd_size, cmd.len);
+ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &ssd, &ssd_size);
+
+ memcpy (dbs, &ssd.desc, sizeof(*dbs));
+ if (status == SANE_STATUS_GOOD &&
+ ((unsigned int) _3btol(ssd.len) <= sizeof(*dbs) || _3btol(ssd.desc.filled) == 0)) {
+ DBG (11, "get_data_status: busy\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ DBG (11, "<< get_data_status %lu\n", (unsigned long) ssd_size);
+ return (status);
+}
+
+#if 0
+static SANE_Status
+ibm_wait_ready_tur (int fd)
+{
+ struct timeval now, start;
+ SANE_Status status;
+
+ gettimeofday (&start, 0);
+
+ while (1)
+ {
+ DBG(3, "scsi_wait_ready: sending TEST_UNIT_READY\n");
+
+ status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0);
+ 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(1, "scsi_wait_ready: test unit ready failed (%s)\n",
+ sane_strstatus (status));
+ /* fall through */
+ case SANE_STATUS_DEVICE_BUSY:
+ gettimeofday (&now, 0);
+ if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
+ {
+ DBG(1, "ibm_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now.tv_sec - start.tv_sec));
+ return SANE_STATUS_INVAL;
+ }
+ usleep (100000); /* retry after 100ms */
+ break;
+
+ case SANE_STATUS_GOOD:
+ return status;
+ }
+ }
+ return SANE_STATUS_INVAL;
+}
+#endif
+
+static SANE_Status
+ibm_wait_ready (Ibm_Scanner * s)
+{
+ struct scsi_status_desc dbs;
+ 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(1, "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(1, "ibm_wait_ready: timed out after %lu seconds\n",
+ (u_long) (now - start));
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_STATUS_GOOD:
+ DBG(11, "ibm_wait_ready: %d bytes ready\n", _3btol(dbs.filled));
+ return status;
+ break;
+ }
+ usleep (1000000); /* retry after 100ms */
+ }
+ return SANE_STATUS_INVAL;
+}