home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
msvp98b1.lzh
/
MSNPDI.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-05-14
|
57KB
|
1,687 lines
NAME MSNPDI
; File MSNPDI.ASM
; Packet Driver and ODI interface
;
; Copyright (C) 1985, 1992, Trustees of Columbia University in the
; City of New York. Permission is granted to any individual or institution
; to use this software as long as it is not sold for profit. This copyright
; notice must be retained. This software may not be included in commercial
; products without written permission of Columbia University.
;
; Permission is granted to use this file as a guide in developing commercial
; products. If substantial portions are used crediting the source is
; recommended.
;
; Written by Joe R. Doupnik, Utah State University, Logan Utah 84322
; jrd@cc.usu.edu, jrd@usu.Bitnet.
;
; Packet Driver reference:
; "PC/TCP Version 1.09 Packet Driver Specification", FTP Software, Inc.,
; September-14-1989.
;
; ODI references:
; "Open Data-Link Interface Developer's Guide for DOS Network Layer Protocol
; Stacks", Novell Inc, document number 100-001218-001 (1992, but no printed
; date).
; "Open Data-Link Interface Developer's Guide for NetWare v3.1x Server
; Driver Protocol Stacks", Novell Inc, document number 100-001196-00, v1.0,
; 19 Sept 1991.
; "Open Data-Link Interface LAN Driver Developer's Guide for DOS", Novell Inc,
; document number 107-000010-001, Revision i, 13 Nov 1990.
;
; C language interface presumes the Small memory model and Microsoft C.
; Assembler is MS MASM v6.
;
; These procedures interface between ODI or a Packet Driver and the main
; protocol stack to both send and receive packets. The upper levels provide
; and receive packets framed as Ethernet_II (Blue Book/DIX), and receive
; ARP information particular to the physical frames involved. Conversion of
; this internal form to the actual framing method on the wire is done by ODI.
; The receive buffer is external to this routine. Received packets are linked
; into the buffer on an order of arrival basis (and sized to fit). High level
; reception has to poll the receive buffer queue. Transmitted packets are
; operated with a single external buffer, and sending blocks until the lower
; level driver material is ready for us. Initialization, status, attachment,
; and disengagment procedures are here.
; External int kpdint is used by pdinit() to select between Packet Driver
; and ODI interfaces. Call pdinit() to initialize the system, then call
; pdaccess() to register each desired frame TYPE, call pdclose() to release
; each frame TYPE.
; Packet senders call pkt_send() directly.
; Packet receivers examine the external packet buffer for packets.
; ARP information is returned in external ints arp_hardware (hardware ARP
; type code) and MAC_len (length of MAC address, in bytes). See table below
; for the possible pairs.
; Packet Driver usage is limited to Ethernet_II/DIX and SLIP. ODI usage is
; at least Ethernets, Token Ring, Arcnet, and others untested here.
;
; Edit history
; Last edit
; 26 August 1992
getintv equ 35h ; DOS get interrupt vector to es:bx
dos equ 21h
lf equ 0ah
fopen equ 3dh ; DOS file operations
fclose equ 3eh
fread equ 3fh
pdgetinfo equ 1 ; Packet Driver functions
pd_access equ 2
pd_release equ 3
pd_send equ 4
pd_get_address equ 6
eaddr_len equ 6 ; length of an Ethernet address
ETH_MSS equ 534 ; Correlate with msntcp.h
IF_EII equ 1 ; Ethernet II interface type
IF_SLIP equ 6 ; SLIP interface type
link struc ; receiver buffer link structure
flag db 0 ; buffer use flag
bufnum db 0 ; buffer write sequence number
count dw 0 ; count of bytes to follow
link ends
linksize equ 4 ; bytes in link structure
; Novell ODI material, based on LSL v1.2.
; Most of these header structures were taken from Novell file ODI.INC,
; and parts have been tailored for Kermit. Information on the "new" item in
; the lookahead structure is compliments of Novell, private correspondence.
; Event Control Block Structure
ECBstruct struc
nextlink dd 0 ; leave intact
prevlink dd 0 ; leave intact
status dw 0 ; general status
esr dd 0 ; addr of event service routine
stackid dw 0 ; protocol stack ident
protid db 6 dup (0) ; sending only
boardnum dw 0 ; sending, MILD board number
immaddr db 6 dup (0) ; MAC destination address
driverws db 4 dup (0) ; their work space
protocolws dw 4 dup (0) ; our work space
datalen dw 0 ; total length of sent buffer
fragcount dw 1 ; number of buffer pieces
frag1addr dw 0,0 ; seg:offset of first buffer
frag1len dw 0 ; length of first buf frag
ECBstruct ends ; 26 words
; Look Ahead Structure
LookAheadStruc struc
LMediaHeaderPtr dd 0 ; pointer to MAC header
LookAheadPtr dd 0 ; pointer to pkt Data
LookAheadLen dw 0 ; length of pkt Data field
LProtID db 6 dup (0) ; protocol ident
LBoardNum dw -1 ; logical board of rcv'd pkt
DataLookAheadDataSize dd 0 ; new field, exists if bit 7 of the
; Driver's Configuration Table Mode Flags is set.
LookAheadStruc ends
; Rx Destination Address Type (First byte of ECB.DriverWS)
ECB_DIRECT equ 00h ;Physical destination address
ECB_MULTICAST equ 01h ;Multicast destination address
ECB_BROADCAST equ 03h ;Broadcast destination address
; System Error Code Definitions
LSLERR_OUT_OF_RESOURCES equ 8001h
LSLERR_BAD_PARAMETER equ 8002h
LSLERR_NO_MORE_ITEMS equ 8003h
LSLERR_ITEM_NOT_PRESENT equ 8004h
LSLERR_FAIL equ 8005h
LSLERR_RX_OVERFLOW equ 8006h
LSLERR_CANCELLED equ 8007h
LSLERR_BAD_COMMAND equ 8008h
LSLERR_DUPLICATE_ENTRY equ 8009h
LSLERR_NO_SUCH_HANDLER equ 800ah
LSLERR_NO_SUCH_DRIVER equ 800bh
; LSL MLID Services Function Codes
MLIDSUP_GET_ECB equ 0
MLIDSUP_RETURN_ECB equ 1
MLIDSUP_DEFRAG_ECB equ 2
MLIDSUP_SCHEDULE_AES_EVENT equ 3
MLIDSUP_CANCEL_AES_EVENT equ 4
MLIDSUP_GET_INTERVAL_MARKER equ 5
MLIDSUP_DEREGISTER_MLID equ 6
MLIDSUP_HOLD_RECV_EVENT equ 7
MLIDSUP_START_CRITICAL_SECTION equ 8
MLIDSUP_END_CRITICAL_SECTION equ 9
MLIDSUP_CRITICAL_SECTION_STATUS equ 10
MLIDSUP_SERVICE_EVENTS equ 11
MLIDSUP_SEND_COMPLETE equ 14
MLIDSUP_ADD_PID equ 15
MLIDSUP_GET_STACK_ECB equ 16
; LSL Protocol Stack Services Function Codes
PROTSUP_GET_ECB equ 0
PROTSUP_RETURN_ECB equ 1
PROTSUP_SCHEDULE_AES_EVENT equ 3
PROTSUP_CANCEL_EVENT equ 4
PROTSUP_GET_INTERVAL_MARK equ 5
PROTSUP_REGISTER_STACK equ 6
PROTSUP_DEREGISTER_STACK equ 7
PROTSUP_REGISTER_DEFAULT_STACK equ 8
PROTSUP_DEREGISTER_DEFAULT_STACK equ 9
PROTSUP_REGISTER_PRESCAN_STACK equ 10
PROTSUP_DEREGISTER_PRESCAN_STACK equ 11
PROTSUP_SEND_PACKET equ 12
PROTSUP_GET_PROTNUM_FROM_NAME equ 16
PROTSUP_GET_PID_PROTNUM_MLIDNUM equ 17
PROTSUP_GET_MLID_CTL_ENTRY equ 18
PROTSUP_GET_PROTO_CTL_ENTRY equ 19
PROTSUP_GET_LSL_STATS equ 20
PROTSUP_BIND_STACK_TO_MLID equ 21
PROTSUP_UNBIND_STACK_FROM_MLID equ 22
PROTSUP_ADD_PID equ 23
PROTSUP_RELINQUISH_CONTROL equ 24
PROTSUP_GET_LSL_CONFIG equ 25
; LSL General Services Function Codes
GENSERV_ALLOC_MEMORY equ 0
GENSERV_FREE_MEMORY equ 1
GENSERV_REALLOC_MEMORY equ 2
GENSERV_MEMORY_STATISTICS equ 3
GENSERV_ADD_MEMORY_TO_POOL equ 4
GENSERV_ADD_GENERAL_SERVICE equ 5
GENSERV_REMOVE_GENERAL_SERVICE equ 6
GENSERV_GET_NETCFG_PATH equ 7
; LSL Configuration Table
LSLConfigurationStructure struc
LConfigTableMajorVer db 1
LConfigTableMinorVer db 0
LNumLSLRxBuffers dd 0
LRxBufferSize dd 0 ;Buffer size NOT including ECB struc size
LMajorVersion db 0
LMinorVersion db 0
LConfigTableReserved db 16 dup (0)
LSLConfigurationStructure ends
; MLID Control Commands
GET_MLID_CONFIGURATION equ 0
GET_MLID_STATISTICS equ 1
ADD_MULTICAST_ADDRESS equ 2
DELETE_MULTICAST_ADDRESS equ 3
MLID_SHUTDOWN equ 5
MLID_RESET equ 6
CREATE_CONNECTION equ 7
REMOVE_CONNECTION equ 8
SET_LOOK_AHEAD_SIZE equ 9
DRIVER_POLL equ 12
; MLID Configuration Table Structure
MLIDConfigurationStructure struc
MSignature db 'HardwareDriverMLID',8 dup (' ')
MConfigTableMajorVer db 1
MConfigTableMinorVer db 11
MNodeAddress db 6 dup (?)
MModeFlags dw ?
MBoardNumber dw ?
MBoardInstance dw ?
MMaxPacketSize dw ?
MBestDataSize dw ?
MWorstDataSize dw ?
MCardLongName dd ?
MCardShortName dd ? ; visible board name
MFrameString dd ?
MReserved0 dw 0 ;Must be set to 0
MFrameID dw ?
MTransportTime dw ?
MRouteHandler dd ? ;Only for Token-Ring
MLookAheadSize dw ?
MLineSpeed dw ? ;In Mbps or Kbps
MReserved1 db 8 dup (0) ;Must be set to 0
MMLIDMajorVer db ?
MMLIDMinorVer db ?
MFlags dw ?
MSendRetries dw ?
MLink dd ?
MSharingFlags dw ?
MSlot dw ?
MIOAddress1 dw ?
MIORange1 dw ?
MIOAddress2 dw ?
MIORange2 dw ?
MMemoryAddress1 dd ?
MMemorySize1 dw ?
MMemoryAddress2 dd ?
MMemorySize2 dw ?
MIntLine1 db ?
MIntLine2 db ?
MDMALine1 db ?
MDMALine2 db ?
MLIDConfigurationStructure ends
; MLID Config Table 'MFlags' bit definitions.
EISA equ 01h ;EISA Bus
ISA equ 02h ;PC/AT Bus
MCA equ 04h ;PS/2 MCA Bus
Len_Info equ 40h ; pkt data length in lookahead info
; MLID Config Table 'MModeFlags' bit definitions (no promiscuous mode).
MRealDriverBit equ 0001h
MUsesDMABit equ 0002h
MGuaranteedDeliveryBit equ 0004h ;100% reliable on transmits
MMulticastBit equ 0008h
MNeedsPollingBit equ 0020h
MRawSendBit equ 0040h
; Registered Stack structure, used during registration only
StackInfoStruc struc
StackNamePtr dd ip_string ; ptr to short name
StackReceiveHandler dd ip_rcvr ; rcv routine
StackControlHandler dd pcontrol ; control routine
StackInfoStruc ends
; Protocol Control Commands
GET_STACK_CONFIGURATION equ 0
GET_STACK_STATISTICS equ 1
BIND_TO_MLID equ 2
UNBIND_FROM_MLID equ 3
INFORM_MLID_DEREGISTERED equ 4
; Protocol Configuration Table
ProtocolConfigStructure struc
PConfigTableMajorVer db 1
PConfigTableMinorVer db 0
PProtocolLongName dd plname
PProtocolShortName dd psname ; "KERMIT"
PProtocolMajorVer db 3 ; MSK v3.12
PProtocolMinorVer db 12
PConfigTableReserved db 16 dup (0)
ProtocolConfigStructure ends
; Protocol Statistics Table
ProtocolStatStructure struc
PStatTableMajorVer db 1
PStatTableMinorVer db 0
PNumGenericCounters dw 3 ; just those below
PValidCounterMask dd 111b ; bitfield, 3 valids
PTotalTxPackets dw 2 dup (0)
PTotalRxPackets dw 2 dup (0)
PIgnoredRxPackets dw 2 dup (0)
PNumCustomCounters dw 0 ; none
ProtocolStatStructure ends
pinfo struc ; per protocol local data for ecb
pstack dw 0 ; StackID
pprotid db 6 dup (0) ; ProtID
pboard dw 0 ; boardnum
pinfo ends
_TEXT SEGMENT WORD PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT WORD PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
_BSS ENDS
DGROUP GROUP CONST, _BSS, _DATA
ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP, ES:NOTHING
_DATA SEGMENT
extrn _pktbuf_wrote:word, _pktwnum:byte, _kpdint:word
extrn _eth_addr:byte, _arp_hardware:word, _MAC_len:word
pdsignature db 'PKT DRVR' ; signature of a Packet Driver
if_type dw 0 ; interface type
if_class db 0 ; interface class
if_num db 0 ; interface number
if_func db 0 ; interface functionality
if_version dw 0 ; interface version
iptype db 8,0 ; IP packet type
iptypelen equ $-iptype ; length of type field for iptype
pktbufoff dw 0 ; offset of packet buffer
SLIPmac dw 0,0,2 ; fake SLIP dest Ethernet address
; ODI material
useodi db 0 ; non-zero if using ODI for transport
lslsig db 'LINKSUP$' ; LSL presence signature
lslsiglen equ $-lslsig
lslinit dd 0 ; LSL init entry point
; LSL entry structure, do not separate
lslsupport dd 0 ; LSL protocol support API entry point
lslservice dd 0 ; LSL general services API entry point
mlidcont dd 0 ; MLID Control entry point
ecbr_qty equ 4 ; number of receive ECB's to allocate
maketab MACRO ; macro to make receiver ecbs
cnt = 0
rept ecbr_qty - 1
ecbstruct <,,,odircmp>
cnt = cnt + 1
endm
ENDM
ecbr ecbstruct <,,,odircmp> ; first receiver ECB
maketab ; make table of the other ecbr's
ecbx ecbstruct <,,,odixcmp> ; one ECB for transmission
ecbr_busy db ecbr_qty dup (0) ; our ecbr locks
ecbx_busy db 0 ; non-zero if ECBx owned by ODI
ecbr_num dw 0 ; temp to hold index of ecbr/ecbr_busy
rcvtype dw 0 ; temp, holds protocol TYPE for rcv
pconfig ProtocolConfigStructure <> ; as the name says
pstats ProtocolStatStructure <> ; protocol statistics
registerstk StackInfoStruc <> ; bound stack setup structure
plname db 13,'MS-DOS Kermit',0 ; cnt, protocol stack long name, null
protword db 8,'PROTOCOL',0 ; three NET.CFG keywords for Kermit
psname db 6,'KERMIT',0 ; cnt, protocol stack short name, null
bindword db 4,'BIND',0 ; third keyword
ip_type equ 0008h ; Protocol TYPEs, big endian/net order
arp_type equ 0608h
rarp_type equ 3580h
ip_string db 2,'IP',0 ; strings to match in NET.CFG file
arp_string db 3,'ARP',0 ; to select pkt TYPEs
rarp_string db 4,'RARP',0 ; RARP is optional
ip_stackid pinfo <> ; StackID, Protid, boardnum
arp_stackid pinfo <> ; for each protocol
rarp_stackid pinfo <>
bcast db 6 dup (0ffh) ; Broadcast address, for reception
readnetcfg db 0 ; non-zero if have read NET.CFG
useboard dw -1 ; board to be used, -1 = not inited
bdname db 0,16 dup (0) ; length, 15 text, null, bound board
tells_len db 0 ; if MLID tells pkt len for lookahead
tempb db 0
temp dw 0
; parallel lists of NetWare ODI frame types, address lengths, ARP idents
frame_type db 2,3,4,5,6,7, 9,10,11,14,15,16,23
num_frames equ ($ - frame_type)
frame_adlen db 6,6,6,6,6,6, 1,6, 6, 1, 6, 6, 6
hardware_type db 1,6,6,6,6,12,4,6, 6, 7, 6, 6, 6
_DATA ENDS
; ODI Frame types, frame strings, length (bytes) of a MAC level address:
; type frame string MAC_len hardware comments
; 0 VIRTUAL_LAN 0 0 no MAC header used
; 1 LOCALTALK 6 11 Apple (Ether/Tokentalk is 802)
; 2 ETHERNET_II 6 1 Blue Book
; 3 ETHERNET_802.2 6 6 802.3 with 802.2 wrap
; 4 TOKEN-RING 6 4 802.5 with 802.2 wrap
; 5 ETHERNET_802.3 6 6 802.3 "raw", old Novell
; 6 802.4 6 6 Token Bus
; 7 NOVELL_PCN2 6 12 Novell's IBM PCnet2
; 8 GNET 6 4 Gateway, assumed TRN-like
; 9 PRONET-10 1 4 Proteon TRN-like
; 10 ETHERNET_SNAP 6 1 802.3 with 802.2+SNAP
; 11 TOKEN-RING_SNAP 6 6 802.5 with 802.2+SNAP
; 12 LANPAC_II 6 ? Racore
; 13 ISDN 6 ? telco
; 14 NOVELL_RX-NET 1 7 Arcnet-like
; 15 IBM_PCN2_802.2 6 12 IBM PCnet2 with 802.2
; 16 IBM_PCN2_SNAP 6 12 IBM PCnet2,802.2+SNAP
; 17 OMNINET/4 ? ? Corvus
; 18 3270_COAXA ? ? Harris
; 19 IP ? ? tunneled
; 20 FDDI_802.2 6 ?
; 21 IVDLAN_802.9 6 ? Commtex
; 22 DATACO_OSI ? ? Dataco
; 23 FDDI_SNAP 6 6 802.7, with 802.2+SNAP
;
; ARP hardware field, from RFC 1060
; Type Description
; ---- -----------
; 1 Ethernet (10Mb)
; 2 Experimental Ethernet (3Mb)
; 3 Amateur Radio AX.25
; 4 Proteon ProNET Token Ring
; 5 Chaos
; 6 IEEE 802 Networks
; 7 ARCNET
; 8 Hyperchannel
; 9 Lanstar
; 10 Autonet Short Address
; 11 LocalTalk
; 12 LocalNet (IBM PCNet or SYTEK LocalNET)
_TEXT segment
pktdrvr proc near ; Packet Driver interrupt invokation
PKTDRVI:int 60h ; Interrupt number, modified by startup code
ret
pktdrvr endp
; pdinit(ðeraddress)
; Initialize Packet Driver or ODI for use by this program. Stores Ethernet
; address (or MAC address). _kpdint is 0 to scan first for a Packet Driver
; interrupt and fall back to search for ODI, or is a number 60h..7fh to
; target only that PD interrupt, or is 'DO' to target only ODI. If a PD is
; used then _kpdint is modified to be the found interrupt value.
; A 6 byte MAC level address is returned for convenience, even for SLIP.
; Returns 1 for success, 0 for failure.
public _pdinit
_pdinit proc near
push bp
mov bp,sp
push es
push si
push di
push ds
mov ax,dgroup
mov ds,ax
mov ax,[bp+4+0] ; get offset of pktbuf
mov pktbufoff,ax ; save locally
mov cx,60h ; interrupt range
cmp _kpdint,0 ; user value given?
je pdinit1 ; e = no
mov cx,_kpdint ; use it
cmp cx,'DO' ; special indicator to use ODI?
je pdinit2 ; e = yes, skip Packet Driver
pdinit1:mov ah,getintv ; get interrupt vector to es:bx
mov al,cl ; vector number
int dos
mov si,offset dgroup:pdsignature ; look for signature
push cx
mov cx,length pdsignature ; length of string
mov di,bx
add di,3 ; sig starts 3 bytes from entry point
cld
repe cmpsb ; compare bytes
pop cx
je pdinit3 ; e = found a match
cmp _kpdint,0 ; user value given?
jne pdinit2 ; ne = yes, so fail
inc cx
cmp cx,80h ; at end of range?
jna pdinit1 ; na = not yet, try another int
; no Packet Driver found
pdinit2:call odichk ; see if ODI is available
jc pdinit5 ; c = no
mov useodi,1 ; say using ODI
mov _kpdint,'DO' ; signal ODI via PD interrupt variable
mov if_class,1 ; say Ethernet_II for internal work
mov di,[bp+4+2] ; get offset of user's buffer
mov cx,eaddr_len ; length of address provided
mov si,offset DGROUP:SLIPmac ; get fake Ethernet address
cld ; in case code wants it early
push ds
pop es
push di
rep movsb ; copy to user buffer
mov cx,ecbr_qty
mov di,offset DGROUP:ecbr_busy ; clear receive ecb busy flags
xor al,al
rep stosb
pop di
clc
pdinit5:jmp pdret ; exit (carry set is failure)
; Packet Driver details
pdinit3:mov byte ptr PKTDRVI+1,cl ; force in new PD interrupt, code mod
mov _kpdint,cx ; remember interrupt number
; find Ethernet address
mov ah,pdgetinfo ; get Packet Driver information
mov al,0ffh
xor bx,bx ; optional handle
push ds ; this call changes ds and si
push si
call pktdrvr ; call the Packet Driver
pop si
pop ds
jc pdret ; c = failure
pdinit6:mov if_type,dx ; save details for access calls
mov if_class,ch
mov if_num,cl
mov if_func,al
mov if_version,bx
mov ah,pd_access ; access packets
mov al,ch ; Ethernet class
mov bx,dx ; type
mov dl,cl ; interface number
mov cx,iptypelen ; type length for iptype
mov si,offset dgroup:iptype ; address of TYPE
mov di,cs
mov es,di
mov di,offset pdrcvr ; ES:DI is our Packet Driver receiver
call pktdrvr
jc pdret ; c = failure
mov bx,ax ; put returned handle in BX
mov ax,DGROUP ; our data segment
mov es,ax ; segment of Ethernet address buffer
mov di,[bp+4+2] ; get offset of user's buffer
mov cx,eaddr_len ; length of address wanted
cmp if_class,IF_SLIP ; interface class of SLIP?
jne pdinit4 ; ne = no
mov si,offset DGROUP:SLIPmac ; get fake Ethernet address
cld
push di ; save in case PD actually uses it
rep movsb ; copy to user buffer
pop di
pdinit4:mov ah,pd_get_address ; get the Ethernet address
push bx ; save handle
call pktdrvr ; get Ethernet address to es:di buf
pop bx
pushf ; save carry flag
mov ah,pd_release ; release this Type, bx has handle
call pktdrvr
popf ; recover carry flag
pdret: mov ax,1 ; return C status, 1 for success
jnc pdret1
xor ax,ax ; 0 for failure
pdret1: pop ds ; success
pop di
pop si
pop es
mov sp,bp ; restore stack
pop bp ; recover bp reg
ret
_pdinit endp
; int pdinfo(& int version, & int class, & int pdtype, & int number,
; & int functionality)
; Get Packet Driver pedigree
public _pdinfo
_pdinfo proc near
push bp
mov bp,sp
push di
push cx
push bx
push ax ; save al for later use
mov di,[bp+4+0]
mov bx,if_version
mov [di],bx ; return version
mov al,if_class ; class
xor ah,ah
mov di,[bp+4+2]
mov [di],ax ; return as an int
mov di,[bp+4+4]
mov dx,if_type
mov [di],dx ; type
mov di,[bp+4+6]
xor ch,ch
mov cl,if_num
mov [di],cx ; interface number, as an int
pop ax ; recover al
mov di,[bp+4+8]
xor ah,ah
mov al,if_func
mov [di],ax ; functionality, as an int
mov ax,1 ; C style exit status, 1 = success
pop bx
pop cx
pop di
pop bp
ret
_pdinfo endp
; int pdclose(int handle)
; Close a Packet Driver or ODI handle.
; Returns (in AX) 1 if successful, else 0.
public _pdclose
_pdclose proc near
push bp
mov bp,sp
push bx
mov bx,[bp+4+0] ; handle
cmp useodi,0 ; using ODI?
je pdclos2 ; e = no
mov ax,bx ; get handle
call odiunbind ; unbind from LSL and MLID
jmp short pdclos3
pdclos2:mov ah,pd_release ; release_type
call pktdrvr
pdclos3:mov ax,1 ; assume success
jnc pdclos1 ; nc = success
xor ax,ax ; 0 for failure
pdclos1:pop bx
pop bp
ret
_pdclose endp
; int pdaccess(char *type, typelen, int *handle)
; Register access for packet TYPE with the Packet Driver or ODI
; Provides a handle for the TYPE.
; Returns 1 for success, 0 for failure.
public _pdaccess
_pdaccess proc near
push bp
mov bp,sp
push es
push si
push di
push ds
push es
mov ax,dgroup ; set up data segment addressibility
mov ds,ax
mov al,if_class ; interface class (frame)
mov bx,if_type ; interface type (vendor)
mov dl,if_num ; interface number (board number)
xor dh,dh
mov si,[bp+4+0] ; get offset of packet TYPE buffer
mov cx,[bp+4+2] ; typelen (length of buf contents)
cmp useodi,0 ; using ODI?
je pdacc8 ; e = no
mov ax,[si] ; provide TYPE
call odibind ; Bind to a virtual board
jc pdacc1 ; c = fail, error code in AX
jmp short pdacc9 ; store handle returned in AX
pdacc8: cmp if_class,IF_SLIP ; SLIP?
jne pdacc3 ; ne = no
xor cx,cx ; TYPE len = 0 means accept all types
pdacc3: mov di,cs ; ES:DI is our Packet Driver receiver
mov es,di
mov di,offset pdrcvr ; local receiver
mov ah,pd_access ; set access
call pktdrvr
jc pdacc1 ; c = failure
pdacc9: mov si,[bp+4+4] ; offset of handle
mov [si],ax ; return the handle
pdacc1: mov ax,1 ; C level status, 1 = success
jnc pdacc2 ; nc = success
xor ax,ax ; 0 = failure
pdacc2: pop es
pop ds
pop di
pop si
pop es
pop bp
ret
_pdaccess endp
; int pkt_send(char *buffer, int length)
; returns 1 on success, 0 on failure
; Send a packet.
public _pkt_send
_pkt_send proc near
push bp
mov bp,sp
push es
push ds
push si
push di ; don't trust lower levels on regs
push cx
mov ax,dgroup ; segment of outgoing buffer
mov ds,ax ; will be DS:SI for Packet Driver
mov si,[bp+4+0] ; buffer's offset (seg is dgroup)
mov cx,[bp+4+2] ; buffer's length
cmp useodi,0 ; using ODI?
je pktsen5 ; e =no, use PD
pktsen3:cmp ecbx_busy,0 ; is ODI transmit done yet?
je pktsen4 ; e = yes, else we can't touch it yet
mov bx,PROTSUP_RELINQUISH_CONTROL
call lslsupport ; give time to LSL
jmp short pktsen3 ; wait for done
pktsen4:call odixmt ; do LSL transmit with DS:SI and CX
sti ; interrupts back on (xmt turns off)
jmp short pktsen1 ; done (AX non-zero if error)
; Note that checking for transmission errors is not readily done with async
; sending, so we cross our fingers and hope for the best.
;
; Packet Driver sending
pktsen5:mov ah,pd_send ; send packet (buffer = ds:si)
call pktdrvr ; invoke Packet Driver
; common exit
pktsen1:mov ax,1 ; return C level success (1)
jnc pktsen2 ; nc = success
xor ax,ax ; else C level failure (0)
pktsen2:pop cx
pop di
pop si
pop ds
pop es
pop bp
ret
_pkt_send endp
; Our Packet Driver receiver, far called only by the Packet Driver and our
; local ODI code (odircvr and odircmp).
; Packet buffer linked list -
; each link is db flag ; 1 = free, 2 = in use, 4 = allocated
; ; but not in use yet, 0 = end of buf
; ; 8 = read but not freed.
; db _pktwnum ; sequential number of pkt written
; dw count ; length of data field
; db count dup (?) ; the allocated data field
; The head of the chain has a link like all others.
; The end of the chain has a link with flag == 0 and count = -BUFISZE
; to point to the beginning of the buffer (circular).
; Packet buffer garbage collection is done after the PD transfers a
; buffer to the application, and does so by relinking adjacent free
; blocks.
; _pktbuf_wrote is used to remember the link where the last write occurred
; and should be initialized to the tail link to point the next write to
; the beginning of the buffer.
; The Packet Driver and our ODI routines call this first with AX = 0 to
; obtain a buffer pointer in ES:DI from us (0:0 if we refuse the pkt) with
; CX = packet size, and again later with AX = 1 to post completion.
pdrcvr proc far ; dummy Packet Driver receiver
or ax,ax ; kind of upcall from PD
jz pdrcvr1 ; z = first, get-a-buffer
; Second upcall, packet has xfered, DS:SI set by caller to buffer
pushf ; save interrupt status
cli ; interrupts off
push ds
push si
push bx
push ax ; assume DS:SI is one of our buffers
mov ax,DGROUP
mov ds,ax ; set ds to our data segment
or si,si ; is it legal (from first upcall)?
jz pdrcvr11 ; z = no, ignore this call
sub si,linksize ; backup to link info
cmp byte ptr [si].flag,4 ; is this buffer allocated (4)?
jne pdrcvr8 ; ne = no, do cleanups and quit
mov byte ptr [si].flag,2 ; flag = 2 for buffer is now ready
mov si,pktbufoff ; start of packet buffer
; join contiguous free links
pdrcvr8:mov al,[si].flag ; flags byte
cmp al,1 ; is link free?
jne pdrcvr10 ; ne = no, look for a free link
pdrcvr9:mov bx,[si].count ; count (length) of this link
mov al,[bx+si+linksize].flag; flag of following link
cmp al,1 ; is next link free?
jne pdrcvr10 ; ne = no, look for free link
mov ax,[bx+si+linksize].count ; count taken from next link
add ax,linksize ; plus the next link's info field
add [si].count,ax ; add it to this count (merge links)
jmp short pdrcvr9 ; re-examine this new longer link
pdrcvr10:or al,al ; end of list?
jz pdrcvr11 ; z = yes
add si,[si].count ; look at next link (add count)
add si,linksize ; and link info
jmp short pdrcvr8 ; keep looking
pdrcvr11:pop ax
pop bx
pop si
pop ds
popf
ret ; yes, quit
pdrcvr1:pushf
cli
push ds ; First upcall, provide buffer ptr
push dx ; return buffer in ES:SI
push cx
mov di,DGROUP ; get local addressibility
mov ds,di
mov es,di ; packet buffer is in same group
mov di,_pktbuf_wrote ; where last write occurred
or di,di ; NULL?
jz pdrcvr4 ; z = yes, write nothing
mov dl,100 ; retry counter, breaks endless loops
cmp [di].flag,1 ; is this link free?
je pdrcvr5 ; e = yes, use it
pdrcvr2:add di,[di].count ; point at next link (add count and
add di,linksize ; link overhead)
dec dl ; loop breaker count down
or dl,dl
jz pdrcvr4 ; z = in an endless loop, exit
cmp [di].flag,1 ; is this link free (1)?
je pdrcvr5 ; e = yes, setup storage
cmp di,_pktbuf_wrote ; have we come full circle?
jne pdrcvr2 ; ne = no, keep looking
pdrcvr4:pop cx
pop dx
pop ds ; failure or buffer not available (0)
xor ax,ax ; return what we received in ax
xor di,di ; return ES:DI as null to reject
mov es,di
popf
ret
; this link is free
pdrcvr5:cmp cx,ETH_MSS+40+12 +20 ; LARGEST PACKET WE ACCEPT
ja pdrcvr4 ; a = too large, decline it
add cx,2 ; defense for 8/16 bit xfr mistakes
mov ax,[di].count ; length of available data space
cmp ax,cx ; cx is incoming size, enough space?
jl pdrcvr2 ; l = no, go to next link
mov [di].flag,4 ; mark link flag as being alloc'd (4)
mov dh,_pktwnum ; write pkt sequencer number
mov [di].bufnum,dh ; store in buffer, permits out of
inc _pktwnum ; temporal order deliveries
mov _pktbuf_wrote,di ; remember where we wrote last
sub ax,cx ; allocated minus incoming packet
cmp ax,60+linksize ; enough for new link and miminal pkt?
jl pdrcvr6 ; l = not enough for next pkt
mov [di].count,cx ; update space really used
push di ; save this link pointer
add di,linksize ; plus current link info
add di,cx ; plus space used = new link point
sub ax,linksize ; available minus new link info
mov [di].flag,1 ; mark new link as free (1)
mov [di].count,ax ; size of new free data area
pop di ; return to current link
pdrcvr6:add di,linksize ; point at data portion
pdrcvr7:xor ax,ax ; return what we received in ax
pop cx
pop dx
pop ds ; ES:DI is the pkt buffer address
popf ; CX is size of requested buffer
ret
pdrcvr endp
; Begin Novell ODI support routines
; Note that while we use Ethernet_II (6 dest, 6 source, 2 TYPE bytes) to/from
; internal consumers the frame format to/from ODI is in the hands of ODI.
; Hopefully this will permit TCP/IP operation over all supported frame types.
; ARP/RARP packets are sized to the frame in use.
;
; Check for LSL presence, and if present then get entry points.
; Returns carry set if failure, else carry clear.
; This procedure is closely modeled upon the Novell example.
odichk proc near
cmp useodi,0 ; already inited?
je odichk0 ; e = no
clc
ret
odichk0:push es
mov ah,getintv ; get LSL via multiplexer interrupt
mov al,2fh ; vector 2fh
int dos ; to es:bx
mov ax,es
mov cx,ax
or cx,bx ; check if vector exists
jnz odichk1 ; nz = yes
pop es
stc
ret
odichk1:mov ax,0c000h ; look at multiplexer slots c0 et seq
push si
push di
odichk2:push ax
int 2fh
cmp al,0ffh ; is slot in use?
pop ax
je odichk4 ; e = yes, check for LSL being there
odichk3:inc ah ; next slot
or ah,ah ; wrapped?
jnz odichk2 ; nz = no, keep looking
pop di
pop si
pop es
stc ; not found, fail
ret
odichk4:mov di,si ; es:si should point to "LINKSUP$"
mov si,offset DGROUP:lslsig ; expected signature
mov cx,lslsiglen ; length
repe cmpsb ; check for signature
jne odichk3 ; ne = no match, try next Int 2fh slot
mov word ptr lslinit,bx ; found entry, save init entry point
mov ax,es ; returned in es:bx
mov word ptr lslinit+2,ax
mov ax,ds
mov es,ax ; get LSL main support/service addrs
mov si,offset DGROUP:lslsupport ; address of LSL entry point array
mov bx,2 ; request support/service entry points
; fills in far addresses of lslsupport and lslservice routines
call lslinit ; call LSL initialization routine
pop di
pop si
pop es
clc ; success
ret
odichk endp
; Bind a protocol TYPE to an ODI virtual board.
; Enter with TYPE (big endian/network order) in AX.
; Packet reception begins immediately upon a successful bind.
; Uses NET.CFG if information is available.
; Obtain StackID (our ident to the LSL), ProtID (ident of LSL's decoder),
; and boardnumber (the logical board), then bind to start reception. Do for
; one of our protocols.
; Returns PD handle (TYPE) in AX and carry clear upon success, else carry set.
odibind proc near
push ax
push bx
push si
push di
push es
mov bx,DGROUP
mov es,bx
cmp ax,ip_type ; IP, 0x0008h?
jne odibind1 ; ne = no
mov ax,offset DGROUP:ip_string ; put IP string in request
mov bx,offset ip_rcvr ; set address of receiver esr
mov di,offset DGROUP:ip_stackid ; set address of stackid struc
jmp short odibind3
odibind1:cmp ax,arp_type ; ARP, 0x0608?
jne odibind2 ; ne = no
mov ax,offset DGROUP:arp_string
mov bx,offset arp_rcvr
mov di,offset DGROUP:arp_stackid
jmp short odibind3
odibind2:cmp ax,rarp_type ; RARP, 0x3580?
je odibind2a ; e = yes
jmp odibindx ; ne = no, fail
odibind2a:mov ax,offset DGROUP:rarp_string
mov bx,offset rarp_rcvr
mov di,offset DGROUP:rarp_stackid
odibind3:mov word ptr registerstk.StackNamePtr,ax ; insert ptr to string
mov word ptr registerstk.StackReceiveHandler,bx ; setup esr addr
; Note: to use Prescan or Default registrations delete StackNamePtr & StackID.
; StackID is not used with these latter methods, and their reception begins
; at registration rather than at bind (so this area would be redesigned).
mov bx,PROTSUP_REGISTER_STACK ; register the protocol by name
mov si,offset DGROUP:registerstk ; registration form pointer
push di ; save ptr to xxx_stackid storage
call lslsupport ; call LSL with the address in es:si
pop di
jz odibind3a ; z = success
jmp odibindx ; nz = failure
odibind3a:mov [di].pstack,bx ; save returned StackID (LSL's handle
; for our protocol stack)
cmp readnetcfg,0 ; have read NET.CFG for BIND info?
jne odibind4 ; ne = yes
mov useboard,-1 ; clear board-to-use word
call getbind ; find Kermit's bind board in NET.CFG
inc readnetcfg ; say have read the file
cmp word ptr bdname,256*'#'+2 ; is board name #<digit>?
jne odibind4 ; ne = no, assume regular driver name
mov al,bdname+2 ; get ascii digit
sub al,'1' ; remove ascii bias (external=1 based)
xor ah,ah ; but we are zero based internally
cmp al,8 ; arbitrary limit of 8 boards
ja odibind4 ; a = out of range, ignore value
mov useboard,ax ; and make this the board number
mov bdname,0 ; and don't use bdname as a name
odibind4:mov [di].pboard,0 ; assume board zero to start loop
mov ax,useboard ; board to be used, if any
or ax,ax ; boards 0 and up are legal
jl odibind5 ; l = no board found yet, search
mov [di].pboard,ax ; specify board, get ProtID
odibind5:mov bx,PROTSUP_GET_MLID_CTL_ENTRY ; get MLID control entry
mov ax,[di].pboard ; for this board
push di
call lslsupport ; call LSL for the address to es:si
pop di
mov word ptr mlidcont,si
mov word ptr mlidcont+2,es ; MLID control routine
jz odibind7 ; z=success, have a board to work with
cmp ax,LSLERR_NO_MORE_ITEMS ; out of items?
je odibind5a ; e = yes, no more boards
cmp ax,LSLERR_ITEM_NOT_PRESENT ; other boards may exist?
je odibind7 ; e = yes
odibind5a:jmp odibindx ; fail
odibind7:mov bx,PROTSUP_GET_PID_PROTNUM_MLIDNUM ; get ProtID from StackID
mov ax,[di].pstack ; StackID
mov cx,[di].pboard ; and assumed board number
mov si,DGROUP
mov es,si ; set es:di to the ProtID buffer
lea si,[di].pprotid ; in our storage slot per protocol
push di ;
call lslsupport ; ask LSL for the ProtID string
pop di ; to that 6-byte buffer
jz odibind9 ; z = success, found a recognizer
cmp useboard,0 ; has a board been pre-identified?
jge odibind5a ; ge = yes, so the matchup failed
inc [di].pboard ; next board
jmp short odibind5 ; keep looking for a board
odibind9:mov bx,GET_MLID_CONFIGURATION ; get MLID config ptr to es:si
mov ax,[di].pboard
call mlidcont ; call MLID control routine
jnz odibindx ; nz = failure
cmp bdname,0 ; was a board name bound via BIND?
je odibin10 ; e = no, don't check on it
push es ; save pointer to MLID config table
push di
push si
les di,es:[si].MCardShortName ; get short name of this board
lea si,bdname ; desired board name string
mov cl,bdname ; length of desired board name
inc cl ; include length byte
xor ch,ch
cld
repe cmpsb ; compare len,string for both
pop si
pop di
pop es
je odibin10 ; e = found desired board
inc [di].pboard ; try next board
jmp short odibind5 ; keep looking for the desired board
odibin10:mov ax,[di].pboard ; get current board number
mov useboard,ax ; remember for next protocol
mov bx,es:[si].MFrameID ; frame ident, for get_hwd
call get_hwd ; get hardware specifics
or bx,bx ; returned _MAC_len, if any
jnz odibin11 ; nz = found match
mov bx,6 ; else default to 6 bytes
odibin11:push es
push si ; save config pointer
lea si,es:[si].MNodeAddress ; point to address in config struct
push ds ; save ds
push di
mov di,offset DGROUP:_eth_addr; where our MAC address is stored
mov ax,ds
mov cx,es
mov es,ax
mov ds,cx
cld
mov cx,bx ; MAC address length, bytes
rep movsb ; copy MAC address to global array
pop di
pop ds
pop si ; recover configuration table pointer
pop es
mov tells_len,0 ; presume no lookahead data length
test es:[si].MFlags,Len_Info ; capas bit for length provided (new)
jz odibin12 ; z = does not provide
inc tells_len ; say provides length
odibin12:mov bx,PROTSUP_BIND_STACK_TO_MLID ; Bind stack to MLID
mov ax,[di].pstack ; StackID
mov cx,[di].pboard ; board number
call lslsupport ; bind our protocol stack to board
jnz odibindx ; nz = failure
pop es ; received packets can interrupt now
pop di
pop si
pop bx
pop ax
clc
ret
odibindx:pop es
pop di
pop si
pop bx
pop ax
stc ; say failure
ret
odibind endp
; Worker for odibind. Find NET.CFG, extract name of board driver from pair of
; lines reading as below (Protocol must be in column 1, bind must be indented)
; Protocol Kermit Kermit's main section header
; bind <board_driver_name> indented, without the <> signs
;or
; Protocol Kermit
; bind #<digit> selects DOS driver load order (from 1)
;
; Examples -
; Protocol Kermit
; bind exos
;or
; Protocol Kermit
; bind #2
; and elsewhere there is the board driver section:
; Link Driver exos
;
; If found put the board driver name in array bdname, as length byte, string,
; then a null. If not found make length byte bdname be zero. We treat NET.CFG
; as case insensitive.
; Unless we use the special Kermit section then LSL will assign to us the
; first board loaded by DOS supporting the frame kind of our protocol.
; Link Driver section line "Protocol name type frame" simply associates a
; frame kind with the name and type, but not with a board. L.D. section line
; frame <frame kind> attaches that frame kind to the board, if it fits.
; Kermit uses "name" in the above line to pinpoint a protocol, not a board.
getbind proc near
mov bdname,0 ; clear board name length
push ds
mov bx,GENSERV_GET_NETCFG_PATH ; get fully formed NET.CFG name
call lslservice ; from LSL general services to ds:dx
jz getbin1 ; z = success
pop ds ; fail
ret
getbin1:mov ah,fopen ; open file NET.CFG
mov al,40h ; for reading, deny none
int dos ; returns file handle in ax
pop ds
mov temp,ax ; save handle for getbyte
jnc getbin2 ; nc = success
ret ; carry set for failure
getbin2:mov bx,1 ; subscript, at start of a line
getbin3:call getbyte ; read a byte, uppercased
jnc getbin4 ; nc = success
ret ; c = end of file
getbin4:cmp protword[bx],al ; compare to "PROTOCOL"
jne getbin5 ; ne = failure, scan for end of line
inc bx
cmp bl,protword ; length, matched all bytes?
jbe getbin3 ; be = no, match more
jmp short getbin6 ; ae = yes, next phrase
ret ; fail out
getbin5:cmp al,LF ; end of a line?
je getbin2 ; e = yes, scan for PROTOCOL again
call getbyte
jnc getbin5 ; keep consuming line material
ret ; fail out at end of file
; Short Name following "PROTOCOL"
getbin6:call getbyte ; get separator char, discard
jnc getbin7
ret ; c = eof
getbin7:call getbyte ; read short name of protocol
jnc getbin7a ; nc = text
ret ; return on eof
getbin7a:cmp al,' ' ; white space?
jbe getbin7 ; be = yes, stay in this state
mov bx,1 ; subscript
getbin8:cmp psname[bx],al ; compare to our protocol short name
jne getbin5 ; ne = failure, scan for end of line
cmp bl,psname ; matched all bytes?
jae getbin9 ; ae = yes, next phrase
inc bx
call getbyte ; get next byte to match
jnc getbin8 ; nc = not eof yet
ret
getbin9:call getbyte ; go to next line, enforce whitespace
jc getbin20 ; c = eof
cmp al,LF ; end of a line?
jne getbin9 ; ne = no, scan for end of line
call getbyte ; look for whitespace
jc getbin20 ; c = eof
cmp al,' ' ; required whitespace?
ja getbin5 ; a = no, start over
getbin10:call getbyte ; look for keyword "BIND"
jc getbin20
cmp al,' ' ; white space?
jbe getbin10 ; be = yes, stay in this state
mov bx,1 ; subscript
getbin11:cmp bindword[bx],al ; compare to "BIND"
jne getbin5 ; ne = failure, start over
cmp bl,bindword ; matched all bytes?
jae getbin12 ; ae = yes, next phrase
call getbyte
jc getbin20 ; c = eof
inc bx
jmp short getbin11 ; keep reading
getbin12:call getbyte ; skip white space before board name
jc getbin20
cmp al,' ' ; white space?
jbe getbin12 ; be = yes, skip it
getbin13:mov bl,bdname ; board name, length byte, starts at 0
xor bh,bh
inc bl
mov bdname,bl ; update length of board driver name
xor ah,ah ; get a null
mov word ptr bdname[bx],ax ; store as board short name,null
cmp bx,15 ; legal limit on short name?
jbe getbin14 ; be = ok
mov bdname,ah ; illegal, clear board name length
jmp getbin15 ; and quit
getbin14:call getbyte
jc getbin20 ; reached eof, is ok
cmp al,' ' ; usable text?
ja getbin13 ; a = yes, else stop storing name
getbin15:call getbyte ; read to eof so file is closed
jnc getbin15 ; nc = got a char
getbin20:ret
getbind endp
; Worker for getbind. Delivers one byte per call from NET.CFG, upper cased.
; Returns carry set and NET.CFG file closed at end of file.
; Temp has NET.CFG file handle, tempb is our one byte buffer for disk i/o.
getbyte proc near
mov dx,offset tempb ; ds:dx points to start of buffer
mov ah,fread ; read from file to buffer
mov cx,1 ; this many bytes
push bx
mov bx,temp ; get file handle
int dos
pop bx
jc getbyt2 ; c = failure
cmp ax,1 ; got the single byte?
jb getbyt2 ; b = no, failure
mov al,tempb ; return read byte
cmp al,'z' ; in lower case range?
ja getbyt1 ; a = no
cmp al,'a' ; in lower case range?
jb getbyt1 ; b = no
and al,not 20h ; lower to upper case
getbyt1:clc ; carry clear for success
ret ; return char in AL
getbyt2:push bx
mov bx,temp ; file handle
mov ah,fclose ; close the file
int dos
pop bx
stc ; say EOF or other failure
ret
getbyte endp
; Worker for odibind.
; Enter with BX holding Novell frame type from the MLID configuration table.
; Set _arp_hardware and _MAC_len and return BX holding _MAC_len value, else
; if frame is not supported return BX = 0. These two values are needed by the
; ARP functions. This list searching method is to accomodate the ever
; expanding quantity of frame types appearing with ODI; we deal with those we
; understand (sic).
get_hwd proc near
push es
push di
mov ax,DGROUP
mov es,ax
mov al,bl ; get frame value (MLID config)
xor bx,bx ; prepare no-match return value
mov di,offset frame_type ; list to search
mov cx,num_frames ; number of elements in the list
cld
repne scasb ; byte search
jne get_hwd1 ; ne = no match, fail
sub di,offset frame_type+1 ; make di be an index along the list
mov al,hardware_type[di] ; ARP/RARP hardware type ident
xor ah,ah
xchg ah,al ; put in network order (big endian)
mov _arp_hardware,ax ; hardware type for ARP/RARP pkts
mov bl,frame_adlen[di] ; array of MAC lengths for frame types
xor bh,bh
mov _MAC_len,bx ; save MAC address length (1..6 bytes)
get_hwd1:pop di
pop es
ret ; return _MAC_len in BX
get_hwd endp
; Unbind a protocol TYPE from an ODI virtual board
; Enter with protocol TYPE (net order) in AX, return carry set if failure.
; The TYPE is used as our handle to the application.
; Prescan and Default methods call lslsupport with the board number in AX
; rather than StackID and use matching PROTSUP_DEREGISTER_* code.
odiunbind proc near
cmp ax,ip_type ; IP, 0x0008h?
jne odiunb1 ; ne = no
mov ax,ip_stackid.pstack ; StackID
jmp short odiunb3
odiunb1:cmp ax,arp_type ; ARP, 0x0608?
jne odiunb2 ; ne = no
mov ax,arp_stackid.pstack
jmp short odiunb3
odiunb2:cmp ax,rarp_type ; RARP, 0x3580?
jne odiunb4 ; ne = no
mov ax,rarp_stackid.pstack
odiunb3:mov bx,PROTSUP_DEREGISTER_STACK ; deregister stack (StackID in AX)
call lslsupport ; stops reception now
jnz odiunb4 ; nz = failure
clc ; success
ret
odiunb4:stc ; failure
ret
odiunbind endp
; ODI receive interrupt handler, for use only by the LSL.
; Called with DS:DI pointing to lookahead structure, interrupts are off.
; Returns ES:SI pointing at ECB, AX = 0 if we want pkt, AX = 8001h if decline.
; There are three of these, one each for IP, ARP, and RARP. All have the
; same calling convention and all jump to odircv to do the real work.
; The length of the arriving packet is available if the MLID supports the
; new (mid-May 1992) capability, our "tells_len"; otherwise we make an
; intelligent guess based on the protocol header. These entry points can be
; called multiple times before receive-completion, and likely will be, so we
; use several ecb's to accept requests.
ip_rcvr proc far
push bx
push cx
push di
push ds
mov cx,ds ; DS:DI from LSL
mov es,cx ; use ES for LSL items
mov ax,DGROUP ; set DS to our data segment
mov ds,ax
push es
push si
cmp tells_len,0 ; have data length available?
je ip_rec1 ; e = no
les si,es:[di].DataLookAheadDataSize ; ptr to what it says
mov cx,word ptr es:[si] ; get length of data field
add cx,4 ; for overzealous board transfers
pop si
pop es
jmp far ptr odircv
ip_rec1:les si,es:[di].LookAheadPtr ; point at data lookahead ptr
mov cx,word ptr es:[si+2] ; IP pkt header, length word
cmp byte ptr es:[si],45h ; validate IP pkt kind (ver/hlen)?
pop si
pop es
jne ip_rcvr1 ; ne = invalid, decline
xchg ch,cl ; net to local order
add cx,14+8 ; our MAC level addressing + 8 safety
ip_rec2:mov ax,ip_stackid.pstack ; StackID for ecb structure
mov rcvtype,ip_type ; store protocol TYPE int
cmp cx,ETH_MSS+40+12+20 ; LARGEST ACCEPTABLE PKT
jbe odircv ; be = acceptable length
ip_rcvr1:add pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
adc pstats.PIgnoredRxPackets+2,0
mov ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
or ax,ax ; set Z flag to match AX
pop ds
pop di
pop cx
pop bx
ret
ip_rcvr endp
; RARP protocol receive service routine, similar to ip_rcvr.
rarp_rcvr proc far
push bx
push cx
push di
push ds
mov cx,ds ; DS:SI from LSL
mov es,cx
mov ax,DGROUP ; set DS to our data segment
mov ds,ax
mov ax,arp_stackid.pstack ; StackID for ecb structure
mov rcvtype,rarp_type ; store protocol TYPE int
jmp short arp_common ; do ARP/RARP common code
rarp_rcvr endp
; ARP protocol receive service routine, similar to ip_rcvr.
arp_rcvr proc far
push bx
push cx
push di
push ds
mov cx,ds ; DS:SI from LSL
mov es,cx
mov ax,DGROUP ; set DS to our data segment
mov ds,ax
mov ax,arp_stackid.pstack ; StackID for ecb structure
mov rcvtype,arp_type ; store protocol TYPE int
arp_common: ; common code for ARP/RARP
push es
push si
cmp tells_len,0 ; have data length available?
je arp_com1 ; e = no
les si,es:[di].DataLookAheadDataSize ; ptr to what it says
mov cx,word ptr es:[si] ; get length of data field
add cx,4 ; for overzealous board transfers
pop si
pop es
jmp short odircv
arp_com1:les si,es:[di].LookAheadPtr ; point at lookahead ptr for Data
mov cx,word ptr es:[si+4] ; ARP/RARP pkt header, length bytes
add cl,ch ; add HA and IP address lengths
xor ch,ch
add cl,cl ; for host and target
adc ch,0
add cx,8 ; plus ARP/RARP main header
cmp word ptr es:[si+2],ip_type ; ARP/RARP Protocol type of IP?
pop si
pop es
jne ip_rcvr1 ; ne = invalid, decline
; fall through to odircv
arp_rcvr endp
; General worker for ip_rcvr, arp_rcvr, rarp_rcvr. These are invoked by the
; LSL when their kind of packet arrives. This module creates the ECB and
; dispatches it to the LSL. Operating at LSL interrupt level.
; ES:DI is ptr to ODI Lookahead structure, DS is our data seg.
; AX is stackid for invoked protocol kind, CX is (guessed) pkt length overall.
; Rcvtype is the current invoked protocol TYPE (0008h is IP etc).
; When done store in the ecb's protocolws array:
; dw protocol TYPE (0008h for IP etc)
; dw subscript of this ecbr item (for use by odircmp)
; dw <unused>,<unused>
; Return ES:SI as pointer to a free ecb and AX = 0 to accept pkt, else
; return AX = 8001h to decline pkt (and to ignore ES:SI). Set Z flag to
; match AX value.
odircv proc far
add pstats.PtotalRxPackets,1 ; update ODI statistics counter
adc pstats.PtotalRxPackets+2,0
push ax
push cx
mov cx,ecbr_qty ; number of receive ecb's
xor bx,bx ; find a free ecb for this packet
odircv8:cmp ecbr_busy[bx],0 ; is this ECB free?
jne odircv9 ; ne = no, try next
mov ecbr_num,bx ; remember index for end of proc
mov ax,size ecbstruct ; size of an ecb
mul bl ; times this subscript
add ax,offset DGROUP:ecbr ; start of receive ecbs
mov bx,ax ; offset of free ecb
pop cx
pop ax
jmp short odircv2 ; use ds:[bx] for address of ecb
odircv9:inc bx ; next byte in busy array
loop odircv8
pop cx ; failed to find a free ecbr
pop ax
odircv1:add pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
adc pstats.PIgnoredRxPackets+2,0
mov ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
or ax,ax ; set Z flag for ODI
pop ds
pop di
pop cx
pop bx
ret
; ds:[bx] is ptr to a free ecbr
odircv2:mov [bx].stackid,ax ; StackID from odircv entry points
mov ax,es:[di].LBoardNum ; boardnum from ProtocolID lookahead
mov [bx].boardnum,ax ; store in ecbr
mov ax,rcvtype ; get TYPE from odircv entry points
mov word ptr [bx].protocolws,ax ; save TYPE for odircmp
mov ax,ecbr_num ; ecbr index
mov word ptr [bx].protocolws+2,ax ; save index for odircmp
cmp cx,64 ; min packet for Ethernet + 4 spare
jae odircv3 ; ae = no padding needed here
mov cx,64 ; padded min pkt plus 4 spare bytes
odircv3:push bx ; get a buffer of length CX bytes
xor ax,ax ; set AX = 0 for PD "get buf" call
call pdrcvr ; use PD buffer allocator code
pop bx ; ES:DI = buffer pointer, CX = length
mov ax,es
or ax,di ; check for refused pkt (es:di = NULL)
jz odircv1 ; z = pkt refused (no buffer space)
add di,6+6+2 ; skip our MAC header for ecb use
sub cx,6+6+2 ; less same length for MLID
mov [bx].frag1addr,di ; offset of buffer which MLID sees
mov ax,es
mov [bx].frag1addr+2,ax ; seg of buffer
mov [bx].datalen,cx ; length of buffer for MLID/LSL use
mov [bx].frag1len,cx ; ditto
mov ax,DGROUP ; segment of our ecb's
mov es,ax
mov si,bx ; return ES:SI pointing to ECB
mov bx,ecbr_num ; get ecbr index
mov ecbr_busy[bx],1 ; mark this ecbr as busy
pop ds
pop di
pop cx
pop bx
xor ax,ax ; return AX = 0 to accept
ret
odircv endp
; ODI receive-complete call-back routine for use only by the LSL.
; Enter with ES:SI pointing at ECB, interrupts are off.
; Returns nothing.
; There is no guarantee that this routine will be called in the sequence
; which packets arrived, so we carry the bookkeeping in the delivered ECB:
; TYPE is for Ethernet_II struct results, ecbr_busy is don't touch interlock.
; Note that we have to construct our own "destination" MAC address.
; es:[si].status is 0 (success), 8006h (buffer overrun), 8007h (canceled).
; StackID field is from LSL, and it's 0ffffh if using Prescan, and undefined
; if using Default. The manual says LSL, but not MLID, calls are ok in here.
odircmp proc far
push ds
push ax
push bx
push cx
mov ax,DGROUP
mov ds,ax ; set ds to our data segment
cmp es:[si].frag1addr+2,0 ; segment of pkt being confirmed
je odircmp5 ; e = illegal, ignore this call
cmp es:[si].status,0 ; check ECB status for failure
je odircmp1 ; e = success
mov es:[si].protocolws,0 ; write TYPE of 0 to permit queueing
odircmp1:push di ; put dest,src,TYPE into pkt buffer
push es
push si ; save ecbr's si
mov cl,byte ptr es:[si].driverws ; kind of destination, from LSL
mov di,es:[si].frag1addr ; start of our pkt buffer + 6+6+2
sub di,6+6+2 ; back to start, for our MAC header
push ds ; WATCH this, presumes ES == DS!
pop es ; set ES to DS (where pkt buffer is)
mov si,offset DGROUP:_eth_addr ; our hardware address
cmp cl,ECB_BROADCAST ; a broadcast?
jne odircmp2 ; ne = no, use our address
mov si,offset DGROUP:bcast ; fill with all 1's
odircmp2:mov cx,3 ; 6 byte addresses to our application
cld
rep movsw ; store source address in pkt buffer
pop si ; recover ecb si
push si ; save it again
mov ax,es:[si].protocolws ; get TYPE, from odircv
lea si,es:[si].immaddr ; offset to MAC address of sender
mov cx,3 ; three words worth, garbage and all
rep movsw ; copy to packet buffer
stosw ; and write TYPE to packet buffer
pop si
pop es
pop di
mov cx,es:[si].datalen ; length of data field from ecb
add cx,6+6+2 ; plus space for dest,src,TYPE
push es ; save ecb's es:si
push si
mov si,es:[si].frag1addr ; offset of pkt being confirmed
sub si,6+6+2 ; adj to beginning, for our MAC header
mov ax,1 ; set AX = 1 for buffer done call
call pdrcvr ; do post processing of buffer
pop si
pop es
odircmp5:xor ax,ax
cmp es:[si].frag1addr+2,ax ; segment of pkt being confirmed
je odircmp6 ; e = illegal, ignore this call
mov es:[si].frag1addr+2,ax ; clear pkt buffer pointer (seg)
mov es:[si].protocolws,ax ; clear packet TYPE
mov bx,es:[si].protocolws+2 ; point to ecb index
mov ecbr_busy[bx],al ; say this ecb is free now
odircmp6:pop cx
pop bx
pop ax
pop ds
ret
odircmp endp
; ODI transmission routine
; Enter with ds:si pointing at full Ethernet_II packet, cx = length (bytes)
; Once sent the ecb belongs to ODI until the xmt-complete routine is called.
odixmt proc near
push si
push di
push es
mov ax,ds
mov es,ax
cmp ecbx_busy,0 ; is this transmitter ecb free?
je odixmt8 ; e = yes
odixmt7:stc ; say failure
jmp odixmt6 ; abandon
odixmt8:mov ax,cx ; overall Ethernet_II length
sub ax,6+6+2 ; omit our MAC header
jc odixmt6 ; c = failure, abandon
mov ecbx.datalen,ax ; setup ECB overall data
mov ecbx.frag1len,ax ; and fragment length
mov cx,3 ; three words of dest MAC address
mov di,offset DGROUP:ecbx.immaddr ; destination address in ecb
rep movsw ; copy destination MAC address
add si,6 ; skip source Ethernet address
lodsw ; get protocol TYPE, move it to AX
mov ecbx.frag1addr,si ; offset of packet data
mov si,ds
mov ecbx.frag1addr+2,si ; segment of packet data
; Note: Prescan and Default methods use Raw Send: put 0ffffh in StackID
; and include the full frame header in the data field. Check MLID
; configuration word ModeFlags, MRawSendBit, for Raw Send capability.
cmp ax,ip_type ; IP, 0x0008h?
jne odixmt1 ; ne = no
mov si,offset DGROUP:ip_stackid
jmp short odixmt3
odixmt1:cmp ax,arp_type ; ARP, 0x0608?
jne odixmt2 ; ne = no
mov si,offset DGROUP:arp_stackid
jmp short odixmt3
odixmt2:cmp ax,rarp_type ; RARP, 0x3580?
jne odixmt7 ; ne = error, do not send
mov si,offset DGROUP:rarp_stackid
odixmt3:mov cx,5 ; stackid, protid, boardnum
mov di,offset DGROUP:ecbx.stackid ; get stack ident area
rep movsw ; copy to ecbx
mov ecbx_busy,1 ; set ECBx busy flag to busy state
add pstats.PtotalTxPackets,1 ; update ODI statistics counter
adc pstats.PtotalTxPackets+2,0
mov si,offset DGROUP:ecbx ; set es:si to ECB
mov bx,PROTSUP_SEND_PACKET ; send it
call lslsupport ; call LSL with ecbx ptr in es:si
clc ; success so far, ints are still off
odixmt6:pop es
pop di
pop si
ret
odixmt endp
; ODI transmission-complete processor, for use only by the LSL.
; Returns nothing. Unlocks ECB busy flag.
odixcmp proc far
push ds
push ax
mov ax,DGROUP ; set addressibility
mov ds,ax
mov ecbx_busy,0 ; set ECB busy flag to not-busy
pop ax
pop ds
ret
odixcmp endp
; ODI Protocol (that's us) Control routine, required, called from outside.
; In principle we should have one of these for each protocol (IP, ARP, RARP)
; by putting a different StackControlHandler address in registerstk, but I
; doubt that anyone is that interested in such detailed counts.
; Return AX clear, Z flag set, and ES:SI as table pointer if success, else
; return AX with error code and Z flag clear.
pcontrol proc far
cmp bx,GET_STACK_CONFIGURATION ; get stack configuration?
jne pcont1 ; ne = no
mov si,DGROUP
mov es,si ; es:si points to configuration table
mov si,offset DGROUP:pconfig; the table
xor ax,ax ; set Z flag
ret
pcont1: cmp bx,GET_STACK_STATISTICS ; get stack statistics?
jne pcont2 ; ne = no
mov si,DGROUP
mov es,si ; es:si points to statistics table
mov si,offset DGROUP:pstats ; the table
xor ax,ax ; set Z flag
ret
pcont2: mov ax,LSLERR_OUT_OF_RESOURCES ; other functions, report error
or ax,ax ; clear Z flag
ret
pcontrol endp
_TEXT ends
end