summaryrefslogtreecommitdiff
path: root/sanei/sanei_ab306.c
diff options
context:
space:
mode:
Diffstat (limited to 'sanei/sanei_ab306.c')
-rw-r--r--sanei/sanei_ab306.c583
1 files changed, 583 insertions, 0 deletions
diff --git a/sanei/sanei_ab306.c b/sanei/sanei_ab306.c
new file mode 100644
index 0000000..91d647d
--- /dev/null
+++ b/sanei/sanei_ab306.c
@@ -0,0 +1,583 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Andreas Czechanowski and David Mosberger
+ 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-proprietary SCSI-over-parallel-port
+ interface. */
+
+#include "../include/sane/config.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_IO_H
+# include <sys/io.h> /* use where available (glibc 2.x, for example) */
+#elif HAVE_ASM_IO_H
+# include <asm/io.h> /* ugly, but backwards compatible */
+#elif defined (__i386__) && defined (__GNUC__)
+
+static __inline__ void
+outb (u_char value, u_long port)
+{
+ __asm__ __volatile__ ("outb %0,%1" : : "a" (value), "d" ((u_short) port));
+}
+
+static __inline__ u_char
+inb (u_long port)
+{
+ u_char value;
+
+ __asm__ __volatile__ ("inb %1,%0" : "=a" (value) : "d" ((u_short)port));
+ return value;
+}
+
+#else
+# define IO_SUPPORT_MISSING
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_ab306.h"
+
+#if (defined(HAVE_IOPERM) || defined(__FreeBSD__)) && !defined(IO_SUPPORT_MISSING)
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME sanei_ab306
+#include "../include/sane/sanei_debug.h"
+
+#define PORT_DEV "/dev/port"
+#define AB306_CIO 0x379 /* control i/o port */
+
+#if defined(__FreeBSD__)
+static int dev_io_fd = 0;
+#endif
+
+typedef struct port
+ {
+ u_long base; /* i/o base address */
+ int port_fd; /* >= 0 when using /dev/port */
+ u_int lstat;
+ u_int in_use : 1, /* port in use? */
+ active : 1; /* port was active at some point */
+ }
+Port;
+
+static Port port[] =
+ {
+ {0x26b, -1, 0, 0, 0},
+ {0x2ab, -1, 0, 0, 0},
+ {0x2eb, -1, 0, 0, 0},
+ {0x22b, -1, 0, 0, 0},
+ {0x32b, -1, 0, 0, 0},
+ {0x36b, -1, 0, 0, 0},
+ {0x3ab, -1, 0, 0, 0},
+ {0x3eb, -1, 0, 0, 0}
+ };
+
+static const SANE_Byte wakeup[] =
+ {
+ 0x47, 0x55, 0x54, 0x53, 0x02, 0x01, 0x80
+ };
+
+static u_char cdb_sizes[8] =
+ {
+ 6, 10, 10, 12, 12, 12, 10, 10
+ };
+#define CDB_SIZE(opcode) cdb_sizes[(((opcode) >> 5) & 7)]
+
+static void
+ab306_outb (Port *p, u_long addr, u_char val)
+{
+
+ if (p->port_fd >= 0)
+ {
+ if ((u_long) lseek (p->port_fd, addr, SEEK_SET) != addr)
+ return;
+ if (write (p->port_fd, &val, 1) != 1)
+ return;
+ }
+ else
+ outb (val, addr);
+}
+
+static int
+ab306_inb (Port *p, u_long addr)
+{
+ u_char ch;
+
+ if (p->port_fd >= 0)
+ {
+ if ((u_long) lseek (p->port_fd, addr, SEEK_SET) != addr)
+ return -1;
+ if (read (p->port_fd, &ch, 1) != 1)
+ return -1;
+ return ch;
+ }
+ else
+ return inb (addr);
+}
+
+/* Send a single command-byte over the AB306N-interface. */
+static void
+ab306_cout (Port *p, int val)
+{
+ u_long base = p->base;
+
+ while ((ab306_inb (p, base + 1) & 0x80)); /* wait for dir flag */
+ ab306_outb (p, base, val);
+ ab306_outb (p, base + 1, 0xe0);
+ while ((ab306_inb (p, base + 1) & 0x80) == 0); /* wait for ack */
+ ab306_outb (p, base + 1, 0x60);
+}
+
+/* Read a single response-byte from the SANEI_AB306N-interface. */
+static int
+ab306_cin (Port *p)
+{
+ u_long base = p->base;
+ u_char val;
+
+ while ((ab306_inb (p, base + 1) & 0x80) == 0); /* wait for dir flag */
+ val = ab306_inb (p, base);
+ ab306_outb (p, base + 1, 0xe0); /* ack received byte */
+ while (ab306_inb (p, base + 1) & 0x80);
+ ab306_outb (p, base + 1, 0x60); /* reset ack */
+ return val;
+}
+
+static SANE_Status
+ab306_write (Port *p, const void *buf, size_t len)
+{
+ u_long base = p->base;
+ u_int i;
+ int cksum = 0;
+
+ DBG(3, "ab306_write: waiting for scanner to be ready %02x\n",
+ ab306_inb (p, base + 1));
+ while ((ab306_inb (p, base + 1) & 0x20) == 0);
+ usleep (10000);
+
+ DBG(4, "ab306_write: writing data\n");
+ for (i = 0; i < len; ++i)
+ {
+ ab306_cout (p, ((const u_char *) buf)[i]);
+ cksum += ((const u_char *) buf)[i];
+ }
+
+ DBG(4, "ab306_write: writing checksum\n");
+ ab306_cout (p, -cksum & 0xff);
+
+ DBG(3, "ab306_write: waiting for scanner to be NOT ready %02x\n",
+ ab306_inb (p, base + 1));
+ while ((ab306_inb (p, base + 1) & 0x20) != 0);
+ usleep (10000);
+
+ DBG(4, "ab306_write: reading ack\n");
+ cksum = ab306_cin (p);
+ if (cksum != 0xa5)
+ {
+ DBG(0, "ab306_write: checksum error (%02x!=a5) when sending command!\n",
+ cksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* Abort a running scan by pulling C6 low for a while. */
+static void
+ab306_abort (Port *p)
+{
+ ab306_outb (p, p->base + 1, 0x20);
+ while ((ab306_inb (p, p->base + 1) & 0x80));
+ ab306_outb (p, p->base + 1, 0x60);
+}
+
+/* Open the device, <dev> must contain a valid port number (as string)
+ returns port number and I/O method in <*fdp> (not a file
+ descriptor) turns the scanner on setting C5 and C6. */
+SANE_Status
+sanei_ab306_open (const char *dev, int *fdp)
+{
+ static int first_time = 1;
+ SANE_Status status;
+ u_char byte;
+ u_long base;
+ char *end;
+ int i, j;
+
+ if (first_time)
+ {
+ first_time = 0;
+ DBG_INIT();
+ }
+
+ base = strtol (dev, &end, 0);
+ if (end == dev || *end)
+ {
+ DBG(1, "sanei_ab306_open: `%s' is not a valid port number\n", dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ for (i = 0; i < NELEMS(port); ++i)
+ if (port[i].base == base)
+ break;
+
+ if (i >= NELEMS(port))
+ {
+ DBG(1, "sanei_ab306_open: %lx is not a valid base address\n", base);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (port[i].in_use)
+ {
+ DBG(1, "sanei_ab306_open: port %lx is already in use\n", base);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ status = sanei_ab306_get_io_privilege (i);
+
+#if defined(__FreeBSD__)
+ status = sanei_ab306_get_io_privilege (i);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "sanei_ab306_ioport: using inb/outb access\n");
+ for (j = 0; j < NELEMS(wakeup); ++j)
+ {
+ byte = wakeup[j];
+ if (j == NELEMS(wakeup) - 1)
+ byte |= i;
+ outb (byte, AB306_CIO);
+ }
+
+#else /* !defined(__FreeBSD__) */
+ if (ioperm (AB306_CIO, 1, 1) != 0)
+ {
+ DBG(1, "sanei_ab306_ioport: using /dev/port access\n");
+ if (port[i].port_fd < 0)
+ port[i].port_fd = open (PORT_DEV, O_RDWR);
+ if (port[i].port_fd < 0)
+ return SANE_STATUS_IO_ERROR;
+ for (j = 0; j < NELEMS(wakeup); ++j)
+ {
+ if (lseek (port[i].port_fd, AB306_CIO, SEEK_SET) != AB306_CIO)
+ return SANE_STATUS_IO_ERROR;
+ byte = wakeup[j];
+ if (j == NELEMS(wakeup) - 1)
+ byte |= i;
+ if (write (port[i].port_fd, &byte, 1) != 1)
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else
+ {
+ DBG(1, "sanei_ab306_ioport: using inb/outb access\n");
+ for (j = 0; j < NELEMS(wakeup); ++j)
+ {
+ byte = wakeup[j];
+ if (j == NELEMS(wakeup) - 1)
+ byte |= i;
+ outb (byte, AB306_CIO);
+ }
+ status = sanei_ab306_get_io_privilege (i);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+#endif /* !defined(__FreeBSD__) */
+
+ ab306_outb (port + i, port[i].base + 1, 0x60);
+ port[i].in_use = 1;
+ port[i].active = 1;
+ *fdp = i;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_ab306_close (int fd)
+{
+ Port *p = port + fd;
+
+ if (p->in_use)
+ {
+ if (p->port_fd >= 0)
+ {
+ close (p->port_fd);
+ p->port_fd = -1;
+ }
+ p->in_use = 0;
+ }
+}
+
+/* Get I/O permission to the configuration port and the desired
+ operating ports. */
+SANE_Status
+sanei_ab306_get_io_privilege (int fd)
+{
+ if (port[fd].port_fd < 0)
+ {
+#if defined(__FreeBSD__)
+ if (dev_io_fd == 0)
+ dev_io_fd = open ("/dev/io", O_RDONLY);
+ if (dev_io_fd < 0)
+ return SANE_STATUS_IO_ERROR;
+#else /* !defined(__FreeBSD__) */
+ if (ioperm (port[fd].base, 3, 1) != 0)
+ return SANE_STATUS_IO_ERROR;
+#endif /* !defined(__FreeBSD__) */
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* Send a command via the SANEI_AB306N-interface, get response when
+ <dst_size> is > 0. */
+SANE_Status
+sanei_ab306_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ Port *p = port + fd;
+ const u_char *cp = src;
+ size_t cdb_size = CDB_SIZE(cp[0]);
+ SANE_Status status;
+ u_char byte;
+
+ /* If this is a READ_SCANNED_DATA command, reset lstat: */
+ switch (cp[0])
+ {
+ case 0x08: /* scsi READ_SCANNED_DATA command */
+ /* Initialize lstat to the current status, because we need bit 4
+ (0x10) as toggle bit for reading lines. */
+ p->lstat = 0x34;
+ break;
+
+ case 0x1b: /* scsi START_STOP command */
+ if (!cp[4])
+ {
+ /* it's a STOP */
+ ab306_abort (p);
+ return SANE_STATUS_GOOD;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ status = ab306_write (p, src, 6);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (src_size > cdb_size)
+ {
+ status = ab306_write (p, cp + cdb_size, src_size - cdb_size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (dst && *dst_size > 0)
+ {
+ u_int i, cksum = 0;
+
+ DBG(3, "sanei_ab306_cmd: waiting for scanner to be NOT ready %02x\n",
+ ab306_inb (p, p->base + 1));
+ while ((ab306_inb (p, p->base + 1) & 0x20) != 0);
+
+ for (i = 0; i < *dst_size; i++)
+ {
+ byte = ab306_cin (p);
+ cksum += byte;
+ ((u_char *) dst)[i] = byte;
+ }
+ cksum += ab306_cin (p); /* add in checksum */
+
+ if ((cksum & 0xff) != 0)
+ {
+ DBG(0, "sanei_ab306_cmd: checksum error (%2x!=0) when receiving "
+ "after command!\n", cksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ ab306_cout (p, 0); /* dummy byte (will be discarded) */
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* Read scan-data from the AB306N-device. Read <lines> lines, of which
+ every one has <bpl> bytes. */
+SANE_Status
+sanei_ab306_rdata (int fd, int planes, SANE_Byte * buf, int lines, int bpl)
+{
+ Port *p = port + fd;
+ int lcnt, pcnt, bcnt, xmax;
+ SANE_Byte *lsave_bp;
+ int nstat;
+
+ DBG(2, "sanei_ab306_rdata: start\n");
+
+ /* lstat should be set by a call to sanei_ab306_init_toggle before ! */
+ while ((ab306_inb (p, p->base + 1) & 0x80) == 0);
+ /* the lines-loop: */
+ for (lcnt = 0; lcnt < lines; ++lcnt)
+ {
+ lsave_bp = buf;
+ /* the planes-loop: */
+ for (pcnt = 0; pcnt < planes; ++pcnt)
+ {
+ xmax = bpl / planes;
+ do
+ nstat = ab306_inb (p, p->base + 1);
+ while (((p->lstat ^ nstat) & 0x10) == 0);
+
+ if (p->port_fd >= 0)
+ {
+ /* the pixel-loop: */
+ for (bcnt = 0; bcnt < xmax; bcnt++)
+ {
+ if ((u_long) lseek (p->port_fd, p->base, SEEK_SET) != p->base)
+ return SANE_STATUS_IO_ERROR;
+ if (read (p->port_fd, buf, 1) != 1)
+ return SANE_STATUS_IO_ERROR;
+ ++buf;
+ }
+ }
+ else
+ {
+ /* the pixel-loop: */
+ for (bcnt = 0; bcnt < xmax; bcnt++)
+ {
+ *(u_char *) buf = inb (p->base);
+ ++buf;
+ }
+ }
+ p->lstat = nstat;
+ }
+ }
+ DBG(2, "sanei_ab306_rdata: done\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_ab306_exit (void)
+{
+ int i;
+
+ for (i = 0; i < NELEMS(port); ++i)
+ if (port[i].active)
+ {
+ port[i].active = 0;
+ /* power off the scanner: */
+ ab306_outb (port + i, port[i].base + 1, 0x00);
+ }
+#if defined(__FreeBSD)
+ if (dev_io_fd >0)
+ close (dev_io_fd);
+#endif /* defined(__FreeBSD__) */
+}
+
+SANE_Status
+sanei_ab306_test_ready (int fd)
+{
+ Port *p = port + fd;
+ u_char byte;
+
+ byte = ab306_inb (p, p->base + 1);
+ if (byte & 0x20)
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+#else /* !HAVE_IOPERM */
+
+SANE_Status
+sanei_ab306_open (const char *devname, int *fdp)
+{
+ *fdp = -1;
+ return SANE_STATUS_INVAL;
+}
+
+void
+sanei_ab306_close (int fd)
+{
+}
+
+void
+sanei_ab306_exit (void)
+{
+}
+
+SANE_Status
+sanei_ab306_get_io_privilege (int fd)
+{
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_ab306_test_ready (int fd)
+{
+ return SANE_STATUS_GOOD; /* non-existent device is always ready... */
+}
+
+SANE_Status
+sanei_ab306_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t *dst_size)
+{
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_ab306_rdata (int fd, int planes, SANE_Byte *buf, int lines, int bpl)
+{
+ return SANE_STATUS_INVAL;
+}
+
+#endif /* !HAVE_IOPERM */