]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/gdb/gdb/ser-go32.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / gdb / gdb / ser-go32.c
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>
135 typedef 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
152 static int intrcnt;
153 static int cnts[NCNT];
154 static 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. */
176 unsigned char icu_oldmask;
177
178 /* Maximum of 8 interrupts (we don't handle the slave icu yet). */
179 #define NINTR   8
180
181 static 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   }
191 intrupts[NINTR];
192
193
194 static 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   }
212 ports[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
231 static int dos_open (struct serial *scb, const char *name);
232 static void dos_raw (struct serial *scb);
233 static int dos_readchar (struct serial *scb, int timeout);
234 static int dos_setbaudrate (struct serial *scb, int rate);
235 static int dos_write (struct serial *scb, const char *str, int len);
236 static void dos_close (struct serial *scb);
237 static serial_ttystate dos_get_tty_state (struct serial *scb);
238 static int dos_set_tty_state (struct serial *scb, serial_ttystate state);
239 static 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
247 static int
248 dos_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
264 static int
265 dos_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 \f
274
275
276 static void
277 dos_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
359 ISR (0) ISR (1) ISR (2) ISR (3) /* OK */
360 ISR (4) ISR (5) ISR (6) ISR (7) /* OK */
361
362 typedef void (*isr_t) (void);
363
364 static 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 \f
370
371
372 static struct intrupt *
373 dos_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
428 static void
429 dos_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 \f
453
454
455 static int
456 dos_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
503 ok:
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
560 static void
561 dos_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 \f
600
601
602 static int
603 dos_noop (struct serial *scb)
604 {
605   return 0;
606 }
607
608 static void
609 dos_raw (struct serial *scb)
610 {
611   /* Always in raw mode */
612 }
613
614 static int
615 dos_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
632 static serial_ttystate
633 dos_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
656 static int
657 dos_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
666 static int
667 dos_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
677 static int
678 dos_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
689 static void
690 dos_print_tty_state (struct serial *scb, serial_ttystate ttystate,
691                      struct ui_file *stream)
692 {
693   /* Nothing to print */
694   return;
695 }
696
697 static int
698 dos_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
720 static int
721 dos_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
752 static int
753 dos_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
779 static int
780 dos_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
820 static int
821 dos_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
840 static 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
863 static void
864 dos_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
893 void
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 }