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