/* 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;
};
};