summaryrefslogtreecommitdiff
path: root/backend/hp-scl.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/hp-scl.c')
-rw-r--r--backend/hp-scl.c2118
1 files changed, 2118 insertions, 0 deletions
diff --git a/backend/hp-scl.c b/backend/hp-scl.c
new file mode 100644
index 0000000..e58508b
--- /dev/null
+++ b/backend/hp-scl.c
@@ -0,0 +1,2118 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Geoffrey T. Dairiki
+ 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 is part of a SANE backend for HP Scanners supporting
+ HP Scanner Control Language (SCL).
+*/
+
+/*
+ $Log$
+ Revision 1.15 2008/03/28 14:37:36 kitno-guest
+ add usleep to improve usb performance, from jim a t meyering d o t net
+
+ Revision 1.14 2004-10-04 18:09:05 kig-guest
+ Rename global function hp_init_openfd to sanei_hp_init_openfd
+
+ Revision 1.13 2004/03/27 13:52:39 kig-guest
+ Keep USB-connection open (was problem with Linux 2.6.x)
+
+ Revision 1.12 2003/10/09 19:34:57 kig-guest
+ Redo when TEST UNIT READY failed
+ Redo when read returns with 0 bytes (non-SCSI only)
+
+
+*/
+
+/*
+#define STUBS
+extern int sanei_debug_hp;*/
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+#include "../include/lalloca.h" /* Must be first */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "../include/lassert.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_pio.h"
+
+#include "hp.h"
+
+#include "../include/sane/sanei_backend.h"
+
+#include "hp-option.h"
+#include "hp-scsi.h"
+#include "hp-scl.h"
+#include "hp-device.h"
+
+#define HP_SCSI_INQ_LEN (36)
+#define HP_SCSI_CMD_LEN (6)
+#define HP_SCSI_BUFSIZ (HP_SCSI_MAX_WRITE + HP_SCSI_CMD_LEN)
+
+#define HP_MAX_OPEN_FD 16
+static struct hp_open_fd_s /* structure to save info about open file descriptor */
+{
+ char *devname;
+ HpConnect connect;
+ int fd;
+} asHpOpenFd[HP_MAX_OPEN_FD];
+
+
+/*
+ *
+ */
+struct hp_scsi_s
+{
+ int fd;
+ char * devname;
+
+ /* Output buffering */
+ hp_byte_t buf[HP_SCSI_BUFSIZ];
+ hp_byte_t * bufp;
+
+ hp_byte_t inq_data[HP_SCSI_INQ_LEN];
+};
+
+#define HP_TMP_BUF_SIZE (1024*4)
+#define HP_WR_BUF_SIZE (1024*4)
+
+typedef struct
+{
+ HpProcessData procdata;
+
+ int outfd;
+ const unsigned char *map;
+
+ unsigned char *image_buf; /* Buffer to store complete image (if req.) */
+ unsigned char *image_ptr;
+ int image_buf_size;
+
+ unsigned char *tmp_buf; /* Buffer for scan data to get even number of bytes */
+ int tmp_buf_size;
+ int tmp_buf_len;
+
+ unsigned char wr_buf[HP_WR_BUF_SIZE];
+ unsigned char *wr_ptr;
+ int wr_buf_size;
+ int wr_left;
+} PROCDATA_HANDLE;
+
+
+/* Initialize structure where we remember out open file descriptors */
+void
+sanei_hp_init_openfd ()
+{int iCount;
+ memset (asHpOpenFd, 0, sizeof (asHpOpenFd));
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ asHpOpenFd[iCount].fd = -1;
+}
+
+
+/* Look if the device is still open */
+static SANE_Status
+hp_GetOpenDevice (const char *devname, HpConnect connect, int *pfd)
+
+{int iCount;
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if (!asHpOpenFd[iCount].devname) continue;
+ if ( (strcmp (asHpOpenFd[iCount].devname, devname) == 0)
+ && (asHpOpenFd[iCount].connect == connect) )
+ {
+ if (pfd) *pfd = asHpOpenFd[iCount].fd;
+ DBG(3, "hp_GetOpenDevice: device %s is open with fd=%d\n", devname,
+ asHpOpenFd[iCount].fd);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_GetOpenDevice: device %s not open\n", devname);
+ return SANE_STATUS_INVAL;
+}
+
+/* Add an open file descriptor. This also decides */
+/* if we keep a connection open or not. */
+static SANE_Status
+hp_AddOpenDevice (const char *devname, HpConnect connect, int fd)
+
+{int iCount, iKeepOpen;
+ static int iInitKeepFlags = 1;
+
+ /* The default values which connections to keep open or not */
+ static int iKeepOpenSCSI = 0;
+ static int iKeepOpenUSB = 1;
+ static int iKeepOpenDevice = 0;
+ static int iKeepOpenPIO = 0;
+
+ if (iInitKeepFlags) /* Change the defaults by environment */
+ {char *eptr;
+
+ iInitKeepFlags = 0;
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_SCSI");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenSCSI = (*eptr == '1');
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_USB");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenUSB = (*eptr == '1');
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_DEVICE");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenDevice = (*eptr == '1');
+
+ eptr = getenv ("SANE_HP_KEEPOPEN_PIO");
+ if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
+ iKeepOpenPIO = (*eptr == '1');
+ }
+
+ /* Look if we should keep it open or not */
+ iKeepOpen = 0;
+ switch (connect)
+ {
+ case HP_CONNECT_SCSI: iKeepOpen = iKeepOpenSCSI;
+ break;
+ case HP_CONNECT_PIO : iKeepOpen = iKeepOpenPIO;
+ break;
+ case HP_CONNECT_USB : iKeepOpen = iKeepOpenUSB;
+ break;
+ case HP_CONNECT_DEVICE : iKeepOpen = iKeepOpenDevice;
+ break;
+ case HP_CONNECT_RESERVE:
+ break;
+ }
+ if (!iKeepOpen)
+ {
+ DBG(3, "hp_AddOpenDevice: %s should not be kept open\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if (!asHpOpenFd[iCount].devname) /* Is this entry free ? */
+ {
+ asHpOpenFd[iCount].devname = sanei_hp_strdup (devname);
+ if (!asHpOpenFd[iCount].devname) return SANE_STATUS_NO_MEM;
+ DBG(3, "hp_AddOpenDevice: added device %s with fd=%d\n", devname, fd);
+ asHpOpenFd[iCount].connect = connect;
+ asHpOpenFd[iCount].fd = fd;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_AddOpenDevice: %s not added\n", devname);
+ return SANE_STATUS_NO_MEM;
+}
+
+
+/* Check if we have remembered an open file descriptor */
+static SANE_Status
+hp_IsOpenFd (int fd, HpConnect connect)
+
+{int iCount;
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if ( (asHpOpenFd[iCount].devname != NULL)
+ && (asHpOpenFd[iCount].fd == fd)
+ && (asHpOpenFd[iCount].connect == connect) )
+ {
+ DBG(3, "hp_IsOpenFd: %d is open\n", fd);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_IsOpenFd: %d not open\n", fd);
+ return SANE_STATUS_INVAL;
+}
+
+
+static SANE_Status
+hp_RemoveOpenFd (int fd, HpConnect connect)
+
+{int iCount;
+
+ for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
+ {
+ if ( (asHpOpenFd[iCount].devname != NULL)
+ && (asHpOpenFd[iCount].fd == fd)
+ && (asHpOpenFd[iCount].connect == connect) )
+ {
+ sanei_hp_free (asHpOpenFd[iCount].devname);
+ asHpOpenFd[iCount].devname = NULL;
+ DBG(3, "hp_RemoveOpenFd: removed %d\n", asHpOpenFd[iCount].fd);
+ asHpOpenFd[iCount].fd = -1;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG(3, "hp_RemoveOpenFd: %d not removed\n", fd);
+ return SANE_STATUS_INVAL;
+}
+
+
+static SANE_Status
+hp_nonscsi_write (HpScsi this, hp_byte_t *data, size_t len, HpConnect connect)
+
+{int n = -1;
+ size_t loc_len;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (len <= 0) return SANE_STATUS_GOOD;
+
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE: /* direct device-io */
+ n = write (this->fd, data, len);
+ break;
+
+ case HP_CONNECT_PIO: /* Use sanepio interface */
+ n = sanei_pio_write (this->fd, data, len);
+ break;
+
+ case HP_CONNECT_USB: /* Not supported */
+ loc_len = len;
+ status = sanei_usb_write_bulk ((SANE_Int)this->fd, data, &loc_len);
+ n = loc_len;
+ break;
+
+ case HP_CONNECT_RESERVE:
+ n = -1;
+ break;
+
+ default:
+ n = -1;
+ break;
+ }
+
+ if (n == 0) return SANE_STATUS_EOF;
+ else if (n < 0) return SANE_STATUS_IO_ERROR;
+
+ return status;
+}
+
+static SANE_Status
+hp_nonscsi_read (HpScsi this, hp_byte_t *data, size_t *len, HpConnect connect,
+ int UNUSEDARG isResponse)
+
+{int n = -1;
+ static int retries = -1;
+ size_t save_len = *len;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ if (*len <= 0) return SANE_STATUS_GOOD;
+
+ if (retries < 0) /* Read environment */
+ {char *eptr = getenv ("SANE_HP_RDREDO");
+
+ retries = 1; /* Set default value */
+ if (eptr != NULL)
+ {
+ if (sscanf (eptr, "%d", &retries) != 1) retries = 1; /* Restore default */
+ else if (retries < 0) retries = 0; /* Allow no retries here */
+ }
+ }
+
+ for (;;) /* Retry on EOF */
+ {
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE:
+ n = read (this->fd, data, *len);
+ break;
+
+ case HP_CONNECT_PIO:
+ n = sanei_pio_read (this->fd, data, *len);
+ break;
+
+ case HP_CONNECT_USB:
+ status = sanei_usb_read_bulk((SANE_Int)this->fd, (SANE_Byte *)data, len);
+ n = *len;
+ break;
+
+ case HP_CONNECT_RESERVE:
+ n = -1;
+ break;
+
+ default:
+ n = -1;
+ break;
+ }
+ if ((n != 0) || (retries <= 0)) break;
+ retries--;
+ usleep (100*1000); /* sleep 0.1 seconds */
+ *len = save_len; /* Restore value */
+ }
+
+ if (n == 0) return SANE_STATUS_EOF;
+ else if (n < 0) return SANE_STATUS_IO_ERROR;
+
+ *len = n;
+ return status;
+}
+
+static SANE_Status
+hp_nonscsi_open (const char *devname, int *fd, HpConnect connect)
+
+{int lfd, flags;
+ SANE_Int dn;
+ SANE_Status status = SANE_STATUS_INVAL;
+
+#ifdef _O_RDWR
+ flags = _O_RDWR;
+#else
+ flags = O_RDWR;
+#endif
+#ifdef _O_EXCL
+ flags |= _O_EXCL;
+#else
+ flags |= O_EXCL;
+#endif
+#ifdef _O_BINARY
+ flags |= _O_BINARY;
+#endif
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE:
+ lfd = open (devname, flags);
+ if (lfd < 0)
+ {
+ DBG(1, "hp_nonscsi_open: open device %s failed (%s)\n", devname,
+ strerror (errno) );
+ status = (errno == EACCES) ? SANE_STATUS_ACCESS_DENIED : SANE_STATUS_INVAL;
+ }
+ else
+ status = SANE_STATUS_GOOD;
+ break;
+
+ case HP_CONNECT_PIO:
+ status = sanei_pio_open (devname, &lfd);
+ break;
+
+ case HP_CONNECT_USB:
+ DBG(17, "hp_nonscsi_open: open usb with \"%s\"\n", devname);
+ status = sanei_usb_open (devname, &dn);
+ lfd = (int)dn;
+ break;
+
+ case HP_CONNECT_RESERVE:
+ status = SANE_STATUS_INVAL;
+ break;
+
+ default:
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "hp_nonscsi_open: open device %s failed\n", devname);
+ }
+ else
+ {
+ DBG(17,"hp_nonscsi_open: device %s opened, fd=%d\n", devname, lfd);
+ }
+
+ if (fd) *fd = lfd;
+
+ return status;
+}
+
+static void
+hp_nonscsi_close (int fd, HpConnect connect)
+
+{
+ switch (connect)
+ {
+ case HP_CONNECT_DEVICE:
+ close (fd);
+ break;
+
+ case HP_CONNECT_PIO:
+ sanei_pio_close (fd);
+ break;
+
+ case HP_CONNECT_USB:
+ sanei_usb_close (fd);
+ break;
+
+ case HP_CONNECT_RESERVE:
+ break;
+
+ default:
+ break;
+ }
+ DBG(17,"hp_nonscsi_close: closed fd=%d\n", fd);
+}
+
+SANE_Status
+sanei_hp_nonscsi_new (HpScsi * newp, const char * devname, HpConnect connect)
+{
+ HpScsi new;
+ SANE_Status status;
+ int iAlreadyOpen = 0;
+
+ new = sanei_hp_allocz(sizeof(*new));
+ if (!new)
+ return SANE_STATUS_NO_MEM;
+
+ /* Is the device already open ? */
+ if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD )
+ {
+ iAlreadyOpen = 1;
+ }
+ else
+ {
+ status = hp_nonscsi_open(devname, &new->fd, connect);
+ if (FAILED(status))
+ {
+ DBG(1, "nonscsi_new: open failed (%s)\n", sane_strstatus(status));
+ sanei_hp_free(new);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* For SCSI-devices we would have the inquire command here */
+ strncpy ((char *)new->inq_data, "\003zzzzzzzHP ------ R000",
+ sizeof (new->inq_data));
+
+ new->bufp = new->buf + HP_SCSI_CMD_LEN;
+ new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 );
+ if ( new->devname ) strcpy (new->devname, devname);
+
+ *newp = new;
+
+ /* Remember the open device */
+ if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd);
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+hp_scsi_close (HpScsi this, int completely)
+{HpConnect connect;
+
+ DBG(3, "scsi_close: closing fd %ld\n", (long)this->fd);
+
+ connect = sanei_hp_scsi_get_connect (this);
+
+ if (!completely) /* May we keep the device open ? */
+ {
+ if ( hp_IsOpenFd (this->fd, connect) == SANE_STATUS_GOOD )
+ {
+ DBG(3, "scsi_close: not closing. Keep open\n");
+ return;
+ }
+
+ }
+ assert(this->fd >= 0);
+
+ if (connect != HP_CONNECT_SCSI)
+ hp_nonscsi_close (this->fd, connect);
+ else
+ sanei_scsi_close (this->fd);
+
+ DBG(3,"scsi_close: really closed\n");
+
+ /* Remove a remembered open device */
+ hp_RemoveOpenFd (this->fd, connect);
+}
+
+
+SANE_Status
+sanei_hp_scsi_new (HpScsi * newp, const char * devname)
+{
+ static hp_byte_t inq_cmd[] = { 0x12, 0, 0, 0, HP_SCSI_INQ_LEN, 0};
+ static hp_byte_t tur_cmd[] = { 0x00, 0, 0, 0, 0, 0};
+ size_t inq_len = HP_SCSI_INQ_LEN;
+ HpScsi new;
+ HpConnect connect;
+ SANE_Status status;
+ int iAlreadyOpen = 0;
+
+ connect = sanei_hp_get_connect (devname);
+
+ if (connect != HP_CONNECT_SCSI)
+ return sanei_hp_nonscsi_new (newp, devname, connect);
+
+ new = sanei_hp_allocz(sizeof(*new));
+ if (!new)
+ return SANE_STATUS_NO_MEM;
+
+ /* Is the device still open ? */
+ if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD )
+ {
+ iAlreadyOpen = 1;
+ }
+ else
+ {
+ status = sanei_scsi_open(devname, &new->fd, 0, 0);
+ if (FAILED(status))
+ {
+ DBG(1, "scsi_new: open failed (%s)\n", sane_strstatus(status));
+ sanei_hp_free(new);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ DBG(3, "scsi_inquire: sending INQUIRE\n");
+ status = sanei_scsi_cmd(new->fd, inq_cmd, 6, new->inq_data, &inq_len);
+ if (FAILED(status))
+ {
+ DBG(1, "scsi_inquire: inquiry failed: %s\n", sane_strstatus(status));
+ sanei_scsi_close(new->fd);
+ sanei_hp_free(new);
+ return status;
+ }
+
+ {char vendor[9], model[17], rev[5];
+ memset (vendor, 0, sizeof (vendor));
+ memset (model, 0, sizeof (model));
+ memset (rev, 0, sizeof (rev));
+ memcpy (vendor, new->inq_data + 8, 8);
+ memcpy (model, new->inq_data + 16, 16);
+ memcpy (rev, new->inq_data + 32, 4);
+
+ DBG(3, "vendor=%s, model=%s, rev=%s\n", vendor, model, rev);
+ }
+
+ DBG(3, "scsi_new: sending TEST_UNIT_READY\n");
+ status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0);
+ if (FAILED(status))
+ {
+ DBG(1, "hp_scsi_open: test unit ready failed (%s)\n",
+ sane_strstatus(status));
+ usleep (500*1000); /* Wait 0.5 seconds */
+ DBG(3, "scsi_new: sending TEST_UNIT_READY second time\n");
+ status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0);
+ }
+
+ if (FAILED(status))
+ {
+ DBG(1, "hp_scsi_open: test unit ready failed (%s)\n",
+ sane_strstatus(status));
+
+ sanei_scsi_close(new->fd);
+ sanei_hp_free(new);
+ return status; /* Fix problem with non-scanner devices */
+ }
+
+ new->bufp = new->buf + HP_SCSI_CMD_LEN;
+ new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 );
+ if ( new->devname ) strcpy (new->devname, devname);
+
+ *newp = new;
+
+ /* Remember the open device */
+ if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* The "completely" parameter was added for OfficeJet support.
+ * For JetDirect connections, closing and re-opening the scan
+ * channel is very time consuming. Also, the OfficeJet G85
+ * unloads a loaded document in the ADF when the scan channel
+ * gets closed. The solution is to "completely" destroy the
+ * connection, including closing and deallocating the PTAL
+ * channel, when initially probing the device in hp-device.c,
+ * but leave it open while the frontend is actually using the
+ * device (from hp-handle.c), and "completely" destroy it when
+ * the frontend closes its handle. */
+void
+sanei_hp_scsi_destroy (HpScsi this,int completely)
+{
+ /* Moved to hp_scsi_close():
+ * assert(this->fd >= 0);
+ * DBG(3, "scsi_close: closing fd %d\n", this->fd);
+ */
+
+ hp_scsi_close (this, completely);
+ if ( this->devname ) sanei_hp_free (this->devname);
+ sanei_hp_free(this);
+}
+
+hp_byte_t *
+sanei_hp_scsi_inq (HpScsi this)
+{
+ return this->inq_data;
+}
+
+const char *
+sanei_hp_scsi_vendor (HpScsi this)
+{
+ static char buf[9];
+ memcpy(buf, sanei_hp_scsi_inq(this) + 8, 8);
+ buf[8] = '\0';
+ return buf;
+}
+
+const char *
+sanei_hp_scsi_model (HpScsi this)
+{
+
+ static char buf[17];
+ memcpy(buf, sanei_hp_scsi_inq(this) + 16, 16);
+ buf[16] = '\0';
+ return buf;
+}
+
+const char *
+sanei_hp_scsi_devicename (HpScsi this)
+{
+ return this->devname;
+}
+
+hp_bool_t
+sanei_hp_is_active_xpa (HpScsi scsi)
+{HpDeviceInfo *info;
+ int model_num;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ if (info->active_xpa < 0)
+ {
+ model_num = sanei_hp_get_max_model (scsi);
+ info->active_xpa = (model_num >= 17);
+ DBG(5,"sanei_hp_is_active_xpa: model=%d, active_xpa=%d\n",
+ model_num, info->active_xpa);
+ }
+ return info->active_xpa;
+}
+
+int
+sanei_hp_get_max_model (HpScsi scsi)
+
+{HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ if (info->max_model < 0)
+ {enum hp_device_compat_e compat;
+ int model_num;
+
+ if ( sanei_hp_device_probe_model ( &compat, scsi, &model_num, 0)
+ == SANE_STATUS_GOOD )
+ info->max_model = model_num;
+ }
+ return info->max_model;
+}
+
+
+int
+sanei_hp_is_flatbed_adf (HpScsi scsi)
+
+{int model = sanei_hp_get_max_model (scsi);
+
+ return ((model == 2) || (model == 4) || (model == 5) || (model == 8));
+}
+
+
+HpConnect
+sanei_hp_get_connect (const char *devname)
+
+{const HpDeviceInfo *info;
+ HpConnect connect = HP_CONNECT_SCSI;
+ int got_connect_type = 0;
+
+ info = sanei_hp_device_info_get (devname);
+ if (!info)
+ {
+ DBG(1, "sanei_hp_get_connect: Could not get info for %s. Assume SCSI\n",
+ devname);
+ connect = HP_CONNECT_SCSI;
+ }
+ else
+ if ( !(info->config_is_up) )
+ {
+ DBG(1, "sanei_hp_get_connect: Config not initialized for %s. Assume SCSI\n",
+ devname);
+ connect = HP_CONNECT_SCSI;
+ }
+ else
+ {
+ connect = info->config.connect;
+ got_connect_type = info->config.got_connect_type;
+ }
+
+ /* Beware of using a USB-device as a SCSI-device (not 100% perfect) */
+ if ((connect == HP_CONNECT_SCSI) && !got_connect_type)
+ {int maybe_usb;
+
+ maybe_usb = ( strstr (devname, "usb")
+ || strstr (devname, "uscanner")
+ || strstr (devname, "ugen"));
+ if (maybe_usb)
+ {static int print_warning = 1;
+
+ if (print_warning)
+ {
+ print_warning = 0;
+ DBG(1,"sanei_hp_get_connect: WARNING\n");
+ DBG(1," Device %s assumed to be SCSI, but device name\n",devname);
+ DBG(1," looks like USB. Will continue with USB.\n");
+ DBG(1," If you really want it as SCSI, add the following\n");
+ DBG(1," to your file .../etc/sane.d/hp.conf:\n");
+ DBG(1," %s\n", devname);
+ DBG(1," option connect-scsi\n");
+ DBG(1," The same warning applies to other device names containing\n");
+ DBG(1," \"usb\", \"uscanner\" or \"ugen\".\n");
+ }
+ connect = HP_CONNECT_DEVICE;
+ }
+ }
+ return connect;
+}
+
+HpConnect
+sanei_hp_scsi_get_connect (HpScsi this)
+
+{
+ return sanei_hp_get_connect (sanei_hp_scsi_devicename (this));
+}
+
+
+static SANE_Status
+hp_scsi_flush (HpScsi this)
+{
+ hp_byte_t * data = this->buf + HP_SCSI_CMD_LEN;
+ size_t len = this->bufp - data;
+ HpConnect connect;
+
+ assert(len < HP_SCSI_MAX_WRITE);
+ if (len == 0)
+ return SANE_STATUS_GOOD;
+
+ this->bufp = this->buf;
+
+ DBG(16, "scsi_flush: writing %lu bytes:\n", (unsigned long) len);
+ DBGDUMP(16, data, len);
+
+ *this->bufp++ = 0x0A;
+ *this->bufp++ = 0;
+ *this->bufp++ = len >> 16;
+ *this->bufp++ = len >> 8;
+ *this->bufp++ = len;
+ *this->bufp++ = 0;
+
+ connect = sanei_hp_scsi_get_connect (this);
+ if (connect == HP_CONNECT_SCSI)
+ return sanei_scsi_cmd (this->fd, this->buf, HP_SCSI_CMD_LEN + len, 0, 0);
+ else
+ return hp_nonscsi_write (this, this->buf+HP_SCSI_CMD_LEN, len, connect);
+}
+
+static size_t
+hp_scsi_room (HpScsi this)
+{
+ return this->buf + HP_SCSI_BUFSIZ - this->bufp;
+}
+
+static SANE_Status
+hp_scsi_need (HpScsi this, size_t need)
+{
+ assert(need < HP_SCSI_MAX_WRITE);
+
+ if (need > hp_scsi_room(this))
+ RETURN_IF_FAIL( hp_scsi_flush(this) );
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_scsi_write (HpScsi this, const void *data, size_t len)
+{
+ if ( len < HP_SCSI_MAX_WRITE )
+ {
+ RETURN_IF_FAIL( hp_scsi_need(this, len) );
+ memcpy(this->bufp, data, len);
+ this->bufp += len;
+ }
+ else
+ {size_t maxwrite = HP_SCSI_MAX_WRITE - 16;
+ const char *c_data = (const char *)data;
+
+ while ( len > 0 )
+ {
+ if ( maxwrite > len ) maxwrite = len;
+ RETURN_IF_FAIL( hp_scsi_write(this, c_data, maxwrite) );
+ c_data += maxwrite;
+ len -= maxwrite;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+hp_scsi_scl(HpScsi this, HpScl scl, int val)
+{
+ char group = tolower(SCL_GROUP_CHAR(scl));
+ char param = toupper(SCL_PARAM_CHAR(scl));
+ int count;
+
+ assert(IS_SCL_CONTROL(scl) || IS_SCL_COMMAND(scl));
+ assert(isprint(group) && isprint(param));
+
+ RETURN_IF_FAIL( hp_scsi_need(this, 10) );
+
+ /* Dont try to optimize SCL-commands like using <ESC>*a1b0c5T */
+ /* Some scanners have problems with it (e.g. HP Photosmart Photoscanner */
+ /* with window position/extent, resolution) */
+ count = sprintf((char *)this->bufp, "\033*%c%d%c", group, val, param);
+ this->bufp += count;
+
+ assert(count > 0 && this->bufp < this->buf + HP_SCSI_BUFSIZ);
+
+ return hp_scsi_flush(this);
+}
+
+/* Read it bytewise */
+static SANE_Status
+hp_scsi_read_slow (HpScsi this, void * dest, size_t *len)
+{static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 };
+ size_t leftover = *len;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char *start_dest = (unsigned char *)dest;
+ unsigned char *next_dest = start_dest;
+
+ DBG(16, "hp_scsi_read_slow: Start reading %d bytes bytewise\n", (int)*len);
+
+ while (leftover > 0) /* Until we got all the bytes */
+ {size_t one = 1;
+
+ read_cmd[2] = 0;
+ read_cmd[3] = 0;
+ read_cmd[4] = 1; /* Read one byte */
+
+ status = sanei_scsi_cmd (this->fd, read_cmd, sizeof(read_cmd),
+ next_dest, &one);
+ if ((status != SANE_STATUS_GOOD) || (one != 1))
+ {
+ DBG(250,"hp_scsi_read_slow: Reading byte %d: status=%s, len=%d\n",
+ (int)(next_dest-start_dest), sane_strstatus(status), (int)one);
+ }
+
+ if (status != SANE_STATUS_GOOD) break; /* Finish on error */
+
+ next_dest++;
+ leftover--;
+ }
+
+ *len = next_dest-start_dest; /* This is the number of bytes we got */
+
+ DBG(16, "hp_scsi_read_slow: Got %d bytes\n", (int)*len);
+
+ if ((status != SANE_STATUS_GOOD) && (*len > 0))
+ {
+ DBG(16, "We got some data. Ignore the error \"%s\"\n",
+ sane_strstatus(status));
+ status = SANE_STATUS_GOOD;
+ }
+ return status;
+}
+
+/* The OfficeJets tend to return inquiry responses containing array
+ * data in two packets. The added "isResponse" parameter tells
+ * whether we should keep reading until we get
+ * a well-formed response. Naturally, this parameter would be zero
+ * when reading scan data. */
+static SANE_Status
+hp_scsi_read (HpScsi this, void * dest, size_t *len, int isResponse)
+{
+ HpConnect connect;
+
+ RETURN_IF_FAIL( hp_scsi_flush(this) );
+
+ connect = sanei_hp_scsi_get_connect (this);
+ if (connect == HP_CONNECT_SCSI)
+ {int read_bytewise = 0;
+
+ if (*len <= 32) /* Is it a candidate for reading bytewise ? */
+ {const HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get (sanei_hp_scsi_devicename (this));
+ if ((info != NULL) && (info->config_is_up) && info->config.dumb_read)
+ read_bytewise = 1;
+ }
+
+ if ( ! read_bytewise )
+ {static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 };
+ read_cmd[2] = *len >> 16;
+ read_cmd[3] = *len >> 8;
+ read_cmd[4] = *len;
+
+ RETURN_IF_FAIL( sanei_scsi_cmd (this->fd, read_cmd,
+ sizeof(read_cmd), dest, len) );
+ }
+ else
+ {
+ RETURN_IF_FAIL (hp_scsi_read_slow (this, dest, len));
+ }
+ }
+ else
+ {
+ RETURN_IF_FAIL( hp_nonscsi_read (this, dest, len, connect, isResponse) );
+ }
+ DBG(16, "scsi_read: %lu bytes:\n", (unsigned long) *len);
+ DBGDUMP(16, dest, *len);
+ return SANE_STATUS_GOOD;
+}
+
+
+static int signal_caught = 0;
+
+static RETSIGTYPE
+signal_catcher (int sig)
+{
+ DBG(1,"signal_catcher(sig=%d): old signal_caught=%d\n",sig,signal_caught);
+ if (!signal_caught)
+ signal_caught = sig;
+}
+
+static void
+hp_data_map (register const unsigned char *map, register int count,
+ register unsigned char *data)
+{
+ if (count <= 0) return;
+ while (count--)
+ {
+ *data = map[*data];
+ data++;
+ }
+}
+
+static const unsigned char *
+hp_get_simulation_map (const char *devname, const HpDeviceInfo *info)
+{
+ hp_bool_t sim_gamma, sim_brightness, sim_contrast;
+ int k, ind;
+ const unsigned char *map = NULL;
+ static unsigned char map8x8[256];
+
+ sim_gamma = info->simulate.gamma_simulate;
+ sim_brightness = sanei_hp_device_simulate_get (devname, SCL_BRIGHTNESS);
+ sim_contrast = sanei_hp_device_simulate_get (devname, SCL_CONTRAST);
+
+ if ( sim_gamma )
+ {
+ map = &(info->simulate.gamma_map[0]);
+ }
+ else if ( sim_brightness && sim_contrast )
+ {
+ for (k = 0; k < 256; k++)
+ {
+ ind = info->simulate.contrast_map[k];
+ map8x8[k] = info->simulate.brightness_map[ind];
+ }
+ map = &(map8x8[0]);
+ }
+ else if ( sim_brightness )
+ map = &(info->simulate.brightness_map[0]);
+ else if ( sim_contrast )
+ map = &(info->simulate.contrast_map[0]);
+
+ return map;
+}
+
+
+/* Check the native byte order on the local machine */
+static hp_bool_t
+is_lowbyte_first_byteorder (void)
+
+{unsigned short testvar = 1;
+ unsigned char *testptr = (unsigned char *)&testvar;
+
+ if (sizeof (unsigned short) == 2)
+ return (testptr[0] == 1);
+ else if (sizeof (unsigned short) == 4)
+ return ((testptr[0] == 1) || (testptr[2] == 1));
+ else
+ return ( (testptr[0] == 1) || (testptr[2] == 1)
+ || (testptr[4] == 1) || (testptr[6] == 1));
+}
+
+/* The SANE standard defines that 2-byte data must use the full 16 bit range.
+ * Byte order returned by the backend must be native byte order.
+ * Scaling to 16 bit and byte order is achived by hp_scale_to_16bit.
+ * for >8 bits data, take the two data bytes and scale their content
+ * to the full 16 bit range, using
+ * scaled = unscaled << (newlen - oldlen) +
+ * unscaled >> (oldlen - (newlen - oldlen)),
+ * with newlen=16 and oldlen the original bit depth.
+ */
+static void
+hp_scale_to_16bit(int count, register unsigned char *data, int depth,
+ hp_bool_t invert)
+{
+ register unsigned int tmp;
+ register unsigned int mask;
+ register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder ();
+ unsigned int shift1 = 16 - depth;
+ unsigned int shift2 = 2*depth - 16;
+ int k;
+
+ if (count <= 0) return;
+
+ mask = 1;
+ for (k = 1; k < depth; k++) mask |= (1 << k);
+
+ if (lowbyte_first)
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp = (tmp << shift1) + (tmp >> shift2);
+ if (invert) tmp = ~tmp;
+ *data++ = tmp & 255U;
+ *data++ = (tmp >> 8) & 255U;
+ }
+ }
+ else /* Highbyte first */
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp = (tmp << shift1) + (tmp >> shift2);
+ if (invert) tmp = ~tmp;
+ *data++ = (tmp >> 8) & 255U;
+ *data++ = tmp & 255U;
+ }
+ }
+}
+
+
+static void
+hp_scale_to_8bit(int count, register unsigned char *data, int depth,
+ hp_bool_t invert)
+{
+ register unsigned int tmp, mask;
+ register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder ();
+ unsigned int shift1 = depth-8;
+ int k;
+ unsigned char *dataout = data;
+
+ if ((count <= 0) || (shift1 <= 0)) return;
+
+ mask = 1;
+ for (k = 1; k < depth; k++) mask |= (1 << k);
+
+ if (lowbyte_first)
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp >>= shift1;
+ if (invert) tmp = ~tmp;
+ *(dataout++) = tmp & 255U;
+ data += 2;
+ }
+ }
+ else /* Highbyte first */
+ {
+ while (count--) {
+ tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
+ tmp >>= shift1;
+ if (invert) tmp = ~tmp;
+ *(dataout++) = tmp & 255U;
+ data += 2;
+ }
+ }
+}
+
+static void
+hp_soft_invert(int count, register unsigned char *data) {
+ while (count>0) {
+ *data = ~(*data);
+ data++;
+ count--;
+ }
+}
+
+static PROCDATA_HANDLE *
+process_data_init (HpProcessData *procdata, const unsigned char *map,
+ int outfd, hp_bool_t use_imgbuf)
+
+{PROCDATA_HANDLE *ph = sanei_hp_alloc (sizeof (PROCDATA_HANDLE));
+ int tsz;
+
+ if (ph == NULL) return NULL;
+
+ memset (ph, 0, sizeof (*ph));
+ memcpy (&(ph->procdata), procdata, sizeof (*procdata));
+ procdata = &(ph->procdata);
+
+ tsz = (HP_TMP_BUF_SIZE <= 0) ? procdata->bytes_per_line : HP_TMP_BUF_SIZE;
+ ph->tmp_buf = sanei_hp_alloc (tsz);
+ if (ph->tmp_buf == NULL)
+ {
+ sanei_hp_free (ph);
+ return NULL;
+ }
+ ph->tmp_buf_size = tsz;
+ ph->tmp_buf_len = 0;
+
+ ph->map = map;
+ ph->outfd = outfd;
+
+ if ( procdata->mirror_vertical || use_imgbuf)
+ {
+ tsz = procdata->lines*procdata->bytes_per_line;
+ if (procdata->out8) tsz /= 2;
+ ph->image_ptr = ph->image_buf = sanei_hp_alloc (tsz);
+ if ( !ph->image_buf )
+ {
+ procdata->mirror_vertical = 0;
+ ph->image_buf_size = 0;
+ DBG(1, "process_scanline_init: Not enough memory to mirror image\n");
+ }
+ else
+ ph->image_buf_size = tsz;
+ }
+
+ ph->wr_ptr = ph->wr_buf;
+ ph->wr_buf_size = ph->wr_left = sizeof (ph->wr_buf);
+
+ return ph;
+}
+
+
+static SANE_Status
+process_data_write (PROCDATA_HANDLE *ph, unsigned char *data, int nbytes)
+
+{int ncopy;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+
+ /* Fill up write buffer */
+ ncopy = ph->wr_left;
+ if (ncopy > nbytes) ncopy = nbytes;
+
+ memcpy (ph->wr_ptr, data, ncopy);
+ ph->wr_ptr += ncopy;
+ ph->wr_left -= ncopy;
+ data += ncopy;
+ nbytes -= ncopy;
+
+ if ( ph->wr_left > 0 ) /* Did not fill up the write buffer ? Finished */
+ return SANE_STATUS_GOOD;
+
+ DBG(12, "process_data_write: write %d bytes\n", ph->wr_buf_size);
+ /* Don't write data if we got a signal in the meantime */
+ if ( signal_caught
+ || (write (ph->outfd, ph->wr_buf, ph->wr_buf_size) != ph->wr_buf_size))
+ {
+ DBG(1, "process_data_write: write failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ ph->wr_ptr = ph->wr_buf;
+ ph->wr_left = ph->wr_buf_size;
+
+ /* For large amount of data write it from data-buffer */
+ while ( nbytes > ph->wr_buf_size )
+ {
+ if ( signal_caught
+ || (write (ph->outfd, data, ph->wr_buf_size) != ph->wr_buf_size))
+ {
+ DBG(1, "process_data_write: write failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ nbytes -= ph->wr_buf_size;
+ data += ph->wr_buf_size;
+ }
+
+ if ( nbytes > 0 ) /* Something left ? Save it to (empty) write buffer */
+ {
+ memcpy (ph->wr_ptr, data, nbytes);
+ ph->wr_ptr += nbytes;
+ ph->wr_left -= nbytes;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+process_scanline (PROCDATA_HANDLE *ph, unsigned char *linebuf,
+ int bytes_per_line)
+
+{int out_bytes_per_line = bytes_per_line;
+ HpProcessData *procdata;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+ procdata = &(ph->procdata);
+
+ if ( ph->map )
+ hp_data_map (ph->map, bytes_per_line, linebuf);
+
+ if (procdata->bits_per_channel > 8)
+ {
+ if (procdata->out8)
+ {
+ hp_scale_to_8bit( bytes_per_line/2, linebuf,
+ procdata->bits_per_channel,
+ procdata->invert);
+ out_bytes_per_line /= 2;
+ }
+ else
+ {
+ hp_scale_to_16bit( bytes_per_line/2, linebuf,
+ procdata->bits_per_channel,
+ procdata->invert);
+ }
+ } else if (procdata->invert) {
+ hp_soft_invert(bytes_per_line,linebuf);
+ }
+
+ if ( ph->image_buf )
+ {
+ DBG(5, "process_scanline: save in memory\n");
+
+ if ( ph->image_ptr+out_bytes_per_line-1
+ <= ph->image_buf+ph->image_buf_size-1 )
+ {
+ memcpy(ph->image_ptr, linebuf, out_bytes_per_line);
+ ph->image_ptr += out_bytes_per_line;
+ }
+ else
+ {
+ DBG(1, "process_scanline: would exceed image buffer\n");
+ }
+ }
+ else /* Save scanlines in a bigger buffer. */
+ { /* Otherwise we will get performance problems */
+
+ RETURN_IF_FAIL ( process_data_write (ph, linebuf, out_bytes_per_line) );
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+process_data (PROCDATA_HANDLE *ph, unsigned char *read_ptr, int nread)
+
+{int bytes_left;
+ HpProcessData *procdata;
+
+ if (nread <= 0) return SANE_STATUS_GOOD;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+
+ procdata = &(ph->procdata);
+ if ( ph->tmp_buf_len > 0 ) /* Something left ? */
+ {
+ bytes_left = ph->tmp_buf_size - ph->tmp_buf_len;
+ if (nread < bytes_left) /* All to buffer ? */
+ {
+ memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, nread);
+ ph->tmp_buf_len += nread;
+ return SANE_STATUS_GOOD;
+ }
+ memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, bytes_left);
+ read_ptr += bytes_left;
+ nread -= bytes_left;
+ RETURN_IF_FAIL ( process_scanline (ph, ph->tmp_buf, ph->tmp_buf_size) );
+ ph->tmp_buf_len = 0;
+ }
+ while (nread > 0)
+ {
+ if (nread >= ph->tmp_buf_size)
+ {
+ RETURN_IF_FAIL ( process_scanline (ph, read_ptr, ph->tmp_buf_size) );
+ read_ptr += ph->tmp_buf_size;
+ nread -= ph->tmp_buf_size;
+ }
+ else
+ {
+ memcpy (ph->tmp_buf, read_ptr, nread);
+ ph->tmp_buf_len = nread;
+ nread = 0;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+static SANE_Status
+process_data_flush (PROCDATA_HANDLE *ph)
+
+{SANE_Status status = SANE_STATUS_GOOD;
+ HpProcessData *procdata;
+ unsigned char *image_data;
+ size_t image_len;
+ int num_lines, bytes_per_line;
+ int nbytes;
+
+ if (ph == NULL) return SANE_STATUS_INVAL;
+
+ if ( ph->tmp_buf_len > 0 )
+ process_scanline (ph, ph->tmp_buf, ph->tmp_buf_len);
+
+ if ( ph->wr_left != ph->wr_buf_size ) /* Something in write buffer ? */
+ {
+ nbytes = ph->wr_buf_size - ph->wr_left;
+ if ( signal_caught || (write (ph->outfd, ph->wr_buf, nbytes) != nbytes))
+ {
+ DBG(1, "process_data_flush: write failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ ph->wr_ptr = ph->wr_buf;
+ ph->wr_left = ph->wr_buf_size;
+ }
+
+ procdata = &(ph->procdata);
+ if ( ph->image_buf )
+ {
+ bytes_per_line = procdata->bytes_per_line;
+ if (procdata->out8) bytes_per_line /= 2;
+ image_len = (size_t) (ph->image_ptr - ph->image_buf);
+ num_lines = ((int)(image_len + bytes_per_line-1)) / bytes_per_line;
+
+ DBG(3, "process_data_finish: write %d bytes from memory...\n",
+ (int)image_len);
+
+ if ( procdata->mirror_vertical )
+ {
+ image_data = ph->image_buf + (num_lines-1) * bytes_per_line;
+ while (num_lines > 0 )
+ {
+ if ( signal_caught
+ || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line))
+ {
+ DBG(1,"process_data_finish: write from memory failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ num_lines--;
+ image_data -= bytes_per_line;
+ }
+ }
+ else
+ {
+ image_data = ph->image_buf;
+ while (num_lines > 0 )
+ {
+ if ( signal_caught
+ || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line))
+ {
+ DBG(1,"process_data_finish: write from memory failed: %s\n",
+ signal_caught ? "signal caught" : strerror(errno));
+ status = SANE_STATUS_IO_ERROR;
+ break;
+ }
+ num_lines--;
+ image_data += bytes_per_line;
+ }
+ }
+ }
+ return status;
+}
+
+
+static void
+process_data_finish (PROCDATA_HANDLE *ph)
+
+{
+ DBG(12, "process_data_finish called\n");
+
+ if (ph == NULL) return;
+
+ if (ph->image_buf != NULL) sanei_hp_free (ph->image_buf);
+
+ sanei_hp_free (ph->tmp_buf);
+ sanei_hp_free (ph);
+}
+
+
+SANE_Status
+sanei_hp_scsi_pipeout (HpScsi this, int outfd, HpProcessData *procdata)
+{
+ /* We will catch these signals, and rethrow them after cleaning up,
+ * anything not in this list, we will ignore. */
+ static int kill_sig[] = {
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGPIPE, SIGALRM, SIGTERM,
+ SIGUSR1, SIGUSR2, SIGBUS,
+#ifdef SIGSTKFLT
+ SIGSTKFLT,
+#endif
+#ifdef SIGIO
+ SIGIO,
+#else
+# ifdef SIGPOLL
+ SIGPOLL,
+# endif
+#endif
+#ifdef SIGXCPU
+ SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ SIGXFSZ,
+#endif
+#ifdef SIGVTALRM
+ SIGVTALRM,
+#endif
+#ifdef SIGPWR
+ SIGPWR,
+#endif
+ };
+#define HP_NSIGS (sizeof(kill_sig)/sizeof(kill_sig[0]))
+ struct SIGACTION old_handler[HP_NSIGS];
+ struct SIGACTION sa;
+ sigset_t old_set, sig_set;
+ int i;
+ int bits_per_channel = procdata->bits_per_channel;
+
+#define HP_PIPEBUF 32768
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct {
+ size_t len;
+ void * id;
+ hp_byte_t cmd[6];
+ hp_byte_t data[HP_PIPEBUF];
+ } buf[2], *req = NULL;
+
+ int reqs_completed = 0;
+ int reqs_issued = 0;
+ char *image_buf = 0;
+ char *read_buf = 0;
+ const HpDeviceInfo *info;
+ const char *devname = sanei_hp_scsi_devicename (this);
+ int enable_requests = 1;
+ int enable_image_buffering = 0;
+ const unsigned char *map = NULL;
+ HpConnect connect;
+ PROCDATA_HANDLE *ph = NULL;
+ size_t count = procdata->lines * procdata->bytes_per_line;
+
+ RETURN_IF_FAIL( hp_scsi_flush(this) );
+
+ connect = sanei_hp_get_connect (devname);
+ info = sanei_hp_device_info_get (devname);
+
+ assert (info);
+
+ if ( info->config_is_up )
+ {
+ enable_requests = info->config.use_scsi_request;
+ enable_image_buffering = info->config.use_image_buffering;
+ }
+ else
+ {
+ enable_requests = 0;
+ }
+
+ if (connect != HP_CONNECT_SCSI)
+ enable_requests = 0;
+
+ /* Currently we can only simulate 8 bits mapping */
+ if (bits_per_channel == 8)
+ map = hp_get_simulation_map (devname, info);
+
+ sigfillset(&sig_set);
+ sigprocmask(SIG_BLOCK, &sig_set, &old_set);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_catcher;
+ sigfillset(&sa.sa_mask);
+
+ sigemptyset(&sig_set);
+ for (i = 0; i < (int)(HP_NSIGS); i++)
+ {
+ sigaction(kill_sig[i], &sa, &old_handler[i]);
+ sigaddset(&sig_set, kill_sig[i]);
+ }
+ signal_caught = 0;
+ sigprocmask(SIG_UNBLOCK, &sig_set, 0);
+
+ /* Wait for front button push ? */
+ if ( procdata->startscan )
+ {
+ for (;;)
+ {int val = 0;
+
+ if (signal_caught) goto quit;
+ sanei_hp_scl_inquire (this, SCL_FRONT_BUTTON, &val, 0, 0);
+ if (val) break;
+ usleep ((unsigned long)333*1000); /* Wait 1/3 second */
+ }
+ status = sanei_hp_scl_startScan (this, procdata->startscan);
+ if (status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "do_read: Error starting scan in reader process\n");
+ goto quit;
+ }
+ }
+ ph = process_data_init (procdata, map, outfd, enable_image_buffering);
+
+ if ( ph == NULL )
+ {
+ DBG(1, "do_read: Error with process_data_init()\n");
+ goto quit;
+ }
+
+ DBG(1, "do_read: Start reading data from scanner\n");
+
+ if (enable_requests) /* Issue SCSI-requests ? */
+ {
+ while (count > 0 || reqs_completed < reqs_issued)
+ {
+ while (count > 0 && reqs_issued < reqs_completed + 2)
+ {
+ req = buf + (reqs_issued++ % 2);
+
+ req->len = HP_PIPEBUF;
+ if (count < req->len)
+ req->len = count;
+ count -= req->len;
+
+ req->cmd[0] = 0x08;
+ req->cmd[1] = 0;
+ req->cmd[2] = req->len >> 16;
+ req->cmd[3] = req->len >> 8;
+ req->cmd[4] = req->len;
+ req->cmd[5] = 0;
+
+ DBG(3, "do_read: entering request to read %lu bytes\n",
+ (unsigned long) req->len);
+
+ status = sanei_scsi_req_enter(this->fd, req->cmd, 6,
+ req->data, &req->len, &req->id);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "do_read: Error from scsi_req_enter: %s\n",
+ sane_strstatus(status));
+ goto quit;
+ }
+ if (signal_caught)
+ goto quit;
+ }
+
+ if (signal_caught)
+ goto quit;
+
+ assert(reqs_completed < reqs_issued);
+ req = buf + (reqs_completed++ % 2);
+
+ DBG(3, "do_read: waiting for data\n");
+ status = sanei_scsi_req_wait(req->id);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "do_read: Error from scsi_req_wait: %s\n",
+ sane_strstatus(status));
+ goto quit;
+ }
+ if (signal_caught)
+ goto quit;
+
+ status = process_data (ph, (unsigned char *)req->data, (int)req->len);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1,"do_read: Error in process_data\n");
+ goto quit;
+ }
+ }
+ }
+ else /* Read directly */
+ {
+ read_buf = sanei_hp_alloc ( HP_PIPEBUF );
+ if (!read_buf)
+ {
+ DBG(1, "do_read: not enough memory for read buffer\n");
+ goto quit;
+ }
+
+ while (count > 0)
+ {size_t nread;
+
+ if (signal_caught)
+ goto quit;
+
+ DBG(5, "do_read: %lu bytes left to read\n", (unsigned long)count);
+
+ nread = HP_PIPEBUF;
+ if (nread > count) nread = count;
+
+ DBG(3, "do_read: try to read data (%lu bytes)\n", (unsigned long)nread);
+
+ status = hp_scsi_read (this, read_buf, &nread, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(1, "do_read: Error from scsi_read: %s\n",sane_strstatus(status));
+ goto quit;
+ }
+
+ DBG(3, "do_read: got %lu bytes\n", (unsigned long)nread);
+
+ if (nread <= 0)
+ {
+ DBG(1, "do_read: Nothing read\n");
+ continue;
+ }
+
+ status = process_data (ph, (unsigned char *)read_buf, (int)nread);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1,"do_read: Error in process_data\n");
+ goto quit;
+ }
+ count -= nread;
+ }
+ }
+
+ process_data_flush (ph);
+
+quit:
+
+ process_data_finish (ph);
+
+ if ( image_buf ) sanei_hp_free ( image_buf );
+ if ( read_buf ) sanei_hp_free ( read_buf );
+
+ if (enable_requests && (reqs_completed < reqs_issued))
+ {
+ DBG(1, "do_read: cleaning up leftover requests\n");
+ while (reqs_completed < reqs_issued)
+ {
+ req = buf + (reqs_completed++ % 2);
+ sanei_scsi_req_wait(req->id);
+ }
+ }
+
+ sigfillset(&sig_set);
+ sigprocmask(SIG_BLOCK, &sig_set, 0);
+ for (i = 0; i < (int)(HP_NSIGS); i++)
+ sigaction(kill_sig[i], &old_handler[i], 0);
+ sigprocmask(SIG_SETMASK, &old_set, 0);
+
+ if (signal_caught)
+ {
+ DBG(1, "do_read: caught signal %d\n", signal_caught);
+ raise(signal_caught);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ return status;
+}
+
+
+
+/*
+ *
+ */
+
+static SANE_Status
+_hp_scl_inq (HpScsi scsi, HpScl scl, HpScl inq_cmnd,
+ void *valp, size_t *lengthp)
+{
+ size_t bufsize = 16 + (lengthp ? *lengthp: 0);
+ char * buf = alloca(bufsize);
+ char expect[16], expect_char;
+ int val, count;
+ SANE_Status status;
+
+ if (!buf)
+ return SANE_STATUS_NO_MEM;
+
+ /* Flush data before sending inquiry. */
+ /* Otherwise scanner might not generate a response. */
+ RETURN_IF_FAIL( hp_scsi_flush (scsi)) ;
+
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, inq_cmnd, SCL_INQ_ID(scl)) );
+ usleep (1000); /* 500 works, too, but not 100 */
+
+ status = hp_scsi_read(scsi, buf, &bufsize, 1);
+ if (FAILED(status))
+ {
+ DBG(1, "scl_inq: read failed (%s)\n", sane_strstatus(status));
+ return status;
+ }
+
+ if (SCL_PARAM_CHAR(inq_cmnd) == 'R')
+ expect_char = 'p';
+ else
+ expect_char = tolower(SCL_PARAM_CHAR(inq_cmnd) - 1);
+
+ count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char);
+ if (memcmp(buf, expect, count) != 0)
+ {
+ DBG(1, "scl_inq: malformed response: expected '%s', got '%.*s'\n",
+ expect, count, buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ if (buf[0] == 'N')
+ { /* null response */
+ DBG(3, "scl_inq: parameter %d unsupported\n", SCL_INQ_ID(scl));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (sscanf(buf, "%d%n", &val, &count) != 1)
+ {
+ DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ expect_char = lengthp ? 'W' : 'V';
+ if (*buf++ != expect_char)
+ {
+ DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n",
+ expect_char, buf - 1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (!lengthp)
+ *(int *)valp = val; /* Get integer value */
+ else
+ {
+ if (val > (int)*lengthp)
+ {
+ DBG(1, "scl_inq: inquiry returned %d bytes, expected <= %lu\n",
+ val, (unsigned long) *lengthp);
+ return SANE_STATUS_IO_ERROR;
+ }
+ *lengthp = val;
+ memcpy(valp, buf , *lengthp); /* Get binary data */
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sanei_hp_scl_upload_binary (HpScsi scsi, HpScl scl, size_t *lengthhp,
+ char **bufhp)
+{
+ size_t bufsize = 16, sv;
+ char * buf = alloca(bufsize);
+ char * bufstart = buf;
+ char * hpdata;
+ char expect[16], expect_char;
+ int n, val, count;
+ SANE_Status status;
+
+ if (!buf)
+ return SANE_STATUS_NO_MEM;
+
+ assert ( IS_SCL_DATA_TYPE (scl) );
+
+ /* Flush data before sending inquiry. */
+ /* Otherwise scanner might not generate a response. */
+ RETURN_IF_FAIL( hp_scsi_flush (scsi)) ;
+
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_UPLOAD_BINARY_DATA, SCL_INQ_ID(scl)) );
+
+ status = hp_scsi_read(scsi, buf, &bufsize, 0);
+ if (FAILED(status))
+ {
+ DBG(1, "scl_upload_binary: read failed (%s)\n", sane_strstatus(status));
+ return status;
+ }
+
+ expect_char = 't';
+ count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char);
+ if (memcmp(buf, expect, count) != 0)
+ {
+ DBG(1, "scl_upload_binary: malformed response: expected '%s', got '%.*s'\n",
+ expect, count, buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ if (buf[0] == 'N')
+ { /* null response */
+ DBG(1, "scl_upload_binary: parameter %d unsupported\n", SCL_INQ_ID(scl));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (sscanf(buf, "%d%n", &val, &count) != 1)
+ {
+ DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf);
+ return SANE_STATUS_IO_ERROR;
+ }
+ buf += count;
+
+ expect_char = 'W';
+ if (*buf++ != expect_char)
+ {
+ DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n",
+ expect_char, buf - 1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ *lengthhp = val;
+ *bufhp = hpdata = sanei_hp_alloc ( val );
+ if (!hpdata)
+ return SANE_STATUS_NO_MEM;
+
+ if (buf < bufstart + bufsize)
+ {
+ n = bufsize - (buf - bufstart);
+ if (n > val) n = val;
+ memcpy (hpdata, buf, n);
+ hpdata += n;
+ val -= n;
+ }
+
+ status = SANE_STATUS_GOOD;
+ if ( val > 0 )
+ {
+ sv = val;
+ status = hp_scsi_read(scsi, hpdata, &sv, 0);
+ if (status != SANE_STATUS_GOOD)
+ sanei_hp_free ( *bufhp );
+ }
+
+ return status;
+}
+
+
+SANE_Status
+sanei_hp_scl_set(HpScsi scsi, HpScl scl, int val)
+{
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, val) );
+
+
+#ifdef PARANOID
+ RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_scl_inquire(HpScsi scsi, HpScl scl, int * valp, int * minp, int * maxp)
+{
+ HpScl inquiry = ( IS_SCL_CONTROL(scl)
+ ? SCL_INQUIRE_PRESENT_VALUE
+ : SCL_INQUIRE_DEVICE_PARAMETER );
+
+ assert(IS_SCL_CONTROL(scl) || IS_SCL_PARAMETER(scl));
+ assert(IS_SCL_CONTROL(scl) || (!minp && !maxp));
+
+ if (valp)
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, 0) );
+ if (minp)
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl,
+ SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
+ if (maxp)
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl,
+ SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) );
+ return SANE_STATUS_GOOD;
+}
+
+#ifdef _HP_NOT_USED
+static SANE_Status
+hp_scl_get_bounds(HpScsi scsi, HpScl scl, int * minp, int * maxp)
+{
+ assert(IS_SCL_CONTROL(scl));
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
+ return _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0);
+}
+#endif
+
+#ifdef _HP_NOT_USED
+static SANE_Status
+hp_scl_get_bounds_and_val(HpScsi scsi, HpScl scl,
+ int * minp, int * maxp, int * valp)
+{
+ assert(IS_SCL_CONTROL(scl));
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) );
+ return _hp_scl_inq(scsi, scl, SCL_INQUIRE_PRESENT_VALUE, valp, 0);
+}
+#endif
+
+SANE_Status
+sanei_hp_scl_download(HpScsi scsi, HpScl scl, const void * valp, size_t len)
+{
+ assert(IS_SCL_DATA_TYPE(scl));
+
+ sanei_hp_scl_clearErrors ( scsi );
+ RETURN_IF_FAIL( hp_scsi_need(scsi, 16) );
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_TYPE, SCL_INQ_ID(scl)) );
+ /* Download type not supported ? */
+ RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_LENGTH, len) );
+ RETURN_IF_FAIL( hp_scsi_write(scsi, valp, len) );
+
+#ifdef PARANOID
+ RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_scl_upload(HpScsi scsi, HpScl scl, void * valp, size_t len)
+{
+ size_t nread = len;
+ HpScl inquiry = ( IS_SCL_DATA_TYPE(scl)
+ ? SCL_UPLOAD_BINARY_DATA
+ : SCL_INQUIRE_DEVICE_PARAMETER );
+
+ assert(IS_SCL_DATA_TYPE(scl) || IS_SCL_PARAMETER(scl));
+
+ RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, &nread) );
+ if (IS_SCL_PARAMETER(scl) && nread < len)
+ ((char *)valp)[nread] = '\0';
+ else if (len != nread)
+ {
+ DBG(1, "scl_upload: requested %lu bytes, got %lu\n",
+ (unsigned long) len, (unsigned long) nread);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_scl_calibrate(HpScsi scsi)
+{
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_CALIBRATE, 0) );
+ return hp_scsi_flush(scsi);
+}
+
+SANE_Status
+sanei_hp_scl_startScan(HpScsi scsi, HpScl scl)
+{
+ char *msg = "";
+
+ if (scl == SCL_ADF_SCAN) msg = " (ADF)";
+ else if (scl == SCL_XPA_SCAN) msg = " (XPA)";
+ else scl = SCL_START_SCAN;
+
+ DBG(1, "sanei_hp_scl_startScan: Start scan%s\n", msg);
+
+ /* For active XPA we must not use XPA scan */
+ if ((scl == SCL_XPA_SCAN) && sanei_hp_is_active_xpa (scsi))
+ {
+ DBG(3,"Map XPA scan to scan because of active XPA\n");
+ scl = SCL_START_SCAN;
+ }
+
+ RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, 0) );
+ return hp_scsi_flush(scsi);
+}
+
+SANE_Status
+sanei_hp_scl_reset(HpScsi scsi)
+{
+ RETURN_IF_FAIL( hp_scsi_write(scsi, "\033E", 2) );
+ RETURN_IF_FAIL( hp_scsi_flush(scsi) );
+ return sanei_hp_scl_errcheck(scsi);
+}
+
+SANE_Status
+sanei_hp_scl_clearErrors(HpScsi scsi)
+{
+ RETURN_IF_FAIL( hp_scsi_flush(scsi) );
+ RETURN_IF_FAIL( hp_scsi_write(scsi, "\033*oE", 4) );
+ return hp_scsi_flush(scsi);
+}
+
+static const char *
+hp_scl_strerror (int errnum)
+{
+ static const char * errlist[] = {
+ "Command Format Error",
+ "Unrecognized Command",
+ "Parameter Error",
+ "Illegal Window",
+ "Scaling Error",
+ "Dither ID Error",
+ "Tone Map ID Error",
+ "Lamp Error",
+ "Matrix ID Error",
+ "Cal Strip Param Error",
+ "Gross Calibration Error"
+ };
+
+ if (errnum >= 0 && errnum < (int)(sizeof(errlist)/sizeof(errlist[0])))
+ return errlist[errnum];
+ else
+ switch(errnum) {
+ case 1024: return "ADF Paper Jam";
+ case 1025: return "Home Position Missing";
+ case 1026: return "Paper Not Loaded";
+ default: return "??Unkown Error??";
+ }
+}
+
+/* Check for SCL errors */
+SANE_Status
+sanei_hp_scl_errcheck (HpScsi scsi)
+{
+ int errnum;
+ int nerrors;
+ SANE_Status status;
+
+ status = sanei_hp_scl_inquire(scsi, SCL_CURRENT_ERROR_STACK, &nerrors,0,0);
+ if (!FAILED(status) && nerrors)
+ status = sanei_hp_scl_inquire(scsi, SCL_OLDEST_ERROR, &errnum,0,0);
+ if (FAILED(status))
+ {
+ DBG(1, "scl_errcheck: Can't read SCL error stack: %s\n",
+ sane_strstatus(status));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (nerrors)
+ {
+ DBG(1, "Scanner issued SCL error: (%d) %s\n",
+ errnum, hp_scl_strerror(errnum));
+
+ sanei_hp_scl_clearErrors (scsi);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}