/* dislc.c
   L. Stewart October 26, 1982  2:41 PM
   D. Swinehart July 12, 1982  8:45 AM
   L. Stewart November 13, 1982  4:30 PM, enqueue on clients q, if present
   L. Stewart November 23, 1982  9:39 AM, put back statics
   L. Stewart November 28, 1982  3:48 PM, flush Alloc
   L. Stewart November 28, 1982  5:17 PM, cleanup
   L. Stewart January 21, 1983  6:13 PM, new EtherHost
   L. Stewart January 22, 1983  3:33 PM, keep running
   L. Stewart February 23, 1983  3:01 PM, new EnableInt
   L. Stewart February 25, 1983  11:02 AM, rename SLCReset
   L. Stewart March 6, 1983  4:30 PM, remove Alloc.h
   LCS April 4, 1983, remove GetPBI from interrupt code
   LCS May 4, 1983, mods to transmit code, possible leak
   LCS May 31, 1983, Txpkt was not interrupt safe!
   LCS July 9, 1983, change tx timeout, add statics
			speed up receive interrupt handler
   LCS August 15, 1983  6:10 PM, SLCInit(CurrentHost)
   LCS August 15, 1983  6:49 PM, timestamps
   DCS February 19, 1985 1:41:24 pm PST, program around gateway transmission shortcomings
 */

#include <Lark.h>
#include <Ec.h>
#include <Env.h>
#include <Queue.h>
#include <Pup.h>

struct ccb {
  char cmda;
  char *addra;
  char counta;
  char stata;
  };

/* Runtime */
extern int DoubleEq();
extern int ReadTmr();

extern int currentHost;
extern int localHost;
extern int localNet;
extern int lenPup;
extern struct SocketEntry pupSockets[1 /*maxPupSockets*/];
static struct SocketEntry *lastSocket;

/* transmit and receive SLC control blocks */

#define numRBp1	4
#define numRB	3
static struct ccb rccb[numRBp1];
static struct PBI *rpbi[numRB];
static int nextrx;
static int lastrx;

static struct ccb txccb;
static struct PBI *txPBI;
/* if (txPBI != 0) a transmission is in progress */

static int rBufCount;  /* byte count of receive buffers */

/* transmit and receive timeout timers */
static int txtmr[1];
static int rxtmr[1];

static struct Queue outQ[1];  /* ether output queue */

static int etherStack[40];  /* stack space */

/* statistics */
static int slcri;     /* interrupt counters */
static int slcti;
static int slcrp;     /* packet counters */
static int slctp;
static int slrsmash;  /* SLC resets due to timeout */
static int sltsmash;
static int noBuffer;  /* lost packets due to buffer shortage */
static int nonPup;    /* non Pup packets received (and discarded) */
static int incomplete; /* input interrupts when operation not complete */
static int recvCollisionCode; /* See mySLRInt */

static EtherPr()
  {
  for (;;) {
    Block();
    if (TmrExp(rxtmr)) {
      slrsmash += 1;
      IWDC();
      CSLCReset();  /* violent ! */
      DWDC();
      };
    Block();
    if (TmrExp(txtmr)) {
      IWDC();
      if (txPBI && TmrExp(txtmr)) {
        sltsmash += 1;
        CSLCReset();  /* violent ! */
        };
      DWDC();
      };
    };
  };

static int *teei;

static mySLTInt()
  {
  OutByte(0x0024, 0x000e);
  if (txPBI) {
    if (txccb.stata & 0x80) {   /* why? */
      teei = ((int *) txPBI->pup) - 1;  /* encap->type */
      *teei = ReadTmr();  /* tx timestamp */
      Enqueue(txPBI->queue, txPBI);
      txPBI = 0;
      Txpkt();
      };
    };
  slcti += 1;
  return (true);
  };

static struct ccb *rxccb;
static struct PBI *ripbi;
static struct Pup *riPup;
static struct EtherEncapsulation *ee;
static int ckLoc;

static mySLRInt()
  {
  OutByte(0x0020, 0x0009);  /* clear MM interrupt */
  /* handle a burst of packets */
  for (;;) {
    rxccb = &rccb[nextrx];
    if ((rxccb->stata & 0x80) == 0) { incomplete += 1; break; };
    ripbi = rpbi[nextrx];
    riPup = ripbi->pup;
    if (rxccb->stata == recvCollisionCode)) {
    /* receiver collision detect -- transmitter probably clipped final CRC bit short.
       Checksum may also be spuriously wrong, so don't check it (indicate ignore checksum)
       This check is enabled by patching the value 0xCA into the initialization of
       recvCollisionCode, in InitEther, below. */
      rxccb->stata = 0x88;
      ckLoc = (riPup.length<<1) - pupOvWords;
      if ((ckLoc >= 0) && (ckLoc <=300)) riPup->data.words[ckLoc] = 0xFFFF;
      };
    if (rxccb->stata == 0x88) {
      ee = (struct EtherEncapsulation *) (((int) riPup) - 4);
      if (ee->type == typePup) {
        ee->type = ReadTmr(); /* arrival timestamp */
        IntLev1(); /* may change ripbi, invalidates riPup */
        rpbi[nextrx] = ripbi;
        };
      else nonPup += 1;
      };
    rxccb->cmda = 0;
    rxccb->stata = 0;
    rxccb->counta = rBufCount;

    /* next line can't use riPup because IntLev1 may have
       made it invalid */
    rxccb->addra = ((char *) ripbi->pup) - 4
    rccb[lastrx].cmda = 4;
    lastrx = nextrx;
    nextrx += 1;
    if (nextrx == numRB) nextrx = 0;
    };
  SetTmr(16000, rxtmr);
  /* if receiver stopped, restart it */
  if ((slrstat() & 0x000c) == 0) SLRReset();
  slcri += 1;
  return (true);
  };

static struct PBI *npbi;
static int il1temp, il1i;
static int dNet, dHost;
static struct PupSocket *dSocket;
static struct SocketEntry *socketI;

/* The socket dispatch runs at interrupt level,
   but the associcated procedure is called by
   pup level 1 process
 */

IntLev1()
  {
  slcrp += 1;
  if (riPup->sPort.host == 0) goto BcastSource;
  /* If the destination is not us, discard packet. */
  if ((dNet = riPup->dPort.net) == 0) goto ZeroDNet;
  if (dNet != localNet) {
    if (!localNet) SetLocalNet(dNet);
    else goto Misaddressed;
    };

  /* Checksum checking is done in PupImpl. */

  /* Pups not addressed to our multicast
     or specific address cannot be supported.
     Not sure they could be received, anyhow. */

  /* test currentHost first, because we're busiest then */

  if ((dHost = riPup->dPort.host) == currentHost) goto DHostOK;
  if (dHost == localHost) goto DHostOK;
  if (dHost == 0) goto DHostOK;
  goto BadHost;
  DHostOK:

  dSocket = &riPup->dPort.socket;
  for (socketI = pupSockets; socketI <= lastSocket; socketI += 1) {
    if (DoubleEq(&socketI->port.socket, dSocket)) {
      il1temp = (int) socketI->PortProc;
      ripbi->clientWord = il1temp;
      if ((npbi = MaybeGetPBI(0)) != 0) {
        Enqueue(socketI->q, ripbi)
        ripbi = npbi;
        };
      else noBuffer += 1;
      goto FoundIt;
      };
    };
  Misaddressed:
  BcastSource:
  ZeroDNet:
  BadHost:
  FoundIt:
  };

InitEther(ctxQ, x)
  struct Queue *ctxQ;
  int x;
  {
  int myc;
  recvCollisionCode = 0xFF; /* Disables feature.  Enable with proper patch in larkX.patches. */
  txPBI = 0;
  nonPup = noBuffer = slcri = slcti = slrsmash = sltsmash = 0;
  lastSocket = pupSockets + maxPupSockets;
  rBufCount = (lenPup + lenEtherEncapsulation) << 1;
  if (rBufCount > 256) CallSwat(ecPup1 + 19);
  rBufCount = (-rBufCount) & 0x00ff;
  myc = InitNContext("EtherPr", etherStack, 50, &EtherPr);
  Enqueue(ctxQ, myc);
  InitQueue(outQ);
  IWDC();
  CSLCReset();
  DWDC();
  EnableInt(&mySLTInt, SLTTyp);
  EnableInt(&mySLRInt, SLRTyp);
  };

/* after changing currentHost, call this */
NewHost()
  {
  IWDC();
  CSLCReset();
  DWDC();
  };

static CSLCReset()
  {
  SLCInit(currentHost);
  SLRPBISetup();
  SLRReset();
  OutByte(0x0024, 0x0088);	/* enable SLT IO interrupts */
  if (txPBI) {
    Enqueue(txPBI->queue, txPBI);
    txPBI = 0;
    IWDC();
    Txpkt();
    DWDC();
    };
  };

static SLRPBISetup()
  {
  int i;
  for (i=0; i<numRB; i+=1) {
    if (rpbi[i] == 0) rpbi[i] = GetPBI(0);
    };
  };

static SLRReset()
  {
  int i;
  struct ccb *mccb;
  OutByte(0x0020, 0x0048);	/* enable SLR MM interrupts */
  mccb = rccb;
  for (i=0; i<numRB; i+=1) {
    if (rpbi[i] == 0) return;    /* very bad event! */
    mccb->cmda = 0x04;   /* chain */
    mccb->addra = rpbi[i]->pup;
    mccb->addra = mccb->addra - 4;
    mccb->counta = rBufCount;
    mccb->stata = 0;
    mccb += 1;
    };
  mccb->cmda = 0x00f1;   /* nop plus indirect */
  mccb->addra = rccb;
  mccb->counta = rBufCount;
  mccb->stata = 0;
  rccb[numRB-1].cmda = 0;  /* not chained! */
  nextrx = 0;
  lastrx = numRB-1;
  SetTmr(20000, rxtmr);
  SLRStart(rccb);
  };

TransmitPacket(tpbi)
  struct PBI *tpbi;
  {
  Enqueue(outQ, tpbi);
  IWDC();
  Txpkt();
  DWDC();
  };

/* all callers of this procedure are at interrupt level! */
static Txpkt()
  {
  int len;
  if (txPBI) return;
  SetTmr(60, txtmr);
  txPBI = Dequeue(outQ);
  if (txPBI == 0) return;
  len = Swab(txPBI->pup->length);
  len = (len + 5) & 0x0fffe;
  if (len >= 256) CallSwat(ecPup1+20);
  /* enable IO interrupt */
  txccb.cmda = 0x52;
  txccb.addra = txPBI->pup;
  txccb.addra = txccb.addra - 4;
  txccb.counta = len & 0x00ff;
  txccb.stata = 0;
  SLTStart(&txccb);
  slctp += 1;
  };