/* LarkNet.c
   LCS November 1, 1982  9:03 AM
   LCS November 11, 1982  11:45 AM, new From Net design
   LCS November 13, 1982  4:37 PM, new PupLevel 1
   LCS November 30, 1982  12:41 PM, protocol bugs
   LCS December 8, 1982  4:37 PM, LarkSlave addrs
   LCS December 27, 1982  11:04 AM, new SSilThresh
   LCS January 14, 1983  4:06 PM, Lark.mesa revisions
   LCS March 15, 1983  1:27 PM, new voice protocol
   LCS March 21, 1983  9:41 PM, bugs in new voice protocol
   LCS March 22, 1983  9:00 AM, add, WDT
   LCS March 29, 1983  4:36 PM, bugs in protocol
   LCS April 18, 1983  10:47 AM, new Lark interface
   LCS April 21, 1983  11:54 AM, safety in PlayPacket
   LCS May 4, 1983  10:46 AM, lost pbi on transmit
   LCS May 20, 1983  11:49 AM, new SSilThresh
   LCS May 27, 1983  4:08 PM, reorganized, echo suppression
   LCS June 2, 1983  11:53 AM, statics and cleanup
   LCS June 6, 1983  11:50 AM, squelchTail
   LCS June 14, 1983  11:01 AM, EchoSuppression
   LCS August 12, 1983  12:50 PM, fixes for interactive calls
   LCS August 16, 1983  12:54 PM, histograms, etc.
   LCS August 17, 1983  12:39 PM, variable aj buffering
 */

#include	"Ec.h"
#include	"Env.h"
#include	"RPC.h"
#include	"Queue.h"
#include	"Pup.h"
#include	"Encrypt.h"
#include	"LarkNet.h"
#include	"LarkSlave.h"
#include	"LarkVCB.h"

extern Encrypt();
extern ReleasePBI();
extern SendPup();

struct EncryptionKey keyTable[16];

struct VCB vcb[5];
extern int wToneCB;
extern int lastHalf;

/* stats */
int lostTx;
int lostRx;
int ppDiscard;
int txHisto[84];
int rxHisto[84];

extern int zeroBufferTime;
extern int bufferClock;

/* parameters */

int SSilThresh;	/* standard silence threshold */
int MaxSquelchTail;
int ajdelay;  /* e.g. 10 ms */
int tooLate;  /* e.g. 3 ms */
int tooEarly;  /* e.g. 40 ms */
int adjustTooEarly;  /* e.g. 10 ms */
int firstajdelay;  /* e.g. 3 ms */

StartNet(specs)
  struct CSpecs *specs;
  {
  struct PBI *pbi;
  struct VCB *p;
  int *kp;
  /* it needs a legit. buffer field */
  if ((specs->buffer < 0) || (specs->buffer > 4)) return(false);
  /* p is a pointer to the VCB for this connection */
  p = &vcb[specs->buffer];
  if (p->active) {
    CallSwat(ecLarkImpl+2);
    StopNet(specs->buffer);
    };
  InitVCB(specs->buffer);
  MoveBlock(&p->lport, &specs->lsoc[0], lenPort);
  MoveBlock(&p->rport, &specs->rsoc[0], lenPort);
  /* until set up, force key table pointer to 0 */
  p->encIndex = specs->keyIndex & 017;
  /* then handle in the usual way */
  if (p->encIndex == 0) kp = 0;
  else kp = (int *) &keyTable[p->encIndex];
  p->loECB.kp = kp;
  p->hiECB.kp = kp;
  p->floECB.kp = kp;
  p->fhiECB.kp = kp;
  p->daECB.kp = kp;
  p->dbECB.kp = kp;
  switch (specs->buffer) {

    /* for the input buffers set active to fire up the process */
    case IIn1:
    case IIn2: {
      AquireTxPBIs(p, 1);
      break;
      };

    /* for the output buffers set up a socket */
    case IOut1:
    case IOut2:
    case IOut3: {
      if (!(p->lsoc = OpenLevel1Socket(&p->lport, 0, &p->vpq))) CallSwat(ecPup1+10);
      break;
      };
    default: return;
    };
  p->lastEndTime = p->now = bufferClock;
  p->active = true;
  };

AquireTxPBIs(p, n)
  struct VCB *p;
  int n;
  {
  struct PBI *pbi;
  struct Pup *pup;
  struct LLPkt *llp;

  pbi = GetPBI(0);
  pup = pbi->pup;
  llp = (struct LLPkt *) &pup->data;
  pbi->queue = &p->vpq;
  pbi->clientWord = zeroBufferTime;
  pup->transport = 0;
  MoveBlock(&pup->dPort, &p->rport, lenPort);
  MoveBlock(&pup->sPort, &p->lport, lenPort);
  llp->mode = 0;
  llp->encIndex = p->encIndex;
  llp->blank2 = 0;
  pup->type = LarkVoiceType;
  Enqueue(&p->vpq, pbi);
  };

StopNet(id)
  int id;
  {
  int tmr;
  struct VCB *p;
  struct PBI *pbi;
  if ((id < 0) || (id > 4)) CallSwat(ecLarkImpl+2);
  p = &vcb[id];
  if (!p->active) return;
  p->active = false;
  switch (id) {

    /* for the inputs, free the buffer */
    case IIn1:
    case IIn2: {
      SetTmr(1000, &tmr);
      while ((pbi = Dequeue(&p->vpq)) == 0) {
        Block();
        if (TmrExp(&tmr)) CallSwat(ecLarkImpl+3);
        };
      ReleasePBI(pbi);
      break;
      };

    /* for the output buffers, destroy the socket */
    case IOut1:
    case IOut2:
    case IOut3: {
      CloseLevel1Socket(p->lsoc);
      Zero(&p->lport, lenPort);
      while ((pbi = Dequeue(&p->vpq)) != 0) ReleasePBI(pbi);
      while ((pbi = Dequeue(&p->pq)) != 0) ReleasePBI(pbi);
      p->pqlength = 0;
      break;
      };
    default: CallSwat(ecNotImp+1);
    };
  };

static struct VCB *fnvcb;
/* use static variables for net transmission */
static struct PBI *tnpbi;
static struct Pup *tnpup;
static struct LLPkt *tnllp;
static struct VCB *tnvcb;

ToNet()
  {
  fnvcb = tnvcb = &vcb[IIn1];
  GainLoop(*tnvcb->silVal);
  if (tnvcb->active) LocToNet();
  fnvcb = tnvcb = &vcb[IIn2];
  GainLoop(*tnvcb->silVal);
  if (tnvcb->active) LocToNet();
  };

/* this procedure is called once per voice packet */

static LocToNet()
  {

  /* time for this packet */
  if (lastHalf) tnvcb->now += 20;
  else tnvcb->now = zeroBufferTime;

  /* if silence threshold exceeded, send voice packet */
  if (*tnvcb->silVal > tnvcb->silThresh) {
    if (tnvcb->squelchTail != MaxSquelchTail) SendVoice();
    else SendFirstVoice();
    tnvcb->squelchTail = 0;
    return;
    };
  if (tnvcb->squelchTail < MaxSquelchTail) {
    SendVoice();
    tnvcb->squelchTail += 1;
    return;
    };
  if((tnvcb->now - tnvcb->lastEndTime) >= MaxSilenceMS) {
    SetupPup();
    if (tnpbi == 0) return;
    /* 8 is for sizeof(LLPkt) */
    tnpup->length = Swab(8 + pupOvBytes);
    tnpup->type = LarkVoiceType;
    SendPup(tnpbi);
    };
  };

static SendVoice()
  {
  SetupPup();
  if (tnpbi == 0) return;
  /* 8 is for sizeof(LLPkt) */
  tnpup->length = Swab(168 + pupOvBytes);
  tnpup->type = LarkVoiceType;
  if (lastHalf) {
    tnvcb->hiECB.dstp = &tnllp->data[0];
    tnvcb->hiECB.item = (int) tnpbi;
    Encrypt(&tnvcb->hiECB);
    };
  else {
    tnvcb->loECB.dstp = &tnllp->data[0];
    tnvcb->loECB.item = (int) tnpbi;
    Encrypt(&tnvcb->loECB);
    };
  };

static SendFirstVoice()
  {
  SetupPup();
  if (tnpbi == 0) return;
  /* 8 is for sizeof(LLPkt) */
  tnpup->length = Swab(224 + pupOvBytes);
  tnpup->id[0] = Swab(Swab(tnpup->id[0]) - 7);
  tnpup->type = LarkFirstVoiceType;
  if (lastHalf) {
    tnvcb->fhiECB.dstp = &tnllp->data[0];
    tnvcb->fhiECB.item = (int) tnpbi;
    Encrypt(&tnvcb->fhiECB);
    };
  else {
    tnvcb->floECB.dstp = &tnllp->data[0];
    tnvcb->loECB.dstp = &tnllp->data[56];
    tnvcb->loECB.item = (int) tnpbi;
    Encrypt(&tnvcb->floECB);
    };
  };

static int *tnhp;

static SetupPup()
  {
  tnpbi = Dequeue(&tnvcb->vpq);
  if (tnpbi == 0) {
    lostTx += 1;
    if (TmrExp(&tnvcb->dtimer)) CallSwat(ecLarkImpl+4);
    return;
    };
  SetTmr(1000, &tnvcb->dtimer);
  tnpup = tnpbi->pup;
  tnhp = ((int *) tnpup) - 1;
  tnllp = (struct LLPkt *) &tnpup->data;
  Histogram(*tnhp - tnpbi->clientWord, txHisto);
  tnpup->id[0] = Swab(tnvcb->lastEndTime);
  tnpup->id[1] = Swab(tnvcb->numPackets);
  tnvcb->numPackets += 1;
  tnllp->energy = Swab(*tnvcb->silVal);
  tnllp->silenceMS = Swab(tnvcb->now - tnvcb->lastEndTime);
  tnpbi->clientWord = tnvcb->lastEndTime = tnvcb->now + 20;

/* the stuff below this line isn't needed because we
   recycle our own pbis and this stuff doesn't
   change during a connection
 */
  return;
  tnpbi->queue = &tnvcb->vpq;
  tnpup->transport = 0;
  MoveBlock(&tnpup->dPort, &tnvcb->rport, lenPort);
  MoveBlock(&tnpup->sPort, &tnvcb->lport, lenPort);
  tnllp->mode = 0;
  tnllp->encIndex = tnvcb->encIndex;
  tnllp->blank2 = 0;
  };

/* Reception of packets from the Ethernet proceeds
    in several stages

  1.  The Ethernet receive interrupt procedure enqueues
      packets on p->vpq for a VCB.
  2.  CheckFromNet is called in a context to
      process arriving packets.  Check discards
      uninteresting packets, responds to probes, and
      schedules packets to be played at a given real time in
      the future. Scheduled packets are hung on p->pq
      (processed queue).

   3. PlayPackets is called in a process to decrypt
      packets into the ring buffer when there is space.
  */


FromNet()
  {
  GetBufferTime();
  fnvcb = &vcb[IOut1];
  CheckFromNet();
  PlayPackets();
  fnvcb = &vcb[IOut2];
  CheckFromNet();
  PlayPackets();
  fnvcb = &vcb[IOut3];
  CheckFromNet();
  PlayPackets();
  };

static struct PBI *fnpbi;
static struct PBI *fnpbib;
static struct Pup *fnpup;
static struct LLPkt *fnllp;
static int fnTimeToPlay;
static int fnTimeToWait;
static int fnTimeToPlayVoice;
static int fnPktID;
static int fnSilenceMS;
static int fnVoiceMS;
static int fnDiff;

CheckFromNet()
  {
  fnpbi = Dequeue(&fnvcb->vpq);

  if (fnpbi == 0) return;  /* queue empty */
/* perhaps I should now run down the queue, if there is
   anything else on it, clearing any proberequest bits,
   because the request will not have been acted on in
   a timely fashion by the time I get around to handling it.
 */
  fnpup = fnpbi->pup;
  fnllp = (struct LLPkt *) &fnpup->data;
  fnTimeToPlay = Swab(fnpup->id[0]);
  fnPktID = Swab(fnpup->id[1]);
  if (fnPktID != fnvcb->expPktID) lostRx += 1;
  fnvcb->expPktID = fnPktID + 1;
  fnllp->silenceMS &= 0xff07;  /* arbitrary mask */
  fnSilenceMS = Swab(fnllp->silenceMS);
  fnVoiceMS = ((Swab(fnpup->length) - 8) - pupOvBytes) >> 3;
  fnTimeToPlayVoice = fnTimeToPlay + fnSilenceMS;

  if (fnpup->type == LarkVoiceType) {
    /* compute a timeToPlay and stick the packet into pq */
    /* lastEndTime is according to our clock
       expPlayTime is according to sending Lark's clock
     */
    /* if this is the first packet on this connection,
       or if there is a gap (silence or lost packet),
       then resynch
      */
    if (fnvcb->firstPacket || (fnTimeToPlayVoice != fnvcb->expPlayTime)) {
      /* set expPlayTime to end of the voice in this pkt */
      fnvcb->expPlayTime = fnTimeToPlayVoice + fnVoiceMS;
      fnvcb->firstPacket = false;
      /* set end of scheduled voice to now plus anti-jitter
         plus voice in this packet */
      fnvcb->lastEndTime = ReadTmr() + ajdelay;
      };
    else {
      /* (fnTimeToPlayVoice == fnvcb->expPlayTime) */
      /* This is the packet we expect. Schedule it
         for the tail of the previous packet and update
         the various expected numbers.
       */
      /* set expPlayTime to end of the voice in this pkt */
      fnvcb->expPlayTime = fnTimeToPlayVoice + fnVoiceMS;
      /* The next if statements handle clock drift in the
         steady state.
         If our clock is fast, then packets arrive
         too slowly.  Eventually, one will arrive too late.
         In this case, leave a larger gap now, rather than
         continue late each time.  (Reschedule)
         If our clock is slow, then packets pile up.
         When the buffering gets too high, overlap
         two packets by a little.
       */
      fnDiff = fnvcb->lastEndTime - bufferClock;
      if (fnDiff < tooLate) fnvcb->lastEndTime = ReadTmr() + ajdelay;
      if (fnDiff > tooEarly) fnvcb->lastEndTime -= adjustTooEarly;

      /* otherwise schedule packet on heels of
         last packet
       */
      };
    fnpup->id[0] = Swab(fnvcb->lastEndTime);
    fnvcb->lastEndTime += fnVoiceMS;
    if (fnvcb->pqlength <= MaxPQL) {
      Enqueue(&fnvcb->pq, fnpbi);
      fnvcb->pqlength += 1;
      };
    else {
      ReleasePBI(fnpbi);
      };
    return;
    };

  if (fnpup->type == LarkFirstVoiceType) {
    fnvcb->firstPacket = false;
    /* set expPlayTime to end of the voice in this pkt */
    fnvcb->expPlayTime = fnTimeToPlayVoice + fnVoiceMS;
    /* set end of scheduled voice to now plus anti-jitter
         plus voice in this packet */
    fnvcb->lastEndTime = ReadTmr() + firstajdelay;
    fnpup->id[0] = Swab(fnvcb->lastEndTime);
    fnvcb->lastEndTime += fnVoiceMS;
    if (fnvcb->pqlength <= MaxPQL) {
      Enqueue(&fnvcb->pq, fnpbi);
      fnvcb->pqlength += 1;
      };
    else {
      ReleasePBI(fnpbi);
      };
    return;
    };

  if (fnpup->type == BluejayVoiceType) {
    /* special test for empty probe */
    if ((fnSilenceMS + fnVoiceMS) == 0) {
      fnpbib = fnpbi;
      if (fnllp->mode & 1) SendProbeReply();
      else ReleasePBI(fnpbi);
      return;
      };
    if (fnllp->mode & 1) {
      fnpbib = MaybeGetPBI();
      if (fnpbib) {
        /* copy the pup header */
        MoveBlock(fnpbib->pup, fnpbi->pup, pupOvWords);
        SendProbeReply();
        };
      };
    fnTimeToWait = fnTimeToPlayVoice - ReadTmr();
    if ((fnTimeToWait < 0) || (fnTimeToWait > 500)) {
      ReleasePBI(fnpbi);
      return;
      };
    fnpup->id[0] = Swab(fnTimeToPlayVoice);
    if (fnvcb->pqlength <= MaxPQL) {
      Enqueue(&fnvcb->pq, fnpbi);
      fnvcb->pqlength += 1;
      };
    else ReleasePBI(fnpbi);
    return;
    };
  /* unknown packet types */
  ReleasePBI(fnpbi);
  };

struct PRPkt {
  int bluejayID;
  int maxPktSize;
  int maxPkts;
  int blank;
  };

static struct Pup *sprPup;
static struct PRPkt *sprPrp;

static SendProbeReply()
  {
  /* send probe response */
  sprPup = fnpbib->pup;
  sprPup->length = Swab(pupOvBytes + 8);
  sprPrp = (struct PRPkt *) &sprPup->data;
  sprPrp->bluejayID = sprPup->id[1];
  sprPrp->maxPktSize = Swab(160);
  sprPrp->maxPkts = Swab(MaxPlaybackPkts);
  sprPrp->blank = 0;
  SwapSourceAndDest(sprPup);
  sprPup->type = ProbeReplyType;
  sprPup->id[0] = Swab(ReadTmr());
  sprPup->id[1] = fnvcb->numPackets;
  fnvcb->numPackets += 1;
  SendPup(fnpbib);
  };

  /*  packets in the pq are scheduled for certain
       times, and are in order.
   */

static struct PBI *pppbi;
static struct Pup *pppup;
static struct LLPkt *ppllp;
static int ppTimeToPlay;
static int ppTimeToWait;
static int ppVoiceSamples;
static int ppVoiceMS;
static int ppBufferIndex;
static int ppBytesDecrypted;
static int ppKeyIndex;
static int *pphp;

PlayPackets()
  {

  /* update bufferClock and zeroBufferTime */
  GetBufferTime();
  /* peek at queue without dequeue */
  pppbi = (struct PBI *) fnvcb->pq.head;
  if (pppbi == 0) {  /* queue empty */
    if ((bufferClock - fnvcb->lastEndTime) > 20) {
      fnvcb->lastEndTime += 20;
      GainLoop(0);
      };
    return;
    };

  pppup = pppbi->pup;
  ppllp = (struct LLPkt *) &pppup->data;
  ppTimeToPlay = Swab(pppup->id[0]);
  ppTimeToWait = ppTimeToPlay - bufferClock;

  /* discard unreasonable packets */
  if ((ppTimeToWait < -10) || (ppTimeToWait > 500)) {
    ppDiscard += 1;
    fnvcb->firstPacket = true;
    ReleasePBI(Dequeue(&fnvcb->pq));
    fnvcb->pqlength -= 1;
    return;
    };

  /* voiceSamples only must be positive */
  ppVoiceSamples = ((Swab(pppup->length) - pupOvBytes) - 8) & 0x0ff8;
  ppVoiceMS = ppVoiceSamples >> 3;
  /* there is room in the buffer if both the head
     (timeToPlay) and tail (timeToPlay + voiceMS) are
     in the next 40 ms */
  if ((ppTimeToWait >= 40) || ((ppTimeToWait + ppVoiceMS) >= 40)) return;

  /* now we know the packet will fit in the buffer

      assert: (bufferClock - zeroBufferTime) < 40
        timeToPlay == timeToWait + bufferClock
      and
        timeToWait < 40
      so
        (timeToPlay - bufferClock) < 40
      so
        (timeToPlay - zeroBufferTime) < 80
      The packet goes in the buffer at index
      ((timeToPlay - zeroBufferTime) mod 40 ) << 3
    */
/* temporary tone override */
  if (wToneCB) {
    ReleasePBI(Dequeue(&fnvcb->pq));
    fnvcb->pqlength -= 1;
    return;
    };
  Dequeue(&fnvcb->pq);
  fnvcb->pqlength -= 1;
  if ((ppVoiceMS <= 0) || (ppVoiceMS > 40)) {
    ReleasePBI(pppbi);
    return;
    };
  ppBufferIndex = ppTimeToPlay - zeroBufferTime;
  /* ppTimeToPlay - zeroBufferTime can be up to 80 */
  if (ppBufferIndex < 0 ) ppBufferIndex += 40;
  if (ppBufferIndex >= 40 ) ppBufferIndex -= 40;
  if ((ppBufferIndex < 0) || (ppBufferIndex >= 40)) CallSwat(ecLarkImpl + 25);
  /* now multiply by 8 to get real index */
  ppBufferIndex = ppBufferIndex << 3;
  fnvcb->daECB.srcp = &ppllp->data[0];
  fnvcb->daECB.dstp = &fnvcb->bufbase[ppBufferIndex];
  ppKeyIndex = ppllp->encIndex; /* & 017, but why bother? */
  if (ppKeyIndex == 0) {
    fnvcb->daECB.kp = fnvcb->dbECB.kp = 0;
    };
  else {
    fnvcb->daECB.kp = fnvcb->dbECB.kp = (char *) &keyTable[ppKeyIndex];
    };
  if ((ppBufferIndex + ppVoiceSamples) > 320) {
    /* can't do it in one BLT */
    fnvcb->daECB.proc = (int) &Encrypt;
    fnvcb->daECB.item = (int) &fnvcb->dbECB;
    fnvcb->daECB.count = ppBytesDecrypted = 320 - ppBufferIndex;
    fnvcb->dbECB.srcp = &ppllp->data[ppBytesDecrypted];
    fnvcb->dbECB.count = ppVoiceSamples - ppBytesDecrypted;
    fnvcb->dbECB.proc = (int) &ReleasePBI;  /* not needed */
    fnvcb->dbECB.item = (int) pppbi;
    };
  else {
    fnvcb->daECB.count = ppVoiceSamples;
    fnvcb->daECB.proc = (int) &ReleasePBI;
    fnvcb->daECB.item = (int) pppbi;
    };
  /* force a reschedule if the packet is late by
     the time we get here
   */
  if ((ppTimeToWait < 0) && (pppup->type == LarkVoiceType)) fnvcb->firstPacket = true;
  pphp = ((int *) pppup) - 1;
  Histogram(ReadTmr() - *pphp, rxHisto);
  Encrypt(&fnvcb->daECB);
/* should only gain control the appropriate channel,
   but which ? */
  GainLoop(Swab(ppllp->energy));
  };

static GainLoop(energy)
  int energy;
  {
  int i;
  int gl[];
  gl = &fnvcb->gainTable[0];
  if (energy > fnvcb->lastEnergy) {
    fnvcb->lastEnergy = energy;
    i = 0;
    if (energy < gl[0]) goto setGain;
    i = 1;
    if (energy < gl[1]) goto setGain;
    i = 2;
    if (energy < gl[2]) goto setGain;
    i = 3;
    if (energy < gl[3]) goto setGain;
    i = 4;
setGain:
    fnvcb->gainLevel = i;
    fnvcb->gainCount = fnvcb->decayTime;
    SetIngain(fnvcb->gainChannel, i);
    return;
    };
  if (fnvcb->gainLevel > 0) {
    fnvcb->gainCount -= 1;
    if (fnvcb->gainCount <= 0) {
      fnvcb->gainLevel -= 1;
      fnvcb->lastEnergy = gl[fnvcb->gainLevel];
      fnvcb->gainCount = fnvcb->decayTime;
      SetIngain(fnvcb->gainChannel, fnvcb->gainLevel);
      return;
      };
    };
    else {
      fnvcb->lastEnergy = 0;
      };
  };