/* 
 * Mach Operating System
 * Copyright (c) 1994,1993,1991,1990 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

#if NCOM > 0

#include <string.h>
#include <util/atoi.h>

#include <mach/std_types.h>
#include <sys/types.h>
#include <kern/printf.h>
#include <kern/mach_clock.h>
#include <device/conf.h>
#include <device/device_types.h>
#include <device/tty.h>
#include <device/io_req.h>

#include <i386/ipl.h>
#include <i386/pio.h>
#include <i386/spl.h>
#include <chips/busses.h>
#include <i386at/autoconf.h>
#include <i386at/com.h>
#include <i386at/comreg.h>

#include <device/cons.h>

static void comparam(int);

static vm_offset_t com_std[NCOM] = { 0 };
struct bus_device *cominfo[NCOM];
struct bus_driver comdriver = {
	comprobe, 0, comattach, 0, com_std, "com", cominfo, 0, 0, 0};

struct tty com_tty[NCOM];
int commodem[NCOM];
int comcarrier[NCOM] = {0, 0,};
boolean_t comfifo[NCOM];
boolean_t comtimer_active;
int comtimer_state[NCOM];

#define RCBAUD B115200
static int rcline = -1;
static struct bus_device *comcndev;

/* XX */
extern char *kernel_cmdline;

#define ISPEED	B115200
#define IFLAGS	(EVENP|ODDP|ECHO|CRMOD|XTABS|LITOUT)

u_short divisorreg[] = {
	0,	2304,	1536,	1047,		/*     0,    50,    75,   110*/
	857,	 768,	 576,	 384,	 192,	/*   134.5, 150,   200,   300,   600*/
	 96,	  64,		  48,		/*  1200,  1800,  2000,  2400 */
      		  24,	  	  12,		/*  3600,  4800,  7200,  9600 */
	6,	   3,	   2,	   1};		/* 19200, 38400, 56000,115200 */


/*
 *
 * Probes are called during kernel boot: return 1 to mean that
 * the relevant device is present today.
 *
 */
static int
comprobe_general(struct bus_device *dev, int noisy)
{
	u_short	addr = dev->address;
	int	unit = dev->unit;
	int     oldctl, oldmsb;
	char    *type = "8250";
	int     i;

	if ((unit < 0) || (unit >= NCOM)) {
		printf("com %d out of range\n", unit);
		return(0);
	}
	oldctl = inb(LINE_CTL(addr));	 /* Save old value of LINE_CTL */
	oldmsb = inb(BAUD_MSB(addr));	 /* Save old value of BAUD_MSB */
	outb(LINE_CTL(addr), 0);	 /* Select INTR_ENAB */    
	outb(BAUD_MSB(addr), 0);
	if (inb(BAUD_MSB(addr)) != 0)
	  {
	    outb(LINE_CTL(addr), oldctl);
	    outb(BAUD_MSB(addr), oldmsb);
	    return 0;
	  }
	outb(LINE_CTL(addr), iDLAB);	 /* Select BAUD_MSB */
	outb(BAUD_MSB(addr), 255);
	if (inb(BAUD_MSB(addr)) != 255)
	  {
	    outb(LINE_CTL(addr), oldctl);
	    outb(BAUD_MSB(addr), oldmsb);
	    return 0;
	  }
	outb(LINE_CTL(addr), 0);	 /* Select INTR_ENAB */
	if (inb(BAUD_MSB(addr)) != 0)	 /* Check that it has kept its value*/
	  {
	    outb(LINE_CTL(addr), oldctl);
	    outb(BAUD_MSB(addr), oldmsb);
	    return 0;
	  }

	/* Com port found, now check what chip it has */
	
	for(i = 0; i < 256; i++)	 /* Is there Scratch register */
	  {
	    outb(SCR(addr), i);
	    if (inb(SCR(addr)) != i)
	      break;
	  }
	if (i == 256)
	  {				 /* Yes == 450 or 460 */
	    outb(SCR(addr), 0);
	    type = "82450 or 16450";
	    outb(FIFO_CTL(addr), iFIFOENA | iFIFO14CH);	 /* Enable fifo */
	    if ((inb(FIFO_CTL(addr)) & iFIFO14CH) != 0)
	      {				 /* Was it successful */
		/* if both bits are not set then broken xx550 */
		if ((inb(FIFO_CTL(addr)) & iFIFO14CH) == iFIFO14CH)
		  {
		    type = "82550 or 16550";
		    comfifo[unit] = TRUE;
		  }
		else
		  {
		    type = "82550 or 16550 with non-working FIFO";
		  }
		outb(INTR_ID(addr), 0x00); /* Disable fifos */
	      }
	  }
	if (noisy)
		printf("com%d: %s chip.\n", unit, type);
	return 1;
}

/*
 * Probe routine for use during kernel startup when it is probing
 * all of bus_device_init
 */
int
comprobe(vm_offset_t port, struct bus_ctlr *dev)
{
	return comprobe_general((struct bus_device *)dev, /*noisy*/ 0);
}

/*
 * Probe routine for use by the console
 */
int
comcnprobe(struct consdev *cp)
{
	struct	bus_device *b;
	int	maj, unit, pri;

#define CONSOLE_PARAMETER " console=com"
	u_char *console = (u_char *) strstr(kernel_cmdline, CONSOLE_PARAMETER);

	if (console)
		mach_atoi(console + strlen(CONSOLE_PARAMETER), &rcline);

	if (strncmp(kernel_cmdline, CONSOLE_PARAMETER + 1,
		    strlen(CONSOLE_PARAMETER) - 1) == 0)
            mach_atoi((u_char*)kernel_cmdline + strlen(CONSOLE_PARAMETER) - 1,
			  &rcline);

	maj = 0;
	unit = -1;
	pri = CN_DEAD;

	for (b = bus_device_init; b->driver; b++)
		if (strcmp(b->name, "com") == 0
		    && b->unit == rcline
		    && comprobe_general(b, /*quiet*/ 0))
		{
			/* Found one */
			comcndev = b;
			unit = b->unit;
			pri = CN_REMOTE;
			break;
		}

	cp->cn_dev = makedev(maj, unit);
	cp->cn_pri = pri;

	return 0;
}


/*
 *
 * Device Attach's are called during kernel boot, but only if the matching
 * device Probe returned a 1.
 *
 */
void
comattach(struct bus_device *dev)
{
	u_char	unit = dev->unit;
	u_short	addr = dev->address;

	if (unit >= NCOM) {
		printf(", disabled by NCOM configuration\n");
		return;
	}

	take_dev_irq(dev);
	printf(", port = %zx, spl = %zu, pic = %d. (DOS COM%d)",
	       dev->address, dev->sysdep, dev->sysdep1, unit+1);

/*	comcarrier[unit] = addr->flags;*/
	commodem[unit] = 0;

	outb(INTR_ENAB(addr), 0);
	outb(MODEM_CTL(addr), 0);
	while (!(inb(INTR_ID(addr))&1)) {
		(void) inb(LINE_STAT (addr)); 	/* reset overrun error etc */
		(void) inb(TXRX      (addr)); 	/* reset data-ready */
		(void) inb(MODEM_STAT(addr)); 	/* reset modem status reg */
	}
}

/*
 * Attach/init routine for console.  This isn't called by
 * configure_bus_device which sets the alive, adaptor, and minfo
 * fields of the bus_device struct (comattach is), therefore we do
 * that by hand.
 */
int
comcninit(struct consdev *cp)
{
	u_char	unit = comcndev->unit;
	u_short	addr = comcndev->address;
		      
	take_dev_irq(comcndev);

	comcndev->alive = 1;
	comcndev->adaptor = 0;
	cominfo[minor(cp->cn_dev)] = comcndev;

	outb(LINE_CTL(addr), iDLAB);
	outb(BAUD_LSB(addr), divisorreg[RCBAUD] & 0xff);
	outb(BAUD_MSB(addr), divisorreg[RCBAUD] >>8);
	outb(LINE_CTL(addr), i8BITS);
	outb(INTR_ENAB(addr), 0);
	outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2);

	{
		char	msg[128];
		volatile unsigned char *p = (volatile unsigned char *)phystokv(0xb8000);
		int	i;

		sprintf(msg, "    **** using COM port %d for console ****",
			unit+1);
		for (i = 0; msg[i]; i++) {
			p[2*i] = msg[i];
			p[2*i+1] = (0<<7)	/* blink */
				   | (0x0<<4)	/* bg */
				   | (1<<3)	/* hi-intensity */
				   | 0x4;	/* fg */
		}
	}

	return 0;
}

/*
 *	Probe for COM<dev> after autoconfiguration.
 *	Used to handle PCMCIA modems, which may appear
 *	at any time.
 */
static boolean_t com_reprobe(
	int	unit)
{
	struct bus_device	*device;

	/*
	 *	Look for COM device <unit> in the device
	 *	initialization list.  It must not be alive
	 *	(otherwise we would have opened it already).
	 */
	for (device = bus_device_init; device->driver; device++) {
	    if (device->driver == &comdriver && device->unit == unit &&
		!device->alive && device->ctlr == (char)-1)
	    {
		/*
		 *	Found an entry for com port <unit>.
		 *	Probe it.
		 */
		if (configure_bus_device(device->name,
					 device->address,
					 device->phys_address,
					 0,
					 "atbus"))
		    return TRUE;
	    }
	}
	return FALSE;
}

io_return_t comopen(
	dev_t dev,
	int flag,
	io_req_t ior)
{
	int		unit = minor(dev);
	u_short		addr;
	struct bus_device	*isai;
	struct tty	*tp;
	spl_t		s;
	io_return_t	result;

	if (unit >= NCOM)
	    return D_NO_SUCH_DEVICE;	/* no such device */
	if ((isai = cominfo[unit]) == 0 || isai->alive == 0) {
	    /*
	     *	Try to probe it again
	     */
	    if (!com_reprobe(unit))
		return D_NO_SUCH_DEVICE;
	    if ((isai = cominfo[unit]) == 0 || isai->alive == 0)
		return D_NO_SUCH_DEVICE;
	}
	tp = &com_tty[unit];

	if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) {
		ttychars(tp);
		tp->t_addr = (char *)isai->address;
		tp->t_dev = dev;
		tp->t_oproc = comstart;
		tp->t_stop = comstop;
		tp->t_mctl = commctl;
		tp->t_getstat = comgetstat;
		tp->t_setstat = comsetstat;
		if (tp->t_ispeed == 0) {
			tp->t_ispeed = ISPEED;
			tp->t_ospeed = ISPEED;
			tp->t_flags = IFLAGS;
			tp->t_state &= ~TS_BUSY;
		}
	}
/*rvb	tp->t_state |= TS_WOPEN; */
	if ((tp->t_state & TS_ISOPEN) == 0)
		comparam(unit);
	addr = (uintptr_t)tp->t_addr;

	s = spltty();
	if (!comcarrier[unit])	/* not originating */
		tp->t_state |= TS_CARR_ON;
	else {
		int modem_stat = inb(MODEM_STAT(addr));
		if (modem_stat & iRLSD)
			tp->t_state |= TS_CARR_ON;
		else
			tp->t_state &= ~TS_CARR_ON;
		fix_modem_state(unit, modem_stat);
	} 
	splx(s);

	result = char_open(dev, tp, flag, ior);

	if (!comtimer_active) {
		comtimer_active = TRUE;
		comtimer(NULL);
	}

	s = spltty();
	while(!(inb(INTR_ID(addr))&1)) { /* while pending interrupts */
		(void) inb(LINE_STAT (addr)); /* reset overrun error  */
		(void) inb(TXRX      (addr)); /* reset data-ready	    */
		(void) inb(MODEM_STAT(addr)); /* reset modem status   */
	}
	splx(s);
	return result;
}

void comclose(dev_t dev, int flag)
{
	struct tty	*tp = &com_tty[minor(dev)];
	u_short		addr = (uintptr_t)tp->t_addr;

	ttyclose(tp);
	if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) { 
		outb(INTR_ENAB(addr), 0);
		outb(MODEM_CTL(addr), 0);
		tp->t_state &= ~TS_BUSY;
		commodem[minor(dev)] = 0;
		if (comfifo[minor(dev)] != 0)
			outb(INTR_ID(addr), 0x00); /* Disable fifos */
	}
	return;
}

io_return_t comread(dev_t dev, io_req_t ior)
{
	return char_read(&com_tty[minor(dev)], ior);
}

io_return_t comwrite(dev_t dev, io_req_t ior)
{
	return char_write(&com_tty[minor(dev)], ior);
}

io_return_t comportdeath(dev_t dev, mach_port_t port)
{
	return (tty_portdeath(&com_tty[minor(dev)], (ipc_port_t)port));
}

io_return_t
comgetstat(
  dev_t dev,
  dev_flavor_t flavor,
  dev_status_t data, /* pointer to OUT array */
  mach_msg_type_number_t *count /* out */
  )
{
	io_return_t	result = D_SUCCESS;
	int		unit = minor(dev);

	switch (flavor) {
	case TTY_MODEM:
		fix_modem_state(unit, inb(MODEM_STAT(cominfo[unit]->address)));
		*data = commodem[unit];
		*count = 1;
		break;
	default:
		result = tty_get_status(&com_tty[unit], flavor, data, count);
		break;
	}
	return (result);
}

io_return_t
comsetstat(
	dev_t		dev,
	dev_flavor_t	flavor,
	dev_status_t	data,
	mach_msg_type_number_t	count)
{
	io_return_t	result = D_SUCCESS;
	int 		unit = minor(dev);
	struct tty	*tp = &com_tty[unit];

	switch (flavor) {
	case TTY_SET_BREAK:
		commctl(tp, TM_BRK, DMBIS);
		break;
	case TTY_CLEAR_BREAK:
		commctl(tp, TM_BRK, DMBIC);
		break;
	case TTY_MODEM:
		commctl(tp, *data, DMSET);
		break;
	default:
		result = tty_set_status(&com_tty[unit], flavor, data, count);
		if (result == D_SUCCESS && flavor == TTY_STATUS)
			comparam(unit);
		return (result);
	}
	return (D_SUCCESS);
}

void
comintr(int unit)
{
	struct tty		*tp = &com_tty[unit];
	u_short			addr = cominfo[unit]->address;
	static char 		comoverrun = 0;
	char			c, line, intr_id;
	int			line_stat;

	while (! ((intr_id=(inb(INTR_ID(addr))&MASKi)) & 1))
	    switch (intr_id) { 
		case MODi: 
		    /* modem change */
			commodem_intr(unit, inb(MODEM_STAT(addr)));
			break;

		case TRAi:
			comtimer_state[unit] = 0;
			tp->t_state &= ~(TS_BUSY|TS_FLUSH);
			tt_write_wakeup(tp);
			(void) comstart(tp);
			break;
		case RECi:
		case CTIi:         /* Character timeout indication */
			if (tp->t_state&TS_ISOPEN) {
				int escape = 0;
				while ((line = inb(LINE_STAT(addr))) & iDR) {
					c = inb(TXRX(addr));

					if (c == 0x1b) {
						escape = 1;
						continue;
					}

#if MACH_KDB
					if (escape && c == 'D'-('A'-1))
						/* ctrl-alt-d pressed,
						   invoke debugger */
						kdb_kintr();
					else
#endif /* MACH_KDB */
					if (escape) {
						ttyinput(0x1b, tp);
						ttyinput(c, tp);
					}
					else
						ttyinput(c, tp);

					escape = 0;
				}

				if (escape)
					/* just escape */
					ttyinput(0x1b, tp);
			} else
				tt_open_wakeup(tp);
			break;
		case LINi: 
			line_stat = inb(LINE_STAT(addr));

			if ((line_stat & iPE) &&
			    ((tp->t_flags&(EVENP|ODDP)) == EVENP ||
			     (tp->t_flags&(EVENP|ODDP)) == ODDP)) {
				/* parity error */;
			} else 	if (line_stat&iOR && !comoverrun) {
				printf("com%d: overrun\n", unit);
				comoverrun = 1;
			} else if (line_stat & (iFE | iBRKINTR)) {
				/* framing error or break */
				ttyinput(tp->t_breakc, tp);
			}
			break;
		}
}

static void
comparam(int unit)
{
	struct tty	*tp = &com_tty[unit];
	u_short		addr = (uintptr_t)tp->t_addr;
	spl_t		s = spltty();
	int		mode;

        if (tp->t_ispeed == B0) {
		tp->t_state |= TS_HUPCLS;
		outb(MODEM_CTL(addr), iOUT2);
		commodem[unit] = 0;
		splx(s);
		return;
	}

	/* Do input buffering */
	if (tp->t_ispeed >= B300)
		tp->t_state |= TS_MIN;

	outb(LINE_CTL(addr), iDLAB);
	outb(BAUD_LSB(addr), divisorreg[tp->t_ispeed] & 0xff);
	outb(BAUD_MSB(addr), divisorreg[tp->t_ispeed] >> 8);

	if (tp->t_flags & (RAW|LITOUT|PASS8))
		mode = i8BITS;
	else
		mode = i7BITS | iPEN;
	if (tp->t_flags & EVENP)
		mode |= iEPS;
	if (tp->t_ispeed == B110)
		/*
		 * 110 baud uses two stop bits -
		 * all other speeds use one
		 */
		mode |= iSTB;

	outb(LINE_CTL(addr), mode);

	outb(INTR_ENAB(addr), iTX_ENAB|iRX_ENAB|iMODEM_ENAB|iERROR_ENAB);
	if (comfifo[unit])
		outb(FIFO_CTL(addr), iFIFOENA|iFIFO14CH);
	outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2);
	commodem[unit] |= (TM_DTR|TM_RTS);
        splx(s);
}

int comst_1, comst_2, comst_3, comst_4, comst_5 = 14;

void
comstart(struct tty *tp)
{
	int nch;
#if 0
	int i;
#endif

	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) {
comst_1++;
		return;
	}
	if ((!queue_empty(&tp->t_delayed_write)) &&
	    (tp->t_outq.c_cc <= TTLOWAT(tp))) {
comst_2++;
		tt_write_wakeup(tp);
	}
	if (!tp->t_outq.c_cc) {
comst_3++;
		return;
	}

#if 0
	i = (comfifo[minor(tp->t_dev)]) ? /*14*/comst_5 : 1;

	tp->t_state |= TS_BUSY;
	while (i-- > 0) {
		nch = getc(&tp->t_outq);
		if (nch == -1) break;
		if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) {
		    timeout(ttrstrt, (char *)tp, (nch & 0x7f) + 6);
		    tp->t_state |= TS_TIMEOUT;
comst_4++;
		    return(0);
		}
		outb(TXRX((uintptr_t)tp->t_addr), nch);
	}
#else
	nch = getc(&tp->t_outq);
	if (nch == -1)
		return;
	if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) {
	    timeout((timer_func_t *)ttrstrt, (char *)tp, (nch & 0x7f) + 6);
	    tp->t_state |= TS_TIMEOUT;
comst_4++;
	    return;
	}
	outb(TXRX((uintptr_t)tp->t_addr), nch);
	tp->t_state |= TS_BUSY;
#endif
}

/* Check for stuck xmitters */
int comtimer_interval = 5;

void
comtimer(void * param)
{
	spl_t	s = spltty();
	struct tty *tp = com_tty;
	int i, nch;

	for (i = 0; i < NCOM; i++, tp++) {
		if ((tp->t_state & TS_ISOPEN) == 0)
			continue;
		if (!tp->t_outq.c_cc)
			continue;
		if (++comtimer_state[i] < 2)
			continue;
		/* Its stuck */
printf("Tty %p was stuck\n", tp);
		nch = getc(&tp->t_outq);
		outb(TXRX((uintptr_t)tp->t_addr), nch);
	}

	splx(s);
	timeout(comtimer, 0, comtimer_interval*hz);
}

/*
 * Set receive modem state from modem status register.
 */
void
fix_modem_state(
	int	unit, 
	int	modem_stat)
{
	int	stat = 0;

	if (modem_stat & iCTS)
	    stat |= TM_CTS;	/* clear to send */
	if (modem_stat & iDSR)
	    stat |= TM_DSR;	/* data set ready */
	if (modem_stat & iRI)
	    stat |= TM_RNG;	/* ring indicator */
	if (modem_stat & iRLSD)
	    stat |= TM_CAR;	/* carrier? */

	commodem[unit] = (commodem[unit] & ~(TM_CTS|TM_DSR|TM_RNG|TM_CAR))
				| stat;
}

/*
 * Modem change (input signals)
 */
void
commodem_intr(
	int	unit,
	int	stat)
{
	int	changed;

	changed = commodem[unit];
	fix_modem_state(unit, stat);
	stat = commodem[unit];

	/* Assumption: if the other party can handle
	   modem signals then it should handle all
	   the necessary ones. Else fix the cable. */

	changed ^= stat;	/* what changed ? */

	if (changed & TM_CTS)
		tty_cts( &com_tty[unit], stat & TM_CTS );

#if 0
	if (changed & TM_CAR)
		ttymodem( &com_tty[unit], stat & TM_CAR );
#endif

}

/*
 * Set/get modem bits
 */
int
commctl(
	struct tty	*tp,
	int		bits,
	int		how)
{
	spl_t		s;
	int		unit;
	vm_offset_t	dev_addr;
	int		b = 0;	/* Suppress gcc warning */

	unit = minor(tp->t_dev);

	if (bits == TM_HUP) { /* close line (internal) */
		bits = TM_DTR | TM_RTS;
		how = DMBIC;
	}

	if (how == DMGET) return commodem[unit];

	dev_addr = cominfo[unit]->address;

	s = spltty();

	switch (how) {
	case DMSET:
		b = bits; break;
	case DMBIS:
		b = commodem[unit] | bits; break;
	case DMBIC:
		b = commodem[unit] & ~bits; break;
	}
	commodem[unit] = b;

	if (bits & TM_BRK) {
		if (b & TM_BRK) {
			outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) | iSETBREAK);
		} else {
			outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) & ~iSETBREAK);
		}
	}

#if 0
	/* do I need to do something on this ? */
	if (bits & TM_LE) {	/* line enable */
	}
#endif
#if 0
	/* Unsupported */
	if (bits & TM_ST) {	/* secondary transmit */
	}
	if (bits & TM_SR) {	/* secondary receive */
	}
#endif
	if (bits & (TM_DTR|TM_RTS)) {	/* data terminal ready, request to send */
		how = iOUT2;
		if (b & TM_DTR) how |= iDTR;
		if (b & TM_RTS) how |= iRTS;
		outb(MODEM_CTL(dev_addr), how);
	}

	splx(s);

	/* the rest are inputs */
	return commodem[unit];
}

void
comstop(
	struct tty 	*tp,
	int		flags)
{
	if ((tp->t_state & TS_BUSY) && (tp->t_state & TS_TTSTOP) == 0)
	    tp->t_state |= TS_FLUSH;
}

/*
 *
 * Code to be called from debugger.
 *
 */
void compr_addr(vm_offset_t addr)
{
	/* The two line_stat prints may show different values, since
	*  touching some of the registers constitutes changing them.
	*/
	printf("LINE_STAT(%zu) %x\n",
		LINE_STAT(addr), inb(LINE_STAT(addr)));

	printf("TXRX(%zu) %x, INTR_ENAB(%zu) %x, INTR_ID(%zu) %x, LINE_CTL(%zu) %x,\n\
MODEM_CTL(%zu) %x, LINE_STAT(%zu) %x, MODEM_STAT(%zu) %x\n",
	TXRX(addr), 	 inb(TXRX(addr)),
	INTR_ENAB(addr), inb(INTR_ENAB(addr)),
	INTR_ID(addr), 	 inb(INTR_ID(addr)),
	LINE_CTL(addr),  inb(LINE_CTL(addr)),
	MODEM_CTL(addr), inb(MODEM_CTL(addr)),
	LINE_STAT(addr), inb(LINE_STAT(addr)),
	MODEM_STAT(addr),inb(MODEM_STAT(addr)));
}

int compr(int unit)
{
	compr_addr(cominfo[unit]->address);
	return(0);
}

int
comgetc(int unit)
{
	u_short	addr = (u_short)(cominfo[unit]->address);
	spl_t	s = spltty();
	int	c;

	while((inb(LINE_STAT(addr)) & iDR) == 0) ;

	c = inb(TXRX(addr));
	splx(s);
	return c;
}

/*
 * Routines for the console
 */
int
comcnputc(dev_t dev, int c)
{
	u_short addr = (u_short)(cominfo[minor(dev)]->address);

	/* Wait for transmitter to empty */
	while((inb(LINE_STAT(addr)) & iTHRE) == 0)
		continue;

	/* send the char */
	if (c == '\n')
		comcnputc(dev, '\r');
	outb(addr, c);

	return 0;
}

int
comcngetc(dev_t dev, int wait)
{
	u_short	addr = (u_short)(cominfo[minor(dev)]->address);
	int	c;

	while((inb(LINE_STAT(addr)) & iDR) == 0)
		if (! wait)
			return 0;

	c = inb(TXRX(addr));
	return c & 0x7f;
}

#endif	/* NCOM */
