summaryrefslogtreecommitdiff
path: root/sanei/sanei_pio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sanei/sanei_pio.c')
-rw-r--r--sanei/sanei_pio.c604
1 files changed, 604 insertions, 0 deletions
diff --git a/sanei/sanei_pio.c b/sanei/sanei_pio.c
new file mode 100644
index 0000000..ef00861
--- /dev/null
+++ b/sanei/sanei_pio.c
@@ -0,0 +1,604 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998 Christian Bucher
+ Copyright (C) 1998 Kling & Hautzinger GmbH
+ 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 bi-directional parallel-port
+ interface. */
+
+/*
+ RESTRICTIONS:
+
+ - This interface is very timing sensitive, be carefull with setting
+ debug levels.
+ */
+
+#include "../include/sane/config.h"
+
+#define BACKEND_NAME sanei_pio
+#include "../include/sane/sanei_backend.h" /* pick up compatibility defs */
+
+#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 HAVE_SYS_HW_H
+# include <sys/hw.h>
+#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_pio.h"
+
+#if defined (HAVE_IOPERM) && !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 <time.h>
+
+#include "../include/sane/saneopts.h"
+
+#define PORT_DEV "/dev/port"
+
+/* base 278 (lpt2)
+
+ ioport stat ctrl
+ offs 0 1 2
+ len 1 1 1 */
+
+/* Port definitions (`N' at end begin of label means negated signal) */
+
+#define PIO_IOPORT 0 /* rel. addr io port */
+
+#define PIO_STAT 1 /* rel. addr status port */
+#define PIO_STAT_BUSY (1<<7) /* BUSY Pin */
+#define PIO_STAT_NACKNLG (1<<6) /* ~ACKNLG Pin */
+
+#define PIO_CTRL 2 /* rel. addr control port */
+#define PIO_CTRL_IE (1<<5) /* Input enable */
+#define PIO_CTRL_IRQE (1<<4) /* enable IRQ */
+#define PIO_CTRL_DIR (1<<3) /* DIR pin, DIR=1 => out */
+#define PIO_CTRL_NINIT (1<<2) /* reset output */
+#define PIO_CTRL_FDXT (1<<1) /* Paper FEED (unused) */
+#define PIO_CTRL_NSTROBE (1<<0) /* strobe pin */
+
+#define PIO_APPLYRESET 2000 /* reset in 10us at init time */
+
+#define DL40 6
+#define DL50 7
+#define DL60 8
+#define DL61 9
+#define DL70 10
+#define DL71 11
+
+#ifdef NDEBUG
+# define DBG_INIT()
+#endif
+
+typedef struct
+ {
+ u_long base; /* i/o base address */
+ int fd; /* >= 0 when using /dev/port */
+ int max_time_seconds;/* forever if <= 0 */
+ u_int in_use; /* port in use? */
+ }
+PortRec, *Port;
+
+static PortRec port[] =
+ {
+ {0x378, -1, 0, 0},
+ {0x278, -1, 0, 0}
+ };
+
+extern int setuid (uid_t);
+
+static inline int pio_outb (const Port port, u_char val, u_long addr);
+static inline int pio_inb (const Port port, u_char * val, u_long addr);
+static inline int pio_wait (const Port port, u_char val, u_char mask);
+static inline void pio_ctrl (const Port port, u_char val);
+static inline void pio_delay (const Port port);
+static inline void pio_init (const Port port);
+static void pio_reset (const Port port);
+static int pio_write (const Port port, const u_char * buf, int n);
+static int pio_read (const Port port, u_char * buf, int n);
+static int pio_open (const char *dev, SANE_Status * status);
+
+static inline int
+pio_outb (const Port port, u_char val, u_long addr)
+{
+
+ if (-1 == port->fd)
+ outb (val, addr);
+ else
+ {
+ if (addr != (u_long)lseek (port->fd, addr, SEEK_SET))
+ return -1;
+ if (1 != write (port->fd, &val, 1))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int
+pio_inb (const Port port, u_char * val, u_long addr)
+{
+
+ if (-1 == port->fd)
+ *val = inb (addr);
+ else
+ {
+ if (addr != (u_long)lseek (port->fd, addr, SEEK_SET))
+ return -1;
+ if (1 != read (port->fd, val, 1))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int
+pio_wait (const Port port, u_char val, u_char mask)
+{
+ int stat = 0;
+ long poll_count = 0;
+ time_t start = time(NULL);
+
+ DBG (DL60, "wait on port 0x%03lx for %02x mask %02x\n",
+ port->base, (int) val, (int) mask);
+ DBG (DL61, " BUSY %s\n", (mask & PIO_STAT_BUSY) ?
+ (val & PIO_STAT_BUSY ? "on" : "off") : "-");
+ DBG (DL61, " NACKNLG %s\n",
+ (mask & PIO_STAT_NACKNLG) ? (val & PIO_STAT_NACKNLG ? "on" : "off")
+ : "-");
+ for (;;)
+ {
+ ++poll_count;
+ stat = inb (port->base + PIO_STAT);
+ if ((stat & mask) == (val & mask))
+ {
+ DBG (DL60, "got %02x after %ld tries\n", stat, poll_count);
+ DBG (DL61, " BUSY %s\n", stat & PIO_STAT_BUSY ? "on" : "off");
+ DBG (DL61, " NACKNLG %s\n",
+ stat & PIO_STAT_NACKNLG ? "on" : "off");
+
+ return stat;
+ }
+ if(poll_count>1000)
+ {
+ if ((port->max_time_seconds>0) && (time(NULL)-start >= port->max_time_seconds))
+ break;
+ usleep(1);
+ }
+
+ }
+ DBG (DL60, "got %02x aborting after %ld\n", stat, poll_count);
+ DBG (DL61, " BUSY %s\n", stat & PIO_STAT_BUSY ? "on" : "off");
+ DBG (DL61, " NACKNLG %s\n", stat & PIO_STAT_NACKNLG ? "on" : "off");
+ DBG (1, "polling time out, abort\n");
+ exit (-1);
+}
+
+static inline void
+pio_ctrl (const Port port, u_char val)
+{
+ DBG (DL60, "ctrl on port 0x%03lx %02x %02x\n",
+ port->base, (int) val, (int) val ^ PIO_CTRL_NINIT);
+
+ val ^= PIO_CTRL_NINIT;
+
+ DBG (DL61, " IE %s\n", val & PIO_CTRL_IE ? "on" : "off");
+ DBG (DL61, " IRQE %s\n", val & PIO_CTRL_IRQE ? "on" : "off");
+ DBG (DL61, " DIR %s\n", val & PIO_CTRL_DIR ? "on" : "off");
+ DBG (DL61, " NINIT %s\n", val & PIO_CTRL_NINIT ? "on" : "off");
+ DBG (DL61, " FDXT %s\n", val & PIO_CTRL_FDXT ? "on" : "off");
+ DBG (DL61, " NSTROBE %s\n", val & PIO_CTRL_NSTROBE ? "on" : "off");
+
+ outb (val, port->base + PIO_CTRL);
+
+ return;
+}
+
+static inline void
+pio_delay (const Port port)
+{
+ inb (port->base + PIO_STAT); /* delay */
+
+ return;
+}
+
+static inline void
+pio_init (const Port port)
+{
+ pio_ctrl (port, PIO_CTRL_IE);
+ return;
+}
+
+static void
+pio_reset (const Port port)
+{
+ int n;
+
+ DBG (DL40, "reset\n");
+
+ for (n = PIO_APPLYRESET; --n >= 0;)
+ {
+ outb ((PIO_CTRL_IE | PIO_CTRL_NINIT) ^ PIO_CTRL_NINIT,
+ port->base + PIO_CTRL);
+ }
+ pio_init (port);
+
+ DBG (DL40, "end reset\n");
+
+ return;
+}
+
+static int
+pio_write (const Port port, const u_char * buf, int n)
+{
+ int k;
+
+ DBG (DL40, "write\n");
+
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+ pio_ctrl (port, PIO_CTRL_DIR | PIO_CTRL_IE); /* praeoutput */
+ pio_wait (port, PIO_STAT_NACKNLG, PIO_STAT_NACKNLG); /* acknlg */
+ pio_ctrl (port, PIO_CTRL_DIR); /* output */
+
+ for (k = 0; k < n; k++, buf++)
+ {
+ DBG (DL40, "write byte\n");
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY | PIO_STAT_NACKNLG,
+ PIO_STAT_BUSY | PIO_STAT_NACKNLG); /* busyack */
+#endif
+ DBG (DL60, "out %02x\n", (int) *buf);
+
+ outb (*buf, port->base + PIO_IOPORT);
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+ pio_ctrl (port, PIO_CTRL_DIR | PIO_CTRL_NSTROBE); /* outputstrobe */
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+ pio_ctrl (port, PIO_CTRL_DIR); /* output */
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+
+ DBG (DL40, "end write byte\n");
+ }
+
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY | PIO_STAT_NACKNLG,
+ PIO_STAT_BUSY | PIO_STAT_NACKNLG); /* busyack */
+#endif
+
+ pio_ctrl (port, PIO_CTRL_DIR | PIO_CTRL_IE); /* praeoutput */
+ DBG (DL40, "end write\n");
+ return k;
+}
+
+static int
+pio_read (const Port port, u_char * buf, int n)
+{
+ int k;
+
+ DBG (DL40, "read\n");
+
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+ pio_ctrl (port, PIO_CTRL_IE); /* input */
+
+ for (k = 0; k < n; k++, buf++)
+ {
+ DBG (DL40, "read byte\n");
+
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY | PIO_STAT_NACKNLG);
+ /* busynack */
+#endif
+ pio_ctrl (port, PIO_CTRL_IE | PIO_CTRL_NSTROBE); /* inputstrobe */
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+ pio_ctrl (port, PIO_CTRL_IE); /* input */
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY | PIO_STAT_NACKNLG);
+ /* busynack */
+#endif
+
+ *buf = inb (port->base + PIO_IOPORT);
+ DBG (DL60, "in %02x\n", (int) *buf);
+ DBG (DL40, "end read byte\n");
+ }
+
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+ pio_ctrl (port, PIO_CTRL_IE); /* input */
+ DBG (DL40, "end read\n");
+ return k;
+}
+
+/*
+ Open the device, <dev> must contain a valid port number (as string).
+ */
+
+static int
+pio_open (const char *dev, SANE_Status * status)
+{
+ static int first_time = 1;
+ u_long base;
+ int n;
+
+ if (first_time)
+ {
+ first_time = 0;
+
+ DBG_INIT ();
+ /* set root uid */
+ if (0 > setuid (0))
+ {
+ DBG (1, "sanei_pio_open: setuid failed: errno = %d\n", errno);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+ }
+ /* read port number */
+ {
+ char *end;
+
+ base = strtol (dev, &end, 0);
+
+ if ((end == dev) || *end)
+ {
+ DBG (1, "sanei_pio_open: `%s' is not a valid port number\n", dev);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+ }
+
+ if (0 == base)
+ {
+ DBG (1, "sanei_pio_open: 0x%03lx is not a valid base address\n", base);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+ for (n = 0; n < NELEMS (port); n++)
+ if (port[n].base == base)
+ break;
+
+ if (NELEMS (port) <= n)
+ {
+ DBG (1, "sanei_pio_open: 0x%03lx is not a valid base address\n", base);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+ if (port[n].in_use)
+ {
+ DBG (1, "sanei_pio_open: port 0x%03lx is already in use\n", base);
+ *status = SANE_STATUS_DEVICE_BUSY;
+ return -1;
+ }
+ port[n].base = base;
+ port[n].fd = -1;
+ port[n].max_time_seconds = 10;
+ port[n].in_use = 1;
+
+ if (ioperm (port[n].base, 3, 1))
+ {
+ DBG (1, "sanei_pio_open: cannot get io privilege for port 0x%03lx\n",
+ port[n].base);
+ *status = SANE_STATUS_IO_ERROR;
+ return -1;
+ }
+
+ pio_reset (&port[n]);
+
+ *status = SANE_STATUS_GOOD;
+ return n;
+}
+
+SANE_Status
+sanei_pio_open (const char *dev, int *fdp)
+{
+ SANE_Status status;
+
+ *fdp = pio_open (dev, &status);
+ return status;
+}
+
+void
+sanei_pio_close (int fd)
+{
+ Port p = port + fd;
+
+ if ((0 > fd) && (NELEMS (port) <= fd))
+ return;
+
+ if (!p->in_use)
+ return;
+
+ if (-1 != p->fd)
+ {
+ close (p->fd);
+ p->fd = -1;
+ }
+
+ p->in_use = 0;
+
+ return;
+}
+
+int
+sanei_pio_read (int fd, u_char * buf, int n)
+{
+ if ((0 > fd) && (NELEMS (port) <= fd))
+ return -1;
+
+ if (!port[fd].in_use)
+ return -1;
+
+ return pio_read (&port[fd], buf, n);
+}
+
+int
+sanei_pio_write (int fd, const u_char * buf, int n)
+{
+ if ((0 > fd) && (NELEMS (port) <= fd))
+ return -1;
+
+ if (!port[fd].in_use)
+ return -1;
+
+ return pio_write (&port[fd], buf, n);
+}
+
+#else /* !HAVE_IOPERM */
+
+#ifdef __BEOS__
+
+#include <fcntl.h>
+
+SANE_Status
+sanei_pio_open (const char *dev, int *fdp)
+{
+ int fp;
+
+ /* open internal parallel port */
+ fp=open("/dev/parallel/parallel1",O_RDWR);
+
+ *fdp=fp;
+ if(fp<0) return SANE_STATUS_INVAL;
+ return(SANE_STATUS_GOOD);
+}
+
+
+void
+sanei_pio_close (int fd)
+{
+ close(fd);
+ return;
+}
+
+int
+sanei_pio_read (int fd, u_char * buf, int n)
+{
+ return(read(fd,buf,n));
+}
+
+int
+sanei_pio_write (int fd, const u_char * buf, int n)
+{
+ return(write(fd,buf,n));
+}
+
+#else /* !__BEOS__ */
+
+SANE_Status
+sanei_pio_open (const char *dev, int *fdp)
+{
+ *fdp = -1;
+ return SANE_STATUS_INVAL;
+}
+
+
+void
+sanei_pio_close (int fd)
+{
+ return;
+}
+
+int
+sanei_pio_read (int fd, u_char * buf, int n)
+{
+ return -1;
+}
+
+int
+sanei_pio_write (int fd, const u_char * buf, int n)
+{
+ return -1;
+}
+#endif /* __BEOS__ */
+
+#endif /* !HAVE_IOPERM */