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;
}
}