This is serial.c in view mode; [Download] [Up]
#include "includes.h"
#include "debug.h"
extern char *term_server;
extern int fudge_flow;
extern int byte_shift;
/*
* Handles the serial side of things.
*
* 4 main routines..
* do_serial_in() is called when the serial port is ready for reading and
* the in packet buffer isn't full.
* do_serial_out() is called when the serial port is available for writing and
* there are packets waiting.
*
* Compression is done at this level. I opted for the computational more
* expensive method of trying to compress each packet, rewinding the dictionary
* if it failed.
*
* this module sees 4 buffers.
* serial_in[] are characters read from modem.
* serial_out[] are characters waiting to be sent to modem.
*
* Note that serial_in() won't be called if link_in() has any characters in it.
*/
/*----------------------------------------------------------------------*/
/* function prototypes for this module */
void do_ack(int);
void send_ack(int);
int check_match(int, int);
/*----------------------------------------------------------------------*/
/* Various ring buffers */
struct Buffer serial_in = {{0},0,0,0}, serial_out = {{0},0,0,0};
int inhabit_send = 0;
#define PUT_SERIAL(c) add_to_buffer(&serial_out, c)
#define GET_SERIAL() get_from_buffer(&serial_in);
/* Packet information */
struct Packet_out p_out[32];
int p_out_s, p_out_e;
int p_out_num;
struct Packet_in p_in[32];
int p_in_e;
int p_in_num;
void serial_init() {
int i;
p_out_s =
p_out_e =
p_out_num =
p_in_e =
p_in_num = 0;
for (i = 0; i < 32;++i) {
p_out[i].type = -1;
p_in[i].type = -1;
}
}
/*---------------------------------------------------------------------------*/
/* Takes a byte, and puts it in the serial buffer. */
/* If nessecery, it emits escape characters untill the byte is valid */
/* for the serial line. It adds 33. I hope 33 is a generator for */
/* 0-255. As you can see, it is VERY expensive if there are a lot of */
/* escaped characters. Hopefully this won't be the case. */
void put_serial(int a) {
static int f_c = 1;
if (fudge_flow && !--f_c) { /* If we need to generate */
/* flow control characters, and */
/* there has been enough */
/* intervening characters, */
/* then generate an XON. */
PUT_SERIAL(17); /* emit an XON */
f_c = fudge_flow; /* and reset the counter. */
}
a ^= byte_shift; /* and shift it to try and avoid */
/* characters that need escapeing. */
a &= out_mask;
DEBUG_PED(stderr, "o%X\n", a); /* debugging.. */
while (escapes[a]) { /* Ok. While it is a */
/* character that needs escapeing, we */
/* emit escapes and try again. */
PUT_SERIAL('^'); /* emit the escape character. */
a = (a + 33); /* and pick a new character. I think */
/* 33 is a generator for [0..255], but */
/* I haven't checked. */
a &= out_mask;
}
PUT_SERIAL(a); /* Now put the character out. */
}
/* collect stats on the distribution of input characters */
void do_histagram(int h)
{
static int counter = 0;
static long hist_gram[256];
h &= 255;
++hist_gram[h];
++counter;
#if 0
if (counter > 50000) {
int i;
FILE *fp;
fp = fopen("hist", "w");
for (i = 0; i < 256;++i)
fprintf(fp, "%d had %d counts\n", i, hist_gram[i]);
counter = 0;
fclose(fp);
}
#endif
return;
}
/* Ok. get a byte from the serial in buffer */
/* we handle character escapes here. */
int get_serial(void) {
int a; /* Our byte. */
static int state = 0;
while (serial_in.size) {
if (state == 0) { /* If we aren't in the middle of */
/* handleing and escape.. */
a = GET_SERIAL(); /* then get the next byte. */
a &= in_mask;
if (a == '^') { /* Is it an escape?? */
state++; /* yes, go to escape handleing. */
continue;
}
if (ignores[a]) continue; /* This char MUST be line noise */
/* We were told the remote system will */
/* never generate this character. */
DEBUG_PED(stderr, "i%X\n", a); /* For debugging.. */
return (a ^ byte_shift) & in_mask; /* Ok. Return it modulo the */
/* byte shift. As the */
/* byte_shift could be larger */
/* than the mask, mask it */
/* again. */
}
a = GET_SERIAL(); /* Ok. escape handleing. Get the next */
/* byte. */
a &= in_mask;
if ( a== '^') { /* Ok! It is an escaped escape. Try again. */
++state;
continue;
}
if (ignores[a]) continue;
/* control junk. else it is line */
/* noise. Either way, we don't want it. */
a = (a - 33 * state) ^ byte_shift; /* Ok. Work out what the byte */
/* should be.. */
state = 0; /* and return to normal character */
/* processing. */
a &= in_mask;
DEBUG_PED(stderr, "i%X\n", a); /* debug */
return a; /* and return byte to caller. */
}
return -1; /* Nothing left in buffer. Return EOF */
}
/*---------------------------------------------------------------------------*/
/* Main routines. */
/* This is very horrible. We find the packet that has been waiting for a
* ack the longest and send it..... This should be a list structure
* But that would be very messy to do. (Where do we add new packets?? )
* This works fine for now. We will wait and see what sort of cpu time
* it uses..
*/
void do_serial_out(int force) {
int i, j, l;
/* port is ready for writing. see */
/* whats available. */
if (!p_out_num || serial_out.size || inhabit_send)
return; /* Hmm. How did this get called. */
/* Check for timeouts.. */
/* First we find the packet that has */
/* been waiting longest. */
l = p_out_s;
for ( j = p_out_s, i = 0; i < 32;++i, j = ((j+1)&31)) {
if (p_out[j].type < 0) continue;
if (p_out[j].timeout < p_out[l].timeout || p_out[l].type < 0)
l = j;
}
/* Then we check to make sure that */
/* this is longer than the minimum */
/* packet timeout period and that we */
/* actually found a packet. */
if (p_out[l].type < 0)
return;
if (current_time - p_out[l].timeout < packet_timeout)
return;
/* And then we send it. */
if (fudge_flow && p_out[l].timeout)
put_serial(19);
put_serial('A'+p_out[l].type);
put_serial(p_out[l].len);
put_serial(l);
j = update_crc(update_crc(update_crc(0, 'A'+p_out[l].type),
p_out[l].len), l);
put_serial(j & 255);
DEBUG_CHECK(stderr, "%s:header check == %x\n", term_server, j&255);
for (j = 0; j < p_out[l].len;++ j)
put_serial(p_out[l].data[j]);
j = check_sum(p_out[l].data, j , out_mask);
DEBUG_CHECK(stderr, "%s:p %d len %d checksum == %x\n", term_server,
l, p_out[l].len , j);
put_serial(j & 255);
put_serial((j >> 8) & 255);
if (p_out[l].timeout)
WARNING(stderr, "%s:timed out at %d trans %d\n", term_server,
current_time - p_out[l].timeout, p_out[l].trans);
p_out[l].timeout = current_time;
p_out[l].trans++;
}
void do_serial_in() {
int i, j, check;
static int /* various state flags */
curr_p_stage = 0,
curr_p_len = 0,
curr_p_index = 0,
curr_p_type = 0,
curr_p_num = 0;
static un_char header[4];
extern int breakout_char;
static int breakout = 0;
if (p_in_num > 14 || !serial_in.size)
return; /* packet window is full */
while (serial_in.size) {
DEBUG_STATE(stderr, "d_s_i state %d len %d ind %d type %d num %d\n",
curr_p_stage, curr_p_len, curr_p_index, curr_p_type,
curr_p_num);
switch (curr_p_stage) {
case 0: /* waiting for packet header */
i = get_serial();
if ( i < 0) break;
if (i < 'A' || i > ('A'+MAX_TYPE)) { /* noise */
noise(i);
if ( i == (breakout_char^byte_shift)) {
++breakout;
if (breakout == 5)
do_shutdown = 1;
}
else if ( i == ('1' ^ byte_shift) && breakout == 4) {
curr_p_stage = 4;
inhabit_send = 1; /* Don't send packets while in this */
/* mode. */
breakout = 0;
}
else breakout = 0;
break;
}
/* ok. for now we will assume that it */
/* is a header. */
curr_p_index = 0;
header[curr_p_index++] = i;
curr_p_stage = 1;
break;
case 1: /* read header */
i = get_serial();
if (i < 0)
break;
header[curr_p_index++] = i;
if (curr_p_index < 4)
break; /* more to read yet */
/* Ok. We have the whole header. Check */
/* the checksum */
i = update_crc(update_crc(update_crc(0, header[0]&in_mask),
header[1]&in_mask), header[2]&in_mask);
if (!check_match( i & in_mask,header[3])) {
DEBUG_CHECK(stderr, "%s:d_s_i: check calc %x, read %x\n",
term_server, i, header[3]);
/* checksum failed. treat it as noise */
/* and try again */
for (i =0; i < 4;++i)
noise(header[i]);
curr_p_stage = 0;
break;
}
/* ok. checksum was good. see what */
/* type packet is */
if (header[0] == 'D') { /* it is an ack */
/* we do a more strigent checksum here */
/* cos bad acks are a real pain in the butt */
DEBUG_SER(stderr, "Got ack.\n");
if (!check_match((header[0] ^ header[1]) & in_mask,
header[2]&in_mask)) {
DEBUG_CHECK(stderr,
"d_s_i: secondary. calc %x, read %x\n",
header[0] ^ header[1], header[2]);
/* heh heh. bad secondary checksum. */
/* lose that sucker */
curr_p_stage = 0;
break;
}
do_ack(header[1]);
curr_p_stage = 0;
break;
}
/* see if we want the packet */
i = (header[2] - p_in_e) & 31;
if ( i > 16) {
DEBUG_SER(stderr, "d_s_i: Got old packet %d\n", i);
/* an old packet. just lose it */
send_ack(header[2]);
curr_p_stage = 3;
curr_p_len = header[1] + 2;
break;
}
curr_p_num = header[2] & 31;
if (p_in[curr_p_num].type >= 0) {
DEBUG_SER(stderr, "Got dup packet\n");
/* Hmm. We think we already have it . */
/* check it */
if (header[1] != p_in[curr_p_num].len) {
/* We have a different length !! */
alert("Duplicate packet received with a different\n");
alert("length. sigh \n");
/* just ignore it for now. Will HAVE */
/* to be fixed */
curr_p_stage = 0;
break;
}
/* discard following data and checksum */
curr_p_stage = 3;
curr_p_len = header[1] + 2;
break;
}
curr_p_stage = 2;
curr_p_index = 0;
curr_p_len = header[1] + 2; /* get checksum as well. */
break;
case 2: /* read data */
i = get_serial();
if (i < 0)
break;
p_in[curr_p_num].data[curr_p_index ++ ] = i;
if (curr_p_index < curr_p_len)
break; /* more to read yet */
/* all read. now test the checksum */
j = (p_in[curr_p_num].data[curr_p_len - 1] <<8) +
p_in[curr_p_num].data[curr_p_len -2] ;
check = check_sum(p_in[curr_p_num].data, curr_p_len-2, in_mask);
if (!check_match(j, check) ) {
DEBUG_CHECK(stderr, "d_s_i: main calced = %x, read = %x\n",
check, j);
/* failed checksum . sigh. back to beginning */
for (i =0; i < curr_p_len;++i)
noise(p_in[curr_p_num].data[i]);
curr_p_stage = 0;
break;
}
/* ok. got a complete packet. */
/* whack it in list. This is */
/* difficult. grin. */
send_ack(curr_p_num);
p_in[curr_p_num].type = header[0] - 'A';
p_in[curr_p_num].len = curr_p_len - 2;
++p_in_num;
DEBUG_LINK(stderr, "Packet %d added to in Q\n", curr_p_num);
curr_p_stage = 0;
break;
case 3: /* discard */
if (curr_p_index <= 0) { /* sanity check */
curr_p_stage = 0;
break;
}
if (get_serial() < 0)
break;
if (!--curr_p_index)
curr_p_stage = 0;
break;
case 4: /* This state is special. This is the */
/* meta-state used by term to check */
/* for characters that are getting */
/* eaten, or to do other things that */
/* aren't based on packets. */
i = get_serial();
if (i < 0) break;
if (meta_state(i))
break;
/* Return to normal packet handleing. */
inhabit_send = 0;
curr_p_stage = 0;
break;
default:
curr_p_stage = 0;
break;
}
}
}
void do_ack(int num) {
DEBUG_SER(stderr, "Got ack for packet %d\n", num);
num &= 31;
if (p_out[num].type < 0) {
DEBUG_SER(stderr, "Was dup.\n");
return;
}
p_out[num].type = -1;
while (p_out[p_out_e].type < 0 && p_out_num > 0) {
p_out_e = (p_out_e + 1 ) & 31;
--p_out_num;
}
}
void send_ack(int num) {
/* yucky eh?? 4 bytes to transmit 1. */
/* Oh well. I fix it in some other version */
/* lets get it working first. ;) */
DEBUG_SER(stderr, "Sending ack for %d\n", num);
put_serial('D');
put_serial(num);
put_serial(num ^ 'D');
put_serial( update_crc(update_crc(update_crc(0, 'D'),
num), num ^ 'D'));
}
int check_match(int check, int calc) {
if (!seven_bit_in)
return check == calc;
return ((check & 127) == (calc & 127)) &&
(((check >> 8) & 127) == ((calc>>8) & 127));
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.