// Pup1Init.bcpl - Level 1 initialization and destruction
// Copyright Xerox Corporation 1979, 1980, 1981

// Last modified November 21, 1981  11:40 AM by Taft

get "Pup0.decl"
get "Pup1.decl"
get "PupRoute.decl"

external
[
// outgoing procedures
InitPupLevel1; DestroyPupLevel1

// incoming procedures
Enqueue; Dequeue; Unqueue; PupLevel1; InitializeContext; Max; SetTimer
Allocate; Free; Noop; SysErr; CallSwat; Zero; DefaultArgs
OpenLevel1Socket; CloseLevel1Socket; CompletePup; GetPBI; ReleasePBI
GatewayListener; InitForwarder; LocateNet
HLookup; HInsert; HDelete; HEnumerate; Call0; Call1; Call2; Call3
RTLookup; RTEnumerate; RTInsert; RTDelete
InitAltoEther; InitAltoEIA; InitAltoComProc; InitAltoImp; InitAltoPRU
InitNovaEther; InitNovaMCA; InitNovaImp; InitNovaSLA

// incoming statics
ndbQ; pbiFreeQ; pbiIQ; pbiTQ; socketQ; gatewayIQ; pupRT; dPSIB
pupZone; pupCtxQ; lenPBI; lenPup; maxPupDataBytes; pupLevel1Ctx
numNets; gatewayListenerSoc; socketSequence
]

static [ listenerCtx ]

//----------------------------------------------------------------------------
let InitPupLevel1(zone, ctxQ, numPBI, pupDataBytes, numRTE; numargs na) be
//----------------------------------------------------------------------------
[
DefaultArgs(lv na, -3, defaultPupDataBytes, 9)
// define some defaults and other constants
pupZone = zone
pupCtxQ = ctxQ
maxPupDataBytes = pupDataBytes
lenPup = (pupOvBytes+pupDataBytes)/2
lenPBI = lenPBIOverhead+lenPup

// level one Queues -- DestroyPupLevel1 knows that pbiIQ is first
let level1Qs = Allocate(zone, 10); Zero(level1Qs, 10)
pbiIQ = level1Qs	// level 1 raw pup input queue
pbiTQ = level1Qs+2	// level 1 default transmitted queue
socketQ = level1Qs+4	// level 1 socket list
pbiFreeQ = level1Qs+6	// empty pbi queue
ndbQ = level1Qs+8	// net data block queue
gatewayIQ = pbiFreeQ	// changed by gateway init code

// free packet buffer queue
for i = 1 to numPBI do Enqueue(pbiFreeQ, Allocate(zone, lenPBI))

// Initialize all connected network interfaces
compileif numAltoEther ge 1 then [ InitAltoEther(zone, ctxQ, 0) ]
compileif numAltoEther ge 2 then [ InitAltoEther(zone, ctxQ, 1) ]
compileif numAltoEther ge 3 then [ InitAltoEther(zone, ctxQ, 2) ]
compileif numAltoComProc ge 1 then [ InitAltoComProc(zone, ctxQ) ]
compileif numAltoEIA ge 1 then [ InitAltoEIA(zone, ctxQ) ]
compileif numAltoImp ge 1 then [ InitAltoImp(zone, ctxQ) ]
compileif numAltoPRU ge 1 then [ InitAltoPRU(zone, ctxQ) ]

compileif numNovaEther ge 1 then [ InitNovaEther(zone, ctxQ, #73) ]
compileif numNovaEther ge 2 then [ InitNovaEther(zone, ctxQ, #63) ]
compileif numNovaEther ge 3 then [ InitNovaEther(zone, ctxQ, #53) ]
compileif numNovaMCA ge 1 then [ InitNovaMCA(zone, ctxQ, #6) ]
compileif numNovaMCA ge 2 then [ InitNovaMCA(zone, ctxQ, #46) ]
compileif numNovaImp ge 1 then [ InitNovaImp(zone, ctxQ) ]
compileif numNovaSLA ge 1 then [ InitNovaSLA(zone, ctxQ) ]

// InitPupLevel1 (cont'd)

// Find out how many networks there are
numNets = 0
let ndb = ndbQ!0
while ndb ne 0 do [ numNets = numNets+1; ndb = ndb!0 ]
if numNets eq 0 then SysErr(0, ecNoNetworkInterface)

// Set up default pup socket info block
dPSIB = Allocate(zone, lenPSIB)
let pbiAlloc = Max(numPBI-numNets, 2)
dPSIB>>PSIB.numTPBI = pbiAlloc  // Allow socket all PBIs
dPSIB>>PSIB.maxTPBI = pbiAlloc  //  except one per net
dPSIB>>PSIB.maxIPBI = pbiAlloc-1
dPSIB>>PSIB.numIPBI = pbiAlloc-1
dPSIB>>PSIB.maxOPBI = pbiAlloc-1
dPSIB>>PSIB.numOPBI = pbiAlloc-1
dPSIB>>PSIB.doChecksum = true
SetTimer(lv socketSequence, 0)  // randomize default socket numbers

// initialize "Hash table" access
HLookup, HInsert, HDelete, HEnumerate = Call0, Call1, Call2, Call3

// Set up routing table
numRTE = numRTE+numNets  // add one entry per directly-connected net
let n = lenRT + numRTE*lenRTQI
pupRT = Allocate(zone, n); Zero(pupRT, n)
pupRT>>HTP.lookup = RTLookup
pupRT>>HTP.enumerate = RTEnumerate
pupRT>>HTP.insert = RTInsert
pupRT>>HTP.delete = RTDelete
for i = 0 to numRTE-1 do
   [
   let rtqi = pupRT+lenRT+i*lenRTQI
   rtqi>>RTQI.rte.net = rtFreeEntry
   Enqueue(lv pupRT>>RT.head, rtqi)
   ]
LocateNet(0)  // Insert entry for net 0 and request routing probe
HInsert(pupRT, 0)>>RTE.ndb = ndbQ!0  // Default network is first on ndbQ

// Fire up background processes
pupLevel1Ctx = InitializeContext(Allocate(zone, 175), 175, PupLevel1)
Enqueue(ctxQ, pupLevel1Ctx)
listenerCtx = InitializeContext(Allocate(zone, 160), 160, GatewayListener)
Enqueue(ctxQ, listenerCtx)

// Open a socket for GatewayListener process
gatewayListenerSoc = Allocate(zone, lenPupSoc)
let portAdr = table [ 0; 0; psRouteInfo ]
OpenLevel1Socket(gatewayListenerSoc, portAdr, portAdr)
InitForwarder(numPBI)
]

//----------------------------------------------------------------------------
and DestroyPupLevel1() be
//----------------------------------------------------------------------------
// Destroys Pup package level 1 and level 0 and returns all allocated storage.
[
Unqueue(pupCtxQ, pupLevel1Ctx); Free(pupZone, pupLevel1Ctx)
Unqueue(pupCtxQ, listenerCtx); Free(pupZone, listenerCtx)

// Close all sockets to release any PBIs on their IQs -- but we know nothing
// about PBIs hung onto by higher-level protocols (e.g., RTP, BSP).
while socketQ!0 ne 0 do CloseLevel1Socket(socketQ!0)

// Destroy the level 0 drivers in reverse order of their creation
// so their userFinishProcs have a chance to unwind properly.
while ndbQ!0 ne 0 do (ndbQ!1)>>NDB.destroy(ndbQ!1)
ndbQ = 0

while pbiIQ!0 ne 0 do Free(pupZone, Dequeue(pbiIQ))  // all the PBIs
while pbiTQ!0 ne 0 do Free(pupZone, Dequeue(pbiTQ))
while pbiFreeQ!0 ne 0 do Free(pupZone, Dequeue(pbiFreeQ))

Free(pupZone, pbiIQ)  // all the level1Qs
Free(pupZone, dPSIB)
Free(pupZone, pupRT)
Free(pupZone, gatewayListenerSoc)
]