#ifndef	lint
static char sccsid[] = "@(#)reader.c 1.5 88/12/21";
#endif

#include <stdio.h>
#include "cbratp.h"

static u_char	seq;		/* sequence id of current input packet */
static u_char	ack;		/* ack id of current input packet */
static u_char	control;	/* control byte of current input packet */
static u_char	state = IDLE;	/* state of reading process */
static u_char	sync_crc[3];	/* buffer to store sync packets */
static short	count;		/* free size in input packet buffer */
static u_char	*rbase;		/* input packet buffer */

static waiting;			/* waiting for synchronous channel reply */
static balanced;		/* true if no unacked packets sent */

/*
 * reader_proc()
 *	reads characters, fills packets and processes valid packets
 *	reader_proc has three states:
 *	IDLE:		waiting for a packet. once we receive characters 
 *			state changes to FILLING.
 *	FILLING:	packet is partially received. once it is received
 *			its crc is computed and if valid it is processed.
 *			if invalid the state changes to ABORTING
 *	ABORTING:	all characters are rejected until we receive a 
 *			start/stop character. Then state changes to IDLE.
 *
 *	returns when either no characters are ready for input or we have
 *	received a valid no sync packet.
 */
reader_proc()
{
    register int	c;

    while (rs232ready() > 0) {
	switch (state) {
	case IDLE:
	    if ((c = rs232in()) < 0)
		break;
	    control = c;
	    seq = SEQ(c);
	    ack = ACK(c);
	    state = FILLING;
	    if (c&SYNC) {
		count = 3;
		rbase = reader.ptr = sync_crc;
	    }
	    else {
		/* if the packet is a retransmittion and not the one
		 * we are expecting the ABORT
		 */
		if (((c & RETRY) && seq != NEXT(reader.lastack))
		    || in_coming[seq].f_state != IDLE) {
#ifdef	DEBUG
		    if (rs232_debug) {
			(void) fprintf(rs232_debugfile, "reject duplicate %d.%d\n", seq, ack);
		    }
#endif	DEBUG
		    should_sync = 1;
		    state = ABORT;
		    break;
		}
		count = MAXPKTSIZE;
		rbase = reader.ptr = in_coming[seq].f_buf;
		in_coming[seq].f_flags = c & 3;
	    }
	case FILLING:
	    /* loop while not the end of the packet, input or frame buffer */
	    while ((c = rs232in()) >= 0 && --count >= 0) {
		*reader.ptr++ = c;
	    }
	    if (c == NEED_MORE)
		return;
	    if (check_crc(control))
		(void) process();
	    else {
#ifdef	DEBUG
		if (rs232_debug) {
		    u_char *p = rbase;

		    (void) fprintf(rs232_debugfile, "reject: seq=%d, ack=%d, myseq=%d, myack=%d",
			    seq, ack, reader.lastack, writer.frame);
		    if (rbase != sync_crc)
			(void) fprintf(rs232_debugfile, "\tchannel = %d\n", *p++);
		    if (reader.ptr - rbase) {

			(void) fprintf(rs232_debugfile, "\t[");
			for (p; p != reader.ptr; p++)
			    (void) fprintf(rs232_debugfile, " %02x", *p);
			(void) fprintf(rs232_debugfile, " ]");
		    }
		    (void) fprintf(rs232_debugfile, "\n");
		}
#endif	DEBUG
		should_sync = 1;
	    }
	    state = IDLE;
	    if (control&SYNC) {
		if (rs232_stats != NULL) {
		    (void) fprintf(rs232_stats, "s\t0\t0\n");
		    (void) fflush(rs232_stats);
		}
		break;
	    }
	    if (rs232_stats != NULL)
		(void) fprintf(rs232_stats, "r\t%d\t%d\n", in_coming[seq].f_len,
			in_coming[seq].f_ulen);
	    return;
	case ABORT:
	    /* skip all characters */
	    while ((c = rs232in()) >= 0)
		;
	    if (c == START) {
		state = IDLE;
		should_sync = 1;
	    }
	    break;
	}
    }
}

/*
 * process()
 *	process valid packet.
 *	all outgoing packets from writer.lastack to ack are cleared
 *	if we have received a continuous run of packets then the packet is
 *	ack'd and the packet is interpreted.
 */
process()
{
    register int	i;

    out_going[ack].f_state = IDLE;
    for (i = writer.lastack; i != ack; i = NEXT(i)) 
	out_going[i].f_state = IDLE;
    writer.lastack = ack;
	
    balanced = 0;
    if ((SYNC & control)) {
	balanced = (writer.frame == ack && reader.lastack == seq);
	return 0;
    }

    in_coming[seq].f_state = READ;

    /* if the packet is the next one we expect check for a run of packets
     * we may have received 1 3 2
     */
    if (seq == NEXT(reader.lastack)) {
	for (i = seq; in_coming[i].f_state == READ; i = NEXT(i)) {
	    in_coming[i].f_state = IDLE;
	    reader.lastack = i;
	    if (interpret(&in_coming[i]))
		return 1;
	}
    }

    should_sync = 1;
    return 1;
}

/*
 * interpret(f)
 * 	interpret packet. uncompress the packet if it was compressed
 *	concatenate it onto other data waiting on this channel.
 *	if it is not a continuation packet run the channel specific
 *	function on the data.
 */
interpret(f)
    struct frame *f;
{
    struct channel *c = chan[*f->f_buf];
    register u_char *p = f->f_buf + 1;
    int i = 0;

    if (c == NULL)
	return 0;
    
    f->f_ulen = --f->f_len;
    if (f->f_flags&CMP_FLAG)
	f->f_ulen = uncompress(p, f->f_ulen);

    if (c->len == 0)
	c->buf = (u_char *) malloc((unsigned) f->f_ulen);
    else {
	u_char *t = c->buf;

	c->buf = (u_char *) malloc((unsigned) (f->f_ulen + c->len));
	bcopy((char *) t, (char *) c->buf, (int) c->len);
	free((char *) t);
    }
    bcopy((char *) p, (char *) (c->buf + c->len), (int) f->f_ulen);
    c->len += f->f_ulen;
    if ((f->f_flags & CNT_FLAG) == 0) {
	i = (*types[c->type]) (*f->f_buf);
	c->len = 0;
	/* if we are waiting for this channel then reset waiting */
	if (*f->f_buf + 1 == waiting)
	    waiting = 0;
    }
    return i;
}

/* wait_for(c)
 *	waits for a reply on channel c.
 */
wait_for(c)
    u_char	c;
{
    waiting = c + 1;

    while (waiting)
    {
	wakeup_io();
    }
}

/*
 * drain()
 *	returns when all outgoing packets have been sent and no
 *	packets have not been acked
 */
drain()
{
    draining = 1;
    while (out_queue.size != 0 || balanced == 0)
	wakeup_io();
    draining = 0;
}

/*
 * wakeup_io()
 */
wakeup_io()
{
    if (!pkt_mode)
	return;

    do {
	reader_proc();
	writer_proc();
    } while (rs232oready() > 0);
}

