| /* Remote serial interface for local (hardwired) serial ports for GO32. | 
 |    Copyright (C) 1992-2022 Free Software Foundation, Inc. | 
 |  | 
 |    Contributed by Nigel Stephens, Algorithmics Ltd. (nigel@algor.co.uk). | 
 |  | 
 |    This version uses DPMI interrupts to handle buffered i/o | 
 |    without the separate "asynctsr" program. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "gdbcmd.h" | 
 | #include "serial.h" | 
 | /* | 
 |  * NS16550 UART registers | 
 |  */ | 
 |  | 
 | #define COM1ADDR	0x3f8 | 
 | #define COM2ADDR	0x2f8 | 
 | #define COM3ADDR	0x3e8 | 
 | #define COM4ADDR	0x3e0 | 
 |  | 
 | #define	com_data	0	/* data register (R/W) */ | 
 | #define	com_dlbl	0	/* divisor latch low (W) */ | 
 | #define	com_ier		1	/* interrupt enable (W) */ | 
 | #define	com_dlbh	1	/* divisor latch high (W) */ | 
 | #define	com_iir		2	/* interrupt identification (R) */ | 
 | #define	com_fifo	2	/* FIFO control (W) */ | 
 | #define	com_lctl	3	/* line control register (R/W) */ | 
 | #define	com_cfcr	3	/* line control register (R/W) */ | 
 | #define	com_mcr		4	/* modem control register (R/W) */ | 
 | #define	com_lsr		5	/* line status register (R/W) */ | 
 | #define	com_msr		6	/* modem status register (R/W) */ | 
 |  | 
 | /* | 
 |  * Constants for computing 16 bit baud rate divisor (lower byte | 
 |  * in com_dlbl, upper in com_dlbh) from 1.8432MHz crystal.  Divisor is | 
 |  * 1.8432 MHz / (16 * X) for X bps.  If the baud rate can't be set | 
 |  * to within +- (desired_rate*SPEED_TOLERANCE/1000) bps, we fail. | 
 |  */ | 
 | #define COMTICK		(1843200/16) | 
 | #define SPEED_TOLERANCE	30	/* thousandths; real == desired +- 3.0% */ | 
 |  | 
 | /* interrupt enable register */ | 
 | #define	IER_ERXRDY	0x1	/* int on rx ready */ | 
 | #define	IER_ETXRDY	0x2	/* int on tx ready */ | 
 | #define	IER_ERLS	0x4	/* int on line status change */ | 
 | #define	IER_EMSC	0x8	/* int on modem status change */ | 
 |  | 
 | /* interrupt identification register */ | 
 | #define	IIR_FIFO_MASK	0xc0	/* set if FIFOs are enabled */ | 
 | #define	IIR_IMASK	0xf	/* interrupt cause mask */ | 
 | #define	IIR_NOPEND	0x1	/* nothing pending */ | 
 | #define	IIR_RLS		0x6	/* receive line status */ | 
 | #define	IIR_RXRDY	0x4	/* receive ready */ | 
 | #define	IIR_RXTOUT	0xc	/* receive timeout */ | 
 | #define	IIR_TXRDY	0x2	/* transmit ready */ | 
 | #define	IIR_MLSC	0x0	/* modem status */ | 
 |  | 
 |  | 
 | /* fifo control register */ | 
 | #define	FIFO_ENABLE	0x01	/* enable fifo */ | 
 | #define	FIFO_RCV_RST	0x02	/* reset receive fifo */ | 
 | #define	FIFO_XMT_RST	0x04	/* reset transmit fifo */ | 
 | #define	FIFO_DMA_MODE	0x08	/* enable dma mode */ | 
 | #define	FIFO_TRIGGER_1	0x00	/* trigger at 1 char */ | 
 | #define	FIFO_TRIGGER_4	0x40	/* trigger at 4 chars */ | 
 | #define	FIFO_TRIGGER_8	0x80	/* trigger at 8 chars */ | 
 | #define	FIFO_TRIGGER_14	0xc0	/* trigger at 14 chars */ | 
 |  | 
 | /* character format control register */ | 
 | #define	CFCR_DLAB	0x80	/* divisor latch */ | 
 | #define	CFCR_SBREAK	0x40	/* send break */ | 
 | #define	CFCR_PZERO	0x30	/* zero parity */ | 
 | #define	CFCR_PONE	0x20	/* one parity */ | 
 | #define	CFCR_PEVEN	0x10	/* even parity */ | 
 | #define	CFCR_PODD	0x00	/* odd parity */ | 
 | #define	CFCR_PENAB	0x08	/* parity enable */ | 
 | #define	CFCR_STOPB	0x04	/* 2 stop bits */ | 
 | #define	CFCR_8BITS	0x03	/* 8 data bits */ | 
 | #define	CFCR_7BITS	0x02	/* 7 data bits */ | 
 | #define	CFCR_6BITS	0x01	/* 6 data bits */ | 
 | #define	CFCR_5BITS	0x00	/* 5 data bits */ | 
 |  | 
 | /* modem control register */ | 
 | #define	MCR_LOOPBACK	0x10	/* loopback */ | 
 | #define	MCR_IENABLE	0x08	/* output 2 = int enable */ | 
 | #define	MCR_DRS		0x04	/* output 1 = xxx */ | 
 | #define	MCR_RTS		0x02	/* enable RTS */ | 
 | #define	MCR_DTR		0x01	/* enable DTR */ | 
 |  | 
 | /* line status register */ | 
 | #define	LSR_RCV_FIFO	0x80	/* error in receive fifo */ | 
 | #define	LSR_TSRE	0x40	/* transmitter empty */ | 
 | #define	LSR_TXRDY	0x20	/* transmitter ready */ | 
 | #define	LSR_BI		0x10	/* break detected */ | 
 | #define	LSR_FE		0x08	/* framing error */ | 
 | #define	LSR_PE		0x04	/* parity error */ | 
 | #define	LSR_OE		0x02	/* overrun error */ | 
 | #define	LSR_RXRDY	0x01	/* receiver ready */ | 
 | #define	LSR_RCV_MASK	0x1f | 
 |  | 
 | /* modem status register */ | 
 | #define	MSR_DCD		0x80 | 
 | #define	MSR_RI		0x40 | 
 | #define	MSR_DSR		0x20 | 
 | #define	MSR_CTS		0x10 | 
 | #define	MSR_DDCD	0x08 | 
 | #define	MSR_TERI	0x04 | 
 | #define	MSR_DDSR	0x02 | 
 | #define	MSR_DCTS	0x01 | 
 |  | 
 | #include <time.h> | 
 | #include <dos.h> | 
 | #include <go32.h> | 
 | #include <dpmi.h> | 
 | typedef unsigned long u_long; | 
 |  | 
 | /* 16550 rx fifo trigger point */ | 
 | #define FIFO_TRIGGER	FIFO_TRIGGER_4 | 
 |  | 
 | /* input buffer size */ | 
 | #define CBSIZE	4096 | 
 |  | 
 | #define RAWHZ	18 | 
 |  | 
 | #ifdef DOS_STATS | 
 | #define CNT_RX		16 | 
 | #define CNT_TX		17 | 
 | #define CNT_STRAY	18 | 
 | #define CNT_ORUN	19 | 
 | #define NCNT		20 | 
 |  | 
 | static int intrcnt; | 
 | static size_t cnts[NCNT]; | 
 | static char *cntnames[NCNT] = | 
 | { | 
 |   /* h/w interrupt counts.  */ | 
 |   "mlsc", "nopend", "txrdy", "?3", | 
 |   "rxrdy", "?5", "rls", "?7", | 
 |   "?8", "?9", "?a", "?b", | 
 |   "rxtout", "?d", "?e", "?f", | 
 |   /* s/w counts.  */ | 
 |   "rxcnt", "txcnt", "stray", "swoflo" | 
 | }; | 
 |  | 
 | #define COUNT(x) cnts[x]++ | 
 | #else | 
 | #define COUNT(x) | 
 | #endif | 
 |  | 
 | /* Main interrupt controller port addresses.  */ | 
 | #define ICU_BASE	0x20 | 
 | #define ICU_OCW2	(ICU_BASE + 0) | 
 | #define ICU_MASK	(ICU_BASE + 1) | 
 |  | 
 | /* Original interrupt controller mask register.  */ | 
 | unsigned char icu_oldmask; | 
 |  | 
 | /* Maximum of 8 interrupts (we don't handle the slave icu yet).  */ | 
 | #define NINTR	8 | 
 |  | 
 | static struct intrupt | 
 |   { | 
 |     char inuse; | 
 |     struct dos_ttystate *port; | 
 |     _go32_dpmi_seginfo old_rmhandler; | 
 |     _go32_dpmi_seginfo old_pmhandler; | 
 |     _go32_dpmi_seginfo new_rmhandler; | 
 |     _go32_dpmi_seginfo new_pmhandler; | 
 |     _go32_dpmi_registers regs; | 
 |   } | 
 | intrupts[NINTR]; | 
 |  | 
 |  | 
 | static struct dos_ttystate | 
 |   { | 
 |     int base; | 
 |     int irq; | 
 |     int refcnt; | 
 |     struct intrupt *intrupt; | 
 |     int fifo; | 
 |     int baudrate; | 
 |     unsigned char cbuf[CBSIZE]; | 
 |     unsigned int first; | 
 |     unsigned int count; | 
 |     int txbusy; | 
 |     unsigned char old_mcr; | 
 |     int ferr; | 
 |     int perr; | 
 |     int oflo; | 
 |     int msr; | 
 |   } | 
 | ports[4] = | 
 | { | 
 |   { | 
 |     COM1ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 | 
 |   } | 
 |   , | 
 |   { | 
 |     COM2ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 | 
 |   } | 
 |   , | 
 |   { | 
 |     COM3ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 | 
 |   } | 
 |   , | 
 |   { | 
 |     COM4ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 | 
 |   } | 
 | }; | 
 |  | 
 | static int dos_open (struct serial *scb, const char *name); | 
 | static void dos_raw (struct serial *scb); | 
 | static int dos_readchar (struct serial *scb, int timeout); | 
 | static int dos_setbaudrate (struct serial *scb, int rate); | 
 | static int dos_write (struct serial *scb, const void *buf, size_t count); | 
 | static void dos_close (struct serial *scb); | 
 | static serial_ttystate dos_get_tty_state (struct serial *scb); | 
 | static int dos_set_tty_state (struct serial *scb, serial_ttystate state); | 
 | static int dos_baudconv (int rate); | 
 |  | 
 | #define inb(p,a)	inportb((p)->base + (a)) | 
 | #define outb(p,a,v)	outportb((p)->base + (a), (v)) | 
 | #define disable()	asm volatile ("cli"); | 
 | #define enable()	asm volatile ("sti"); | 
 |  | 
 |  | 
 | static int | 
 | dos_getc (volatile struct dos_ttystate *port) | 
 | { | 
 |   int c; | 
 |  | 
 |   if (port->count == 0) | 
 |     return -1; | 
 |  | 
 |   c = port->cbuf[port->first]; | 
 |   disable (); | 
 |   port->first = (port->first + 1) & (CBSIZE - 1); | 
 |   port->count--; | 
 |   enable (); | 
 |   return c; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | dos_putc (int c, struct dos_ttystate *port) | 
 | { | 
 |   if (port->count >= CBSIZE - 1) | 
 |     return -1; | 
 |   port->cbuf[(port->first + port->count) & (CBSIZE - 1)] = c; | 
 |   port->count++; | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static void | 
 | dos_comisr (int irq) | 
 | { | 
 |   struct dos_ttystate *port; | 
 |   unsigned char iir, lsr, c; | 
 |  | 
 |   disable ();			/* Paranoia */ | 
 |   outportb (ICU_OCW2, 0x20);	/* End-Of-Interrupt */ | 
 | #ifdef DOS_STATS | 
 |   ++intrcnt; | 
 | #endif | 
 |  | 
 |   port = intrupts[irq].port; | 
 |   if (!port) | 
 |     { | 
 |       COUNT (CNT_STRAY); | 
 |       return;			/* not open */ | 
 |     } | 
 |  | 
 |   while (1) | 
 |     { | 
 |       iir = inb (port, com_iir) & IIR_IMASK; | 
 |       switch (iir) | 
 | 	{ | 
 |  | 
 | 	case IIR_RLS: | 
 | 	  lsr = inb (port, com_lsr); | 
 | 	  goto rx; | 
 |  | 
 | 	case IIR_RXTOUT: | 
 | 	case IIR_RXRDY: | 
 | 	  lsr = 0; | 
 |  | 
 | 	rx: | 
 | 	  do | 
 | 	    { | 
 | 	      c = inb (port, com_data); | 
 | 	      if (lsr & (LSR_BI | LSR_FE | LSR_PE | LSR_OE)) | 
 | 		{ | 
 | 		  if (lsr & (LSR_BI | LSR_FE)) | 
 | 		    port->ferr++; | 
 | 		  else if (lsr & LSR_PE) | 
 | 		    port->perr++; | 
 | 		  if (lsr & LSR_OE) | 
 | 		    port->oflo++; | 
 | 		} | 
 |  | 
 | 	      if (dos_putc (c, port) < 0) | 
 | 		{ | 
 | 		  COUNT (CNT_ORUN); | 
 | 		} | 
 | 	      else | 
 | 		{ | 
 | 		  COUNT (CNT_RX); | 
 | 		} | 
 | 	    } | 
 | 	  while ((lsr = inb (port, com_lsr)) & LSR_RXRDY); | 
 | 	  break; | 
 |  | 
 | 	case IIR_MLSC: | 
 | 	  /* could be used to flowcontrol Tx */ | 
 | 	  port->msr = inb (port, com_msr); | 
 | 	  break; | 
 |  | 
 | 	case IIR_TXRDY: | 
 | 	  port->txbusy = 0; | 
 | 	  break; | 
 |  | 
 | 	case IIR_NOPEND: | 
 | 	  /* No more pending interrupts, all done.  */ | 
 | 	  return; | 
 |  | 
 | 	default: | 
 | 	  /* Unexpected interrupt, ignore.  */ | 
 | 	  break; | 
 | 	} | 
 |       COUNT (iir); | 
 |     } | 
 | } | 
 |  | 
 | #define ISRNAME(x) dos_comisr##x | 
 | #define ISR(x) static void ISRNAME(x)(void) {dos_comisr(x);} | 
 |  | 
 | ISR (0) ISR (1) ISR (2) ISR (3) /* OK */ | 
 | ISR (4) ISR (5) ISR (6) ISR (7) /* OK */ | 
 |  | 
 | typedef void (*isr_t) (void); | 
 |  | 
 | static isr_t isrs[NINTR] = | 
 |   { | 
 |        ISRNAME (0), ISRNAME (1), ISRNAME (2), ISRNAME (3), | 
 |        ISRNAME (4), ISRNAME (5), ISRNAME (6), ISRNAME (7) | 
 |   }; | 
 |  | 
 |  | 
 |  | 
 | static struct intrupt * | 
 | dos_hookirq (unsigned int irq) | 
 | { | 
 |   struct intrupt *intr; | 
 |   unsigned int vec; | 
 |   isr_t isr; | 
 |  | 
 |   if (irq >= NINTR) | 
 |     return 0; | 
 |  | 
 |   intr = &intrupts[irq]; | 
 |   if (intr->inuse) | 
 |     return 0; | 
 |  | 
 |   vec = 0x08 + irq; | 
 |   isr = isrs[irq]; | 
 |  | 
 |   /* Setup real mode handler.  */ | 
 |   _go32_dpmi_get_real_mode_interrupt_vector (vec, &intr->old_rmhandler); | 
 |  | 
 |   intr->new_rmhandler.pm_selector = _go32_my_cs (); | 
 |   intr->new_rmhandler.pm_offset = (u_long) isr; | 
 |   if (_go32_dpmi_allocate_real_mode_callback_iret (&intr->new_rmhandler, | 
 | 						   &intr->regs)) | 
 |     { | 
 |       return 0; | 
 |     } | 
 |  | 
 |   if (_go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->new_rmhandler)) | 
 |     { | 
 |       return 0; | 
 |     } | 
 |  | 
 |   /* Setup protected mode handler.  */ | 
 |   _go32_dpmi_get_protected_mode_interrupt_vector (vec, &intr->old_pmhandler); | 
 |  | 
 |   intr->new_pmhandler.pm_selector = _go32_my_cs (); | 
 |   intr->new_pmhandler.pm_offset = (u_long) isr; | 
 |   _go32_dpmi_allocate_iret_wrapper (&intr->new_pmhandler); | 
 |  | 
 |   if (_go32_dpmi_set_protected_mode_interrupt_vector (vec, | 
 | 						      &intr->new_pmhandler)) | 
 |     { | 
 |       return 0; | 
 |     } | 
 |  | 
 |   /* Setup interrupt controller mask.  */ | 
 |   disable (); | 
 |   outportb (ICU_MASK, inportb (ICU_MASK) & ~(1 << irq)); | 
 |   enable (); | 
 |  | 
 |   intr->inuse = 1; | 
 |   return intr; | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | dos_unhookirq (struct intrupt *intr) | 
 | { | 
 |   unsigned int irq, vec; | 
 |   unsigned char mask; | 
 |  | 
 |   irq = intr - intrupts; | 
 |   vec = 0x08 + irq; | 
 |  | 
 |   /* Restore old interrupt mask bit.  */ | 
 |   mask = 1 << irq; | 
 |   disable (); | 
 |   outportb (ICU_MASK, inportb (ICU_MASK) | (mask & icu_oldmask)); | 
 |   enable (); | 
 |  | 
 |   /* Remove real mode handler.  */ | 
 |   _go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->old_rmhandler); | 
 |   _go32_dpmi_free_real_mode_callback (&intr->new_rmhandler); | 
 |  | 
 |   /* Remove protected mode handler.  */ | 
 |   _go32_dpmi_set_protected_mode_interrupt_vector (vec, &intr->old_pmhandler); | 
 |   _go32_dpmi_free_iret_wrapper (&intr->new_pmhandler); | 
 |   intr->inuse = 0; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static int | 
 | dos_open (struct serial *scb, const char *name) | 
 | { | 
 |   struct dos_ttystate *port; | 
 |   int fd, i; | 
 |  | 
 |   if (strncasecmp (name, "/dev/", 5) == 0) | 
 |     name += 5; | 
 |   else if (strncasecmp (name, "\\dev\\", 5) == 0) | 
 |     name += 5; | 
 |  | 
 |   if (strlen (name) != 4 || strncasecmp (name, "com", 3) != 0) | 
 |     { | 
 |       errno = ENOENT; | 
 |       return -1; | 
 |     } | 
 |  | 
 |   if (name[3] < '1' || name[3] > '4') | 
 |     { | 
 |       errno = ENOENT; | 
 |       return -1; | 
 |     } | 
 |  | 
 |   /* FIXME: this is a Bad Idea (tm)!  One should *never* invent file | 
 |      handles, since they might be already used by other files/devices. | 
 |      The Right Way to do this is to create a real handle by dup()'ing | 
 |      some existing one.  */ | 
 |   fd = name[3] - '1'; | 
 |   port = &ports[fd]; | 
 |   if (port->refcnt++ > 0) | 
 |     { | 
 |       /* Device already opened another user.  Just point at it.  */ | 
 |       scb->fd = fd; | 
 |       return 0; | 
 |     } | 
 |  | 
 |   /* Force access to ID reg.  */ | 
 |   outb (port, com_cfcr, 0); | 
 |   outb (port, com_iir, 0); | 
 |   for (i = 0; i < 17; i++) | 
 |     { | 
 |       if ((inb (port, com_iir) & 0x38) == 0) | 
 | 	goto ok; | 
 |       (void) inb (port, com_data);	/* clear recv */ | 
 |     } | 
 |   errno = ENODEV; | 
 |   return -1; | 
 |  | 
 | ok: | 
 |   /* Disable all interrupts in chip.  */ | 
 |   outb (port, com_ier, 0); | 
 |  | 
 |   /* Tentatively enable 16550 fifo, and see if it responds.  */ | 
 |   outb (port, com_fifo, | 
 | 	FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER); | 
 |   sleep (1); | 
 |   port->fifo = ((inb (port, com_iir) & IIR_FIFO_MASK) == IIR_FIFO_MASK); | 
 |  | 
 |   /* clear pending status reports.  */ | 
 |   (void) inb (port, com_lsr); | 
 |   (void) inb (port, com_msr); | 
 |  | 
 |   /* Enable external interrupt gate (to avoid floating IRQ).  */ | 
 |   outb (port, com_mcr, MCR_IENABLE); | 
 |  | 
 |   /* Hook up interrupt handler and initialise icu.  */ | 
 |   port->intrupt = dos_hookirq (port->irq); | 
 |   if (!port->intrupt) | 
 |     { | 
 |       outb (port, com_mcr, 0); | 
 |       outb (port, com_fifo, 0); | 
 |       errno = ENODEV; | 
 |       return -1; | 
 |     } | 
 |  | 
 |   disable (); | 
 |  | 
 |   /* record port */ | 
 |   port->intrupt->port = port; | 
 |   scb->fd = fd; | 
 |  | 
 |   /* Clear rx buffer, tx busy flag and overflow count.  */ | 
 |   port->first = port->count = 0; | 
 |   port->txbusy = 0; | 
 |   port->oflo = 0; | 
 |  | 
 |   /* Set default baud rate and mode: 9600,8,n,1 */ | 
 |   i = dos_baudconv (port->baudrate = 9600); | 
 |   outb (port, com_cfcr, CFCR_DLAB); | 
 |   outb (port, com_dlbl, i & 0xff); | 
 |   outb (port, com_dlbh, i >> 8); | 
 |   outb (port, com_cfcr, CFCR_8BITS); | 
 |  | 
 |   /* Enable all interrupts.  */ | 
 |   outb (port, com_ier, IER_ETXRDY | IER_ERXRDY | IER_ERLS | IER_EMSC); | 
 |  | 
 |   /* Enable DTR & RTS.  */ | 
 |   outb (port, com_mcr, MCR_DTR | MCR_RTS | MCR_IENABLE); | 
 |  | 
 |   enable (); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | dos_close (struct serial *scb) | 
 | { | 
 |   struct dos_ttystate *port; | 
 |   struct intrupt *intrupt; | 
 |  | 
 |   if (!scb) | 
 |     return; | 
 |  | 
 |   port = &ports[scb->fd]; | 
 |  | 
 |   if (port->refcnt-- > 1) | 
 |     return; | 
 |  | 
 |   if (!(intrupt = port->intrupt)) | 
 |     return; | 
 |  | 
 |   /* Disable interrupts, fifo, flow control.  */ | 
 |   disable (); | 
 |   port->intrupt = 0; | 
 |   intrupt->port = 0; | 
 |   outb (port, com_fifo, 0); | 
 |   outb (port, com_ier, 0); | 
 |   enable (); | 
 |  | 
 |   /* Unhook handler, and disable interrupt gate.  */ | 
 |   dos_unhookirq (intrupt); | 
 |   outb (port, com_mcr, 0); | 
 |  | 
 |   /* Check for overflow errors.  */ | 
 |   if (port->oflo) | 
 |     { | 
 |       gdb_printf (gdb_stderr, | 
 | 		  "Serial input overruns occurred.\n"); | 
 |       gdb_printf (gdb_stderr, "This system %s handle %d baud.\n", | 
 | 		  port->fifo ? "cannot" : "needs a 16550 to", | 
 | 		  port->baudrate); | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /* Implementation of the serial_ops flush_output method.  */ | 
 |  | 
 | static int | 
 | dos_flush_output (struct serial *scb) | 
 | { | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Implementation of the serial_ops setparity method.  */ | 
 |  | 
 | static int | 
 | dos_setparity (struct serial *scb, int parity) | 
 | { | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Implementation of the serial_ops drain_output method.  */ | 
 |  | 
 | static int | 
 | dos_drain_output (struct serial *scb) | 
 | { | 
 |   return 0; | 
 | } | 
 |  | 
 | static void | 
 | dos_raw (struct serial *scb) | 
 | { | 
 |   /* Always in raw mode.  */ | 
 | } | 
 |  | 
 | static int | 
 | dos_readchar (struct serial *scb, int timeout) | 
 | { | 
 |   struct dos_ttystate *port = &ports[scb->fd]; | 
 |   long then; | 
 |   int c; | 
 |  | 
 |   then = rawclock () + (timeout * RAWHZ); | 
 |   while ((c = dos_getc (port)) < 0) | 
 |     { | 
 |       QUIT; | 
 |  | 
 |       if (timeout >= 0 && (rawclock () - then) >= 0) | 
 | 	return SERIAL_TIMEOUT; | 
 |     } | 
 |  | 
 |   return c; | 
 | } | 
 |  | 
 |  | 
 | static serial_ttystate | 
 | dos_get_tty_state (struct serial *scb) | 
 | { | 
 |   struct dos_ttystate *port = &ports[scb->fd]; | 
 |   struct dos_ttystate *state; | 
 |  | 
 |   /* Are they asking about a port we opened?  */ | 
 |   if (port->refcnt <= 0) | 
 |     { | 
 |       /* We've never heard about this port.  We should fail this call, | 
 | 	 unless they are asking about one of the 3 standard handles, | 
 | 	 in which case we pretend the handle was open by us if it is | 
 | 	 connected to a terminal device.  This is because Unix | 
 | 	 terminals use the serial interface, so GDB expects the | 
 | 	 standard handles to go through here.  */ | 
 |       if (scb->fd >= 3 || !isatty (scb->fd)) | 
 | 	return NULL; | 
 |     } | 
 |  | 
 |   state = XNEW (struct dos_ttystate); | 
 |   *state = *port; | 
 |   return (serial_ttystate) state; | 
 | } | 
 |  | 
 | static serial_ttystate | 
 | dos_copy_tty_state (struct serial *scb, serial_ttystate ttystate) | 
 | { | 
 |   struct dos_ttystate *state; | 
 |  | 
 |   state = XNEW (struct dos_ttystate); | 
 |   *state = *(struct dos_ttystate *) ttystate; | 
 |  | 
 |   return (serial_ttystate) state; | 
 | } | 
 |  | 
 | static int | 
 | dos_set_tty_state (struct serial *scb, serial_ttystate ttystate) | 
 | { | 
 |   struct dos_ttystate *state; | 
 |  | 
 |   state = (struct dos_ttystate *) ttystate; | 
 |   dos_setbaudrate (scb, state->baudrate); | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | dos_flush_input (struct serial *scb) | 
 | { | 
 |   struct dos_ttystate *port = &ports[scb->fd]; | 
 |  | 
 |   disable (); | 
 |   port->first = port->count = 0; | 
 |   if (port->fifo) | 
 |     outb (port, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_TRIGGER); | 
 |   enable (); | 
 |   return 0; | 
 | } | 
 |  | 
 | static void | 
 | dos_print_tty_state (struct serial *scb, serial_ttystate ttystate, | 
 | 		     struct ui_file *stream) | 
 | { | 
 |   /* Nothing to print.  */ | 
 |   return; | 
 | } | 
 |  | 
 | static int | 
 | dos_baudconv (int rate) | 
 | { | 
 |   long x, err; | 
 |  | 
 |   if (rate <= 0) | 
 |     return -1; | 
 |  | 
 | #define divrnd(n, q)	(((n) * 2 / (q) + 1) / 2) /* Divide and round off.  */ | 
 |   x = divrnd (COMTICK, rate); | 
 |   if (x <= 0) | 
 |     return -1; | 
 |  | 
 |   err = divrnd (1000 * COMTICK, x * rate) - 1000; | 
 |   if (err < 0) | 
 |     err = -err; | 
 |   if (err > SPEED_TOLERANCE) | 
 |     return -1; | 
 | #undef divrnd | 
 |   return x; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | dos_setbaudrate (struct serial *scb, int rate) | 
 | { | 
 |   struct dos_ttystate *port = &ports[scb->fd]; | 
 |  | 
 |   if (port->baudrate != rate) | 
 |     { | 
 |       int x; | 
 |       unsigned char cfcr; | 
 |  | 
 |       x = dos_baudconv (rate); | 
 |       if (x <= 0) | 
 | 	{ | 
 | 	  gdb_printf (gdb_stderr, "%d: impossible baudrate\n", rate); | 
 | 	  errno = EINVAL; | 
 | 	  return -1; | 
 | 	} | 
 |  | 
 |       disable (); | 
 |       cfcr = inb (port, com_cfcr); | 
 |  | 
 |       outb (port, com_cfcr, CFCR_DLAB); | 
 |       outb (port, com_dlbl, x & 0xff); | 
 |       outb (port, com_dlbh, x >> 8); | 
 |       outb (port, com_cfcr, cfcr); | 
 |       port->baudrate = rate; | 
 |       enable (); | 
 |     } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | dos_setstopbits (struct serial *scb, int num) | 
 | { | 
 |   struct dos_ttystate *port = &ports[scb->fd]; | 
 |   unsigned char cfcr; | 
 |  | 
 |   disable (); | 
 |   cfcr = inb (port, com_cfcr); | 
 |  | 
 |   switch (num) | 
 |     { | 
 |     case SERIAL_1_STOPBITS: | 
 |       outb (port, com_cfcr, cfcr & ~CFCR_STOPB); | 
 |       break; | 
 |     case SERIAL_1_AND_A_HALF_STOPBITS: | 
 |     case SERIAL_2_STOPBITS: | 
 |       outb (port, com_cfcr, cfcr | CFCR_STOPB); | 
 |       break; | 
 |     default: | 
 |       enable (); | 
 |       return 1; | 
 |     } | 
 |   enable (); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | dos_write (struct serial *scb, const void *buf, size_t count) | 
 | { | 
 |   volatile struct dos_ttystate *port = &ports[scb->fd]; | 
 |   size_t fifosize = port->fifo ? 16 : 1; | 
 |   long then; | 
 |   size_t cnt; | 
 |   const char *str = (const char *) buf; | 
 |  | 
 |   while (count > 0) | 
 |     { | 
 |       QUIT; | 
 |  | 
 |       /* Send the data, fifosize bytes at a time.  */ | 
 |       cnt = fifosize > count ? count : fifosize; | 
 |       port->txbusy = 1; | 
 |       /* Francisco Pastor <fpastor.etra-id@etra.es> says OUTSB messes | 
 | 	 up the communications with UARTs with FIFOs.  */ | 
 | #ifdef UART_FIFO_WORKS | 
 |       outportsb (port->base + com_data, str, cnt); | 
 |       str += cnt; | 
 |       count -= cnt; | 
 | #else | 
 |       for ( ; cnt > 0; cnt--, count--) | 
 | 	outportb (port->base + com_data, *str++); | 
 | #endif | 
 | #ifdef DOS_STATS | 
 |       cnts[CNT_TX] += cnt; | 
 | #endif | 
 |       /* Wait for transmission to complete (max 1 sec).  */ | 
 |       then = rawclock () + RAWHZ; | 
 |       while (port->txbusy) | 
 | 	{ | 
 | 	  if ((rawclock () - then) >= 0) | 
 | 	    { | 
 | 	      errno = EIO; | 
 | 	      return SERIAL_ERROR; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | dos_sendbreak (struct serial *scb) | 
 | { | 
 |   volatile struct dos_ttystate *port = &ports[scb->fd]; | 
 |   unsigned char cfcr; | 
 |   long then; | 
 |  | 
 |   cfcr = inb (port, com_cfcr); | 
 |   outb (port, com_cfcr, cfcr | CFCR_SBREAK); | 
 |  | 
 |   /* 0.25 sec delay */ | 
 |   then = rawclock () + RAWHZ / 4; | 
 |   while ((rawclock () - then) < 0) | 
 |     continue; | 
 |  | 
 |   outb (port, com_cfcr, cfcr); | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 | static const struct serial_ops dos_ops = | 
 | { | 
 |   "hardwire", | 
 |   dos_open, | 
 |   dos_close, | 
 |   NULL,				/* fdopen, not implemented */ | 
 |   dos_readchar, | 
 |   dos_write, | 
 |   dos_flush_output, | 
 |   dos_flush_input, | 
 |   dos_sendbreak, | 
 |   dos_raw, | 
 |   dos_get_tty_state, | 
 |   dos_copy_tty_state, | 
 |   dos_set_tty_state, | 
 |   dos_print_tty_state, | 
 |   dos_setbaudrate, | 
 |   dos_setstopbits, | 
 |   dos_setparity, | 
 |   dos_drain_output, | 
 |   (void (*)(struct serial *, int))NULL	/* Change into async mode.  */ | 
 | }; | 
 |  | 
 | int | 
 | gdb_pipe (int pdes[2]) | 
 | { | 
 |   /* No support for pipes.  */ | 
 |   errno = ENOSYS; | 
 |   return -1; | 
 | } | 
 |  | 
 | static void | 
 | info_serial_command (const char *arg, int from_tty) | 
 | { | 
 |   struct dos_ttystate *port; | 
 | #ifdef DOS_STATS | 
 |   int i; | 
 | #endif | 
 |  | 
 |   for (port = ports; port < &ports[4]; port++) | 
 |     { | 
 |       if (port->baudrate == 0) | 
 | 	continue; | 
 |       gdb_printf ("Port:\tCOM%ld (%sactive)\n", (long)(port - ports) + 1, | 
 | 		  port->intrupt ? "" : "not "); | 
 |       gdb_printf ("Addr:\t0x%03x (irq %d)\n", port->base, port->irq); | 
 |       gdb_printf ("16550:\t%s\n", port->fifo ? "yes" : "no"); | 
 |       gdb_printf ("Speed:\t%d baud\n", port->baudrate); | 
 |       gdb_printf ("Errs:\tframing %d parity %d overflow %d\n\n", | 
 | 		  port->ferr, port->perr, port->oflo); | 
 |     } | 
 |  | 
 | #ifdef DOS_STATS | 
 |   gdb_printf ("\nTotal interrupts: %d\n", intrcnt); | 
 |   for (i = 0; i < NCNT; i++) | 
 |     if (cnts[i]) | 
 |       gdb_printf ("%s:\t%lu\n", cntnames[i], (unsigned long) cnts[i]); | 
 | #endif | 
 | } | 
 |  | 
 | void _initialize_ser_dos (); | 
 | void | 
 | _initialize_ser_dos () | 
 | { | 
 |   serial_add_interface (&dos_ops); | 
 |  | 
 |   /* Save original interrupt mask register.  */ | 
 |   icu_oldmask = inportb (ICU_MASK); | 
 |  | 
 |   /* Mark fixed motherboard irqs as inuse.  */ | 
 |   intrupts[0].inuse =		/* timer tick */ | 
 |     intrupts[1].inuse =		/* keyboard */ | 
 |     intrupts[2].inuse = 1;	/* slave icu */ | 
 |  | 
 |   add_setshow_zinteger_cmd ("com1base", class_obscure, &ports[0].base, _("\ | 
 | Set COM1 base i/o port address."), _("\ | 
 | Show COM1 base i/o port address."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_setshow_zinteger_cmd ("com1irq", class_obscure, &ports[0].irq, _("\ | 
 | Set COM1 interrupt request."), _("\ | 
 | Show COM1 interrupt request."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_setshow_zinteger_cmd ("com2base", class_obscure, &ports[1].base, _("\ | 
 | Set COM2 base i/o port address."), _("\ | 
 | Show COM2 base i/o port address."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_setshow_zinteger_cmd ("com2irq", class_obscure, &ports[1].irq, _("\ | 
 | Set COM2 interrupt request."), _("\ | 
 | Show COM2 interrupt request."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_setshow_zinteger_cmd ("com3base", class_obscure, &ports[2].base, _("\ | 
 | Set COM3 base i/o port address."), _("\ | 
 | Show COM3 base i/o port address."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_setshow_zinteger_cmd ("com3irq", class_obscure, &ports[2].irq, _("\ | 
 | Set COM3 interrupt request."), _("\ | 
 | Show COM3 interrupt request."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_setshow_zinteger_cmd ("com4base", class_obscure, &ports[3].base, _("\ | 
 | Set COM4 base i/o port address."), _("\ | 
 | Show COM4 base i/o port address."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_setshow_zinteger_cmd ("com4irq", class_obscure, &ports[3].irq, _("\ | 
 | Set COM4 interrupt request."), _("\ | 
 | Show COM4 interrupt request."), NULL, | 
 | 			    NULL, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 |  | 
 |   add_info ("serial", info_serial_command, | 
 | 	    _("Print DOS serial port status.")); | 
 | } |