/* support for serial communication using COMBIOS/MBBIOS/FOSSIL on the IBM-PC */

#include <stdio.h>
#include "global.h"
#include "asy.h"
#include "combios.h"
#include "slip.h"
#include "iface.h"

struct com com[ASY_MAX];		/* info about the COMBIOS ports */

extern unsigned int combios();		/* INT14 access routine */
					/* args are AX and DX */

static char badspeed[] = "Bad speed on COM\n";

/* Initialize COM port "dev" */
int
com_init(dev)
int16 dev;
{
	if (com[dev].drtype != 0){	/* port already in use? */
		printf("COM%d: already in use\n",dev + 1);
		return 1;
	}

	switch (combios(0x400,dev))	/* Probe driver & INIT (FOSSIL) */
	{
	case 0x1954:			/* FOSSIL */
		com[dev].drtype = 3;	/* remember it is FOSSIL */
		combios(0x0ff0,dev);	/* set flowctrl (off for now) */
		combios(0x1000,dev);	/* ^C/^K checks OFF */
		break;

	case 0xaa55:			/* COMBIOS/MBBIOS */
		com[dev].drtype = 2;	/* remember it is MBBIOS */
		combios(0x0901,dev);	/* set options (flow off for now) */
		break;

	default:
		com[dev].drtype = 1;	/* unknown type of driver */
		printf("Warning: no COMBIOS/MBBIOS/FOSSIL loaded for COM%d\n",dev + 1);
		break;
	}

	if (combios(0x00e3,dev) == 0x00e3) {
		com[dev].drtype = 0;
		printf("No BIOS support for COM%d\n",dev + 1);
		return 1;
	}
	return 0;
}
/* Terminate COM port for "iface" */
int
com_stop(iface)
struct interface *iface;
{
	combios(0x0000,iface->dev);	/* set parameters 00 (=close) */
	if (com[iface->dev].drtype == 3) /* if it is FOSSIL */
		combios(0x0500,iface->dev); /* call it's uninit hook */
	com[iface->dev].drtype = 0;
}
/* Asynchronous line I/O control */
com_ioctl(interface,argc,argv)
struct interface *interface;
int argc;
char *argv[];
{
	if(argc < 2){
		printf("%u %c\n",com[interface->dev].speed,
				 "nxh"[com[interface->dev].mode]);
		return 0;
	}
	return com_speed(interface->dev,atoi(argv[0]),argv[1]);
}
/* Set asynch line speed */
int
com_speed(dev,speed,hands)
int16 dev;
unsigned speed;
char *hands;
{
	unsigned char parm0,parm9;	/* parameters for COMBIOS */
	int i;
	int mode;

	for (mode = 2; mode >= 0; mode--)
		if (*hands == "nxh"[mode])
			break;		/* set mode to 0 (none) 1 XON 2 hardw */

	switch (com[dev].drtype)	/* action depends on driver type */
	{
	case 1:				/* unknown type, assume standard */
		switch (speed)
		{
		case	300: parm0 = 0x43; break;
		case	600: parm0 = 0x63; break;
		case   1200: parm0 = 0x83; break;
		case   2400: parm0 = 0xa3; break;
		case   4800: parm0 = 0xc3; break;
		case   9600: parm0 = 0xe3; break;
		default:
			printf(badspeed);
			return 1;
		}

		combios((unsigned int) parm0,dev);	/* set speed */
		break;

	case 2:				/* COMBIOS/MBBIOS */
		switch (speed)
		{
		case	300: parm0 = 0x43; parm9 = 0x00; break;
		case	600: parm0 = 0x63; parm9 = 0x00; break;
		case   1200: parm0 = 0x83; parm9 = 0x00; break;
		case   2400: parm0 = 0xa3; parm9 = 0x00; break;
		case   4800: parm0 = 0xc3; parm9 = 0x00; break;
		case   9600: parm0 = 0xe3; parm9 = 0x00; break;
		case  14400: parm0 = 0x23; parm9 = 0x20; break;
		case  19200: parm0 = 0x43; parm9 = 0x20; break;
		case  28800: parm0 = 0x63; parm9 = 0x20; break;
		case  38400: parm0 = 0x83; parm9 = 0x20; break;
		case  57600: parm0 = 0xa3; parm9 = 0x20; break;
		case 115200: parm0 = 0xc3; parm9 = 0x20; break;
		case 330400: parm0 = 0xe3; parm9 = 0x20; break;
		default:
			printf(badspeed);
			return 1;
		}

		combios(0x0901 | parm9 | ((mode == 2)? 4 : 0),dev);
		combios((unsigned int) parm0,dev);

		if (com[dev].speed != speed) {		/* speed change? */
			com[dev].txbuf = 0;
			for (i = 0; i < 5; i++) {	/* send some chars */
				if ((combios(0x0100 | FR_END,dev) & 0xa000) != 0x2000)
				{			/* timeout or full */
					com[dev].txbuf = speed / 100;
					break;		/* store chars for .1 s */
				}
			}
		}
		break;

	case 3:				/* FOSSIL */
		switch (speed)
		{
		case	300: parm0 = 0x43; break;
		case	600: parm0 = 0x63; break;
		case   1200: parm0 = 0x83; break;
		case   2400: parm0 = 0xa3; break;
		case   4800: parm0 = 0xc3; break;
		case   9600: parm0 = 0xe3; break;
		case  19200: parm0 = 0x03; break;
		case  38400: parm0 = 0x23; break;
		default:
			printf(badspeed);
			return 1;
		}

		parm9 = 0xf0;			/* calculate handshake parm */
		if (mode == 1)			/* XON/XOFF */
			parm9 |= 9;
		else
			if (mode == 2)		/* hardware (RTS/CTS) */
				parm9 |= 2;

		combios(0xf00 | parm9,dev);	/* set handshake */
		combios((unsigned int) parm0,dev);	/* set speed */
		break;
	}

	com[dev].mode = mode;
	com[dev].speed = speed;
	return 0;
}
