/* 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 "commands.h"
#include "mbuf.h"
#include "iface.h"
#include "kiss.h"
#include "devparam.h"
#include "slip.h"
#include "asy.h"
#include "pktdrvr.h"
#include "kisspoll.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: kiss.c,v 1.17 1997/01/19 21:13:05 root Exp $";
#endif

static int kiss_stop (struct iface * iface);

#ifdef POLLEDKISS
/* this line is to fake out the linker */
void *polledkiss_fakeit = (void *) kiss_poller;		/*lint !e611 */
#endif


/* Send raw data packet on KISS TNC */
int
kiss_raw (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] |= uchar (iface->port << 4);	/*lint !e701 */
	if (iface->port) {
		iface->rawsndcnt++;
		iface->lastsent = secclock ();
	}
	/* slip_raw also increments sndrawcnt */
	(void) slip_raw (Slip[iface->xdev].iface, bp);
	return 0;
}



/* Process incoming KISS TNC frame */
void
kiss_recv (struct iface *iface, struct mbuf *bp)
{
char kisstype;
struct iface *kissif;
int port;
struct mbuf **bpp = &bp;
#ifdef RXECHO
struct mbuf *bbp;
#endif


	kisstype = (char) PULLCHAR (bpp);
	port = kisstype >> 4;			/*lint !e702 */

	if ((kissif = Slip[iface->xdev].kiss[port]) == NULLIF) {
		free_p (bp);
		return;
	}
	switch (kisstype & 0xf) {
		case PARAM_DATA:
#ifdef RXECHO
			/* Now we can echo the packet if configured (see config.c) */
			if (kissif->rxecho) {
				(void) dup_p (&bbp, bp, 0, len_p (bp));
				(void) (*kissif->rxecho->raw) (kissif->rxecho, bbp);
			}
#endif
			ax_recv (kissif, bp);
			break;
		default:
			free_p (bp);
			break;
	}
}



/* Perform device control on KISS TNC by sending control messages */
int32
kiss_ioctl (struct iface *iface, int cmd, int set, int32 val)
{
struct mbuf *hbp;
char *cp;
int32 rval = 0L;

	/* 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:
		case PARAM_RETURN2:
			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 = (char *) hbp->data;
			*cp++ = (char) cmd;
			*cp = (char) val;
			hbp->cnt = 2;
			if (hbp->data[0] != (unsigned char) PARAM_RETURN)
				hbp->data[0] |= uchar(iface->port << 4);	/*lint !e701 */
			if (iface->port) {
				iface->rawsndcnt++;
				iface->lastsent = secclock ();
			}
			/* Even more "raw" than kiss_raw */
			(void) slip_raw (Slip[iface->xdev].iface, hbp);
			rval = val;
			break;
		case PARAM_SPEED:	/* These go to the local asy driver */
		case PARAM_DTR:
		case PARAM_RTS:
		case PARAM_UP:
		case PARAM_DOWN:
#ifdef UNIX
		case PARAM_MIN:/* almost certainly not its intended use... */
#endif
			rval = asy_ioctl (iface, cmd, set, val);
			break;
		default:	/* Not implemented */
			rval = -1;
			break;
	}
	return rval;
}



static int
kiss_stop (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 (int argc, char **argv, void *p OPTIONAL)
{
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;
	}
	/* Check for ASY type interface ! - WG7J */
	if (if_asy->type != CL_AX25) {
		tprintf ("Multidrop KISS not allowed in interface: %s\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) {
		tputs ("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 = (int16) atoi (argv[4]);
	else
		if_kiss->mtu = if_asy->mtu;

	if_kiss->iface_metric = 1;
	if_kiss->dev = if_asy->dev;
	if_kiss->stop = kiss_stop;
	(void) setencap (if_kiss, "AX25");
	if_kiss->ioctl = kiss_ioctl;
	if_kiss->raw = kiss_raw;
	if (if_kiss->hwaddr == NULLCHAR)
		if_kiss->hwaddr = mallocw (AXALEN);
	if (if_kiss->ipcall == NULLCHAR)
		if_kiss->ipcall = mallocw (AXALEN);
	memcpy (if_kiss->ipcall, Mycall, 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;
}
