diff options
Diffstat (limited to 'sanei/sanei_ab306.c')
-rw-r--r-- | sanei/sanei_ab306.c | 583 |
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 */ |