summaryrefslogtreecommitdiff
path: root/backend/umax-usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/umax-usb.c')
-rw-r--r--backend/umax-usb.c324
1 files changed, 324 insertions, 0 deletions
diff --git a/backend/umax-usb.c b/backend/umax-usb.c
new file mode 100644
index 0000000..c26aeae
--- /dev/null
+++ b/backend/umax-usb.c
@@ -0,0 +1,324 @@
+/* ---------------------------------------------------------------------- */
+
+/* sane - Scanner Access Now Easy.
+
+ umax-usb.c
+
+ (C) 2001-2002 Frank Zago
+
+ 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 a SANE backend for UMAX USB flatbed scanners. */
+
+
+/* ---------------------------------------------------------------------- */
+
+#include "../include/sane/sanei_usb.h"
+
+#include "../include/sane/sanei_pv8630.h"
+
+/* USB specific parts */
+
+/* Apparently this will recover from some errors. */
+static void pv8630_mini_init_scanner(int fd)
+{
+ DBG(DBG_info, "mini_init_scanner\n");
+
+ /* (re-)init the device (?) */
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04 );
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02 );
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02 );
+
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+}
+
+/* Length of the CDB given the SCSI command. The last two are not
+ correct (vendor reserved). */
+static u_char cdb_sizes[8] = {
+ 6, 10, 10, 6, 16, 12, 0, 0
+};
+#define CDB_SIZE(opcode) cdb_sizes[(((opcode) >> 5) & 7)]
+
+/* Sends a CDB to the scanner. Also sends the parameters and receives
+ * the data, if necessary. When this function returns with a
+ * SANE_STATUS_GOOD, the SCSI command has been completed.
+ *
+ * Note: I don't know about deferred commands.
+ */
+static SANE_Status sanei_umaxusb_cmd(int fd, const void *src, size_t src_size, void *dst, size_t * dst_size)
+{
+ unsigned char result;
+ size_t cmd_size = CDB_SIZE (*(const char *) src);
+ size_t param_size = src_size - cmd_size;
+ const char * param_ptr = ((const char *) src) + cmd_size;
+ size_t tmp_len;
+
+ DBG(DBG_info, "Sending SCSI cmd 0x%02x cdb len %ld, param len %ld, result len %ld\n", ((const unsigned char *)src)[0], (long)cmd_size, (long)param_size, dst_size? (long)*dst_size:(long)0);
+
+ /* This looks like some kind of pre-initialization. */
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+
+ /* Send the CDB and check it's been received OK. */
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x16);
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkwrite(fd, cmd_size);
+
+ tmp_len = cmd_size;
+ sanei_pv8630_bulkwrite(fd, src, &tmp_len);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, 1);
+
+ result = 0xA5; /* to be sure */
+ tmp_len = 1;
+ sanei_pv8630_bulkread(fd, &result, &tmp_len);
+ if (result != 0) {
+ DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
+ if (result == 8) {
+ pv8630_mini_init_scanner(fd);
+ }
+ return(SANE_STATUS_IO_ERROR);
+ }
+
+ /* Send the parameters and check they've been received OK. */
+ if (param_size) {
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkwrite(fd, param_size);
+
+ tmp_len = param_size;
+ sanei_pv8630_bulkwrite(fd, param_ptr, &tmp_len);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, 1);
+
+ result = 0xA5; /* to be sure */
+ tmp_len = 1;
+ sanei_pv8630_bulkread(fd, &result, &tmp_len);
+ if (result != 0) {
+ DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
+ if (result == 8) {
+ pv8630_mini_init_scanner(fd);
+ }
+ return(SANE_STATUS_IO_ERROR);
+ }
+ }
+
+ /* If the SCSI command expect a return, get it. */
+ if (dst_size != NULL && *dst_size != 0 && dst != NULL) {
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, *dst_size);
+ sanei_pv8630_bulkread(fd, dst, dst_size);
+
+ DBG(DBG_info, " SCSI cmd returned %lu bytes\n", (u_long) *dst_size);
+
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_flush_buffer(fd);
+ sanei_pv8630_prep_bulkread(fd, 1);
+
+ result = 0x5A; /* just to be sure */
+ tmp_len = 1;
+ sanei_pv8630_bulkread(fd, &result, &tmp_len);
+ if (result != 0) {
+ DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
+ if (result == 8) {
+ pv8630_mini_init_scanner(fd);
+ }
+ return(SANE_STATUS_IO_ERROR);
+ }
+ }
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+
+ DBG(DBG_info, " SCSI command successfully executed\n");
+
+ return(SANE_STATUS_GOOD);
+}
+
+/* Initialize the PowerVision 8630. */
+static SANE_Status pv8630_init_umaxusb_scanner(int fd)
+{
+ DBG(DBG_info, "Initializing the PV8630\n");
+
+ /* Init the device */
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
+ sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
+
+ sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
+
+ sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x16);
+
+ DBG(DBG_info, "PV8630 initialized\n");
+
+ return(SANE_STATUS_GOOD);
+}
+
+
+/*
+ * SCSI functions for the emulation.
+ *
+ * The following functions emulate their sanei_scsi_* counterpart.
+ *
+ */
+
+
+/*
+ * sanei_umaxusb_req_wait() and sanei_umaxusb_req_enter()
+ *
+ * I don't know if it is possible to queue the reads to the
+ * scanner. So The queing is disabled. The performance does not seems
+ * to be bad anyway.
+ */
+
+static void *umaxusb_req_buffer; /* keep the buffer ptr as an ID */
+
+static SANE_Status sanei_umaxusb_req_enter (int fd,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+{
+ umaxusb_req_buffer = *idp = dst;
+ return(sanei_umaxusb_cmd(fd, src, src_size, dst, dst_size));
+}
+
+static SANE_Status
+sanei_umaxusb_req_wait (void *id)
+{
+ if (id != umaxusb_req_buffer) {
+ DBG(DBG_info, "sanei_umaxusb_req_wait: AIE, invalid id\n");
+ return(SANE_STATUS_IO_ERROR);
+ }
+ return(SANE_STATUS_GOOD);
+}
+
+/* Open the device.
+ */
+static SANE_Status
+sanei_umaxusb_open (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+ SANE_Status status;
+
+ handler = handler; /* silence gcc */
+ handler_arg = handler_arg; /* silence gcc */
+
+ status = sanei_usb_open (dev, fdp);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (1, "sanei_umaxusb_open: open of `%s' failed: %s\n",
+ dev, sane_strstatus(status));
+ return status;
+ } else {
+ SANE_Word vendor;
+ SANE_Word product;
+
+ /* We have openned the device. Check that it is a USB scanner. */
+ if (sanei_usb_get_vendor_product (*fdp, &vendor, &product) != SANE_STATUS_GOOD) {
+ /* This is not a USB scanner, or SANE or the OS doesn't support it. */
+ sanei_usb_close(*fdp);
+ *fdp = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* So it's a scanner. Does this backend support it?
+ * Only the UMAX 2200 USB is currently supported. */
+ if ((vendor != 0x1606) || (product != 0x0230)) {
+ sanei_usb_close(*fdp);
+ *fdp = -1;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* It's a good scanner. Initialize it.
+ *
+ * Note: pv8630_init_umaxusb_scanner() is for the UMAX
+ * 2200. Other UMAX scanner might need a different
+ * initialization routine. */
+
+ pv8630_init_umaxusb_scanner(*fdp);
+ }
+
+ return(SANE_STATUS_GOOD);
+}
+
+/* sanei_umaxusb_open_extended() is just a passthrough for sanei_umaxusb_open(). */
+static SANE_Status
+sanei_umaxusb_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg, int *buffersize)
+{
+ buffersize = buffersize;
+ return(sanei_umaxusb_open(dev, fdp, handler, handler_arg));
+}
+
+/* Close the scanner. */
+static void
+sanei_umaxusb_close (int fd)
+{
+ sanei_usb_close(fd);
+}
+
+
+
+