#ifdef OSK
/* OS- and machine-dependent stuff for asynch ports on OS-9
 * Copyright 1993 Brian A. Lantz, KO4KS
 */
#include "global.h"
#include <dos.h>
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "n8250.h"
#include "asy.h"
#include "devparam.h"
#include "pc.h"
#include <sgstat.h>
#include <modes.h>

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: osk_asy.c,v 1.9 1996/12/23 22:44:37 root Exp $";
#endif

extern int _gs_opt();
extern void _ss_opt();

#define ASY_MAX 5
#define PARAM_SPEED 10

static void asy_tx (int dev,void *p1,void *p2);

struct asy Asy[ASY_MAX];

/* Initialize asynch port "dev" */
int
asy_init(dev,ifp,arg1,arg2,bufsize,trigchar,monitor,speed,force,triglevel)
int dev;
struct iface *ifp;
char *arg1,*arg2;	/* Attach args for address and vector */
int16 bufsize;
int trigchar;
char monitor;
long speed;
int force;
int triglevel;
{
register unsigned base;
register struct fifo *fp;
register struct asy *ap;
char termname[8], prefix[4];
char *ifn;
struct sgbuf sbuf;
short *nm = (short *) prefix;
        
        ap = &Asy[dev];
        ap->iface = ifp;

	ap->addr = atoi(arg2);   /* hex string for device name prefix */
	*nm = htoi(arg1);   /* hex-coded ASCII for name prefix "t" etc */
	if (!prefix[0])
		prefix[0] = 't';

	/* Set up receiver FIFO */
	fp = &ap->fifo;
	fp->buf = mallocw(bufsize);
	fp->bufsize = bufsize;
	fp->wp = fp->rp = fp->buf;
	fp->cnt = 0;
	fp->hiwat = 0;
	fp->overrun = 0;
	base = ap->addr;
	ap->trigchar = trigchar;

        sprintf (termname, "/%s%d", prefix, base);
        if ((ap->vec = (unsigned) fopen(termname, "a+")) == 0) {
        	tprintf("asy%d: Open failed.\n", dev);
        	return -1;
        }
        setbuf ((FILE *) ap->vec, NULL);
	asy_speed(dev,speed);

        _gs_opt(fileno((FILE *)ap->vec), &sbuf);
        sbuf.sg_case = 0;
        sbuf.sg_backsp = 0;
        sbuf.sg_delete = 0;
        sbuf.sg_echo = 0;
        sbuf.sg_alf = 0;
/*        sbuf.sg_nulls = 0;	*/
        sbuf.sg_pause = 0;
        sbuf.sg_page = 0;
        sbuf.sg_bspch = 0;
        sbuf.sg_dlnch = 0;
        sbuf.sg_eorch = 0;
        sbuf.sg_eofch = 0;
        sbuf.sg_rlnch = 0;
        sbuf.sg_dulnch = 0;
        sbuf.sg_psch = 0;
        sbuf.sg_kbich = 0;
        sbuf.sg_kbach = 0;
        sbuf.sg_bsech = 0;
        sbuf.sg_bellch = 0;
        sbuf.sg_xon = 0;
        sbuf.sg_xoff = 0;
/*        sbuf.sg_tabcr = 0;
        sbuf.sg_tabsiz = 0;	*/
        _ss_opt(fileno((FILE *)ap->vec), &sbuf);

	ifp->txproc = newproc( ifn = if_name(ifp," tx"),
			256, asy_tx, dev, ifp, NULL, 0);
	free(ifn);

        return 0;
}


int
asy_stop(ifp)
struct iface *ifp;
{
	register unsigned base;
	register struct asy *ap;

	ap = &Asy[ifp->dev];
	free(ap->fifo.buf);
	fclose ((FILE *)ap->vec);
	ap->vec = 0;
	return 0;
}


/* Asynchronous line I/O control */
int32
asy_ioctl(ifp,cmd,set,val)
struct iface *ifp;
int cmd;
int set;
int32 val;
{
struct asy *ap = &Asy[ifp->dev];
int16 base = ap->addr;

	switch(cmd){
	case PARAM_SPEED:
		if(set)
			asy_speed(ifp->dev,val);
		return ap->speed;
#ifdef notyet
	case PARAM_DTR:
		if(set) {
			writebit(base+MCR,MCR_DTR,(int)val);
			ap->dtr_usage = (val) ? MOVED_UP : MOVED_DOWN;
		}
		return (inportb(base+MCR) & MCR_DTR) ? TRUE : FALSE;
	case PARAM_RTS:
		if(set) {
			writebit(base+MCR,MCR_RTS,(int)val);
			ap->rts_usage = (val) ? MOVED_UP : MOVED_DOWN;
		}
		return (inportb(base+MCR) & MCR_RTS) ? TRUE : FALSE;
	case PARAM_DOWN:
		clrbit(base+IER,(char)IER_DAV);
		clrbit(base+MCR,MCR_RTS);
		clrbit(base+MCR,MCR_DTR);
		ap->rts_usage = MOVED_DOWN;
		ap->dtr_usage = MOVED_DOWN;
		return FALSE;
	case PARAM_UP:
		setbit(base+IER,(char)IER_DAV);
		setbit(base+MCR,MCR_RTS);
		setbit(base+MCR,MCR_DTR);
		ap->rts_usage = MOVED_UP;
		ap->dtr_usage = MOVED_UP;
		return TRUE;
	case PARAM_BLIND:
		setbit(base+IER,(char)IER_DAV);
		ap->rlsd_line_control = IGNORED;

		if ( ap->monitor != NULL ) {
			killproc( ap->monitor );
			ap->monitor = NULL;
		}

		/* can't see what we are doing, so pretend we're up */
		if ( ifp->iostatus != NULL ) {
			(*ifp->iostatus)(ifp, PARAM_UP, 0L);
		}
		return TRUE;
#endif
	};
	return -1;
}


/* Set asynch line speed */
int
asy_speed(dev,speed)
int dev;
long speed;
{
	return 0;
}


/* Start transmission of a buffer on the serial transmitter */
static void
asy_output(dev,buf,cnt)
int dev;
char *buf;
unsigned short cnt;
{
struct asy *asyp;

	if(dev < 0 || dev >= ASY_MAX)
		return;
	asyp = &Asy[dev];
	if(asyp->iface == NULLIF)
		return;
	asyp->txints++;
	asyp->txchar += cnt;
	fwrite (buf, 1, cnt, (FILE *)asyp->vec);
}


/* Blocking read from asynch line
 * Returns character or -1 if aborting
 * Returns -2 (CD_DOWN) if carrier checking is on and dropped
 */
int
get_asy(dev)
int dev;
{
char i_state;
register struct fifo *fp;
char c;

	fp = &Asy[dev].fifo;

	while(fp->cnt == 0){
		if(kwait(fp) != 0)
			return -1;
	}
	fp->cnt--;

	c = *fp->rp++;
	if(fp->rp >= &fp->buf[fp->bufsize])
		fp->rp = fp->buf;

	return uchar(c);
}

/* Process received characters */
static int
asyrxint(asyp)
struct asy *asyp;
{
register struct fifo *fp;
char c,lsr;
int cnt = 0, doneone = 0;
int trigseen = FALSE;

	if (!asyp->vec)
		return cnt;
	fp = &asyp->fifo;
	for(;;){
		if(_gs_rdy(fileno((FILE *)asyp->vec)) > 0){
			asyp->rxchar++;
			doneone = 1;
			fread (&c, 1, 1, (FILE *)asyp->vec);
			if(asyp->trigchar == uchar(c))
				trigseen = TRUE;
			/* If buffer is full, we have no choice but
			 * to drop the character
			 */
			if(fp->cnt != fp->bufsize){
				*fp->wp++ = c;
				if(fp->wp >= &fp->buf[fp->bufsize])
					/* Wrap around */
					fp->wp = fp->buf;
				fp->cnt++;
				if(fp->cnt > fp->hiwat)
					fp->hiwat = fp->cnt;
				cnt++;
			} else
				fp->overrun++;
		} else
			break;
	}
	if(cnt > asyp->rxhiwat)
		asyp->rxhiwat = cnt;
	if(trigseen)
		ksignal(fp,1);
	if (doneone)
		asyp->rxints++;
	return cnt;
}


/* Poll the asynch input queues; called on every clock tick.
 * This helps limit the interrupt ring buffer occupancy when long
 * packets are being received.
 */
void
asytimer()
{
register struct asy *asyp;
register struct fifo *fp;
register int i;

	for(i=0;i<ASY_MAX;i++){
		asyp = &Asy[i];
		asyrxint (asyp);
		fp = &asyp->fifo;
		if(fp->cnt != 0)
			ksignal(fp,1);
#ifdef notyet TIPMAIL
		if (Tipsuspended[i].ifp)	{
			if (!carrier_detect(i))	{
				char buf[40];
				sprintf (buf, "start tip %s %s %d\n", Tipsuspended[i].ifp->name, (Tipsuspended[i].modem) ? "m" : "t", Tipsuspended[i].timeout);
				cmdparse(Cmds,buf,NULL);
				Tipsuspended[i].ifp = 0;
				Tipsuspended[i].modem = 0;
				Tipsuspended[i].timeout = 0;
			}
		}
#endif
	}
}


int
doasystat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct asy *asyp;

	for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
		if(asyp->iface == NULLIF)
			continue;

		tprintf("%s:",asyp->iface->name);
		if(asyp->trigchar != -1)
			tprintf(" [trigger 0x%02x]",asyp->trigchar);
#ifdef notyet
		switch (asyp->cts_flow_control) {
		case MOVED_DOWN:
		case MOVED_UP:
			tprintf(" [cts flow control]");
			break;
		};
		switch (asyp->rlsd_line_control) {
		case MOVED_DOWN:
		case MOVED_UP:
			tprintf(" [rlsd line control]");
			break;
		case IGNORED:
			tprintf(" [blind]");
			break;
		};
#endif
		tprintf(" %lu bps\n",asyp->speed);

		tprintf(" RX: %lu int, %lu chr, %lu hw over, %lu hw hi\n",
			asyp->rxints,
			asyp->rxchar,
			asyp->overrun,
			asyp->rxhiwat);
		asyp->rxhiwat = 0;
		if(tprintf(" TX: %lu int, %lu chars, %u q\n",
			asyp->txints, asyp->txchar, len_q(asyp->sndq)) == EOF)
			break;
	}
	return 0;
}


/* Send a message on the specified serial line */
int
asy_send(dev,bp)
int dev;
struct mbuf *bp;
{
	if(dev < 0 || dev >= ASY_MAX)
		return -1;
	enqueue(&Asy[dev].sndq,bp);
	return 0;
}



/* Serial transmit process, common to all protocols */
static void
asy_tx(dev,p1,p2)
int dev;
void *p1;
void *p2;
{
register struct mbuf *bp;
struct asy *asyp;

	asyp = &Asy[dev];

	for(;;){
		/* Fetch a buffer for transmission */
		while(asyp->sndq == NULLBUF)
			kwait(&asyp->sndq);

		bp = dequeue(&asyp->sndq);

		while(bp != NULLBUF){
			/* Start the transmitter */
			asy_output(dev,bp->data,bp->cnt);

			/* Now do next buffer on chain */
			bp = free_mbuf(bp);
		}
	}
}


int
carrier_detect(dev)
int dev;
{
	return 1;
}



#endif /* OSK */

