/* LAPB (AX.25) timer recovery routines
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Mods by G1EMM
 */
#include "global.h"
#ifdef AX25
#include "mbuf.h"
#include "ax25.h"
#ifdef NETROM
#include "netrom.h"
#endif

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: lapbtime.c,v 1.11 2000/05/09 16:48:26 brian Exp $";
#endif

static void tx_enq (struct ax25_cb * axp);

int lapbtimertype = 0;		/* default to binary exponential */


/* Called whenever timer T1 expires */
void
recover (void *p)
{
register struct ax25_cb *axp = (struct ax25_cb *) p;
long waittime = dur_timer (&axp->t1);
long maxwait;

	axp->flags.retrans = 1;
	axp->retries++;

	/* update the counters for retries out */
	if (axp && axp->iface)
		axp->iface->axcnt.retries++;
#ifdef NETROM
		{
			struct nrnbr_tab *np;
			if ((axp->iface->flags & IS_NR_IFACE) && ((np = find_nrnbr (axp->remote, axp->iface)) !=NULL))
				np->retries ++;
		}
#endif

	if (!axp || !axp->iface || !axp->iface->ax25)
		return;
		
	switch (axp->iface->ax25->lapbtimertype) {
		case 2:	/* original backoff mode*/
			set_timer (&axp->t1, axp->srt * 2);
			break;
		case 1:	/* linear backoff mode */
			if ((1L << axp->retries) < axp->iface->ax25->blimit)
				set_timer (&axp->t1, dur_timer (&axp->t1) + axp->srt);
			break;
		case 0:	/* exponential backoff mode */
			if ((1L << axp->retries) < axp->iface->ax25->blimit)
				set_timer (&axp->t1, dur_timer (&axp->t1) * 2);
			break;
		default:
			break;
	}
	/* IF a maximum is set, and we surpass it, use the maximum */
	maxwait = axp->iface->ax25->maxwait;

	if (maxwait && (waittime > maxwait))
		waittime = maxwait;

	set_timer (&axp->t1, waittime);

	switch (axp->state) {
		case LAPB_SETUP:
			if (axp->n2 != 0 && axp->retries > axp->n2) {
#ifdef NETROM
				nr_derate (axp);
#endif
				free_q (&axp->txq);
				axp->reason = LB_TIMEOUT;
				lapbstate (axp, LAPB_DISCONNECTED);
			} else {
				(void) sendctl (axp, LAPB_COMMAND, SABM | PF);
				start_timer (&axp->t1);
			}
			break;
		case LAPB_DISCPENDING:
			if (axp->n2 != 0 && axp->retries > axp->n2) {
#ifdef NETROM
				nr_derate (axp);
#endif
				axp->reason = LB_TIMEOUT;
				lapbstate (axp, LAPB_DISCONNECTED);
			} else {
				(void) sendctl (axp, LAPB_COMMAND, DISC | PF);
				start_timer (&axp->t1);
			}
			break;
		case LAPB_CONNECTED:
		case LAPB_RECOVERY:
			if (axp->n2 != 0 && axp->retries > axp->n2) {
				/* Give up */
#ifdef NETROM
				nr_derate (axp);
#endif
				(void) sendctl (axp, LAPB_RESPONSE, DM | PF);
				free_q (&axp->txq);
				axp->reason = LB_TIMEOUT;
				lapbstate (axp, LAPB_DISCONNECTED);
			} else {
				/* Transmit poll */
				tx_enq (axp);
				lapbstate (axp, LAPB_RECOVERY);
			}
			break;
		default:
			break;
	}
}



/* Send a poll (S-frame command with the poll bit set) */
void
pollthem (void *p)
{
register struct ax25_cb *axp;

	axp = (struct ax25_cb *) p;
	if (axp->proto == V1)
		return;		/* Not supported in the old protocol */
	switch (axp->state) {
		case LAPB_RECOVERY:
		case LAPB_CONNECTED:
			axp->retries = 0;
			tx_enq (axp);
			lapbstate (axp, LAPB_RECOVERY);
			break;
		default:
			break;
	}
}



/* Called whenever timer T4 (link rudundancy timer) expires */
void
redundant (void *p)
{
register struct ax25_cb *axp;

	axp = (struct ax25_cb *) p;
	switch (axp->state) {
		case LAPB_CONNECTED:
		case LAPB_RECOVERY:
			axp->retries = 0;
			(void) sendctl (axp, LAPB_COMMAND, DISC | PF);
			start_timer (&axp->t1);
			free_q (&axp->txq);
			lapbstate (axp, LAPB_DISCPENDING);
			break;
		default:
			break;
	}
}



/* Transmit query */
static void
tx_enq (register struct ax25_cb *axp)
{
char ctl;
struct mbuf *bp;

	/* I believe that retransmitting the oldest unacked
	 * I-frame tends to give better performance than polling,
	 * as long as the frame isn't too "large", because
	 * chances are that the I frame got lost anyway.
	 * This is an option in LAPB, but not in the official AX.25.
	 */
	if (axp->txq != NULLBUF && axp->pthresh != 65535 && (len_p (axp->txq) < axp->pthresh || axp->proto == V1)) {
		/* Retransmit oldest unacked I-frame */
		(void) dup_p (&bp, axp->txq, 0, len_p (axp->txq));
		ctl = PF | I | (((axp->vs - axp->unack) & MMASK) << 1) | (axp->vr << 5);	/*lint !e701 !e734 */
		(void) sendframe (axp, LAPB_COMMAND, ctl, bp);
	} else {
		ctl = len_p (axp->rxq) >= axp->window ? RNR | PF : RR | PF;
		(void) sendctl (axp, LAPB_COMMAND, ctl);
	}
	axp->response = 0;
	stop_timer (&axp->t3);
	start_timer (&axp->t1);
}

#endif /* AX25 */

