home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msvp98b1.lzh / MSNPDI.ASM < prev    next >
Assembly Source File  |  1993-05-14  |  57KB  |  1,687 lines

  1.     NAME    MSNPDI
  2. ; File MSNPDI.ASM
  3. ; Packet Driver and ODI interface 
  4. ;
  5. ;  Copyright (C) 1985, 1992, Trustees of Columbia University in the 
  6. ;  City of New York.  Permission is granted to any individual or institution
  7. ;  to use this software as long as it is not sold for profit.  This copyright
  8. ;  notice must be retained.  This software may not be included in commercial
  9. ;  products without written permission of Columbia University.
  10. ;
  11. ;  Permission is granted to use this file as a guide in developing commercial
  12. ;  products. If substantial portions are used crediting the source is 
  13. ;  recommended.
  14. ;
  15. ; Written by Joe R. Doupnik, Utah State University, Logan Utah 84322
  16. ;  jrd@cc.usu.edu, jrd@usu.Bitnet.
  17. ;
  18. ; Packet Driver reference:
  19. ; "PC/TCP Version 1.09 Packet Driver Specification", FTP Software, Inc.,
  20. ;  September-14-1989.
  21. ;
  22. ; ODI references:
  23. ; "Open Data-Link Interface Developer's Guide for DOS Network Layer Protocol 
  24. ;  Stacks", Novell Inc, document number 100-001218-001 (1992, but no printed 
  25. ;  date).
  26. ; "Open Data-Link Interface Developer's Guide for NetWare v3.1x Server 
  27. ;  Driver Protocol Stacks", Novell Inc, document number 100-001196-00, v1.0,
  28. ;  19 Sept 1991.
  29. ; "Open Data-Link Interface LAN Driver Developer's Guide for DOS", Novell Inc,
  30. ;  document number 107-000010-001, Revision i, 13 Nov 1990.
  31. ;
  32. ; C language interface presumes the Small memory model and Microsoft C.
  33. ; Assembler is MS MASM v6.
  34. ;
  35. ; These procedures interface between ODI or a Packet Driver and the main
  36. ; protocol stack to both send and receive packets. The upper levels provide
  37. ; and receive packets framed as Ethernet_II (Blue Book/DIX), and receive
  38. ; ARP information particular to the physical frames involved. Conversion of
  39. ; this internal form to the actual framing method on the wire is done by ODI.
  40. ; The receive buffer is external to this routine. Received packets are linked
  41. ; into the buffer on an order of arrival basis (and sized to fit). High level
  42. ; reception has to poll the receive buffer queue. Transmitted packets are 
  43. ; operated with a single external buffer, and sending blocks until the lower 
  44. ; level driver material is ready for us. Initialization, status, attachment, 
  45. ; and disengagment procedures are here.
  46. ; External int kpdint is used by pdinit() to select between Packet Driver
  47. ; and ODI interfaces. Call pdinit() to initialize the system, then call
  48. ; pdaccess() to register each desired frame TYPE, call pdclose() to release
  49. ; each frame TYPE.
  50. ; Packet senders call pkt_send() directly.
  51. ; Packet receivers examine the external packet buffer for packets.
  52. ; ARP information is returned in external ints arp_hardware (hardware ARP
  53. ; type code) and MAC_len (length of MAC address, in bytes). See table below
  54. ; for the possible pairs.
  55. ; Packet Driver usage is limited to Ethernet_II/DIX and SLIP. ODI usage is
  56. ; at least Ethernets, Token Ring, Arcnet, and others untested here.
  57. ; Edit history
  58. ; Last edit
  59. ; 26 August 1992
  60.  
  61. getintv        equ    35h        ; DOS get interrupt vector to es:bx
  62. dos        equ    21h
  63. lf        equ    0ah
  64. fopen        equ    3dh        ; DOS file operations
  65. fclose        equ    3eh
  66. fread        equ    3fh
  67.  
  68. pdgetinfo    equ    1        ; Packet Driver functions
  69. pd_access     equ    2
  70. pd_release    equ    3
  71. pd_send        equ    4
  72. pd_get_address    equ    6
  73. eaddr_len    equ    6        ; length of an Ethernet address
  74. ETH_MSS        equ    534        ; Correlate with msntcp.h
  75. IF_EII        equ    1        ; Ethernet II interface type
  76. IF_SLIP        equ    6        ; SLIP interface type
  77.  
  78. link    struc                ; receiver buffer link structure
  79.     flag    db    0        ; buffer use flag
  80.     bufnum    db    0        ; buffer write sequence number
  81.     count    dw    0        ; count of bytes to follow
  82. link    ends
  83. linksize equ    4            ; bytes in link structure
  84.  
  85. ; Novell ODI material, based on LSL v1.2.
  86. ; Most of these header structures were taken from Novell file ODI.INC,
  87. ; and parts have been tailored for Kermit. Information on the "new" item in
  88. ; the lookahead structure is compliments of Novell, private correspondence.
  89.  
  90. ;  Event Control Block Structure
  91. ECBstruct    struc
  92.     nextlink    dd    0        ; leave intact
  93.     prevlink    dd    0        ; leave intact
  94.     status        dw    0        ; general status
  95.     esr        dd    0        ; addr of event service routine
  96.     stackid        dw    0        ; protocol stack ident
  97.     protid        db    6 dup (0)    ; sending only
  98.     boardnum    dw    0        ; sending, MILD board number
  99.     immaddr        db    6 dup (0)    ; MAC destination address
  100.     driverws    db    4 dup (0)    ; their work space
  101.     protocolws    dw    4 dup (0)    ; our work space
  102.     datalen        dw    0        ; total length of sent buffer
  103.     fragcount    dw    1        ; number of buffer pieces
  104.     frag1addr    dw    0,0        ; seg:offset of first buffer
  105.     frag1len    dw    0        ; length of first buf frag
  106. ECBstruct    ends                ; 26 words
  107.  
  108. ;  Look Ahead Structure
  109. LookAheadStruc    struc
  110.     LMediaHeaderPtr    dd    0        ; pointer to MAC header
  111.     LookAheadPtr    dd    0        ; pointer to pkt Data
  112.     LookAheadLen    dw    0        ; length of pkt Data field
  113.     LProtID        db    6 dup (0)    ; protocol ident
  114.     LBoardNum    dw    -1        ; logical board of rcv'd pkt
  115.     DataLookAheadDataSize dd 0    ; new field, exists if bit 7 of the 
  116.             ; Driver's Configuration Table Mode Flags is set.
  117. LookAheadStruc    ends
  118.  
  119. ;  Rx Destination Address Type (First byte of ECB.DriverWS)
  120. ECB_DIRECT        equ    00h        ;Physical destination address
  121. ECB_MULTICAST        equ    01h        ;Multicast destination address
  122. ECB_BROADCAST        equ    03h        ;Broadcast destination address
  123.  
  124. ;  System Error Code Definitions
  125. LSLERR_OUT_OF_RESOURCES    equ    8001h
  126. LSLERR_BAD_PARAMETER    equ    8002h
  127. LSLERR_NO_MORE_ITEMS    equ    8003h
  128. LSLERR_ITEM_NOT_PRESENT    equ    8004h
  129. LSLERR_FAIL        equ    8005h
  130. LSLERR_RX_OVERFLOW    equ    8006h
  131. LSLERR_CANCELLED    equ    8007h
  132. LSLERR_BAD_COMMAND    equ    8008h
  133. LSLERR_DUPLICATE_ENTRY    equ    8009h
  134. LSLERR_NO_SUCH_HANDLER    equ    800ah
  135. LSLERR_NO_SUCH_DRIVER    equ    800bh
  136.  
  137. ;  LSL MLID Services Function Codes
  138. MLIDSUP_GET_ECB                   equ    0
  139. MLIDSUP_RETURN_ECB             equ    1
  140. MLIDSUP_DEFRAG_ECB           equ    2
  141. MLIDSUP_SCHEDULE_AES_EVENT     equ    3
  142. MLIDSUP_CANCEL_AES_EVENT       equ    4
  143. MLIDSUP_GET_INTERVAL_MARKER    equ    5
  144. MLIDSUP_DEREGISTER_MLID           equ    6  
  145. MLIDSUP_HOLD_RECV_EVENT           equ    7  
  146. MLIDSUP_START_CRITICAL_SECTION    equ    8  
  147. MLIDSUP_END_CRITICAL_SECTION       equ    9  
  148. MLIDSUP_CRITICAL_SECTION_STATUS    equ    10 
  149. MLIDSUP_SERVICE_EVENTS              equ    11
  150. MLIDSUP_SEND_COMPLETE              equ    14 
  151. MLIDSUP_ADD_PID            equ    15
  152. MLIDSUP_GET_STACK_ECB        equ    16
  153.  
  154. ;  LSL Protocol Stack Services Function Codes
  155. PROTSUP_GET_ECB                equ    0
  156. PROTSUP_RETURN_ECB            equ    1
  157. PROTSUP_SCHEDULE_AES_EVENT         equ    3
  158. PROTSUP_CANCEL_EVENT            equ    4
  159. PROTSUP_GET_INTERVAL_MARK        equ    5
  160. PROTSUP_REGISTER_STACK            equ    6  
  161. PROTSUP_DEREGISTER_STACK        equ    7  
  162. PROTSUP_REGISTER_DEFAULT_STACK        equ    8  
  163. PROTSUP_DEREGISTER_DEFAULT_STACK    equ    9  
  164. PROTSUP_REGISTER_PRESCAN_STACK        equ    10 
  165. PROTSUP_DEREGISTER_PRESCAN_STACK    equ    11 
  166. PROTSUP_SEND_PACKET            equ    12 
  167. PROTSUP_GET_PROTNUM_FROM_NAME        equ    16 
  168. PROTSUP_GET_PID_PROTNUM_MLIDNUM        equ    17 
  169. PROTSUP_GET_MLID_CTL_ENTRY        equ    18 
  170. PROTSUP_GET_PROTO_CTL_ENTRY        equ    19 
  171. PROTSUP_GET_LSL_STATS            equ    20 
  172. PROTSUP_BIND_STACK_TO_MLID        equ    21 
  173. PROTSUP_UNBIND_STACK_FROM_MLID        equ    22 
  174. PROTSUP_ADD_PID                equ    23 
  175. PROTSUP_RELINQUISH_CONTROL        equ    24 
  176. PROTSUP_GET_LSL_CONFIG            equ    25
  177.  
  178. ;  LSL General Services Function Codes
  179. GENSERV_ALLOC_MEMORY        equ    0
  180. GENSERV_FREE_MEMORY        equ    1
  181. GENSERV_REALLOC_MEMORY        equ    2
  182. GENSERV_MEMORY_STATISTICS    equ    3
  183. GENSERV_ADD_MEMORY_TO_POOL    equ    4
  184. GENSERV_ADD_GENERAL_SERVICE    equ    5
  185. GENSERV_REMOVE_GENERAL_SERVICE    equ    6
  186. GENSERV_GET_NETCFG_PATH        equ    7
  187.  
  188. ;  LSL Configuration Table
  189. LSLConfigurationStructure    struc
  190.     LConfigTableMajorVer    db    1
  191.     LConfigTableMinorVer    db    0
  192.     LNumLSLRxBuffers    dd    0
  193.     LRxBufferSize        dd    0    ;Buffer size NOT including ECB struc size
  194.     LMajorVersion        db    0
  195.     LMinorVersion        db    0
  196.     LConfigTableReserved    db    16 dup (0)
  197. LSLConfigurationStructure    ends
  198.  
  199. ;  MLID Control Commands
  200. GET_MLID_CONFIGURATION        equ    0
  201. GET_MLID_STATISTICS        equ    1
  202. ADD_MULTICAST_ADDRESS        equ    2
  203. DELETE_MULTICAST_ADDRESS    equ    3
  204. MLID_SHUTDOWN            equ    5
  205. MLID_RESET            equ    6
  206. CREATE_CONNECTION        equ    7
  207. REMOVE_CONNECTION        equ    8
  208. SET_LOOK_AHEAD_SIZE        equ    9
  209. DRIVER_POLL            equ    12
  210.  
  211. ;  MLID Configuration Table Structure
  212. MLIDConfigurationStructure    struc
  213.     MSignature        db    'HardwareDriverMLID',8 dup (' ')
  214.     MConfigTableMajorVer    db    1
  215.     MConfigTableMinorVer    db    11
  216.     MNodeAddress        db    6 dup (?)
  217.     MModeFlags        dw    ?
  218.     MBoardNumber        dw    ?
  219.     MBoardInstance        dw    ?
  220.     MMaxPacketSize        dw    ?
  221.     MBestDataSize        dw    ?
  222.     MWorstDataSize        dw    ?
  223.     MCardLongName        dd    ?
  224.     MCardShortName        dd    ?        ; visible board name
  225.     MFrameString        dd    ?
  226.     MReserved0        dw    0        ;Must be set to 0
  227.     MFrameID        dw    ?
  228.     MTransportTime        dw    ?
  229.     MRouteHandler        dd    ?        ;Only for Token-Ring
  230.     MLookAheadSize        dw    ?
  231.     MLineSpeed        dw    ?        ;In Mbps or Kbps
  232.     MReserved1        db    8 dup (0)    ;Must be set to 0
  233.     MMLIDMajorVer        db    ?
  234.     MMLIDMinorVer        db    ?
  235.     MFlags            dw    ?
  236.     MSendRetries        dw    ?
  237.     MLink            dd    ?
  238.     MSharingFlags        dw    ?
  239.     MSlot            dw    ?
  240.     MIOAddress1        dw    ?
  241.     MIORange1        dw    ?
  242.     MIOAddress2        dw     ?
  243.     MIORange2        dw    ?
  244.     MMemoryAddress1        dd    ?
  245.     MMemorySize1        dw    ?
  246.     MMemoryAddress2        dd    ?
  247.     MMemorySize2        dw    ?
  248.     MIntLine1        db    ?
  249.     MIntLine2        db    ?
  250.     MDMALine1        db    ?
  251.     MDMALine2        db    ?
  252. MLIDConfigurationStructure    ends
  253.  
  254. ;  MLID Config Table 'MFlags' bit definitions.
  255. EISA    equ    01h            ;EISA Bus
  256. ISA     equ    02h            ;PC/AT Bus
  257. MCA     equ    04h            ;PS/2 MCA Bus
  258. Len_Info equ    40h            ; pkt data length in lookahead info
  259.  
  260. ;  MLID Config Table 'MModeFlags' bit definitions (no promiscuous mode).
  261. MRealDriverBit        equ    0001h
  262. MUsesDMABit        equ    0002h
  263. MGuaranteedDeliveryBit    equ    0004h        ;100% reliable on transmits
  264. MMulticastBit        equ    0008h
  265. MNeedsPollingBit    equ    0020h
  266. MRawSendBit        equ    0040h
  267.  
  268. ; Registered Stack structure, used during registration only
  269. StackInfoStruc    struc
  270.     StackNamePtr        dd    ip_string    ; ptr to short name
  271.     StackReceiveHandler    dd    ip_rcvr     ; rcv routine
  272.     StackControlHandler    dd    pcontrol    ; control routine
  273. StackInfoStruc    ends
  274.  
  275. ;  Protocol Control Commands
  276. GET_STACK_CONFIGURATION        equ    0
  277. GET_STACK_STATISTICS        equ    1
  278. BIND_TO_MLID            equ    2
  279. UNBIND_FROM_MLID        equ    3
  280. INFORM_MLID_DEREGISTERED    equ    4
  281.  
  282. ;  Protocol Configuration Table
  283. ProtocolConfigStructure    struc
  284.     PConfigTableMajorVer    db    1
  285.     PConfigTableMinorVer    db    0
  286.     PProtocolLongName    dd    plname
  287.     PProtocolShortName    dd    psname        ; "KERMIT"
  288.     PProtocolMajorVer    db    3         ; MSK v3.12
  289.     PProtocolMinorVer    db    12
  290.     PConfigTableReserved    db    16 dup (0)
  291. ProtocolConfigStructure    ends
  292.  
  293. ;  Protocol Statistics Table
  294. ProtocolStatStructure    struc
  295.     PStatTableMajorVer    db    1
  296.     PStatTableMinorVer    db    0
  297.     PNumGenericCounters    dw    3        ; just those below
  298.     PValidCounterMask    dd    111b        ; bitfield, 3 valids
  299.     PTotalTxPackets        dw    2 dup (0)
  300.     PTotalRxPackets        dw    2 dup (0)
  301.     PIgnoredRxPackets    dw    2 dup (0)
  302.     PNumCustomCounters    dw    0        ; none
  303. ProtocolStatStructure    ends
  304.  
  305. pinfo    struc                ; per protocol local data for ecb
  306.     pstack    dw    0        ; StackID
  307.     pprotid    db    6 dup (0)    ; ProtID
  308.     pboard    dw    0        ; boardnum
  309. pinfo    ends
  310.  
  311. _TEXT    SEGMENT  WORD PUBLIC 'CODE'
  312. _TEXT    ENDS
  313. _DATA    SEGMENT  WORD PUBLIC 'DATA'
  314. _DATA    ENDS
  315. CONST    SEGMENT  WORD PUBLIC 'CONST'
  316. CONST    ENDS
  317. _BSS    SEGMENT  WORD PUBLIC 'BSS'
  318. _BSS    ENDS
  319. DGROUP    GROUP    CONST, _BSS, _DATA
  320.     ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES:NOTHING
  321.  
  322. _DATA      SEGMENT
  323.     extrn    _pktbuf_wrote:word, _pktwnum:byte, _kpdint:word
  324.     extrn    _eth_addr:byte, _arp_hardware:word, _MAC_len:word
  325.  
  326. pdsignature    db    'PKT DRVR'    ; signature of a Packet Driver
  327. if_type        dw    0        ; interface type
  328. if_class    db    0        ; interface class
  329. if_num        db    0        ; interface number
  330. if_func        db    0        ; interface functionality
  331. if_version    dw    0        ; interface version
  332. iptype        db    8,0        ; IP packet type
  333. iptypelen    equ    $-iptype    ; length of type field for iptype
  334. pktbufoff    dw    0        ; offset of packet buffer
  335. SLIPmac        dw    0,0,2        ; fake SLIP dest Ethernet address
  336.                     ; ODI material
  337. useodi        db    0        ; non-zero if using ODI for transport
  338. lslsig        db    'LINKSUP$'    ; LSL presence signature
  339. lslsiglen    equ    $-lslsig
  340. lslinit        dd    0        ; LSL init entry point
  341.                     ; LSL entry structure, do not separate
  342. lslsupport    dd    0        ; LSL protocol support API entry point
  343. lslservice    dd    0        ; LSL general services API entry point
  344.  
  345. mlidcont     dd    0        ; MLID Control entry point
  346.  
  347. ecbr_qty    equ    4        ; number of receive ECB's to allocate
  348. maketab    MACRO                ; macro to make receiver ecbs
  349. cnt = 0
  350.     rept ecbr_qty - 1
  351.     ecbstruct <,,,odircmp>
  352. cnt = cnt + 1
  353.     endm
  354. ENDM
  355. ecbr        ecbstruct <,,,odircmp>    ; first receiver ECB
  356.         maketab            ; make table of the other ecbr's
  357. ecbx        ecbstruct <,,,odixcmp>    ; one ECB for transmission
  358. ecbr_busy    db     ecbr_qty dup (0) ; our ecbr locks
  359. ecbx_busy    db    0        ; non-zero if ECBx owned by ODI
  360. ecbr_num    dw    0        ; temp to hold index of ecbr/ecbr_busy
  361. rcvtype        dw    0        ; temp, holds protocol TYPE for rcv
  362. pconfig     ProtocolConfigStructure <>    ; as the name says
  363. pstats        ProtocolStatStructure <>    ; protocol statistics
  364. registerstk     StackInfoStruc <>    ; bound stack setup structure
  365. plname        db 13,'MS-DOS Kermit',0    ; cnt, protocol stack long name, null
  366. protword     db 8,'PROTOCOL',0    ; three NET.CFG keywords for Kermit
  367. psname        db 6,'KERMIT',0        ; cnt, protocol stack short name, null
  368. bindword     db 4,'BIND',0        ; third keyword
  369. ip_type        equ    0008h        ; Protocol TYPEs, big endian/net order
  370. arp_type    equ    0608h
  371. rarp_type    equ    3580h
  372. ip_string     db    2,'IP',0    ; strings to match in NET.CFG file
  373. arp_string    db    3,'ARP',0    ;  to select pkt TYPEs
  374. rarp_string    db    4,'RARP',0    ;  RARP is optional
  375. ip_stackid    pinfo    <>        ; StackID, Protid, boardnum 
  376. arp_stackid    pinfo    <>        ;  for each protocol
  377. rarp_stackid    pinfo    <>
  378. bcast        db    6 dup (0ffh)    ; Broadcast address, for reception
  379. readnetcfg    db    0        ; non-zero if have read NET.CFG
  380. useboard    dw    -1        ; board to be used, -1 = not inited
  381. bdname        db    0,16 dup (0)    ; length, 15 text, null, bound board
  382. tells_len    db    0        ; if MLID tells pkt len for lookahead
  383. tempb        db    0
  384. temp        dw    0
  385. ; parallel lists of NetWare ODI frame types, address lengths, ARP idents
  386. frame_type    db    2,3,4,5,6,7, 9,10,11,14,15,16,23
  387. num_frames    equ    ($ - frame_type)
  388. frame_adlen    db    6,6,6,6,6,6, 1,6, 6, 1, 6, 6, 6
  389. hardware_type    db    1,6,6,6,6,12,4,6, 6, 7, 6, 6, 6
  390. _DATA      ENDS
  391.  
  392. ; ODI Frame types, frame strings, length (bytes) of a MAC level address:
  393. ; type    frame string        MAC_len   hardware    comments
  394. ; 0    VIRTUAL_LAN        0    0    no MAC header used
  395. ; 1    LOCALTALK        6    11    Apple (Ether/Tokentalk is 802)
  396. ; 2    ETHERNET_II        6    1    Blue Book
  397. ; 3    ETHERNET_802.2        6    6    802.3 with 802.2 wrap
  398. ; 4    TOKEN-RING        6    4    802.5 with 802.2 wrap
  399. ; 5    ETHERNET_802.3        6    6    802.3 "raw", old Novell
  400. ; 6    802.4            6    6    Token Bus
  401. ; 7    NOVELL_PCN2          6    12    Novell's IBM PCnet2
  402. ; 8    GNET              6    4    Gateway, assumed TRN-like
  403. ; 9    PRONET-10        1     4    Proteon TRN-like
  404. ; 10    ETHERNET_SNAP        6    1    802.3 with 802.2+SNAP
  405. ; 11    TOKEN-RING_SNAP        6    6    802.5 with 802.2+SNAP
  406. ; 12    LANPAC_II        6    ?    Racore
  407. ; 13    ISDN            6    ?    telco
  408. ; 14    NOVELL_RX-NET        1    7    Arcnet-like
  409. ; 15    IBM_PCN2_802.2        6    12    IBM PCnet2 with 802.2
  410. ; 16    IBM_PCN2_SNAP        6    12    IBM PCnet2,802.2+SNAP
  411. ; 17    OMNINET/4        ?    ?    Corvus
  412. ; 18    3270_COAXA        ?    ?    Harris
  413. ; 19    IP            ?    ?    tunneled
  414. ; 20    FDDI_802.2        6    ?
  415. ; 21    IVDLAN_802.9        6    ?    Commtex
  416. ; 22    DATACO_OSI        ?    ?    Dataco
  417. ; 23    FDDI_SNAP        6    6    802.7, with 802.2+SNAP
  418. ;
  419. ;      ARP hardware field, from RFC 1060
  420. ;     Type   Description                 
  421. ;     ----   -----------                
  422. ;    1    Ethernet (10Mb)
  423. ;    2    Experimental Ethernet (3Mb)
  424. ;    3    Amateur Radio AX.25
  425. ;    4    Proteon ProNET Token Ring
  426. ;    5    Chaos
  427. ;       6    IEEE 802 Networks
  428. ;       7    ARCNET
  429. ;       8    Hyperchannel
  430. ;       9    Lanstar
  431. ;      10    Autonet Short Address
  432. ;      11    LocalTalk
  433. ;      12    LocalNet (IBM PCNet or SYTEK LocalNET)
  434.  
  435. _TEXT    segment
  436.  
  437. pktdrvr    proc    near            ; Packet Driver interrupt invokation
  438. PKTDRVI:int    60h        ; Interrupt number, modified by startup code
  439.     ret
  440. pktdrvr    endp
  441.  
  442. ; pdinit(ðeraddress)
  443. ; Initialize Packet Driver or ODI for use by this program. Stores Ethernet
  444. ; address (or MAC address). _kpdint is 0 to scan first for a Packet Driver
  445. ; interrupt and fall back to search for ODI, or is a number 60h..7fh to
  446. ; target only that PD interrupt, or is 'DO' to target only ODI. If a PD is
  447. ; used then _kpdint is modified to be the found interrupt value.
  448. ; A 6 byte MAC level address is returned for convenience, even for SLIP. 
  449. ; Returns 1 for success, 0 for failure.
  450.  
  451.     public    _pdinit
  452. _pdinit    proc    near
  453.     push    bp
  454.     mov    bp,sp
  455.     push    es
  456.     push    si
  457.     push    di
  458.     push    ds
  459.     mov    ax,dgroup
  460.     mov    ds,ax
  461.     mov    ax,[bp+4+0]        ; get offset of pktbuf
  462.     mov    pktbufoff,ax        ; save locally
  463.     mov    cx,60h            ; interrupt range
  464.     cmp    _kpdint,0        ; user value given?
  465.     je    pdinit1            ; e = no
  466.     mov    cx,_kpdint        ; use it
  467.     cmp    cx,'DO'            ; special indicator to use ODI?
  468.     je    pdinit2            ; e = yes, skip Packet Driver
  469. pdinit1:mov    ah,getintv        ; get interrupt vector to es:bx
  470.     mov    al,cl            ; vector number
  471.     int    dos
  472.     mov    si,offset dgroup:pdsignature ; look for signature
  473.     push    cx
  474.     mov    cx,length pdsignature    ; length of string
  475.     mov    di,bx
  476.     add    di,3            ; sig starts 3 bytes from entry point
  477.     cld
  478.     repe    cmpsb            ; compare bytes
  479.     pop    cx
  480.     je    pdinit3            ; e = found a match
  481.     cmp    _kpdint,0        ; user value given?
  482.     jne    pdinit2            ; ne = yes, so fail
  483.     inc    cx
  484.     cmp    cx,80h            ; at end of range?
  485.     jna    pdinit1            ; na = not yet, try another int
  486.                     ; no Packet Driver found
  487. pdinit2:call    odichk            ; see if ODI is available
  488.     jc    pdinit5            ; c = no
  489.     mov    useodi,1        ; say using ODI
  490.     mov    _kpdint,'DO'        ; signal ODI via PD interrupt variable
  491.     mov    if_class,1        ; say Ethernet_II for internal work
  492.     mov    di,[bp+4+2]        ; get offset of user's buffer
  493.     mov    cx,eaddr_len        ; length of address provided
  494.     mov    si,offset DGROUP:SLIPmac ; get fake Ethernet address
  495.     cld                ;  in case code wants it early
  496.     push    ds
  497.     pop    es
  498.     push    di
  499.     rep    movsb            ; copy to user buffer
  500.     mov    cx,ecbr_qty
  501.     mov    di,offset DGROUP:ecbr_busy ; clear receive ecb busy flags
  502.     xor    al,al
  503.     rep    stosb
  504.     pop    di
  505.     clc
  506. pdinit5:jmp    pdret            ; exit (carry set is failure)
  507.                      ; Packet Driver details
  508. pdinit3:mov    byte ptr PKTDRVI+1,cl    ; force in new PD interrupt, code mod
  509.     mov    _kpdint,cx        ; remember interrupt number
  510.                     ; find Ethernet address
  511.     mov    ah,pdgetinfo        ; get Packet Driver information
  512.     mov    al,0ffh
  513.     xor    bx,bx            ; optional handle
  514.     push    ds            ; this call changes ds and si
  515.     push    si
  516.     call    pktdrvr            ; call the Packet Driver
  517.     pop    si
  518.     pop    ds
  519.     jc    pdret            ; c = failure
  520. pdinit6:mov    if_type,dx        ; save details for access calls
  521.     mov    if_class,ch
  522.     mov    if_num,cl
  523.     mov    if_func,al
  524.     mov    if_version,bx
  525.     mov    ah,pd_access        ; access packets
  526.     mov    al,ch            ; Ethernet class
  527.     mov    bx,dx            ; type
  528.     mov    dl,cl            ; interface number
  529.     mov    cx,iptypelen        ; type length for iptype
  530.     mov    si,offset dgroup:iptype    ; address of TYPE
  531.     mov    di,cs
  532.     mov    es,di
  533.     mov    di,offset pdrcvr    ; ES:DI is our Packet Driver receiver
  534.     call    pktdrvr
  535.     jc    pdret            ; c = failure
  536.     mov    bx,ax            ; put returned handle in BX
  537.     mov    ax,DGROUP        ; our data segment
  538.     mov    es,ax            ; segment of Ethernet address buffer
  539.     mov    di,[bp+4+2]        ; get offset of user's buffer
  540.     mov    cx,eaddr_len        ; length of address wanted
  541.     cmp    if_class,IF_SLIP    ; interface class of SLIP?
  542.     jne    pdinit4            ; ne = no
  543.     mov    si,offset DGROUP:SLIPmac ; get fake Ethernet address
  544.     cld
  545.     push    di            ; save in case PD actually uses it
  546.     rep    movsb            ; copy to user buffer
  547.     pop    di
  548. pdinit4:mov    ah,pd_get_address    ; get the Ethernet address
  549.     push    bx            ; save handle
  550.     call    pktdrvr            ; get Ethernet address to es:di buf
  551.     pop    bx
  552.     pushf                ; save carry flag
  553.     mov    ah,pd_release        ; release this Type, bx has handle
  554.     call    pktdrvr
  555.     popf                ; recover carry flag
  556. pdret:    mov    ax,1            ; return C status, 1 for success
  557.     jnc    pdret1
  558.     xor    ax,ax            ; 0 for failure
  559. pdret1:    pop    ds            ; success
  560.     pop    di
  561.     pop    si
  562.     pop    es
  563.     mov    sp,bp            ; restore stack
  564.     pop    bp            ; recover bp reg
  565.     ret
  566. _pdinit    endp
  567.  
  568. ; int pdinfo(& int version, & int class, & int pdtype, & int number,
  569. ;  & int functionality)
  570. ; Get Packet Driver pedigree
  571.     public    _pdinfo
  572. _pdinfo    proc    near
  573.     push    bp
  574.     mov    bp,sp
  575.     push    di
  576.     push    cx
  577.     push    bx
  578.     push    ax            ; save al for later use
  579.     mov    di,[bp+4+0]
  580.     mov    bx,if_version
  581.     mov    [di],bx            ; return version
  582.     mov    al,if_class        ; class
  583.     xor    ah,ah
  584.     mov    di,[bp+4+2]
  585.     mov    [di],ax            ; return as an int
  586.     mov    di,[bp+4+4]
  587.     mov    dx,if_type
  588.     mov    [di],dx            ; type
  589.     mov    di,[bp+4+6]
  590.     xor    ch,ch
  591.     mov    cl,if_num
  592.     mov    [di],cx            ; interface number, as an int
  593.     pop    ax            ; recover al
  594.     mov    di,[bp+4+8]
  595.     xor    ah,ah
  596.     mov    al,if_func
  597.     mov    [di],ax            ; functionality, as an int
  598.     mov    ax,1            ; C style exit status, 1 = success
  599.     pop    bx
  600.     pop    cx
  601.     pop    di
  602.     pop    bp
  603.     ret
  604. _pdinfo    endp
  605.  
  606. ; int pdclose(int handle)
  607. ; Close a Packet Driver or ODI handle.
  608. ; Returns (in AX) 1 if successful, else 0.
  609.     public    _pdclose
  610. _pdclose    proc    near
  611.     push    bp
  612.     mov    bp,sp
  613.     push    bx
  614.     mov    bx,[bp+4+0]        ; handle
  615.     cmp    useodi,0        ; using ODI?
  616.     je    pdclos2            ; e = no
  617.     mov    ax,bx            ; get handle
  618.     call    odiunbind        ; unbind from LSL and MLID
  619.     jmp    short pdclos3
  620.  
  621. pdclos2:mov    ah,pd_release        ; release_type
  622.     call    pktdrvr
  623.  
  624. pdclos3:mov    ax,1            ; assume success
  625.     jnc    pdclos1            ; nc = success
  626.     xor    ax,ax            ; 0 for failure
  627. pdclos1:pop    bx
  628.     pop    bp
  629.     ret
  630. _pdclose    endp
  631.  
  632. ; int pdaccess(char *type, typelen, int *handle)
  633. ; Register access for packet TYPE with the Packet Driver or ODI
  634. ; Provides a handle for the TYPE.
  635. ; Returns 1 for success, 0 for failure.
  636.     public    _pdaccess
  637. _pdaccess proc    near
  638.     push    bp
  639.     mov    bp,sp
  640.     push    es
  641.     push    si
  642.     push    di
  643.     push    ds
  644.     push    es
  645.     mov    ax,dgroup        ; set up data segment addressibility
  646.     mov    ds,ax
  647.     mov    al,if_class        ; interface class (frame)
  648.     mov    bx,if_type        ; interface type (vendor)
  649.     mov    dl,if_num        ; interface number (board number)
  650.     xor    dh,dh
  651.     mov    si,[bp+4+0]        ; get offset of packet TYPE buffer
  652.     mov    cx,[bp+4+2]        ; typelen (length of buf contents)
  653.     cmp    useodi,0        ; using ODI?
  654.     je    pdacc8            ; e = no
  655.     mov    ax,[si]            ; provide TYPE
  656.     call    odibind            ; Bind to a virtual board
  657.     jc    pdacc1            ; c = fail, error code in AX
  658.     jmp    short pdacc9        ; store handle returned in AX
  659.  
  660. pdacc8:    cmp    if_class,IF_SLIP    ; SLIP?
  661.     jne    pdacc3            ; ne = no
  662.     xor    cx,cx            ; TYPE len = 0 means accept all types
  663. pdacc3:    mov    di,cs            ; ES:DI is our Packet Driver receiver
  664.     mov    es,di
  665.     mov    di,offset pdrcvr    ; local receiver
  666.     mov    ah,pd_access        ; set access
  667.     call    pktdrvr
  668.     jc    pdacc1            ; c = failure
  669. pdacc9:    mov    si,[bp+4+4]        ; offset of handle
  670.     mov    [si],ax            ; return the handle
  671. pdacc1:    mov    ax,1            ; C level status, 1 = success
  672.     jnc    pdacc2            ; nc = success
  673.     xor    ax,ax            ; 0 = failure
  674. pdacc2:    pop    es
  675.     pop    ds
  676.     pop    di
  677.     pop    si
  678.     pop    es
  679.     pop    bp
  680.     ret
  681. _pdaccess endp
  682.  
  683. ; int pkt_send(char *buffer, int length)
  684. ; returns 1 on success, 0 on failure
  685. ; Send a packet.
  686.     public    _pkt_send
  687. _pkt_send    proc    near
  688.     push    bp
  689.     mov    bp,sp
  690.     push    es
  691.     push    ds
  692.     push    si
  693.     push    di            ; don't trust lower levels on regs
  694.     push    cx
  695.     mov    ax,dgroup        ; segment of outgoing buffer
  696.     mov    ds,ax            ;  will be DS:SI for Packet Driver
  697.     mov    si,[bp+4+0]        ; buffer's offset (seg is dgroup)
  698.     mov    cx,[bp+4+2]        ; buffer's length
  699.  
  700.     cmp    useodi,0        ; using ODI?
  701.     je    pktsen5            ; e =no, use PD
  702. pktsen3:cmp    ecbx_busy,0        ; is ODI transmit done yet?
  703.     je    pktsen4            ; e = yes, else we can't touch it yet
  704.     mov    bx,PROTSUP_RELINQUISH_CONTROL
  705.     call    lslsupport        ; give time to LSL
  706.     jmp    short pktsen3        ; wait for done
  707. pktsen4:call    odixmt            ; do LSL transmit with DS:SI and CX
  708.     sti                ; interrupts back on (xmt turns off)
  709.     jmp    short pktsen1        ; done (AX non-zero if error)
  710. ; Note that checking for transmission errors is not readily done with async
  711. ; sending, so we cross our fingers and hope for the best.
  712.                     ;
  713.                     ; Packet Driver sending
  714. pktsen5:mov    ah,pd_send        ; send packet (buffer = ds:si)
  715.     call    pktdrvr            ; invoke Packet Driver
  716.                     ; common exit
  717. pktsen1:mov    ax,1            ; return C level success (1)
  718.     jnc    pktsen2            ; nc = success
  719.     xor    ax,ax            ; else C level failure (0)
  720. pktsen2:pop    cx
  721.     pop    di
  722.     pop    si
  723.     pop    ds
  724.     pop    es
  725.          pop    bp
  726.     ret
  727. _pkt_send endp
  728.  
  729. ; Our Packet Driver receiver, far called only by the Packet Driver and our
  730. ; local ODI code (odircvr and odircmp).
  731. ; Packet buffer linked list -
  732. ;    each link is    db    flag    ; 1 = free, 2 = in use, 4 = allocated
  733. ;                    ;  but not in use yet, 0 = end of buf
  734. ;                    ; 8 = read but not freed.
  735. ;            db    _pktwnum ; sequential number of pkt written
  736. ;            dw    count    ; length of data field
  737. ;            db count dup (?) ; the allocated data field
  738. ;    The head of the chain has a link like all others.
  739. ;    The end of the chain has a link with flag == 0 and count = -BUFISZE
  740. ;    to point to the beginning of the buffer (circular).
  741. ;    Packet buffer garbage collection is done after the PD transfers a
  742. ;    buffer to the application, and does so by relinking adjacent free
  743. ;    blocks.
  744. ; _pktbuf_wrote is used to remember the link where the last write occurred
  745. ; and should be initialized to the tail link to point the next write to
  746. ; the beginning of the buffer.
  747. ; The Packet Driver and our ODI routines call this first with AX = 0 to 
  748. ; obtain a buffer pointer in ES:DI from us (0:0 if we refuse the pkt) with 
  749. ; CX = packet size, and again later with AX = 1 to post completion.
  750.  
  751. pdrcvr    proc    far            ; dummy Packet Driver receiver
  752.     or    ax,ax            ; kind of upcall from PD
  753.     jz    pdrcvr1            ; z = first, get-a-buffer
  754.     ; Second upcall, packet has xfered, DS:SI set by caller to buffer
  755.     pushf                ; save interrupt status
  756.     cli                ; interrupts off
  757.      push    ds
  758.     push    si
  759.     push    bx
  760.     push    ax            ; assume DS:SI is one of our buffers
  761.     mov    ax,DGROUP
  762.     mov    ds,ax            ; set ds to our data segment
  763.     or    si,si            ; is it legal (from first upcall)?
  764.     jz    pdrcvr11        ; z = no, ignore this call
  765.     sub    si,linksize        ; backup to link info
  766.     cmp    byte ptr [si].flag,4    ; is this buffer allocated (4)?
  767.     jne    pdrcvr8            ; ne = no, do cleanups and quit
  768.     mov    byte ptr [si].flag,2    ; flag = 2 for buffer is now ready
  769.     mov    si,pktbufoff         ; start of packet buffer
  770.                     ; join contiguous free links
  771. pdrcvr8:mov    al,[si].flag        ; flags byte
  772.     cmp    al,1            ; is link free?
  773.     jne    pdrcvr10        ; ne = no, look for a free link
  774. pdrcvr9:mov    bx,[si].count        ; count (length) of this link
  775.     mov    al,[bx+si+linksize].flag; flag of following link
  776.     cmp    al,1             ; is next link free?
  777.     jne    pdrcvr10        ; ne = no, look for free link
  778.     mov    ax,[bx+si+linksize].count ; count taken from next link
  779.     add    ax,linksize        ;  plus the next link's info field
  780.     add    [si].count,ax        ; add it to this count (merge links)
  781.     jmp    short pdrcvr9        ; re-examine this new longer link
  782.  
  783. pdrcvr10:or    al,al            ; end of list?
  784.     jz    pdrcvr11        ; z = yes
  785.     add    si,[si].count        ; look at next link (add count)
  786.     add    si,linksize        ; and link info
  787.     jmp    short pdrcvr8        ; keep looking
  788. pdrcvr11:pop    ax
  789.     pop    bx
  790.     pop    si
  791.     pop    ds
  792.     popf
  793.     ret                ; yes, quit
  794.  
  795. pdrcvr1:pushf
  796.     cli
  797.     push    ds            ; First upcall, provide buffer ptr
  798.     push    dx            ; return buffer in ES:SI
  799.     push    cx
  800.     mov    di,DGROUP        ; get local addressibility
  801.     mov    ds,di
  802.     mov    es,di            ; packet buffer is in same group
  803.     mov    di,_pktbuf_wrote    ; where last write occurred
  804.     or    di,di            ; NULL?
  805.     jz    pdrcvr4            ; z = yes, write nothing
  806.     mov    dl,100            ; retry counter, breaks endless loops
  807.     cmp    [di].flag,1        ; is this link free?
  808.     je    pdrcvr5            ; e = yes, use it
  809.  
  810. pdrcvr2:add    di,[di].count        ; point at next link (add count and
  811.     add    di,linksize        ;  link overhead)
  812.     dec    dl            ; loop breaker count down
  813.     or    dl,dl
  814.     jz    pdrcvr4            ; z = in an endless loop, exit
  815.     cmp    [di].flag,1        ; is this link free (1)?
  816.     je    pdrcvr5            ; e = yes, setup storage
  817.     cmp    di,_pktbuf_wrote     ; have we come full circle?
  818.     jne    pdrcvr2            ; ne = no, keep looking
  819. pdrcvr4:pop    cx
  820.     pop    dx
  821.     pop    ds            ; failure or buffer not available (0)
  822.     xor    ax,ax            ; return what we received in ax
  823.     xor    di,di            ; return ES:DI as null to reject
  824.     mov    es,di
  825.     popf
  826.     ret
  827.                     ; this link is free
  828. pdrcvr5:cmp    cx,ETH_MSS+40+12 +20    ; LARGEST PACKET WE ACCEPT
  829.     ja    pdrcvr4            ; a = too large, decline it
  830.     add    cx,2            ; defense for 8/16 bit xfr mistakes
  831.     mov    ax,[di].count        ; length of available data space
  832.     cmp    ax,cx            ; cx is incoming size, enough space?
  833.     jl    pdrcvr2            ; l = no, go to next link
  834.     mov    [di].flag,4        ; mark link flag as being alloc'd (4)
  835.     mov    dh,_pktwnum         ; write pkt sequencer number
  836.     mov    [di].bufnum,dh        ; store in buffer, permits out of
  837.     inc    _pktwnum        ;  temporal order deliveries
  838.     mov    _pktbuf_wrote,di    ; remember where we wrote last
  839.     sub    ax,cx            ; allocated minus incoming packet
  840.     cmp    ax,60+linksize        ; enough for new link and miminal pkt?
  841.     jl    pdrcvr6            ; l = not enough for next pkt
  842.     mov    [di].count,cx        ; update space really used
  843.     push    di            ; save this link pointer
  844.     add    di,linksize        ; plus current link info
  845.     add    di,cx            ; plus space used = new link point
  846.     sub    ax,linksize        ; available minus new link info
  847.     mov    [di].flag,1        ; mark new link as free (1)
  848.     mov    [di].count,ax        ; size of new free data area
  849.     pop    di            ; return to current link
  850. pdrcvr6:add    di,linksize        ; point at data portion
  851. pdrcvr7:xor    ax,ax            ; return what we received in ax
  852.     pop    cx
  853.     pop    dx
  854.     pop    ds            ; ES:DI is the pkt buffer address
  855.     popf                ; CX is size of requested buffer
  856.     ret
  857. pdrcvr    endp
  858.  
  859.  
  860. ; Begin Novell ODI support routines
  861. ; Note that while we use Ethernet_II (6 dest, 6 source, 2 TYPE bytes) to/from
  862. ; internal consumers the frame format to/from ODI is in the hands of ODI.
  863. ; Hopefully this will permit TCP/IP operation over all supported frame types.
  864. ; ARP/RARP packets are sized to the frame in use.
  865. ;
  866. ; Check for LSL presence, and if present then get entry points.
  867. ; Returns carry set if failure, else carry clear.
  868. ; This procedure is closely modeled upon the Novell example.
  869. odichk    proc    near
  870.     cmp    useodi,0        ; already inited?
  871.     je    odichk0            ; e = no
  872.     clc
  873.     ret
  874. odichk0:push    es
  875.     mov    ah,getintv        ; get LSL via multiplexer interrupt
  876.     mov    al,2fh            ; vector 2fh
  877.     int    dos            ; to es:bx
  878.     mov    ax,es
  879.     mov    cx,ax
  880.     or    cx,bx            ; check if vector exists
  881.     jnz    odichk1            ; nz = yes
  882.     pop    es
  883.     stc
  884.     ret
  885.  
  886. odichk1:mov    ax,0c000h        ; look at multiplexer slots c0 et seq
  887.     push    si
  888.     push    di
  889. odichk2:push    ax
  890.     int    2fh
  891.     cmp    al,0ffh            ; is slot in use?
  892.     pop    ax
  893.     je    odichk4            ; e = yes, check for LSL being there
  894. odichk3:inc    ah            ; next slot
  895.     or    ah,ah            ; wrapped?
  896.     jnz    odichk2            ; nz = no, keep looking
  897.     pop    di
  898.     pop    si
  899.     pop    es
  900.     stc                ; not found, fail
  901.     ret
  902.  
  903. odichk4:mov    di,si            ; es:si should point to "LINKSUP$"
  904.     mov    si,offset DGROUP:lslsig    ; expected signature
  905.     mov    cx,lslsiglen        ; length
  906.     repe    cmpsb            ; check for signature
  907.     jne    odichk3            ; ne = no match, try next Int 2fh slot
  908.     mov    word ptr lslinit,bx    ; found entry, save init entry point
  909.     mov    ax,es            ;  returned in es:bx
  910.     mov    word ptr lslinit+2,ax
  911.     mov    ax,ds
  912.     mov    es,ax            ; get LSL main support/service addrs
  913.     mov    si,offset DGROUP:lslsupport ; address of LSL entry point array
  914.     mov    bx,2            ; request support/service entry points
  915.         ; fills in far addresses of lslsupport and lslservice routines
  916.     call    lslinit            ; call LSL initialization routine
  917.     pop    di
  918.     pop    si
  919.     pop    es
  920.     clc                ; success
  921.     ret
  922. odichk    endp
  923.  
  924. ; Bind a protocol TYPE to an ODI virtual board.
  925. ; Enter with TYPE (big endian/network order) in AX.
  926. ; Packet reception begins immediately upon a successful bind.
  927. ; Uses NET.CFG if information is available.
  928. ; Obtain StackID (our ident to the LSL), ProtID (ident of LSL's decoder),
  929. ; and boardnumber (the logical board), then bind to start reception. Do for
  930. ; one of our protocols.
  931. ; Returns PD handle (TYPE) in AX and carry clear upon success, else carry set.
  932. odibind proc    near
  933.     push    ax
  934.     push    bx
  935.     push    si
  936.     push    di
  937.     push    es
  938.     mov    bx,DGROUP
  939.     mov    es,bx
  940.     cmp    ax,ip_type            ; IP, 0x0008h?
  941.     jne    odibind1            ; ne = no
  942.     mov    ax,offset DGROUP:ip_string    ; put IP string in request
  943.     mov    bx,offset ip_rcvr        ; set address of receiver esr
  944.     mov    di,offset DGROUP:ip_stackid    ; set address of stackid struc
  945.     jmp    short odibind3
  946.  
  947. odibind1:cmp    ax,arp_type            ; ARP, 0x0608?
  948.     jne    odibind2            ; ne = no
  949.     mov    ax,offset DGROUP:arp_string
  950.     mov    bx,offset arp_rcvr
  951.     mov    di,offset DGROUP:arp_stackid
  952.     jmp    short odibind3
  953.  
  954. odibind2:cmp    ax,rarp_type            ; RARP, 0x3580?
  955.     je    odibind2a            ; e = yes
  956.     jmp    odibindx            ; ne = no, fail
  957. odibind2a:mov    ax,offset DGROUP:rarp_string
  958.     mov    bx,offset rarp_rcvr
  959.     mov    di,offset DGROUP:rarp_stackid
  960.  
  961. odibind3:mov    word ptr registerstk.StackNamePtr,ax ; insert ptr to string
  962.     mov    word ptr registerstk.StackReceiveHandler,bx  ; setup esr addr
  963.  
  964. ; Note: to use Prescan or Default registrations delete StackNamePtr & StackID.
  965. ; StackID is not used with these latter methods, and their reception begins
  966. ; at registration rather than at bind (so this area would be redesigned).
  967.  
  968.     mov    bx,PROTSUP_REGISTER_STACK ; register the protocol by name
  969.     mov    si,offset DGROUP:registerstk ; registration form pointer
  970.     push    di            ; save ptr to xxx_stackid storage
  971.     call    lslsupport        ; call LSL with the address in es:si
  972.     pop    di
  973.     jz    odibind3a        ; z = success
  974.     jmp    odibindx        ; nz = failure
  975. odibind3a:mov    [di].pstack,bx        ; save returned StackID (LSL's handle
  976.                     ;  for our protocol stack)
  977.     cmp    readnetcfg,0        ; have read NET.CFG for BIND info?
  978.     jne    odibind4        ; ne = yes
  979.     mov    useboard,-1        ; clear board-to-use word
  980.     call    getbind            ; find Kermit's bind board in NET.CFG
  981.     inc    readnetcfg        ; say have read the file
  982.     cmp    word ptr bdname,256*'#'+2 ; is board name #<digit>?
  983.     jne    odibind4        ; ne = no, assume regular driver name
  984.     mov    al,bdname+2        ; get ascii digit
  985.     sub    al,'1'            ; remove ascii bias (external=1 based)
  986.     xor    ah,ah            ;  but we are zero based internally
  987.     cmp    al,8            ; arbitrary limit of 8 boards
  988.     ja    odibind4        ; a = out of range, ignore value
  989.     mov    useboard,ax        ; and make this the board number
  990.     mov    bdname,0        ; and don't use bdname as a name
  991. odibind4:mov    [di].pboard,0        ; assume board zero to start loop
  992.     mov    ax,useboard        ; board to be used, if any
  993.     or    ax,ax            ; boards 0 and up are legal
  994.     jl    odibind5        ; l = no board found yet, search
  995.     mov    [di].pboard,ax        ; specify board, get ProtID
  996.  
  997. odibind5:mov    bx,PROTSUP_GET_MLID_CTL_ENTRY    ; get MLID control entry
  998.     mov    ax,[di].pboard        ; for this board
  999.     push    di
  1000.     call    lslsupport        ; call LSL for the address to es:si
  1001.     pop    di
  1002.     mov    word ptr mlidcont,si
  1003.     mov    word ptr mlidcont+2,es    ; MLID control routine
  1004.     jz    odibind7        ; z=success, have a board to work with
  1005.     cmp    ax,LSLERR_NO_MORE_ITEMS ; out of items?
  1006.     je    odibind5a        ; e = yes, no more boards
  1007.       cmp    ax,LSLERR_ITEM_NOT_PRESENT ; other boards may exist?
  1008.     je    odibind7        ; e = yes
  1009. odibind5a:jmp    odibindx        ; fail
  1010.  
  1011. odibind7:mov    bx,PROTSUP_GET_PID_PROTNUM_MLIDNUM ; get ProtID from StackID
  1012.     mov    ax,[di].pstack        ; StackID
  1013.     mov    cx,[di].pboard        ;  and assumed board number
  1014.     mov    si,DGROUP
  1015.     mov    es,si            ; set es:di to the ProtID buffer
  1016.     lea    si,[di].pprotid        ;  in our storage slot per protocol
  1017.     push    di            ;
  1018.     call    lslsupport        ; ask LSL for the ProtID string
  1019.     pop    di            ;  to that 6-byte buffer
  1020.     jz    odibind9        ; z = success, found a recognizer
  1021.     cmp    useboard,0        ; has a board been pre-identified?
  1022.     jge    odibind5a        ; ge = yes, so the matchup failed
  1023.     inc    [di].pboard        ; next board
  1024.     jmp    short odibind5        ; keep looking for a board
  1025.  
  1026. odibind9:mov    bx,GET_MLID_CONFIGURATION ; get MLID config ptr to es:si
  1027.     mov    ax,[di].pboard
  1028.     call    mlidcont        ; call MLID control routine
  1029.     jnz    odibindx        ; nz = failure
  1030.     cmp    bdname,0        ; was a board name bound via BIND?
  1031.     je    odibin10        ; e = no, don't check on it
  1032.     push    es            ; save pointer to MLID config table
  1033.     push    di
  1034.     push    si
  1035.     les    di,es:[si].MCardShortName ; get short name of this board
  1036.     lea    si,bdname        ; desired board name string
  1037.     mov    cl,bdname        ; length of desired board name
  1038.     inc    cl            ; include length byte
  1039.     xor    ch,ch
  1040.     cld
  1041.     repe    cmpsb            ; compare  len,string  for both
  1042.     pop    si
  1043.     pop    di
  1044.     pop    es
  1045.     je    odibin10        ; e = found desired board
  1046.     inc    [di].pboard        ; try next board
  1047.     jmp    short odibind5        ; keep looking for the desired board
  1048.  
  1049. odibin10:mov    ax,[di].pboard        ; get current board number
  1050.     mov    useboard,ax        ; remember for next protocol
  1051.     mov    bx,es:[si].MFrameID    ; frame ident, for get_hwd
  1052.     call    get_hwd            ; get hardware specifics
  1053.     or    bx,bx            ; returned _MAC_len, if any
  1054.     jnz    odibin11        ; nz = found match
  1055.     mov    bx,6            ;  else default to 6 bytes
  1056. odibin11:push    es
  1057.     push    si            ; save config pointer
  1058.     lea    si,es:[si].MNodeAddress    ; point to address in config struct
  1059.     push    ds            ; save ds
  1060.     push    di
  1061.     mov    di,offset DGROUP:_eth_addr; where our MAC address is stored
  1062.     mov    ax,ds
  1063.     mov    cx,es
  1064.     mov    es,ax
  1065.     mov    ds,cx
  1066.     cld
  1067.     mov    cx,bx            ; MAC address length, bytes
  1068.     rep    movsb            ; copy MAC address to global array
  1069.     pop    di
  1070.     pop    ds
  1071.     pop    si            ; recover configuration table pointer
  1072.     pop    es
  1073.     mov    tells_len,0        ; presume no lookahead data length
  1074.     test    es:[si].MFlags,Len_Info    ; capas bit for length provided (new)
  1075.     jz    odibin12        ; z = does not provide
  1076.     inc    tells_len        ; say provides length
  1077.  
  1078. odibin12:mov    bx,PROTSUP_BIND_STACK_TO_MLID    ; Bind stack to MLID
  1079.     mov    ax,[di].pstack        ; StackID
  1080.     mov    cx,[di].pboard        ; board number
  1081.     call    lslsupport        ; bind our protocol stack to board
  1082.     jnz    odibindx        ; nz = failure
  1083.     pop    es            ; received packets can interrupt now
  1084.     pop    di
  1085.     pop    si
  1086.     pop    bx
  1087.     pop    ax
  1088.     clc
  1089.     ret
  1090. odibindx:pop    es
  1091.     pop    di
  1092.     pop    si
  1093.     pop    bx
  1094.     pop    ax
  1095.     stc                ; say failure
  1096.     ret
  1097. odibind endp
  1098.  
  1099. ; Worker for odibind. Find NET.CFG, extract name of board driver from pair of
  1100. ; lines reading as below (Protocol must be in column 1, bind must be indented)
  1101. ; Protocol Kermit            Kermit's main section header
  1102. ;   bind  <board_driver_name>        indented, without the <> signs
  1103. ;or
  1104. ; Protocol Kermit
  1105. ;   bind #<digit>            selects DOS driver load order (from 1)
  1106. ;
  1107. ; Examples -
  1108. ; Protocol Kermit
  1109. ;   bind exos
  1110. ;or
  1111. ; Protocol Kermit
  1112. ;   bind #2
  1113. ;            and elsewhere there is the board driver section:
  1114. ; Link Driver exos
  1115. ;
  1116. ; If found put the board driver name in array bdname, as length byte, string,
  1117. ; then a null. If not found make length byte bdname be zero. We treat NET.CFG
  1118. ; as case insensitive.
  1119. ; Unless we use the special Kermit section then LSL will assign to us the
  1120. ; first board loaded by DOS supporting the frame kind of our protocol.
  1121. ; Link Driver section line "Protocol name type frame"  simply associates a
  1122. ; frame kind with the name and type, but not with a board. L.D. section line
  1123. ; frame <frame kind> attaches that frame kind to the board, if it fits.
  1124. ; Kermit uses "name" in the above line to pinpoint a protocol, not a board.
  1125. getbind    proc    near
  1126.     mov    bdname,0        ; clear board name length
  1127.     push    ds
  1128.     mov    bx,GENSERV_GET_NETCFG_PATH ; get fully formed NET.CFG name
  1129.     call    lslservice        ;  from LSL general services to ds:dx
  1130.     jz    getbin1            ; z = success
  1131.     pop    ds            ; fail
  1132.     ret
  1133. getbin1:mov    ah,fopen        ; open file NET.CFG
  1134.     mov    al,40h            ;  for reading, deny none
  1135.     int    dos            ; returns file handle in ax
  1136.     pop    ds
  1137.     mov    temp,ax            ; save handle for getbyte
  1138.     jnc    getbin2            ; nc = success
  1139.     ret                ; carry set for failure
  1140.  
  1141. getbin2:mov    bx,1            ; subscript, at start of a line
  1142.  
  1143. getbin3:call    getbyte            ; read a byte, uppercased
  1144.     jnc    getbin4            ; nc = success
  1145.     ret                ; c = end of file
  1146. getbin4:cmp    protword[bx],al        ; compare to "PROTOCOL"
  1147.     jne    getbin5            ; ne = failure, scan for end of line
  1148.     inc    bx
  1149.     cmp    bl,protword        ; length, matched all bytes?
  1150.     jbe    getbin3            ; be = no, match more
  1151.     jmp    short getbin6        ; ae = yes, next phrase
  1152.     ret                ; fail out 
  1153.  
  1154. getbin5:cmp    al,LF            ; end of a line?
  1155.     je    getbin2            ; e = yes, scan for PROTOCOL again
  1156.     call    getbyte
  1157.     jnc    getbin5            ; keep consuming line material
  1158.     ret                ; fail out at end of file
  1159.                     ; Short Name following "PROTOCOL"
  1160. getbin6:call    getbyte            ; get separator char, discard
  1161.     jnc    getbin7
  1162.     ret                ; c = eof
  1163. getbin7:call    getbyte            ; read short name of protocol
  1164.     jnc    getbin7a        ; nc = text
  1165.     ret                ; return on eof
  1166. getbin7a:cmp    al,' '            ; white space?
  1167.     jbe    getbin7            ; be = yes, stay in this state
  1168.     mov    bx,1            ; subscript
  1169. getbin8:cmp    psname[bx],al        ; compare to our protocol short name
  1170.     jne    getbin5            ; ne = failure, scan for end of line
  1171.     cmp    bl,psname        ; matched all bytes?
  1172.     jae    getbin9            ; ae = yes, next phrase
  1173.     inc    bx
  1174.     call    getbyte            ; get next byte to match
  1175.     jnc    getbin8            ; nc = not eof yet
  1176.     ret
  1177.  
  1178. getbin9:call    getbyte            ; go to next line, enforce whitespace
  1179.     jc    getbin20        ; c = eof
  1180.     cmp    al,LF            ; end of a line?
  1181.     jne    getbin9            ; ne = no, scan for end of line
  1182.     call    getbyte            ; look for whitespace
  1183.     jc    getbin20        ; c = eof
  1184.     cmp    al,' '            ; required whitespace?
  1185.     ja    getbin5            ; a = no, start over
  1186.  
  1187. getbin10:call    getbyte            ; look for keyword "BIND"
  1188.     jc    getbin20
  1189.     cmp    al,' '            ; white space?
  1190.     jbe    getbin10        ; be = yes, stay in this state
  1191.     mov    bx,1            ; subscript
  1192. getbin11:cmp    bindword[bx],al        ; compare to "BIND"
  1193.     jne    getbin5            ; ne = failure, start over
  1194.     cmp    bl,bindword        ; matched all bytes?
  1195.     jae    getbin12        ; ae = yes, next phrase
  1196.     call    getbyte
  1197.     jc    getbin20        ; c = eof
  1198.     inc    bx
  1199.     jmp    short getbin11        ; keep reading
  1200.  
  1201. getbin12:call    getbyte            ; skip white space before board name
  1202.     jc    getbin20            
  1203.     cmp    al,' '            ; white space?
  1204.     jbe    getbin12        ; be = yes, skip it
  1205.  
  1206. getbin13:mov    bl,bdname        ; board name, length byte, starts at 0
  1207.     xor    bh,bh
  1208.     inc    bl
  1209.     mov    bdname,bl        ; update length of board driver name
  1210.     xor    ah,ah            ; get a null
  1211.     mov    word ptr bdname[bx],ax    ; store as board short name,null
  1212.     cmp    bx,15            ; legal limit on short name?
  1213.     jbe    getbin14        ; be = ok
  1214.     mov    bdname,ah        ; illegal, clear board name length
  1215.     jmp    getbin15        ; and quit
  1216. getbin14:call    getbyte
  1217.     jc    getbin20        ; reached eof, is ok
  1218.            cmp    al,' '            ; usable text?
  1219.     ja    getbin13        ; a = yes, else stop storing name
  1220. getbin15:call    getbyte            ; read to eof so file is closed
  1221.     jnc    getbin15        ; nc = got a char
  1222. getbin20:ret    
  1223. getbind    endp
  1224.  
  1225. ; Worker for getbind. Delivers one byte per call from NET.CFG, upper cased.
  1226. ; Returns carry set and NET.CFG file closed at end of file.
  1227. ; Temp has NET.CFG file handle, tempb is our one byte buffer for disk i/o.
  1228. getbyte    proc    near
  1229.     mov    dx,offset tempb        ; ds:dx points to start of buffer
  1230.     mov    ah,fread        ; read from file to buffer
  1231.     mov    cx,1            ; this many bytes
  1232.     push    bx
  1233.     mov    bx,temp            ; get file handle
  1234.     int    dos
  1235.     pop    bx
  1236.     jc    getbyt2            ; c = failure
  1237.     cmp    ax,1            ; got the single byte?
  1238.     jb    getbyt2            ; b = no, failure
  1239.     mov    al,tempb        ; return read byte
  1240.     cmp    al,'z'            ; in lower case range?
  1241.     ja    getbyt1            ; a = no
  1242.     cmp    al,'a'            ; in lower case range?
  1243.     jb    getbyt1            ; b = no
  1244.     and    al,not 20h        ; lower to upper case
  1245. getbyt1:clc                ; carry clear for success
  1246.     ret                ; return char in AL
  1247. getbyt2:push    bx
  1248.     mov    bx,temp            ; file handle
  1249.     mov    ah,fclose        ; close the file
  1250.     int    dos
  1251.     pop    bx
  1252.     stc                ; say EOF or other failure
  1253.      ret
  1254. getbyte    endp
  1255.  
  1256. ; Worker for odibind.
  1257. ; Enter with BX holding Novell frame type from the MLID configuration table. 
  1258. ; Set _arp_hardware and _MAC_len and return BX holding _MAC_len value, else 
  1259. ; if frame is not supported return BX = 0. These two values are needed by the
  1260. ; ARP functions. This list searching method is to accomodate the ever 
  1261. ; expanding quantity of frame types appearing with ODI; we deal with those we
  1262. ; understand (sic).
  1263. get_hwd    proc    near
  1264.     push    es
  1265.     push    di
  1266.     mov    ax,DGROUP
  1267.     mov    es,ax
  1268.     mov    al,bl            ; get frame value (MLID config)
  1269.     xor    bx,bx            ; prepare no-match return value
  1270.     mov    di,offset frame_type    ; list to search
  1271.     mov    cx,num_frames        ; number of elements in the list
  1272.     cld
  1273.     repne    scasb            ; byte search
  1274.     jne    get_hwd1        ; ne = no match, fail
  1275.     sub    di,offset frame_type+1    ; make di be an index along the list
  1276.     mov    al,hardware_type[di]    ; ARP/RARP hardware type ident
  1277.     xor    ah,ah
  1278.     xchg    ah,al            ; put in network order (big endian)
  1279.     mov    _arp_hardware,ax    ; hardware type for ARP/RARP pkts
  1280.     mov    bl,frame_adlen[di]    ; array of MAC lengths for frame types
  1281.     xor    bh,bh
  1282.     mov    _MAC_len,bx        ; save MAC address length (1..6 bytes)
  1283. get_hwd1:pop    di
  1284.     pop    es
  1285.     ret                ; return _MAC_len in BX
  1286. get_hwd    endp
  1287.  
  1288. ; Unbind a protocol TYPE from an ODI virtual board
  1289. ; Enter with protocol TYPE (net order) in AX, return carry set if failure.
  1290. ; The TYPE is used as our handle to the application.
  1291. ; Prescan and Default methods call lslsupport with the board number in AX 
  1292. ; rather than StackID and use matching PROTSUP_DEREGISTER_* code.
  1293. odiunbind proc    near
  1294.     cmp    ax,ip_type        ; IP, 0x0008h?
  1295.     jne    odiunb1            ; ne = no
  1296.     mov    ax,ip_stackid.pstack    ; StackID
  1297.     jmp    short odiunb3
  1298.  
  1299. odiunb1:cmp    ax,arp_type        ; ARP, 0x0608?
  1300.     jne    odiunb2            ; ne = no
  1301.     mov    ax,arp_stackid.pstack
  1302.     jmp    short odiunb3
  1303.  
  1304. odiunb2:cmp    ax,rarp_type        ; RARP, 0x3580?
  1305.     jne    odiunb4            ; ne = no
  1306.     mov    ax,rarp_stackid.pstack
  1307.  
  1308. odiunb3:mov    bx,PROTSUP_DEREGISTER_STACK ; deregister stack (StackID in AX)
  1309.     call    lslsupport        ; stops reception now
  1310.     jnz    odiunb4            ; nz = failure
  1311.     clc                ; success
  1312.     ret
  1313. odiunb4:stc                ; failure
  1314.     ret
  1315. odiunbind endp
  1316.  
  1317. ; ODI receive interrupt handler, for use only by the LSL.
  1318. ; Called with DS:DI pointing to lookahead structure, interrupts are off.
  1319. ; Returns ES:SI pointing at ECB, AX = 0 if we want pkt, AX = 8001h if decline.
  1320. ; There are three of these, one each for IP, ARP, and RARP. All have the
  1321. ; same calling convention and all jump to odircv to do the real work. 
  1322. ; The length of the arriving packet is available if the MLID supports the
  1323. ; new (mid-May 1992) capability, our "tells_len"; otherwise we make an
  1324. ; intelligent guess based on the protocol header. These entry points can be 
  1325. ; called multiple times before receive-completion, and likely will be, so we
  1326. ; use several ecb's to accept requests.
  1327. ip_rcvr    proc    far
  1328.     push    bx
  1329.     push    cx
  1330.     push    di
  1331.     push    ds
  1332.     mov    cx,ds            ; DS:DI from LSL
  1333.     mov    es,cx            ; use ES for LSL items
  1334.     mov    ax,DGROUP        ; set DS to our data segment
  1335.     mov    ds,ax
  1336.     push    es
  1337.     push    si
  1338.     cmp    tells_len,0        ; have data length available?
  1339.     je    ip_rec1            ; e = no
  1340.     les    si,es:[di].DataLookAheadDataSize ; ptr to what it says
  1341.     mov    cx,word ptr es:[si]    ; get length of data field
  1342.     add    cx,4            ; for overzealous board transfers
  1343.     pop    si
  1344.     pop    es
  1345.     jmp    far ptr odircv
  1346.  
  1347. ip_rec1:les    si,es:[di].LookAheadPtr    ; point at data lookahead ptr
  1348.     mov    cx,word ptr es:[si+2]    ; IP pkt header, length word
  1349.     cmp    byte ptr es:[si],45h    ; validate IP pkt kind (ver/hlen)?
  1350.     pop    si
  1351.     pop    es
  1352.     jne    ip_rcvr1        ; ne = invalid, decline
  1353.     xchg    ch,cl            ; net to local order
  1354.     add    cx,14+8            ; our MAC level addressing + 8 safety
  1355. ip_rec2:mov    ax,ip_stackid.pstack    ; StackID for ecb structure
  1356.     mov    rcvtype,ip_type        ; store protocol TYPE int
  1357.     cmp    cx,ETH_MSS+40+12+20    ; LARGEST ACCEPTABLE PKT
  1358.     jbe    odircv            ; be = acceptable length
  1359.  
  1360. ip_rcvr1:add    pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
  1361.     adc    pstats.PIgnoredRxPackets+2,0
  1362.     mov    ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
  1363.     or    ax,ax            ; set Z flag to match AX
  1364.     pop    ds
  1365.     pop    di
  1366.     pop    cx
  1367.     pop    bx
  1368.     ret
  1369. ip_rcvr    endp
  1370.  
  1371. ; RARP protocol receive service routine, similar to ip_rcvr.
  1372. rarp_rcvr proc    far
  1373.     push    bx
  1374.     push    cx
  1375.     push    di
  1376.     push    ds
  1377.     mov    cx,ds            ; DS:SI from LSL
  1378.     mov    es,cx
  1379.     mov    ax,DGROUP        ; set DS to our data segment
  1380.     mov    ds,ax
  1381.     mov    ax,arp_stackid.pstack    ; StackID for ecb structure
  1382.     mov    rcvtype,rarp_type    ; store protocol TYPE int
  1383.     jmp    short arp_common    ; do ARP/RARP common code
  1384. rarp_rcvr endp
  1385.  
  1386. ; ARP protocol receive service routine, similar to ip_rcvr.
  1387. arp_rcvr proc    far
  1388.     push    bx
  1389.     push    cx
  1390.     push    di
  1391.     push    ds
  1392.     mov    cx,ds            ; DS:SI from LSL
  1393.     mov    es,cx
  1394.     mov    ax,DGROUP        ; set DS to our data segment
  1395.     mov    ds,ax
  1396.     mov    ax,arp_stackid.pstack    ; StackID for ecb structure
  1397.     mov    rcvtype,arp_type    ; store protocol TYPE int
  1398.  
  1399. arp_common:                ; common code for ARP/RARP
  1400.     push    es
  1401.     push    si
  1402.     cmp    tells_len,0        ; have data length available?
  1403.     je    arp_com1        ; e = no
  1404.     les    si,es:[di].DataLookAheadDataSize ; ptr to what it says
  1405.     mov    cx,word ptr es:[si]    ; get length of data field
  1406.     add    cx,4            ; for overzealous board transfers
  1407.     pop    si
  1408.     pop    es
  1409.     jmp    short odircv
  1410.  
  1411. arp_com1:les    si,es:[di].LookAheadPtr    ; point at lookahead ptr for Data
  1412.     mov    cx,word ptr es:[si+4]    ; ARP/RARP pkt header, length bytes
  1413.     add    cl,ch            ; add HA and IP address lengths
  1414.     xor    ch,ch
  1415.     add    cl,cl            ; for host and target
  1416.     adc    ch,0
  1417.     add    cx,8            ; plus ARP/RARP main header
  1418.     cmp    word ptr es:[si+2],ip_type ; ARP/RARP Protocol type of IP?
  1419.     pop    si
  1420.     pop    es
  1421.     jne    ip_rcvr1        ; ne = invalid, decline
  1422.     ; fall through to odircv
  1423. arp_rcvr endp
  1424.  
  1425. ; General worker for ip_rcvr, arp_rcvr, rarp_rcvr. These are invoked by the
  1426. ; LSL when their kind of packet arrives. This module creates the ECB and
  1427. ; dispatches it to the LSL. Operating at LSL interrupt level.
  1428. ; ES:DI is ptr to ODI Lookahead structure, DS is our data seg.
  1429. ; AX is stackid for invoked protocol kind, CX is (guessed) pkt length overall.
  1430. ; Rcvtype is the current invoked protocol TYPE (0008h is IP etc).
  1431. ; When done store in the ecb's protocolws array:
  1432. ;    dw    protocol TYPE (0008h for IP etc)
  1433. ;    dw    subscript of this ecbr item (for use by odircmp)
  1434. ;    dw    <unused>,<unused>
  1435. ; Return ES:SI as pointer to a free ecb and AX = 0 to accept pkt, else
  1436. ; return AX = 8001h to decline pkt (and to ignore ES:SI). Set Z flag to
  1437. ; match AX value.
  1438. odircv    proc    far
  1439.     add    pstats.PtotalRxPackets,1 ; update ODI statistics counter
  1440.     adc    pstats.PtotalRxPackets+2,0
  1441.     push    ax
  1442.     push    cx
  1443.     mov    cx,ecbr_qty        ; number of receive ecb's
  1444.     xor    bx,bx            ; find a free ecb for this packet
  1445. odircv8:cmp    ecbr_busy[bx],0        ; is this ECB free?
  1446.     jne    odircv9            ; ne = no, try next
  1447.     mov    ecbr_num,bx        ; remember index for end of proc
  1448.     mov    ax,size ecbstruct    ; size of an ecb
  1449.     mul    bl            ; times this subscript
  1450.     add    ax,offset DGROUP:ecbr    ; start of receive ecbs
  1451.     mov    bx,ax            ; offset of free ecb
  1452.     pop    cx
  1453.     pop    ax
  1454.     jmp    short odircv2        ; use ds:[bx] for address of ecb
  1455. odircv9:inc    bx            ; next byte in busy array
  1456.     loop    odircv8
  1457.     pop    cx            ; failed to find a free ecbr
  1458.     pop    ax
  1459.  
  1460. odircv1:add    pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
  1461.     adc    pstats.PIgnoredRxPackets+2,0
  1462.     mov    ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
  1463.     or    ax,ax            ; set Z flag for ODI
  1464.     pop    ds
  1465.     pop    di
  1466.     pop    cx
  1467.     pop    bx
  1468.     ret
  1469.                     ; ds:[bx] is ptr to a free ecbr
  1470. odircv2:mov    [bx].stackid,ax        ; StackID from odircv entry points
  1471.     mov    ax,es:[di].LBoardNum    ; boardnum from ProtocolID lookahead
  1472.     mov    [bx].boardnum,ax    ; store in ecbr
  1473.     mov    ax,rcvtype        ; get TYPE from odircv entry points
  1474.     mov    word ptr [bx].protocolws,ax    ; save TYPE for odircmp
  1475.     mov    ax,ecbr_num        ; ecbr index
  1476.     mov    word ptr [bx].protocolws+2,ax    ; save index for odircmp
  1477.  
  1478.     cmp    cx,64            ; min packet for Ethernet + 4 spare
  1479.     jae    odircv3            ; ae = no padding needed here
  1480.     mov    cx,64            ; padded min pkt plus 4 spare bytes
  1481. odircv3:push    bx            ; get a buffer of length CX bytes
  1482.     xor    ax,ax            ; set AX = 0 for PD "get buf" call
  1483.     call    pdrcvr            ; use PD buffer allocator code
  1484.     pop    bx            ; ES:DI = buffer pointer, CX = length
  1485.     mov    ax,es
  1486.     or    ax,di            ; check for refused pkt (es:di = NULL)
  1487.     jz    odircv1            ; z = pkt refused (no buffer space)
  1488.     add    di,6+6+2        ; skip our MAC header for ecb use
  1489.     sub    cx,6+6+2        ; less same length for MLID
  1490.     mov    [bx].frag1addr,di    ; offset of buffer which MLID sees
  1491.     mov    ax,es
  1492.     mov    [bx].frag1addr+2,ax    ; seg of buffer
  1493.     mov    [bx].datalen,cx        ; length of buffer for MLID/LSL use
  1494.     mov    [bx].frag1len,cx    ; ditto
  1495.     mov    ax,DGROUP        ; segment of our ecb's
  1496.     mov    es,ax
  1497.     mov    si,bx            ; return ES:SI pointing to ECB
  1498.     mov    bx,ecbr_num        ; get ecbr index
  1499.     mov    ecbr_busy[bx],1        ; mark this ecbr as busy
  1500.     pop    ds
  1501.     pop    di
  1502.     pop    cx
  1503.     pop    bx
  1504.     xor    ax,ax            ; return AX = 0 to accept
  1505.     ret
  1506. odircv    endp
  1507.  
  1508. ; ODI receive-complete call-back routine for use only by the LSL.
  1509. ; Enter with ES:SI pointing at ECB, interrupts are off.
  1510. ; Returns nothing.
  1511. ; There is no guarantee that this routine will be called in the sequence
  1512. ; which packets arrived, so we carry the bookkeeping in the delivered ECB:
  1513. ; TYPE is for Ethernet_II struct results, ecbr_busy is don't touch interlock.
  1514. ; Note that we have to construct our own "destination" MAC address.
  1515. ; es:[si].status is 0 (success), 8006h (buffer overrun), 8007h (canceled).
  1516. ; StackID field is from LSL, and it's 0ffffh if using Prescan, and undefined
  1517. ; if using Default. The manual says LSL, but not MLID, calls are ok in here.
  1518. odircmp    proc    far
  1519.     push    ds
  1520.     push    ax
  1521.     push    bx
  1522.     push    cx
  1523.     mov    ax,DGROUP
  1524.     mov    ds,ax            ; set ds to our data segment
  1525.     cmp    es:[si].frag1addr+2,0    ; segment of pkt being confirmed
  1526.     je    odircmp5        ; e = illegal, ignore this call
  1527.     cmp    es:[si].status,0     ; check ECB status for failure
  1528.     je    odircmp1        ; e = success
  1529.     mov    es:[si].protocolws,0    ; write TYPE of 0 to permit queueing
  1530. odircmp1:push    di            ; put dest,src,TYPE into pkt buffer
  1531.     push    es
  1532.     push    si            ; save ecbr's si
  1533.     mov    cl,byte ptr es:[si].driverws ; kind of destination, from LSL
  1534.     mov    di,es:[si].frag1addr    ; start of our pkt buffer + 6+6+2
  1535.     sub    di,6+6+2        ; back to start, for our MAC header
  1536.     push    ds            ; WATCH this, presumes ES == DS!
  1537.     pop    es            ; set ES to DS (where pkt buffer is)
  1538.     mov    si,offset DGROUP:_eth_addr ; our hardware address
  1539.     cmp    cl,ECB_BROADCAST     ; a broadcast?
  1540.     jne    odircmp2        ; ne = no, use our address
  1541.     mov    si,offset DGROUP:bcast    ; fill with all 1's
  1542. odircmp2:mov    cx,3            ; 6 byte addresses to our application
  1543.     cld
  1544.      rep    movsw            ; store source address in pkt buffer
  1545.     pop    si            ; recover ecb si
  1546.     push    si            ; save it again
  1547.     mov    ax,es:[si].protocolws     ; get TYPE, from odircv
  1548.     lea    si,es:[si].immaddr    ; offset to MAC address of sender
  1549.     mov    cx,3            ; three words worth, garbage and all
  1550.     rep    movsw            ; copy to packet buffer
  1551.     stosw                ; and write TYPE to packet buffer
  1552.     pop    si
  1553.     pop    es
  1554.     pop    di
  1555.     mov    cx,es:[si].datalen    ; length of data field from ecb
  1556.     add    cx,6+6+2        ; plus space for dest,src,TYPE
  1557.     push    es            ; save ecb's es:si
  1558.     push    si
  1559.     mov    si,es:[si].frag1addr    ; offset of pkt being confirmed
  1560.     sub    si,6+6+2        ; adj to beginning, for our MAC header
  1561.     mov    ax,1            ; set AX = 1 for buffer done call
  1562.     call    pdrcvr            ; do post processing of buffer
  1563.     pop    si
  1564.     pop    es
  1565.  
  1566. odircmp5:xor    ax,ax
  1567.     cmp    es:[si].frag1addr+2,ax    ; segment of pkt being confirmed
  1568.     je    odircmp6        ; e = illegal, ignore this call
  1569.     mov    es:[si].frag1addr+2,ax    ; clear pkt buffer pointer (seg)
  1570.     mov    es:[si].protocolws,ax     ; clear packet TYPE
  1571.     mov    bx,es:[si].protocolws+2    ; point to ecb index
  1572.     mov    ecbr_busy[bx],al    ; say this ecb is free now
  1573. odircmp6:pop    cx
  1574.     pop    bx
  1575.     pop    ax
  1576.     pop    ds
  1577.     ret
  1578. odircmp    endp
  1579.  
  1580. ; ODI transmission routine
  1581. ; Enter with ds:si pointing at full Ethernet_II packet, cx = length (bytes)
  1582. ; Once sent the ecb belongs to ODI until the xmt-complete routine is called.
  1583. odixmt    proc    near
  1584.     push    si
  1585.     push    di
  1586.     push    es
  1587.     mov    ax,ds
  1588.     mov    es,ax
  1589.     cmp    ecbx_busy,0        ; is this transmitter ecb free?
  1590.     je    odixmt8            ; e = yes
  1591. odixmt7:stc                ; say failure
  1592.     jmp    odixmt6            ; abandon
  1593.  
  1594. odixmt8:mov    ax,cx            ; overall Ethernet_II length
  1595.     sub    ax,6+6+2        ; omit our MAC header
  1596.     jc    odixmt6            ; c = failure, abandon
  1597.     mov    ecbx.datalen,ax        ; setup ECB overall data
  1598.     mov    ecbx.frag1len,ax    ; and fragment length
  1599.  
  1600.     mov    cx,3            ; three words of dest MAC address
  1601.     mov    di,offset DGROUP:ecbx.immaddr    ; destination address in ecb
  1602.     rep    movsw            ; copy destination MAC address
  1603.     add    si,6            ; skip source Ethernet address
  1604.     lodsw                ; get protocol TYPE, move it to AX
  1605.     mov    ecbx.frag1addr,si    ; offset of packet data
  1606.     mov    si,ds
  1607.     mov    ecbx.frag1addr+2,si    ; segment of packet data
  1608.  
  1609. ; Note: Prescan and Default methods use Raw Send: put 0ffffh in StackID
  1610. ; and include the full frame header in the data field. Check MLID 
  1611. ; configuration word ModeFlags, MRawSendBit, for Raw Send capability.
  1612.  
  1613.     cmp    ax,ip_type        ; IP, 0x0008h?
  1614.     jne    odixmt1            ; ne = no
  1615.     mov    si,offset DGROUP:ip_stackid
  1616.     jmp    short odixmt3
  1617.  
  1618. odixmt1:cmp    ax,arp_type        ; ARP, 0x0608?
  1619.     jne    odixmt2            ; ne = no
  1620.     mov    si,offset DGROUP:arp_stackid
  1621.     jmp    short odixmt3
  1622.  
  1623. odixmt2:cmp    ax,rarp_type        ; RARP, 0x3580?
  1624.     jne    odixmt7            ; ne = error, do not send
  1625.     mov    si,offset DGROUP:rarp_stackid
  1626.  
  1627. odixmt3:mov    cx,5            ; stackid, protid, boardnum
  1628.     mov    di,offset DGROUP:ecbx.stackid    ; get stack ident area
  1629.     rep    movsw            ; copy to ecbx
  1630.  
  1631.     mov    ecbx_busy,1        ; set ECBx busy flag to busy state
  1632.     add    pstats.PtotalTxPackets,1 ; update ODI statistics counter
  1633.     adc    pstats.PtotalTxPackets+2,0
  1634.  
  1635.     mov    si,offset DGROUP:ecbx    ; set es:si to ECB
  1636.     mov    bx,PROTSUP_SEND_PACKET    ; send it
  1637.     call    lslsupport        ; call LSL with ecbx ptr in es:si
  1638.     clc                ; success so far, ints are still off
  1639. odixmt6:pop    es
  1640.     pop    di
  1641.     pop    si
  1642.     ret
  1643. odixmt    endp
  1644.  
  1645. ; ODI transmission-complete processor, for use only by the LSL.
  1646. ; Returns nothing. Unlocks ECB busy flag.
  1647. odixcmp    proc    far
  1648.     push    ds
  1649.     push    ax
  1650.     mov    ax,DGROUP        ; set addressibility
  1651.     mov    ds,ax
  1652.     mov    ecbx_busy,0        ; set ECB busy flag to not-busy
  1653.     pop    ax
  1654.     pop    ds
  1655.     ret
  1656. odixcmp    endp
  1657.  
  1658. ; ODI Protocol (that's us) Control routine, required, called from outside.
  1659. ; In principle we should have one of these for each protocol (IP, ARP, RARP)
  1660. ; by putting a different StackControlHandler address in registerstk, but I 
  1661. ; doubt that anyone is that interested in such detailed counts.
  1662. ; Return AX clear, Z flag set, and ES:SI as table pointer if success, else
  1663. ; return AX with error code and Z flag clear.
  1664. pcontrol proc    far
  1665.     cmp    bx,GET_STACK_CONFIGURATION ; get stack configuration?
  1666.     jne    pcont1            ; ne = no
  1667.     mov    si,DGROUP
  1668.     mov    es,si            ; es:si points to configuration table
  1669.     mov    si,offset DGROUP:pconfig; the table
  1670.     xor    ax,ax            ; set Z flag
  1671.     ret
  1672. pcont1:    cmp    bx,GET_STACK_STATISTICS    ; get stack statistics?
  1673.     jne    pcont2            ; ne = no
  1674.     mov    si,DGROUP
  1675.     mov    es,si            ; es:si points to statistics table
  1676.     mov    si,offset DGROUP:pstats    ; the table
  1677.     xor    ax,ax            ; set Z flag
  1678.     ret
  1679. pcont2:    mov    ax,LSLERR_OUT_OF_RESOURCES ; other functions, report error
  1680.     or    ax,ax            ; clear Z flag
  1681.     ret
  1682. pcontrol endp
  1683. _TEXT    ends
  1684.         end
  1685.  
  1686.