summaryrefslogtreecommitdiff
path: root/backend/epson2-io.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/epson2-io.c')
-rw-r--r--backend/epson2-io.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/backend/epson2-io.c b/backend/epson2-io.c
new file mode 100644
index 0000000..13adef4
--- /dev/null
+++ b/backend/epson2-io.c
@@ -0,0 +1,392 @@
+/*
+ * I/O routines for Epson scanners
+ *
+ * Based on Kazuhiro Sasayama previous
+ * Work on epson.[ch] file from the SANE package.
+ * Please see those files for original copyrights.
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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, version 2.
+ */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "sane/config.h"
+
+#include <ctype.h>
+
+#include "epson2.h"
+#include "epson2-io.h"
+
+#include "sane/sanei_scsi.h"
+#include "sane/sanei_usb.h"
+#include "sane/sanei_pio.h"
+#include "sane/sanei_tcp.h"
+
+#include "epson2_scsi.h"
+#include "epson_usb.h"
+#include "epson2_net.h"
+
+#include "byteorder.h"
+
+/* flaming hack to get USB scanners
+ * working without timeouts under linux
+ * (cribbed from fujitsu.c)
+ */
+unsigned int r_cmd_count = 0;
+unsigned int w_cmd_count = 0;
+
+int
+e2_send(Epson_Scanner * s, void *buf, size_t buf_size, size_t reply_len,
+ SANE_Status * status)
+{
+ DBG(15, "%s: size = %lu, reply = %lu\n",
+ __func__, (u_long) buf_size, (u_long) reply_len);
+
+ if (buf_size == 2) {
+ char *cmd = buf;
+
+ switch (cmd[0]) {
+ case ESC:
+ DBG(9, "%s: ESC %c\n", __func__, cmd[1]);
+ break;
+
+ case FS:
+ DBG(9, "%s: FS %c\n", __func__, cmd[1]);
+ break;
+ }
+ }
+
+ if (DBG_LEVEL >= 125) {
+ unsigned int k;
+ const unsigned char *s = buf;
+
+ for (k = 0; k < buf_size; k++) {
+ DBG(125, "buf[%d] %02x %c\n", k, s[k],
+ isprint(s[k]) ? s[k] : '.');
+ }
+ }
+
+ if (s->hw->connection == SANE_EPSON_NET) {
+ if (reply_len == 0) {
+ DBG(0,
+ "Cannot send this command to a networked scanner\n");
+ return SANE_STATUS_INVAL;
+ }
+ return sanei_epson_net_write(s, 0x2000, buf, buf_size,
+ reply_len, status);
+ } else if (s->hw->connection == SANE_EPSON_SCSI) {
+ return sanei_epson2_scsi_write(s->fd, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_EPSON_PIO) {
+ size_t n;
+
+ if (buf_size == (n = sanei_pio_write(s->fd, buf, buf_size)))
+ *status = SANE_STATUS_GOOD;
+ else
+ *status = SANE_STATUS_INVAL;
+
+ return n;
+
+ } else if (s->hw->connection == SANE_EPSON_USB) {
+ size_t n;
+ n = buf_size;
+ *status = sanei_usb_write_bulk(s->fd, buf, &n);
+ w_cmd_count++;
+ DBG(20, "%s: cmd count, r = %d, w = %d\n",
+ __func__, r_cmd_count, w_cmd_count);
+
+ return n;
+ }
+
+ *status = SANE_STATUS_INVAL;
+ return 0;
+ /* never reached */
+}
+
+ssize_t
+e2_recv(Epson_Scanner * s, void *buf, ssize_t buf_size,
+ SANE_Status * status)
+{
+ ssize_t n = 0;
+
+ DBG(15, "%s: size = %ld, buf = %p\n", __func__, (long) buf_size, buf);
+
+ if (s->hw->connection == SANE_EPSON_NET) {
+ n = sanei_epson_net_read(s, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_EPSON_SCSI) {
+ n = sanei_epson2_scsi_read(s->fd, buf, buf_size, status);
+ } else if (s->hw->connection == SANE_EPSON_PIO) {
+ if (buf_size ==
+ (n = sanei_pio_read(s->fd, buf, (size_t) buf_size)))
+ *status = SANE_STATUS_GOOD;
+ else
+ *status = SANE_STATUS_INVAL;
+ } else if (s->hw->connection == SANE_EPSON_USB) {
+ /* !!! only report an error if we don't read anything */
+ n = buf_size; /* buf_size gets overwritten */
+ *status =
+ sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf,
+ (size_t *) & n);
+ r_cmd_count += (n + 63) / 64; /* add # of packets, rounding up */
+ DBG(20, "%s: cmd count, r = %d, w = %d\n",
+ __func__, r_cmd_count, w_cmd_count);
+
+ if (n > 0)
+ *status = SANE_STATUS_GOOD;
+ }
+
+ if (n < buf_size) {
+ DBG(1, "%s: expected = %lu, got = %ld\n", __func__,
+ (u_long) buf_size, (long) n);
+ *status = SANE_STATUS_IO_ERROR;
+ }
+
+ /* dump buffer if appropriate */
+ if (DBG_LEVEL >= 127 && n > 0) {
+ int k;
+ const unsigned char *s = buf;
+
+ for (k = 0; k < n; k++)
+ DBG(127, "buf[%d] %02x %c\n", k, s[k],
+ isprint(s[k]) ? s[k] : '.');
+ }
+
+ return n;
+}
+
+/* Simple function to exchange a fixed amount of
+ * data with the scanner
+ */
+
+SANE_Status
+e2_txrx(Epson_Scanner * s, unsigned char *txbuf, size_t txlen,
+ unsigned char *rxbuf, size_t rxlen)
+{
+ SANE_Status status;
+
+ e2_send(s, txbuf, txlen, rxlen, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+
+ e2_recv(s, rxbuf, rxlen, &status);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status));
+ }
+
+ return status;
+}
+
+/* This function should be used to send codes that only requires the scanner
+ * to give back an ACK or a NAK.
+ */
+SANE_Status
+e2_cmd_simple(Epson_Scanner * s, void *buf, size_t buf_size)
+{
+ unsigned char result;
+ SANE_Status status;
+
+ DBG(12, "%s: size = %lu\n", __func__, (u_long) buf_size);
+
+ status = e2_txrx(s, buf, buf_size, &result, 1);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: failed, %s\n", __func__, sane_strstatus(status));
+ return status;
+ }
+
+ if (result == ACK)
+ return SANE_STATUS_GOOD;
+
+ if (result == NAK) {
+ DBG(3, "%s: NAK\n", __func__);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(1, "%s: result is neither ACK nor NAK but 0x%02x\n", __func__,
+ result);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* receives a 4 or 6 bytes information block from the scanner*/
+SANE_Status
+e2_recv_info_block(Epson_Scanner * s, unsigned char *scanner_status,
+ size_t info_size, size_t * payload_size)
+{
+ SANE_Status status;
+ unsigned char info[6];
+
+ if (s->hw->connection == SANE_EPSON_PIO)
+ e2_recv(s, info, 1, &status);
+ else
+ e2_recv(s, info, info_size, &status);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ /* check for explicit NAK */
+ if (info[0] == NAK) {
+ DBG(1, "%s: command not supported\n", __func__);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* check the first byte: if it's not STX, bail out */
+ if (info[0] != STX) {
+ DBG(1, "%s: expecting STX, got %02X\n", __func__, info[0]);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* if connection is PIO read the remaining bytes. */
+ if (s->hw->connection == SANE_EPSON_PIO) {
+ e2_recv(s, &info[1], info_size - 1, &status);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (scanner_status)
+ *scanner_status = info[1];
+
+ if (payload_size) {
+ *payload_size = le16atoh(&info[2]);
+
+ if (info_size == 6)
+ *payload_size *= le16atoh(&info[4]);
+
+ DBG(14, "%s: payload length: %lu\n", __func__,
+ (u_long) *payload_size);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/* This function can be called for commands that
+ * will be answered by the scanner with an info block of 4 bytes
+ * and a variable payload. The payload is passed back to the caller
+ * in **buf. The caller must free it if != NULL,
+ * even if the status != SANE_STATUS_GOOD.
+ */
+
+SANE_Status
+e2_cmd_info_block(SANE_Handle handle, unsigned char *params,
+ unsigned char params_len, size_t reply_len,
+ unsigned char **buf, size_t * buf_len)
+{
+ SANE_Status status;
+ Epson_Scanner *s = (Epson_Scanner *) handle;
+ size_t len;
+
+ DBG(13, "%s, params len = %d, reply len = %lu, buf = %p\n",
+ __func__, params_len, (u_long) reply_len, (void *) buf);
+
+ if (buf == NULL)
+ return SANE_STATUS_INVAL;
+
+ /* initialize */
+ *buf = NULL;
+
+ /* send command, we expect the info block + reply_len back */
+ e2_send(s, params, params_len,
+ reply_len ? reply_len + 4 : 0, &status);
+
+ if (status != SANE_STATUS_GOOD)
+ goto end;
+
+ status = e2_recv_info_block(s, NULL, 4, &len);
+ if (status != SANE_STATUS_GOOD)
+ goto end;
+
+ /* do we need to provide the length of the payload? */
+ if (buf_len)
+ *buf_len = len;
+
+ /* no payload, stop here */
+ if (len == 0)
+ goto end;
+
+ /* if a reply_len has been specified and the actual
+ * length differs, throw a warning
+ */
+ if (reply_len && (len != reply_len)) {
+ DBG(1, "%s: mismatched len - expected %lu, got %lu\n",
+ __func__, (u_long) reply_len, (u_long) len);
+ }
+
+ /* allocate and receive the payload */
+ *buf = malloc(len);
+
+ if (*buf) {
+ memset(*buf, 0x00, len);
+ e2_recv(s, *buf, len, &status); /* receive actual data */
+ } else
+ status = SANE_STATUS_NO_MEM;
+ end:
+
+ if (status != SANE_STATUS_GOOD) {
+ DBG(1, "%s: failed, %s\n", __func__, sane_strstatus(status));
+
+ if (*buf) {
+ free(*buf);
+ *buf = NULL;
+ }
+ }
+
+ return status;
+}
+
+
+/* This is used for ESC commands with a single byte parameter. Scanner
+ * will answer with ACK/NAK.
+ */
+SANE_Status
+e2_esc_cmd(Epson_Scanner * s, unsigned char cmd, unsigned char val)
+{
+ SANE_Status status;
+ unsigned char params[2];
+
+ DBG(8, "%s: cmd = 0x%02x, val = %d\n", __func__, cmd, val);
+ if (!cmd)
+ return SANE_STATUS_UNSUPPORTED;
+
+ params[0] = ESC;
+ params[1] = cmd;
+
+ status = e2_cmd_simple(s, params, 2);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ params[0] = val;
+
+ return e2_cmd_simple(s, params, 1);
+}
+
+/* Send an ACK to the scanner */
+
+SANE_Status
+e2_ack(Epson_Scanner * s)
+{
+ SANE_Status status;
+ e2_send(s, S_ACK, 1, 0, &status);
+ return status;
+}
+
+SANE_Status
+e2_ack_next(Epson_Scanner * s, size_t reply_len)
+{
+ SANE_Status status;
+ e2_send(s, S_ACK, 1, reply_len, &status);
+ return status;
+}
+
+SANE_Status
+e2_cancel(Epson_Scanner * s)
+{
+ DBG(1, "%s\n", __func__);
+ return e2_cmd_simple(s, S_CAN, 1);
+}