#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "ax25.h"
#include "lapb.h"
#include <ctype.h>

struct ax25_cb *ax25_cb[NHASH];
struct ax25_call *ax25_digi[NHASH];
struct ax25_call *ax25_call[NHASH];
struct ax25_call *ax25_excl[NHASH];
struct ax25_call *ax25_port;

/* Default AX.25 parameters */
int16 t1init = SEC2TICK(10);	/* FRACK of 10 seconds */
int16 t2init = SEC2TICK(1);	/* 1 sec acknowledgment delay */
int16 t3init = 0;		/* No keep-alive polling */
int16 t4init = 0;		/* No disconnection of idle links */
int16 maxframe = 1;		/* Stop and wait */
int16 maxsamml = 0;		/* No framesammler */
int16 n2 = 10;			/* 10 retries */
int16 axwindow = 2048;		/* 2K incoming text before RNR'ing */
int16 paclen = 256;		/* 256-byte I fields */
int16 pthresh = 128;		/* Send polls for packets larger than this */

/* Address hash function. Exclusive-ORs each byte, ignoring
 * such insignificant, annoying things as E and H bits
 */
static
int16
ax25hash(s)
register struct ax25_addr *s;
{
	register char x;

	x = s->call[0] ^ s->call[1] ^ s->call[2] ^
	    s->call[3] ^ s->call[4] ^ s->call[5];
	x ^= s->ssid & SSID;
	return (uchar(x) & 0xfe) % NHASH;
}
/* Look up entry in hash table */
struct ax25_cb *
find_ax25(dest,source)
register struct ax25_addr *dest;
struct ax25_addr *source;
{
	int16 hashval;
	register struct ax25_cb *axp;
	/*int i_state;*/

	/* Find appropriate hash chain */
	hashval = ax25hash(dest);

	/* Search hash chain */
	/*i_state = disable();*/
	for(axp = ax25_cb[hashval]; axp != NULLAX25; axp = axp->next){
		if(addreq(&axp->addr.dest,dest) &&
		   addreq(&axp->addr.source,source)){
			/*restore(i_state);*/
			return axp;
		}
	}
	/*restore(i_state);*/
	return NULLAX25;
}

/* Remove address entry from hash table */
del_ax25(axp)
register struct ax25_cb *axp;
{
	int16 hashval;
	int i;
	/*int i_state;*/

	if(axp == NULLAX25)
		return;
	/* Remove from hash header list if first on chain */
	hashval = ax25hash(&axp->addr.dest);
	/*i_state = disable();*/

	/* Remove from chain list */
	if(ax25_cb[hashval] == axp)
		ax25_cb[hashval] = axp->next;
	if(axp->prev != NULLAX25)
		axp->prev->next = axp->next;
	if(axp->next != NULLAX25)
		axp->next->prev = axp->prev;

	/* Timers should already be stopped, but just in case... */
	stop_timer(&axp->t1);
	stop_timer(&axp->t2);
	stop_timer(&axp->t3);
	stop_timer(&axp->t4);

	/* Free allocated resources */
	free_q(&axp->txq);
	free_p(axp->rxasm);
	free_q(&axp->rxq);
	for(i = 0; i < 8; i++){
		free_p(axp->sammlq[i]);
		axp->sammlq[i] = NULLBUF;
	}
	free((char *)axp);

	/*restore(i_state);*/
}

/* Create an ax25 control block. Allocate a new structure, if necessary,
 * and fill it with all the defaults. The caller
 * is still responsible for filling in the reply address
 */
struct ax25_cb *
cr_ax25(dest,source)
struct ax25_addr *dest,*source;
{
	void recover(),send_ack(),pollthem(),disc_ax25(),ax_incom();
	register struct ax25_cb *axp;
	int16 hashval;
	/*int i_state;*/

	if(dest == NULLAXADDR || source == NULLAXADDR)
		return NULLAX25;

	if((axp = find_ax25(dest,source)) == NULLAX25){
		/* Not already in table; create an entry
		 * and insert it at the head of the chain
		 */
		/* Find appropriate hash chain */
		hashval = ax25hash(dest);
		/*i_state = disable();*/
		axp = (struct ax25_cb *)calloc(1,sizeof(struct ax25_cb));
		if(axp == NULLAX25)
			return NULLAX25;
		/* Insert at beginning of chain */
		axp->prev = NULLAX25;
		axp->next = ax25_cb[hashval];
		if(axp->next != NULLAX25)
			axp->next->prev = axp;
		ax25_cb[hashval] = axp;
		/*restore(i_state);*/
	}
	axp->maxframe = maxframe;
	axp->maxsamml = maxsamml;
	axp->window = axwindow;
	axp->paclen = paclen;
	axp->proto = V2;	/* Default, can be changed by other end */
	axp->pthresh = pthresh;
	axp->n2 = n2;
	axp->t1.start = t1init;
	axp->t1.func = recover;
	axp->t1.arg = (char *)axp;

	axp->t2.start = t2init;
	axp->t2.func = send_ack;
	axp->t2.arg = (char *)axp;

	axp->t3.start = t3init;
	axp->t3.func = pollthem;
	axp->t3.arg = (char *)axp;

	axp->t4.start = t4init;
	axp->t4.func = disc_ax25;
	axp->t4.arg = (char *)axp;

	axp->r_upcall = ax_incom;
	return axp;
}

/* Look up ax25 callsign entry in hash table (digi, call or excl) */
struct ax25_call *
find_axcall(table,addr)
struct ax25_call *table[];
register struct ax25_addr *addr;
{
	int16 hashval;
	register struct ax25_call *axp;

	/* Find appropriate hash chain */
	hashval = ax25hash(addr);

	/* Search hash chain */
	for(axp = table[hashval]; axp != NULLAXCALL; axp = axp->next){
		if(addreq(&axp->addr,addr)){
			return axp;
		}
	}
	return NULLAXCALL;
}

/* Remove address entry from hash table (digi, call or excl) */
del_axcall(table,axp)
struct ax25_call *table[];
register struct ax25_call *axp;
{
	int16 hashval;

	if(axp == NULLAXCALL)
		return;
	/* Remove from hash header list if first on chain */
	hashval = ax25hash(&axp->addr);

	/* Remove from chain list */
	if(table[hashval] == axp)
		table[hashval] = axp->next;
	if(axp->prev != NULLAXCALL)
		axp->prev->next = axp->next;
	if(axp->next != NULLAXCALL)
		axp->next->prev = axp->prev;

	free((char *) axp);
}

/* Create an ax25 call control block. Allocate a new structure, if necessary,
 * and fill it with all the defaults.
 */
struct ax25_call *
cr_axcall(table,addr)
struct ax25_call *table[];
struct ax25_addr *addr;
{
	register struct ax25_call *axp;
	int16 hashval;

	if(addr == NULLAXADDR)
		return NULLAXCALL;

	if((axp = find_axcall(table,addr)) == NULLAXCALL){
		/* Not already in table; create an entry
		 * and insert it at the head of the chain
		 */
		/* Find appropriate hash chain */
		hashval = ax25hash(addr);
		axp = (struct ax25_call *)calloc(1,sizeof(struct ax25_call));
		if(axp == NULLAXCALL)
			return NULLAXCALL;
		ASSIGN(axp->addr,*addr);
		/* Insert at beginning of chain */
		axp->prev = NULLAXCALL;
		axp->next = table[hashval];
		if(axp->next != NULLAXCALL)
			axp->next->prev = axp;
		table[hashval] = axp;
	}

	return axp;
}


/*
 * setcall - convert callsign plus substation ID of the form
 * "KA9Q-0" to AX.25 (shifted) address format.
 * also converts the source to uppercase (uniform output)
 *   Address extension bit is set (pe1chl: this seems more convenient)
 *   Return -1 on error, 0 if OK
 */
int
setcall(out,call)
struct ax25_addr *out;
register char *call;
{
	int csize;
	unsigned ssid;
	register int i;
	register char *cp,*dp;

	if(out == (struct ax25_addr *)NULL || call == NULLCHAR || *call == '\0'){
		return -1;
	}
	/* Find dash, if any, separating callsign from ssid
	 * Then compute length of callsign field and make sure
	 * it isn't excessive
	 */
	dp = index(call,'-');
	if(dp == NULLCHAR)
		csize = strlen(call);
	else
		csize = (int) (dp - call);
	if(csize < 3 || csize > ALEN)
		return -1;
	/* Now find and convert ssid, if any */
	if(dp != NULLCHAR){
		dp++;	/* skip dash */
		ssid = atoi(dp);
		if(ssid > 15)
			return -1;
	} else
		ssid = 0;
	/* Copy upper-case callsign, left shifted one bit */
	cp = out->call;
	for(i=0;i<csize;i++){
		if(!isascii(*call) || !isalnum(*call))
			return -1;
		*call = toupper(*call);
		*cp++ = *call++ << 1;
	}
	/* Pad with shifted spaces if necessary */
	for(;i<ALEN;i++)
		*cp++ = ' ' << 1;

	/* Insert substation ID field, set reserved bits and E(extension) */
	out->ssid = 0x60 | (ssid << 1) | E;
	return 0;
}
/*
 * valid_call: validate an address according to AX.25 rules
 * return 0 for valid address
 */
int
valid_addr (addr)
struct ax25_addr *addr;
{
	register char *cp;
	register char c;
	int i;

	cp = addr->call;
	for(i=ALEN;i != 0;i--){
		if ((c = (*cp++ >> 1) & 0x7f) == ' ' && i <= (ALEN-3))
			continue;

		if (!isupper(c) && !isdigit(c))
			return -1;
	}
	return 0;
}
/* Set a digipeater string in an ARP table entry */
setpath(out,in,cnt)
char *out;	/* Target char array containing addresses in net form */
char *in[];	/* Input array of tokenized callsigns in ASCII */
int cnt;	/* Number of callsigns in array */
{
	struct ax25_addr addr;
	char *putaxaddr();

	if(cnt == 0)
		return;
	while(cnt-- != 0){
		setcall(&addr,*in++);
		addr.ssid &= ~E;
		out = putaxaddr(out,&addr);
	}
	out[-1] |= E;
}
addreq(a,b)
register struct ax25_addr *a,*b;
{
	if(memcmp(a->call,b->call,ALEN) != 0)
		return 0;
	if((a->ssid & SSID) != (b->ssid & SSID))
		return 0;
	return 1;
}
/* Convert encoded AX.25 address to printable string (9 chars max) */
pax25(e,addr)
char *e;
struct ax25_addr *addr;
{
	register int i;
	char c,*cp;

	cp = addr->call;
	for(i=ALEN;i != 0;i--){
		if ((c = (*cp++ >> 1) & 0x7f) < ' ')
			c = '?';
		if(c == ' ')
			break;
		*e++ = c;
	}
	if ((addr->ssid & SSID) != 0)
		sprintf(e,"-%d",(addr->ssid >> 1) & 0xf);	/* ssid */
	else
		*e = 0;
}
/* Print a string of AX.25 addresses in the form
 * "KA9Q-0 [via N4HY-0,N2DSY-2]"
 * This can be up to 93 characters...
 * It prints the destination plus digi's of the passed ax25 header
 */
pdax25(e,addr)
register char *e;
register struct ax25 *addr;
{
	register int i;

	pax25(e,&addr->dest);			/* first the destination */

	for (i = 0; i < addr->ndigis; i++) {	/* then the digipeaters */
		if(i == 0)
			strcat(e," via ");
		else
			strcat(e,",");
		e += strlen(e);
		pax25(e,&(addr->digis[i]));
	}
}
/* Print a string of AX.25 addresses in the form
 * "KA9Q-0 [via N4HY-0,N2DSY-2]"
 * This can be up to 93 characters...
 * Designed for use by ARP - arg is a char string
 */
psax25(e,addr)
register char *e;
register char *addr;
{
	int i;
	struct ax25_addr axaddr;
	char *getaxaddr();

	for(i=0;;i++){
		/* Create local copy in host-format structure */
		addr = getaxaddr(&axaddr,addr);

		/* Create ASCII representation and append to output */
		pax25(e,&axaddr);

		if(axaddr.ssid & E)
			break;
		if(i == 0)
			strcat(e," via ");
		else
			strcat(e,",");

		e += strlen(e);
	}
}
char *
getaxaddr(ap,cp)
register struct ax25_addr *ap;
register char *cp;
{
	memcpy(ap->call,cp,ALEN);
	cp += ALEN;
	ap->ssid = *cp++;
	return cp;
}
char *
putaxaddr(cp,ap)
register char *cp;
register struct ax25_addr *ap;
{
	memcpy(cp,ap->call,ALEN);
	cp += ALEN;
	*cp++ = ap->ssid;
	return cp;
}

/* Convert a host-format AX.25 header into a mbuf ready for transmission */
struct mbuf *
htonax25(hdr,data)
register struct ax25 *hdr;
struct mbuf *data;
{
	struct mbuf *bp;
	register char *cp;
	register int16 i;

	if(hdr == (struct ax25 *)NULL || hdr->ndigis > MAXDIGIS)
		return NULLBUF;

	/* Allocate space for return buffer */
	i = AXALEN * (2 + hdr->ndigis);
	if((bp = pushdown(data,i)) == NULLBUF)
		return NULLBUF;

	/* Now convert */
	cp = bp->data;

	hdr->dest.ssid &= ~E;	/* Dest E-bit is always off */
	/* Encode command/response in C bits */
	switch(hdr->cmdrsp){
	case COMMAND:
		hdr->dest.ssid |= C;
		hdr->source.ssid &= ~C;
		break;
	case RESPONSE:
		hdr->dest.ssid &= ~C;
		hdr->source.ssid |= C;
		break;
	default:
		hdr->dest.ssid &= ~C;
		hdr->source.ssid &= ~C;
		break;
	}
	cp = putaxaddr(cp,&hdr->dest);

	/* Set E bit on source address if no digis */
	if(hdr->ndigis == 0){
		hdr->source.ssid |= E;
		putaxaddr(cp,&hdr->source);
		return bp;
	}
	hdr->source.ssid &= ~E;
	cp = putaxaddr(cp,&hdr->source);

	/* All but last digi get copied with E bit off */
	for(i=0; i < hdr->ndigis - 1; i++){
		hdr->digis[i].ssid &= ~E;
		cp = putaxaddr(cp,&hdr->digis[i]);
	}
	hdr->digis[i].ssid |= E;
	cp = putaxaddr(cp,&hdr->digis[i]);
	return bp;
}
/* Convert an AX.25 ARP table entry into a host format address structure
 * ready for use in transmitting a packet
 */
int
atohax25(hdr,hwaddr,source)
register struct ax25 *hdr;
register char *hwaddr;
struct ax25_addr *source;
{
	register struct ax25_addr *axp;

	hwaddr = getaxaddr(&hdr->dest,hwaddr);	/* Destination address */
	ASSIGN(hdr->source,*source);		/* Source address */
	if(hdr->dest.ssid & E){
		/* No digipeaters */
		hdr->ndigis = 0;
		hdr->dest.ssid &= ~E;
		hdr->source.ssid |= E;
		return 2;
	}
	hdr->source.ssid &= ~E;
	hdr->dest.ssid &= ~E;
	for(axp = hdr->digis; axp < &hdr->digis[MAXDIGIS]; axp++){
		hwaddr = getaxaddr(axp,hwaddr);
		if(axp->ssid & E){
			hdr->ndigis = (int) (axp - hdr->digis) + 1;
			return hdr->ndigis;
		}
	}
	return -1;
}
/* Convert a network-format AX.25 header into a host format structure
 * Return -1 if error, number of addresses if OK
 */
int
ntohax25(hdr,bpp)
register struct ax25 *hdr;	/* Output structure */
struct mbuf **bpp;
{
	register struct ax25_addr *axp;
	char *getaxaddr();
	char buf[AXALEN];

	if(pullup(bpp,buf,AXALEN) < AXALEN)
		return -1;
	getaxaddr(&hdr->dest,buf);

	if(pullup(bpp,buf,AXALEN) < AXALEN)
		return -1;
	getaxaddr(&hdr->source,buf);

	/* Process C bits to get command/response indication */
	if((hdr->source.ssid & C) == (hdr->dest.ssid & C))
		hdr->cmdrsp = UNKNOWN;
	else if(hdr->source.ssid & C)
		hdr->cmdrsp = RESPONSE;
	else
		hdr->cmdrsp = COMMAND;

	hdr->ndigis = 0;
	if(hdr->source.ssid & E)
		return 2;	/* No digis */

	/* Process digipeaters */
	for(axp = hdr->digis;axp < &hdr->digis[MAXDIGIS]; axp++){
		if(pullup(bpp,buf,AXALEN) < AXALEN)
			return -1;
		getaxaddr(axp,buf);
		if(axp->ssid & E){	/* Last one */
			hdr->ndigis = (int) (axp - hdr->digis) + 1;
			return hdr->ndigis + 2;
		}
	}
	return -1;	/* Too many digis */
}

/* Figure out the frame type from the control field
 * This is done by masking out any sequence numbers and the
 * poll/final bit after determining the general class (I/S/U) of the frame
 */
int16
ftype(control)
register char control;
{
	if((control & 1) == 0)	/* An I-frame is an I-frame... */
		return I;
	if(control & 2)		/* U-frames use all except P/F bit for type */
		return(uchar(control) & ~PF);
	else			/* S-frames use low order 4 bits for type */
		return(control & 0xf);
}


