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