Trailing-Edge
-
PDP-10 Archives
-
BB-P363B-SM_1985
-
t20/nmlt20/nmldlw.b36
There is 1 other file named nmldlw.b36 in the archive. Click here to see a list.
! UPD ID= 334, SNARK:<6.1.NML>NMLDLW.B36.32, 10-Jul-85 13:57:34 by MCCOLLUM
! Make DLW_SERVICE_EVENT a global routine and rename it to
! NML$DLW_SERVICE_EVENT
!
! UPD ID= 324, SNARK:<6.1.NML>NMLDLW.B36.23, 10-May-85 15:35:11 by GLINDELL
! 1. When waiting for NI activity, don't set up a timeout value unless
! the circuit substate is one of the ones indicating a load or dump.
! If no load or dump is in progress, then there is no need to wake
! up every MOP_TIME_OUT seconds to post a 'timeout' entry in the
! LDAQ queue.
!
! 2. To once and for all address the 'On-starting' problem (?), add a
! new task (DLW_CHECK_DTE_TASK) that runs 30 seconds after each
! "successful" initialization of protocol with the MCB. It turns out
! that we on the 20 run into some race that we do not understand. The
! MCB will seem to initialize successfully but will then commit suicide
! immediately. ROUTER will then turn off the circuit for a second and
! we probably fall into that window. So, add a task that will be run
! after each successful initialization. The task will sleep for 30
! seconds and then post another 'check circuit'.
!
! UPD ID= 290, SNARK:<6.1.NML>NMLDLW.B36.22, 15-Mar-85 13:13:47 by GLINDELL
! Change DLW_NI_TASK so that we accept 'request program' and 'request dump
! service' messages that are addressed to us specifically, and is not on the
! multicast.
!
! UPD ID= 284, SNARK:<6.1.NML>NMLDLW.B36.21, 13-Mar-85 14:19:40 by GLINDELL
! Don't care if GET_OTHER_INFO runs into any problems in DUMP code - this
! circumvents PLUTO bug in 'REQUEST DUMP SERVICE' message. Put back check
! on GET_OTHER_INFO when PLUTO does the right thing.
!
! UPD ID= 282, SNARK:<6.1.NML>NMLDLW.B36.20, 11-Mar-85 16:02:36 by GLINDELL
! More problems with NML running out of memory: move setting of substate
! to AUTOSERVICE from callers (DLW_WORK and DLW_NI_TASK) into DLW_QUEUE.
!
! UPD ID= 280, SNARK:<6.1.NML>NMLDLW.B36.19, 10-Mar-85 14:26:24 by GLINDELL
! 1. Memory wasn't deallocated in a lot of places. NMLT20 sooner or later
! would run out of memory.
! 2. Remove edit history prior to phase IV.
!
! UPD ID= 277, SNARK:<6.1.NML>NMLDLW.B36.18, 6-Mar-85 23:10:43 by GLINDELL
! 1. If we receive anything on the multicast and we are already loading
! or dumping across the NI, then just throw away the multicast message.
! Anything on the NI that we really should load can wait since the
! requestor will retry.
!
! 2. Add hardware address as argument to NML$DLW_READ_LDAQ
!
! 3. Expand on DLW_SERVICE_EVENT
!
! UPD ID= 274, SNARK:<6.1.NML>NMLDLW.B36.17, 6-Mar-85 11:35:46 by GLINDELL
! Work to support loading and dumping when STATE is SERVICE
!
! UPD ID= 266, SNARK:<6.1.NML>NMLDLW.B36.16, 1-Mar-85 10:02:06 by GLINDELL
! Lots of work to support POSEIDON, fix ETHERNET dump, clean up DTE reload.
!
! UPD ID= 260, SNARK:<6.1.NML>NMLDLW.B36.15, 10-Feb-85 14:49:47 by GLINDELL
! 1. DTE reload does not work properly any more - reinstate call to
! NML$CALCULATE_CIRCUIT_STATE
! 2. Remove definitions of STATE/SUBSTATE/SERVICE values
!
! UPD ID= 247, SNARK:<6.1.NML>NMLDLW.B36.14, 25-Jan-85 13:58:43 by HALPIN
! New routines READ_REQUEST_PROGRAM and GET_OTHER_INFO, to extract
! needed info from the MOP Request Program messages.
! Moved the DLWQ filed definitions to NMLCOM.REQ
!
! Remove call to NML$CALCULATE_CIRCUIT_STATE because it overwrites
! the NML -LOADING substate, during a LOAD NODE REQUEST, causing reloads
! to fail.
!
! UPD ID= 226, SNARK:<6.1.NML>NMLDLW.B36.13, 11-Jan-85 15:58:47 by HALPIN
! DTE Data Link watcher calls NML$CALCULATE_CIRCUIT_STATE to insure that
! NML's circuit info is up to date. Don't try any service functions
! if DTE's STATE is ON with no substate value, i.e. ON-RUNNING, so it
! doesn't reload a running MCB.
!
! UPD ID= 219, SNARK:<6.1.NML>NMLDLW.B36.12, 17-Dec-84 10:25:27 by HALPIN
! Reply to a REQUEST_DUMP_SERVICE message on the NI with ASSISTANCE_
! VOLUNTEER before queueing up the DUMP Request to NML.
!
! UPD ID= 216, SNARK:<6.1.NML>NMLDLW.B36.11, 12-Dec-84 18:46:54 by HALPIN
! Fix somemore (and I hope all now!) MOP Buffer Deallocation bugs in
! DLW_NI_TASK.
!
! UPD ID= 212, SNARK:<6.1.NML>NMLDLW.B36.10, 12-Dec-84 10:19:32 by HALPIN
! Change the formating of the 'LOAD NODE' NICE messgae in DLW_QUEUE. Only
! put the Node name into the Entity Id field, not both name and number.
! In DLW_NI_TASK deallocate the MOP Buffer if there is no active Load or
! Dump requests to send it to.
!
! UPD ID= 203, SNARK:<6.1.NML>NMLDLW.B36.8, 10-Dec-84 15:22:09 by HALPIN
! Get MONSYM Library file out of default directory, not BLI:
!
! UPD ID= 184, SNARK:<6.1.NML>NMLDLW.B36.7, 7-Dec-84 09:28:21 by HALPIN
! Assign an interrupt channel for NI Receives. Pass PSI channel numbers
! in calls to $NMU_DLX_OPEN.
! New routine DLW_NI_RECEIVER, called when a Receive Interrupt occurs
! from the NI% JSYS.
! DLW_NI_TASK receives all messages on the NI's LOAD_DUMP Portal. Sends
! any messages for an active LOAD/DUMP request to NML via the Load/Dump
! Assistance Queue (LDAQ). Any received messages on the Load/Dump Assistance
! Multicast Address generates a new Load/Dump Service request.
! New Global routine, NML$DLW_READ_LDAQ, provided for NMLDTL to read
! MOP messages off the LDAQueue.
!
! UPD ID= 176, SNARK:<6.1.NML>NMLDLW.B36.6, 19-Nov-84 15:50:59 by HALPIN
! Fix Byte pointer to NI circuit id in DLW_NI_TASK.
!
! UPD ID= 174, SNARK:<6.1.NML>NMLDLW.B36.5, 19-Nov-84 10:27:06 by HALPIN
! Create new Data Link Watcher Task for the NI. It will Enable the
! LOAD/DUMP ASSISTANCE Multicast address on NI-0-0 and watch for Request
! Program (LOAD) and Request Dump Service message.
!
! UPD ID= 121, SNARK:<6.1.NML>NMLDLW.B36.4, 5-Oct-84 14:29:36 by HALPIN
! Allow DLW to service a circuit if the substate is STARTING.
!
! UPD ID= 107, SLICE:<6.1.NML>NMLDLW.B36.3, 18-Sep-84 16:23:25 by GUNN
! WORK:<GUNN.NML>NMLDLW.B36.2 24-Aug-84 17:04:33, Edit by GUNN
!
! Add dummy argument in call to $NMU_DLX_OPEN to account for
! PHYSICAL ADDRESS parameter required for Ethernet.
!
! UPD ID= 34, SNARK:<6.1.NML>NMLDLW.B36.2, 24-May-84 16:20:00 by GLINDELL
! DSKC:NMLDLW.B36[10,5665,SOURCE,TOPS10] 13-Dec-83 12:52:08, Edit by GROSSMAN
!
! Fix simultaneous loading of multiple MCB's. DLW_QUEUE was using a pointer to
! the circuit id (the id was being kept in a local variable). Unfortunately,
! when DLW_DEQUEUE was called this would point to the last circuit that
! DLW_QUEUE played with. Fix it by pointing to a service request (DLWQ) block,
! which is a somewhat more permanent structure.
! Edit=54
!
! DSKC:NMLDLW.B36[10,5665,SOURCE,TOPS10] 1-Dec-83 17:08:44, Edit by GROSSMAN
!
! Add many messages to DLW_WORK so that people can see what is going on.
! Edit=53
!
! DSKC:NMLDLW.B36[10,5665,SOURCE,TOPS10] 1-Dec-83 01:41:25, Edit by GROSSMAN
!
! Clean up the routine DLW_TASK by splitting it into two routines. The new
! routine is called DLW_WORK.
! Edit=52
!
! DSKC:NMLDLW.B36[10,5665,SOURCE,TOPS10] 13-Oct-83 00:17:16, Edit by GROSSMAN
!
! Implement PPN and password verification.
! Always set RB_PRIV to $TRUE in this module.
! Edit=37
!
! PH4:<BUILD>NMLDLW.B36.5 4-Mar-83 16:46:00, Edit by GUNN
!
! Ident 11.
! Merge DECnet-10 changes as of approximately 7-OCT-82.
! Uncomment the 'DECLARE_JSYS (BOOT)'.
!
module NMLDLW ( ! Data link watcher task
ident = 'X03.06'
) =
begin
!
! COPYRIGHT (C) 1981,1985 BY
! DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS 01754
!
! THIS SOFTWARE IS FURNISHED UNDER A LICENSE FOR USE ONLY ON A SINGLE
! COMPUTER SYSTEM AND MAY BE COPIED ONLY WITH THE INCLUSION OF THE
! ABOVE COPYRIGHT NOTICE. THIS SOFTWARE, OR ANY OTHER COPIES THEREOF
! MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON
! EXCEPT FOR USE ON SUCH SYSTEM AND TO ONE WHO AGREES TO THESE LICENSE
! TERMS. TITLE TO AND OWNERSHIP OF THE SOFTWARE SHALL AT ALL TIMES
! REMAIN IN DEC.
!
! THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE
! AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT
! CORPORATION.
!
! DEC ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS
! SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DEC.
!
!++
!
! Facility: LSG DECnet Management
!
! Abstract: This module contains the code for the TOPS20
! Data Link Watcher. It monitors the DTE devices.
! On startup, it attempts to initiate protocol on
! the device. If it fails it issues a LOAD VIA command.
! When a topology change interrupt occurs, it polls
! the DTE devices. If a DTE that has previously been
! loaded goes offline, it issues a DUMP VIA command; then
! a LOAD VIA command is issued.
!
! Environment: TOPS20
!
! Author: Steven M. Jenness Creation date: 30-May-81
! James J. Halpin Phase IV upgrade
!
!--
!
! Include files
!
library 'NMLLIB'; ! Get all required definitions
library 'MONSYM'; ! Monitor symbols
library 'JLNKG'; ! JSYS linkage definitions
library 'BLI:TENDEF'; ! Get MONxxx definitions
!
! Global routines
!
forward routine
NML$DLW_INITIALIZE : novalue, ! DLW task creation/initialization
NML$DLW_SERVICE_EVENT : novalue; ! Service event generator
!
! Local routines
!
forward routine
%if $tops20
%then
DLW_CHECK_DTE_TASK : novalue, ! DTE Check DTE task
%fi
DLW_DTE_TASK : novalue, ! DTE Data link watcher task
DLW_NI_TASK : novalue, ! NI Data link watcher task
DLW_WORK, ! Actual work routine for DLW_DTE_TASK
DLW_QUEUE : novalue, ! Create load/dump request
DLW_DEQUEUE : novalue, ! Post process load/dump response
REQUEST_SERVICE : novalue, ! Process a request MOP message
READ_REQUEST_PROGRAM, ! Read a Request Program MOP Message
READ_REQUEST_DUMP_SERVICE, ! Read a Request Dump Service MOP msg
GET_OTHER_INFO, ! Read 'Other Info' fields of MOP Msgs
DLW_NI_RECEIVER : NI_RECEIVER_INTERRUPT_LINKAGE novalue;
!
! Define structures for CHECK DTE task
!
$field CHK_DTE_FIELDS =
set
CHK_DTE_EVENT = [$sub_block(Q_ENTRY_SIZE)],
CHK_DTE_CIRCUIT_NAME = [$string(17)]
tes;
literal
CHK_DTE_SIZE = $field_set_size,
CHK_DTE_ALLOCATION = $field_set_units;
macro
CHK_DTE_BLOCK = block [CHK_DTE_SIZE] field (CHK_DTE_FIELDS) %;
!
! Owned variables
!
own
NI_RCV_CHANNEL,
NI_RECEIVE_EVENT : EVENT_BLOCK,
LOAD_DUMP_ASST_QUEUE : SQ_HEADER,
LINK_SERVICE_QUEUE : SQ_HEADER,
CHECK_DTE_QUEUE : SQ_HEADER;
! CH$ASCIC (STRING, ...)
!
! Return a pointer to an ASCIC character position sequence
! formed by the characters represented by the string-params,
! which are interpreted as for the %string lexical function.
macro
DLW$ASCIC [ ] =
ch$ptr (uplit (%string (%char (%charcount (%remaining)), %remaining))) %;
macro
LDAQ_BLOCK = block [LDAQ_SIZE] field (LDAQ_FIELDS) %;
!
! Circuit parameter numbers
!
literal
CPARM_STATE = 0, ! Circuit STATE
CPARM_SUBSTATE = 1, ! Circuit SUBSTATE
CPARM_SERVICE = 100; ! Circuit SERVICE
!
! Device type definitions
!
literal
DEVTYP_NI = 15,
DEVTYP_DTE = 20;
!
! External routines
!
external routine
NMU$MEMORY_MANAGER, ! Memory get/release routines
NML$REQUEST_MANAGER, ! NML NICE function management
NMU$SCHED_MANAGER, ! Scheduler
NMU$TEXT_MANAGER, ! Text processor
NML$GET_VDB_PARAMETER, ! Get volatile data base parameter
NML$SET_VDB_PARAMETER, ! Set volatile data base parameter
NML$CLR_VDB_PARAMETER, ! Clear volatile data base parameter
NML$FIND_NDB_HDA, ! Find Node ID using Hardware Address
NMU$DLX_DEVICE_TYPE, ! Return Circuit device type code
NML$CALCULATE_CIRCUIT_STATE : novalue, ! Calculate circuit state
NML$DECLARE_EVENT : novalue; ! Declare event internally to NML
external
%debug_data_base;
%global_routine ('NML$DLW_INITIALIZE') : novalue =
!++
! Functional description:
!
! This routine is called at startup time to initialize the Data
! Link Watcher tasks.
!
! Formal parameters: none
!
! Routine value: none
!
!--
begin
%if $tops20
%then
NMU$SQUEUE_RESET (CHECK_DTE_QUEUE);
NMU$SCHED_CREATE (DLW_CHECK_DTE_TASK,
200,
0,
ch$asciz ('CHECK_DTE_TASK'));
%fi
NMU$SQUEUE_RESET (LINK_SERVICE_QUEUE);
NMU$SCHED_CREATE (DLW_DTE_TASK, 200, 0, ch$asciz ('DTE_LINK-WATCHER'));
NMU$SCHED_EVENT (NI_RECEIVE_EVENT, $true);
NMU$SQUEUE_RESET (LOAD_DUMP_ASST_QUEUE);
NMU$SCHED_CREATE (DLW_NI_TASK, 200, 0, ch$asciz ('NI_LINK-WATCHER'));
NI_RCV_CHANNEL = ALLOCATE_INTERRUPT_CHANNEL (DLW_NI_RECEIVER);
ACTIVATE_INTERRUPT (.NI_RCV_CHANNEL);
end; ! End of NML$DLW_INITIALIZE
%global_routine ('NML$DLW_CHECK_CIRCUIT', CIRCUIT_ID) : novalue =
!++
!
! Functional description:
!
! This routine is called from the local event reading interface to
! indicate that CIRCUIT_ID has had a circuit down event. It will then
! queue up a request for the data link watcher task.
!
! Formal parameters:
!
! .CIRCUIT_ID Byte pointer to counted ASCII string id.
!
!--
begin
local
TEMP : ref DLWQ_BLOCK;
TEMP = NMU$MEMORY_GET (DLWQ_ALLOCATION);
if .TEMP eql 0 then
TASK_ERROR ('Unable to allocate memory for DLW service');
ch$move (ch$rchar (.CIRCUIT_ID) + 1,
.CIRCUIT_ID,
ch$ptr (TEMP [DLWQ_CIRCUIT_NAME],,8));
NMU$SQUEUE_INSERT (LINK_SERVICE_QUEUE, TEMP [DLWQ_EVENT]);
end;
%if $tops20
%then
%routine ('DLW_CHECK_DTE_TASK') : novalue =
!++
! Functional description:
!
! This task is scheduled when a successful DTE initialization has
! occurred. Because of some race in the DTE code we may miss a
! "suicide" and leave the circuit in 'On-starting'. This task will
! be scheduled when a successful initialization has occurred. It
! will sleep 30 seconds and then schedule another check.
!
!--
while $true do
begin
local PTR : ref CHK_DTE_BLOCK;
PTR = NMU$SQUEUE_REMOVE (CHECK_DTE_QUEUE);
NMU$SCHED_SLEEP (30); ! Sleep 30 seconds before check
NML$DLW_CHECK_CIRCUIT (ch$ptr (PTR [CHK_DTE_CIRCUIT_NAME],,8));
NMU$MEMORY_RELEASE (.PTR, CHK_DTE_ALLOCATION);
end; ! End of DLW_CHECK_DTE_TASK
%fi ! End of TOPS20 conditional
%routine ('DLW_DTE_TASK') : novalue =
!++
! Functional description:
!
! This routine is the main body of the Data Link Watcher task.
! It is event driven via NMLEVL, which calls NML$DLW_CHECK_CIRCUIT
! when it gets a circuit down event.
!
! It tries to initiate protocol on the DTE device.
! If initialization fails it issues a dump and then tries
! to load the DN20.
!
! If the DN20 is determined to be dead, the link watcher will
! stop service on it's DTE until it sees that the DN20 has
! been running.
!
! Formal parameters: none
!
! Routine value: none
!
!--
!
! Run this task forever (until NML dies)
!
begin
local
SERVICE_REQUEST : ref DLWQ_BLOCK,
CHK_DTE_REQ : ref CHK_DTE_BLOCK,
TEMP_PTR;
while $true do
begin
!
! Wait for LINK_SERVICE_QUEUE to become non-empty.
!
SERVICE_REQUEST = NMU$SQUEUE_REMOVE (LINK_SERVICE_QUEUE);
if (CHK_DTE_REQ = NMU$MEMORY_GET (CHK_DTE_ALLOCATION)) neqa 0
then begin
TEMP_PTR = ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8);
ch$move (ch$rchar (.TEMP_PTR) + 1,
.TEMP_PTR,
ch$ptr (CHK_DTE_REQ [CHK_DTE_CIRCUIT_NAME],,8));
end;
! Process request
if not DLW_WORK (.SERVICE_REQUEST)
then begin
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
NMU$MEMORY_RELEASE (.CHK_DTE_REQ, CHK_DTE_ALLOCATION);
end
else if .CHK_DTE_REQ neqa 0
then NMU$SQUEUE_INSERT (CHECK_DTE_QUEUE,
CHK_DTE_REQ [CHK_DTE_EVENT]);
end; ! Wnd WHILE $TRUE
end; ! End of DLW_DTE_TASK
%routine ('DLW_WORK', SERVICE_REQUEST : ref DLWQ_BLOCK) =
!++
!
! This routine is the real worker routine for the data link watcher task.
! The reason for encapsulating this routine on it's own is due to the fact
! that it is simpler to write BLISS code in a non-loop context. Ie: if an
! error is detected here, it can just exit the routine (DLW_WORK), and return
! to the encapsulating routine (DLW_DTE_TASK) which will simply restart this
! routine again.
!
! Routine value:
!
! $true Request is being serviced
! $false Request cannot be serviced
!--
begin
local
CIRCUIT_STATE, ! NML circuit state parameter
CIRCUIT_SERVICE, ! NML circuit service parameter
CIRCUIT_SUBSTATE, ! NML circuit substate
CIRCUIT_ID, ! Byte pointer to circuit name
CIRCUIT_BLOCK, ! Circuit block from NMU$DLX_OPEN
RSP_POINTER, ! Pointer to dummy NICE response buffer
RSP: vector [40]; ! Dummy NICE response buffer
CIRCUIT_ID = ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8);
RSP_POINTER = ch$ptr (RSP,,8);
!
! If this isn't a front end to the system, then
! forget about it.
!
if not $NMU_DLX_FRONT_END_DEVICE (.CIRCUIT_ID)
then
return $false;
!
! Calculate new circuit state and substate
!
NML$CALCULATE_CIRCUIT_STATE (.CIRCUIT_ID);
!
! Get STATE parameter for circuit.
! If not set...assume it is on
!
! Note: we get the STATE as known by network management, i.e. the
! monitor is not queried.
if not NML$GET_VDB_PARAMETER (CIRCUIT_,
.CIRCUIT_ID,
CPARM_STATE,
CIRCUIT_STATE)
then
CIRCUIT_STATE = CIRCUIT_OFF;
!
! Get SERVICE parameter for circuit.
! If not set .. assume it is disabled.
!
if not NML$GET_VDB_PARAMETER (CIRCUIT_,
.CIRCUIT_ID,
CPARM_SERVICE,
CIRCUIT_SERVICE)
then
CIRCUIT_SERVICE = CIRCUIT_DISABLED;
!
! If SERVICE DISABLE, then go away since we are not allowed to do anything
!
if .CIRCUIT_SERVICE neq CIRCUIT_ENABLED
then
return $false;
!
! Get circuit substate. If none, assume no substate.
! Again, this is the network management substate, and is not read from
! the monitor.
!
if not NML$GET_VDB_PARAMETER (CIRCUIT_,
.CIRCUIT_ID,
CPARM_SUBSTATE,
CIRCUIT_SUBSTATE)
then
CIRCUIT_SUBSTATE = CIRCUIT_NO_SUBSTATE;
!
! If the circuit has a substate other than STARTING,
! (NML currently using it) then just return now.
!
! The LOAD and DUMP commands set a substate, so if a substate other
! than STARTING, is present, we know that the device is being
! loaded/dumped.
if (.CIRCUIT_SUBSTATE neq CIRCUIT_STARTING)
then return $false;
!
! Open the circuit for protocol service
!
! For DTE-20s, the OPEN does nothing
CIRCUIT_BLOCK = $NMU_DLX_OPEN (DLX_PROTOCOL,
CIRCUIT_,
.CIRCUIT_ID,
0, ! Dummy Arg for Physical Address
-1, ! PSI Interrupts not used here.
-1,
-1,
.RSP_POINTER);
if .CIRCUIT_BLOCK leqa 0
then
begin
%debug (DLW_TRACE,
(TRACE_INFO ('Problem servicing %X: %X',
.CIRCUIT_ID, .RSP_POINTER)));
return $false;
end;
!
! Process based on current circuit status
!
selectoneu .CIRCUIT_STATE of
set
[CIRCUIT_ON, CIRCUIT_SERVICE]:
begin
!
! First check if DECnet protocol is already running
! If it is, we are done and it would be wrong to try to
! initialize protocol
if not $NMU_DLX_DECNET_RUNNING (.CIRCUIT_BLOCK, .RSP_POINTER)
then begin
!
! Try to initialize protocol if that's the problem
! For DTE-20s, this will try to initialize protocol 4 times.
! If all 4 attempts fails, a DUMP request for the device will
! be entered. The 4 attempts seem to be needed because of a
! timing window.
!
! If we fail to initialize protocol, then queue a dump request
if not $NMU_DLX_START_PROTOCOL (.CIRCUIT_BLOCK, .RSP_POINTER)
then DLW_QUEUE (.SERVICE_REQUEST, DUMP_);
end;
end;
[OTHERWISE]:
! For DTE-20s, terminate protocol
$NMU_DLX_STOP_PROTOCOL (.CIRCUIT_BLOCK, .RSP_POINTER);
tes;
! The following call does nothing to DTE-20s
$NMU_DLX_CLOSE (.CIRCUIT_BLOCK);
return $true;
end; ! End of DLW_WORK
%routine ('DLW_QUEUE', SERVICE_REQUEST : ref DLWQ_BLOCK,
FNC_CODE) : novalue =
!++
! Functional description:
!
! This routine issues a internal NICE function request. It sends
! either a LOAD VIA or DUMP VIA command for DTE's or LOAD NODE or
! DUMP NODE command for NI's. The NICE message constructed
! and sent is of the form:
!
! LOAD_, CIRCUIT_, "circuit-id"
!
! DUMP_, CIRCUIT_, "circuit-id"
!
! LOAD_, NODE_E, "node-id"
!
! <or>
!
! DUMP_, NODE_E, "node-id"
!
! The response processing routine is set to DLW_DEQUEUE.
!
! Formal parameters:
!
! .SERVICE_REQUEST Address of DLW service request block
! .FNC_CODE Either LOAD_ or DUMP_
!
! Routine value: none
!
!--
begin
local
REQ_NO, ! NML function request number
REQ : ref REQUEST_BLOCK, ! Request block
ID_LENGTH, ! Number of bytes in entity id
NODE_ADDR, ! Node address fron Entity Id
ENT_PTR, ! Local pointer to entity id
CKT_DEVTYP, ! Device Type code
NICE_PTR, ! Nice message pointer
CIRCUIT_ID, ! Byte pointer to circuit name
CIRCUIT_SERVICE; ! Value of SERVICE parameter (ENABLED?)
CIRCUIT_ID = ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8);
! Check that SERVICE is ENABLED on the circuit
if not NML$GET_VDB_PARAMETER (CIRCUIT_,
.CIRCUIT_ID,
CPARM_SERVICE,
CIRCUIT_SERVICE)
then CIRCUIT_SERVICE = CIRCUIT_DISABLED;
if .CIRCUIT_SERVICE neq CIRCUIT_ENABLED
then begin
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
return;
end;
!
! Log "automatic line service" event
!
NML$DLW_SERVICE_EVENT (.FNC_CODE,
ch$ptr(SERVICE_REQUEST [DLWQ_HARDWARE_ADDRESS],,8),
.SERVICE_REQUEST [DLWQ_PROG_TYPE], .CIRCUIT_ID, 0);
!
! Allocate request block
! Allocate space for a NICE message buffer
! Build pointer to start of NICE message buffer
!
REQ = NMU$MEMORY_GET (REQUEST_BLOCK_ALLOCATION);
REQ [RB_NICE_ALLOCATION] = ch$allocation (25, 8);
REQ [RB_NICE] = NMU$MEMORY_GET (.REQ [RB_NICE_ALLOCATION]);
REQ [RB_NICE_POINTER] = NICE_PTR = ch$ptr (.REQ [RB_NICE],, 8);
REQ [RB_PRV_LOCAL] = $TRUE; ! Internal processes always have privs
REQ [RB_PRV_SERVICE] = $TRUE; ! ...
CKT_DEVTYP = $NMU_DLX_DEVICE_TYPE (.CIRCUIT_ID);
SERVICE_REQUEST [DLWQ_NODE_ID] =
NML$FIND_NDB_HDA (ch$ptr(SERVICE_REQUEST [DLWQ_HARDWARE_ADDRESS],,8));
! Do a LOOP CIRCUIT if device type is DTE or if we don't have a node match
! Do a LOOP NODE otherwise.
if (.CKT_DEVTYP eql DEVTYP_DTE) or (.SERVICE_REQUEST [DLWQ_NODE_ID] eql 0)
then begin
!
! Build NICE message to dump across the circuit
!
ENT_PTR = .CIRCUIT_ID;
PUTB (.FNC_CODE, NICE_PTR);
PUTB (CIRCUIT_, NICE_PTR);
ID_LENGTH = GETB (ENT_PTR);
PUTB (.ID_LENGTH, NICE_PTR);
ch$move (.ID_LENGTH, .ENT_PTR, .NICE_PTR);
REQ [RB_NICE_LENGTH] = 3 + .ID_LENGTH;
end
else if .CKT_DEVTYP eql DEVTYP_NI
then begin
ENT_PTR = .SERVICE_REQUEST [DLWQ_NODE_ID];
PUTB (.FNC_CODE, NICE_PTR);
PUTB (NODE_E, NICE_PTR);
NODE_ADDR = GETW (ENT_PTR); ! Read/Skip past the Node Address
ID_LENGTH = GETB (ENT_PTR);
PUTB (.ID_LENGTH, NICE_PTR);
ch$move (.ID_LENGTH, .ENT_PTR, .NICE_PTR);
REQ [RB_NICE_LENGTH] = 3 + .ID_LENGTH;
end
else begin ! Unknown device type
NMU$MEMORY_RELEASE (.REQ[RB_NICE], .REQ [RB_NICE_ALLOCATION]);
NMU$MEMORY_RELEASE (.REQ, REQUEST_BLOCK_ALLOCATION);
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
return;
end;
!
! Setup cells in the request block needed to perform
! a DUMP VIA function
!
REQ [RB_EXECUTOR] = 0;
REQ [RB_DATA] = .SERVICE_REQUEST;
REQ [RB_DATA_ALLOCATION] = 0;
REQ [RB_COMPLETION] = DLW_DEQUEUE;
REQ [RB_DLW_DATA] = .SERVICE_REQUEST;
!
! Now set substate to AUTOSERVICE and then queue the NICE request
!
NML$SET_VDB_PARAMETER (CIRCUIT_,
.CIRCUIT_ID,
CPARM_SUBSTATE,
%ref (CIRCUIT_AUTOSERVICE));
REQ_NO = NML$REQUEST_ENTER (.REQ);
%debug (DLW_TRACE,
(if .FNC_CODE eql DUMP_
then TRACE_INFO ('Auto-DUMP request #%D entered for %#A',
.REQ_NO, .ID_LENGTH, .ENT_PTR)
else TRACE_INFO ('Auto-LOAD request #%D entered for %#A',
.REQ_NO, .ID_LENGTH, .ENT_PTR)));
end; ! End of DLW_QUEUE
%routine ('DLW_DEQUEUE', REQ : ref REQUEST_BLOCK) : novalue =
!++
! Functional description:
!
! This routine processing the response from a DLW NICE function
! request. If it is a DUMP_ request it will issue the corresponding
! LOAD_ request. If it is a LOAD_ request it will set the CIRCUIT
! status to the appropriate value. It will also free up any
! memory allocated for the request.
!
! Formal parameters:
!
! .REQ Request block address
!
! Routine value: none
!
!--
begin
local
RSP_PTR, ! Pointer to response message
RSP_CODE, ! NICE response code
NICE_PTR, ! NICE message pointer
FNC_CODE, ! NICE function code
CKT_PTR, ! Circuit ID pointer
SERVICE_REQUEST : ref DLWQ_BLOCK; ! Service request block
!
! Get pointer to auto-serviced circuit
! Get pointer to response message
! Get pointer to nice message
! Get response code
! Get function request was to execute
!
SERVICE_REQUEST = .REQ [RB_DATA];
CKT_PTR = ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8);
RSP_PTR = ch$ptr (.REQ [RB_RESPONSE],, 8);
NICE_PTR = ch$ptr (.REQ [RB_NICE],, 8);
FNC_CODE = ch$rchar_a (NICE_PTR);
NML$DLW_SERVICE_EVENT (.FNC_CODE,
ch$ptr(SERVICE_REQUEST [DLWQ_HARDWARE_ADDRESS],,8),
.SERVICE_REQUEST [DLWQ_PROG_TYPE], .CKT_PTR,
.RSP_PTR);
RSP_CODE = ch$rchar_a (RSP_PTR);
!
! Finish quickly if request was cancelled
!
if .REQ [RB_STATE] eql RB$CANCELLED
then
begin
%debug (DLW_TRACE,
(begin
if .FNC_CODE eql DUMP_
then TRACE_INFO ('Auto-DUMP cancelled on %X',
.CKT_PTR)
else TRACE_INFO ('Auto-LOAD cancelled on %X',
.CKT_PTR);
end));
NML$CLR_VDB_PARAMETER (CIRCUIT_, .CKT_PTR,
CPARM_SUBSTATE);
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
NMU$MEMORY_RELEASE (.REQ [RB_NICE], .REQ [RB_NICE_ALLOCATION]);
if .REQ [RB_RESPONSE] neqa 0
then NMU$MEMORY_RELEASE (.REQ [RB_RESPONSE],
.REQ [RB_RESPONSE_ALLOCATION]);
NMU$MEMORY_RELEASE (.REQ, REQUEST_BLOCK_ALLOCATION);
return;
end;
!
! Display result of request
!
%debug (DLW_TRACE,
(begin
if .FNC_CODE eql DUMP_
then
if .RSP_CODE neq NICE$_SUC
then TRACE_INFO ('Auto-DUMP failed on %X',
.CKT_PTR)
else TRACE_INFO ('Auto-DUMP done on %X',
.CKT_PTR)
else
if .RSP_CODE neq NICE$_SUC
then TRACE_INFO ('Auto-LOAD failed on %X',
.CKT_PTR)
else TRACE_INFO ('Auto-LOAD done on %X',
.CKT_PTR);
if .RSP_CODE neq NICE$_SUC
then TRACE_INFO (' Return code = %D Detail = %D; %X',
.RSP_CODE <0, 8, 1>, GETW (RSP_PTR), .RSP_PTR);
end));
!
! Now release REQ block
if .REQ [RB_RESPONSE] neqa 0
then NMU$MEMORY_RELEASE (.REQ [RB_RESPONSE],
.REQ [RB_RESPONSE_ALLOCATION]);
NMU$MEMORY_RELEASE (.REQ [RB_NICE], .REQ [RB_NICE_ALLOCATION]);
NMU$MEMORY_RELEASE (.REQ, REQUEST_BLOCK_ALLOCATION);
!
! If a dump was just done .. try to do a LOAD
! If a load was just done .. set the circuit substate
!
if .FNC_CODE eql DUMP_
then DLW_QUEUE (.SERVICE_REQUEST, LOAD_)
else ! It was a LOAD
if .RSP_CODE eql NICE$_SUC
then NML$CLR_VDB_PARAMETER (CIRCUIT_, .CKT_PTR,
CPARM_SUBSTATE)
else NML$SET_VDB_PARAMETER (CIRCUIT_, .CKT_PTR,
CPARM_SUBSTATE, %ref (CIRCUIT_FAILED));
!
! Release SERVICE_REQUEST block if we just did a LOAD - if we did a DUMP
! it is queued to be loaded.
!
if .FNC_CODE eql LOAD_
then NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
end; ! End of DLW_DEQUEUE
%global_routine ('NML$DLW_SERVICE_EVENT', FUNC_CODE,
HARDWARE_ADDRESS,
PROGRAM_TYPE,
CIRCUIT_PTR,
RSP_PTR) : novalue =
!++
! Functional description:
!
! This routine initiates an "automatic line service" event
! for the specified entity.
!
! Formal parameters:
!
! .FUNC_CODE Function code (LOAD_, DUMP_)
! .HARDWARE_ADDRESS Pointer to hardware address of node (for NI).
! .PROGRAM_TYPE Program type for LOAD_ (secondary, tertiary,
! system).
! .CIRCUIT_PTR Pointer to entity-id string
! .RSP_PTR If zero, then function requested,
! otherwise pointer to response message
!
! Routine value: none
!
!--
begin
local
DATA : vector [ch$allocation (86, 8)],
DATA_PTR;
DATA_PTR = ch$ptr (DATA,, 8);
! First parameter #0 = SERVICE. It is a CS-1 with values 0 (LOAD) and
! 1 (DUMP).
begin
literal
DATA_ID = 0^15 + 0,
DATA_TYPE = 1^7 + 0^6 + 1;
ch$wchar_a (DATA_ID and %o'377', DATA_PTR);
ch$wchar_a (DATA_ID^-8 and %o'377', DATA_PTR);
ch$wchar_a (DATA_TYPE, DATA_PTR);
ch$wchar_a ((if .FUNC_CODE eql LOAD_ then 0 else 1), DATA_PTR);
end;
! Now do parameter 2500 (TOPS-20 specific). It is only generated if the
! circuit is the NI, and it gives the hardware address of the system
! requesting the service.
!
! It is network management event parameter 2500, type HI-6.
! Only generate physical address if this is on the NI circuit
if $NMU_DLX_DEVICE_TYPE (.CIRCUIT_PTR) eql DEVTYP_NI
then begin
literal
DATA_ID = 0^15 + 2500,
DATA_TYPE = 2^4 + 6; ! HI-6
local
ADR_PTR,
INDEX,
BYT_VEC : VECTOR [6];
ch$wchar_a (DATA_ID and %o'377', DATA_PTR);
ch$wchar_a (DATA_ID^-8 and %o'377', DATA_PTR);
ch$wchar_a (DATA_TYPE, DATA_PTR);
! Now write hex image, but in reverse order....
ADR_PTR = .HARDWARE_ADDRESS;
incr INDEX from 0 to 5 do BYT_VEC [.INDEX] = GETB (ADR_PTR);
decr INDEX from 5 to 0 do ch$wchar_a (.BYT_VEC [.INDEX], DATA_PTR);
end;
! Parameter #1 is the status code. It is a CM-1 CS-1 containing the NICE
! return code. I.e. we do not do the CM-2 or CM-3 versions of it.
begin ! Parameter #1
local
RESP_PTR, VAL;
literal
DATA_ID = 0^15 + 1,
CM_1_DATA_TYPE = 1^7 + 1^6 + 1,
C_1_DATA_TYPE = 1^7 + 0^6 + 1;
ch$wchar_a (DATA_ID and %o'377', DATA_PTR);
ch$wchar_a (DATA_ID^-8 and %o'377', DATA_PTR);
ch$wchar_a (CM_1_DATA_TYPE, DATA_PTR);
ch$wchar_a (C_1_DATA_TYPE, DATA_PTR);
! If there was no response pointer supplied in the call, then we
! should write a 0 = REQUESTED. Otherwise write the response byte.
VAL = (if (RESP_PTR = .RSP_PTR) eql 0
then 0
else ch$rchar_a (RESP_PTR));
ch$wchar_a (.VAL, DATA_PTR);
end;
! If this is a load request (response pointer 0) then write software type as
! well. It is parameter 8, C-1 with values 0,1,2 (secondary, tertiary,
! system).
if (.RSP_PTR eql 0) and (.FUNC_CODE eql LOAD_)
then begin
literal
DATA_ID = 0^15 + 8,
DATA_TYPE = 1^7 + 0^6 + 1; ! C-1
ch$wchar_a (DATA_ID and %o'377', DATA_PTR);
ch$wchar_a (DATA_ID^-8 and %o'377', DATA_PTR);
ch$wchar_a (DATA_TYPE, DATA_PTR);
ch$wchar_a (.PROGRAM_TYPE, DATA_PTR);
end;
! Now generate the REB to be supplied to the event logger
begin
local
REB : RAW_EVENT_BLOCK;
! Generate a 0.3 (Automatic line service) either if this is a request or
! if it was a successful operation. Generate 0.7 (Aborted service requst)
! if the service failed.
! Assume 0.3
REB [REB_EVENT_CODE] = 0^6 + 3;
if .RSP_PTR neq 0
then begin
local RESP_PTR;
RESP_PTR = .RSP_PTR;
if GETB (RESP_PTR) neq NICE$_SUC then REB [REB_EVENT_CODE] = 0^6 + 7;
end;
REB [REB_ENTITY_TYPE] = CIRCUIT_;
REB [REB_ENTITY_POINTER] = .CIRCUIT_PTR;
REB [REB_DATA_POINTER] = ch$ptr (DATA,, 8);
REB [REB_DATA_LENGTH] = ch$diff (.DATA_PTR, ch$ptr (DATA,, 8));
REB [REB_SOURCE_POINTER] = 0;
REB [REB_TIME_BLOCK] = 0;
NML$DECLARE_EVENT (REB);
end;
end; ! End of DLW_SERVICE_EVENT
%routine ('DLW_NI_TASK') : novalue =
!++
! Functional description:
!
! This routine is the main body of the NI Data Link Watcher task.
!
! Formal parameters: none
!
! Routine value: none
!
!--
!
! Run this task forever (until NML dies)
!
begin
literal
MOP_BUFFER_LENGTH = 1506, ! MOP message buffer in bytes
MOP_BUFFER_SIZE = ch$allocation (MOP_BUFFER_LENGTH, 8),
MOP_BUFFER_ALLOCATION = MOP_BUFFER_SIZE * %upval,
MOP_TIME_OUT = 4;
local
CIRCUIT_BLOCK : ref CD_BLOCK,
LDA_MSG : ref LDAQ_BLOCK,
MOP_BUFFER,
MOP_BUFFER_PTR,
MOP_FUNCTION,
RESULT,
RSP_POINTER,
RSP: vector [40],
SERVICE_REQUEST : ref DLWQ_BLOCK,
CIRCUIT_POINTER,
CIRCUIT_ID : vector [ch$allocation (10, 8)],
TEMP_PTR;
CIRCUIT_POINTER = DLW$ASCIC ('NI-0-0');
RSP_POINTER = ch$ptr (RSP,,8);
TEMP_PTR = .CIRCUIT_POINTER;
ch$move (ch$rchar (.TEMP_PTR)+1,
.TEMP_PTR,
ch$ptr( CIRCUIT_ID,,8));
while $true
do begin
TEMP_PTR = ch$ptr( CIRCUIT_ID,,8);
CIRCUIT_BLOCK = $NMU_DLX_OPEN (DLX_LDA,
CIRCUIT_,
.TEMP_PTR,
0,
.NI_RCV_CHANNEL,
-1,
-1,
.RSP_POINTER);
if .CIRCUIT_BLOCK leqa 0
then NMU$SCHED_SLEEP (60)
else begin
RESULT = $NMU_DLX_ENABLE_MULTICAST (.CIRCUIT_BLOCK,
.RSP_POINTER);
if .RESULT
then exitloop
else begin
RESULT = $NMU_DLX_CLOSE (.CIRCUIT_BLOCK);
NMU$SCHED_SLEEP (60);
end;
end;
end;
while $true ! Loop forever, Unless there is an
do begin ! error return from DLX.
MOP_BUFFER = NMU$MEMORY_GET (MOP_BUFFER_ALLOCATION);
if .MOP_BUFFER eql 0 then
TASK_ERROR ('Unable to allocate MOP buffer for DLW service');
MOP_BUFFER_PTR = ch$ptr (.MOP_BUFFER,,8);
SERVICE_REQUEST = NMU$MEMORY_GET (DLWQ_ALLOCATION);
if .SERVICE_REQUEST eql 0 then
TASK_ERROR ('Unable to allocate memory for DLW service');
TEMP_PTR = ch$ptr( CIRCUIT_ID,,8);
ch$move (ch$rchar (.TEMP_PTR)+1,
.TEMP_PTR,
ch$ptr( SERVICE_REQUEST[DLWQ_CIRCUIT_NAME],,8));
CIRCUIT_BLOCK [CD_KLNI_HRDWADR] = ch$ptr (SERVICE_REQUEST [DLWQ_HARDWARE_ADDRESS],,8);
CIRCUIT_BLOCK [CD_KLNI_MULTICAST] = $false; ! Clear Multicast received flag
!
! Wait until the NI% JSYS wakes us up.
!
if (
local CIRCUIT_SUBSTATE, STATUS;
TEMP_PTR = ch$ptr (CIRCUIT_ID,,8);
if not NML$GET_VDB_PARAMETER (CIRCUIT_,
.TEMP_PTR,
CPARM_SUBSTATE,
CIRCUIT_SUBSTATE)
then CIRCUIT_SUBSTATE = CIRCUIT_NO_SUBSTATE;
selectone .CIRCUIT_SUBSTATE of
set
[CIRCUIT_LOADING,
CIRCUIT_DUMPING,
CIRCUIT_AUTOSERVICE to CIRCUIT_AUTODUMPING]:
STATUS = NMU$SCHED_WAIT (NI_RECEIVE_EVENT, MOP_TIME_OUT);
[otherwise]:
! We would like to sleep forever here, but NMU$SCHED_WAIT
! doesn't do what we expect it to with a time of 0.
! So set up to wait a "long" time (300 seconds)
STATUS = NMU$SCHED_WAIT (NI_RECEIVE_EVENT, 300);
tes;
.STATUS
)
then
begin
RESULT = $NMU_DLX_READ (.CIRCUIT_BLOCK,
DLX_DATA,
.MOP_BUFFER_PTR,
MOP_BUFFER_LENGTH,
0,
.RSP_POINTER);
if .RESULT leq 0
then begin
TEMP_PTR =
ch$ptr( SERVICE_REQUEST[DLWQ_CIRCUIT_NAME],,8);
%debug (DLW_TRACE,
(TRACE_INFO ('Problem servicing %X: %X',
.TEMP_PTR, .RSP_POINTER)));
NMU$MEMORY_RELEASE (.MOP_BUFFER,
MOP_BUFFER_ALLOCATION);
NMU$MEMORY_RELEASE (.SERVICE_REQUEST,
DLWQ_ALLOCATION);
exitloop;
end;
if .CIRCUIT_BLOCK [CD_KLNI_MULTICAST]
then begin
! If it is a multicast message, then it should be
! a 'REQUEST PROGRAM' or 'REQUEST DUMP SERVICE'
! message. If not, just discard it.
REQUEST_SERVICE (.MOP_BUFFER,
.SERVICE_REQUEST,
.RESULT,
$true);
! Release MOP buffer
NMU$MEMORY_RELEASE (.MOP_BUFFER,
MOP_BUFFER_ALLOCATION);
end
! Not MULTICAST
else begin
local TEMP, FCN_CODE, CIRCUIT_SUBSTATE;
! An incoming message not on the multicast can be of
! three categories:
! (i) a message that is part of an ongoing load or dump
! In this case, the circuit substate will be loading
! or dumping.
! (ii) a RPM or RDS that is an ACK of a previous
! 'assistance volounteer'. The substate will be
! loading or dumping, and we should queue the
! message to NMLDTL
! (iii) a RPM or RDS that is addressed to us specifically
! The circuit substate will NOT be loading or
! dumping. Queue the request to be serviced.
if not NML$GET_VDB_PARAMETER (
CIRCUIT_,
ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8),
CPARM_SUBSTATE,
CIRCUIT_SUBSTATE)
then CIRCUIT_SUBSTATE = CIRCUIT_NO_SUBSTATE;
TEMP = ch$ptr (.MOP_BUFFER,,8);
FCN_CODE = GETB (TEMP);
if ((.FCN_CODE eql REQUEST_PROGRAM)
or (.FCN_CODE eql REQUEST_DUMP_SERVICE))
and not
((.CIRCUIT_SUBSTATE eql CIRCUIT_LOADING)
or (.CIRCUIT_SUBSTATE eql CIRCUIT_LOADING)
or ((.CIRCUIT_SUBSTATE geq CIRCUIT_AUTOSERVICE)
and (.CIRCUIT_SUBSTATE leq CIRCUIT_AUTODUMPING)))
then begin
! Call common routine to process request message
REQUEST_SERVICE (.MOP_BUFFER,
.SERVICE_REQUEST,
.RESULT,
$false);
! Release MOP buffer
NMU$MEMORY_RELEASE (.MOP_BUFFER,
MOP_BUFFER_ALLOCATION);
end
else begin
! Make sure that we are interested in the message.
! That is the case only while we are loading or
! dumping.
selectone .CIRCUIT_SUBSTATE of
set
[CIRCUIT_LOADING,
CIRCUIT_DUMPING,
CIRCUIT_AUTOSERVICE to CIRCUIT_AUTODUMPING]:
! Yes, we are interested. Put this message on
! a queue to be read by the appropriate routine
! in NMLDTL. The NML$DLW_READ_LDAQ is responsible
! for deallocating the MOP buffer.
begin
if (LDA_MSG = NMU$MEMORY_GET (LDAQ_ALLOCATION)) eql 0
then TASK_ERROR (
'Unable to allocate memory for DLW service');
LDA_MSG [LDAQ_CIRCUIT_HANDLE] = .CIRCUIT_BLOCK;
! Copy hardware address
ch$move (
6,
ch$ptr(SERVICE_REQUEST [DLWQ_HARDWARE_ADDRESS],,8),
ch$ptr( LDA_MSG [LDAQ_SAD],,8));
LDA_MSG [LDAQ_BUF_PTR] = ch$ptr (.MOP_BUFFER,,8);
LDA_MSG [LDAQ_MSG_LEN] = .RESULT;
LDA_MSG [LDAQ_BUFFER_ADDRESS] = .MOP_BUFFER;
LDA_MSG [LDAQ_BUFFER_SIZE] = MOP_BUFFER_ALLOCATION;
! Put the MOP message on the LOAD DUMP sched queue
NMU$SQUEUE_INSERT (LOAD_DUMP_ASST_QUEUE,
LDA_MSG [LDAQ_EVENT]);
end;
[otherwise]:
! We are not interested, just release MOP buffer
NMU$MEMORY_RELEASE (.MOP_BUFFER,
MOP_BUFFER_ALLOCATION);
tes;
! Release the DLWQ block
NMU$MEMORY_RELEASE (.SERVICE_REQUEST,
DLWQ_ALLOCATION);
end;
end;
end
! Here when call to NMU$SCHED_WAIT times out
else begin
local
CIRCUIT_SUBSTATE;
TEMP_PTR = ch$ptr (CIRCUIT_ID,,8);
if not NML$GET_VDB_PARAMETER (CIRCUIT_,
.TEMP_PTR,
CPARM_SUBSTATE,
CIRCUIT_SUBSTATE)
then CIRCUIT_SUBSTATE = CIRCUIT_NO_SUBSTATE;
selectone .CIRCUIT_SUBSTATE of
set
[CIRCUIT_LOADING,
CIRCUIT_DUMPING,
CIRCUIT_AUTOSERVICE to CIRCUIT_AUTODUMPING]:
begin
LDA_MSG = NMU$MEMORY_GET (LDAQ_ALLOCATION);
if .LDA_MSG eql 0 then
TASK_ERROR (
'Unable to allocate memory for DLW service');
LDA_MSG [LDAQ_CIRCUIT_HANDLE] = .CIRCUIT_BLOCK;
LDA_MSG [LDAQ_MSG_LEN] = -2;
LDA_MSG [LDAQ_BUFFER_ADDRESS] = .MOP_BUFFER;
LDA_MSG [LDAQ_BUFFER_SIZE] = MOP_BUFFER_ALLOCATION;
NMU$SQUEUE_INSERT (LOAD_DUMP_ASST_QUEUE,
LDA_MSG [LDAQ_EVENT]);
end;
[otherwise]:
NMU$MEMORY_RELEASE (.MOP_BUFFER, MOP_BUFFER_ALLOCATION);
tes;
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
end;
end; ! 2nd while $true
end; ! End of DLW_NI_TASK
%routine ('REQUEST_SERVICE', MOP_BUFFER,
SERVICE_REQUEST : ref DLWQ_BLOCK,
LENGTH,
MULTICAST) : novalue =
!++
! Functional description:
!
! This routine processes an incoming MOP request for service, i.e. a
! 'request program message' or 'request dump service'.
!
! The routine is responsible for disposing of the SERVICE_REQUEST (either
! by deallocation it or queueing it to DLW_QUEUE.
!
! The routine should NOT deallocate the MOP BUFFER.
!
! Parameters:
!
! MOP_BUFFER Address of MOP buffer
! SERVICE_REQUEST Address of DLWQ block
! LENGTH # of characters in MOP message
! MULTICAST TRUE if the message came in on the multicast
begin
local
MOP_BUFFER_PTR,
CIRCUIT_SUBSTATE;
! Check if a substate is set - if so, just discard this message.
! We cannot process it because DLW_QUEUE will set the circuit state
! to AUTOSERVICE, which would mess up any ongoing load or dump.
!
! The whole queueing of loads and dumps could certainly be improved;
! this code was written for DTE's and was twisted into fitting into
! the NI scheme. Time constraints....
if not NML$GET_VDB_PARAMETER (
CIRCUIT_,
ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8),
CPARM_SUBSTATE,
CIRCUIT_SUBSTATE)
then CIRCUIT_SUBSTATE = CIRCUIT_NO_SUBSTATE;
if (.CIRCUIT_SUBSTATE geq CIRCUIT_LOADING)
and (.CIRCUIT_SUBSTATE leq CIRCUIT_AUTOTRIGGERING)
then begin
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
return; ! Nothing more to do
end;
! Now dispatch depending on MOP function code
MOP_BUFFER_PTR = ch$ptr (.MOP_BUFFER,,8);
selectone GETB (MOP_BUFFER_PTR) of
set
[REQUEST_PROGRAM]:
! Read the request program message and decompose it. If it is
! OK, then queue it for service. Otherwise, just release the
! DLWQ block.
if READ_REQUEST_PROGRAM (.MOP_BUFFER_PTR,
.SERVICE_REQUEST,
.LENGTH - 1) ! (We read 1 byte already)
then begin
! If this came in on the multicast and it is not a request
! for a secondary loader, then we should flag that this
! request needs an 'ASSISTANCE VOLOUNTEER'
if (.MULTICAST eql $true)
and (.SERVICE_REQUEST [DLWQ_PROG_TYPE] neq SECONDARY_LOADER)
then SERVICE_REQUEST [DLWQ_NEED_RPM] = $true;
! Queue the load request
DLW_QUEUE (.SERVICE_REQUEST, LOAD_);
end
else begin
! Issue debugging message
%debug (DLW_TRACE,
(TRACE_INFO (
'Illegal request program message received on %X',
ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8))));
! and release the DLWQ_BLOCK
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
end;
[REQUEST_DUMP_SERVICE]:
! Process the REQUEST_DUMP_SERVICE message and decompose it. If it
! is OK, queue it for service. If not, just release the DLWQ block
if READ_REQUEST_DUMP_SERVICE (.MOP_BUFFER_PTR,
.SERVICE_REQUEST,
.LENGTH - 1) ! We read 1 byte already
then begin
! If this came in on the multicast, then flag that an
! 'ASSISTANCE VOLOUNTEER' is needed.
if .MULTICAST eql $true
then SERVICE_REQUEST [DLWQ_NEED_RPM] = $true;
! Now queue a dump request
DLW_QUEUE (.SERVICE_REQUEST, DUMP_);
end
else begin
! Issue debugging message
%debug (DLW_TRACE,
(TRACE_INFO (
'Illegal request dump service message received on %X',
ch$ptr (SERVICE_REQUEST [DLWQ_CIRCUIT_NAME],,8))));
! and release the DLWQ_BLOCK
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
end;
[otherwise]:
NMU$MEMORY_RELEASE (.SERVICE_REQUEST, DLWQ_ALLOCATION);
tes;
end; ! End of REQUEST_SERVICE
%global_routine ('NML$DLW_READ_LDAQ', CD : ref CD_BLOCK,
USAGE,
PTR,
LEN,
RSP_POINTER,
ADDR) =
!++
! Functional description:
!
! This routine reads a maintenance message from the specified
! circuit.
!
! Formal parameters:
!
! .CD Identifier for circuit
! .USAGE What type of read is supposed to be done
! (DLX_DATA for MOP data)
! .PTR Pointer to message buffer
! .LEN Number of bytes in message buffer to write
! .RSP_POINTER Pointer to NICE response buffer
! .ADDR Pointer to hardware address we want to read
! from
!
! Routine value:
!
! Number of bytes read on circuit
!
! or
!
! -2 for read timeout
! -1 for any other error
!
!--
begin
local
LDA_MSG : ref LDAQ_BLOCK,
TEMP;
while $true do
begin
LDA_MSG = NMU$SQUEUE_REMOVE (LOAD_DUMP_ASST_QUEUE);
if (ch$eql (6, .ADDR, 6, ch$ptr (LDA_MSG [LDAQ_SAD],,8))
or .LDA_MSG [LDAQ_MSG_LEN] eql -2)
then exitloop ! Have a good message
else begin
NMU$MEMORY_RELEASE (.LDA_MSG [LDAQ_BUFFER_ADDRESS],
.LDA_MSG [LDAQ_BUFFER_SIZE]);
NMU$MEMORY_RELEASE (.LDA_MSG, LDAQ_ALLOCATION);
end;
end;
if .CD neq .LDA_MSG [LDAQ_CIRCUIT_HANDLE]
then begin
NMU$MEMORY_RELEASE (.LDA_MSG [LDAQ_BUFFER_ADDRESS],
.LDA_MSG [LDAQ_BUFFER_SIZE]);
NMU$MEMORY_RELEASE (.LDA_MSG, LDAQ_ALLOCATION);
return -1;
end;
if (( TEMP = .LDA_MSG [LDAQ_MSG_LEN]) gtr .LEN)
then begin
NMU$MEMORY_RELEASE (.LDA_MSG [LDAQ_BUFFER_ADDRESS],
.LDA_MSG [LDAQ_BUFFER_SIZE]);
NMU$MEMORY_RELEASE (.LDA_MSG, LDAQ_ALLOCATION);
return -1;
end;
if .TEMP eql -2
then begin
NMU$MEMORY_RELEASE (.LDA_MSG [LDAQ_BUFFER_ADDRESS],
.LDA_MSG [LDAQ_BUFFER_SIZE]);
NMU$MEMORY_RELEASE (.LDA_MSG, LDAQ_ALLOCATION);
return -2;
end;
! Everything well, copy message into callers buffer
ch$move (.TEMP,
.LDA_MSG [LDAQ_BUF_PTR],
.PTR);
NMU$MEMORY_RELEASE (.LDA_MSG [LDAQ_BUFFER_ADDRESS],
.LDA_MSG [LDAQ_BUFFER_SIZE]);
NMU$MEMORY_RELEASE (.LDA_MSG, LDAQ_ALLOCATION);
return .TEMP;
end; ! Of NML$DLW_READ_LDAQ
%routine ('READ_REQUEST_PROGRAM', MOP_PTR, SERVICE_REQUEST : ref DLWQ_BLOCK, LEN) =
!++
! Functional Description:
! This routine reads information out of the Request
! Program MOP message and stores it into the appropriate
! field of the SERVICE_REQUEST Block.
!
! Formal Parameters:
!
! MOP_PTR Character pointer to the second byte of the Request
! Program message.
!
! SERVICE_REQUEST Address of a Data Link Watcher Service Request
! Queue Block.
!
! LEN Number of bytes left in MOP Message.
!
! Routine Value:
! $true If successful.
! $false Error return.
!
!--
begin
local
TEMP : $SIGNED_BYTE_VALUE,
CNT;
CNT = 0;
if NEXTB (.LEN, CNT)
then SERVICE_REQUEST[DLWQ_DEVTYP] = GETB (MOP_PTR)
else return $false;
if NEXTB (.LEN, CNT)
then SERVICE_REQUEST[DLWQ_FORMAT_VER] = GETB (MOP_PTR)
else return $false;
if NEXTB (.LEN, CNT)
then SERVICE_REQUEST[DLWQ_PROG_TYPE] = GETB (MOP_PTR)
else return $false;
if NEXTB (.LEN, CNT)
then TEMP = GETB (MOP_PTR)
else return $false;
SERVICE_REQUEST[DLWQ_SID_FORM] = .TEMP[VALUE];
if .SERVICE_REQUEST[DLWQ_SID_FORM] gtr 0
then if NEXTN (.LEN, CNT, .SERVICE_REQUEST[DLWQ_SID_FORM])
then begin
ch$move (.TEMP,
.MOP_PTR,
ch$ptr (SERVICE_REQUEST[DLWQ_SOFTWARE_ID],,8));
MOP_PTR = ch$plus (.MOP_PTR, .TEMP);
end
else return $false;
if NEXTB (.LEN, CNT)
then SERVICE_REQUEST[DLWQ_PROCESSOR] = GETB (MOP_PTR)
else return $false;
return GET_OTHER_INFO (.MOP_PTR, .SERVICE_REQUEST, .LEN, .CNT);
end;
%routine ('GET_OTHER_INFO', MOP_PTR, SERVICE_REQUEST : ref DLWQ_BLOCK, LEN, CNT) =
!++
! Functional Description:
! This routine read the 'Other Info' portion of
! any MOP message.
!
!
! Formal Parameters:
!
! MOP_PTR Character pointer to the second byte of the Request
! Program message.
!
! SERVICE_REQUEST Address of a Data Link Watcher Service Request
! Queue Block.
!
! LEN Number of bytes in MOP Message.
!
! CNT Number of bytes already read from the MOP Message.
!
! Routine Value:
! $true If successful.
! $false Error return.
!
!--
begin
literal DATA_LINK_BUFFER_SIZE = 401;
while NEXTW (.LEN, CNT) do
begin
local INFO_TYPE,
INFO_LENGTH;
INFO_TYPE = GETW (MOP_PTR);
selectone .INFO_TYPE of
set
[0]:
return $true;
[DATA_LINK_BUFFER_SIZE]:
begin
if NEXTB (.LEN, CNT)
then INFO_LENGTH = GETB (MOP_PTR)
else return $false;
if not .INFO_LENGTH eql 2 then return $false;
if NEXTW (.LEN, CNT)
then SERVICE_REQUEST[DLWQ_DL_BUFSIZ] = GETW (MOP_PTR);
end;
[otherwise]:
begin
if NEXTB (.LEN, CNT)
then INFO_LENGTH = GETB (MOP_PTR)
else return $false;
if NEXTN (.LEN, CNT, .INFO_LENGTH)
then GETN (MOP_PTR, .INFO_LENGTH)
else return $false;
end;
tes;
end;
return $true;
end;
%routine ('READ_REQUEST_DUMP_SERVICE', MOP_PTR,
SERVICE_REQUEST : ref DLWQ_BLOCK,
LEN) =
!++
!
! Functional description:
! This routine reads information out of the 'Request dump service'
! message and stores it in the appropriate field of the SERVICE_
! REQUEST block.
!
! Parameter:
! MOP_PTR Character pointer to MOP message
! SERVICE_REQUEST Address of DLWQ block
! LEN Number of bytes left in MOP message
!
! Notes:
! The format of the REQUEST DUMP SERVICE message is:
!
! CODE DEVICE FORMAT MEMORY BITS OTHER INFO
! TYPE VERSION SIZE
!
! The only valid OTHER INFO parameter is DATA LINK BUFFER SIZE (401)
!--
begin
local CNT;
CNT = 0;
! Read device type
if NEXTB (.LEN, CNT)
then SERVICE_REQUEST [DLWQ_DEVTYP] = GETB (MOP_PTR)
else return $false;
! Read format version
if NEXTB (.LEN, CNT)
then SERVICE_REQUEST [DLWQ_FORMAT_VER] = GETB (MOP_PTR)
else return $false;
! Read memory size
if NEXTN (.LEN, CNT, 4)
then SERVICE_REQUEST [DLWQ_MEMORY_SIZE] = GETN (MOP_PTR, 4)
else return $false;
! Skip over bits
if NEXTB (.LEN, CNT)
then GETB (MOP_PTR)
else return $false;
! Let the GET_OTHER_INFO routine care about the data link buffer size
GET_OTHER_INFO (.MOP_PTR, .SERVICE_REQUEST, .LEN, .CNT);
$true
end;
%routine ('DLW_NI_RECEIVER') NI_RECEIVER_INTERRUPT_ROUTINE novalue =
!+
! Functional Description:
! This routine is called whenever a NI% JSYS Receive Completion
! Interrupt occurs.
!-
begin
NMU$SCHED_FLAG (NI_RECEIVE_EVENT);
PROCESS_WAKE;
end; ! End of DLW_NI_RECEIVER
end ! End of NMLDLW
eludom