

/*
 * ieee1284.c:
 *
 * Copyright (c) 2001 James McKenzie <james@fishsoup.dhs.org>,
 * All rights reserved.
 *
 */

static char rcsid[] =
  "$Id: ieee1284.c,v 1.25 2001/10/12 13:51:21 root Exp root $";


/*
 * $Log: ieee1284.c,v $
 * Revision 1.25  2001/10/12 13:51:21  root
 * #
 *
 * Revision 1.18  2001/01/07 22:36:20  root
 * #
 *
 * Revision 1.17  2001/01/07 22:26:43  root
 * #
 *
 * Revision 1.16  2001/01/07 17:34:54  root
 * #
 *
 * Revision 1.15  2001/01/07 15:58:09  root
 * #
 *
 * Revision 1.14  2001/01/07 15:14:43  root
 * #
 *
 * Revision 1.13  2001/01/07 03:48:47  root
 * #
 *
 * Revision 1.12  2001/01/06 18:11:23  root
 * #
 *
 * Revision 1.11  2001/01/06 16:51:57  root
 * #
 *
 * Revision 1.10  2001/01/05 03:41:34  root
 * #
 *
 * Revision 1.9  2001/01/04 22:04:57  root
 * #
 *
 * Revision 1.8  2001/01/04 22:01:29  root
 * #
 *
 * Revision 1.7  2001/01/04 15:51:12  root
 * #
 *
 * Revision 1.6  2001/01/04 15:51:02  root
 * #
 *
 * Revision 1.5  2001/01/04 13:52:31  root
 * #
 *
 * Revision 1.4  2001/01/04 06:07:21  root
 * #
 *
 * Revision 1.3  2001/01/04 04:39:27  root
 * #
 *
 * Revision 1.2  2001/01/04 02:57:39  root
 * #
 *
 */


#include "project.h"
#include <sys/io.h>

#ifdef ALPHA
#include <asm/io.h>
#define inb_p(a) inb(a)
#define outb_p(a,b) outb(a,b)
#endif

static int base = 0;
static int ecp = 0;

#define WAITLOOP 80000
#define LWAITTIME  8
#define WAITTIME  4

#define DIRWAIT  usleep(1000)

#define DATA (base)
#define ECPAFIFO (base)
#define DSR (base+1)
#define DCR (base+2)
#define CFIFO (base+0x400)
#define ECPDFIFO (base+0x400)
#define TFIFO (base+0x400)
#define CNFGA (base+0x400)
#define CNFGB (base+0x401)
#define ECR (base+0x402)


#define DSR_NBUSY	0x80
#define DSR_NACK	0x40
#define DSR_PERROR	0x20
#define DSR_SELECT	0x10
#define DSR_NFAULT	0x08

#define DCR_DIRECTION	0x20
#define DCR_ACKINTEN	0x10
#define DCR_SELECTIN	0x08
#define DCR_NINIT	0x04
#define DCR_AUTOFD	0x02
#define DCR_STROBE	0x01

#define ECR_MODE_SPP		0x00
#define ECR_MODE_EPP		0x20
#define ECR_MODE_SPP_FIFO	0x40
#define ECR_MODE_ECP		0x60
#define ECR_MODE_CONF		0xe0

#define ECR_NERRINTREN		0x10
#define ECR_DMAEN		0x08
#define ECR_SERVICEINTR		0x04
#define ECR_FULL		0x02
#define ECR_EMPTY		0x01



#define inv(a) ((a=='+') ? '-' :'+')

__inline__ static void long_halt(void)
{
usleep(1000);
}

__inline__ static void short_halt(void)
{
}

static void
do_dcr (int c, int v)
{
  if (v & 0x20)
    printf (" %cReadEn", c);
  if (v & 0x10)
    printf (" %cIntEn", c);
  if (v & 0x08)
    printf (" %cSelectIn", inv (c));
  if (v & 0x04)
    printf (" %cnInit", c);
  if (v & 0x02)
    printf (" %cnAutoFd", inv (c));
  if (v & 0x01)
    printf (" %cnStrobe", inv (c));
}

static void
do_dcrud (int up, int down)
{
  do_dcr ('+', up);
  do_dcr ('-', down);
}


static void
do_dsr (int c, int v)
{

  if (v & 0x80)
    printf (" %cBusy", inv (c));
  if (v & 0x40)
    printf (" %cnAck", c);
  if (v & 0x20)
    printf (" %cPError", c);
  if (v & 0x10)
    printf (" %cSelect", c);
  if (v & 0x08)
    printf (" %cnFault", c);
}


static void
do_dsrud (int up, int down)
{
  do_dsr ('+', up);
  do_dsr ('-', down);
}


static void
set_dcr (int new)
{
  static int old = 0;
  int up, down;

#ifdef DEBUG
  up = (new ^ old) & new;
  down = (new ^ old) & ~new;

  printf ("DCR:  %02x (last %02x +%02x -%02x)", new, old, up, down);
  do_dcrud (up, down);
  printf ("\n");
#endif

  old = new;
  outb_p (new, DCR);
}

static void
assert_dcr (int what)
{
  int i = inb_p (DCR);

  i |= what;

  set_dcr (i);
}

static void
deassert_dcr (int what)
{
  int i = inb_p (DCR);

  i &= ~what;

  set_dcr (i);
}


static int
read_dsr (void)
{
  static int old = 0;
  int new = inb_p (DSR);
  int up, down;

  up = (new ^ old) & new;
  down = (new ^ old) & ~new;

#ifdef DEBUG
  printf ("DSR:  %02x (was  %02x +%02x -%02x)", new, old, up, down);
  do_dsrud (up, down);
  printf ("\n");
#endif

  old = new;

  return new;
}

static void
write_data (int i)
{
#ifdef DEBUG
  printf ("DATA: W %02x\n", i);
#endif
  outb_p (i, DATA);
}

static int
read_data (void)
{
  int i = inb_p (DATA);
#ifdef DEBUG
  printf ("DATA: R %02x\n", i);
#endif
  return i;
}



static void
write_ecr (int i)
{
#ifdef DEBUG
  printf ("ECR:  W %02x\n", i);
#endif
  outb_p (i, ECR);
}
static int
read_ecr (void)
{
  int i = inb_p (ECR);
#ifdef DEBUG
  printf ("ECR:  R %02x\n", i);
#endif
  return i;
}

static int
timeup (int t, struct timeval *then)
{
  struct timeval now, diff;

  gettimeofday (&now, NULL);

  timersub (&now, then, &diff);

#ifdef DEBUG
  printf ("Timeup %d.%06d of %d\n", diff.tv_sec, diff.tv_usec, t);
#endif

  if (diff.tv_sec >= t)
    return 1;

  return 0;
}

static int
ecr_wait (int t, int a, int d)
{
  int i;
  struct timeval start;

#ifdef DEBUG
  read_ecr ();
  printf ("ECR:  Wait +%02x -%02x %02x...", a, d, inb_p (ECR));
  fflush (stdout);
#endif

  gettimeofday (&start, NULL);

  i = 0;
  while ((inb_p (ECR) & a) != a)
    {

      if (i > WAITLOOP)
        {
	  long_halt();
          if (timeup (t, &start))
            {

#ifdef DEBUG
              printf ("+Failed %02x\n", inb_p (ECR));
              read_ecr ();
#endif
              return 0;
            }
          i = 0;
        }
	  short_halt();
      i++;
    }


  while ((inb_p (ECR) & d))
    {

      if (i > WAITLOOP)
        {
	  long_halt();
          if (timeup (t, &start))
            {

#ifdef DEBUG
              printf ("-Failed %02x\n", inb_p (ECR));
              read_ecr ();
#endif
              return 0;
            }
          i = 0;
        }
	  short_halt();
      i++;
    }

#ifdef DEBUG
  printf ("Ok %02x\n", inb_p (ECR));
  read_ecr ();
#endif

  return 1;
}


static int
dsr_wait (int t, int a, int d)
{
  struct timeval start;
  int i = 0;

#ifdef DEBUG
  read_dsr ();
  printf ("DSR:  Wait +%02x -%02x %02x...", a, d, inb_p (DSR));
  fflush (stdout);
#endif

  gettimeofday (&start, NULL);


  while ((inb_p (DSR) & a) != a)
    {
      if (i > WAITLOOP)
        {
	long_halt();
          if (timeup (t, &start))
            {
#ifdef DEBUG
              printf ("+Failed %02x\n", inb_p (DSR));
              read_dsr ();
#endif
              return 0;
            }
          i = 0;
        }
	short_halt();
      i++;
    }

  while ((inb_p (DSR) & d))
    {
      if (i > WAITLOOP)
        {
	long_halt();
          if (timeup (t, &start))
            {
#ifdef DEBUG
              printf ("-Failed %02x\n", inb_p (DSR));
              read_dsr ();
#endif
              return 0;
            }
          i = 0;
        }
	short_halt();
      i++;
    }

#ifdef DEBUG
  printf ("Ok %02x\n", inb_p (DSR));
  read_dsr ();
#endif

  return 1;
}


static int
not1284_setup (void)
{
#ifdef DEBUG
  printf ("NOT1284\n");
#endif
  deassert_dcr (DCR_AUTOFD);
  assert_dcr (DCR_NINIT);

  if (!dsr_wait (WAITTIME, DSR_PERROR | DSR_NFAULT, 0))
    return 0;

  deassert_dcr (DCR_DIRECTION);
  assert_dcr (DCR_SELECTIN | DCR_NINIT);

  if (!dsr_wait (WAITTIME, DSR_PERROR | DSR_NFAULT, 0))
    return 0;

  assert_dcr (DCR_AUTOFD);

  if (!dsr_wait (WAITTIME, DSR_NACK | DSR_SELECT, DSR_PERROR))
    return 0;

  deassert_dcr (DCR_AUTOFD);


  if (!dsr_wait (WAITTIME, DSR_NBUSY, 0))
    return 0;

  return 1;
}


static int
ieee1284_negsetup (int i)
{
#ifdef DEBUG
  printf ("1284_negsetup\n");
#endif

  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_EPP);
  write_data (i);

  assert_dcr (DCR_AUTOFD);      //Autofd-

  deassert_dcr (DCR_SELECTIN);  //Selectin+

  if (!dsr_wait (WAITTIME, DSR_PERROR | DSR_SELECT | DSR_NFAULT, DSR_NACK))
    return 0;

  assert_dcr (DCR_STROBE);
  deassert_dcr (DCR_STROBE | DCR_AUTOFD);

  if (!dsr_wait (WAITTIME, DSR_NBUSY | DSR_SELECT, DSR_PERROR))
    return 0;

  if (!dsr_wait (WAITTIME, DSR_NACK, 0))
    return 0;

  assert_dcr (DCR_AUTOFD);

  if (!dsr_wait (WAITTIME, DSR_PERROR, 0))
    return 0;

  return 1;

}

static void
foo (void)
{
#ifdef DEBUG
  printf ("foo\n");
#endif
  set_dcr (0xc);

//Something about setting AUTOFD if 
  //0x78 and not 0xa0
}

int
ieee1284_writebyte (unsigned char b)
{
#ifdef DEBUG
  printf ("1284_writebyte(%x)\n", b);
#endif
  if (!dsr_wait (WAITTIME, DSR_NBUSY, 0))
    return 0;

  write_data (b);

  assert_dcr (DCR_STROBE);

  if (!dsr_wait (WAITTIME, 0, DSR_NBUSY))
    {
      MOAN ("ieee1284_writebyte: failed\n");
      return 0;
    }

  deassert_dcr (DCR_STROBE | DCR_AUTOFD);

  if (!dsr_wait (WAITTIME, DSR_NBUSY, 0))
    {
      MOAN ("ieee1284_writebyte: failed\n");
      return 0;
    }

  assert_dcr (DCR_AUTOFD);


  return 1;
}

int
ieee1284_ftor (void)
{
#ifdef DEBUG
  printf ("1284_ftor()\n");
#endif
  assert_dcr (DCR_DIRECTION);
  assert_dcr (DCR_AUTOFD);
  DIRWAIT;
  deassert_dcr (DCR_NINIT);
  if (!dsr_wait (LWAITTIME, 0, DSR_PERROR))
    {
      MOAN ("ieee1284_ftor: failed\n");
      return 0;
    }
  return 1;
}

int
ieee1284_readbyte (unsigned char *i)
{
#ifdef DEBUG
  printf ("1284_readbyte()=");
  fflush (stdout);
#endif

  assert_dcr (DCR_AUTOFD);
  if (!dsr_wait (LWAITTIME, 0, DSR_NACK))
    {
#ifdef DEBUG
      printf ("Failed\n");
#endif
      MOAN ("ieee1284_readbyte: failed\n");
      return 0;
    }
  *i = read_data ();
  deassert_dcr (DCR_AUTOFD);
  if (!dsr_wait (LWAITTIME, DSR_NACK, 0))
    {
#ifdef DEBUG
      printf ("Failed\n");
#endif
      MOAN ("ieee1284_readbyte: failed\n");
      return 0;
    }
  assert_dcr (DCR_AUTOFD);
#ifdef DEBUG
  printf ("%02x\n", *i);
#endif

  return 1;
}


int
ieee1284_rtof (void)
{
#ifdef DEBUG
  printf ("1284_ftor()\n");
#endif
  assert_dcr (DCR_NINIT);
  DIRWAIT;
  if (!dsr_wait (WAITTIME, DSR_PERROR | DSR_NBUSY | DSR_NACK, 0))
    {
      MOAN ("ieee1284_rtof: failed\n");
      return 0;
    }
  deassert_dcr (DCR_DIRECTION);
  return 1;
}




int
ieee1284_term (void)
{
#ifdef DEBUG
  printf ("1284_term\n");
#endif
  deassert_dcr (DCR_AUTOFD);
  assert_dcr (DCR_SELECTIN);

  if (!dsr_wait (WAITTIME, DSR_PERROR, 0))
    return 0;
#if 0
  if (!dsr_wait (WAITTIME, DSR_NACK, 0))
    return 0;
#endif

  assert_dcr (DCR_AUTOFD | DCR_NINIT);

#if 0
  if (!dsr_wait (WAITTIME, DSR_NACK, 0))
    return 0;
  deassert_dcr (DCR_AUTOFD);
#endif

  return 1;
}





int
yepp_wakeupandping (void)
{
  unsigned char j;
  int trys = 0;

  do
    {
      fprintf (stderr, "yepp_wakeupandping: try %d\n", ++trys);
      foo ();
      if (!ieee1284_negsetup (0x10))
        not1284_setup ();


      if (ieee1284_writebyte (0xa))
        {

          j = 0;

          if (((read_dsr () & 0xa0) == 0xa0))
            {                   // FIXME:

              ieee1284_ftor ();
              ieee1284_readbyte (&j);
              ieee1284_rtof ();
            }

          ieee1284_term ();

          if (j == 1)
            {
              fprintf (stderr, "yepp_wakeupandping: success\n");
              return 1;
            }
        }


      foo ();

      sleep (2);
      usleep (10000);
      set_dcr (0xe);
      usleep (10000);
      set_dcr (0xc);


    }
  while (trys < 4);

  MOAN ("yepp_wakeupandping: failed\n");


  return 0;
}

int
yepp_phase1 (void)
{
  foo ();
  if (!ieee1284_negsetup (0x10))
    {
      MOAN ("yepp_phase1: failed\n");
      return 0;
    }
  return 1;
}

int
yepp_phase2 (void)
{
  if (!ieee1284_ftor ())
    {
      MOAN ("yepp_phase2: failed\n");
      return 0;
    }
  return 1;
}

int
yepp_phase3 (void)
{
  if (!ieee1284_rtof ())
    {
      MOAN ("yepp_phase3: failed\n");
      return 0;
    }
  return 1;
}

void
yepp_phase4 (void)
{
  ieee1284_term ();

  deassert_dcr (DCR_AUTOFD);

//set_dcr(0x0c);
}


void
yepp_release (void)
{
  write_data (0x0);
  set_dcr (0x0c);
  set_dcr (0x0b);
}


int
yepp_read (char *buf, int i)
{
  int red = 0;

#ifdef DEBUG
  printf ("Yeppread %d...", i);
  fflush (stdout);
#endif
  while (i--)
    {
      if (!ieee1284_readbyte (buf))
        {
#ifdef DEBUG
          printf ("%d (Failed)\n", red);
#endif
          MOAN ("yepp_read: short (%d/%d) write\n", red, i);
          return red;
        }
      red++;
      buf++;
    }
#ifdef DEBUG
  printf ("%d (OK)\n", red);
#endif
  return red;
}


static int
yepp_ecp_write (char *buf, int i)
{
  int red;


#ifdef DEBUG
  printf ("ECP Yeppwrite %d...", i);
  fflush (stdout);
#endif

  red = 0;

  if (!dsr_wait (WAITTIME, DSR_NBUSY, 0))
    {
#ifdef DEBUG
      printf ("%d (Failed)\n", red);
#endif
      MOAN ("yepp_ecp_write: short (%d/%d) write\n", red, i);
      return red;
    }

  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_ECP);

  deassert_dcr (DCR_AUTOFD);


  while (i--)
    {
      if (!ecr_wait (WAITTIME, 0, ECR_FULL))
        {
#ifdef DEBUG
          printf ("%d (Failed)\n", red);
#endif
          assert_dcr (DCR_AUTOFD);
          write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_EPP);
          MOAN ("yepp_ecp_write: short (%d/%d) write\n", red, i);
          return red;
        }

      outb (*buf, ECPDFIFO);

      red++;
      buf++;
    }

  ecr_wait (WAITTIME, ECR_EMPTY, 0);

  assert_dcr (DCR_AUTOFD);
  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_EPP);

#ifdef DEBUG
  printf ("%d (OK)\n", red);
#endif
  return red;
}


int
yepp_write (char *buf, int i)
{
  int red = 0;

  if (ecp)
    return yepp_ecp_write (buf, i);

#ifdef DEBUG
  printf ("Yeppwrite %d...", i);
  fflush (stdout);
#endif
  while (i--)
    {
      if (!ieee1284_writebyte (*buf))
        {
#ifdef DEBUG
          printf ("%d (Failed)\n", red);
#endif
          MOAN ("yepp_write: short (%d/%d) write\n", red, i);
          return red;
        }
      red++;
      buf++;
    }
#ifdef DEBUG
  printf ("%d (OK)\n", red);
#endif
  return red;
}

static int
ieee1284_probe (void)
{
//Incase direction==in
  fprintf (stderr, "ieee1284_probe: probing at 0x%04x\n", base);

  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_EPP);
  deassert_dcr (DCR_DIRECTION);

  write_data (0x55);
  if (read_data () != 0x55)
    return 0;
  write_data (0xaa);
  if (read_data () != 0xaa)
    return 0;

//Ok port exists check for PS2 mode
  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_EPP);

  assert_dcr (DCR_DIRECTION);
  write_data (0x55);

  if (read_data () == 0x55)
    {
      deassert_dcr (DCR_DIRECTION);
      fprintf (stderr,
               "ieee1284_probe: port at 0x%04x is not bidirectional\n", base);
      return 0;
    }

  deassert_dcr (DCR_DIRECTION);
  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_ECP);

  if ((read_ecr () & 0xe0) != 0x60)
    {
      fprintf (stderr, "ieee1284_probe: PS2 style port at 0x%04x\n", base);
      return 1;
    }

  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_EPP);

  if ((read_ecr () & 0xe0) != 0x20)
    {
      fprintf (stderr, "ieee1284_probe: PS2 style port at 0x%04x\n", base);
      return 1;
    }
  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_SPP);

  if ((read_ecr () & 0xe0) != 0x00)
    {
      fprintf (stderr, "ieee1284_probe: PS2 style port at 0x%04x\n", base);
      return 1;
    }


  fprintf (stderr, "ieee1284_probe: ECP port at 0x%04x\n", base);
  ecp++;
  write_ecr (ECR_NERRINTREN | ECR_SERVICEINTR | ECR_MODE_EPP);
  return 1;

}

int
ieee1284_init (int port)
{
  int ports[] = { 0x3bc, 0x378, 0x278, 0 };
  int *portp = ports;

  if (!iopl (3))
    {
      fprintf (stderr,
               "ieee1284: may have failed to get access to hardware\n");
      fprintf (stderr, "ieee1284: code must run as superuser\n");
    }

  if (port)
    {
      base = port;
      if (ieee1284_probe ())
        return 1;
    }

  while (*portp)
    {
      base = *portp;
      if (ieee1284_probe ())
        return 1;
      portp++;
    }

  return 0;

}
