() (****************************************************************) () (Copyright (c) 1992, Carnegie Mellon University) () (All Rights Reserved) () (Permission is hereby granted to use, copy, modify, and) (distribute this software provided that the above copyright) (notice appears in all copies and that any distribution be for) (noncommercial purposes.) () (Carnegie Mellon University disclaims all warranties with regard) (to this software. In no event shall Carnegie Mellon University) (be liable for any special, indirect, or consequential damages) (or any damages whatsoever resulting from loss of use, data, or) (profits arising out of or in connection with the use or) (performance of this software.) () (****************************************************************) () (This file processed with symbol generator: 27-MAY-1991 17:10:54.27.) (Code Examples\code_appendix) (A skeleton server written in C\CMUIP_CODE_1) (A skeleton server written in BLISS-32\CMUIP_CODE_2) (A skeleton transport - SKDRV.EXE\CMUIP_CODE_3)

This is an example of the basic layout of an IP transport. It was derived in great haste from the SLIP transport so don't expect it to be error-free. It is provided merely to give the transport designer a feel for transports, and to act as a starting point for transport design. The source code for the skeleton transport may be found included with the CMU-OpenVMS/IP source code, specifically in IPTRANS.SRC. (SKDRV.BLI\CMUIP_CODE_4) %Title 'Skeleton IP transport module' %Sbttl 'Driver overview' %( File: SKDRV.BLI Module: SK_driver Facility: IP transport Author: Bruce R. Miller CMU Network Development dept. Date: May 30, 1990 Abstract: Skel_DRIVER provides upper level protocols with access to (something) using the Skel protocol. This module has three main routines Skel_INIT, Skel_XMIT and Skel_RECEIVE. Skel_INIT is called at initialization time to perform any required set up functions. Skel_XMIT is called at run time to send network packets to the network. It is normally called by the IP_SEND routine buy may be called by any other routine in the network module. Skel_RECEIVE is always initiated by an AST whenever a packet is received. The ASTs are initially set up by the Skel_INIT routine and subsequently set up by Skel_RECEIVE itself. Modification History: 30-May-1990 Initial Version 1.0 )% %Sbttl 'Module definitions' MODULE Skel_DRIVER( IDENT='1.2', LANGUAGE(BLISS32), ADDRESSING_MODE(EXTERNAL=LONG_RELATIVE, NONEXTERNAL=LONG_RELATIVE), LIST(NOREQUIRE,ASSEMBLY,OBJECT,BINARY), OPTIMIZE,OPTLEVEL=3,ZIP) = BEGIN LIBRARY 'SYS$LIBRARY:LIB'; ! VMS system defintions LIBRARY 'CMUIP_SRC:[central]NETXPORT'; ! Bliss Transportability package LIBRARY 'CMUIP_SRC:[central]NETVMS'; ! VMS specifics LIBRARY 'CMUIP_SRC:[central]NETCONFIG'; ! Device interface specs. LIBRARY 'CMUIP_SRC:[central]NETDEVICES'; ! Helpfull macros... LIBRARY 'SKDrv'; FORWARD ROUTINE Skel_Receive : NOVALUE; LITERAL IOS_len = 8, !Byte length of standard QIO IOSB ASTEFN = 4, !Value of event flag for AST routines Qhead_len = 8; !Byte length of standard VMS Q header %SBTTL 'Declare the device information block used to describe entry points.' OWN ! DRV$Device_Info is a list of everything we want the IPACP ! to know about us... Initialized by ROUTINE DRV$TRANSPORT_INIT. DRV$Device_Info : Device_Info_Structure; GLOBAL ! The IPACP_Interface tells us all about the IPACP. It gives us ! entry points, literals and global pointers. See NETDEVICES.REQ ! for a complete explaination of this structure. ! Note: This pointer must be named "IPACP_Interface" IPACP_Interface : REF IPACP_Info_Structure; !**************************************************************************** LITERAL EMPTY_QUEUE = 3 : UNSIGNED(8); ROUTINE Skel_FreeBufs ( Skel_Int : REF Skel_Interface_Structure) : NOVALUE = ! AST routine to deallocate read buffers on error. ! Scheduled by shutdown routine. BEGIN LOCAL BUFF : REF SkelRCV_QB_Structure; ! Flush IP buffers WHILE REMQUE(.Skel_Int[SKI$recv_Qhead],BUFF) NEQ EMPTY_QUEUE DO DRV$Seg_Free( DRV$MAX_PHYSICAL_BUFSIZE,.BUFF); ! Say that this has been done. Skel_Int[SKI$need_2_free] = FALSE; END; !**************************************************************************** ROUTINE Skel_shutdown ( Skel_Int ) : NOVALUE = ! ! Shut the device controller down. Issue shutdown command to controller ! if online, set offline, general cleanup. ! BEGIN MAP Skel_Int : REF Skel_Interface_Structure; LOCAL dev_config : REF Device_Configuration_Entry, IOS : Skel_iosb_structure; ! Disallow ASTs DRV$NOINT; ! Set device offline. dev_config = .Skel_Int [SKI$Dev_config]; dev_config[dc_online] = False; ! Shutdown the IP channel IF .Skel_Int[SKI$IO_chan] NEQ 0 THEN BEGIN $CANCEL(chan=.Skel_Int[SKI$IO_chan]); END; ! Schedule AST to deallocate all buffers IF .Skel_Int[SKI$IO_queued] THEN BEGIN Skel_Int[SKI$IO_queued] = FALSE; Skel_Int[SKI$need_2_free] = TRUE; $DCLAST(astadr = Skel_FreeBufs, astprm = .Skel_Int); END; ! Allow AST's again DRV$OKINT; END; !**************************************************************************** %SBTTL 'Start asynchronous I/O on the line' ROUTINE Start_read ( Skel_Int : REF Skel_Interface_Structure) : NOVALUE = BEGIN LOCAL Buff: REF SkelRCV_QB_structure, RC; Buff = DRV$Seg_get( DRV$MAX_PHYSICAL_BUFSIZE ); INSQUE(.Buff, .Skel_Int[SKI$recv_Qtail]); RC = $QIO(chan=.Skel_Int[SKI$IO_chan], Func = IO$_READVBLK, IOSB = Buff[SkelRCV$vms_code], astadr = Skel_receive, astprm = .Skel_Int, P1 = Buff[SkelRCV$data], P2 = DRV$MAX_PHYSICAL_BUFSIZE-(Qhead_len+IOS_len)); IF .RC NEQ SS$_Normal THEN BEGIN ! If we cannot even start the reads, shut down the line DRV$error_fao('Skel start read request failure, RC=!XL',.RC); Skel_shutdown(.Skel_Int); END END; %Sbttl 'Skel driver init routine' %(****************************************************************************** Function: Initialize the Asynchronous line 1. Assign the device. 2. Get 4 buffers and issue 4 IO$_READVBLK functions with AST's. Inputs: dev_config : pointer to address of the device configuration entry Outputs: Device channel number is stored in config table and online bit is set ******************************************************************************* )% ROUTINE Skel$Init ( dev_config : REF Device_Configuration_Entry, IPACP_Int, max_retry, MPBS) : NOVALUE = BEGIN EXTERNAL ROUTINE LIB$GET_VM : ADDRESSING_MODE(GENERAL); LOCAL RC, Skel_Int : REF Skel_Interface_Structure, Skel_Chan; ! Setup the global IPACP_Interface = .IPACP_Int; ! Hold AST's until done here DRV$NOINT; ! Assign Line IF NOT (rc=$Assign (devnam = Dev_config[dc_devname], chan =Skel_chan)) THEN ! Line assign failed BEGIN DRV$Fatal_FAO('!%T Skel $ASSIGN failure, status=%X!XL!/',0,.rc); RETURN; END; ! Allocate and Initialize the Skel controller block ! Allocate VM !!!HACK!!! When are we going to deallocate this? Ever? IF NOT (LIB$GET_VM(%REF(Skel_Interface_size*4),Skel_Int)) THEN BEGIN ! Couldn't allocate memory for controller block DRV$Fatal_FAO('Skel LIB$GET_VM failure (dev="!AS"), EC = !XL' , dev_config[dc_devname],.rc); RETURN END; ! Zero out the memory block CH$FILL(%CHAR(0),Skel_Interface_size*4,.Skel_Int); ! Fill in the blanks... Skel_Int[SKI$IO_chan] = .Skel_chan; Skel_Int[SKI$max_retry] = .max_retry; ! Maximum # of conseq. retries Skel_Int[SKI$MPBS] = .MPBS; ! Maximum Physical Buffer Size Skel_Int[SKI$Flags] = 0; ! Just making sure... ! Set-up the receive queue Skel_Int[SKI$recv_Qhead] = Skel_Int[SKI$recv_Qhead]; Skel_Int[SKI$recv_Qtail] = Skel_Int[SKI$recv_Qhead]; ! set double-link between Skel_Int and dev_config blocks Skel_Int [ SKI$dev_config ] = .dev_config; dev_config [ dc_dev_interface ] = .Skel_Int; Skel_Int[SKI$IO_chan] = .Skel_chan; ! Supply four receive buffers to device controller INCR I FROM 0 TO (MAX_RCV_BUF-1) DO BEGIN start_read(.Skel_Int); END; ! Indicate that I/O has been started and that device is ready Skel_Int [ SKI$Skel_started ] = true; Skel_Int [ SKI$IO_queued ] = true; Dev_config [ dc_online ] = True; ! Ok to take AST's again DRV$OKINT; END; %Sbttl 'Asynch line driver xmit' %(***************************************************************************** Function: This routine is called by the higher level protocol to transmit a datagram to the asynchronous line. All information about a datagram is found on the Net_send_Q for this device. Each Q entry will be processed and deleted from the Q. Inputs: dev_config - dev_config table entry for this device. Net_send_queue on the dev_config table entry for this device Outputs: None. ******************************************************************************* )% ROUTINE Skel$Xmit ( dev_config : REF Device_Configuration_Entry ) : NOVALUE = BEGIN LABEL X; LOCAL RC, IOS: Skel_IOSB_structure, QB: REF BLOCK[] Field(QB_net_send), xchan, p; BIND Skel_Int = dev_config[dc_dev_interface] : REF Skel_Interface_Structure; ! Check if a request is on the Net_send_Q for this device IF (REMQUE(.Dev_config[dc_send_Qhead],QB)) EQL Empty_Queue THEN RETURN; ! The Q is empty ! Make sure device is online IF NOT .Dev_config[dc_online] THEN BEGIN ! Device is offline DRV$ERROR_FAO('!%T Skel device !XL offline (xmit)!/',0,.Dev_config); END ELSE BEGIN xchan = .Skel_Int[SKI$IO_chan]; RC = $QIOW( CHAN = .xchan, FUNC = IO$_WRITEVBLK IOSB = IOS, P1 = QB[NSQ$Data], P2 = .QB[NSQ$Datasize]); ! Release work buffer DRV$Seg_free(.QB[NSQ$Datasize] * 2 + 2, .wrkbuf); ! Check for $QIO error IF NOT (.RC) THEN BEGIN DRV$error_fao('Skel $QIOW start error (send),RC=!XL',.RC); END else begin ! Check for device driver error IF .IOS[Skel$vms_code] NEQ SS$_Normal THEN BEGIN DRV$error_fao('Skel driver error (send),VMS_code=!XL', .IOS[Skel$vms_code]); END; end; END; ! Delete buffer and release QBlk IF .QB[NSQ$delete] THEN DRV$Seg_free(.QB[NSQ$del_buf_size],.QB[NSQ$del_buf]); DRV$Qblk_free(.QB); END; %SBTTL 'Skel driver recv' %(***************************************************************************** Function: This routine is an AST interrupt routine. It is started when the terminal driver has received a packet and issues an AST to this routine. The packet is passed to the higher protocol for processing. A new buffer is rented, put on the receive Queue and then passed to the device for subsequent packet arrivals. Inputs: Skel_Int : address of serial line interface information block Outputs: Calls IP_Receive to deliver the datagram. ******************************************************************************* )% ROUTINE Skel_receive ( Skel_Int : REF Skel_Interface_Structure ) : NOVALUE = BEGIN LOCAL dev_config : REF Device_Configuration_Entry, Rbuf : REF SkelRCV_QB_structure, RC; dev_config = .Skel_Int [ SKI$dev_config ]; ! Set flag indicating interrupt in progress DRV$AST_in_progress = True; ! If device not online, then give message and punt IF NOT .dev_config[dc_online] THEN BEGIN !~~ DRV$OPR(%ASCID 'Skel receive AST when offline'); DRV$AST_in_progress = False; RETURN; END; ! Get first input packet off the queue REMQUE(.Skel_Int[SKI$recv_Qhead],Rbuf); ! Check read status IF (RC = .Rbuf[SkelRCV$vms_code]) NEQ SS$_Normal THEN BEGIN DRV$Seg_Free(DRV$MAX_PHYSICAL_BUFSIZE,.Rbuf); DRV$error_fao('!%T Skel read error, status=%X!XL !/',0,.RC); start_read(.Skel_Int); DRV$AST_In_Progress = False; RETURN; END; ! Get another buffer and put it on the receive Q for this device Start_read(.Skel_Int); ! Send datagram to IP DRV$IP_Receive(.Rbuf, DRV$MAX_PHYSICAL_BUFSIZE, Rbuf[SkelRCV$data],.p,.dev_config); DRV$AST_in_progress = False; END; ROUTINE Skel$Dump(dev_config, funct, buffer, sizeAdrs) = BEGIN RETURN(true); END; GLOBAL ROUTINE DRV$TRANSPORT_INIT = ! Initialize the transport information/entry vector ! Must be done at run time to avoid .ADDRESS fixups... BEGIN DRV$Device_Info[DI$Init] = Skel$Init; DRV$Device_Info[DI$Xmit] = Skel$XMit; DRV$Device_Info[DI$Dump] = Skel$Dump; DRV$Device_Info[DI$Check] = 0; DRV$Device_Info END; END ELUDOM (SKDRV.REQ\CMUIP_CODE_5) %Title 'Ethernet device driver' %Sbttl 'Driver overview' %( File: SKDRV.REQ Module: SK_driver Facility: IP transport Author: Bruce R. Miller CMU Network Development dept. Date: May 30, 1990 )% LIBRARY 'CMUIP_SRC:[central]NETXPORT'; %Sbttl 'Skeleton Driver Structures' ! This structure defines the interface information that is specific ! to the Skeleton driver. $FIELD Skel_Interface_Fields = SET SKI$dev_config = [$Address], SKI$IO_Chan = [$bytes(4)], SKI$recv_Qhead = [$Address], SKI$recv_Qtail = [$Address], SKI$restart_time = [$Bytes(4)], SKI$restart_count = [$bytes(2)], SKI$retry_count = [$bytes(2)], SKI$max_retry = [$bytes(2)], SKI$MPBS = [$bytes(2)], SKI$Flags = [$Bytes(2)], $OVERLAY(SKI$Flags) SKI$need_2_free = [$bit], ! SK_shutdown buf free pending SKI$IO_queued = [$bit], ! SK I/O has been started SKI$Skel_started = [$bit] ! Skel started at least once TES; LITERAL Skel_Interface_size = $Field_set_size; MACRO Skel_Interface_Structure = BLOCK[Skel_Interface_size] FIELD(Skel_Interface_Fields)%; ! This structure defines the VMS I/O status block for the driver $FIELD Skel_iosb = SET Skel$vms_code = [$short_integer], Skel$trm_offset = [$short_integer], Skel$trm = [$byte], Skel$iosb_unused1 = [$byte], Skel$trm_size = [$short_integer] TES; LITERAL Skel_iosb_len = $Field_set_size; MACRO Skel_iosb_structure = BLOCK[Skel_iosb_len] FIELD(Skel_iosb)%; ! Receive buffer Q structure $FIELD QB_Skel_rcv = SET SkelRCV$next = [$address], ! Queue entry SkelRCV$last = [$address], SkelRCV$vms_code = [$short_integer], ! I/O Status Block SkelRCV$trm_offset = [$short_integer], SkelRCV$trm = [$byte], SkelRCV$unused = [$byte], SkelRCV$trm_size = [$short_integer], SkelRCV$data = [$byte] ! Start of data TES; LITERAL SkelRCV_QB_len = $Field_set_size; MACRO SkelRCV_QB_structure = BLOCK[SkelRCV_QB_len] FIELD(QB_Skel_rcv)%; LITERAL MAX_RCV_BUF = 4; (SKDRV_TRANS.MAR\CMUIP_CODE_6)

Note that the transfer vector module is identical for all of the transports included with CMU-OpenVMS/IP.

.Title SKIPDrv_Transfer - IPACP skeleton support module trns. vec. ; ; SKIPDrv_Trans.Mar ; ; Description: ; ; The SKIPDrv.exe image is a loadable run-time image which ; provides the IPACP with access to serial lines. ; ; Transfer Vector for the driver support module: The vector ; provides the hooks for a standardized set of routines which ; allow the IPACP to communicate to various communication ; devices in a device-independant manner. The transfer ; vector points to a Device_Info_Structure as defined in ; Device ; ; The transfer vector is needed to build it as a shareable image. ; Macro is the only language that can build the tranfer vector ; in the way that is expected by the shareable libraries. ; Macro is the only language that can generate the necessary ; object language text information relocation record for manipulating ; the symbol name address. ; ; Written By: Bruce R. Miller 02-Feb-1990 CMU NetDev ; ; Modifications: ; ;--------------------------------------------------------------------------- ; ; Shareable image transfer vector ; .PSECT $TRANSFER$ PIC,USR,CON,REL,LCL,SHR,NOEXE,RD,NOWRT,QUAD .ALIGN QUAD .TRANSFER DRV$TRANSPORT_INIT .MASK DRV$TRANSPORT_INIT JMP G^DRV$TRANSPORT_INIT+2 .END (DESCRIP.MMS\CMUIP_CODE_7) !++ ! DESCRIP.MMS ! ! Copyright (C) 1988 Carnegie Mellon University ! ! Description: ! ! File for building the IP transports ! ! Written By: ! ! Bruce R. Miller 05-Feb-1990 CMU Network Development ! ! Modifications: ! !-- ! Define switches and executable name BFLAGS = $(DEBUG) /TERMINAL=STATISTICS /OBJECT=$(MMS$TARGET_NAME) /list MFLAGS = /LIST All : SLDrv.EXE ! Rules for building the Skeleton IP transport module SKDrv.EXE : SKDrv.OBJ SKDrv_Trans.OBJ LINK /nodebug/notrace/MAP/sym /SHARE=SKDrv.EXE - SKDrv_Trans, SKDrv, - CMUIP_SRC:[CENTRAL]NETDEVICES.OBJ/sel,- CMUIP_SRC:[CENTRAL]NETMACLIB.OBJ/sel,- SYS$SYSTEM:SYS.STB/SEL SKDrv.OBJ : SKDrv.BLI SKDrv.L32 SKDrv_Trans.OBJ : SKDrv_Trans.MAR SKDrv.L32 : SKDrv.req