/* Generic serial line interface routines
 * Copyright 1991 Phil Karn, KA9Q
 */
/* Mods by G1EMM */
#include "global.h"
#include "commands.h"
#include "proc.h"
#include "iface.h"
#include "slhc.h"
#ifdef UNIX
#include "unixasy.h"
#else
#include "n8250.h"
#endif
#include "asy.h"
#include "kiss.h"
#include "pktdrvr.h"
#include "ppp.h"
#include "slip.h"
#include "nrs.h"
#include "ctype.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: asy.c,v 1.20 1997/09/07 00:31:16 root Exp $";
#endif

int SLIP_MAX = _SLIP_MAX;
int ASY_MAX = _ASY_MAX;

static int asy_detach (struct iface * ifp);
extern void pasy (struct asy *asyp);


/* Attach a serial interface to the system
 * argv[0]: hardware type, must be "asy"
 * argv[1]: I/O address, e.g., "0x3f8"
 * argv[2]: vector, e.g., "4"  In hex, 0x prefix optional.
 * argv[3]: mode, may be:
 *		"slip" (point-to-point SLIP)
 *		"ax25" (AX.25 frame format in SLIP for raw TNC)
 *		"nrs" (NET/ROM format serial protocol)
 *		"ppp" (Point-to-Point Protocol, RFC1171, RFC1172)
 *		"pkiss" (Polled KISS ala G8BPQ)
 * argv[4]: interface label, e.g., "sl0"
 * argv[5]: receiver ring buffer size in bytes
 * argv[6]: maximum transmission unit, bytes
 * argv[7]: interface speed, e.g, "9600"
 * argv[8]: optional flags,
 *		'c' BPQ-style checksum used in kiss mode - WG7J
 *		'v' for Van Jacobson TCP header compression (SLIP only,
 *		    use ppp command for VJ compression with PPP);
 *		'f' for forced use of the 16550 fifo's - WG7J
 *		'dd' to set 16550 trigger level to 'dd' (an integer) - WG7J
 *		't' to display pkiss outbound polling on trace display
 *		    (supressed by default)
 */
int
asy_attach (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
register struct iface *ifp;
struct asy *asyp;
char *ifn;
int dev;
int xdev;
int trigchar = -1;
char monitor = FALSE;
int polled = 0;
int triglevel = 0;
int force = 0;
char *cp;
#if	defined(SLIP) || defined(AX25)
struct slip *sp = (struct slip *)0;
#endif
#ifdef	NRS
struct nrs *np = (struct nrs *)0;
#endif

	if (if_lookup (argv[4]) != NULLIF) {
		tprintf (Existingiface, argv[4]);
		return -1;
	}
	/* Find unused asy control block */
	for (dev = 0; dev < ASY_MAX; dev++) {
		asyp = &Asy[dev];
		if (asyp->iface == NULLIF)
			break;
	}
	if (dev >= ASY_MAX) {
		tputs ("Too many async controllers\n");
		return -1;
	}

	/* Create interface structure and fill in details */
	ifp = (struct iface *) callocw (1, sizeof (struct iface));

	ifp->addr = Ip_addr;
	ifp->name = strdup (argv[4]);
	ifp->iface_metric = 1;
	ifp->mtu = (int16) atoi (argv[6]);
	ifp->dev = dev;
	ifp->stop = asy_detach;

	/*Check for forced 16550 fifo usage - WG7J */
	if ((argc > 8) && ((cp = strchr (argv[8], 'f')) != NULLCHAR)) {
		force = 1;
		if ((triglevel = atoi (++cp)) == 0)	/* is there an additional arg ? */
			if (argc > 9)
				triglevel = atoi (argv[9]);
	}
#ifdef	SLIP
	if (stricmp (argv[3], "SLIP") == 0) {
		for (xdev = 0; xdev < SLIP_MAX; xdev++) {
			sp = &Slip[xdev];
			if (sp->iface == NULLIF)
				break;
		}
		if (!sp || xdev >= SLIP_MAX) {
			tputs ("Too many slip devices\n");
			free (ifp->name);
			free ((char *) ifp);
			return -1;
		}
		(void) setencap (ifp, "SLIP");
		ifp->ioctl = asy_ioctl;
		ifp->raw = slip_raw;
		ifp->show = slip_status;
		ifp->flags = 0;
		ifp->xdev = xdev;

		sp->iface = ifp;
		sp->send = asy_send;
		sp->get = get_asy;
		sp->type = CL_SERIAL_LINE;
		trigchar = FR_END;
#ifdef VJCOMPRESS
		if ((argc > 8) && (strchr (argv[8], 'v') != NULLCHAR)) {
			sp->escaped |= SLIP_VJCOMPR;
			sp->slcomp = slhc_init (16, 16);
		}
#else
		sp->slcomp = NULL;
#endif /* VJCOMPRESS */
		ifp->rxproc = newproc (ifn = if_name (ifp, " rx"), 256, asy_rx, xdev, NULL, NULL, 0);
		free (ifn);
	} else
#endif
#ifdef	AX25
	    if (stricmp (argv[3], "AX25") == 0
#ifdef POLLEDKISS
	      || stricmp (argv[3], "PKISS") == 0
#endif
	      ) {
		/* Set up a SLIP link to use AX.25 */
		for (xdev = 0; xdev < SLIP_MAX; xdev++) {
			sp = &Slip[xdev];
			if (sp->iface == NULLIF)
				break;
		}
		if (!sp || xdev >= SLIP_MAX) {
			tputs ("Too many ax25"
#ifdef POLLEDKISS
			       "or pkiss"
#endif
			       " devices\n");
			free (ifp->name);
			free ((char *) ifp);
			return -1;
		}
		(void) setencap (ifp, "AX25");
#ifdef KISS
		ifp->ioctl = kiss_ioctl;
		ifp->raw = kiss_raw;
#endif
		ifp->show = slip_status;
		ifp->port = 0;	/* G1EMM */
		if (ifp->hwaddr == NULLCHAR)
			ifp->hwaddr = mallocw (AXALEN);
		memcpy (ifp->hwaddr, Mycall, AXALEN);
		if (ifp->ipcall == NULLCHAR)
			ifp->ipcall = mallocw (AXALEN);
		memcpy (ifp->ipcall, Mycall, AXALEN);
		ifp->xdev = xdev;

		sp->iface = ifp;
		sp->send = asy_send;
		sp->kiss[ifp->port] = ifp;	/* G1EMM */
		sp->get = get_asy;
		sp->type = CL_KISS;
		sp->polled = sp->usecrc = sp->tracepoll = 0;
		trigchar = FR_END;
		ifp->rxproc = newproc (ifn = if_name (ifp, " rx"), 256, asy_rx, xdev, NULL, NULL, 0);
#ifdef POLLEDKISS
		if (toupper (*argv[3]) == 'P') {
			polled = 1;
			sp->polled = sp->usecrc = 1;	/* PKISS => usecrc (for compatibility) */
			if ((argc > 8) && strchr (argv[8], 't') != NULLCHAR)
				sp->tracepoll = 1;
		}
		if ((argc > 8) && (strchr (argv[8], 'c') != NULLCHAR))
			sp->usecrc = 1;
#endif
		free (ifn);
	    } else
#endif
#ifdef	NRS
	    if (stricmp (argv[3], "NRS") == 0) {
		/* Set up a net/rom serial iface */
		for (xdev = 0; xdev < SLIP_MAX; xdev++) {
			np = &Nrs[xdev];
			if (np->iface == NULLIF)
				break;
		}
		if (!np || xdev >= SLIP_MAX) {
			tputs ("Too many nrs devices\n");
			free (ifp->name);
			free ((char *) ifp);
			return -1;
		}
		/* no call supplied? */
		(void) setencap (ifp, "AX25");
		ifp->ioctl = asy_ioctl;
		ifp->raw = nrs_raw;
		/*		ifp->show = nrs_status; */
		ifp->hwaddr = mallocw (AXALEN);
		memcpy (ifp->hwaddr, Mycall, AXALEN);
		ifp->ipcall = mallocw (AXALEN);
		memcpy (ifp->ipcall, Mycall, AXALEN);
		ifp->xdev = xdev;
		np->iface = ifp;
		np->send = asy_send;
		np->get = get_asy;
		trigchar = ETX;
		ifp->rxproc = newproc (ifn = if_name (ifp, " nrs"), 256, nrs_recv, xdev, NULL, NULL, 0);
		free (ifn);
	    } else
#endif
#ifdef	PPP
	    if (stricmp (argv[3], "PPP") == 0) {
		/* Setup for Point-to-Point Protocol */
		trigchar = HDLC_FLAG;
		monitor = TRUE;
		(void) setencap (ifp, "PPP");
		ifp->ioctl = asy_ioctl;
		ifp->flags = FALSE;
		/* Initialize parameters for various PPP phases/protocols */
		if (ppp_init (ifp) != 0) {
			tputs ("Cannot allocate PPP control block\n");
			free (ifp->name);
			free ((char *) ifp);
			return -1;
		}
	    } else
#endif /* PPP */
	    {
		tprintf ("Mode %s unknown for interface %s\n", argv[3], argv[4]);
		free (ifp->name);
		free ((char *) ifp);
		return -1;
	    }

	/* Link in the interface */
	ifp->next = Ifaces;
	Ifaces = ifp;
#ifdef UNIX
	/* *ix version can fail (e.g. "device locked"). Detach if it does. */
	if (asy_init (dev, ifp, argv[1], argv[2], (int16) atol (argv[5]),
	    trigchar, monitor, (int16) atol (argv[7]), force, triglevel, polled) == -1)
		(void) if_detach (ifp);
#else
	(void) asy_init (dev, ifp, argv[1], argv[2], (int16) atol (argv[5]),
		trigchar, monitor, (int16) atol (argv[7]), force, triglevel, polled);
#endif
	return 0;
}


static int
asy_detach (ifp)
struct iface *ifp;
{
	(void) asy_stop (ifp);

#ifdef	SLIP
	if (stricmp (ifp->iftype->name, "SLIP") == 0) {
		Slip[ifp->xdev].iface = NULLIF;
#ifdef VJCOMPRESS
		slhc_free (Slip[ifp->xdev].slcomp);
		Slip[ifp->xdev].slcomp = NULL;
#endif /* VJCOMPRESS */
	} else
#endif
#ifdef	AX25
	if (stricmp (ifp->iftype->name, "AX25") == 0 && Slip[ifp->xdev].iface == ifp)
		Slip[ifp->xdev].iface = NULLIF;
	else
#endif
#ifdef	NRS
	if (stricmp (ifp->iftype->name, "AX25") == 0 && Nrs[ifp->xdev].iface == ifp) 
		Nrs[ifp->xdev].iface = NULLIF;
	else
#endif
#ifdef	PPP
	if (stricmp (ifp->iftype->name, "PPP") == 0)
		(void) ppp_free (ifp);
	else
#endif
	{
		tprintf ("invalid type %s for interface %s\n", ifp->iftype->name, ifp->name);
		free (ifp->name);
		free (ifp);
		return -1;
	}
	return 0;
}


/* Execute user comm command */
int
doasycomm (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
register struct iface *ifp;
register struct asy *ap;
int dev;
struct mbuf *bp;

	if ((ifp = if_lookup (argv[1])) == NULLIF) {
		tprintf (Badinterface, argv[1]);
		return 1;
	}
	for (dev = 0, ap = Asy; dev < ASY_MAX; dev++, ap++)
		if (ap->iface == ifp)
			break;
	if (dev == ASY_MAX) {
		tprintf ("Interface %s not asy port\n", argv[1]);
		return 1;
	}
	bp = pushdown (NULLBUF, (int16) strlen (argv[2]) + 2);
	strncpy ((char *)bp->data, argv[2], strlen(argv[2]) + 1);
	strcat ((char *)bp->data, "\r");
	bp->cnt = (int16) (strlen (argv[2]) + 1);
	(void) asy_send (dev, bp);
	return 0;
}


int
doasystat (int argc, char **argv, void *p OPTIONAL)
{
register struct asy *asyp;
struct iface *ifp;
int i;
int k;

	if (argc < 2) {
		for (k = 0, asyp = Asy; k < ASY_MAX; k++, asyp++) {
			if (asyp->iface != NULLIF)
				pasy (asyp);
		}
		return 0;
	}
	for (i = 1; i < argc; i++) {
		if ((ifp = if_lookup (argv[i])) == NULLIF) {
			tprintf ("Interface %s unknown\n", argv[i]);
			continue;
		}
		for (k = 0, asyp = Asy; k < ASY_MAX; k++, asyp++) {
			if (asyp->iface == ifp) {
				pasy (asyp);
				break;
			}
		}
		if (k == ASY_MAX || !asyp)
			tprintf ("Interface %s not asy\n", argv[i]);
	}

	return 0;
}



#ifdef KISS

/* Attach a pseudo ax25 interface to the system, for local loopback of ax25
 *
 */
int axloop_attach (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
register struct iface *ifp;
int xdev;
struct slip *sp = (struct slip *)0;

	if (if_lookup ("axlp") != NULLIF) {
		tprintf (Existingiface, "axlp");
		return -1;
	}

	/* Create interface structure and fill in details */
	ifp = (struct iface *) callocw (1, sizeof (struct iface));

	ifp->addr = Ip_addr;
	ifp->name = strdup ("axlp");
	ifp->iface_metric = 1;
	ifp->mtu = 1500;
	ifp->dev = 0;
	ifp->flags = LOOPBACK_AX25;
	ifp->stop = asy_detach;


	/* Set up a SLIP link to use AX.25 */
	for (xdev = 0; xdev < SLIP_MAX; xdev++) {
		sp = &Slip[xdev];
		if (sp->iface == NULLIF)
			break;
	}
	if (!sp || xdev >= SLIP_MAX) {
		tputs ("Too many ax25"
#ifdef POLLEDKISS
		       "or pkiss"
#endif
		       " devices\n");
		free (ifp->name);
		free ((char *) ifp);
		return -1;
	}

	(void) setencap (ifp, "AX25");
	ifp->ioctl = kiss_ioctl;
	ifp->raw = kiss_raw;

	ifp->show = slip_status;
	ifp->port = 0;	/* G1EMM */
	if (ifp->hwaddr == NULLCHAR)
		ifp->hwaddr = mallocw (AXALEN);
	memcpy (ifp->hwaddr, Mycall, AXALEN);
	if (ifp->ipcall == NULLCHAR)
		ifp->ipcall = mallocw (AXALEN);
	memcpy (ifp->ipcall, Mycall, AXALEN);
	ifp->xdev = xdev;

	sp->iface = ifp;
	sp->send = asy_send;
	sp->kiss[ifp->port] = ifp;	/* G1EMM */
	sp->get = get_asy;
	sp->type = CL_KISS;
	sp->polled = sp->usecrc = sp->tracepoll = 0;

	ifp->next = Ifaces;
	Ifaces = ifp;
	return 0;
}
#endif

