n2dsy@hou2d.UUCP (G.BEATTIE) (04/11/88)
OK Folks ! I have received several notes and messages about the distribution of our software and documents via the net. Let's understand a few basic points here: ISSUE: I don'thave access to ftp to outside systems from hou2d. If someone has such a package with source for a 3B1 and sends me a DISK, I will get it going. I think I can get a network connection at NJIT, but I will entertain other offers. ISSUE: Hate mail for using the net for distribution. The response has been as follows: 2 mailer generated messages signalling error. 2 human generated messages saying No Good. 6 human generated messages saying Thanks. I wished every comment I make on the net could fair this well! One interesting note: one of the messages that said "Don't do it" came from an individual who doesn't like AFT and is PUSHING SLIP... I will re-examine the distribution mechanism and see what makes sense. I will probably forward out the code through "comp.protocols.iso" in the future. I will continue to send out documents and announcements via several groups. Comments? Thanks, J. Gordon Beattie, Jr. E-mail: ihnp4!hou2d!n2dsy (Unix) n2dsy@kd6th.a3100201.ampr Telephone: 201-615-4168 (Office) 201-615-4669 (Office FAX) Telephone: 201-387-8896 (Home)
n2dsy@hou2d.UUCP (G.BEATTIE) (04/13/88)
/* Machine Independent Asynchronous Framing Technique (AFT) Version 1.0, by John Howell (N2FVN), Copyright August 7, 1987 This software is free for non-commercial use. All commercial rights are retained by the author or his designees. Commercial use without the explicit written permission of the author is prohibited. This package is provided on an 'as is' basis without warranty. */ /* Macros */ #define FLAG 0x007E /* This is the flag character which is used to start and end frames. The send generates separate starting and ending flag characters, but the receiver can accept a single flag to end one frame and start another. */ #define LEAD_IN 0x007D /* This is the lead in character which is used when transparency is required. When the receiver detects this character it takes the following character and exclusive ors it with 0x20 to produce the proper character. This is used to allow the transmission of characters which would not otherwise be received properly. */ #define TRANSPARENT(c) (c ^ 0x20) /* This is the function to be performed to convert a character into one which may be transmitted transparently. This is also used to return a character to its original value since the function is its own inverse. */ #define IDLE_CODE (0x0100 + FLAG) /* This is the idle code with is returned by aft_tx_char when it has no frame to send. The high order byte indicates completion and the low order byte is a flag which might be sent if flags are to be used between frames as filler. */ #define GENERATOR 0x8408 /* This is the generator polynomial for the frame check sequence. The polynomial is the standard CCITT polynomial. It is in reverse bit order for faster calculation. The polynomial is X**16 + X**12 + X**5 + 1. */ #define EXPECT_FCS 0xF0B8 /* This is the expected final FCS for a correctly received frame. */ #define OK 1 /* This is the return code used by some routines to indicate successful completion. */ #define NO_GOOD 0 /* This is the return code used by some routines to indicate unsuccessful completion. */ /* Global declarations */ static char opt_ebdt; /* This is a flag controlling the use of eight-bit data transparency on transmitted and receive frames. */ static char opt_transparency_level; /* This is the transparency level to be used when transmitting frames (0-2). Each increasing level causes more characters to be avoided when sending frames which allows them to be sent in situations where certain characters are not allowed. */ static char opt_suffix_len; static unsigned char opt_suffix[3]; /* This is the suffix string to be added to the end of each frame sent. Any character including a null is allowed. */ static unsigned int opt_max_rx_len; /* This is the maximum size of a receive buffer. It is used when receiving to make sure that a received frame does not go past the end of the buffer. A received frame which is too long will be discarded. */ static unsigned char transparency_level [256] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}; /* For each possible character this is the transparency level at which it should be encoded for transparency. A value of nine is used for character which should never be encoded. */ /* Variables and macros used by aft_tx_char */ static char tx_char_state; /* This is the state of the transmission routine which handles transparency, delimiting of frames, and frame abort. It is used to determine the next action to be performed by aft_tx_char when it is called. State names are given by the 'TCS_' macros. */ #define TCS_IDLE 0 /* This state indicates that no frame is being sent. Calls to aft_tx_char while in this state return the idle code and calls to aft_tx_complete return one. This is the only state in which a call to aft_tx_start is valid. */ #define TCS_START_FLAG 1 /* This state indicates that the starting flag for a frame should be sent when aft_tx_char is next called. This state is entered when aft_tx_start accepts a frame to be sent. */ #define TCS_GET_NEXT_CHAR 2 /* This state indicates that the next character to be sent should be gotten by calling tx_ebdt. Before being sent the character will be checked against a table to see if a substitution is required. If so then the current character is saved and a lead-in character is sent. */ #define TCS_SUBSTITUTE_CHAR 3 /* This state indicates that the next character to be sent is the encoded value of the current character. This is used for transparency purposes. */ #define TCS_SUFFIX 4 /* This state is used when the closing flag has been sent to indicate the need to send any additional defined suffix characters at the end of the frame. Once all suffix characters have been sent the transmitter returns to the TCS_IDLE state. */ #define TCS_ABORT_LEAD_IN 5 /* This state indicates that the caller has decided to abort the frame before it has been completely sent. A lead in character followed by a flag will be sent to indicate that the receiver should discard the frame. */ #define TCS_ABORT_FLAG 6 /* The lead-in character has been sent to abort the current frame. A flag will be now sent to complete the abort sequence. */ static unsigned char tx_encode_char; /* This is the character to be sent after it is encoded for transparency. The value is saved here while waiting for aft_tx_char to be sent after it has returned the transparency lead-in character. */ static char tx_suffix_count; /* This is a count of the number of suffix characters which have been sent so far at the end of the frame. */ /* Variables and macros used by tx_ebdt */ static char tx_ebdt_state; /* This is the state of the eight bit data transparency routines. The value is actually a count of the number of characters which have each contributed their high order bit to the tx_ebdt_char. It is reset back to zero when the ebdt character is sent. */ static unsigned char tx_ebdt_char; /* This is the character being built up from the high order bits of other characters during transmission when eight-bit data transparency is enabled. This will be sent once either seven bits have been collected or the frame ends. */ /* Variables and macros used by tx_data */ static char tx_data_state; /* This is the state of the routines which get data from the transmit buffer, calculate the frame check sequence, and send the frame check sequence. State names are given by the 'TDS_' macros. */ #define TDS_GET_FROM_BUFFER 0 /* This state indicates that the next character to be sent should be gotten from the transmit buffer. The FCS is updated based on the character gotten. Once all characters from the buffer have been sent the FCS will be sent. */ #define TDS_FCS_ONE 1 /* This state indicates that sending of characters from the buffer has completed and that the next character to be sent is the first half of the frame check sequence. */ #define TDS_FCS_TWO 2 /* This state indicates that the first half of the FCS has been sent and so the second half should be sent next. */ #define TDS_COMPLETE 3 /* This state indicates that sending of the FCS is complete. */ static unsigned char *tx_buffer; static unsigned int tx_len_remaining; /* tx_buffer is a pointer to the next character to be sent from the transmit buffer provided by the caller of aft_tx_start. It is incremented as each character is used from the buffer. The length remaining field is decremented as each character is used. When it reaches zero all characters from the supplied buffer have been sent and FCS transmission may begin. */ static unsigned int tx_fcs; /* This is the two bytes of the frame check sequence (FCS) to be sent at the end of the frame being transmitted stored in reverse bit order for easier transmission. The FCS is initialized to all ones before the frame is transmitted. As the frame is sent the FCS is calculated using the algorithm specified in CCITT X.25 using each character as it is obtained from the transmit buffer. */ /* Variables and macros for aft_rx_char */ static unsigned char rx_char_state; /* This is the state of the main reception routine. It is used to determine the next action performed by aft_rx_char when it is called. State names are given by the 'RCS_' macros. */ #define RCS_IDLE 0 /* This state indicates that no buffer has been provided for reception. Received characters are ignored while in this state. */ #define RCS_START_FLAG 1 /* This state indicates that a buffer has been provided to the receive routines and we are waiting to receive the opening flag for a frame. Non-flag characters will be ignored in this state. */ #define RCS_AWAIT_NEXT_CHAR 2 /* This state indicates that the opening flag has been received and characters are being received into the frame buffer. If the character received is the ending flag then the frame size and FCS are verified otherwise the chracter is passed to the eight-bit data transparency routine for processing. */ #define RCS_AWAIT_SUBSTITUTE_CHAR 3 /* This state indicates that we have received a lead in character and are now expecting another character which will decoded and sent to the EBDT routines. If the character received is a flag then this indicates that the sender is aborting the frame so it should be discarded. */ static int rx_ebdt_state; /* This is the number of characters received since the last eight bit data transparency character. This will reach eight when the next EBDT character is received. If the frame ends with this count non-zero then the character just received is the EBDT character for a group of less than seven bytes. */ static unsigned char rx_ebdt_save[7]; /* This array is used to buffer a maximum of seven characters when eight bit data transparency is being used. When the EBDT character is received then each saved character is modified to have the correct eighth bit and transferred to the receive buffer. */ static unsigned char *rx_buffer; /* rx_buffer holds the pointer to the start of the receive buffer. */ static int rx_len, rx_final_len; /* rx_len is the number of characters received so far in the current frame. rx_final_len is initialized to zero and set to the final frame length once the frame has been completely received. */ static unsigned int rx_fcs; /* This is the two bytes of the receive frame check sequence stored in reverse bit order. It is calculated as characters are received using the same method as that used for tx_fcs. At the end of the frame this must match the expected value or the frame is invalid. */ aft_options( o_ebdt, o_transparency_level, o_suffix_len, o_suffix, o_max_rx_len) char o_ebdt; /* Eight-bit data xparency (nonzero) */ char o_transparency_level; /* zero to two */ char o_suffix_len; /* zero to three */ char o_suffix[3]; /* suffix string to be sent */ int o_max_rx_len; /* size of rx buffers */ { int count; /* Suffix character count */ /* Initialize internal state variables */ tx_char_state = TCS_IDLE; rx_char_state = RCS_IDLE; rx_final_len = 0; /* Save selected options */ opt_ebdt = o_ebdt; opt_transparency_level = o_transparency_level; opt_suffix_len = o_suffix_len; for (count = 0; count < sizeof(opt_suffix); count++) opt_suffix[count] = o_suffix[count]; opt_max_rx_len = o_max_rx_len; } int aft_tx_start(length, buffer) unsigned int length; /* length of the buffer */ unsigned char *buffer; /* buffer pointer */ { /* Only allow if transmitter is idle and frame is valid */ if ((tx_char_state != TCS_IDLE) || (length < 2)) return NO_GOOD; /* Set internal state variables for transmission */ tx_data_state = TDS_GET_FROM_BUFFER; tx_buffer = buffer; /* Save pointer to next char */ tx_len_remaining = length; tx_fcs = 0xFFFF; /* Set all FCS bits to one. */ tx_ebdt_state = /* No EBDT char being built */ tx_ebdt_char = tx_suffix_count = 0; /* No suffix characters sent yet. */ /* Now that all is ready the state can be changed. This could not be done before since aft_tx_char might have been called before setup was complete. It is assumed that since the state is a 'char' it can be assigned while interrupts are enabled since it will be changed with a single write to memory. The state is set so that the starting flag will be the next character sent. */ tx_char_state = TCS_START_FLAG; return OK; /* successful return */ } int aft_tx_char() { int c; /* The character to be sent */ /* This routine is responsible for returning each character to be sent over the communication line. It handles generation of starting and ending flags, data transparency, suffix character generation, and frame abort generation. tx_ebdt is called to determine data characters to be sent. */ switch (tx_char_state) { case TCS_IDLE: c = IDLE_CODE; break; case TCS_START_FLAG: /* We are starting to send out a new frame. Send out the starting flag before any data. */ c = FLAG; tx_char_state = TCS_GET_NEXT_CHAR; break; case TCS_GET_NEXT_CHAR: c = tx_ebdt(); /* Get next character */ if (c == IDLE_CODE) { /* The entire frame has been sent. Delimit with a flag. */ c = FLAG; if (opt_suffix_len > 0) tx_char_state = TCS_SUFFIX; else tx_char_state = TCS_IDLE; } else { if (transparency_level[c] <= opt_transparency_level) { /* This character cannot be sent. Use transparency. */ tx_encode_char = c; c = LEAD_IN; tx_char_state = TCS_SUBSTITUTE_CHAR; } } break; case TCS_SUBSTITUTE_CHAR: c = TRANSPARENT(tx_encode_char); tx_char_state = TCS_GET_NEXT_CHAR; break; case TCS_SUFFIX: c = opt_suffix[tx_suffix_count++]; if (tx_suffix_count >= opt_suffix_len) tx_char_state = TCS_IDLE; break; case TCS_ABORT_LEAD_IN: c = LEAD_IN; tx_char_state = TCS_ABORT_FLAG; break; case TCS_ABORT_FLAG: c = FLAG; tx_char_state = TCS_IDLE; } return c; } static int tx_ebdt() { int c; /* Character to be returned */ /* This routine handles eight-bit data transparency. If enabled the high order bit of each character returned by tx_data is removed and added to an eight-bit data transparency character. Once this character has collected seven bits or the frame ends this character is sent. Doing this allows the receiver to recover the high order bits from the previous characters sent. */ if (!opt_ebdt) { /* If eight bit data transparency is not being used then this is just a pass through. If this option is never used then this routine may be completely eliminated for efficiency. */ c = tx_data(); } else { if (tx_ebdt_state == 7) { /* There is a full collection of seven high order bits ready to be sent. */ c = tx_ebdt_char; tx_ebdt_char = tx_ebdt_state = 0; } else { c = tx_data(); if (c != IDLE_CODE) { /* Append the high order bit from the character to be sent to the EBDT character. */ tx_ebdt_char = (tx_ebdt_char << 1) + ((c & 0x80) != 0); c = c & 0x7F; tx_ebdt_state++; } else { /* The end of the frame has been reached. If there have been any high order bits collected then send one last EBDT character. */ if (tx_ebdt_state != 0) { c = tx_ebdt_char << (7 - tx_ebdt_state); tx_ebdt_char = tx_ebdt_state = 0; } else { c = IDLE_CODE; } } } } return c; } static int tx_data() { int c; /* Character to be returned */ char tx_bits, bit_count; /* For FCS calculation */ /* The next character is obtained from the transmit buffer and the FCS calculated. Once all character from the buffer have been sent, the FCS is also sent. */ switch (tx_data_state) { case TDS_GET_FROM_BUFFER: c = *(tx_buffer++); /* FCS calculation */ tx_bits = c; for (bit_count = 0; bit_count < 8; bit_count++) { if ((tx_fcs ^ tx_bits) & 1) tx_fcs = (tx_fcs >> 1) ^ GENERATOR; else tx_fcs = tx_fcs >> 1; tx_bits = tx_bits >> 1; } if (--tx_len_remaining == 0) tx_data_state = TDS_FCS_ONE; break; case TDS_FCS_ONE: /* Send the first character of the frame check sequence. The bits are inverted before sending. */ c = (~tx_fcs) & 0x00ff;; tx_data_state = TDS_FCS_TWO; break; case TDS_FCS_TWO: /* Send the second character of the frame check sequence. The bits are inverted before sending. */ c = (~tx_fcs) >> 8; tx_data_state = TDS_COMPLETE; break; case TDS_COMPLETE: c = IDLE_CODE; break; } return c; } aft_tx_abort() { /* If aft_send_char is being called from within an interrupt service routine and this routine is intended to be used then interrupts must be disabled when this routine is called. The current state is changed to cause an abort sequence to be sent to receiver. */ switch (tx_char_state) { case TCS_START_FLAG: tx_char_state = TCS_IDLE; break; case TCS_GET_NEXT_CHAR: tx_char_state = TCS_ABORT_LEAD_IN; break; case TCS_SUBSTITUTE_CHAR: tx_char_state = TCS_ABORT_FLAG; break; default: break; } } int aft_tx_complete() { return (tx_char_state == TCS_IDLE); } int aft_rx_start(buffer) unsigned char *buffer; { /* Provide a buffer to start receiving the next frame into. If we are already receiving then the request is rejected. */ if (rx_char_state != RCS_IDLE) return NO_GOOD; /* Save the pointer to the buffer and set up for receive. */ rx_buffer = buffer; aft_rx_error(); /* Reset the receive routines */ rx_char_state = RCS_START_FLAG; return OK; } aft_rx_error() { /* Return to the condition of waiting for the starting flag. */ rx_len = rx_final_len = rx_ebdt_state = 0; rx_fcs = 0xffff; if (rx_char_state != RCS_IDLE) rx_char_state = RCS_START_FLAG; } int aft_rx_complete() { return rx_final_len; } int aft_rx_char(c) unsigned char c; { /* Add the next character to the receive buffer depending on the current state. */ if (opt_ebdt) { /* Ignore the high order bit of the character if it cannot be received transparently. */ c = c & 0x7f; } switch (rx_char_state) { case RCS_IDLE: /* If no buffer has been provided then ignore characters. */ break; case RCS_START_FLAG: /* In this state we are hunting for a flag. If this is a flag then the receive operation may begin. */ if (c == FLAG) rx_char_state = RCS_AWAIT_NEXT_CHAR; break; case RCS_AWAIT_NEXT_CHAR: if (c == LEAD_IN) { rx_char_state = RCS_AWAIT_SUBSTITUTE_CHAR; } else { if (c == FLAG) { rx_finish_ebdt(); /* If the frame is too short or the FCS is not correct then the frame is discarded. */ /* For FCS */ rx_final_len = rx_len - 2; if (rx_final_len >= 2 && rx_fcs == EXPECT_FCS) rx_char_state = RCS_IDLE; else aft_rx_error(); } else { rx_ebdt(c); } } break; case RCS_AWAIT_SUBSTITUTE_CHAR: /* Handle the character which follows a lead-in character. If it is a flag then this is a frame abort and so the frame is ignored. Otherwise translate the character and add it to the buffer. */ if (c != FLAG) { rx_ebdt(TRANSPARENT(c)); rx_char_state = RCS_AWAIT_NEXT_CHAR; } else { aft_rx_error(); } break; } return rx_final_len; } rx_ebdt(c) unsigned char c; { int bit_count; /* This routine handles received characters which have passed the normal substitution process. It handles the eight-bit data transparency by saving characters until the transparency character is detected and then adding the correct high order bit to each of the characters. */ if (!opt_ebdt) { rx_data(c); } else { if (rx_ebdt_state >= 7) { for (bit_count = 0; (bit_count < 7) && (rx_ebdt_state != 0); bit_count ++) { c = c << 1; rx_data(rx_ebdt_save[bit_count] | (c & 0x80)); } rx_ebdt_state = 0; } else { rx_ebdt_save[rx_ebdt_state++] = c; } } } rx_finish_ebdt() { unsigned char c; int bit_count; /* This routine finishes any pending EBDT conversion when the end of the frame is detected. */ if (opt_ebdt && rx_ebdt_state > 0) { c = rx_ebdt_save[--rx_ebdt_state]; for (bit_count = 0; (bit_count < rx_ebdt_state) && (rx_ebdt_state != 0); bit_count ++) { c = c << 1; rx_data(rx_ebdt_save[bit_count] | (c & 0x80)); } rx_ebdt_state = 0; } } rx_data(c) unsigned char c; { int bit_count; unsigned char rx_bits; /* FCS calculation */ rx_bits = c; for (bit_count = 0; bit_count < 8; bit_count++) { if ((rx_fcs ^ rx_bits) & 1) rx_fcs = (rx_fcs >> 1) ^ GENERATOR; else rx_fcs = rx_fcs >> 1; rx_bits = rx_bits >> 1; } if (rx_len == opt_max_rx_len) { aft_rx_error(); } else { *(rx_buffer + (rx_len++)) = c; } }