/* dslc.c
   L. Stewart January 21, 1983  5:10 PM
   D. Swinehart July 12, 1982  8:45 AM
   L. Stewart January 21, 1983  7:38 PM, new EtherHost
   L. Stewart January 22, 1983  3:35 PM, new statistics
   L. Stewart January 23, 1983  5:44 PM, Fix StartRx call
 */

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

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

/* Runtime */
extern InByte();
extern Swab();
extern CallSwat();

/* Context package */
extern InitNContext();
extern Block();

/* Queue package */
extern Enqueue();
extern Dequeue();
extern InitQueue();

/* Runtime */
extern int DoubleEq();

/* Timer package */
extern SetTmr();
extern TmrExp();

/* Ethernet machine language */
extern SLCInit();
extern SLTStart();
extern SLRStart();

/* Pup level 1 */
extern GetPBI();
extern MaybeGetPBI();
extern SetLocalNet();

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

/* transmit and receive SLC control blocks */
static struct ccb rxccb;
static struct PBI *rxPBI;

static struct ccb txccb;
static struct PBI *txPBI;
/* if txPBI is non-zero, then a transmission is in progress */
static int rBufCount;

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

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

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

/* statistics */
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 EtherPr()
  {
  struct EtherEncapsulation *ee;
  int temp;
  for (;;) {
    Block();
    /* test for reception or hung receiver */
    if (rxccb.stata & 0x80) {
      if (rxccb.stata == 0x88) {
        temp = (int) rxPBI->pup;
        temp = temp - 4;
        ee = (struct EtherEncapsulation *) temp;
        if (ee->type == 2) {  /* Swabbed typePup */
          rxPBI = IntLev1(rxPBI);
          };
        };
      StartRx();
      };
    if (TmrExp(rxtmr)) {
      SLCReset();  /* violent ! */
      slrsmash += 1;
      };
    if (txPBI && TmrExp(txtmr)) {
      SLCReset();  /* violent ! */
      sltsmash += 1;
      };
    /* test for completion of transmit or hung transmitter */
    if (txPBI) {
      if (txccb.stata & 0x80) {
        Enqueue(txPBI->queue, txPBI);
        txPBI = Dequeue(outQ);
        if (txPBI) Txpkt(txPBI);
        };
      };
    };
  };

/* The socket dispatch runs at interrupt level, but the associcated
   procedure is called by pup level 1 process */
struct PBI *IntLev1(pbi)
  struct PBI *pbi;
  {
  struct Pup *pup;
  struct PBI *npbi;
  int dNet, dHost, i, temp;
  slcrp += 1;
  pup = pbi->pup;
  if (pup->sPort.host == 0) goto BcastSource;
  /* If the destination is not us, discard packet. */
  if ((dNet = pup->dPort.net) == 0) goto ZeroDNet;
  if (dNet != localNet) {
    if (!localNet) SetLocalNet(dNet);
    else goto Misaddressed;
    };
	
  /* If checksum exists and is to be checked, here's the place to do it. */
  /* This line is a simulation of a successful checksum checking activity. */
	
  /* TEMP? Pups not addressed to our multicast or specific address cannot be
  	supported. Not sure they could be received, anyhow. */
  if (((dHost = pup->dPort.host) != 0) && (dHost != currentHost) &&
  	(dHost != localHost)) goto BadHost;
  /* Dispatch Other designated packets (e.g., voice data) */
  for (i = 1; i <= maxPupSockets; ++i) {
    if (DoubleEq(&pupSockets[i].port.socket, &pup->dPort.socket)) {
      temp = (int) pupSockets[i].PortProc;
      pbi->clientWord = temp;
      if ((npbi = MaybeGetPBI(0)) != 0) {
        Enqueue(pupSockets[i].q, pbi)
        pbi = npbi;
        };
      else noBuffer += 1;
      goto FoundIt;
      };
    };
  Misaddressed:
  BcastSource:
  ZeroDNet:
  BadHost:
  FoundIt:
    return(pbi);
  };

InitEther(zone, ctxQ, x)
  struct ZN *zone;
  struct Queue *ctxQ;
  int x;
  {
  int myc;
  txPBI = rxPBI = 0;
  noBuffer = slrsmash = sltsmash = 0;
  rBufCount = (lenPup + lenEtherEncapsulation) << 1;
  if (rBufCount > 256) CallSwat(ecPup1 + 19);
  rBufCount = (-rBufCount) & 0x00ff;
  myc = InitNContext("EtherPr", etherStack, 200, &EtherPr);
  Enqueue(ctxQ, myc);
  InitQueue(outQ);
  SLCReset();
  };

static SLCReset()
  {
  SLCInit(EtherHost());
  if (txPBI) {
    Enqueue(txPBI->queue, txPBI);
    if (txPBI = Dequeue(outQ)) Txpkt(txPBI);
    };
  StartRx();
  };

static StartRx()
  {
  if (rxPBI == 0) rxPBI = GetPBI(0);
  rxccb.cmda = 0x00;
  rxccb.addra = rxPBI->pup;
  rxccb.addra = rxccb.addra - 4;
  rxccb.counta = rBufCount;
  rxccb.stata = 0;
  SetTmr(10000, rxtmr);
  SLRStart(&rxccb);
  };

int EtherHost()
  {
  return ((InByte(piob) & 0x003f) + 0x0040);
  };

TransmitPacket(pbi)
  struct PBI *pbi;
  {
  if (txPBI) Enqueue(outQ, pbi);
  else Txpkt(pbi);
  };

static Txpkt(pbi)
  struct PBI *pbi;
  {
  int len;
  txPBI = pbi;
  len = Swab(txPBI->pup->length);
  len = (len + 5) & 0x0fffe;
  if (len > 256) CallSwat(ecPup1+20);
  txccb.cmda = 0x50;
  txccb.addra = txPBI->pup;
  txccb.addra = txccb.addra - 4;
  txccb.counta = len & 0x00ff;
  txccb.stata = 0;
  SLTStart(&txccb);
  SetTmr(2000, txtmr);
  slctp += 1;
  };