/* Generic serial line interface routines
 * Copyright 1991 Phil Karn, KA9Q
 */
 /* Mods by G1EMM */
#include <stdio.h>
#include "global.h"
#include "config.h"
#include "proc.h"
#include "iface.h"
#include "netuser.h"
#include "slhc.h"
#include "8250.h"
#include "asy.h"
#include "ax25.h"
#include "kiss.h"
#include "pktdrvr.h"
#include "ppp.h"
#include "slip.h"
#include "nrs.h"
#include "commands.h"
#include "mbuf.h"

static int asy_detach __ARGS((struct iface *ifp));


/* 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"
 * 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)
 * 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' for cts flow control;
 *		'r' for RLSD (RS232 pin 8, CD) physical link up/down;
 *		'v' for Van Jacobson TCP header compression (SLIP only,
 *		    use ppp command for VJ compression with PPP);
 */
int
asy_attach(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct iface *ifp;
	struct asy *asyp;
	char *ifn;
	int dev;
	int xdev;
	int trigchar = -1;
	char cts = FALSE;
	char rlsd = FALSE;
#if	defined(SLIP) || defined(AX25)
	struct slip *sp;
#endif
#ifdef	NRS
	struct nrs *np;
#endif

	if(if_lookup(argv[4]) != NULLIF){
		tprintf("Interface %s already exists\n",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){
		tprintf("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->mtu = atoi(argv[6]);
	ifp->dev = dev;
	ifp->stop = asy_detach;

#ifdef	SLIP
	if(stricmp(argv[3],"SLIP") == 0) {
		for(xdev = 0;xdev < SLIP_MAX;xdev++){
			sp = &Slip[xdev];
			if(sp->iface == NULLIF)
				break;
		}
		if(xdev >= SLIP_MAX) {
			tprintf("Too many slip devices\n");
			free(ifp->name);
			free((char *)ifp);
			return -1;
		}
		setencap(ifp,"SLIP");
		ifp->ioctl = asy_ioctl;
		ifp->raw = slip_raw;
		ifp->status = 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) {
		/* 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(xdev >= SLIP_MAX) {
			tprintf("Too many ax25 devices\n");
			free(ifp->name);
			free((char *)ifp);
			return -1;
		}
		setencap(ifp,"AX25");
		ifp->ioctl = kiss_ioctl;
		ifp->raw = kiss_raw;
		ifp->status = slip_status;
		ifp->port = 0;			/* G1EMM */
		if(ifp->hwaddr == NULLCHAR)
			ifp->hwaddr = mallocw(AXALEN);
		memcpy(ifp->hwaddr,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;
		trigchar = FR_END;
		ifp->rxproc = newproc( ifn = if_name( ifp, " rx" ),
			256,asy_rx,xdev,NULL,NULL,0);
		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(xdev >= SLIP_MAX) {
			tprintf("Too many nrs devices\n");
			free(ifp->name);
			free((char *)ifp);
			return -1;
		}
		/* no call supplied? */
		setencap(ifp,"AX25");
		ifp->ioctl = asy_ioctl;
		ifp->raw = nrs_raw;
/*		ifp->status = nrs_status; */
		ifp->hwaddr = mallocw(AXALEN);
		memcpy(ifp->hwaddr,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;
		setencap(ifp,"PPP");
		ifp->ioctl = asy_ioctl;
		ifp->flags = FALSE;
		/* Initialize parameters for various PPP phases/protocols */
		if((argc > 8) && (strchr(argv[8],'r') != NULLCHAR))
			rlsd = TRUE;
		if (ppp_init(ifp) != 0) {
			tprintf("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;
	if((argc > 8) && (strchr(argv[8],'c') != NULLCHAR))
		cts = TRUE;
	asy_init(dev,ifp,argv[1],argv[2],(int16)atol(argv[5]),
		trigchar,cts,rlsd,(int16)atol(argv[7]));
	return 0;
}

static int
asy_detach(ifp)
struct iface *ifp;
{
	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) {
		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;
char *argv[];
void *p;
{
	register struct iface *ifp;
	register struct asy *ap;
	int dev;
	int c;
	struct mbuf *bp;
	
	if((ifp = if_lookup(argv[1])) == NULLIF){
		tprintf("Interface %s unknown\n",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,strlen(argv[2]) + 2 );
	strcpy(bp->data,argv[2]);
	strcat(bp->data,"\r");
	bp->cnt = strlen(argv[2]) + 1;
	asy_send(dev,bp);
	return 0;
}

