/* sane - Scanner Access Now Easy.
   Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
   Copyright (C) 2003 James Perry (scsi_pp functions)
   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 an interface for the Mustek PP chipset A4S2 */

/* debug levels:
   0 - nothing
   1 - errors
   2 - warnings
   3 - things nice to know
   4 - code flow
   5 - detailed flow
   6 - everything 

   These debug levels can be set using the environment variable
   SANE_DEBUG_SANEI_PA4S2 */

#include "../include/sane/config.h"

#define BACKEND_NAME sanei_pa4s2
#include "../include/sane/sanei_backend.h"	/* pick up compatibility defs */

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#if defined(HAVE_LIBIEEE1284)

# include <ieee1284.h>

#elif defined(ENABLE_PARPORT_DIRECTIO)

# if defined(HAVE_SYS_IO_H)
#  if defined (__ICC) && __ICC >= 700
#   define __GNUC__ 2
#  endif
# include <sys/io.h>
#  if defined (__ICC) && __ICC >= 700
#   undef __GNUC__
#  elif defined(__ICC) && defined(HAVE_ASM_IO_H)
#   include <asm/io.h>
#  endif
# elif defined(HAVE_ASM_IO_H)
#  include <asm/io.h>		/* ugly, but backwards compatible */
# elif defined(HAVE_SYS_HW_H)
#  include <sys/hw.h>
# elif defined(__i386__)  && ( defined (__GNUC__) || defined (__ICC) )

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

#else

# define IO_SUPPORT_MISSING

#endif /* HAVE_LIBIEEE1284 */

#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/sanei_pa4s2.h"


#ifdef NDEBUG
#define DBG_INIT()  /* basically, this is already done in sanei_debug.h... */

#define TEST_DBG_INIT()

#else /* !NDEBUG */

static int sanei_pa4s2_dbg_init_called = SANE_FALSE;

#if (!defined __GNUC__ || __GNUC__ < 2 || \
     __GNUC_MINOR__ < (defined __cplusplus ? 6 : 4))

#define TEST_DBG_INIT() if (sanei_pa4s2_dbg_init_called == SANE_FALSE) \
                          {                                            \
                            DBG_INIT();                                \
                            DBG(6, "sanei_pa4s2: interface called for" \
                              " the first time\n");                    \
                            sanei_pa4s2_dbg_init_called = SANE_TRUE;   \
                          }
#else

#define TEST_DBG_INIT() if (sanei_pa4s2_dbg_init_called == SANE_FALSE)   \
                          {                                              \
                            DBG_INIT();                                  \
                            DBG(6, "%s: interface called for"            \
                              " the first time\n", __PRETTY_FUNCTION__); \
                            sanei_pa4s2_dbg_init_called = SANE_TRUE;     \
                          }

#endif

#endif /* NDEBUG */

#if defined(STDC_HEADERS)
# include <errno.h>
# include <stdio.h>
# include <stdlib.h>
#endif
#if defined(HAVE_STRING_H)
# include <string.h>
#elif defined(HAVE_STRINGS_H)
# include <strings.h>
#endif
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif

#include "../include/sane/saneopts.h"


#if (defined (HAVE_IOPERM) || defined (HAVE_LIBIEEE1284)) && !defined (IO_SUPPORT_MISSING)

#if defined(STDC_HEADERS)
# include <errno.h>
# include <stdio.h>
# include <stdlib.h>
#endif
#if defined(HAVE_STRING_H)
# include <string.h>
#elif defined(HAVE_STRINGS_H)
# include <strings.h>
#endif
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif

#include "../include/sane/saneopts.h"

#define PA4S2_MODE_NIB	0
#define PA4S2_MODE_UNI	1
#define PA4S2_MODE_EPP	2

#define PA4S2_ASIC_ID_1013	0xA8
#define PA4S2_ASIC_ID_1015	0xA5
#define PA4S2_ASIC_ID_1505	0xA2


typedef struct
  {
#ifndef HAVE_LIBIEEE1284
    const char name[6];
    u_long base;		/* i/o base address */
#endif
    u_int in_use;		/* port in use? */
    u_int enabled;		/* port enabled? */
    u_int mode;			/* protocoll */
    u_char prelock[3];		/* state of port */
#ifdef HAVE_LIBIEEE1284
    int caps;
#endif
  }
PortRec, *Port;

#if defined (HAVE_LIBIEEE1284)

static struct parport_list pplist;
static PortRec *port;

#else

static PortRec port[] =
{
  {"0x378", 0x378, SANE_FALSE, SANE_FALSE, PA4S2_MODE_NIB,
    {0, 0, 0}},
  {"0x278", 0x278, SANE_FALSE, SANE_FALSE, PA4S2_MODE_NIB,
    {0, 0, 0}},
  {"0x3BC", 0x3BC, SANE_FALSE, SANE_FALSE, PA4S2_MODE_NIB,
    {0, 0, 0}}
};

#endif

static u_int sanei_pa4s2_interface_options = SANEI_PA4S2_OPT_DEFAULT;

extern int setuid (uid_t);	/* should also be in unistd.h */

static int pa4s2_open (const char *dev, SANE_Status * status);
static void pa4s2_readbegin_epp (int fd, u_char reg);
static u_char pa4s2_readbyte_epp (int fd);
static void pa4s2_readend_epp (int fd);
static void pa4s2_readbegin_uni (int fd, u_char reg);
static u_char pa4s2_readbyte_uni (int fd);
static void pa4s2_readend_uni (int fd);
static void pa4s2_readbegin_nib (int fd, u_char reg);
static u_char pa4s2_readbyte_nib (int fd);
static void pa4s2_readend_nib (int fd);
static void pa4s2_writebyte_any (int fd, u_char reg, u_char val);
static int pa4s2_enable (int fd, u_char * prelock);
static int pa4s2_disable (int fd, u_char * prelock);
static int pa4s2_close (int fd, SANE_Status * status);

#if defined (HAVE_LIBIEEE1284)

static const char * pa4s2_libieee1284_errorstr(int error)
{

  switch (error)
    {

      case E1284_OK:
        return "Everything went fine";

      case E1284_NOTIMPL:
        return "Not implemented in libieee1284";

      case E1284_NOTAVAIL:
        return "Not available on this system";

      case E1284_TIMEDOUT:
        return "Operation timed out";

      case E1284_REJECTED:
        return "IEEE 1284 negotiation rejected";

      case E1284_NEGFAILED:
        return "Negotiation went wrong";

      case E1284_NOMEM:
        return "No memory left";

      case E1284_INIT:
        return "Error initializing port";

      case E1284_SYS:
        return "Error interfacing system";

      case E1284_NOID:
        return "No IEEE 1284 ID available";

      case E1284_INVALIDPORT:
        return "Invalid port";

      default:
        return "Unknown error";

    }
}

#endif

static int
pa4s2_init (SANE_Status *status)
{
  static int first_time = SANE_TRUE;
#if defined (HAVE_LIBIEEE1284)
  int result, n;
#endif

  DBG (6, "pa4s2_init: static int first_time = %u\n", first_time);

  if (first_time == SANE_FALSE)
    {
      DBG (5, "pa4s2_init: sanei already initalized\n");
      status = SANE_STATUS_GOOD;
      return 0;
    }

  DBG (5, "pa4s2_init: called for the first time\n");

  first_time = SANE_FALSE;

#if defined (HAVE_LIBIEEE1284)

  DBG (4, "pa4s2_init: initializing libieee1284\n");
  result = ieee1284_find_ports (&pplist, 0);

  if (result)
    {
      DBG (1, "pa4s2_init: initializing IEEE 1284 failed (%s)\n",
           pa4s2_libieee1284_errorstr (result));
      first_time = SANE_TRUE;
      *status = SANE_STATUS_INVAL;
      return -1;
    }

  DBG (3, "pa4s2_init: %d ports reported by IEEE 1284 library\n", pplist.portc);
  
  for (n=0; n<pplist.portc; n++)
    DBG (6, "pa4s2_init: port %d is `%s`\n", n, pplist.portv[n]->name);


  DBG (6, "pa4s2_init: allocating port list\n");
  if ((port = calloc(pplist.portc, sizeof(PortRec))) == NULL)
    {
      DBG (1, "pa4s2_init: not enough free memory\n");
      ieee1284_free_ports(&pplist);
      first_time = SANE_TRUE;
      *status = SANE_STATUS_NO_MEM;
      return -1;
    }

#else

  DBG (4, "pa4s2_init: trying to setuid root\n");

  if (0 > setuid (0))
    {

      DBG (1, "pa4s2_init: setuid failed: errno = %d\n", errno);
      DBG (5, "pa4s2_init: returning SANE_STATUS_INVAL\n");

      *status = SANE_STATUS_INVAL;
      first_time = SANE_TRUE;
      return -1;

    }

  DBG (3, "pa4s2_init: the application is now root\n");
  DBG (3, "pa4s2_init: this is a high security risk...\n");

  DBG (6, "pa4s2_init: ... you'd better start praying\n");

  /* PS: no, i don't trust myself either */

  /* PPS: i'd try rsbac or similar if i were you */

#endif

  DBG (5, "pa4s2_init: initialized successfully\n");
  *status = SANE_STATUS_GOOD;
  return 0;
}

static int
pa4s2_open (const char *dev, SANE_Status * status)
{

  int n, result;
#if !defined (HAVE_LIBIEEE1284)
  u_long base;
#endif
  
  DBG (4, "pa4s2_open: trying to attach dev `%s`\n", dev);

  if ((result = pa4s2_init(status)) != 0)
    {

      DBG (1, "pa4s2_open: failed to initialize\n");
      return result;
    }
  
#if !defined (HAVE_LIBIEEE1284)

  {
    char *end;

    DBG (5, "pa4s2_open: reading port number\n");

    base = strtol (dev, &end, 0);

    if ((end == dev) || (*end != '\0'))
      {

	DBG (1, "pa4s2_open: `%s` is not a valid port number\n", dev);
	DBG (6, "pa4s2_open: the part I did not understand was ...`%s`\n", end);
	DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");

	*status = SANE_STATUS_INVAL;

	return -1;

      }

  }

  DBG (6, "pa4s2_open: read port number 0x%03lx\n", base);

  if (base == 0)
    {

      DBG (1, "pa4s2_open: 0x%03lx is not a valid base address\n", base);
      DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");

      *status = SANE_STATUS_INVAL;
      return -1;

    }
#endif

  DBG (5, "pa4s2_open: looking up port in list\n");

#if defined (HAVE_LIBIEEE1284)

  for (n = 0; n < pplist.portc; n++)
    if (!strcmp(pplist.portv[n]->name, dev))
      break;

  if (pplist.portc <= n)
    {
      DBG (1, "pa4s2_open: `%s` is not a valid device name\n", dev);
      DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");

      *status = SANE_STATUS_INVAL;
      return -1;
    }

#else

  for (n = 0; n < NELEMS (port); n++)
    if (port[n].base == base)
      break;

  if (NELEMS (port) <= n)
    {

      DBG (1, "pa4s2_open: 0x%03lx is not a valid base address\n",
	   base);
      DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");

      *status = SANE_STATUS_INVAL;
      return -1;
    }

#endif

  DBG (6, "pa4s2_open: port is in list at port[%d]\n", n);

  if (port[n].in_use == SANE_TRUE)
    {

#if defined (HAVE_LIBIEEE1284)
      DBG (1, "pa4s2_open: device `%s` is already in use\n", dev);
#else
      DBG (1, "pa4s2_open: port 0x%03lx is already in use\n", base);
#endif
      DBG (5, "pa4s2_open: returning SANE_STATUS_DEVICE_BUSY\n");

      *status = SANE_STATUS_DEVICE_BUSY;
      return -1;

    }

  DBG (5, "pa4s2_open: setting up port data\n");

#if defined (HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_open: name=%s in_use=SANE_TRUE\n", dev);
#else
  DBG (6, "pa4s2_open: base=0x%03lx in_use=SANE_TRUE\n", base);
#endif
  DBG (6, "pa4s2_open: enabled=SANE_FALSE mode=PA4S2_MODE_NIB\n");
  port[n].in_use = SANE_TRUE;
  port[n].enabled = SANE_FALSE;
  port[n].mode = PA4S2_MODE_NIB;


#if defined (HAVE_LIBIEEE1284)

  DBG (5, "pa4s2_open: opening device\n");
  result = ieee1284_open (pplist.portv[n], 0, &port[n].caps);

  if (result)
    {
      DBG (1, "pa4s2_open: could not open device `%s` (%s)\n",
           dev, pa4s2_libieee1284_errorstr (result));
      port[n].in_use = SANE_FALSE;
      DBG (6, "pa4s2_open: marking port %d as unused\n", n);
      *status = SANE_STATUS_ACCESS_DENIED;
      return -1;
    }

#else

  DBG (5, "pa4s2_open: getting io permissions\n");

  /* TODO: insert FreeBSD compatible code here */

  if (ioperm (port[n].base, 5, 1))
    {

      DBG (1, "pa4s2_open: cannot get io privilege for port 0x%03lx\n", 
      		port[n].base);


      DBG (5, "pa4s2_open: marking port[%d] as unused\n", n);
      port[n].in_use = SANE_FALSE;

      DBG (5, "pa4s2_open: returning SANE_STATUS_IO_ERROR\n");
      *status = SANE_STATUS_IO_ERROR;
      return -1;

    }
#endif

  DBG (3, "pa4s2_open: device `%s` opened...\n", dev);

  DBG (5, "pa4s2_open: returning SANE_STATUS_GOOD\n");
  *status = SANE_STATUS_GOOD;

  DBG (4, "pa4s2_open: open dev `%s` as fd %u\n", dev, n);

  return n;

}

#if defined(HAVE_LIBIEEE1284)


#define inbyte0(fd)	ieee1284_read_data(pplist.portv[fd]);
#define inbyte1(fd)	(ieee1284_read_status(pplist.portv[fd]) ^ S1284_INVERTED)
#define inbyte2(fd)	(ieee1284_read_control(pplist.portv[fd]) ^ C1284_INVERTED)
static u_char inbyte4(int fd)
{
  char val;
  ieee1284_epp_read_data(pplist.portv[fd], 0, &val, 1);
  return (u_char)val;
}

#define outbyte0(fd,val)	ieee1284_write_data(pplist.portv[fd], val)
#define outbyte1(fd,val)	/* ieee1284_write_status(pplist.portv[fd], (val) ^ S1284_INVERTED) */
#define outbyte2(fd,val)	ieee1284_write_control(pplist.portv[fd], (val) ^ C1284_INVERTED)

static void outbyte3(int fd, u_char val)
{
  ieee1284_epp_write_addr (pplist.portv[fd], 0, (char *)&val, 1);
}

#else

#define inbyte0(fd)	inb(port[fd].base)
#define inbyte1(fd)	inb(port[fd].base + 1)
#define inbyte2(fd)	inb(port[fd].base + 2)
#define inbyte4(fd)	inb(port[fd].base + 4)

#define outbyte0(fd,val)	outb(val, port[fd].base)
#define outbyte1(fd,val)	outb(val, port[fd].base + 1)
#define outbyte2(fd,val)	outb(val, port[fd].base + 2)
#define outbyte3(fd,val)	outb(val, port[fd].base + 3)

#endif


static void
pa4s2_readbegin_epp (int fd, u_char reg)
{

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_readbegin_epp: selecting register %u at '%s'\n",
       (int) reg, pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_readbegin_epp: selecting register %u at 0x%03lx\n",
       (int) reg, port[fd].base);
#endif

  outbyte0 (fd, 0x20);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x04);
  outbyte3 (fd, reg + 0x18);

}

static u_char
pa4s2_readbyte_epp (int fd)
{

  u_char val = inbyte4 (fd);

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_readbyte_epp: reading value 0x%02x from '%s'\n",
       (int) val, pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_readbyte_epp: reading value 0x%02x at 0x%03lx\n",
       (int) val, port[fd].base);
#endif

  return val;

}

static void
pa4s2_readend_epp (int fd)
{

  DBG (6, "pa4s2_readend_epp: end of reading sequence\n");

  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x00);
  outbyte2 (fd, 0x04);

}

static void
pa4s2_readbegin_uni (int fd, u_char reg)
{

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_readbegin_uni: selecting register %u for '%s'\n",
       (int) reg, pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_readbegin_uni: selecting register %u at 0x%03lx\n",
       (int) reg, port[fd].base);
#endif

  outbyte0 (fd, reg | 0x58);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x04);

}

static u_char
pa4s2_readbyte_uni (int fd)
{
  u_char val;

  outbyte2 (fd, 0x05);
  val = inbyte2(fd);
  val <<= 4;
  val &= 0xE0;
  val |= (inbyte1(fd) >> 3);
  outbyte2 (fd, 0x04);

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_readbyte_uni: reading value 0x%02x from '%s'\n",
       (int) val, pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_readbyte_uni: reading value 0x%02x at 0x%03lx\n",
       (int) val, port[fd].base);
#endif

  return val;
}

static void
pa4s2_readend_uni (int fd)
{

  DBG (6, "pa4s2_readend_uni: end of reading sequence for fd %d\n", fd);

}

static void
pa4s2_readbegin_nib (int fd, u_char reg)
{

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_readbegin_nib: selecting register %u at '%s'\n",
       (int) reg, pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_readbegin_nib: selecting register %u at 0x%03lx\n",
       (int) reg, port[fd].base);
#endif


  outbyte0 (fd, reg | 0x18);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x04);

}

static u_char
pa4s2_readbyte_nib (int fd)
{

  u_char val;
  
  outbyte2 (fd, 0x05);
  val = inbyte1(fd);
  val >>= 4;
  outbyte0 (fd, 0x58);
  val |= inbyte1(fd) & 0xF0;
  val ^= 0x88;
  outbyte0 (fd, 0x00);
  outbyte2 (fd, 0x04);

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_readbyte_nib: reading value 0x%02x from '%s'\n",
  	(int) val, pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_readbyte_nib: reading value 0x%02x at 0x%03lx\n",
       (int) val, port[fd].base);
#endif

  return val;

}

static void
pa4s2_readend_nib (int fd)
{
  DBG (6, "pa4s2_readend_nib: end of reading sequence for fd %d\n", fd);
}

static void
pa4s2_writebyte_any (int fd, u_char reg, u_char val)
{

  /* somebody from Mustek asked me once, why I was writing the same
     value repeatedly to a port. Well, actually I don't know, it just
     works. Maybe the repeated writes could be replaced by appropriate
     delays or even left out completly.
   */
#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_writebyte_any: writing value 0x%02x"
       " in reg %u to '%s'\n", (int) val, (int) reg, pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_writebyte_any: writing value 0x%02x"
       " in reg %u at 0x%03lx\n", (int) val, (int) reg, port[fd].base);
#endif

  outbyte0 (fd, reg | 0x10);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x04);
  outbyte0 (fd, val);
  outbyte2 (fd, 0x05);
  outbyte2 (fd, 0x05);
  outbyte2 (fd, 0x05);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x04);
}

static int
pa4s2_enable (int fd, u_char * prelock)
{
#if defined (HAVE_LIBIEEE1284)
  int result;
  result = ieee1284_claim (pplist.portv[fd]);

  if (result)
    {
      DBG (1, "pa4s2_enable: failed to claim the port (%s)\n",
      	pa4s2_libieee1284_errorstr(result));
      return -1;
    }
#endif

  prelock[0] = inbyte0 (fd);
  prelock[1] = inbyte1 (fd);
  prelock[2] = inbyte2 (fd);
  outbyte2 (fd, (prelock[2] & 0x0F) | 0x04);

  DBG (6, "pa4s2_enable: prelock[] = {0x%02x, 0x%02x, 0x%02x}\n",
       (int) prelock[0], (int) prelock[1], (int) prelock[2]);

  outbyte0 (fd, 0x15);
  outbyte0 (fd, 0x95);
  outbyte0 (fd, 0x35);
  outbyte0 (fd, 0xB5);
  outbyte0 (fd, 0x55);
  outbyte0 (fd, 0xD5);
  outbyte0 (fd, 0x75);
  outbyte0 (fd, 0xF5);
  outbyte0 (fd, 0x01);
  outbyte0 (fd, 0x81);

  return 0;
}

static int
pa4s2_disable (int fd, u_char * prelock)
{

  if ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_ALT_LOCK) != 0)
    {

      DBG (6, "pa4s2_disable: using alternative command set\n");

      outbyte0 (fd, 0x00);
      outbyte2 (fd, 0x04);
      outbyte2 (fd, 0x06);
      outbyte2 (fd, 0x04);

    }

  outbyte2 (fd, prelock[2] & 0x0F);

  outbyte0 (fd, 0x15);
  outbyte0 (fd, 0x95);
  outbyte0 (fd, 0x35);
  outbyte0 (fd, 0xB5);
  outbyte0 (fd, 0x55);
  outbyte0 (fd, 0xD5);
  outbyte0 (fd, 0x75);
  outbyte0 (fd, 0xF5);
  outbyte0 (fd, 0x00);
  outbyte0 (fd, 0x80);

  outbyte0 (fd, prelock[0]);
  outbyte1 (fd, prelock[1]);
  outbyte2 (fd, prelock[2]);

#if defined(HAVE_LIBIEEE1284)
  ieee1284_release (pplist.portv[fd]);
#endif

  DBG (6, "pa4s2_disable: state restored\n");

  return 0;

}

static int
pa4s2_close (int fd, SANE_Status * status)
{
#if defined(HAVE_LIBIEEE1284)
  int result;
#endif
  DBG (4, "pa4s2_close: fd=%d\n", fd);

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "pa4s2_close: this is port '%s'\n", pplist.portv[fd]->name);
#else
  DBG (6, "pa4s2_close: this is port 0x%03lx\n", port[fd].base);
#endif

  DBG (5, "pa4s2_close: checking whether port is enabled\n");

  if (port[fd].enabled == SANE_TRUE)
    {

      DBG (6, "pa4s2_close: disabling port\n");
      pa4s2_disable (fd, port[fd].prelock);

    }

  DBG (5, "pa4s2_close: trying to free io port\n");
#if defined(HAVE_LIBIEEE1284)
  if ((result = ieee1284_close(pplist.portv[fd])) < 0)
#else
  if (ioperm (port[fd].base, 5, 0))
#endif
    {

#if defined(HAVE_LIBIEEE1284)
      DBG (1, "pa4s2_close: can't free port '%s' (%s)\n", 
      		pplist.portv[fd]->name, pa4s2_libieee1284_errorstr(result));
#else
      DBG (1, "pa4s2_close: can't free port 0x%03lx\n", port[fd].base);
#endif

      DBG (5, "pa4s2_close: returning SANE_STATUS_IO_ERROR\n");
      *status = SANE_STATUS_IO_ERROR;
      return -1;

    }

  DBG (5, "pa4s2_close: marking port as unused\n");

  port[fd].in_use = SANE_FALSE;

  DBG (5, "pa4s2_close: returning SANE_STATUS_GOOD\n");

  *status = SANE_STATUS_GOOD;

  return 0;

}

const char **
sanei_pa4s2_devices()
{

  SANE_Status status;
  int n;
  const char **devices;

  TEST_DBG_INIT();

  DBG (4, "sanei_pa4s2_devices: invoked\n");

  if ((n = pa4s2_init(&status)) != 0)
    {

      DBG (1, "sanei_pa4s2_devices: failed to initialize (%s)\n",
      		sane_strstatus(status));
      return calloc(1, sizeof(char *));
    }

#if defined(HAVE_LIBIEEE1284)

  if ((devices = calloc((pplist.portc + 1), sizeof(char *))) == NULL)
    {
      DBG (2, "sanei_pa4s2_devices: not enough free memory\n");
      return calloc(1, sizeof(char *));
    }

  for (n=0; n<pplist.portc; n++)
    devices[n] = pplist.portv[n]->name;

#else

  if ((devices = calloc((NELEMS (port) + 1), sizeof(char *))) == NULL)
    {
      DBG (2, "sanei_pa4s2_devices: not enough free memory\n");
      return calloc(1, sizeof(char *));
    }

  for (n=0 ; n<NELEMS (port) ; n++)
    devices[n] = (char *)port[n].name;
#endif

  return devices;
}

/*
 * Needed for SCSI-over-parallel scanners (Paragon 600 II EP)
 */
SANE_Status
sanei_pa4s2_scsi_pp_get_status(int fd, u_char *status)
{
  u_char stat;

  TEST_DBG_INIT ();

  DBG (6, "sanei_pa4s2_scsi_pp_get_status: called for fd %d\n",
       fd);

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_scsi_pp_get_status: invalid fd %d\n", fd);
      DBG (6, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_scsi_pp_get_status: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
      		pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
	   port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].enabled == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_scsi_pp_get_status: port is not enabled\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
      		pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
	   port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  outbyte2 (fd, 0x4);
  stat = inbyte1 (fd)^0x80;
  *status = (stat&0x2f)|((stat&0x10)<<2)|((stat&0x40)<<1)|((stat&0x80)>>3);
  DBG (5, "sanei_pa4s2_scsi_pp_get_status: status=0x%02X\n", *status);
  DBG (6, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;
}

/*
 * SCSI-over-parallel scanners need this done when a register is
 * selected
 */ 
SANE_Status
sanei_pa4s2_scsi_pp_reg_select (int fd, int reg)
{
  TEST_DBG_INIT ();

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_scsi_pp_reg_select: invalid fd %d\n", fd);
      DBG (6, "sanei_pa4s2_scsi_pp_reg_select: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_scsi_pp_reg_select: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
      		pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
	   port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_scsi_pp_reg_select: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].enabled == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_scsi_pp_reg_select: port is not enabled\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
      		pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
	   port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_scsi_pp_reg_select: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

#if defined(HAVE_LIBIEEE1284)
  DBG (6, "sanei_pa4s2_scsi_pp_reg_select: selecting register %u at port '%s'\n",
       (int) reg, pplist.portv[fd]->name);
#else
  DBG (6, "sanei_pa4s2_scsi_pp_reg_select: selecting register %u at 0x%03lx\n",
       (int) reg, (u_long)port[fd].base);
#endif

  outbyte0 (fd, reg | 0x58);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x06);
  outbyte2 (fd, 0x04);
  outbyte2 (fd, 0x04);

  return SANE_STATUS_GOOD;
}

/*
 * The SCSI-over-parallel scanners need to be handled a bit differently
 * when opened, as they don't return a valid ASIC ID, so this can't be
 * used for detecting valid read modes
 */
SANE_Status
sanei_pa4s2_scsi_pp_open (const char *dev, int *fd)
{

  u_char val;
  SANE_Status status;

  TEST_DBG_INIT ();

  DBG(4, "sanei_pa4s2_scsi_pp_open: called for device '%s'\n", dev);
  DBG(5, "sanei_pa4s2_scsi_pp_open: trying to connect to port\n");

  if ((*fd = pa4s2_open (dev, &status)) == -1)
    {

      DBG (5, "sanei_pa4s2_scsi_pp_open: connection failed\n");

      return status;

    }

  DBG (6, "sanei_pa4s2_scsi_pp_open: connected to device using fd %u\n", *fd);

  DBG (5, "sanei_pa4s2_scsi_pp_open: checking for scanner\n");

  if (sanei_pa4s2_enable (*fd, SANE_TRUE)!=SANE_STATUS_GOOD)
    {
      DBG (3, "sanei_pa4s2_scsi_pp_open: error enabling device\n");
      return SANE_STATUS_IO_ERROR;
    }

  /*
   * Instead of checking ASIC ID, check device status
   */
  if (sanei_pa4s2_scsi_pp_get_status(*fd, &val)!=SANE_STATUS_GOOD)
    {
      DBG (3, "sanei_pa4s2_scsi_pp_open: error getting device status\n");
      sanei_pa4s2_enable (*fd, SANE_FALSE);
      return SANE_STATUS_IO_ERROR;
    }
  val&=0xf0;

  if ((val==0xf0)||(val&0x40)||(!(val&0x20)))
    {
      DBG (3, "sanei_pa4s2_scsi_pp_open: device returned status 0x%02X\n", val);
      sanei_pa4s2_enable (*fd, SANE_FALSE);
      return SANE_STATUS_DEVICE_BUSY;
    }

  if (sanei_pa4s2_enable (*fd, SANE_FALSE)!=SANE_STATUS_GOOD)
    {
      DBG (3, "sanei_pa4s2_scsi_pp_open: error disabling device\n");
      return SANE_STATUS_IO_ERROR;
    }

  /* FIXME: it would be nice to try to use a better mode here, but how to
   * know if it's going to work? */

  DBG (4, "sanei_pa4s2_scsi_pp_open: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sanei_pa4s2_open (const char *dev, int *fd)
{

  u_char asic, val;
  SANE_Status status;

  TEST_DBG_INIT ();

  DBG(4, "sanei_pa4s2_open: called for device '%s'\n", dev);
  DBG(5, "sanei_pa4s2_open: trying to connect to port\n");

  if ((*fd = pa4s2_open (dev, &status)) == -1)
    {

      DBG (5, "sanei_pa4s2_open: connection failed\n");

      return status;

    }

  DBG (6, "sanei_pa4s2_open: connected to device using fd %u\n", *fd);

  DBG (5, "sanei_pa4s2_open: checking for scanner\n");

  sanei_pa4s2_enable (*fd, SANE_TRUE);

  DBG (6, "sanei_pa4s2_open: reading ASIC id\n");

  sanei_pa4s2_readbegin (*fd, 0);

  sanei_pa4s2_readbyte (*fd, &asic);

  sanei_pa4s2_readend (*fd);

  switch (asic)
    {

    case PA4S2_ASIC_ID_1013:
      DBG (3, "sanei_pa4s2_open: detected ASIC id 1013\n");
      break;

    case PA4S2_ASIC_ID_1015:
      DBG (3, "sanei_pa4s2_open: detected ASIC id 1015\n");
      break;

    case PA4S2_ASIC_ID_1505:
      DBG (3, "sanei_pa4s2_open: detected ASIC id 1505\n");
      break;

    default:
      DBG (1, "sanei_pa4s2_open: could not find scanner\n");
      DBG (3, "sanei_pa4s2_open: reported ASIC id 0x%02x\n",
	   asic);

      sanei_pa4s2_enable (*fd, SANE_FALSE);
      DBG (5, "sanei_pa4s2_open: closing port\n");

      sanei_pa4s2_close (*fd);

      DBG (5, "sanei_pa4s2_open: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  sanei_pa4s2_enable (*fd, SANE_FALSE);

  DBG (4, "sanei_pa4s2_open: trying better modes\n");

  while (port[*fd].mode <= PA4S2_MODE_EPP)
    {

      if ((port[*fd].mode == PA4S2_MODE_UNI) &&
      ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_TRY_MODE_UNI) == 0))
	{

	  DBG (3, "sanei_pa4s2_open: skipping mode UNI\n");
	  port[*fd].mode++;
	  continue;

	}

      if ((port[*fd].mode == PA4S2_MODE_EPP) &&
      ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_NO_EPP) != 0))
	{
	  DBG (3, "sanei_pa4s2_open: skipping mode EPP\n");
	  break;
	}


      DBG (5, "sanei_pa4s2_open: trying mode %u\n", port[*fd].mode);

      sanei_pa4s2_enable (*fd, SANE_TRUE);

      sanei_pa4s2_readbegin (*fd, 0);

      sanei_pa4s2_readbyte (*fd, &val);

      if (val != asic)
	{

	  sanei_pa4s2_readend (*fd);
	  sanei_pa4s2_enable (*fd, SANE_FALSE);
	  DBG (5, "sanei_pa4s2_open: mode failed\n");
	  DBG (6, "sanei_pa4s2_open: returned ASIC-ID 0x%02x\n",
	       (int) val);
	  break;

	}

      sanei_pa4s2_readend (*fd);
      sanei_pa4s2_enable (*fd, SANE_FALSE);

      DBG (5, "sanei_pa4s2_open: mode works\n");

      port[*fd].mode++;

    }

  port[*fd].mode--;

  if ((port[*fd].mode == PA4S2_MODE_UNI) &&
      ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_TRY_MODE_UNI) == 0))
    {
      port[*fd].mode--;
    }

  DBG (5, "sanei_pa4s2_open: using mode %u\n", port[*fd].mode);

  DBG (4, "sanei_pa4s2_open: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;

}

void
sanei_pa4s2_close (int fd)
{

  SANE_Status status;

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_close: fd = %d\n", fd);

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_close: fd %d is invalid\n", fd);
      DBG (5, "sanei_pa4s2_close: failed\n");
      return;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_close: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_close: failed\n");
      return;

    }

  DBG (5, "sanei_pa4s2_close: freeing resources\n");

  if (pa4s2_close (fd, &status) == -1)
    {

      DBG (2, "sanei_pa4s2_close: could not close scanner\n");
      DBG (5, "sanei_pa4s2_close: failed\n");
      return;
    }

  DBG (5, "sanei_pa4s2_close: finished\n");

}

SANE_Status
sanei_pa4s2_enable (int fd, int enable)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_enable: called for fd %d with value %d\n",
       fd, enable);

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_enable: fd %d is invalid\n", fd);
      DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_enable: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if ((enable != SANE_TRUE) && (enable != SANE_FALSE))
    {

      DBG (2, "sanei_pa4s2_enable: invalid value %d\n", enable);
      DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if ((unsigned int) enable == port[fd].enabled)
    {

      DBG (3, "sanei_pa4s2_enable: senseless call...\n");
      DBG (4, "sanei_pa4s2_enable: aborting\n");
      DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_GOOD\n");

      return SANE_STATUS_GOOD;

    }

  if (enable == SANE_TRUE)
    {

#if defined(HAVE_LIBIEEE1284)
      DBG (4, "sanei_pa4s2_enable: enable port '%s'\n", pplist.portv[fd]->name);
#else
      DBG (4, "sanei_pa4s2_enable: enable port 0x%03lx\n", port[fd].base);

      /* io-permissions are not inherited after fork (at least not on
         linux 2.2, although they seem to be inherited on linux 2.4),
         so we should make sure we get the permission */
      
      if (ioperm (port[fd].base, 5, 1))
      {
          DBG (1, "sanei_pa4s2_enable: cannot get io privilege for port"
	       " 0x%03lx\n", port[fd].base);

          DBG (5, "sanei_pa4s2_enable:: marking port[%d] as unused\n", fd);
          port[fd].in_use = SANE_FALSE;

          DBG (5, "sanei_pa4s2_enable:: returning SANE_STATUS_IO_ERROR\n");
          return SANE_STATUS_IO_ERROR;
      }
#endif

      if (pa4s2_enable (fd, port[fd].prelock) != 0)
        {
     	  DBG (1, "sanei_pa4s2_enable: failed to enable port\n");
	  DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_IO_ERROR\n");

	  return SANE_STATUS_IO_ERROR;
	}

    }
  else
    {

#if defined(HAVE_LIBIEEE1284)
      DBG (4, "sanei_pa4s2_enable: disable port '%s'\n", 
      		pplist.portv[fd]->name);
#else
      DBG (4, "sanei_pa4s2_enable: disable port 0x%03lx\n", port[fd].base);
#endif

      pa4s2_disable (fd, port[fd].prelock);

    }

  port[fd].enabled = enable;

  DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sanei_pa4s2_readbegin (int fd, u_char reg)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_readbegin: called for fd %d and register %u\n",
       fd, (int) reg);

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_readbegin: invalid fd %d\n", fd);
      DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_readbegin: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].enabled == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_readbegin: port is not enabled\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  switch (port[fd].mode)
    {

    case PA4S2_MODE_EPP:

      DBG (5, "sanei_pa4s2_readbegin: EPP readbegin\n");
      pa4s2_readbegin_epp (fd, reg);
      break;

    case PA4S2_MODE_UNI:

      DBG (5, "sanei_pa4s2_readbegin: UNI readbegin\n");
      pa4s2_readbegin_uni (fd, reg);
      break;

    case PA4S2_MODE_NIB:

      DBG (5, "sanei_pa4s2_readbegin: NIB readbegin\n");
      pa4s2_readbegin_nib (fd, reg);
      break;

    default:

      DBG (1, "sanei_pa4s2_readbegin: port info broken\n");
      DBG (3, "sanei_pa4s2_readbegin: invalid port mode\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readbegin: return SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sanei_pa4s2_readbyte (int fd, u_char * val)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_readbyte: called with fd %d\n", fd);

  if (val == NULL)
    {

      DBG (1, "sanei_pa4s2_readbyte: got NULL pointer as result buffer\n");
      return SANE_STATUS_INVAL;

    }

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_readbyte: invalid fd %d\n", fd);
      DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");
      return SANE_STATUS_INVAL;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_readbyte: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].enabled == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_readbyte: port is not enabled\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  DBG (4, "sanei_pa4s2_readbyte: we hope, the backend called\n");
  DBG (4, "sanei_pa4s2_readbyte: readbegin, so the port is ok...\n");

  DBG (6, "sanei_pa4s2_readbyte: this means, I did not check it - it's\n");
  DBG (6, "sanei_pa4s2_readbyte: not my fault, if your PC burns down.\n");

  switch (port[fd].mode)
    {

    case PA4S2_MODE_EPP:

      DBG (5, "sanei_pa4s2_readbyte: read in EPP mode\n");
      *val = pa4s2_readbyte_epp (fd);
      break;


    case PA4S2_MODE_UNI:

      DBG (5, "sanei_pa4s2_readbyte: read in UNI mode\n");
      *val = pa4s2_readbyte_uni (fd);
      break;


    case PA4S2_MODE_NIB:

      DBG (5, "sanei_pa4s2_readbyte: read in NIB mode\n");
      *val = pa4s2_readbyte_nib (fd);
      break;

    default:

      DBG (1, "sanei_pa4s2_readbyte: port info broken\n");
      DBG (2, "sanei_pa4s2_readbyte: probably the port wasn't"
	   " correct configured...\n");
      DBG (3, "sanei_pa4s2_readbyte: invalid port mode\n");
      DBG (6, "sanei_pa4s2_readbyte: port mode %u\n",
	   port[fd].mode);
      DBG (6, "sanei_pa4s2_readbyte: I told you!!!\n");
      DBG (5, "sanei_pa4s2_readbyte: return"
	   " SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;
    }

  DBG (5, "sanei_pa4s2_readbyte: read finished\n");

  DBG (6, "sanei_pa4s2_readbyte: got value 0x%02x\n", (int) *val);

  DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;

}

SANE_Status
sanei_pa4s2_readend (int fd)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_readend: called for fd %d\n", fd);

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_readend: invalid fd %d\n", fd);
      DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_readend: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].enabled == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_readend: port is not enabled\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  DBG (4, "sanei_pa4s2_readend: we hope, the backend called\n");
  DBG (4, "sanei_pa4s2_readend: readbegin, so the port is ok...\n");

  DBG (6, "sanei_pa4s2_readend: this means, I did not check it - it's\n");
  DBG (6, "sanei_pa4s2_readend: not my fault, if your PC burns down.\n");

  switch (port[fd].mode)
    {

    case PA4S2_MODE_EPP:

      DBG (5, "sanei_pa4s2_readend: EPP mode readend\n");
      pa4s2_readend_epp (fd);
      break;


    case PA4S2_MODE_UNI:

      DBG (5, "sanei_pa4s2_readend: UNI mode readend\n");
      pa4s2_readend_uni (fd);
      break;


    case PA4S2_MODE_NIB:

      DBG (5, "sanei_pa4s2_readend: NIB mode readend\n");
      pa4s2_readend_nib (fd);
      break;

    default:

      DBG (1, "sanei_pa4s2_readend: port info broken\n");
      DBG (2, "sanei_pa4s2_readend: probably the port wasn't"
	   " correct configured...\n");
      DBG (3, "sanei_pa4s2_readend: invalid port mode\n");
      DBG (6, "sanei_pa4s2_readend: port mode %u\n",
	   port[fd].mode);
      DBG (6, "sanei_pa4s2_readend: I told you!!!\n");
      DBG (5, "sanei_pa4s2_readend: return"
	   " SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;
    }


  DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;

}

SANE_Status
sanei_pa4s2_writebyte (int fd, u_char reg, u_char val)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_writebyte: called for fd %d, reg %u and val %u\n",
       fd, (int) reg, (int) val);

#if defined(HAVE_LIBIEEE1284)
  if ((fd < 0) || (fd >= pplist.portc))
#else
  if ((fd < 0) || (fd >= NELEMS (port)))
#endif
    {

      DBG (2, "sanei_pa4s2_writebyte: invalid fd %d\n", fd);
      DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].in_use == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_writebyte: port is not in use\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  if (port[fd].enabled == SANE_FALSE)
    {

      DBG (2, "sanei_pa4s2_writebyte: port is not enabled\n");
#if defined(HAVE_LIBIEEE1284)
      DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
#else
      DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
#endif
      DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  switch (port[fd].mode)
    {

    case PA4S2_MODE_EPP:
    case PA4S2_MODE_UNI:
    case PA4S2_MODE_NIB:

      DBG (5, "sanei_pa4s2_writebyte: NIB/UNI/EPP write\n");
      pa4s2_writebyte_any (fd, reg, val);
      break;

    default:

      DBG (1, "sanei_pa4s2_writebyte: port info broken\n");
      DBG (3, "sanei_pa4s2_writebyte: invalid port mode\n");
      DBG (6, "sanei_pa4s2_writebyte: port mode %u\n",
	   port[fd].mode);
      DBG (5, "sanei_pa4s2_writebyte: return"
	   " SANE_STATUS_INVAL\n");

      return SANE_STATUS_INVAL;

    }

  DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sanei_pa4s2_options (u_int * options, int set)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_options: called with options %u and set = %d\n",
       *options, set);

  if ((set != SANE_TRUE) && (set != SANE_FALSE))
    DBG (2, "sanei_pa4s2_options: value of set is invalid\n");

  if ((set == SANE_TRUE) && (*options > 7))
    DBG (2, "sanei_pa4s2_options: value of *options is invalid\n");

  if (set == SANE_TRUE)
    {

      DBG (5, "sanei_pa4s2_options: setting options to %u\n", *options);

      sanei_pa4s2_interface_options = *options;

    }
  else
    {

      DBG (5, "sanei_pa4s2_options: options are set to %u\n",
	   sanei_pa4s2_interface_options);

      *options = sanei_pa4s2_interface_options;

    }

  DBG (5, "sanei_pa4s2_options: returning SANE_STATUS_GOOD\n");

  return SANE_STATUS_GOOD;

}

#else /* !HAVE_IOPERM */


SANE_Status
sanei_pa4s2_open (const char *dev, int *fd)
{

  TEST_DBG_INIT ();

  if (fd)
  	*fd = -1;

  DBG (4, "sanei_pa4s2_open: called for device `%s`\n", dev);
  DBG (3, "sanei_pa4s2_open: A4S2 support not compiled\n");
  DBG (6, "sanei_pa4s2_open: basically, this backend does only compile\n");
  DBG (6, "sanei_pa4s2_open: on x86 architectures. Furthermore it\n");
  DBG (6, "sanei_pa4s2_open: needs ioperm() and inb()/outb() calls.\n");
  DBG (6, "sanei_pa4s2_open: alternativly it makes use of libieee1284\n");
  DBG (6, "sanei_pa4s2_open: (which isn't present either)\n");
  DBG (5, "sanei_pa4s2_open: returning SANE_STATUS_INVAL\n");

  return SANE_STATUS_INVAL;

}

void
sanei_pa4s2_close (int fd)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_close: called for fd %d\n", fd);
  DBG (2, "sanei_pa4s2_close: fd %d is invalid\n", fd);
  DBG (3, "sanei_pa4s2_close: A4S2 support not compiled\n");
  DBG (6, "sanei_pa4s2_close: so I wonder, why this function is called"
       " anyway.\n");
  DBG (6, "sanei_pa4s2_close: maybe this is a bug in the backend.\n");
  DBG (5, "sanei_pa4s2_close: returning\n");

  return;
}

SANE_Status
sanei_pa4s2_enable (int fd, int enable)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_enable: called for fd %d with value=%d\n",
       fd, enable);
  DBG (2, "sanei_pa4s2_enable: fd %d is invalid\n", fd);

  if ((enable != SANE_TRUE) && (enable != SANE_FALSE))
    DBG (2, "sanei_pa4s2_enable: value %d is invalid\n", enable);

  DBG (3, "sanei_pa4s2_enable: A4S2 support not compiled\n");
  DBG (6, "sanei_pa4s2_enable: oops, I think there's someone going to\n");
  DBG (6, "sanei_pa4s2_enable: produce a lot of garbage...\n");
  DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");

  return SANE_STATUS_INVAL;
}

SANE_Status
sanei_pa4s2_readbegin (int fd, u_char reg)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_readbegin: called for fd %d and register %d\n",
       fd, (int) reg);
  DBG (2, "sanei_pa4s2_readbegin: fd %d is invalid\n", fd);

  DBG (3, "sanei_pa4s2_readbegin: A4S2 support not compiled\n");
  DBG (6, "sanei_pa4s2_readbegin: don't look - this is going to be\n");
  DBG (6, "sanei_pa4s2_readbegin: worse then you'd expect...\n");
  DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");

  return SANE_STATUS_INVAL;

}

SANE_Status
sanei_pa4s2_readbyte (int fd, u_char * val)
{

  TEST_DBG_INIT ();

  if (val)
  	*val = 0;

  DBG (4, "sanei_pa4s2_readbyte: called for fd %d\n", fd);
  DBG (2, "sanei_pa4s2_readbyte: fd %d is invalid\n", fd);
  DBG (3, "sanei_pa4s2_readbyte: A4S2 support not compiled\n");
  DBG (6, "sanei_pa4s2_readbyte: shit happens\n");
  DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");

  return SANE_STATUS_INVAL;
}

SANE_Status
sanei_pa4s2_readend (int fd)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_readend: called for fd %d\n", fd);
  DBG (2, "sanei_pa4s2_readend: fd %d is invalid\n", fd);
  DBG (3, "sanei_pa4s2_readend: A4S2 support not compiled\n");
  DBG (6, "sanei_pa4s2_readend: it's too late anyway\n");
  DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");

  return SANE_STATUS_INVAL;

}

SANE_Status
sanei_pa4s2_writebyte (int fd, u_char reg, u_char val)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_writebyte: called for fd %d and register %d, "
       "value = %u\n", fd, (int) reg, (int) val);
  DBG (2, "sanei_pa4s2_writebyte: fd %d is invalid\n", fd);
  DBG (3, "sanei_pa4s2_writebyte: A4S2 support not compiled\n");
  DBG (6, "sanei_pa4s2_writebyte: whatever backend you're using, tell\n");
  DBG (6, "sanei_pa4s2_writebyte: the maintainer his code has bugs...\n");
  DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_INVAL\n");

  return SANE_STATUS_INVAL;

}

SANE_Status
sanei_pa4s2_options (u_int * options, int set)
{

  TEST_DBG_INIT ();

  DBG (4, "sanei_pa4s2_options: called with options %u and set = %d\n",
       *options, set);

  if ((set != SANE_TRUE) && (set != SANE_FALSE))
    DBG (2, "sanei_pa4s2_options: value of set is invalid\n");

  if ((set == SANE_TRUE) && (*options > 3))
    DBG (2, "sanei_pa4s2_options: value of *options is invalid\n");

  DBG (3, "sanei_pa4s2_options: A4S2 support not compiled\n");
  DBG (5, "sanei_pa4s2_options: returning SANE_STATUS_INVAL\n");

  return SANE_STATUS_INVAL;

}

const char **
sanei_pa4s2_devices()
{
  TEST_DBG_INIT ();
  DBG (4, "sanei_pa4s2_devices: invoked\n");

  DBG (3, "sanei_pa4s2_devices: A4S2 support not compiled\n");
  DBG (5, "sanei_pa4s2_devices: returning empty list\n");

  return calloc(1, sizeof(char *));
}

SANE_Status
sanei_pa4s2_scsi_pp_get_status(int fd, u_char *status)
{
  TEST_DBG_INIT ();
  DBG (4, "sanei_pa4s2_scsi_pp_get_status: fd=%d, status=%p\n",
       fd, (void *) status);
  DBG (3, "sanei_pa4s2_scsi_pp_get_status: A4S2 support not compiled\n");
  return SANE_STATUS_UNSUPPORTED;
}

SANE_Status
sanei_pa4s2_scsi_pp_reg_select (int fd, int reg)
{
  TEST_DBG_INIT ();
  DBG (4, "sanei_pa4s2_scsi_pp_reg_select: fd=%d, reg=%d\n",
       fd, reg);
  DBG (3, "sanei_pa4s2_devices: A4S2 support not compiled\n");
  return SANE_STATUS_UNSUPPORTED;
}

SANE_Status
sanei_pa4s2_scsi_pp_open (const char *dev, int *fd)
{
  TEST_DBG_INIT ();
  DBG (4, "sanei_pa4s2_scsi_pp_open: dev=%s, fd=%p\n",
       dev, (void *) fd);
  DBG (3, "sanei_pa4s2_scsi_pp_open: A4S2 support not compiled\n");
  return SANE_STATUS_UNSUPPORTED;
}

#endif /* !HAVE_IOPERM */
