home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_300
/
314_01
/
mnpdrvr.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-15
|
22KB
|
889 lines
/*=====================================================================
The Microcom MNP Library
(Microsoft C Version)
10/05/89 - lcb.lt_rsn++ fix in rcv_lt...gp
11/4/87 - explicit data typing...gp
8/27/87 - rcv_lt() modified to use lcb.lcl_credit..gp
-----------------------------------------------------------------------
LDRIVER - MNP Link RS-232 Interrupt Service Routines
This module implements asynchronous I/O for IBM PC-compatible
machines and thus much of this code may not be portable to
other dissimilar environments.
This code implements Class 2 of the MNP link protocol,
i.e. it uses the stop/start byte-oriented framing technique.
A Class 3 implementation is not possible in the IBM PC
environment since the communications hardware is not capable
of switching to synchronous (bit-oriented) operation.
Protocol messages and procedures, however, are independent
of the framing technique.
Global routines:
- ascode: service I/O interrupt
- lne_stat: check carrier status
- trigger_sf: initiate transmit interrupts
=====================================================================*/
/* Header files
*/
#include <dos.h>
#include <mnpdat.h>
/* Interrupt-related definitions
*/
#define INT_8259A_PORT 0x20
#define EOI 0x20
#define RCV_DATA 4
#define THR_EMPTY 2
#define MODSTAT_CHNG 0
/* Send framer state names
*/
#define SF_INIT 0
#define SF_DLE 1
#define SF_STX 2
#define SF_INFO 3
#define SF_DLEINFO 4
#define SF_ETX 5
#define SF_FCS1 6
#define SF_FCS2 7
/* Receive framer state names
*/
#define RF_INIT 0
#define RF_DLE 1
#define RF_STX 2
#define RF_HDLEN 3
#define RF_HEADER 4
#define RF_INFOL 5
#define RF_INFO 6
#define RF_FCS1 7
#define RF_FCS2 8
/* Frame octet definitions
*/
#define SYN 0x16
#define DLE 0x10
#define ETX 3
#define STX 2
/* Length of header buffer (maximum acceptable LPDU header)
*/
#define MAX_HDRLEN 100
/* Bit set, clear and test definitions
*/
#define SETBIT1(bit) lcb.status_1 |= bit;
#define SETBIT2(bit) lcb.status_2 |= bit;
#define SETBIT3(bit) lcb.status_3 |= bit;
#define CLRBIT1(bit) lcb.status_1 &= ~bit;
#define CLRBIT2(bit) lcb.status_2 &= ~bit;
#define CLRBIT3(bit) lcb.status_3 &= ~bit;
#define BIT1SET(bit) (lcb.status_1 & bit)
#define BIT2SET(bit) (lcb.status_2 & bit)
#define BIT3SET(bit) (lcb.status_3 & bit)
/* External references
*/
extern USIGN_16 iir_add; /* int id reg address */
extern USIGN_8 linestat; /* line status var */
extern struct TIMERS tmr;
extern struct link_ctl_blk lcb; /* link control block */
extern struct BUFFER rb, ftb, rlkb; /* buffers */
extern USIGN_8 *rb_iptr; /* rcv buf insert pointer */
extern USIGN_16 rb_cnt,tbcnt; /* buffer counts */
extern USIGN_8 rbuf[RBUF_LEN]; /* receive buffer */
/* Function definitions
*/
void snd_framer();
void rcv_framer();
void mod_stat();
void rcv_lt();
void rcv_la();
void rcv_lna();
void rcv_ln();
/* Local data
*/
USIGN_8 *sf_ptr,
*rf_hptr,
*rf_dptr;
USIGN_8 snd_char,
rcv_char;
struct
{
USIGN_8 low;
USIGN_8 hi;
}
snd_fcs, /* send fcs */
rcv_fcs, /* calculated receive fcs */
rfcs; /* received fcs */
USIGN_16 rdle_flg; /* 'previous char DLE' flag */
USIGN_16 rf_hdrlen, /* receive header length */
hdrcnt,
rf_datlen,
sf_state, /* send framer state var */
rf_state, /* receive framer state var */
sf_len,
sf_busy, /* 'send frame' flag */
sf_lt, /* 'sending LT' flag */
frame_snt, /* 'info field sent' flag */
frame_rcvd, /* 'frame received' flag */
frame_dne; /* 'frame sent' flag */
USIGN_8 *rf_tiptr;
struct MNP_CB *p_mnpcb;
struct BLST *dat_struct, *hdr_struct;
USIGN_16 modem_out_busy; /* RS-232 hardware status flag
0=transmitter not active
1=transmitter active */
/* Function declarations
*/
USIGN_8 get_char();
/*GLOBAL***************************************************************
ascode - MNP RS-232 interrupt service routine
This routine is called by 'async' (in module 'async.asm')
to service an async I/O interrupt.
**********************************************************************/
ascode()
{
USIGN_8 int_id_reg; /* value in int id reg */
/* Handle interrupts for as long as there are any.
*/
for (;;)
{
/* Read the Interrupt Identification Register. If bit 0 is on,
** 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.
*/
if ((int_id_reg = inp(iir_add)) & BIT0_ON)
{
outp(INT_8259A_PORT, EOI);
return;
}
/* Since bit 0 is off, there is an interrupt. Take action based
** on the type of interrupt.
*/
switch (int_id_reg)
{
/* Receive buffer full interrupt
*/
case RCV_DATA:
rcv_framer();
if ((sf_busy == FALSE)
|| ((inp(iir_add + 3) & 0x20) == 0))
break;
/* Transmit buffer empty interrupt
*/
case THR_EMPTY:
snd_framer();
break;
/* Modem status change interrupt
*/
case MODSTAT_CHNG:
mod_stat();
break;
}
}
}
/*GLOBAL***************************************************************
lne_stat - physical-connection status routine
**********************************************************************/
SIGN_16 lne_stat()
{
/* If the carrier detect bit is 1, return success (physical-connection
** still active). Otherwise, return with a failure indication.
*/
if (linestat & 0x80)
return (SUCCESS);
else
return (1);
}
/*GLOBAL***************************************************************
trigger_sf - initiate send framer
**********************************************************************/
void trigger_sf()
{
extern USIGN_16 port_add;
/* If the RS-232 transmitter is not already busy, then a byte must
** be placed in the transmit buffer to begin interrupt-driven sending.
** This is the start of a frame and thus the first byte to be sent is
** the SYN which begins the frame start flag. Also the send framer's
** state variable is initialed here.
*/
if (!modem_out_busy)
{
modem_out_busy = TRUE;
sf_state = SF_DLE;
outp(port_add,SYN);
}
}
/*LOCAL----------------------------------------------------------------
get_char - get byte from send buffer
---------------------------------------------------------------------*/
USIGN_8 get_char()
{
/* Return the byte at the current send framer pointer position.
** In the process, advance the point for next time and reduce the
** number of bytes remaining by one.
*/
sf_len--;
return (*sf_ptr++);
}
/*LOCAL----------------------------------------------------------------
mod_stat - modem status change interrupt service routine
---------------------------------------------------------------------*/
void mod_stat()
{
/* Read modem status register and save the value.
*/
linestat = inp(iir_add + 4);
/* If carrier has been lost, signal the mainline that the link
** also has been lost by lowering the 'link established' flag.
*/
if (!(linestat & 0x80))
CLRBIT1(LINK_EST)
}
/*LOCAL----------------------------------------------------------------
put_char - store an LT data byte
---------------------------------------------------------------------*/
void put_char()
{
/* Ignore this byte if the amount of received data exceeds the
** negotiated maximum user data size for the LT LPDU. This could occur
** if the frame is broken or if the correspondent is misbehaving.
*/
if (rf_datlen >= lcb.max_data_sz)
return;
/* Otherwise, accept this byte.
*/
rf_datlen++;
fcscalc (&rcv_fcs, rcv_char);
/* Now store the data byte in the receive buffer using the temporary
** insert pointer. Advance the pointer, checking for wrap.
*/
*rf_tiptr++ = rcv_char;
if (rf_tiptr >= rbuf + RBUF_LEN)
rf_tiptr = rbuf;
}
/*LOCAL----------------------------------------------------------------
put_hdr - store an LPDU header byte
---------------------------------------------------------------------*/
void put_hdr()
{
/* Store header byte in header buffer. Increment count of bytes in
** the buffer and include the byte in the receive fcs.
*/
rf_hdrlen++;
*rf_hptr++ = rcv_char;
fcscalc (&rcv_fcs, rcv_char);
}
/*LOCAL----------------------------------------------------------------
RCV_FRAMER - Receive Framer
---------------------------------------------------------------------*/
void rcv_framer()
{
extern USIGN_16 port_add;
/* Get received data byte and save it for later
*/
rcv_char = inp(port_add);
/* Take action based on current state of receive finite state machine.
** 'rf_state' is the state variable.
*/
switch (rf_state)
{
/* State RF_INIT - This is the initial state. Search the received
** data stream for a SYN character which may be the beginning
** of the start flag sequence of a byte mode frame.
*/
case RF_INIT:
if (rcv_char == SYN)
rf_state++;
break;
/* State RF_DLE - Search for the second byte in the start flag, a DLE
** byte. Go back to the initial state (SYN search) if this byte is
** something other than a DLE.
*/
case RF_DLE:
if (rcv_char == DLE)
rf_state++;
else
rf_state--;
break;
/* State RF_STX - If the received byte is an STX then a frame start flag
** has been found. In this case, initialize to receive an LPDU.
** Otherwise, go back to looking for a SYN.
*/
case RF_STX:
if (rcv_char == STX)
{
get_b(&rlkb,&hdr_struct);
rf_hptr = hdr_struct->bptr;
hdrcnt=rf_hdrlen=rf_datlen=rdle_flg=0;
rcv_fcs.hi=rcv_fcs.low=NULL;
rf_state++;
}
else
rf_state = RF_INIT;
break;
/* State RF_HDLEN - Get the first byte of the LPDU, the length
** indicator. Wait for the next byte if this happens to be a DLE (in a
** good frame the DLE will be doubled). If the value received is
** greater than this implementation's maximum header length, it must be
** a protocol error or the value is broken. In either case, ignore this
** LPDU. This is like a broken frame, so force the sender to ack.
*/
case RF_HDLEN:
if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
{
rdle_flg = 0;
if (rcv_char > MAX_HDRLEN)
{
ret_b(&rlkb, hdr_struct);
SETBIT2(FORCE_ACK)
rf_state = RF_INIT;
}
else
{
hdrcnt = rcv_char;
put_hdr();
rf_state++;
}
}
else
rdle_flg = TRUE;
break;
/* State RF_HEADER - Receive header bytes, assuming the length indicator
** was correct. Any bytes following the header will temporarily be
** assumed to be data.
*/
case RF_HEADER:
if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
{
rdle_flg = FALSE;
put_hdr();
if (!(--hdrcnt))
rf_state++;
}
else
rdle_flg = TRUE;
break;
/* State RF_INFOL - Receive first data byte. Note that we must check
** for the stop flag just in case the length indicator byte was broken. ** When the first data byte is received, the temporary insert pointer
** into the data receive buffer is initialized to the current location
** of the real pointer.
*/
case RF_INFOL:
if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
{
if ((rcv_char == ETX) && rdle_flg)
{
fcscalc(&rcv_fcs, rcv_char);
rf_state = RF_FCS1;
}
else
{
rdle_flg = FALSE;
rf_tiptr = rb_iptr;
put_char();
rf_state++;
}
}
else
rdle_flg = TRUE;
break;
/* State RF_INFO - Receive data bytes until the stop flag is found.
*/
case RF_INFO:
if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
{
if ((rcv_char == ETX) && rdle_flg)
{
fcscalc(&rcv_fcs, rcv_char);
rf_state++;
}
else
put_char();
rdle_flg = FALSE;
}
else
rdle_flg = TRUE;
break;
/* State RF_FCS1 - Receive first byte of fcs.
*/
case RF_FCS1:
rfcs.hi = rcv_char;
rf_state++;
break;
/* State RF_FCS2 - Receive second (low) byte of fcs and take end-of-
** frame action. If the calculated fcs is not identical to that
** received, the received frame is "broken". Signal this to the sender
** by setting the LPDU type to 0 and setting the FORCE_ACK flag. If the
** FCS's match, take action based on the type of LPDU received.
*/
case RF_FCS2:
if ((rcv_fcs.hi != rfcs.hi) || (rcv_fcs.low != rcv_char))
{
lcb.lpdu_type = 0;
SETBIT2(FORCE_ACK);
ret_b(&rlkb, hdr_struct);
}
else
{
switch (lcb.lpdu_type = *(hdr_struct->bptr + PDU_TYPE))
{
case LT:
rcv_lt();
break;
case LA:
rcv_la();
break;
/* Link Request - If an LR arrives during what we think is the data
** phase, the other side may be retransmitting. In this case, just
** discard the LR. Otherwise, save the LR for the mainline to process.
** In either case, signal the mainline to ack.
*/
case LR:
if (BIT1SET(LINK_EST))
ret_b(&rlkb, hdr_struct);
else
rlkb.used_lst = hdr_struct;
SETBIT2(FORCE_ACK)
break;
case LN:
rcv_ln();
break;
case LNA:
rcv_lna();
break;
/* Link Disconnect - Signal link termination to the mainline. If the LD
** contains a user reason byte (signalled by parm 1 having a value of
** 255), save it in the LCB.
*/
case LD:
CLRBIT1(LINK_EST)
if (*(hdr_struct->bptr + 4) == 255)
{
p_mnpcb->ld_source = 1;
p_mnpcb->ld_reason =
*(hdr_struct->bptr+7);
}
else
p_mnpcb->ld_source = 0;
break;
}
}
/* Signal the mainline that a frame has been received and reset the
** receive frame state machine for the next frame.
*/
frame_rcvd = TRUE;
rf_state = RF_INIT;
break;
}
}
/*LOCAL----------------------------------------------------------------
rcv_la - process a received LA LPDU
---------------------------------------------------------------------*/
void rcv_la()
{
register USIGN_8 *p;
USIGN_16 i;
USIGN_8 lt_ret_seq;
/* Ignore this LA LPDU if a destructive attention is in progress.
*/
if (BIT3SET(LN_SENT))
{
ret_b(&rlkb,hdr_struct);
return;
}
/* For reasons of robustness, check the validity of the LA LPDU
*/
p = hdr_struct->bptr + 2; /* point to parm 1 */
if ((*p++ != 1) || (*p != 1))
{
ret_b(&rlkb,hdr_struct);
return;
}
p += 2;
if ((*p++ != 2) || (*p != 1))
{
ret_b(&rlkb,hdr_struct);
return;
}
/* If the receive sequence number of this LA is the same as the last
** one received, it may be an implied negative acknowledgment. Force
** the mainline to consider retransmission by setting the 'force
** retransmission' flag. Otherwise, save the new number as the last
** acked and set the 'good ack received' flag.
*/
p -= 2;
if (*p == lcb.ltssn_acked)
SETBIT2(FORCE_RET)
else
{
lcb.ltssn_acked = *p;
SETBIT1(LA_RECEIVED)
}
/* Process the new credit information received. If it is zero, return.
*/
p += 3;
if ((lcb.rem_credit = *p) == 0)
{
ret_b(&rlkb,hdr_struct);
return;
}
/* If credit is not zero and this ack was a good one, take into account
** the LT LPDUs that may have been sent since the correspondent sent
** this LA. That is, subtract from the credit the number of these not
** yet acked LTs.
*/
if (BIT1SET(LA_RECEIVED) && tbcnt)
{
lt_ret_seq = *(ftb.used_lst->bptr + LT_SEQ);
if (lt_ret_seq <= lcb.ltssn_acked)
i = (lcb.ltssn_acked - lt_ret_seq) + 1;
else
i = (256 - lt_ret_seq) + lcb.ltssn_acked + 1;
i = tbcnt - i;
if (lcb.rem_credit >= i)
{
lcb.rem_credit -= i;
if (lcb.rem_credit < 0)
lcb.rem_credit = 0;
}
else
lcb.rem_credit = 0;
}
/* Clean up
*/
ret_b(&rlkb, hdr_struct);
}
/*LOCAL----------------------------------------------------------------
rcv_ln - process a received LN LPDU
---------------------------------------------------------------------*/
void rcv_ln()
{
register USIGN_8 *p;
/* Check the sequence number of this LN LPDU. If it is the next one
** in the sequence space, signal its arrival to the sender by
** setting the LN_RECEIVED flag. Also remember the type of the LN LPDU
** by saving the type parameter value in the LCB.
*/
p = hdr_struct->bptr + 4;
if (*p == (USIGN_8) ++lcb.ln_rsn)
{
lcb.ln_rtype = *(p + 3);
SETBIT3(LN_RECEIVED)
}
/* If this is not the next one, then decrement the receive sequence
** number which was incremented above and signal the need to send an
** LNA LPDU.
*/
else
{
lcb.ln_rsn--;
SETBIT3(FORCE_LNA)
}
ret_b(&rlkb, hdr_struct);
}
/*LOCAL----------------------------------------------------------------
rcv_lna - process a received LNA LPDU
---------------------------------------------------------------------*/
void rcv_lna()
{
register USIGN_8 *p;
/* If the LNA LPDU is an acknowledgment of an outstanding LN, its
** sequence number will match that of the last LN sent. In this
** case, signal the sender that the LN has been acknowledged.
** Otherwise, just ignore this LPDU.
*/
if (*(hdr_struct->bptr + 4) == lcb.ln_ssn)
{
lcb.ln_ret_cnt = NULL;
CLRBIT3(LN_SENT)
CLRBIT3(LN_TIMER)
tmr.ln = NULL;
}
ret_b(&rlkb, hdr_struct);
}
/*LOCAL----------------------------------------------------------------
rcv_lt - process a received LT LPDU
---------------------------------------------------------------------*/
void rcv_lt()
{
/* Ignore this LT LPDU if a destructive attention is in progress.
*/
if (BIT3SET(LN_SENT))
{
ret_b(&rlkb, hdr_struct);
return;
}
/* Reset window timer, if window timer is enabled.
*/
if (BIT2SET(WNDW_TIMER))
tmr.fcw = lcb.window_tmr;
/* Accept this LT LPDU if it is the next one in the sequence space
** and there is room for it (i.e. there is a receive credit).
*/
if ((lcb.lcl_credit > 0)
&& (USIGN_8)(*(hdr_struct->bptr + LT_SEQ) == (USIGN_8) (lcb.lt_rsn+1)))
{
lcb.lt_rsn++;
--lcb.lcl_credit;
CLRBIT3(DUP_IGNORED)
rb_iptr = rf_tiptr;
rb_cnt = rb_cnt + rf_datlen;
}
/* Otherwise, ignore the LT LPDU. In this case, set the FORCE_ACK flag
** to get the sender to ack as rapidly as possible.
*/
else
{
SETBIT2(FORCE_ACK)
/* If the LT LPDU is the same as the last one which arrived, i.e. it is
** an immediate duplicate, and this is the first occurrence of the
** immediate duplicate, then don't force the sender to ack this time.
*/
if ((*(hdr_struct->bptr + LT_SEQ) == lcb.lt_rsn)
&& !BIT3SET(DUP_IGNORED))
{
CLRBIT2(FORCE_ACK)
SETBIT3(DUP_IGNORED)
}
}
/* Clean up
*/
ret_b(&rlkb, hdr_struct);
}
/*LOCAL----------------------------------------------------------------
snd_framer - handle a transmit holding register empty
interrupt. This subroutine sends a byte-mode frame.
---------------------------------------------------------------------*/
void snd_framer()
{
extern USIGN_16 port_add;
/* Take action based on the current state of the framer state machine.
*/
switch (sf_state)
{
/* State SF_INIT - initial state. If the mainline has designated an
** LPDU to frame, the sf_busy flag will be true. If this is the case,
** begin the start flag of the frame.
*/
case SF_INIT:
if (sf_busy)
{
snd_char = SYN;
sf_state++;
break;
}
else
{
frame_dne = 1;
modem_out_busy = FALSE;
return;
}
/* State SF_DLE - Send DLE of start flag.
*/
case SF_DLE:
snd_fcs.hi = snd_fcs.low = 0;
snd_char = DLE;
sf_state++;
break;
/* State SF_STX - Send STX of start flag.
*/
case SF_STX:
snd_char = STX;
sf_state++;
break;
/* State SF_INFO - As long as there are info field bytes, send a
** byte and double DLE's as necessary. At the end of the information
** field of the frame, start the end flag. Signal the mainline code
** that it can enqueue another frame if it is already waiting on
** 'frame_snt'. Clearing 'sf_busy' will cause the framer to stop after
** the end flag unless another frame has been enqueued.
*/
case SF_INFO:
if (sf_len > 0)
{
if ((snd_char = get_char()) == DLE)
sf_state++;
fcscalc(&snd_fcs, snd_char);
}
else
{
sf_busy = sf_lt = FALSE;
frame_snt = TRUE;
snd_char = DLE;
sf_state = SF_ETX;
}
break;
/* State SF_DLEINFO - Double an info field DLE.
*/
case SF_DLEINFO:
snd_char = DLE;
sf_state--;
break;
/* State SF_ETX - Send ETX of end flag.
*/
case SF_ETX:
snd_char = ETX;
fcscalc(&snd_fcs, snd_char);
sf_state++;
break;
/* State SF_FCS1 - Send first byte of frame check sequence.
*/
case SF_FCS1:
snd_char = snd_fcs.hi;
sf_state++;
break;
/* State SF_FCS2 - Send second byte of frame check sequence.
** After this byte is sent, the send framer goes back to its
** initial state.
*/
case SF_FCS2:
snd_char = snd_fcs.low;
sf_state = SF_INIT;
break;
}
/* Send byte out RS-232 port
*/
outp(port_add, snd_char);
}