/* LAPB (AX.25) timer recovery routines
 * Copyright 1991 Phil Karn, KA9Q
 */
 /* Mods by G1EMM */
#include "global.h"
#include "mbuf.h"
#include "ax25.h"
#include "timer.h"
#include "lapb.h"

static void tx_enq __ARGS((struct ax25_cb *axp));

int lapbtimertype = 0;		/* default to binary exponential */

/* Called whenever timer T1 expires */
void
recover(p)
void *p;
{
	register struct ax25_cb *axp = (struct ax25_cb *)p;

	axp->flags.retrans = 1;
	axp->retries++;

	switch(lapbtimertype){
		case 2:			 /* original backoff mode*/
			set_timer(&axp->t1,axp->srt * 2);
			break;
		case 1:			 /* linear backoff mode */
			if((1L << axp->retries) < Blimit)
				set_timer(&axp->t1,dur_timer(&axp->t1) + axp->srt);
			break;
		case 0:			 /* exponential backoff mode */
			if((1L << axp->retries) < Blimit)
				set_timer(&axp->t1,dur_timer(&axp->t1)*2);
			break;
	}


	switch(axp->state){
	case LAPB_SETUP:
		if(axp->n2 != 0 && axp->retries > axp->n2){
			nr_derate(axp);
			free_q(&axp->txq);
			axp->reason = LB_TIMEOUT;
			lapbstate(axp,LAPB_DISCONNECTED);
		} else {
			sendctl(axp,LAPB_COMMAND,SABM|PF);
			start_timer(&axp->t1);
		}
		break;
	case LAPB_DISCPENDING:
		if(axp->n2 != 0 && axp->retries > axp->n2){
			nr_derate(axp);
			axp->reason = LB_TIMEOUT;
			lapbstate(axp,LAPB_DISCONNECTED);
		} else {
			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 */
			nr_derate(axp);
			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;
	}
}


/* Send a poll (S-frame command with the poll bit set) */
void
pollthem(p)
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_CONNECTED:
		axp->retries = 0;
		tx_enq(axp);
		lapbstate(axp,LAPB_RECOVERY);
		break;
	}
}

/* Called whenever timer T4 (link rudundancy timer) expires */
void
redundant(p)
void *p;
{
	register struct ax25_cb *axp;

	axp = (struct ax25_cb *)p;
	switch(axp->state){
	case LAPB_CONNECTED:
	case LAPB_RECOVERY:
		axp->retries = 0;
		sendctl(axp,LAPB_COMMAND,DISC|PF);
		start_timer(&axp->t1);
		free_q(&axp->txq);
		lapbstate(axp,LAPB_DISCPENDING);
		break;
	}
}

/* Transmit query */
static void
tx_enq(axp)
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
	 && (len_p(axp->txq) < axp->pthresh || axp->proto == V1)){
		/* Retransmit oldest unacked I-frame */
		dup_p(&bp,axp->txq,0,len_p(axp->txq));
		ctl = PF | I | ((axp->vs - axp->unack) & MMASK) << 1
		 | axp->vr << 5;
		sendframe(axp,LAPB_COMMAND,ctl,bp);
	} else {
		ctl = len_p(axp->rxq) >= axp->window ? RNR|PF : RR|PF;	
		sendctl(axp,LAPB_COMMAND,ctl);
	}
	axp->response = 0;	
	stop_timer(&axp->t3);
	start_timer(&axp->t1);
}
