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