home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / lan / driver6s / ipxpkt.asm < prev    next >
Assembly Source File  |  1990-03-15  |  23KB  |  1,104 lines

  1. version    equ    0
  2.  
  3. ; Packet driver to simulate Ethernet on Novell IPX protocol.
  4. ;
  5. ; Paul Kranenburg
  6. ; Department of Computer Science
  7. ; University of Leiden
  8. ; Niels Bohrweg 1
  9. ; PO Box 9512
  10. ; 2300 RA Leiden
  11. ; The Netherlands
  12. ;
  13. ;
  14. ; File: ipxpkt.asm
  15. ;
  16. ;    Compile with: tasm /ml (preserve case)
  17. ;
  18. ;
  19. ; General description:
  20. ;
  21. ; Take destination from the Ethernet packet and feed it to IPX
  22. ; in the Event Control Block Immediate Address field.
  23. ;
  24. ; IPX packets are 576 bytes at most, 30 are needed for the IPX header
  25. ; leaving 546 bytes of user data. Another 4 bytes are used to describe
  26. ; fragments.
  27. ; If NO_OF_SND_BUFS is set to 1, this yields an MTU for this driver of 528.
  28. ; (546 - 4 - sizeof(Ether header)[=14]).
  29. ;
  30. ; If NO_OF_SND_BUFS is set to 3, the Ethernet packet is broken into at most
  31. ; 3 fragments. These are tagged with a Fragment id and shipped.
  32. ;
  33. ; On reception, fragments are kept on a linked list ordered by fragment number
  34. ; and keyed by source node address and fragment id.
  35. ; An IPX event is scheduled to allow for timeout of pending reassembly queues.
  36. ;
  37. ; If all fragments are reassembled, the client is called to provide a buffer for
  38. ; the packet.
  39. ;
  40. ; [ To save on buffer space, the driver could conceivably do with some minimum
  41. ;  number of buffers and call recv_find as soon as a fragment arrives, copy
  42. ;  the contents, and only call recv_copy when all fragments have arrived. However,
  43. ;  I don't think there is a way to notify the client in case a fragment gets lost.]
  44. ;
  45. ; In this code, the number of receive buffers (NO_OF_RCV_BUFS) has been set
  46. ; to 6 (a wild guess).
  47. ; This driver has yet to be tested in a gateway under heavy load. One probably
  48. ; needs more buffers in this case.
  49. ;
  50. ; Buffer space for the receive buffers is allocated after the "end_resident"
  51. ; label. There is a potential problem here: we start listening for packets
  52. ; using these buffers while still in the initialisation code, which is overlaid
  53. ; by the receive buffers. This is why interrupts are turned off wherever possible.
  54. ;
  55. ;
  56. ;
  57.  
  58.  
  59.     include    defs.asm
  60.  
  61. MAX_IPX_LEN    =    576        ; Maximum packet size that can be
  62.                     ; shipped through IPX
  63. IP_socket    =    08061h        ; Socket allocated (by me) for
  64.                     ; Blue book Ether on IPX
  65.  
  66. PEP        =    4        ; Packet Exchange Packet (ipx_type)
  67.  
  68. ipx_header    struc
  69. ipx_chksum    dw    ?        ; Checksum, network byte order
  70. ipx_len        dw    ?        ; Packet length,   "
  71. ipx_prot    db    ?        ; Transport protocol
  72. ipx_type    db    ?        ; Packet type
  73. ipx_destnet    db    4 dup(?)    ; Destination network
  74. ipx_destnode    db    6 dup(?)    ; Destination node
  75. ipx_destsock    dw    ?        ; Destination socket
  76. ipx_srcnet    db    4 dup(?)    ; Source network
  77. ipx_srcnode    db    6 dup(?)    ; Source node
  78. ipx_srcsock    dw    ?        ; Source socket
  79. ipx_header    ends
  80.  
  81.  
  82. frag_dscr    struc
  83. frag_addr    dd    ?        ; Fragment address
  84. frag_size    dw    ?        ; Fragment size
  85. frag_dscr    ends
  86.  
  87. ecb        struc
  88. ecb_link    dd    ?        ;
  89. ecb_esr        dd    ?        ; Event Service Routine
  90. ecb_inuse    db    ?        ; In Use field
  91. ecb_cmplt    db    ?        ; Completion Code
  92. ecb_sock    dw    ?        ; Socket Number
  93. ecb_ipxwork    db    4 dup (?)    ; IPX reserved workspace
  94. ecb_drvwork    db    12 dup (?)    ; Driver reserved workspace
  95. ecb_ia        db    6 dup (?)    ; Immediate Address
  96. ecb_fragcnt    dw    ?        ; Fragment count
  97. ;ecb_dscr    =    $        ; Start of Fragment descriptor list
  98. ecb        ends
  99.  
  100. aes_ecb        struc
  101. aes_link    dd    ?        ;
  102. aes_esr        dd    ?        ; Event Service Routine
  103. aes_inuse    db    ?        ; In Use field
  104. aes_work    db    5 dup (?)    ; Driver reserved workspace
  105. aes_ecb        ends
  106.  
  107.  
  108. ether_frag    struc
  109. ef_fragno    db    ?        ; This fragment number
  110. ef_fragtot    db    ?        ; Total number of fragments comprising the packet
  111. ef_fragid    dw    ?        ; Fragment Id
  112. ether_frag    ends
  113.  
  114.   ifdef ??Version
  115. queue_entry    struc
  116. q_aes        aes_ecb    <>        ; AES structure, used for reassembly timeouts
  117. q_count        db    0        ; Number of fragments currently queued here
  118. q_node        db    6 dup(?)    ; Source node
  119. q_fragid    dw    ?        ; Fragment Id
  120. q_len        dw    ?        ; Total length of user data queued here
  121. q_ecb        dd    ?        ; Ecb pointer to fragment
  122. queue_entry    ends
  123.   else
  124. queue_entry    struc
  125. q_aes        db (size aes_ecb) dup(?); AES structure, used for reassembly timeouts
  126. q_count        db    0        ; Number of fragments currently queued here
  127. q_node        db    6 dup(?)    ; Source node
  128. q_fragid    dw    ?        ; Fragment Id
  129. q_len        dw    ?        ; Total length of user data queued here
  130. q_ecb        dd    ?        ; Ecb pointer to fragment
  131. queue_entry    ends
  132.   endif
  133.  
  134.   ifdef ??Version
  135. u_buf        struc
  136. u_ecb        ecb        <>
  137. u_ipx_frag    frag_dscr    <>
  138. u_frag_frag    frag_dscr    <>
  139. u_data_frag    frag_dscr    <>
  140. u_ipx        ipx_header    <>
  141. u_ether_frag    ether_frag    <>
  142. ;u_data        LABEL        BYTE
  143. u_buf        ends
  144.   else
  145. u_buf        struc
  146. u_ecb        db (size ecb) dup(?)
  147. u_ipx_frag    db (size frag_dscr) dup(?)
  148. u_frag_frag    db (size frag_dscr) dup(?)
  149. u_data_frag    db (size frag_dscr) dup(?)
  150. u_ipx        db (size ipx_header) dup(?)
  151. u_ether_frag    db (size ether_frag) dup(?)
  152. ;u_data        LABEL        BYTE
  153. u_buf        ends
  154.   endif
  155.  
  156. MAX_PAYLOAD    =    MAX_IPX_LEN - SIZE ipx_header - SIZE ether_frag
  157.  
  158. print$        macro    string
  159. ;---------------------------------------;
  160. ;  sends $ terminated string to screen  ;
  161. ;---------------------------------------;
  162.         push    dx
  163.         mov     ah,9
  164.         mov     dx,offset &string&      ; print $ terminated string
  165.         int     21h
  166.         pop    dx
  167.         endm
  168.  
  169. ; ipx function numbers
  170. OPEN_SOCKET        =    0
  171. CLOSE_SOCKET        =    1
  172. SEND_PACKET        =    3
  173. LISTEN            =    4
  174. SCHEDULE_EVENT        =    5
  175. CANCEL_EVENT        =    6
  176. SCHEDULE_SPECIAL_EVENT    =    7
  177. GET_NODE_ADDRESS    =    9
  178. RELINQUISH        =    0Ah
  179.  
  180. call_ipx    macro    opcode,reg1,reg2,reg3,reg4,reg5,reg6,reg7,reg8
  181.         irp N, <reg1,reg2,reg3,reg4,reg5,reg6,reg7,reg8>
  182.             ifnb <N>
  183.                 push    N
  184.             endif
  185.         endm
  186.         mov    bx, opcode
  187. ;better be save here and use Code segment explicitly
  188.         call    cs:IPXentry
  189.         irp N, <reg8,reg7,reg6,reg5,reg4,reg3,reg2,reg1>
  190.             ifnb <N>
  191.                 pop    N
  192.             endif
  193.         endm
  194.         endm
  195.  
  196.  
  197. code    segment    byte public
  198.     assume    cs:code, ds:code
  199.  
  200. IPXentry    dd    ?
  201. FragmentID    dw    ?
  202.  
  203. NO_OF_FRAGMENTS    =    3
  204. NO_OF_RCV_BUFS    =    6
  205. NO_OF_SND_BUFS    =    3
  206. NO_OF_QUEUES    =    NO_OF_RCV_BUFS        ; ????
  207.  
  208. reass_queues    queue_entry    NO_OF_QUEUES dup(<>)
  209.  
  210. rcv_bufs    u_buf    NO_OF_RCV_BUFS dup(<>)
  211. snd_bufs    u_buf    NO_OF_SND_BUFS dup(<>)
  212.  
  213. ;-------------------------------------------------------------------------------
  214. ;
  215. ; local functions
  216. ;
  217. ; A NOTE on style:
  218. ;
  219. ; the functions below seem to liberally load and reload pointers into
  220. ; a register pair involving the ds segment register.
  221. ; In fact, ds almost always contains the code segment as "assumed" above.
  222. ; Also, the distinction between pointers to ecb's and ubuf's / queue's is not made
  223. ; most of the time. This alright as long as the ecb structures remain the first
  224. ; ones declared in u_buf and queue.
  225. ; Need to work out a consistent register usage some day...
  226. ;
  227.  
  228. find_queue    proc    near
  229.     ;
  230.     ; Find/allocate a queue-entry where an ether fragment can be stored.
  231.     ; On entry: es:di -> source node address.
  232.     ;           dx == fragment Id.
  233.     ; Out: si == 0 if no queue entry available,
  234.     ;      otherwise: (ds:)si -> allocated queue-entry.
  235.     ; Must be called with interrupts disabled.
  236.  
  237.     push    cx
  238.     push    bx
  239.     mov    cx, NO_OF_QUEUES
  240.     lea    si, reass_queues
  241.     mov    bx, 0
  242.  
  243. fq_loop:
  244.     mov    al, [si].q_count
  245.     or    al, al
  246.     jnz    fq_1
  247.     or    bx, bx        ;
  248.     jne    fq_2        ; remember first entry not in use
  249.     mov    bx, si        ;
  250.     jmp    short fq_2
  251.  
  252. fq_1:
  253.     push    cx
  254.     push    si
  255.     push    di
  256.     add    si, q_node
  257.     mov    cx, SIZE ipx_srcnode
  258.     cld
  259.     repe    cmpsb
  260.     pop    di
  261.     pop    si
  262.     pop    cx
  263.     jne    fq_2
  264.     cmp    dx, [si].q_fragid
  265.     jne    fq_2
  266.     jmp    short fq_x
  267.  
  268. fq_2:
  269.     add    si, SIZE queue_entry
  270.     loop    fq_loop
  271.  
  272.     mov    si, bx
  273.  
  274. fq_x:
  275.     pop    bx
  276.     pop    cx
  277.     ret
  278. find_queue    endp
  279.  
  280. enqueue        proc    near
  281.     ; Queue an etherpacket fragment on appropriate queue
  282.     ; On entry: es:si -> received ecb.
  283.     ;           cx = length of data in this fragment
  284.     ; Out: carry set if no space available.
  285.     ;      zero flag set if packet on queue complete.
  286.     ;      ds:si -> queue_entry on which fragment was queued.
  287.  
  288.     push    si
  289.     push    es
  290.     mov    ax, 0
  291.     mov    es:[si].u_ecb.ecb_link.offs, ax        ; clear link-field
  292.     mov    es:[si].u_ecb.ecb_link.segm, ax
  293.     mov    di, si
  294.     push    di
  295.     lea    di, es:[si].u_ipx.ipx_srcnode
  296.     mov    dx, es:[si].u_ether_frag.ef_fragid
  297.     call    find_queue
  298.     pop    di
  299.     or    si, si
  300.     jnz    enq_0
  301.     add    sp, 4
  302.     stc
  303.     ret
  304.  
  305. enq_0:
  306.     mov    dl, es:[di].u_ether_frag.ef_fragno
  307.     mov    dh, es:[di].u_ether_frag.ef_fragtot
  308.     cmp    [si].q_count, 0
  309.     jne    enq_3
  310.  
  311. ;this is the first fragment we receive
  312.     pop    [si].q_ecb.segm
  313.     pop    [si].q_ecb.offs
  314.     mov    [si].q_len, cx
  315.     mov    [si].q_count, 1
  316.     cmp    dh, 1            ;
  317.     jne    enq_1            ; short cut if fragment count == 1.
  318.     ret
  319.  
  320. ;initialise queue structure a bit more...
  321. enq_1:
  322.     mov    ax, es:[di].u_ether_frag.ef_fragid
  323.     mov    [si].q_fragid, ax
  324.  
  325. ;copy source node address
  326.     push    bx
  327.     mov    bx, (SIZE ipx_srcnode) - 1
  328.  
  329. enq_2:
  330.     mov    al, es:[di+bx].u_ipx.ipx_srcnode
  331.     mov    ds:[si+bx].q_node, al
  332.     sub    bx, 1
  333.     jnc    enq_2
  334.     pop    bx
  335.  
  336.     mov    ax, cs
  337.     mov    [si].q_aes.aes_esr.segm, ax
  338.     mov    [si].q_aes.aes_esr.offs, offset reass_timeout
  339.     mov    ax, ds
  340.     mov    es, ax
  341.     mov    ax, 2            ; two ticks to timeout
  342.     call_ipx    SCHEDULE_SPECIAL_EVENT,si,dx
  343.     cmp    dh, [si].q_count
  344.     ret
  345.  
  346. ; add ecb to existing queue, keep list ordered by fragment number.
  347. enq_3:
  348.     lea    ax, [si].q_ecb
  349.     push    ax            ; put link field address on stack
  350.     push    ds
  351.     les    di, [si].q_ecb
  352.  
  353. enq_4:
  354.     mov    ax, es
  355.     or    ax, di
  356.     jz    enq_5
  357.     cmp    dl, es:[di].u_ether_frag.ef_fragno
  358.     jb    enq_5
  359.     add    sp, 4
  360. ;    lea    ax, es:[di].u_ecb.ecb_link
  361. ;    push    ax
  362.     push    di
  363.     push    es
  364.     les    di, es:[di].u_ecb.ecb_link
  365.     jmp    enq_4
  366.  
  367. ; enter here with two addresses on the stack:
  368. ;    1) address of ecb to link in
  369. ;    2) address of link field after which to link
  370. ; es:di contains the "next" link.
  371.  
  372. enq_5:
  373.     mov    ax, es
  374.     mov    bx, di
  375.     pop    es
  376.     pop    di
  377.     pop    es:[di].segm
  378.     pop    es:[di].offs
  379.     les    di, es:[di]
  380.     mov    es:[di].u_ecb.ecb_link.segm, ax
  381.     mov    es:[di].u_ecb.ecb_link.offs, bx
  382.     add    [si].q_len, cx
  383.     inc    [si].q_count
  384.     cmp    dh, [si].q_count
  385.     ret
  386.  
  387. enqueue        endp
  388.  
  389. dequeue        proc    near
  390.     ; Send reassembled packet to client and reschedule receive buffers.
  391.     ; On entry: ds:si -> queue.
  392.  
  393.     mov    cx, [si].q_len
  394.     les    di, [si].q_ecb
  395.     les    di, es:[di].u_data_frag.frag_addr
  396.     add    di, 2 * EADDR_LEN
  397.     push    si
  398.     call    recv_find
  399.     pop    si
  400.     mov    ax, es
  401.     or    ax, di
  402.     jz    deq_2
  403.  
  404.     mov    dh, [si].q_count
  405.     mov    cx, [si].q_len
  406.     push    si            ; save our queue address
  407.     push    ds
  408.     push    di            ; save their buffer address
  409.     push    es
  410.     push    cx
  411.     lds    si, ds:[si].q_ecb
  412.     cld
  413.  
  414. ;all set, es:di -> user buffer, ds:si -> first fragment
  415. ;??? save count and source pointer for call to recv_copy
  416.  
  417. deq_1:
  418.     mov    cx, ds:[si].u_ipx.ipx_len
  419.     xchg    cl, ch
  420.     sub    cx, (SIZE ipx_header + SIZE ether_frag)
  421.     push    si
  422.     push    ds
  423.     lds    si, ds:[si].u_data_frag.frag_addr
  424.     rep    movsb
  425.     pop    ds
  426.     pop    si
  427.     lds    si, ds:[si].u_ecb.ecb_link
  428.     dec    dh
  429.     jnz    deq_1
  430.  
  431.     pop    cx            ; recover packet length and address
  432.     pop    ds            ; for completion call
  433.     pop    si            ;
  434.     call    recv_copy
  435.  
  436.     pop    ds            ; recover queue address
  437.     pop    si            ;
  438.  
  439. deq_2:
  440.     mov    ax, ds
  441.     mov    es, ax
  442.     call_ipx    CANCEL_EVENT,si
  443.  
  444.     push    si
  445.     mov    dh, [si].q_count
  446.     les    si, ds:[si].q_ecb
  447.  
  448. deq_3:
  449.     mov    bx, es:[si].ecb_link.offs
  450.     mov    cx, es:[si].ecb_link.segm
  451.     call    listen_proc
  452.     mov    si, bx
  453.     mov    es, cx
  454. ;    les    si, es:[si].u_ecb.ecb_link
  455.     dec    dh
  456.     jnz    deq_3
  457.     pop    si
  458.     mov    [si].q_count, 0
  459.     ret
  460. dequeue        endp
  461.  
  462.  
  463. my_net_address    db    4 dup(?)    ;contiguous 10 byte addrss-area as IPX wants it
  464. my_node_address    db    6 dup(?)
  465.  
  466. from_us        proc    near
  467.     ;
  468.     ; Check if ecb immediate-address is our own.
  469.     ; On entry: es:si -> ecb.
  470.     ; Out: zero flag set if from us.
  471.  
  472.     push    ds
  473.     push    si
  474.     push    di
  475.     push    cx
  476.     mov    ax, cs
  477.     mov    ds, ax
  478.  
  479.     mov    di, si
  480.     add    di, ecb_ia
  481.     lea    si, my_node_address
  482.     mov    cx, SIZE my_node_address
  483.  
  484.     cld
  485.     repe    cmpsb
  486.  
  487.     pop    cx
  488.     pop    di
  489.     pop    si
  490.     pop    ds
  491.     ret
  492. from_us        endp
  493.  
  494. reass_timeout    proc    far
  495.     ; Called by AES when reassembly timeout occurs.
  496.     ; On entry: es:si pointer to ecb.
  497.     ;
  498.  
  499.     push    ds
  500.     mov    ax, cs
  501.     mov    ds, ax
  502.     push    si
  503.     push    es
  504.     mov    dh, es:[si].q_count
  505.     les    si, es:[si].q_ecb
  506.  
  507. reass_to_3:
  508.     mov    bx, es:[si].ecb_link.offs
  509.     mov    cx, es:[si].ecb_link.segm
  510.     call    listen_proc
  511.     mov    si, bx
  512.     mov    es, cx
  513.     dec    dh
  514.     jnz    reass_to_3
  515.  
  516.     pop    es
  517.     pop    si
  518.     mov    es:[si].q_count, 0
  519.  
  520.     pop    ds
  521.     ret
  522. reass_timeout    endp
  523.  
  524. receiver    proc    far
  525.     ;
  526.     ; On entry: es:si pointer to ecb.
  527.     ;
  528.  
  529.     push    ds
  530.     mov    ax, cs
  531.     mov    ds, ax
  532.     mov    al, es:[si].u_ecb.ecb_cmplt
  533.     or    al, al
  534.     jnz    receiver_err
  535.  
  536.     cmp    es:[si].u_ecb.ecb_fragcnt, NO_OF_FRAGMENTS
  537.     jne    receiver_err
  538.  
  539.     call    from_us        ; IPX seems to receive its own broadcasts
  540. ;XXX                ; maybe the Packet type field in the ipx header
  541.                 ; plays a role in this.
  542.     jz    receiver_x
  543.  
  544.     mov    cx, es:[si].u_ipx.ipx_len
  545.     xchg    cl, ch
  546.     sub    cx, (SIZE ipx_header + SIZE ether_frag)
  547.     jbe    receiver_err
  548.  
  549.     push    es        ; save ecb address
  550.     push    si
  551.     call    enqueue
  552.     jnz    rec_1
  553.     call    dequeue
  554.  
  555. rec_1:
  556.     pop    si        ; pop original ecb address
  557.     pop    es
  558.     pop    ds
  559.     ret
  560.  
  561. receiver_err:
  562.     call    count_in_err
  563.  
  564. receiver_x:
  565.     call    listen_proc        ; post listen again
  566.     pop    ds
  567.     cli            ; must return with interrupts disabled, says Novell.
  568.     ret
  569. receiver    endp
  570.  
  571. listen_proc    proc    near
  572.     ;
  573.     ; Post to u_buf for reception.
  574.     ; On entry: es:si -> receive-ecb
  575.     ;
  576.  
  577.     push    bx
  578.  
  579. ;fill in ecb
  580.     mov    es:[si].u_ecb.ecb_esr.offs, offset receiver
  581.     mov    ax, cs
  582.     mov    word ptr es:[si].u_ecb.ecb_esr.segm, ax
  583.     mov    es:[si].u_ecb.ecb_sock, IP_socket
  584.     call_ipx    LISTEN,es,si,di,dx,cx
  585.  
  586.     pop    bx
  587.     ret
  588.  
  589. listen_proc    endp
  590.  
  591. fill_ipxhdr    proc    near
  592.     ;
  593.     ; Fill in ipx header from user data
  594.     ; On entry: ds:si -> user data, cx length, es:bx -> ipx header
  595.     ;
  596.  
  597.     push    si
  598.     push    di
  599.     push    cx
  600. ;
  601. ; clear ipx structure
  602. ;
  603.     mov    di, bx
  604.     mov    al, 0
  605.     mov    cx, SIZE ipx_header
  606.     cld
  607.     rep    stosb
  608.  
  609. ;    pop    cx
  610. ;    push    cx
  611. ;    xchg    cl, ch
  612. ;    mov    es:[bx].ipx_len, cx
  613.  
  614.     mov    di, bx
  615.     add    di, ipx_destnode
  616.     mov    cx, EADDR_LEN
  617.     rep    movsb
  618.  
  619.     mov    di, bx
  620.     add    di, ipx_srcnode
  621.     mov    cx, EADDR_LEN
  622.     rep    movsb
  623.  
  624.     mov    es:[bx].ipx_type, PEP
  625.     mov    es:[bx].ipx_destsock, IP_socket
  626.  
  627.     pop    cx
  628.     pop    di
  629.     pop    si
  630.     ret
  631. fill_ipxhdr    endp
  632.  
  633. route    proc    near
  634.     ;
  635.     ; Determine where to send the packet
  636.     ; On entry: es:si -> ecb and ipx, setup for destination node
  637.     ;
  638.  
  639.     push    ds
  640.     push    si
  641.     push    di
  642.     push    cx
  643.  
  644.     mov    di, si
  645.     add    di, ecb_ia
  646.     add    si, u_ipx + ipx_destnode
  647.     mov    ax, es
  648.     mov    ds, ax                ; ds:si -> ipx_destnode
  649.     mov    cx, SIZE ipx_destnode
  650.     cld
  651.     rep    movsb
  652.  
  653.     pop    cx
  654.     pop    di
  655.     pop    si
  656.     pop    ds
  657.     ret
  658. route    endp
  659.  
  660.     public    int_no
  661. int_no    db    0,0,0,0            ;must be four bytes long for get_number.
  662.  
  663.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  664. driver_class    db    1        ;from the packet spec
  665. driver_type    db    1        ;from the packet spec
  666. driver_name    db    'IPX',0        ;name of the driver.
  667. driver_function    db    2
  668. parameter_list    label    byte
  669.     db    1    ;major rev of packet driver
  670.     db    9    ;minor rev of packet driver
  671.     db    14    ;length of parameter list
  672.     db    EADDR_LEN    ;length of MAC-layer address
  673.   if NO_OF_SND_BUFS eq 1
  674.     dw    528    ;MTU, including MAC headers
  675.   else
  676.     dw    GIANT    ;MTU, including MAC headers
  677.   endif
  678.     dw    MAX_MULTICAST * EADDR_LEN    ;buffer size of multicast addrs
  679.     dw    0    ;(# of back-to-back MTU rcvs) - 1
  680.     dw    0    ;(# of successive xmits) - 1
  681.     dw    0    ;Interrupt # to hook for post-EOI
  682.             ;processing, 0 == none,
  683.  
  684.     public    rcv_modes
  685. rcv_modes    dw    4        ;number of receive modes in our table.
  686.         dw    0,0,0,rcv_mode_3
  687.  
  688.     public    send_pkt
  689. send_pkt:
  690. ;enter with ds:si -> packet, cx = packet length.
  691. ;exit with nc if ok, or else cy if error, dh set to error number.
  692.     assume    ds:nothing
  693.     push    es
  694.     push    di
  695.     mov    ax, cs
  696.     mov    es, ax
  697.  
  698. ;first, compute number of fragments needed, keep in dx
  699.     mov    dx, 0
  700.     mov    ax, cx
  701.  
  702. snd_1:
  703.     inc    dx
  704.     sub    ax, MAX_PAYLOAD
  705.     jnc    snd_1
  706.  
  707. ;can we handle this amount?
  708.     cmp    dx, NO_OF_SND_BUFS
  709.     jbe    snd_frags_ok
  710.  
  711. snd_err:
  712.     call    count_out_err
  713.     pop    di
  714.     pop    es
  715.     mov    dh, CANT_SEND
  716.     stc
  717.     ret
  718.  
  719. snd_frags_ok:
  720.     lea    di, snd_bufs
  721.     push    cx
  722.     mov    cx, dx
  723.     mov    bx, 0
  724.     mov    al, 0
  725.  
  726. snd_free_chk:
  727.     or    al, es:[di+bx].u_ecb.ecb_inuse
  728.     add    bx, SIZE u_buf
  729.     loop    snd_free_chk
  730.  
  731.     pop    cx
  732.     or    al, al
  733.     jnz    snd_err
  734.  
  735.     mov    dh, dl
  736.     mov    dl, 1
  737.     mov    bx, 0
  738.     inc    FragmentID
  739.     push    di
  740.  
  741. snd_next_frag:
  742. ;
  743. ; dh = total number of fragments to send
  744. ; dl = current fragment
  745. ; bx = offset into client buffer (ds:si) for this fragment
  746. ; cx = bytes to go
  747. ; es:di = address of current fragment's ecb
  748. ;
  749.  
  750. ;compute address of associated ipx header
  751.     push    bx
  752.     mov    bx, di
  753.     add    bx, u_ipx
  754.     call    fill_ipxhdr
  755.     pop    bx
  756.  
  757. ;fill in ecb
  758.     mov    ax, 0
  759.     mov    es:[di].u_ecb.ecb_esr.offs, ax
  760.     mov    es:[di].u_ecb.ecb_esr.segm, ax
  761.  
  762.     mov    es:[di].u_ecb.ecb_sock, IP_socket
  763.  
  764.     mov    es:[di].u_ether_frag.ef_fragtot, dh
  765.     mov    es:[di].u_ether_frag.ef_fragno, dl
  766.     mov    ax, FragmentID
  767.     mov    es:[di].u_ether_frag.ef_fragid, ax
  768.  
  769.     mov    ax, ds
  770.     mov    es:[di].u_data_frag.frag_addr.segm, ax
  771.  
  772.     mov    ax, MAX_PAYLOAD
  773.     sub    cx, ax
  774.     jnc    snd_frag1
  775.     add    ax, cx
  776.  
  777. snd_frag1:
  778.     mov    es:[di].u_data_frag.frag_size, ax
  779.     push    si
  780.     add    si, bx
  781.     mov    es:[di].u_data_frag.frag_addr.offs, si
  782.     add    bx, ax
  783.  
  784.     mov    si, di
  785. ;
  786. ; es:si -> ecb to ship
  787. ;
  788.  
  789. ;determine next hop, according to the ecb and ipx
  790.  
  791.     call    route
  792.  
  793.     call_ipx    SEND_PACKET,es,di,dx,cx,bx
  794.     pop    si
  795.  
  796.     add    di, SIZE u_buf
  797.     inc    dl
  798.     cmp    dl, dh
  799.     jbe    snd_next_frag
  800.  
  801.     pop    di
  802.  
  803. ;simple timeout on sends
  804.     mov    cx, 0ffffh
  805.  
  806. snd_wait:
  807.     sti
  808.     mov    bx, 0
  809.     push    cx
  810.     mov    ch, 0
  811.     mov    cl, dh
  812.     mov    al, 0
  813. snd_wait1:
  814.     or    al, es:[di+bx].u_ecb.ecb_inuse
  815.     add    bx, SIZE u_buf
  816.     loop    snd_wait1
  817.     pop    cx
  818.  
  819.     or    al, al
  820.     jz    snd_done
  821.     call_ipx    RELINQUISH,es,di,dx,cx
  822.     loop    snd_wait
  823.  
  824. ;arrive here on timeout, cancel IPX sends
  825.     mov    ch, 0
  826.     mov    cl, dh
  827.     mov    si, di
  828.  
  829. snd_cancel:
  830.     call_ipx    CANCEL_EVENT,es,si,cx
  831.     add    si, SIZE u_buf
  832.     loop    snd_cancel
  833.     jmp    snd_err
  834.  
  835. snd_done:
  836.     mov    bx, 0
  837.     mov    ch, 0
  838.     mov    cl, dh
  839.     mov    al, 0
  840. snd_done1:
  841.     or    al, es:[di+bx].u_ecb.ecb_cmplt
  842.     add    bx, SIZE u_buf
  843.     loop    snd_done1
  844.  
  845.     or    al, al
  846.     jz    snd_ok
  847.     jmp    snd_err
  848.  
  849. snd_ok:
  850.     pop    di
  851.     pop    es
  852.     ret
  853.  
  854.     public    get_address
  855. get_address:
  856. ;get the address of the interface.
  857. ;enter with es:di -> place to get the address, cx = size of address buffer.
  858. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  859.     assume    ds:code
  860.     cmp    cx, SIZE my_node_address
  861.     jb    get_address_error
  862.     push    ds
  863.     push    es
  864.     mov    ax, cs
  865.     mov    ds, ax
  866.     mov    es, ax
  867.     lea    si, my_net_address
  868.     call_ipx    GET_NODE_ADDRESS,di,cx
  869.     pop    es            ;apparently, there is no error code from IPX
  870.     mov    si, offset my_node_address
  871.     mov    cx, SIZE my_node_address
  872.     cld
  873.     rep    movsb
  874.     mov    cx, SIZE my_node_address
  875.     clc
  876.     pop    ds
  877.     ret
  878.  
  879. get_address_error:
  880.     stc
  881.     ret
  882.  
  883.     public    set_address
  884. set_address:
  885. ;enter with ds:si -> Ethernet address, CX = length of address.
  886. ;exit with nc if okay, or cy, dh=error if any errors.
  887.     assume    ds:nothing
  888.     ret
  889.  
  890.  
  891. rcv_mode_3:
  892. ;receive mode 3 is the only one we support, so we don't have to do anything.
  893.     ret
  894.  
  895.  
  896.     public    set_multicast_list
  897. set_multicast_list:
  898. ;enter with es:di ->list of multicast addresses, cx = number of bytes.
  899. ;return nc if we set all of them, or cy,dh=error if we didn't.
  900.     mov    dh,NO_MULTICAST
  901.     stc
  902.     ret
  903.  
  904.  
  905.     public    get_multicast_list
  906. get_multicast_list:
  907. ;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
  908. ;return cy, NO_ERROR if we don't remember all of the addresses ourselves.
  909. ;return cy, NO_MULTICAST if we don't implement multicast.
  910.     mov    dh,NO_MULTICAST
  911.     stc
  912.     ret
  913.  
  914.  
  915.     public    reset_interface
  916. reset_interface:
  917. ;reset the interface.
  918.     assume    ds:code
  919.     ret
  920.  
  921.  
  922. ;called when we want to determine what to do with a received packet.
  923. ;enter with cx = packet length, es:di -> packet type.
  924.     extrn    recv_find: near
  925.  
  926. ;called after we have copied the packet into the buffer.
  927. ;enter with ds:si ->the packet, cx = length of the packet.
  928.     extrn    recv_copy: near
  929.  
  930.     extrn    count_in_err: near
  931.     extrn    count_out_err: near
  932.  
  933.     public    recv
  934. recv:
  935. ;called from the recv isr.  All registers have been saved, and ds=cs.
  936. ;Upon exit, the interrupt will be acknowledged.
  937.     assume    ds:code
  938.     ret
  939.  
  940.  
  941.     public    recv_exiting
  942. recv_exiting:
  943. ;called from the recv isr after interrupts have been acknowledged.
  944. ;Only ds and ax have been saved.
  945.     assume    ds:nothing
  946.     ret
  947.  
  948.     public    terminate
  949. terminate:
  950. ;called when this driver should cease operation.
  951.     assume    ds:nothing
  952.  
  953. ;close socket, outstanding listens should be cancelled automatically
  954.     mov    dx, IP_socket
  955.     call_ipx    CLOSE_SOCKET
  956.  
  957. ;    mov    ax, cs
  958. ;    mov    es, ax
  959. ;    mov    cx, NO_OF_RCV_BUFS
  960. ;    lea    si, rcv_bufs
  961. ;
  962. ;terminate_1:
  963. ;    call_ipx    CANCEL_EVENT,es,si,cx
  964. ;    add    si, SIZE u_buf
  965. ;    loop    terminate_1
  966.  
  967.     ret
  968.  
  969.  
  970. ;any code after this will not be kept after initialization.
  971. end_resident    label    byte
  972.  
  973.  
  974.     public    usage_msg
  975. usage_msg    db    "usage: ipx_pkt <packet_int_no>",CR,LF,'$'
  976.  
  977.     public    copyright_msg
  978. copyright_msg    db    "Packet driver for a generic device, version ",'0'+version,CR,LF,'$'
  979.         db    "Portions Copyright 19xx, J. Random Hacker",CR,LF,'$'
  980.  
  981. no_ipx_msg    db    "IPX presence not detected",CR,LF, '$'
  982. wrong_sock_msg    db    "IPX has no good socket",CR,LF, '$'
  983.  
  984.     extrn    set_recv_isr: near
  985.  
  986. ;enter with si -> argument string, di -> wword to store.
  987. ;if there is no number, don't change the number.
  988.     extrn    get_number: near
  989.  
  990.     public    parse_args
  991. parse_args:
  992.     ret
  993.  
  994.  
  995.     public    etopen
  996. etopen:
  997.  
  998. ;first see if IPX is there
  999.     mov    ax, 07A00h
  1000.     int    2fh
  1001.     cmp    al, 0ffh
  1002.     je    ipx_is_here
  1003.     print$    no_ipx_msg
  1004.     stc
  1005.     ret
  1006.  
  1007. ipx_is_here:
  1008.     mov    ax, es
  1009.     mov    IPXentry.offs, di
  1010.     mov    IPXentry.segm, ax
  1011.  
  1012. ;close socket first, since "head" won't notify us on termination
  1013.     mov    dx, IP_socket
  1014.     call_ipx    CLOSE_SOCKET
  1015.  
  1016. ;next open socket
  1017.     mov    al, 0ffh        ; stay open until explicitly closed
  1018.     mov    dx, IP_socket
  1019.     call_ipx    OPEN_SOCKET
  1020.     or    al, 0
  1021.     jnz    wrong_socket
  1022.     cmp    dx, IP_socket
  1023.     je    good_socket
  1024.  
  1025. ;close socket and exit
  1026. wrong_socket:
  1027.     call_ipx    CLOSE_SOCKET
  1028.     print$    wrong_sock_msg
  1029.     stc
  1030.     ret
  1031.  
  1032. good_socket:
  1033. ;init send buffer fragment list
  1034.     mov    ax, cs
  1035.     mov    es, ax
  1036.  
  1037.     mov    cx, NO_OF_SND_BUFS
  1038.     lea    si, snd_bufs
  1039.  
  1040. et_1:
  1041.     mov    es:[si].u_ecb.ecb_fragcnt, NO_OF_FRAGMENTS
  1042.  
  1043.     mov    bx, si            ; bx = offset ipx_header
  1044.     add    bx, u_ipx
  1045.     mov    es:[si].u_ipx_frag.frag_addr.offs, bx
  1046.     mov    es:[si].u_ipx_frag.frag_addr.segm, ax
  1047.     mov    es:[si].u_ipx_frag.frag_size, SIZE ipx_header
  1048.  
  1049.     mov    bx, si            ; bx = offset ether_frag
  1050.     add    bx, u_ether_frag
  1051.     mov    es:[si].u_frag_frag.frag_addr.offs, bx
  1052.     mov    es:[si].u_frag_frag.frag_addr.segm, ax
  1053.     mov    es:[si].u_frag_frag.frag_size, SIZE ether_frag
  1054.  
  1055.     mov    ax, es
  1056.     add    si, SIZE u_buf
  1057.     loop    et_1
  1058.  
  1059. ;init receive buffers and start listening
  1060.     mov    cx, NO_OF_RCV_BUFS
  1061.     lea    si, rcv_bufs
  1062.     lea    di, end_resident    ; living dangerously...
  1063.     cli                ; don't know if this helps
  1064.  
  1065. et_2:
  1066.     mov    es:[si].u_ecb.ecb_fragcnt, NO_OF_FRAGMENTS
  1067.  
  1068.     mov    bx, si
  1069.     add    bx, u_ipx
  1070.     mov    es:[si].u_ipx_frag.frag_addr.offs, bx
  1071.     mov    es:[si].u_ipx_frag.frag_addr.segm, ax
  1072.     mov    es:[si].u_ipx_frag.frag_size, SIZE ipx_header
  1073.  
  1074.     mov    bx, si
  1075.     add    bx, u_ether_frag
  1076.     mov    es:[si].u_frag_frag.frag_addr.offs, bx
  1077.     mov    es:[si].u_frag_frag.frag_addr.segm, ax
  1078.     mov    es:[si].u_frag_frag.frag_size, SIZE ether_frag
  1079.  
  1080.     mov    es:[si].u_data_frag.frag_addr.offs, di        ; di = offset data buffer
  1081.     mov    es:[si].u_data_frag.frag_addr.segm, ax
  1082.     mov    es:[si].u_data_frag.frag_size, MAX_PAYLOAD
  1083.  
  1084.     call    listen_proc
  1085.     cli                ; just to be certain
  1086.     mov    ax, es
  1087.     add    si, SIZE u_buf
  1088.     add    di, MAX_PAYLOAD
  1089.     loop    et_2
  1090.  
  1091. ;if all is okay,
  1092.     mov    dx,offset end_resident
  1093.     add    dx, NO_OF_RCV_BUFS * MAX_PAYLOAD
  1094.     clc
  1095.     ret
  1096.  
  1097. ;if we got an error,
  1098.     stc
  1099.     ret
  1100.  
  1101. code    ends
  1102.  
  1103.     end
  1104.