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