home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / MODEM / MNPC12.ZIP / MNPDRVR.C < prev    next >
Encoding:
C/C++ Source or Header  |  1987-11-16  |  21.1 KB  |  888 lines

  1. /*=====================================================================
  2.  
  3.                       The Microcom MNP Library
  4.                         (Microsoft C Version)
  5.  
  6.  
  7.     11/4/87 - explicit data typing...gp
  8.     8/27/87 - rcv_lt() modified to use lcb.lcl_credit..gp
  9.  
  10. -----------------------------------------------------------------------
  11.  
  12.         LDRIVER - MNP Link RS-232 Interrupt Service Routines
  13.  
  14.      This module implements asynchronous I/O for IBM PC-compatible
  15.      machines and thus much of this code may not be portable to 
  16.      other dissimilar environments.
  17.  
  18.      This code implements Class 2 of the MNP link protocol,
  19.      i.e. it uses the stop/start byte-oriented framing technique.
  20.      A Class 3 implementation is not possible in the IBM PC 
  21.      environment since the communications hardware is not capable
  22.      of switching to synchronous (bit-oriented) operation.
  23.      Protocol messages and procedures, however, are independent
  24.      of the framining technique.        
  25.  
  26.      Global routines:
  27.  
  28.     - ascode: service I/O interrupt 
  29.     - lne_stat: check carrier status 
  30.     - trigger_sf: initiate transmit interrupts
  31.  
  32. =====================================================================*/
  33.  
  34. /* Header files
  35. */
  36. #include <dos.h>
  37. #include <mnpdat.h>
  38.  
  39. /* Interrupt-related definitions
  40. */
  41. #define INT_8259A_PORT    0x20
  42. #define EOI        0x20
  43. #define RCV_DATA    4
  44. #define THR_EMPTY    2
  45. #define MODSTAT_CHNG    0
  46.  
  47. /* Send framer state names
  48. */
  49. #define SF_INIT     0
  50. #define SF_DLE        1
  51. #define SF_STX        2
  52. #define SF_INFO     3
  53. #define SF_DLEINFO    4
  54. #define SF_ETX        5
  55. #define SF_FCS1     6
  56. #define SF_FCS2     7
  57.  
  58. /* Receive framer state names
  59. */
  60. #define RF_INIT     0
  61. #define RF_DLE        1
  62. #define RF_STX        2
  63. #define RF_HDLEN    3
  64. #define RF_HEADER    4
  65. #define RF_INFOL    5
  66. #define RF_INFO     6
  67. #define RF_FCS1     7
  68. #define RF_FCS2     8
  69.  
  70. /* Frame octet definitions
  71. */
  72. #define SYN        0x16
  73. #define DLE        0x10
  74. #define ETX        3
  75. #define STX        2
  76.  
  77. /* Length of header buffer (maximum acceptable LPDU header)
  78. */
  79. #define MAX_HDRLEN    100
  80.  
  81. /* Bit set, clear and test definitions
  82. */
  83. #define SETBIT1(bit)    lcb.status_1 |= bit;       
  84. #define SETBIT2(bit)    lcb.status_2 |= bit;
  85. #define SETBIT3(bit)    lcb.status_3 |= bit;
  86. #define CLRBIT1(bit)    lcb.status_1 &= ~bit;         
  87. #define CLRBIT2(bit)    lcb.status_2 &= ~bit;
  88. #define CLRBIT3(bit)    lcb.status_3 &= ~bit;
  89. #define BIT1SET(bit)    (lcb.status_1 & bit)         
  90. #define BIT2SET(bit)    (lcb.status_2 & bit)
  91. #define BIT3SET(bit)    (lcb.status_3 & bit)
  92.  
  93. /* External references
  94. */
  95. extern USIGN_16 iir_add;            /* int id reg address */
  96. extern USIGN_8 linestat;        /* line status var */
  97. extern USIGN_16 fcw_tm;            /* window timer */
  98. extern struct link_ctl_blk lcb;  /* link control block */
  99. extern struct BUFFER rb, ftb, rlkb;  /* buffers */
  100. extern USIGN_8 *rb_iptr;        /* rcv buf insert pointer */
  101. extern USIGN_16 rb_cnt,tbcnt;        /* buffer counts */
  102. extern USIGN_8 rbuf[RBUF_LEN];    /* receive buffer */
  103. extern USIGN_16 ln_tm;
  104.  
  105. /* Function definitions
  106. */
  107. void snd_framer();
  108. void rcv_framer();
  109. void mod_stat();
  110. void rcv_lt();
  111. void rcv_la();
  112. void rcv_lna();
  113. void rcv_ln();
  114.  
  115. /* Local data
  116. */
  117. USIGN_8 *sf_ptr,
  118.      *rf_hptr,
  119.      *rf_dptr;
  120. USIGN_8 snd_char,
  121.      rcv_char;
  122. struct 
  123.     {
  124.     USIGN_8 low;
  125.     USIGN_8 hi;
  126.     }
  127.         snd_fcs,            /* send fcs */
  128.           rcv_fcs,            /* calculated receive fcs */
  129.           rfcs;            /* received fcs */
  130. USIGN_16 rdle_flg;        /* 'previous char DLE' flag */
  131. USIGN_16 rf_hdrlen,                /* receive header length  */
  132.     hdrcnt,            
  133.     rf_datlen,
  134.     sf_state,                /* send framer state var */
  135.     rf_state,                /* receive framer state var */
  136.     sf_len,
  137.     sf_busy,                /* 'send frame' flag */
  138.     sf_lt,                /* 'sending LT' flag */
  139.     frame_snt,                /* 'info field sent' flag */ 
  140.     frame_rcvd,                /* 'frame received' flag */
  141.     frame_dne;                /* 'frame sent' flag */
  142. USIGN_8 *rf_tiptr;
  143.  
  144. struct MNP_CB *p_mnpcb;
  145. struct BLST *dat_struct, *hdr_struct;
  146.  
  147. USIGN_16 modem_out_busy;    /* RS-232 hardware status flag
  148.                               0=transmitter not active
  149.                               1=transmitter active */
  150.  
  151. /* Function declarations
  152. */
  153. USIGN_8 get_char();
  154.  
  155. /*GLOBAL***************************************************************
  156.  
  157.     ascode - MNP RS-232 interrupt service routine
  158.  
  159.      This routine is called by 'async' (in module 'async.asm')
  160.      to service an async I/O interrupt.
  161.  
  162. **********************************************************************/
  163.  
  164. ascode()
  165. {
  166.  
  167. USIGN_8 int_id_reg;            /* value in int id reg */
  168.  
  169. /* Handle interrupts for as long as there are any. 
  170. */
  171. for (;;)
  172.     {
  173.  
  174. /* Read the Interrupt Identification Register.  If bit 0 is on, 
  175. ** there is no interrupt.  In this case cancel the interrupt by writing ** the "end-of-interrupt" byte to the Oper Ctl Word 2 and return. 
  176. */
  177.     if ((int_id_reg = inp(iir_add)) & BIT0_ON)
  178.         {
  179.         outp(INT_8259A_PORT, EOI);
  180.         return;
  181.         }
  182.  
  183. /* Since bit 0 is off, there is an interrupt.  Take action based
  184. ** on the type of interrupt. 
  185. */
  186.     switch (int_id_reg)
  187.         {
  188.  
  189. /* Receive buffer full interrupt
  190. */
  191.         case RCV_DATA:                
  192.             rcv_framer();
  193.             break;
  194.  
  195. /* Transmit buffer empty interrupt
  196. */
  197.         case THR_EMPTY:
  198.             snd_framer();
  199.             break;
  200.  
  201. /* Modem status change interrupt
  202. */
  203.         case MODSTAT_CHNG:            
  204.             mod_stat();
  205.             break;
  206.         }
  207.     }
  208. }
  209.  
  210. /*GLOBAL***************************************************************
  211.  
  212.     lne_stat - physical-connection status routine
  213.  
  214. **********************************************************************/
  215.  
  216. SIGN_16 lne_stat()
  217. {
  218.  
  219. /* If the carrier detect bit is 1, return success (physical-connection
  220. ** still active).  Otherwise, return with a failure indication. 
  221. */
  222. if (linestat & 0x80)
  223.     return (SUCCESS);
  224. else
  225.     return (1);
  226. }
  227.  
  228. /*GLOBAL***************************************************************
  229.  
  230.     trigger_sf - initiate send framer
  231.  
  232. **********************************************************************/
  233.  
  234. void trigger_sf()
  235. {
  236.  
  237. extern USIGN_16 port_add;
  238.  
  239. /* If the RS-232 transmitter is not already busy, then a byte must 
  240. ** be placed in the transmit buffer to begin interrupt-driven sending.
  241. ** This is the start of a frame and thus the first byte to be sent is
  242. ** the SYN which begins the frame start flag.  Also the send framer's
  243. ** state variable is initialed here.
  244. */ 
  245. if (!modem_out_busy)
  246.     {
  247.     modem_out_busy = 1;
  248.     sf_state = SF_DLE;                
  249.     outp(port_add, SYN);
  250.     }
  251. }
  252.  
  253. /*LOCAL----------------------------------------------------------------
  254.  
  255.     get_char - get byte from send buffer
  256.  
  257. ---------------------------------------------------------------------*/
  258.  
  259. USIGN_8 get_char()
  260. {
  261.  
  262. /* Return the byte at the current send framer pointer position.
  263. ** In the process, advance the point for next time and reduce the
  264. ** number of bytes remaining by one.
  265. */
  266. sf_len--;                    
  267. return (*sf_ptr++);
  268. }
  269.  
  270. /*LOCAL----------------------------------------------------------------
  271.  
  272.     mod_stat - modem status change interrupt service routine
  273.  
  274. ---------------------------------------------------------------------*/
  275.  
  276. void mod_stat()
  277. {
  278.  
  279. /* Read modem status register and save the value.
  280. */
  281. linestat = inp(iir_add + 4);
  282.  
  283. /* If carrier has been lost, signal the mainline that the link
  284. ** also has been lost by lowering the 'link established' flag. 
  285. */
  286. if (!(linestat & 0x80))
  287.     CLRBIT1(LINK_EST)    
  288.  
  289. }
  290.  
  291. /*LOCAL----------------------------------------------------------------
  292.  
  293.     put_char - store an LT data byte
  294.  
  295. ---------------------------------------------------------------------*/
  296.  
  297. void put_char()
  298. {
  299.  
  300. /* Ignore this byte if the amount of received data exceeds the
  301. ** negotiated maximum user data size for the LT LPDU.  This could occur
  302. ** if the frame is broken or if the correspondent is misbehaving. 
  303. */
  304. if (rf_datlen >= lcb.max_data_sz)
  305.     return;
  306.  
  307. /* Otherwise, accept this byte. 
  308. */
  309. rf_datlen++;                
  310. fcscalc (&rcv_fcs, rcv_char);
  311.  
  312. /* Now store the data byte in the receive buffer using the temporary
  313. ** insert pointer.  Advance the pointer, checking for wrap.
  314. */
  315. *rf_tiptr++ = rcv_char;            
  316. if (rf_tiptr >= rbuf + RBUF_LEN)
  317.     rf_tiptr = rbuf;
  318.  
  319. }
  320.  
  321. /*LOCAL----------------------------------------------------------------
  322.  
  323.     put_hdr - store an LPDU header byte
  324.  
  325. ---------------------------------------------------------------------*/
  326.  
  327. void put_hdr()
  328. {
  329.  
  330. /* Store header byte in header buffer.  Increment count of bytes in
  331. ** the buffer and include the byte in the receive fcs.
  332. */
  333. rf_hdrlen++;
  334. *rf_hptr++ = rcv_char;                  
  335. fcscalc (&rcv_fcs, rcv_char);
  336.  
  337. }
  338.  
  339. /*LOCAL----------------------------------------------------------------
  340.  
  341.     RCV_FRAMER - Receive Framer
  342.  
  343. ---------------------------------------------------------------------*/
  344.  
  345. void rcv_framer()
  346. {
  347.  
  348. extern USIGN_16 port_add;
  349.  
  350. /* Get received data byte and save it for later
  351. */
  352. rcv_char = inp(port_add);
  353.  
  354. /* Take action based on current state of receive finite state machine.
  355. ** 'rf_state' is the state variable.
  356. */
  357. switch (rf_state)
  358.     {
  359.  
  360. /* State RF_INIT - This is the initial state.  Search the received
  361. ** data stream for a SYN character which may be the beginning
  362. ** of the start flag sequence of a byte mode frame. 
  363. */
  364.     case RF_INIT:
  365.         if (rcv_char == SYN)
  366.             rf_state++;    
  367.         break;
  368.  
  369. /* State RF_DLE - Search for the second byte in the start flag, a DLE
  370. ** byte.  Go back to the initial state (SYN search) if this byte is
  371. ** something other than a DLE.
  372. */
  373.     case RF_DLE:
  374.         if (rcv_char == DLE)
  375.              rf_state++; 
  376.         else 
  377.             rf_state--;        
  378.         break;
  379.  
  380. /* State RF_STX - If the received byte is an STX then a frame start flag
  381. ** has been found.    In this case, initialize to receive an LPDU.
  382. ** Otherwise, go back to looking for a SYN. 
  383. */
  384.     case RF_STX:
  385.         if (rcv_char == STX)
  386.             {
  387.             get_b(&rlkb,&hdr_struct);
  388.             rf_hptr = hdr_struct->bptr;
  389.             hdrcnt=rf_hdrlen=rf_datlen=rdle_flg=0;
  390.             rcv_fcs.hi=rcv_fcs.low=NULL;
  391.                 rf_state++;         
  392.                 }
  393.         else                
  394.                 rf_state = RF_INIT;         
  395.         break;
  396.  
  397. /* State RF_HDLEN - Get the first byte of the LPDU, the length
  398. ** indicator.  Wait for the next byte if this happens to be a DLE (in a
  399. ** good frame the DLE will be doubled).  If the value  received is
  400. ** greater than this implementation's maximum header length, it must be
  401. ** a protocol error or the value is broken.  In either case, ignore this
  402. ** LPDU. This is like a broken frame, so force the sender to ack. 
  403. */
  404.     case RF_HDLEN:
  405.         if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
  406.             {
  407.             rdle_flg = 0;        
  408.         
  409.             if (rcv_char > MAX_HDRLEN)
  410.                 {
  411.                 ret_b(&rlkb, hdr_struct);    
  412.                 SETBIT2(FORCE_ACK)
  413.                 rf_state = RF_INIT;        
  414.                 }
  415.                 else
  416.                 {
  417.                 hdrcnt = rcv_char;
  418.                 put_hdr();
  419.                 rf_state++;
  420.                 }
  421.                 }
  422.         else
  423.                 rdle_flg = 1;
  424.         break;
  425.  
  426. /* State RF_HEADER - Receive header bytes, assuming the length indicator
  427. ** was correct.  Any bytes following the header will temporarily be
  428. ** assumed to be data.
  429. */
  430.     case RF_HEADER:
  431.         if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg)) 
  432.             {
  433.                 rdle_flg = 0;
  434.                 put_hdr();
  435.                 if (!(--hdrcnt))
  436.                 rf_state++;
  437.                 }
  438.         else
  439.                 rdle_flg = 1;
  440.         break;
  441.  
  442. /* State RF_INFOL -  Receive first data byte.  Note that we must check
  443. ** for the stop flag just in case the length indicator byte was broken.  ** When the first data byte is received, the temporary insert pointer
  444. ** into the data receive buffer is initialized to the current location
  445. ** of the real pointer.
  446. */
  447.     case RF_INFOL:
  448.         if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
  449.             {
  450.                 if ((rcv_char == ETX) && rdle_flg) 
  451.                 {
  452.                 fcscalc(&rcv_fcs, rcv_char);
  453.                 rf_state = RF_FCS1;
  454.                 }
  455.                 else
  456.                 {
  457.                 rdle_flg = 0;
  458.                     rf_tiptr = rb_iptr;         
  459.                     put_char();
  460.                 rf_state++;            
  461.                 }
  462.             }
  463.         else
  464.                 rdle_flg = 1;
  465.         break;
  466.  
  467. /* State RF_INFO - Receive data bytes until the stop flag is found.
  468. */
  469.     case RF_INFO:
  470.         if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
  471.             {
  472.                 if ((rcv_char == ETX) && rdle_flg)
  473.                 {
  474.                 fcscalc(&rcv_fcs, rcv_char);
  475.                 rf_state++;        
  476.                 }
  477.                 else
  478.                 put_char();
  479.                 rdle_flg = 0;
  480.                 }
  481.         else
  482.                 rdle_flg = 1;
  483.         break;
  484.  
  485. /* State RF_FCS1 - Receive first byte of fcs.
  486. */
  487.     case RF_FCS1:
  488.         rfcs.hi = rcv_char;
  489.         rf_state++;
  490.         break;
  491.  
  492. /* State RF_FCS2 - Receive second (low) byte of fcs and take end-of-
  493. ** frame action.  If the calculated fcs is not identical to that
  494. ** received, the received frame is "broken".  Signal this to the sender
  495. ** by setting the LPDU type to 0 and setting the FORCE_ACK flag.  If the
  496. ** FCS's match, take action based on the type of LPDU received. 
  497. */
  498.     case RF_FCS2:
  499.         if ((rcv_fcs.hi != rfcs.hi) || (rcv_fcs.low != rcv_char))
  500.             {
  501.                 lcb.lpdu_type = 0;
  502.                 SETBIT2(FORCE_ACK);
  503.                 ret_b(&rlkb, hdr_struct);
  504.                 }
  505.         else
  506.             {
  507.                 switch (lcb.lpdu_type = *(hdr_struct->bptr + PDU_TYPE))
  508.                 {
  509.                 case LT:
  510.                         rcv_lt();
  511.                         break;
  512.  
  513.                 case LA:
  514.                         rcv_la();
  515.                         break;
  516.  
  517. /* Link Request - If an LR arrives during what we think is the data
  518. ** phase, the other side may be retransmitting. In this case, just
  519. ** discard the LR.  Otherwise, save the LR for the mainline to process.
  520. ** In either case, signal the mainline to ack. 
  521. */
  522.                 case LR:
  523.                         if (BIT1SET(LINK_EST))
  524.                         ret_b(&rlkb, hdr_struct);
  525.                         else
  526.                         rlkb.used_lst = hdr_struct;  
  527.                         SETBIT2(FORCE_ACK)
  528.                         break;
  529.  
  530.                 case LN:
  531.                         rcv_ln();
  532.                         break;
  533.  
  534.                 case LNA:
  535.                         rcv_lna();
  536.                         break;
  537.  
  538. /* Link Disconnect - Signal link termination to the mainline.  If the LD
  539. ** contains a user reason byte (signalled by parm 1 having a value of
  540. ** 255), save it in the LCB. 
  541. */
  542.                 case LD:
  543.                         CLRBIT1(LINK_EST)
  544.                         if (*(hdr_struct->bptr + 4) == 255)
  545.                         {
  546.                         p_mnpcb->ld_source = 1;
  547.                         p_mnpcb->ld_reason = 
  548.                             *(hdr_struct->bptr+7);
  549.                         }
  550.                         else
  551.                         p_mnpcb->ld_source = 0;
  552.                         break;
  553.                 }
  554.                 }
  555.  
  556. /* Signal the mainline that a frame has been received and reset the
  557. ** receive frame state machine for the next frame. 
  558. */
  559.                 frame_rcvd = 1;            
  560.                 rf_state = RF_INIT;     
  561.                 break;
  562.         }
  563.  
  564. }
  565.  
  566. /*LOCAL----------------------------------------------------------------
  567.  
  568.     rcv_la - process a received LA LPDU
  569.  
  570. ---------------------------------------------------------------------*/
  571.  
  572. void rcv_la()
  573. {
  574.  
  575. register USIGN_8 *p;
  576. USIGN_16 i;
  577. USIGN_8 lt_ret_seq;
  578.  
  579. /* Ignore this LA LPDU if a destructive attention is in progress. 
  580. */
  581. if (BIT3SET(LN_SENT))
  582.     {
  583.     ret_b(&rlkb,hdr_struct);
  584.         return;
  585.         }
  586.  
  587. /* For reasons of robustness, check the validity of the LA LPDU 
  588. */
  589. p = hdr_struct->bptr + 2;            /* point to parm 1 */
  590. if ((*p++ != 1) || (*p != 1))
  591.     {
  592.     ret_b(&rlkb,hdr_struct);
  593.     return;
  594.     }
  595. p += 2;
  596. if ((*p++ != 2) || (*p != 1))
  597.     {
  598.     ret_b(&rlkb,hdr_struct);
  599.     return;
  600.     }
  601.  
  602. /* If the receive sequence number of this LA is the same as the last
  603. ** one received, it may be an implied negative acknowledgment.  Force
  604. ** the mainline to consider retransmission by setting the 'force
  605. ** retransmission' flag.  Otherwise, save the new number as the last
  606. ** acked and set the 'good ack received' flag. 
  607. */
  608. p -= 2;
  609. if (*p == lcb.ltssn_acked)
  610.     SETBIT2(FORCE_RET)
  611. else
  612.     {
  613.     lcb.ltssn_acked = *p;
  614.         SETBIT1(LA_RECEIVED)
  615.         }
  616.  
  617. /* Process the new credit information received. If it is zero, return.
  618. */
  619. p += 3;
  620. if ((lcb.rem_credit = *p) == 0)
  621.     {
  622.     ret_b(&rlkb,hdr_struct);
  623.         return;
  624.         }
  625.  
  626. /* If credit is not zero and this ack was a good one, take into account
  627. ** the LT LPDUs that may have been sent since the correspondent sent
  628. ** this LA.  That is, subtract from the credit the number of these not
  629. ** yet acked LTs. 
  630. */
  631. if (BIT1SET(LA_RECEIVED) && tbcnt)
  632.     {
  633.     lt_ret_seq = *(ftb.used_lst->bptr + LT_SEQ);
  634.  
  635.     if (lt_ret_seq <= lcb.ltssn_acked)
  636.         i = (lcb.ltssn_acked - lt_ret_seq) + 1;
  637.     else
  638.         i = (256 - lt_ret_seq) + lcb.ltssn_acked + 1;
  639.  
  640.         i = tbcnt - i;
  641.         if (lcb.rem_credit >= i)
  642.         {
  643.         lcb.rem_credit -= i;
  644.             if (lcb.rem_credit < 0)
  645.             lcb.rem_credit = 0;
  646.             }
  647.         else
  648.         lcb.rem_credit = 0;
  649.         }
  650.  
  651. /* Clean up 
  652. */
  653. ret_b(&rlkb, hdr_struct);
  654.  
  655. }
  656.  
  657. /*LOCAL----------------------------------------------------------------
  658.  
  659.     rcv_ln - process a received LN LPDU
  660.  
  661. ---------------------------------------------------------------------*/
  662.  
  663. void rcv_ln()
  664. {
  665.  
  666. register USIGN_8 *p;
  667.  
  668. /* Check the sequence number of this LN LPDU.  If it is the next one
  669. ** in the sequence space, signal its arrival to the sender by
  670. ** setting the LN_RECEIVED flag.  Also remember the type of the LN LPDU
  671. ** by saving the type parameter value in the LCB. 
  672. */
  673. p = hdr_struct->bptr + 4;
  674. if (*p == (USIGN_8) ++lcb.ln_rsn)
  675.     {
  676.     lcb.ln_rtype = *(p + 3);
  677.         SETBIT3(LN_RECEIVED)
  678.         }
  679.  
  680. /* If this is not the next one, then decrement the receive sequence
  681. ** number which was incremented above and signal the need to send an
  682. ** LNA LPDU.
  683. */
  684. else
  685.     {
  686.     lcb.ln_rsn--;
  687.     SETBIT3(FORCE_LNA)
  688.     }
  689.  
  690. ret_b(&rlkb, hdr_struct);
  691. }
  692.  
  693. /*LOCAL----------------------------------------------------------------
  694.  
  695.     rcv_lna -  process a received LNA LPDU
  696.  
  697. ---------------------------------------------------------------------*/
  698.  
  699. void rcv_lna()
  700. {
  701.  
  702. register USIGN_8 *p;
  703.  
  704. /* If the LNA LPDU is an acknowledgment of an outstanding LN, its
  705. ** sequence number will match that of the last LN sent.    In this
  706. ** case, signal the sender that the LN has been acknowledged.  
  707. ** Otherwise, just ignore this LPDU. 
  708. */
  709. if (*(hdr_struct->bptr + 4) == lcb.ln_ssn)
  710.     {
  711.     lcb.ln_ret_cnt = NULL;
  712.     CLRBIT3(LN_SENT)
  713.     CLRBIT3(LN_TIMER)
  714.     ln_tm=NULL;    
  715.     }
  716. ret_b(&rlkb, hdr_struct);
  717. }
  718.  
  719. /*LOCAL----------------------------------------------------------------
  720.  
  721.     rcv_lt - process a received LT LPDU
  722.  
  723. ---------------------------------------------------------------------*/
  724.  
  725. void rcv_lt()
  726. {
  727.  
  728. /* Ignore this LT LPDU if a destructive attention is in progress. 
  729. */
  730. if (BIT3SET(LN_SENT))
  731.     {
  732.     ret_b(&rlkb, hdr_struct);
  733.         return;
  734.         }
  735.  
  736. /* Reset window timer, if window timer is enabled. 
  737. */
  738. if (BIT2SET(WNDW_TIMER))
  739.     fcw_tm = lcb.window_tmr;
  740.  
  741. /* Accept this LT LPDU if it is the next one in the sequence space
  742. ** and there is room for it (i.e. there is a receive credit).
  743. */
  744. if ((lcb.lcl_credit > 0)
  745.      && (*(hdr_struct->bptr + LT_SEQ) == (USIGN_8) lcb.lt_rsn + 1))
  746.     { 
  747.     ++lcb.lt_rsn;
  748.     --lcb.lcl_credit;
  749.     CLRBIT3(DUP_IGNORED)
  750.         rb_iptr = rf_tiptr;    
  751.         rb_cnt = rb_cnt + rf_datlen;
  752.         }
  753.  
  754. /* Otherwise, ignore the LT LPDU. In this case, set the FORCE_ACK flag
  755. ** to get the sender to ack as rapidly as possible.
  756. */
  757. else
  758.     {
  759.         SETBIT2(FORCE_ACK)
  760.  
  761. /* If the LT LPDU is the same as the last one which arrived, i.e. it is
  762. ** an immediate duplicate, and this is the first occurrence of the
  763. ** immediate duplicate, then don't force the sender to ack this time. 
  764. */
  765.     if ((*(hdr_struct->bptr + LT_SEQ) == lcb.lt_rsn)
  766.         && !BIT3SET(DUP_IGNORED))
  767.         {
  768.         CLRBIT2(FORCE_ACK)
  769.         SETBIT3(DUP_IGNORED)        
  770.         }
  771.         }
  772.  
  773. /* Clean up 
  774. */
  775. ret_b(&rlkb, hdr_struct);
  776. }
  777.  
  778. /*LOCAL----------------------------------------------------------------
  779.  
  780.     snd_framer - handle a transmit holding register empty
  781.                 interrupt.  This subroutine sends a byte-mode frame.
  782.  
  783. ---------------------------------------------------------------------*/
  784.  
  785. void snd_framer()
  786. {
  787.  
  788. extern USIGN_16 port_add;
  789.  
  790. /* Take action based on the current state of the framer state machine. 
  791. */
  792. switch (sf_state)
  793.     {
  794.  
  795. /* State SF_INIT - initial state.  If the mainline has designated an
  796. ** LPDU to frame, the sf_busy flag will be true.  If this is the case,
  797. ** begin the start flag of the frame. 
  798. */
  799.     case SF_INIT:
  800.     if (sf_busy)
  801.         {
  802.         snd_char = SYN;
  803.         sf_state++;
  804.         break;
  805.         }
  806.     else
  807.         {
  808.         frame_dne = 1;
  809.         modem_out_busy = 0;
  810.         return;
  811.         }
  812.  
  813. /* State SF_DLE - Send DLE of start flag. 
  814. */
  815.     case SF_DLE:
  816.         snd_fcs.hi = snd_fcs.low = 0;
  817.         snd_char = DLE;
  818.         sf_state++;                
  819.         break;
  820.  
  821. /* State SF_STX - Send STX of start flag. 
  822. */
  823.     case SF_STX:
  824.         snd_char = STX;
  825.         sf_state++;
  826.         break;
  827.  
  828. /* State SF_INFO -  As long as there are info field bytes, send a 
  829. ** byte and double DLE's as necessary.  At the end of the information
  830. ** field of the frame, start the end flag.   Signal the mainline code
  831. ** that it can enqueue another frame if it is already waiting on
  832. ** 'frame_snt'.  Clearing 'sf_busy' will cause the framer to stop after
  833. ** the end flag unless another frame has been enqueued. 
  834. */
  835.     case SF_INFO:
  836.         if (sf_len > 0)
  837.             {
  838.             if ((snd_char = get_char()) == DLE)
  839.             sf_state++;
  840.             fcscalc(&snd_fcs, snd_char);
  841.             }
  842.         else
  843.             {
  844.             sf_busy = sf_lt = 0;
  845.             frame_snt = 1;
  846.             snd_char = DLE;
  847.             sf_state = SF_ETX;
  848.             }
  849.         break;
  850.  
  851. /* State SF_DLEINFO - Double an info field DLE.
  852. */
  853.     case SF_DLEINFO:
  854.         snd_char = DLE;
  855.         sf_state--;
  856.         break;
  857.  
  858. /* State SF_ETX - Send ETX of end flag.
  859. */
  860.     case SF_ETX:
  861.         snd_char = ETX;
  862.         fcscalc(&snd_fcs, snd_char);
  863.         sf_state++;
  864.         break;
  865.  
  866. /* State SF_FCS1 - Send first byte of frame check sequence.
  867. */
  868.     case SF_FCS1:
  869.         snd_char = snd_fcs.hi;
  870.         sf_state++;
  871.         break;
  872.  
  873. /* State SF_FCS2 - Send second byte of frame check sequence.
  874. ** After this byte is sent, the send framer goes back to its
  875. ** initial state.
  876. */
  877.     case SF_FCS2:
  878.         snd_char = snd_fcs.low;
  879.         sf_state = SF_INIT;
  880.         break;
  881.     }
  882.  
  883. /* Send byte out RS-232 port 
  884. */
  885. outp(port_add, snd_char);
  886.  
  887. }
  888.