/* Routines for AX.25 encapsulation in KISS TNC
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Modified by G1EMM 19/11/90 to support multi-port KISS mode. 
 */
 /* Mods by G1EMM */

#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "kiss.h"
#include "devparam.h"
#include "slip.h"
#include "asy.h"
#include "ax25.h"

/* Send raw data packet on KISS TNC */
int
kiss_raw(iface,data)
struct iface *iface;
struct mbuf *data;
{
	register struct mbuf *bp;

	/* Put type field for KISS TNC on front */
	if((bp = pushdown(data,1)) == NULLBUF){
		free_p(data);
		return -1;
	}
	bp->data[0] = PARAM_DATA;
	bp->data[0] |= (iface->port << 4);
	if(iface->port){
		iface->rawsndcnt++;
		iface->lastsent = Clock;
	}
	/* slip_raw also increments sndrawcnt */
	slip_raw(Slip[iface->xdev].iface,bp);
	return 0;
}

/* Process incoming KISS TNC frame */
void
kiss_recv(iface,bp)
struct iface *iface;
struct mbuf *bp;
{
	char kisstype;
	struct iface *kissif;
	int port;

	kisstype = PULLCHAR(&bp);
	port = kisstype >> 4;

	if((kissif = Slip[iface->xdev].kiss[port]) == NULLIF){
		free_p(bp);
		return;
	}	

	switch(kisstype & 0xf){
	case PARAM_DATA:
		ax_recv(kissif,bp);
		break;
	default:
		free_p(bp);
		break;
	}
}
/* Perform device control on KISS TNC by sending control messages */
int32
kiss_ioctl(iface,cmd,set,val)
struct iface *iface;
int cmd;
int set;
int32 val;
{
	struct mbuf *hbp;
	char *cp;
	int rval = 0;

	/* At present, only certain parameters are supported by
	 * stock KISS TNCs. As additional params are implemented,
	 * this will have to be edited
	 */
	switch(cmd){
	case PARAM_RETURN:
		set = 1;	/* Note fall-thru */
	case PARAM_TXDELAY:
	case PARAM_PERSIST:
	case PARAM_SLOTTIME:
	case PARAM_TXTAIL:
	case PARAM_FULLDUP:
	case PARAM_HW:
		if(!set){
			rval = -1;	/* Can't read back */
			break;
		}
		/* Allocate space for cmd and arg */
		if((hbp = alloc_mbuf(2)) == NULLBUF){
			free_p(hbp);
			rval = -1;
			break;
		}
		cp = hbp->data;
		*cp++ = cmd;
		*cp = val;
		hbp->cnt = 2;
		if(hbp->data[0] != (char) PARAM_RETURN)
			hbp->data[0] |= (iface->port << 4);
		if(iface->port){
			iface->rawsndcnt++;
			iface->lastsent = Clock;
		}
		/* Even more "raw" than kiss_raw */
		slip_raw(Slip[iface->xdev].iface,hbp);
/*		slip_raw(iface,hbp);	/ * Even more "raw" than kiss_raw */
		rval = val;
		break;
	case PARAM_SPEED:	/* These go to the local asy driver */
	case PARAM_DTR:
	case PARAM_RTS:
		rval = asy_ioctl(iface,cmd,set,val);
		break;
	default:		/* Not implemented */
		rval = -1;
		break;
	}
	return rval;
}

int
kiss_stop(iface)
struct iface *iface;
{
	Slip[iface->xdev].kiss[iface->port] = NULLIF;
	return 0;
}

/* Attach a kiss interface to an existing asy interface in the system
 * argv[0]: hardware type, must be "kiss"
 * argv[1]: master interface, e.g., "ax4"
 * argv[2]: kiss port, e.g., "4"
 * argv[3]: interface label, e.g., "ax0"
 * argv[4]: maximum transmission unit, bytes
 */
int
kiss_attach(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *if_asy, *if_kiss;
	int port;

	if((if_asy = if_lookup(argv[1])) == NULLIF){
		tprintf("Interface %s does not exist\n",argv[1]);
		return -1;
	}

	if(if_lookup(argv[3]) != NULLIF){
		tprintf("Interface %s already exists\n",argv[4]);
		return -1;
	}

	if((port = atoi(argv[2])) == 0){
		tprintf("Port 0 automatically assigned to interface %s\n",argv[1]);
		return -1;
	}

	if(port < 1 || port > 15){
		tprintf("Ports 1 to 15 only\n");
		return -1;
	}

	if(Slip[if_asy->xdev].kiss[port] != NULLIF){
		tprintf("Port %d already allocated on interface %s\n", port, argv[1]);
		return -1;
	}
	/* Create interface structure and fill in details */
	if_kiss = (struct iface *)callocw(1,sizeof(struct iface));
	if_kiss->addr = if_asy->addr;
	if_kiss->name = strdup(argv[3]);

	if(argc >= 5){
		if_kiss->mtu = atoi(argv[4]);
	} else {
		if_kiss->mtu = if_asy->mtu;
	}

	if_kiss->dev = if_asy->dev;
	if_kiss->stop = kiss_stop;
	setencap(if_kiss,"AX25");
	if_kiss->ioctl = kiss_ioctl;
	if_kiss->raw = kiss_raw;
	if(if_kiss->hwaddr == NULLCHAR)
		if_kiss->hwaddr = mallocw(AXALEN);
	memcpy(if_kiss->hwaddr,Mycall,AXALEN);
	if_kiss->xdev = if_asy->xdev;
	if_kiss->next = Ifaces;
	Ifaces = if_kiss;
	if_kiss->port = port;
	Slip[if_kiss->xdev].kiss[if_kiss->port] = if_kiss;
	return 0;
}

