1130812Smarcel/* Remote serial interface for local (hardwired) serial ports for GO32.
2130812Smarcel   Copyright 1992, 1993, 2000, 2001 Free Software Foundation, Inc.
3130812Smarcel
4130812Smarcel   Contributed by Nigel Stephens, Algorithmics Ltd. (nigel@algor.co.uk).
5130812Smarcel
6130812Smarcel   This version uses DPMI interrupts to handle buffered i/o
7130812Smarcel   without the separate "asynctsr" program.
8130812Smarcel
9130812Smarcel   This file is part of GDB.
10130812Smarcel
11130812Smarcel   This program is free software; you can redistribute it and/or modify
12130812Smarcel   it under the terms of the GNU General Public License as published by
13130812Smarcel   the Free Software Foundation; either version 2 of the License, or
14130812Smarcel   (at your option) any later version.
15130812Smarcel
16130812Smarcel   This program is distributed in the hope that it will be useful,
17130812Smarcel   but WITHOUT ANY WARRANTY; without even the implied warranty of
18130812Smarcel   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19130812Smarcel   GNU General Public License for more details.
20130812Smarcel
21130812Smarcel   You should have received a copy of the GNU General Public License
22130812Smarcel   along with this program; if not, write to the Free Software
23130812Smarcel   Foundation, Inc., 59 Temple Place - Suite 330,
24130812Smarcel   Boston, MA 02111-1307, USA.  */
25130812Smarcel
26130812Smarcel#include "defs.h"
27130812Smarcel#include "gdbcmd.h"
28130812Smarcel#include "serial.h"
29130812Smarcel#include "gdb_string.h"
30130812Smarcel
31130812Smarcel
32130812Smarcel/*
33130812Smarcel * NS16550 UART registers
34130812Smarcel */
35130812Smarcel
36130812Smarcel#define COM1ADDR	0x3f8
37130812Smarcel#define COM2ADDR	0x2f8
38130812Smarcel#define COM3ADDR	0x3e8
39130812Smarcel#define COM4ADDR	0x3e0
40130812Smarcel
41130812Smarcel#define	com_data	0	/* data register (R/W) */
42130812Smarcel#define	com_dlbl	0	/* divisor latch low (W) */
43130812Smarcel#define	com_ier		1	/* interrupt enable (W) */
44130812Smarcel#define	com_dlbh	1	/* divisor latch high (W) */
45130812Smarcel#define	com_iir		2	/* interrupt identification (R) */
46130812Smarcel#define	com_fifo	2	/* FIFO control (W) */
47130812Smarcel#define	com_lctl	3	/* line control register (R/W) */
48130812Smarcel#define	com_cfcr	3	/* line control register (R/W) */
49130812Smarcel#define	com_mcr		4	/* modem control register (R/W) */
50130812Smarcel#define	com_lsr		5	/* line status register (R/W) */
51130812Smarcel#define	com_msr		6	/* modem status register (R/W) */
52130812Smarcel
53130812Smarcel/*
54130812Smarcel * Constants for computing 16 bit baud rate divisor (lower byte
55130812Smarcel * in com_dlbl, upper in com_dlbh) from 1.8432MHz crystal.  Divisor is
56130812Smarcel * 1.8432 MHz / (16 * X) for X bps.  If the baud rate can't be set
57130812Smarcel * to within +- (desired_rate*SPEED_TOLERANCE/1000) bps, we fail.
58130812Smarcel */
59130812Smarcel#define COMTICK		(1843200/16)
60130812Smarcel#define SPEED_TOLERANCE	30	/* thousandths; real == desired +- 3.0% */
61130812Smarcel
62130812Smarcel/* interrupt enable register */
63130812Smarcel#define	IER_ERXRDY	0x1	/* int on rx ready */
64130812Smarcel#define	IER_ETXRDY	0x2	/* int on tx ready */
65130812Smarcel#define	IER_ERLS	0x4	/* int on line status change */
66130812Smarcel#define	IER_EMSC	0x8	/* int on modem status change */
67130812Smarcel
68130812Smarcel/* interrupt identification register */
69130812Smarcel#define	IIR_FIFO_MASK	0xc0	/* set if FIFOs are enabled */
70130812Smarcel#define	IIR_IMASK	0xf	/* interrupt cause mask */
71130812Smarcel#define	IIR_NOPEND	0x1	/* nothing pending */
72130812Smarcel#define	IIR_RLS		0x6	/* receive line status */
73130812Smarcel#define	IIR_RXRDY	0x4	/* receive ready */
74130812Smarcel#define	IIR_RXTOUT	0xc	/* receive timeout */
75130812Smarcel#define	IIR_TXRDY	0x2	/* transmit ready */
76130812Smarcel#define	IIR_MLSC	0x0	/* modem status */
77130812Smarcel
78130812Smarcel
79130812Smarcel/* fifo control register */
80130812Smarcel#define	FIFO_ENABLE	0x01	/* enable fifo */
81130812Smarcel#define	FIFO_RCV_RST	0x02	/* reset receive fifo */
82130812Smarcel#define	FIFO_XMT_RST	0x04	/* reset transmit fifo */
83130812Smarcel#define	FIFO_DMA_MODE	0x08	/* enable dma mode */
84130812Smarcel#define	FIFO_TRIGGER_1	0x00	/* trigger at 1 char */
85130812Smarcel#define	FIFO_TRIGGER_4	0x40	/* trigger at 4 chars */
86130812Smarcel#define	FIFO_TRIGGER_8	0x80	/* trigger at 8 chars */
87130812Smarcel#define	FIFO_TRIGGER_14	0xc0	/* trigger at 14 chars */
88130812Smarcel
89130812Smarcel/* character format control register */
90130812Smarcel#define	CFCR_DLAB	0x80	/* divisor latch */
91130812Smarcel#define	CFCR_SBREAK	0x40	/* send break */
92130812Smarcel#define	CFCR_PZERO	0x30	/* zero parity */
93130812Smarcel#define	CFCR_PONE	0x20	/* one parity */
94130812Smarcel#define	CFCR_PEVEN	0x10	/* even parity */
95130812Smarcel#define	CFCR_PODD	0x00	/* odd parity */
96130812Smarcel#define	CFCR_PENAB	0x08	/* parity enable */
97130812Smarcel#define	CFCR_STOPB	0x04	/* 2 stop bits */
98130812Smarcel#define	CFCR_8BITS	0x03	/* 8 data bits */
99130812Smarcel#define	CFCR_7BITS	0x02	/* 7 data bits */
100130812Smarcel#define	CFCR_6BITS	0x01	/* 6 data bits */
101130812Smarcel#define	CFCR_5BITS	0x00	/* 5 data bits */
102130812Smarcel
103130812Smarcel/* modem control register */
104130812Smarcel#define	MCR_LOOPBACK	0x10	/* loopback */
105130812Smarcel#define	MCR_IENABLE	0x08	/* output 2 = int enable */
106130812Smarcel#define	MCR_DRS		0x04	/* output 1 = xxx */
107130812Smarcel#define	MCR_RTS		0x02	/* enable RTS */
108130812Smarcel#define	MCR_DTR		0x01	/* enable DTR */
109130812Smarcel
110130812Smarcel/* line status register */
111130812Smarcel#define	LSR_RCV_FIFO	0x80	/* error in receive fifo */
112130812Smarcel#define	LSR_TSRE	0x40	/* transmitter empty */
113130812Smarcel#define	LSR_TXRDY	0x20	/* transmitter ready */
114130812Smarcel#define	LSR_BI		0x10	/* break detected */
115130812Smarcel#define	LSR_FE		0x08	/* framing error */
116130812Smarcel#define	LSR_PE		0x04	/* parity error */
117130812Smarcel#define	LSR_OE		0x02	/* overrun error */
118130812Smarcel#define	LSR_RXRDY	0x01	/* receiver ready */
119130812Smarcel#define	LSR_RCV_MASK	0x1f
120130812Smarcel
121130812Smarcel/* modem status register */
122130812Smarcel#define	MSR_DCD		0x80
123130812Smarcel#define	MSR_RI		0x40
124130812Smarcel#define	MSR_DSR		0x20
125130812Smarcel#define	MSR_CTS		0x10
126130812Smarcel#define	MSR_DDCD	0x08
127130812Smarcel#define	MSR_TERI	0x04
128130812Smarcel#define	MSR_DDSR	0x02
129130812Smarcel#define	MSR_DCTS	0x01
130130812Smarcel
131130812Smarcel#include <time.h>
132130812Smarcel#include <dos.h>
133130812Smarcel#include <go32.h>
134130812Smarcel#include <dpmi.h>
135130812Smarceltypedef unsigned long u_long;
136130812Smarcel
137130812Smarcel/* 16550 rx fifo trigger point */
138130812Smarcel#define FIFO_TRIGGER	FIFO_TRIGGER_4
139130812Smarcel
140130812Smarcel/* input buffer size */
141130812Smarcel#define CBSIZE	4096
142130812Smarcel
143130812Smarcel#define RAWHZ	18
144130812Smarcel
145130812Smarcel#ifdef DOS_STATS
146130812Smarcel#define CNT_RX		16
147130812Smarcel#define CNT_TX		17
148130812Smarcel#define CNT_STRAY	18
149130812Smarcel#define CNT_ORUN	19
150130812Smarcel#define NCNT		20
151130812Smarcel
152130812Smarcelstatic int intrcnt;
153130812Smarcelstatic int cnts[NCNT];
154130812Smarcelstatic char *cntnames[NCNT] =
155130812Smarcel{
156130812Smarcel  /* h/w interrupt counts. */
157130812Smarcel  "mlsc", "nopend", "txrdy", "?3",
158130812Smarcel  "rxrdy", "?5", "rls", "?7",
159130812Smarcel  "?8", "?9", "?a", "?b",
160130812Smarcel  "rxtout", "?d", "?e", "?f",
161130812Smarcel  /* s/w counts. */
162130812Smarcel  "rxcnt", "txcnt", "stray", "swoflo"
163130812Smarcel};
164130812Smarcel
165130812Smarcel#define COUNT(x) cnts[x]++
166130812Smarcel#else
167130812Smarcel#define COUNT(x)
168130812Smarcel#endif
169130812Smarcel
170130812Smarcel/* Main interrupt controller port addresses. */
171130812Smarcel#define ICU_BASE	0x20
172130812Smarcel#define ICU_OCW2	(ICU_BASE + 0)
173130812Smarcel#define ICU_MASK	(ICU_BASE + 1)
174130812Smarcel
175130812Smarcel/* Original interrupt controller mask register. */
176130812Smarcelunsigned char icu_oldmask;
177130812Smarcel
178130812Smarcel/* Maximum of 8 interrupts (we don't handle the slave icu yet). */
179130812Smarcel#define NINTR	8
180130812Smarcel
181130812Smarcelstatic struct intrupt
182130812Smarcel  {
183130812Smarcel    char inuse;
184130812Smarcel    struct dos_ttystate *port;
185130812Smarcel    _go32_dpmi_seginfo old_rmhandler;
186130812Smarcel    _go32_dpmi_seginfo old_pmhandler;
187130812Smarcel    _go32_dpmi_seginfo new_rmhandler;
188130812Smarcel    _go32_dpmi_seginfo new_pmhandler;
189130812Smarcel    _go32_dpmi_registers regs;
190130812Smarcel  }
191130812Smarcelintrupts[NINTR];
192130812Smarcel
193130812Smarcel
194130812Smarcelstatic struct dos_ttystate
195130812Smarcel  {
196130812Smarcel    int base;
197130812Smarcel    int irq;
198130812Smarcel    int refcnt;
199130812Smarcel    struct intrupt *intrupt;
200130812Smarcel    int fifo;
201130812Smarcel    int baudrate;
202130812Smarcel    unsigned char cbuf[CBSIZE];
203130812Smarcel    unsigned int first;
204130812Smarcel    unsigned int count;
205130812Smarcel    int txbusy;
206130812Smarcel    unsigned char old_mcr;
207130812Smarcel    int ferr;
208130812Smarcel    int perr;
209130812Smarcel    int oflo;
210130812Smarcel    int msr;
211130812Smarcel  }
212130812Smarcelports[4] =
213130812Smarcel{
214130812Smarcel  {
215130812Smarcel    COM1ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
216130812Smarcel  }
217130812Smarcel  ,
218130812Smarcel  {
219130812Smarcel    COM2ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
220130812Smarcel  }
221130812Smarcel  ,
222130812Smarcel  {
223130812Smarcel    COM3ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
224130812Smarcel  }
225130812Smarcel  ,
226130812Smarcel  {
227130812Smarcel    COM4ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
228130812Smarcel  }
229130812Smarcel};
230130812Smarcel
231130812Smarcelstatic int dos_open (struct serial *scb, const char *name);
232130812Smarcelstatic void dos_raw (struct serial *scb);
233130812Smarcelstatic int dos_readchar (struct serial *scb, int timeout);
234130812Smarcelstatic int dos_setbaudrate (struct serial *scb, int rate);
235130812Smarcelstatic int dos_write (struct serial *scb, const char *str, int len);
236130812Smarcelstatic void dos_close (struct serial *scb);
237130812Smarcelstatic serial_ttystate dos_get_tty_state (struct serial *scb);
238130812Smarcelstatic int dos_set_tty_state (struct serial *scb, serial_ttystate state);
239130812Smarcelstatic int dos_baudconv (int rate);
240130812Smarcel
241130812Smarcel#define inb(p,a)	inportb((p)->base + (a))
242130812Smarcel#define outb(p,a,v)	outportb((p)->base + (a), (v))
243130812Smarcel#define disable()	asm volatile ("cli");
244130812Smarcel#define enable()	asm volatile ("sti");
245130812Smarcel
246130812Smarcel
247130812Smarcelstatic int
248130812Smarceldos_getc (volatile struct dos_ttystate *port)
249130812Smarcel{
250130812Smarcel  int c;
251130812Smarcel
252130812Smarcel  if (port->count == 0)
253130812Smarcel    return -1;
254130812Smarcel
255130812Smarcel  c = port->cbuf[port->first];
256130812Smarcel  disable ();
257130812Smarcel  port->first = (port->first + 1) & (CBSIZE - 1);
258130812Smarcel  port->count--;
259130812Smarcel  enable ();
260130812Smarcel  return c;
261130812Smarcel}
262130812Smarcel
263130812Smarcel
264130812Smarcelstatic int
265130812Smarceldos_putc (int c, struct dos_ttystate *port)
266130812Smarcel{
267130812Smarcel  if (port->count >= CBSIZE - 1)
268130812Smarcel    return -1;
269130812Smarcel  port->cbuf[(port->first + port->count) & (CBSIZE - 1)] = c;
270130812Smarcel  port->count++;
271130812Smarcel  return 0;
272130812Smarcel}
273130812Smarcel
274130812Smarcel
275130812Smarcel
276130812Smarcelstatic void
277130812Smarceldos_comisr (int irq)
278130812Smarcel{
279130812Smarcel  struct dos_ttystate *port;
280130812Smarcel  unsigned char iir, lsr, c;
281130812Smarcel
282130812Smarcel  disable ();			/* Paranoia */
283130812Smarcel  outportb (ICU_OCW2, 0x20);	/* End-Of-Interrupt */
284130812Smarcel#ifdef DOS_STATS
285130812Smarcel  ++intrcnt;
286130812Smarcel#endif
287130812Smarcel
288130812Smarcel  port = intrupts[irq].port;
289130812Smarcel  if (!port)
290130812Smarcel    {
291130812Smarcel      COUNT (CNT_STRAY);
292130812Smarcel      return;			/* not open */
293130812Smarcel    }
294130812Smarcel
295130812Smarcel  while (1)
296130812Smarcel    {
297130812Smarcel      iir = inb (port, com_iir) & IIR_IMASK;
298130812Smarcel      switch (iir)
299130812Smarcel	{
300130812Smarcel
301130812Smarcel	case IIR_RLS:
302130812Smarcel	  lsr = inb (port, com_lsr);
303130812Smarcel	  goto rx;
304130812Smarcel
305130812Smarcel	case IIR_RXTOUT:
306130812Smarcel	case IIR_RXRDY:
307130812Smarcel	  lsr = 0;
308130812Smarcel
309130812Smarcel	rx:
310130812Smarcel	  do
311130812Smarcel	    {
312130812Smarcel	      c = inb (port, com_data);
313130812Smarcel	      if (lsr & (LSR_BI | LSR_FE | LSR_PE | LSR_OE))
314130812Smarcel		{
315130812Smarcel		  if (lsr & (LSR_BI | LSR_FE))
316130812Smarcel		    port->ferr++;
317130812Smarcel		  else if (lsr & LSR_PE)
318130812Smarcel		    port->perr++;
319130812Smarcel		  if (lsr & LSR_OE)
320130812Smarcel		    port->oflo++;
321130812Smarcel		}
322130812Smarcel
323130812Smarcel	      if (dos_putc (c, port) < 0)
324130812Smarcel		{
325130812Smarcel		  COUNT (CNT_ORUN);
326130812Smarcel		}
327130812Smarcel	      else
328130812Smarcel		{
329130812Smarcel		  COUNT (CNT_RX);
330130812Smarcel		}
331130812Smarcel	    }
332130812Smarcel	  while ((lsr = inb (port, com_lsr)) & LSR_RXRDY);
333130812Smarcel	  break;
334130812Smarcel
335130812Smarcel	case IIR_MLSC:
336130812Smarcel	  /* could be used to flowcontrol Tx */
337130812Smarcel	  port->msr = inb (port, com_msr);
338130812Smarcel	  break;
339130812Smarcel
340130812Smarcel	case IIR_TXRDY:
341130812Smarcel	  port->txbusy = 0;
342130812Smarcel	  break;
343130812Smarcel
344130812Smarcel	case IIR_NOPEND:
345130812Smarcel	  /* no more pending interrupts, all done */
346130812Smarcel	  return;
347130812Smarcel
348130812Smarcel	default:
349130812Smarcel	  /* unexpected interrupt, ignore */
350130812Smarcel	  break;
351130812Smarcel	}
352130812Smarcel      COUNT (iir);
353130812Smarcel    }
354130812Smarcel}
355130812Smarcel
356130812Smarcel#define ISRNAME(x) dos_comisr##x
357130812Smarcel#define ISR(x) static void ISRNAME(x)(void) {dos_comisr(x);}
358130812Smarcel
359130812SmarcelISR (0) ISR (1) ISR (2) ISR (3) /* OK */
360130812SmarcelISR (4) ISR (5) ISR (6) ISR (7) /* OK */
361130812Smarcel
362130812Smarceltypedef void (*isr_t) (void);
363130812Smarcel
364130812Smarcelstatic isr_t isrs[NINTR] =
365130812Smarcel  {
366130812Smarcel       ISRNAME (0), ISRNAME (1), ISRNAME (2), ISRNAME (3),
367130812Smarcel       ISRNAME (4), ISRNAME (5), ISRNAME (6), ISRNAME (7)
368130812Smarcel  };
369130812Smarcel
370130812Smarcel
371130812Smarcel
372130812Smarcelstatic struct intrupt *
373130812Smarceldos_hookirq (unsigned int irq)
374130812Smarcel{
375130812Smarcel  struct intrupt *intr;
376130812Smarcel  unsigned int vec;
377130812Smarcel  isr_t isr;
378130812Smarcel
379130812Smarcel  if (irq >= NINTR)
380130812Smarcel    return 0;
381130812Smarcel
382130812Smarcel  intr = &intrupts[irq];
383130812Smarcel  if (intr->inuse)
384130812Smarcel    return 0;
385130812Smarcel
386130812Smarcel  vec = 0x08 + irq;
387130812Smarcel  isr = isrs[irq];
388130812Smarcel
389130812Smarcel  /* setup real mode handler */
390130812Smarcel  _go32_dpmi_get_real_mode_interrupt_vector (vec, &intr->old_rmhandler);
391130812Smarcel
392130812Smarcel  intr->new_rmhandler.pm_selector = _go32_my_cs ();
393130812Smarcel  intr->new_rmhandler.pm_offset = (u_long) isr;
394130812Smarcel  if (_go32_dpmi_allocate_real_mode_callback_iret (&intr->new_rmhandler,
395130812Smarcel						   &intr->regs))
396130812Smarcel    {
397130812Smarcel      return 0;
398130812Smarcel    }
399130812Smarcel
400130812Smarcel  if (_go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->new_rmhandler))
401130812Smarcel    {
402130812Smarcel      return 0;
403130812Smarcel    }
404130812Smarcel
405130812Smarcel  /* setup protected mode handler */
406130812Smarcel  _go32_dpmi_get_protected_mode_interrupt_vector (vec, &intr->old_pmhandler);
407130812Smarcel
408130812Smarcel  intr->new_pmhandler.pm_selector = _go32_my_cs ();
409130812Smarcel  intr->new_pmhandler.pm_offset = (u_long) isr;
410130812Smarcel  _go32_dpmi_allocate_iret_wrapper (&intr->new_pmhandler);
411130812Smarcel
412130812Smarcel  if (_go32_dpmi_set_protected_mode_interrupt_vector (vec,
413130812Smarcel						      &intr->new_pmhandler))
414130812Smarcel    {
415130812Smarcel      return 0;
416130812Smarcel    }
417130812Smarcel
418130812Smarcel  /* setup interrupt controller mask */
419130812Smarcel  disable ();
420130812Smarcel  outportb (ICU_MASK, inportb (ICU_MASK) & ~(1 << irq));
421130812Smarcel  enable ();
422130812Smarcel
423130812Smarcel  intr->inuse = 1;
424130812Smarcel  return intr;
425130812Smarcel}
426130812Smarcel
427130812Smarcel
428130812Smarcelstatic void
429130812Smarceldos_unhookirq (struct intrupt *intr)
430130812Smarcel{
431130812Smarcel  unsigned int irq, vec;
432130812Smarcel  unsigned char mask;
433130812Smarcel
434130812Smarcel  irq = intr - intrupts;
435130812Smarcel  vec = 0x08 + irq;
436130812Smarcel
437130812Smarcel  /* restore old interrupt mask bit */
438130812Smarcel  mask = 1 << irq;
439130812Smarcel  disable ();
440130812Smarcel  outportb (ICU_MASK, inportb (ICU_MASK) | (mask & icu_oldmask));
441130812Smarcel  enable ();
442130812Smarcel
443130812Smarcel  /* remove real mode handler */
444130812Smarcel  _go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->old_rmhandler);
445130812Smarcel  _go32_dpmi_free_real_mode_callback (&intr->new_rmhandler);
446130812Smarcel
447130812Smarcel  /* remove protected mode handler */
448130812Smarcel  _go32_dpmi_set_protected_mode_interrupt_vector (vec, &intr->old_pmhandler);
449130812Smarcel  _go32_dpmi_free_iret_wrapper (&intr->new_pmhandler);
450130812Smarcel  intr->inuse = 0;
451130812Smarcel}
452130812Smarcel
453130812Smarcel
454130812Smarcel
455130812Smarcelstatic int
456130812Smarceldos_open (struct serial *scb, const char *name)
457130812Smarcel{
458130812Smarcel  struct dos_ttystate *port;
459130812Smarcel  int fd, i;
460130812Smarcel
461130812Smarcel  if (strncasecmp (name, "/dev/", 5) == 0)
462130812Smarcel    name += 5;
463130812Smarcel  else if (strncasecmp (name, "\\dev\\", 5) == 0)
464130812Smarcel    name += 5;
465130812Smarcel
466130812Smarcel  if (strlen (name) != 4 || strncasecmp (name, "com", 3) != 0)
467130812Smarcel    {
468130812Smarcel      errno = ENOENT;
469130812Smarcel      return -1;
470130812Smarcel    }
471130812Smarcel
472130812Smarcel  if (name[3] < '1' || name[3] > '4')
473130812Smarcel    {
474130812Smarcel      errno = ENOENT;
475130812Smarcel      return -1;
476130812Smarcel    }
477130812Smarcel
478130812Smarcel  /* FIXME: this is a Bad Idea (tm)!  One should *never* invent file
479130812Smarcel     handles, since they might be already used by other files/devices.
480130812Smarcel     The Right Way to do this is to create a real handle by dup()'ing
481130812Smarcel     some existing one.  */
482130812Smarcel  fd = name[3] - '1';
483130812Smarcel  port = &ports[fd];
484130812Smarcel  if (port->refcnt++ > 0)
485130812Smarcel    {
486130812Smarcel      /* Device already opened another user.  Just point at it. */
487130812Smarcel      scb->fd = fd;
488130812Smarcel      return 0;
489130812Smarcel    }
490130812Smarcel
491130812Smarcel  /* force access to ID reg */
492130812Smarcel  outb (port, com_cfcr, 0);
493130812Smarcel  outb (port, com_iir, 0);
494130812Smarcel  for (i = 0; i < 17; i++)
495130812Smarcel    {
496130812Smarcel      if ((inb (port, com_iir) & 0x38) == 0)
497130812Smarcel	goto ok;
498130812Smarcel      (void) inb (port, com_data);	/* clear recv */
499130812Smarcel    }
500130812Smarcel  errno = ENODEV;
501130812Smarcel  return -1;
502130812Smarcel
503130812Smarcelok:
504130812Smarcel  /* disable all interrupts in chip */
505130812Smarcel  outb (port, com_ier, 0);
506130812Smarcel
507130812Smarcel  /* tentatively enable 16550 fifo, and see if it responds */
508130812Smarcel  outb (port, com_fifo,
509130812Smarcel	FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER);
510130812Smarcel  sleep (1);
511130812Smarcel  port->fifo = ((inb (port, com_iir) & IIR_FIFO_MASK) == IIR_FIFO_MASK);
512130812Smarcel
513130812Smarcel  /* clear pending status reports. */
514130812Smarcel  (void) inb (port, com_lsr);
515130812Smarcel  (void) inb (port, com_msr);
516130812Smarcel
517130812Smarcel  /* enable external interrupt gate (to avoid floating IRQ) */
518130812Smarcel  outb (port, com_mcr, MCR_IENABLE);
519130812Smarcel
520130812Smarcel  /* hook up interrupt handler and initialise icu */
521130812Smarcel  port->intrupt = dos_hookirq (port->irq);
522130812Smarcel  if (!port->intrupt)
523130812Smarcel    {
524130812Smarcel      outb (port, com_mcr, 0);
525130812Smarcel      outb (port, com_fifo, 0);
526130812Smarcel      errno = ENODEV;
527130812Smarcel      return -1;
528130812Smarcel    }
529130812Smarcel
530130812Smarcel  disable ();
531130812Smarcel
532130812Smarcel  /* record port */
533130812Smarcel  port->intrupt->port = port;
534130812Smarcel  scb->fd = fd;
535130812Smarcel
536130812Smarcel  /* clear rx buffer, tx busy flag and overflow count */
537130812Smarcel  port->first = port->count = 0;
538130812Smarcel  port->txbusy = 0;
539130812Smarcel  port->oflo = 0;
540130812Smarcel
541130812Smarcel  /* set default baud rate and mode: 9600,8,n,1 */
542130812Smarcel  i = dos_baudconv (port->baudrate = 9600);
543130812Smarcel  outb (port, com_cfcr, CFCR_DLAB);
544130812Smarcel  outb (port, com_dlbl, i & 0xff);
545130812Smarcel  outb (port, com_dlbh, i >> 8);
546130812Smarcel  outb (port, com_cfcr, CFCR_8BITS);
547130812Smarcel
548130812Smarcel  /* enable all interrupts */
549130812Smarcel  outb (port, com_ier, IER_ETXRDY | IER_ERXRDY | IER_ERLS | IER_EMSC);
550130812Smarcel
551130812Smarcel  /* enable DTR & RTS */
552130812Smarcel  outb (port, com_mcr, MCR_DTR | MCR_RTS | MCR_IENABLE);
553130812Smarcel
554130812Smarcel  enable ();
555130812Smarcel
556130812Smarcel  return 0;
557130812Smarcel}
558130812Smarcel
559130812Smarcel
560130812Smarcelstatic void
561130812Smarceldos_close (struct serial *scb)
562130812Smarcel{
563130812Smarcel  struct dos_ttystate *port;
564130812Smarcel  struct intrupt *intrupt;
565130812Smarcel
566130812Smarcel  if (!scb)
567130812Smarcel    return;
568130812Smarcel
569130812Smarcel  port = &ports[scb->fd];
570130812Smarcel
571130812Smarcel  if (port->refcnt-- > 1)
572130812Smarcel    return;
573130812Smarcel
574130812Smarcel  if (!(intrupt = port->intrupt))
575130812Smarcel    return;
576130812Smarcel
577130812Smarcel  /* disable interrupts, fifo, flow control */
578130812Smarcel  disable ();
579130812Smarcel  port->intrupt = 0;
580130812Smarcel  intrupt->port = 0;
581130812Smarcel  outb (port, com_fifo, 0);
582130812Smarcel  outb (port, com_ier, 0);
583130812Smarcel  enable ();
584130812Smarcel
585130812Smarcel  /* unhook handler, and disable interrupt gate */
586130812Smarcel  dos_unhookirq (intrupt);
587130812Smarcel  outb (port, com_mcr, 0);
588130812Smarcel
589130812Smarcel  /* Check for overflow errors */
590130812Smarcel  if (port->oflo)
591130812Smarcel    {
592130812Smarcel      fprintf_unfiltered (gdb_stderr,
593130812Smarcel			  "Serial input overruns occurred.\n");
594130812Smarcel      fprintf_unfiltered (gdb_stderr, "This system %s handle %d baud.\n",
595130812Smarcel			  port->fifo ? "cannot" : "needs a 16550 to",
596130812Smarcel			  port->baudrate);
597130812Smarcel    }
598130812Smarcel}
599130812Smarcel
600130812Smarcel
601130812Smarcel
602130812Smarcelstatic int
603130812Smarceldos_noop (struct serial *scb)
604130812Smarcel{
605130812Smarcel  return 0;
606130812Smarcel}
607130812Smarcel
608130812Smarcelstatic void
609130812Smarceldos_raw (struct serial *scb)
610130812Smarcel{
611130812Smarcel  /* Always in raw mode */
612130812Smarcel}
613130812Smarcel
614130812Smarcelstatic int
615130812Smarceldos_readchar (struct serial *scb, int timeout)
616130812Smarcel{
617130812Smarcel  struct dos_ttystate *port = &ports[scb->fd];
618130812Smarcel  long then;
619130812Smarcel  int c;
620130812Smarcel
621130812Smarcel  then = rawclock () + (timeout * RAWHZ);
622130812Smarcel  while ((c = dos_getc (port)) < 0)
623130812Smarcel    {
624130812Smarcel      if (timeout >= 0 && (rawclock () - then) >= 0)
625130812Smarcel	return SERIAL_TIMEOUT;
626130812Smarcel    }
627130812Smarcel
628130812Smarcel  return c;
629130812Smarcel}
630130812Smarcel
631130812Smarcel
632130812Smarcelstatic serial_ttystate
633130812Smarceldos_get_tty_state (struct serial *scb)
634130812Smarcel{
635130812Smarcel  struct dos_ttystate *port = &ports[scb->fd];
636130812Smarcel  struct dos_ttystate *state;
637130812Smarcel
638130812Smarcel  /* Are they asking about a port we opened?  */
639130812Smarcel  if (port->refcnt <= 0)
640130812Smarcel    {
641130812Smarcel      /* We've never heard about this port.  We should fail this call,
642130812Smarcel	 unless they are asking about one of the 3 standard handles,
643130812Smarcel	 in which case we pretend the handle was open by us if it is
644130812Smarcel	 connected to a terminal device.  This is beacuse Unix
645130812Smarcel	 terminals use the serial interface, so GDB expects the
646130812Smarcel	 standard handles to go through here.  */
647130812Smarcel      if (scb->fd >= 3 || !isatty (scb->fd))
648130812Smarcel	return NULL;
649130812Smarcel    }
650130812Smarcel
651130812Smarcel  state = (struct dos_ttystate *) xmalloc (sizeof *state);
652130812Smarcel  *state = *port;
653130812Smarcel  return (serial_ttystate) state;
654130812Smarcel}
655130812Smarcel
656130812Smarcelstatic int
657130812Smarceldos_set_tty_state (struct serial *scb, serial_ttystate ttystate)
658130812Smarcel{
659130812Smarcel  struct dos_ttystate *state;
660130812Smarcel
661130812Smarcel  state = (struct dos_ttystate *) ttystate;
662130812Smarcel  dos_setbaudrate (scb, state->baudrate);
663130812Smarcel  return 0;
664130812Smarcel}
665130812Smarcel
666130812Smarcelstatic int
667130812Smarceldos_noflush_set_tty_state (struct serial *scb, serial_ttystate new_ttystate,
668130812Smarcel			   serial_ttystate old_ttystate)
669130812Smarcel{
670130812Smarcel  struct dos_ttystate *state;
671130812Smarcel
672130812Smarcel  state = (struct dos_ttystate *) new_ttystate;
673130812Smarcel  dos_setbaudrate (scb, state->baudrate);
674130812Smarcel  return 0;
675130812Smarcel}
676130812Smarcel
677130812Smarcelstatic int
678130812Smarceldos_flush_input (struct serial *scb)
679130812Smarcel{
680130812Smarcel  struct dos_ttystate *port = &ports[scb->fd];
681130812Smarcel  disable ();
682130812Smarcel  port->first = port->count = 0;
683130812Smarcel  if (port->fifo)
684130812Smarcel    outb (port, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_TRIGGER);
685130812Smarcel  enable ();
686130812Smarcel  return 0;
687130812Smarcel}
688130812Smarcel
689130812Smarcelstatic void
690130812Smarceldos_print_tty_state (struct serial *scb, serial_ttystate ttystate,
691130812Smarcel		     struct ui_file *stream)
692130812Smarcel{
693130812Smarcel  /* Nothing to print */
694130812Smarcel  return;
695130812Smarcel}
696130812Smarcel
697130812Smarcelstatic int
698130812Smarceldos_baudconv (int rate)
699130812Smarcel{
700130812Smarcel  long x, err;
701130812Smarcel
702130812Smarcel  if (rate <= 0)
703130812Smarcel    return -1;
704130812Smarcel
705130812Smarcel#define divrnd(n, q)	(((n) * 2 / (q) + 1) / 2) /* divide and round off */
706130812Smarcel  x = divrnd (COMTICK, rate);
707130812Smarcel  if (x <= 0)
708130812Smarcel    return -1;
709130812Smarcel
710130812Smarcel  err = divrnd (1000 * COMTICK, x * rate) - 1000;
711130812Smarcel  if (err < 0)
712130812Smarcel    err = -err;
713130812Smarcel  if (err > SPEED_TOLERANCE)
714130812Smarcel    return -1;
715130812Smarcel#undef divrnd
716130812Smarcel  return x;
717130812Smarcel}
718130812Smarcel
719130812Smarcel
720130812Smarcelstatic int
721130812Smarceldos_setbaudrate (struct serial *scb, int rate)
722130812Smarcel{
723130812Smarcel  struct dos_ttystate *port = &ports[scb->fd];
724130812Smarcel
725130812Smarcel  if (port->baudrate != rate)
726130812Smarcel    {
727130812Smarcel      int x;
728130812Smarcel      unsigned char cfcr;
729130812Smarcel
730130812Smarcel      x = dos_baudconv (rate);
731130812Smarcel      if (x <= 0)
732130812Smarcel	{
733130812Smarcel	  fprintf_unfiltered (gdb_stderr, "%d: impossible baudrate\n", rate);
734130812Smarcel	  errno = EINVAL;
735130812Smarcel	  return -1;
736130812Smarcel	}
737130812Smarcel
738130812Smarcel      disable ();
739130812Smarcel      cfcr = inb (port, com_cfcr);
740130812Smarcel
741130812Smarcel      outb (port, com_cfcr, CFCR_DLAB);
742130812Smarcel      outb (port, com_dlbl, x & 0xff);
743130812Smarcel      outb (port, com_dlbh, x >> 8);
744130812Smarcel      outb (port, com_cfcr, cfcr);
745130812Smarcel      port->baudrate = rate;
746130812Smarcel      enable ();
747130812Smarcel    }
748130812Smarcel
749130812Smarcel  return 0;
750130812Smarcel}
751130812Smarcel
752130812Smarcelstatic int
753130812Smarceldos_setstopbits (struct serial *scb, int num)
754130812Smarcel{
755130812Smarcel  struct dos_ttystate *port = &ports[scb->fd];
756130812Smarcel  unsigned char cfcr;
757130812Smarcel
758130812Smarcel  disable ();
759130812Smarcel  cfcr = inb (port, com_cfcr);
760130812Smarcel
761130812Smarcel  switch (num)
762130812Smarcel    {
763130812Smarcel    case SERIAL_1_STOPBITS:
764130812Smarcel      outb (port, com_cfcr, cfcr & ~CFCR_STOPB);
765130812Smarcel      break;
766130812Smarcel    case SERIAL_1_AND_A_HALF_STOPBITS:
767130812Smarcel    case SERIAL_2_STOPBITS:
768130812Smarcel      outb (port, com_cfcr, cfcr | CFCR_STOPB);
769130812Smarcel      break;
770130812Smarcel    default:
771130812Smarcel      enable ();
772130812Smarcel      return 1;
773130812Smarcel    }
774130812Smarcel  enable ();
775130812Smarcel
776130812Smarcel  return 0;
777130812Smarcel}
778130812Smarcel
779130812Smarcelstatic int
780130812Smarceldos_write (struct serial *scb, const char *str, int len)
781130812Smarcel{
782130812Smarcel  volatile struct dos_ttystate *port = &ports[scb->fd];
783130812Smarcel  int fifosize = port->fifo ? 16 : 1;
784130812Smarcel  long then;
785130812Smarcel  int cnt;
786130812Smarcel
787130812Smarcel  while (len > 0)
788130812Smarcel    {
789130812Smarcel      /* send the data, fifosize bytes at a time */
790130812Smarcel      cnt = fifosize > len ? len : fifosize;
791130812Smarcel      port->txbusy = 1;
792130812Smarcel      /* Francisco Pastor <fpastor.etra-id@etra.es> says OUTSB messes
793130812Smarcel	 up the communications with UARTs with FIFOs.  */
794130812Smarcel#ifdef UART_FIFO_WORKS
795130812Smarcel      outportsb (port->base + com_data, str, cnt);
796130812Smarcel      str += cnt;
797130812Smarcel      len -= cnt;
798130812Smarcel#else
799130812Smarcel      for ( ; cnt > 0; cnt--, len--)
800130812Smarcel	outportb (port->base + com_data, *str++);
801130812Smarcel#endif
802130812Smarcel#ifdef DOS_STATS
803130812Smarcel      cnts[CNT_TX] += cnt;
804130812Smarcel#endif
805130812Smarcel      /* wait for transmission to complete (max 1 sec) */
806130812Smarcel      then = rawclock () + RAWHZ;
807130812Smarcel      while (port->txbusy)
808130812Smarcel	{
809130812Smarcel	  if ((rawclock () - then) >= 0)
810130812Smarcel	    {
811130812Smarcel	      errno = EIO;
812130812Smarcel	      return SERIAL_ERROR;
813130812Smarcel	    }
814130812Smarcel	}
815130812Smarcel    }
816130812Smarcel  return 0;
817130812Smarcel}
818130812Smarcel
819130812Smarcel
820130812Smarcelstatic int
821130812Smarceldos_sendbreak (struct serial *scb)
822130812Smarcel{
823130812Smarcel  volatile struct dos_ttystate *port = &ports[scb->fd];
824130812Smarcel  unsigned char cfcr;
825130812Smarcel  long then;
826130812Smarcel
827130812Smarcel  cfcr = inb (port, com_cfcr);
828130812Smarcel  outb (port, com_cfcr, cfcr | CFCR_SBREAK);
829130812Smarcel
830130812Smarcel  /* 0.25 sec delay */
831130812Smarcel  then = rawclock () + RAWHZ / 4;
832130812Smarcel  while ((rawclock () - then) < 0)
833130812Smarcel    continue;
834130812Smarcel
835130812Smarcel  outb (port, com_cfcr, cfcr);
836130812Smarcel  return 0;
837130812Smarcel}
838130812Smarcel
839130812Smarcel
840130812Smarcelstatic struct serial_ops dos_ops =
841130812Smarcel{
842130812Smarcel  "hardwire",
843130812Smarcel  0,
844130812Smarcel  dos_open,
845130812Smarcel  dos_close,
846130812Smarcel  dos_readchar,
847130812Smarcel  dos_write,
848130812Smarcel  dos_noop,			/* flush output */
849130812Smarcel  dos_flush_input,
850130812Smarcel  dos_sendbreak,
851130812Smarcel  dos_raw,
852130812Smarcel  dos_get_tty_state,
853130812Smarcel  dos_set_tty_state,
854130812Smarcel  dos_print_tty_state,
855130812Smarcel  dos_noflush_set_tty_state,
856130812Smarcel  dos_setbaudrate,
857130812Smarcel  dos_setstopbits,
858130812Smarcel  dos_noop,			/* wait for output to drain */
859130812Smarcel  (void (*)(struct serial *, int))NULL	/* change into async mode */
860130812Smarcel};
861130812Smarcel
862130812Smarcel
863130812Smarcelstatic void
864130812Smarceldos_info (char *arg, int from_tty)
865130812Smarcel{
866130812Smarcel  struct dos_ttystate *port;
867130812Smarcel#ifdef DOS_STATS
868130812Smarcel  int i;
869130812Smarcel#endif
870130812Smarcel
871130812Smarcel  for (port = ports; port < &ports[4]; port++)
872130812Smarcel    {
873130812Smarcel      if (port->baudrate == 0)
874130812Smarcel	continue;
875130812Smarcel      printf_filtered ("Port:\tCOM%ld (%sactive)\n", (long)(port - ports) + 1,
876130812Smarcel		       port->intrupt ? "" : "not ");
877130812Smarcel      printf_filtered ("Addr:\t0x%03x (irq %d)\n", port->base, port->irq);
878130812Smarcel      printf_filtered ("16550:\t%s\n", port->fifo ? "yes" : "no");
879130812Smarcel      printf_filtered ("Speed:\t%d baud\n", port->baudrate);
880130812Smarcel      printf_filtered ("Errs:\tframing %d parity %d overflow %d\n\n",
881130812Smarcel		       port->ferr, port->perr, port->oflo);
882130812Smarcel    }
883130812Smarcel
884130812Smarcel#ifdef DOS_STATS
885130812Smarcel  printf_filtered ("\nTotal interrupts: %d\n", intrcnt);
886130812Smarcel  for (i = 0; i < NCNT; i++)
887130812Smarcel    if (cnts[i])
888130812Smarcel      printf_filtered ("%s:\t%d\n", cntnames[i], cnts[i]);
889130812Smarcel#endif
890130812Smarcel}
891130812Smarcel
892130812Smarcel
893130812Smarcelvoid
894130812Smarcel_initialize_ser_dos (void)
895130812Smarcel{
896130812Smarcel  serial_add_interface (&dos_ops);
897130812Smarcel
898130812Smarcel  /* Save original interrupt mask register. */
899130812Smarcel  icu_oldmask = inportb (ICU_MASK);
900130812Smarcel
901130812Smarcel  /* Mark fixed motherboard irqs as inuse. */
902130812Smarcel  intrupts[0].inuse =		/* timer tick */
903130812Smarcel    intrupts[1].inuse =		/* keyboard */
904130812Smarcel    intrupts[2].inuse = 1;	/* slave icu */
905130812Smarcel
906130812Smarcel  add_show_from_set (
907130812Smarcel		      add_set_cmd ("com1base", class_obscure, var_zinteger,
908130812Smarcel				   (char *) &ports[0].base,
909130812Smarcel				   "Set COM1 base i/o port address.",
910130812Smarcel				   &setlist),
911130812Smarcel		      &showlist);
912130812Smarcel
913130812Smarcel  add_show_from_set (
914130812Smarcel		      add_set_cmd ("com1irq", class_obscure, var_zinteger,
915130812Smarcel				   (char *) &ports[0].irq,
916130812Smarcel				   "Set COM1 interrupt request.",
917130812Smarcel				   &setlist),
918130812Smarcel		      &showlist);
919130812Smarcel
920130812Smarcel  add_show_from_set (
921130812Smarcel		      add_set_cmd ("com2base", class_obscure, var_zinteger,
922130812Smarcel				   (char *) &ports[1].base,
923130812Smarcel				   "Set COM2 base i/o port address.",
924130812Smarcel				   &setlist),
925130812Smarcel		      &showlist);
926130812Smarcel
927130812Smarcel  add_show_from_set (
928130812Smarcel		      add_set_cmd ("com2irq", class_obscure, var_zinteger,
929130812Smarcel				   (char *) &ports[1].irq,
930130812Smarcel				   "Set COM2 interrupt request.",
931130812Smarcel				   &setlist),
932130812Smarcel		      &showlist);
933130812Smarcel
934130812Smarcel  add_show_from_set (
935130812Smarcel		      add_set_cmd ("com3base", class_obscure, var_zinteger,
936130812Smarcel				   (char *) &ports[2].base,
937130812Smarcel				   "Set COM3 base i/o port address.",
938130812Smarcel				   &setlist),
939130812Smarcel		      &showlist);
940130812Smarcel
941130812Smarcel  add_show_from_set (
942130812Smarcel		      add_set_cmd ("com3irq", class_obscure, var_zinteger,
943130812Smarcel				   (char *) &ports[2].irq,
944130812Smarcel				   "Set COM3 interrupt request.",
945130812Smarcel				   &setlist),
946130812Smarcel		      &showlist);
947130812Smarcel
948130812Smarcel  add_show_from_set (
949130812Smarcel		      add_set_cmd ("com4base", class_obscure, var_zinteger,
950130812Smarcel				   (char *) &ports[3].base,
951130812Smarcel				   "Set COM4 base i/o port address.",
952130812Smarcel				   &setlist),
953130812Smarcel		      &showlist);
954130812Smarcel
955130812Smarcel  add_show_from_set (
956130812Smarcel		      add_set_cmd ("com4irq", class_obscure, var_zinteger,
957130812Smarcel				   (char *) &ports[3].irq,
958130812Smarcel				   "Set COM4 interrupt request.",
959130812Smarcel				   &setlist),
960130812Smarcel		      &showlist);
961130812Smarcel
962130812Smarcel  add_info ("serial", dos_info,
963130812Smarcel	    "Print DOS serial port status.");
964130812Smarcel}
965