1/* Remote serial interface for local (hardwired) serial ports for GO32.
2   Copyright 1992, 1993, 2000, 2001 Free Software Foundation, Inc.
3
4   Contributed by Nigel Stephens, Algorithmics Ltd. (nigel@algor.co.uk).
5
6   This version uses DPMI interrupts to handle buffered i/o
7   without the separate "asynctsr" program.
8
9   This file is part of GDB.
10
11   This program is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 2 of the License, or
14   (at your option) any later version.
15
16   This program is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20
21   You should have received a copy of the GNU General Public License
22   along with this program; if not, write to the Free Software
23   Foundation, Inc., 59 Temple Place - Suite 330,
24   Boston, MA 02111-1307, USA.  */
25
26#include "defs.h"
27#include "gdbcmd.h"
28#include "serial.h"
29#include "gdb_string.h"
30
31
32/*
33 * NS16550 UART registers
34 */
35
36#define COM1ADDR	0x3f8
37#define COM2ADDR	0x2f8
38#define COM3ADDR	0x3e8
39#define COM4ADDR	0x3e0
40
41#define	com_data	0	/* data register (R/W) */
42#define	com_dlbl	0	/* divisor latch low (W) */
43#define	com_ier		1	/* interrupt enable (W) */
44#define	com_dlbh	1	/* divisor latch high (W) */
45#define	com_iir		2	/* interrupt identification (R) */
46#define	com_fifo	2	/* FIFO control (W) */
47#define	com_lctl	3	/* line control register (R/W) */
48#define	com_cfcr	3	/* line control register (R/W) */
49#define	com_mcr		4	/* modem control register (R/W) */
50#define	com_lsr		5	/* line status register (R/W) */
51#define	com_msr		6	/* modem status register (R/W) */
52
53/*
54 * Constants for computing 16 bit baud rate divisor (lower byte
55 * in com_dlbl, upper in com_dlbh) from 1.8432MHz crystal.  Divisor is
56 * 1.8432 MHz / (16 * X) for X bps.  If the baud rate can't be set
57 * to within +- (desired_rate*SPEED_TOLERANCE/1000) bps, we fail.
58 */
59#define COMTICK		(1843200/16)
60#define SPEED_TOLERANCE	30	/* thousandths; real == desired +- 3.0% */
61
62/* interrupt enable register */
63#define	IER_ERXRDY	0x1	/* int on rx ready */
64#define	IER_ETXRDY	0x2	/* int on tx ready */
65#define	IER_ERLS	0x4	/* int on line status change */
66#define	IER_EMSC	0x8	/* int on modem status change */
67
68/* interrupt identification register */
69#define	IIR_FIFO_MASK	0xc0	/* set if FIFOs are enabled */
70#define	IIR_IMASK	0xf	/* interrupt cause mask */
71#define	IIR_NOPEND	0x1	/* nothing pending */
72#define	IIR_RLS		0x6	/* receive line status */
73#define	IIR_RXRDY	0x4	/* receive ready */
74#define	IIR_RXTOUT	0xc	/* receive timeout */
75#define	IIR_TXRDY	0x2	/* transmit ready */
76#define	IIR_MLSC	0x0	/* modem status */
77
78
79/* fifo control register */
80#define	FIFO_ENABLE	0x01	/* enable fifo */
81#define	FIFO_RCV_RST	0x02	/* reset receive fifo */
82#define	FIFO_XMT_RST	0x04	/* reset transmit fifo */
83#define	FIFO_DMA_MODE	0x08	/* enable dma mode */
84#define	FIFO_TRIGGER_1	0x00	/* trigger at 1 char */
85#define	FIFO_TRIGGER_4	0x40	/* trigger at 4 chars */
86#define	FIFO_TRIGGER_8	0x80	/* trigger at 8 chars */
87#define	FIFO_TRIGGER_14	0xc0	/* trigger at 14 chars */
88
89/* character format control register */
90#define	CFCR_DLAB	0x80	/* divisor latch */
91#define	CFCR_SBREAK	0x40	/* send break */
92#define	CFCR_PZERO	0x30	/* zero parity */
93#define	CFCR_PONE	0x20	/* one parity */
94#define	CFCR_PEVEN	0x10	/* even parity */
95#define	CFCR_PODD	0x00	/* odd parity */
96#define	CFCR_PENAB	0x08	/* parity enable */
97#define	CFCR_STOPB	0x04	/* 2 stop bits */
98#define	CFCR_8BITS	0x03	/* 8 data bits */
99#define	CFCR_7BITS	0x02	/* 7 data bits */
100#define	CFCR_6BITS	0x01	/* 6 data bits */
101#define	CFCR_5BITS	0x00	/* 5 data bits */
102
103/* modem control register */
104#define	MCR_LOOPBACK	0x10	/* loopback */
105#define	MCR_IENABLE	0x08	/* output 2 = int enable */
106#define	MCR_DRS		0x04	/* output 1 = xxx */
107#define	MCR_RTS		0x02	/* enable RTS */
108#define	MCR_DTR		0x01	/* enable DTR */
109
110/* line status register */
111#define	LSR_RCV_FIFO	0x80	/* error in receive fifo */
112#define	LSR_TSRE	0x40	/* transmitter empty */
113#define	LSR_TXRDY	0x20	/* transmitter ready */
114#define	LSR_BI		0x10	/* break detected */
115#define	LSR_FE		0x08	/* framing error */
116#define	LSR_PE		0x04	/* parity error */
117#define	LSR_OE		0x02	/* overrun error */
118#define	LSR_RXRDY	0x01	/* receiver ready */
119#define	LSR_RCV_MASK	0x1f
120
121/* modem status register */
122#define	MSR_DCD		0x80
123#define	MSR_RI		0x40
124#define	MSR_DSR		0x20
125#define	MSR_CTS		0x10
126#define	MSR_DDCD	0x08
127#define	MSR_TERI	0x04
128#define	MSR_DDSR	0x02
129#define	MSR_DCTS	0x01
130
131#include <time.h>
132#include <dos.h>
133#include <go32.h>
134#include <dpmi.h>
135typedef unsigned long u_long;
136
137/* 16550 rx fifo trigger point */
138#define FIFO_TRIGGER	FIFO_TRIGGER_4
139
140/* input buffer size */
141#define CBSIZE	4096
142
143#define RAWHZ	18
144
145#ifdef DOS_STATS
146#define CNT_RX		16
147#define CNT_TX		17
148#define CNT_STRAY	18
149#define CNT_ORUN	19
150#define NCNT		20
151
152static int intrcnt;
153static int cnts[NCNT];
154static char *cntnames[NCNT] =
155{
156  /* h/w interrupt counts. */
157  "mlsc", "nopend", "txrdy", "?3",
158  "rxrdy", "?5", "rls", "?7",
159  "?8", "?9", "?a", "?b",
160  "rxtout", "?d", "?e", "?f",
161  /* s/w counts. */
162  "rxcnt", "txcnt", "stray", "swoflo"
163};
164
165#define COUNT(x) cnts[x]++
166#else
167#define COUNT(x)
168#endif
169
170/* Main interrupt controller port addresses. */
171#define ICU_BASE	0x20
172#define ICU_OCW2	(ICU_BASE + 0)
173#define ICU_MASK	(ICU_BASE + 1)
174
175/* Original interrupt controller mask register. */
176unsigned char icu_oldmask;
177
178/* Maximum of 8 interrupts (we don't handle the slave icu yet). */
179#define NINTR	8
180
181static struct intrupt
182  {
183    char inuse;
184    struct dos_ttystate *port;
185    _go32_dpmi_seginfo old_rmhandler;
186    _go32_dpmi_seginfo old_pmhandler;
187    _go32_dpmi_seginfo new_rmhandler;
188    _go32_dpmi_seginfo new_pmhandler;
189    _go32_dpmi_registers regs;
190  }
191intrupts[NINTR];
192
193
194static struct dos_ttystate
195  {
196    int base;
197    int irq;
198    int refcnt;
199    struct intrupt *intrupt;
200    int fifo;
201    int baudrate;
202    unsigned char cbuf[CBSIZE];
203    unsigned int first;
204    unsigned int count;
205    int txbusy;
206    unsigned char old_mcr;
207    int ferr;
208    int perr;
209    int oflo;
210    int msr;
211  }
212ports[4] =
213{
214  {
215    COM1ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
216  }
217  ,
218  {
219    COM2ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
220  }
221  ,
222  {
223    COM3ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
224  }
225  ,
226  {
227    COM4ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0
228  }
229};
230
231static int dos_open (struct serial *scb, const char *name);
232static void dos_raw (struct serial *scb);
233static int dos_readchar (struct serial *scb, int timeout);
234static int dos_setbaudrate (struct serial *scb, int rate);
235static int dos_write (struct serial *scb, const char *str, int len);
236static void dos_close (struct serial *scb);
237static serial_ttystate dos_get_tty_state (struct serial *scb);
238static int dos_set_tty_state (struct serial *scb, serial_ttystate state);
239static int dos_baudconv (int rate);
240
241#define inb(p,a)	inportb((p)->base + (a))
242#define outb(p,a,v)	outportb((p)->base + (a), (v))
243#define disable()	asm volatile ("cli");
244#define enable()	asm volatile ("sti");
245
246
247static int
248dos_getc (volatile struct dos_ttystate *port)
249{
250  int c;
251
252  if (port->count == 0)
253    return -1;
254
255  c = port->cbuf[port->first];
256  disable ();
257  port->first = (port->first + 1) & (CBSIZE - 1);
258  port->count--;
259  enable ();
260  return c;
261}
262
263
264static int
265dos_putc (int c, struct dos_ttystate *port)
266{
267  if (port->count >= CBSIZE - 1)
268    return -1;
269  port->cbuf[(port->first + port->count) & (CBSIZE - 1)] = c;
270  port->count++;
271  return 0;
272}
273
274
275
276static void
277dos_comisr (int irq)
278{
279  struct dos_ttystate *port;
280  unsigned char iir, lsr, c;
281
282  disable ();			/* Paranoia */
283  outportb (ICU_OCW2, 0x20);	/* End-Of-Interrupt */
284#ifdef DOS_STATS
285  ++intrcnt;
286#endif
287
288  port = intrupts[irq].port;
289  if (!port)
290    {
291      COUNT (CNT_STRAY);
292      return;			/* not open */
293    }
294
295  while (1)
296    {
297      iir = inb (port, com_iir) & IIR_IMASK;
298      switch (iir)
299	{
300
301	case IIR_RLS:
302	  lsr = inb (port, com_lsr);
303	  goto rx;
304
305	case IIR_RXTOUT:
306	case IIR_RXRDY:
307	  lsr = 0;
308
309	rx:
310	  do
311	    {
312	      c = inb (port, com_data);
313	      if (lsr & (LSR_BI | LSR_FE | LSR_PE | LSR_OE))
314		{
315		  if (lsr & (LSR_BI | LSR_FE))
316		    port->ferr++;
317		  else if (lsr & LSR_PE)
318		    port->perr++;
319		  if (lsr & LSR_OE)
320		    port->oflo++;
321		}
322
323	      if (dos_putc (c, port) < 0)
324		{
325		  COUNT (CNT_ORUN);
326		}
327	      else
328		{
329		  COUNT (CNT_RX);
330		}
331	    }
332	  while ((lsr = inb (port, com_lsr)) & LSR_RXRDY);
333	  break;
334
335	case IIR_MLSC:
336	  /* could be used to flowcontrol Tx */
337	  port->msr = inb (port, com_msr);
338	  break;
339
340	case IIR_TXRDY:
341	  port->txbusy = 0;
342	  break;
343
344	case IIR_NOPEND:
345	  /* no more pending interrupts, all done */
346	  return;
347
348	default:
349	  /* unexpected interrupt, ignore */
350	  break;
351	}
352      COUNT (iir);
353    }
354}
355
356#define ISRNAME(x) dos_comisr##x
357#define ISR(x) static void ISRNAME(x)(void) {dos_comisr(x);}
358
359ISR (0) ISR (1) ISR (2) ISR (3) /* OK */
360ISR (4) ISR (5) ISR (6) ISR (7) /* OK */
361
362typedef void (*isr_t) (void);
363
364static isr_t isrs[NINTR] =
365  {
366       ISRNAME (0), ISRNAME (1), ISRNAME (2), ISRNAME (3),
367       ISRNAME (4), ISRNAME (5), ISRNAME (6), ISRNAME (7)
368  };
369
370
371
372static struct intrupt *
373dos_hookirq (unsigned int irq)
374{
375  struct intrupt *intr;
376  unsigned int vec;
377  isr_t isr;
378
379  if (irq >= NINTR)
380    return 0;
381
382  intr = &intrupts[irq];
383  if (intr->inuse)
384    return 0;
385
386  vec = 0x08 + irq;
387  isr = isrs[irq];
388
389  /* setup real mode handler */
390  _go32_dpmi_get_real_mode_interrupt_vector (vec, &intr->old_rmhandler);
391
392  intr->new_rmhandler.pm_selector = _go32_my_cs ();
393  intr->new_rmhandler.pm_offset = (u_long) isr;
394  if (_go32_dpmi_allocate_real_mode_callback_iret (&intr->new_rmhandler,
395						   &intr->regs))
396    {
397      return 0;
398    }
399
400  if (_go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->new_rmhandler))
401    {
402      return 0;
403    }
404
405  /* setup protected mode handler */
406  _go32_dpmi_get_protected_mode_interrupt_vector (vec, &intr->old_pmhandler);
407
408  intr->new_pmhandler.pm_selector = _go32_my_cs ();
409  intr->new_pmhandler.pm_offset = (u_long) isr;
410  _go32_dpmi_allocate_iret_wrapper (&intr->new_pmhandler);
411
412  if (_go32_dpmi_set_protected_mode_interrupt_vector (vec,
413						      &intr->new_pmhandler))
414    {
415      return 0;
416    }
417
418  /* setup interrupt controller mask */
419  disable ();
420  outportb (ICU_MASK, inportb (ICU_MASK) & ~(1 << irq));
421  enable ();
422
423  intr->inuse = 1;
424  return intr;
425}
426
427
428static void
429dos_unhookirq (struct intrupt *intr)
430{
431  unsigned int irq, vec;
432  unsigned char mask;
433
434  irq = intr - intrupts;
435  vec = 0x08 + irq;
436
437  /* restore old interrupt mask bit */
438  mask = 1 << irq;
439  disable ();
440  outportb (ICU_MASK, inportb (ICU_MASK) | (mask & icu_oldmask));
441  enable ();
442
443  /* remove real mode handler */
444  _go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->old_rmhandler);
445  _go32_dpmi_free_real_mode_callback (&intr->new_rmhandler);
446
447  /* remove protected mode handler */
448  _go32_dpmi_set_protected_mode_interrupt_vector (vec, &intr->old_pmhandler);
449  _go32_dpmi_free_iret_wrapper (&intr->new_pmhandler);
450  intr->inuse = 0;
451}
452
453
454
455static int
456dos_open (struct serial *scb, const char *name)
457{
458  struct dos_ttystate *port;
459  int fd, i;
460
461  if (strncasecmp (name, "/dev/", 5) == 0)
462    name += 5;
463  else if (strncasecmp (name, "\\dev\\", 5) == 0)
464    name += 5;
465
466  if (strlen (name) != 4 || strncasecmp (name, "com", 3) != 0)
467    {
468      errno = ENOENT;
469      return -1;
470    }
471
472  if (name[3] < '1' || name[3] > '4')
473    {
474      errno = ENOENT;
475      return -1;
476    }
477
478  /* FIXME: this is a Bad Idea (tm)!  One should *never* invent file
479     handles, since they might be already used by other files/devices.
480     The Right Way to do this is to create a real handle by dup()'ing
481     some existing one.  */
482  fd = name[3] - '1';
483  port = &ports[fd];
484  if (port->refcnt++ > 0)
485    {
486      /* Device already opened another user.  Just point at it. */
487      scb->fd = fd;
488      return 0;
489    }
490
491  /* force access to ID reg */
492  outb (port, com_cfcr, 0);
493  outb (port, com_iir, 0);
494  for (i = 0; i < 17; i++)
495    {
496      if ((inb (port, com_iir) & 0x38) == 0)
497	goto ok;
498      (void) inb (port, com_data);	/* clear recv */
499    }
500  errno = ENODEV;
501  return -1;
502
503ok:
504  /* disable all interrupts in chip */
505  outb (port, com_ier, 0);
506
507  /* tentatively enable 16550 fifo, and see if it responds */
508  outb (port, com_fifo,
509	FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER);
510  sleep (1);
511  port->fifo = ((inb (port, com_iir) & IIR_FIFO_MASK) == IIR_FIFO_MASK);
512
513  /* clear pending status reports. */
514  (void) inb (port, com_lsr);
515  (void) inb (port, com_msr);
516
517  /* enable external interrupt gate (to avoid floating IRQ) */
518  outb (port, com_mcr, MCR_IENABLE);
519
520  /* hook up interrupt handler and initialise icu */
521  port->intrupt = dos_hookirq (port->irq);
522  if (!port->intrupt)
523    {
524      outb (port, com_mcr, 0);
525      outb (port, com_fifo, 0);
526      errno = ENODEV;
527      return -1;
528    }
529
530  disable ();
531
532  /* record port */
533  port->intrupt->port = port;
534  scb->fd = fd;
535
536  /* clear rx buffer, tx busy flag and overflow count */
537  port->first = port->count = 0;
538  port->txbusy = 0;
539  port->oflo = 0;
540
541  /* set default baud rate and mode: 9600,8,n,1 */
542  i = dos_baudconv (port->baudrate = 9600);
543  outb (port, com_cfcr, CFCR_DLAB);
544  outb (port, com_dlbl, i & 0xff);
545  outb (port, com_dlbh, i >> 8);
546  outb (port, com_cfcr, CFCR_8BITS);
547
548  /* enable all interrupts */
549  outb (port, com_ier, IER_ETXRDY | IER_ERXRDY | IER_ERLS | IER_EMSC);
550
551  /* enable DTR & RTS */
552  outb (port, com_mcr, MCR_DTR | MCR_RTS | MCR_IENABLE);
553
554  enable ();
555
556  return 0;
557}
558
559
560static void
561dos_close (struct serial *scb)
562{
563  struct dos_ttystate *port;
564  struct intrupt *intrupt;
565
566  if (!scb)
567    return;
568
569  port = &ports[scb->fd];
570
571  if (port->refcnt-- > 1)
572    return;
573
574  if (!(intrupt = port->intrupt))
575    return;
576
577  /* disable interrupts, fifo, flow control */
578  disable ();
579  port->intrupt = 0;
580  intrupt->port = 0;
581  outb (port, com_fifo, 0);
582  outb (port, com_ier, 0);
583  enable ();
584
585  /* unhook handler, and disable interrupt gate */
586  dos_unhookirq (intrupt);
587  outb (port, com_mcr, 0);
588
589  /* Check for overflow errors */
590  if (port->oflo)
591    {
592      fprintf_unfiltered (gdb_stderr,
593			  "Serial input overruns occurred.\n");
594      fprintf_unfiltered (gdb_stderr, "This system %s handle %d baud.\n",
595			  port->fifo ? "cannot" : "needs a 16550 to",
596			  port->baudrate);
597    }
598}
599
600
601
602static int
603dos_noop (struct serial *scb)
604{
605  return 0;
606}
607
608static void
609dos_raw (struct serial *scb)
610{
611  /* Always in raw mode */
612}
613
614static int
615dos_readchar (struct serial *scb, int timeout)
616{
617  struct dos_ttystate *port = &ports[scb->fd];
618  long then;
619  int c;
620
621  then = rawclock () + (timeout * RAWHZ);
622  while ((c = dos_getc (port)) < 0)
623    {
624      if (timeout >= 0 && (rawclock () - then) >= 0)
625	return SERIAL_TIMEOUT;
626    }
627
628  return c;
629}
630
631
632static serial_ttystate
633dos_get_tty_state (struct serial *scb)
634{
635  struct dos_ttystate *port = &ports[scb->fd];
636  struct dos_ttystate *state;
637
638  /* Are they asking about a port we opened?  */
639  if (port->refcnt <= 0)
640    {
641      /* We've never heard about this port.  We should fail this call,
642	 unless they are asking about one of the 3 standard handles,
643	 in which case we pretend the handle was open by us if it is
644	 connected to a terminal device.  This is beacuse Unix
645	 terminals use the serial interface, so GDB expects the
646	 standard handles to go through here.  */
647      if (scb->fd >= 3 || !isatty (scb->fd))
648	return NULL;
649    }
650
651  state = (struct dos_ttystate *) xmalloc (sizeof *state);
652  *state = *port;
653  return (serial_ttystate) state;
654}
655
656static int
657dos_set_tty_state (struct serial *scb, serial_ttystate ttystate)
658{
659  struct dos_ttystate *state;
660
661  state = (struct dos_ttystate *) ttystate;
662  dos_setbaudrate (scb, state->baudrate);
663  return 0;
664}
665
666static int
667dos_noflush_set_tty_state (struct serial *scb, serial_ttystate new_ttystate,
668			   serial_ttystate old_ttystate)
669{
670  struct dos_ttystate *state;
671
672  state = (struct dos_ttystate *) new_ttystate;
673  dos_setbaudrate (scb, state->baudrate);
674  return 0;
675}
676
677static int
678dos_flush_input (struct serial *scb)
679{
680  struct dos_ttystate *port = &ports[scb->fd];
681  disable ();
682  port->first = port->count = 0;
683  if (port->fifo)
684    outb (port, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_TRIGGER);
685  enable ();
686  return 0;
687}
688
689static void
690dos_print_tty_state (struct serial *scb, serial_ttystate ttystate,
691		     struct ui_file *stream)
692{
693  /* Nothing to print */
694  return;
695}
696
697static int
698dos_baudconv (int rate)
699{
700  long x, err;
701
702  if (rate <= 0)
703    return -1;
704
705#define divrnd(n, q)	(((n) * 2 / (q) + 1) / 2) /* divide and round off */
706  x = divrnd (COMTICK, rate);
707  if (x <= 0)
708    return -1;
709
710  err = divrnd (1000 * COMTICK, x * rate) - 1000;
711  if (err < 0)
712    err = -err;
713  if (err > SPEED_TOLERANCE)
714    return -1;
715#undef divrnd
716  return x;
717}
718
719
720static int
721dos_setbaudrate (struct serial *scb, int rate)
722{
723  struct dos_ttystate *port = &ports[scb->fd];
724
725  if (port->baudrate != rate)
726    {
727      int x;
728      unsigned char cfcr;
729
730      x = dos_baudconv (rate);
731      if (x <= 0)
732	{
733	  fprintf_unfiltered (gdb_stderr, "%d: impossible baudrate\n", rate);
734	  errno = EINVAL;
735	  return -1;
736	}
737
738      disable ();
739      cfcr = inb (port, com_cfcr);
740
741      outb (port, com_cfcr, CFCR_DLAB);
742      outb (port, com_dlbl, x & 0xff);
743      outb (port, com_dlbh, x >> 8);
744      outb (port, com_cfcr, cfcr);
745      port->baudrate = rate;
746      enable ();
747    }
748
749  return 0;
750}
751
752static int
753dos_setstopbits (struct serial *scb, int num)
754{
755  struct dos_ttystate *port = &ports[scb->fd];
756  unsigned char cfcr;
757
758  disable ();
759  cfcr = inb (port, com_cfcr);
760
761  switch (num)
762    {
763    case SERIAL_1_STOPBITS:
764      outb (port, com_cfcr, cfcr & ~CFCR_STOPB);
765      break;
766    case SERIAL_1_AND_A_HALF_STOPBITS:
767    case SERIAL_2_STOPBITS:
768      outb (port, com_cfcr, cfcr | CFCR_STOPB);
769      break;
770    default:
771      enable ();
772      return 1;
773    }
774  enable ();
775
776  return 0;
777}
778
779static int
780dos_write (struct serial *scb, const char *str, int len)
781{
782  volatile struct dos_ttystate *port = &ports[scb->fd];
783  int fifosize = port->fifo ? 16 : 1;
784  long then;
785  int cnt;
786
787  while (len > 0)
788    {
789      /* send the data, fifosize bytes at a time */
790      cnt = fifosize > len ? len : fifosize;
791      port->txbusy = 1;
792      /* Francisco Pastor <fpastor.etra-id@etra.es> says OUTSB messes
793	 up the communications with UARTs with FIFOs.  */
794#ifdef UART_FIFO_WORKS
795      outportsb (port->base + com_data, str, cnt);
796      str += cnt;
797      len -= cnt;
798#else
799      for ( ; cnt > 0; cnt--, len--)
800	outportb (port->base + com_data, *str++);
801#endif
802#ifdef DOS_STATS
803      cnts[CNT_TX] += cnt;
804#endif
805      /* wait for transmission to complete (max 1 sec) */
806      then = rawclock () + RAWHZ;
807      while (port->txbusy)
808	{
809	  if ((rawclock () - then) >= 0)
810	    {
811	      errno = EIO;
812	      return SERIAL_ERROR;
813	    }
814	}
815    }
816  return 0;
817}
818
819
820static int
821dos_sendbreak (struct serial *scb)
822{
823  volatile struct dos_ttystate *port = &ports[scb->fd];
824  unsigned char cfcr;
825  long then;
826
827  cfcr = inb (port, com_cfcr);
828  outb (port, com_cfcr, cfcr | CFCR_SBREAK);
829
830  /* 0.25 sec delay */
831  then = rawclock () + RAWHZ / 4;
832  while ((rawclock () - then) < 0)
833    continue;
834
835  outb (port, com_cfcr, cfcr);
836  return 0;
837}
838
839
840static struct serial_ops dos_ops =
841{
842  "hardwire",
843  0,
844  dos_open,
845  dos_close,
846  dos_readchar,
847  dos_write,
848  dos_noop,			/* flush output */
849  dos_flush_input,
850  dos_sendbreak,
851  dos_raw,
852  dos_get_tty_state,
853  dos_set_tty_state,
854  dos_print_tty_state,
855  dos_noflush_set_tty_state,
856  dos_setbaudrate,
857  dos_setstopbits,
858  dos_noop,			/* wait for output to drain */
859  (void (*)(struct serial *, int))NULL	/* change into async mode */
860};
861
862
863static void
864dos_info (char *arg, int from_tty)
865{
866  struct dos_ttystate *port;
867#ifdef DOS_STATS
868  int i;
869#endif
870
871  for (port = ports; port < &ports[4]; port++)
872    {
873      if (port->baudrate == 0)
874	continue;
875      printf_filtered ("Port:\tCOM%ld (%sactive)\n", (long)(port - ports) + 1,
876		       port->intrupt ? "" : "not ");
877      printf_filtered ("Addr:\t0x%03x (irq %d)\n", port->base, port->irq);
878      printf_filtered ("16550:\t%s\n", port->fifo ? "yes" : "no");
879      printf_filtered ("Speed:\t%d baud\n", port->baudrate);
880      printf_filtered ("Errs:\tframing %d parity %d overflow %d\n\n",
881		       port->ferr, port->perr, port->oflo);
882    }
883
884#ifdef DOS_STATS
885  printf_filtered ("\nTotal interrupts: %d\n", intrcnt);
886  for (i = 0; i < NCNT; i++)
887    if (cnts[i])
888      printf_filtered ("%s:\t%d\n", cntnames[i], cnts[i]);
889#endif
890}
891
892
893void
894_initialize_ser_dos (void)
895{
896  serial_add_interface (&dos_ops);
897
898  /* Save original interrupt mask register. */
899  icu_oldmask = inportb (ICU_MASK);
900
901  /* Mark fixed motherboard irqs as inuse. */
902  intrupts[0].inuse =		/* timer tick */
903    intrupts[1].inuse =		/* keyboard */
904    intrupts[2].inuse = 1;	/* slave icu */
905
906  add_show_from_set (
907		      add_set_cmd ("com1base", class_obscure, var_zinteger,
908				   (char *) &ports[0].base,
909				   "Set COM1 base i/o port address.",
910				   &setlist),
911		      &showlist);
912
913  add_show_from_set (
914		      add_set_cmd ("com1irq", class_obscure, var_zinteger,
915				   (char *) &ports[0].irq,
916				   "Set COM1 interrupt request.",
917				   &setlist),
918		      &showlist);
919
920  add_show_from_set (
921		      add_set_cmd ("com2base", class_obscure, var_zinteger,
922				   (char *) &ports[1].base,
923				   "Set COM2 base i/o port address.",
924				   &setlist),
925		      &showlist);
926
927  add_show_from_set (
928		      add_set_cmd ("com2irq", class_obscure, var_zinteger,
929				   (char *) &ports[1].irq,
930				   "Set COM2 interrupt request.",
931				   &setlist),
932		      &showlist);
933
934  add_show_from_set (
935		      add_set_cmd ("com3base", class_obscure, var_zinteger,
936				   (char *) &ports[2].base,
937				   "Set COM3 base i/o port address.",
938				   &setlist),
939		      &showlist);
940
941  add_show_from_set (
942		      add_set_cmd ("com3irq", class_obscure, var_zinteger,
943				   (char *) &ports[2].irq,
944				   "Set COM3 interrupt request.",
945				   &setlist),
946		      &showlist);
947
948  add_show_from_set (
949		      add_set_cmd ("com4base", class_obscure, var_zinteger,
950				   (char *) &ports[3].base,
951				   "Set COM4 base i/o port address.",
952				   &setlist),
953		      &showlist);
954
955  add_show_from_set (
956		      add_set_cmd ("com4irq", class_obscure, var_zinteger,
957				   (char *) &ports[3].irq,
958				   "Set COM4 interrupt request.",
959				   &setlist),
960		      &showlist);
961
962  add_info ("serial", dos_info,
963	    "Print DOS serial port status.");
964}
965