1 /* 2 Copyright (C) 1996-1997 Id Software, Inc. 3 4 This program is free software; you can redistribute it and/or 5 modify it under the terms of the GNU General Public License 6 as published by the Free Software Foundation; either version 2 7 of the License, or (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 See the GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 // net_comx.c 21 22 #include <dos.h> 23 #include <dpmi.h> 24 25 #define NUM_COM_PORTS 2 26 27 #define ERR_TTY_LINE_STATUS -1 28 #define ERR_TTY_MODEM_STATUS -2 29 #define ERR_TTY_NODATA -3 30 31 #define QUEUESIZE 8192 32 #define QUEUEMASK (QUEUESIZE - 1) 33 34 typedef struct 35 { 36 volatile int head; 37 volatile int tail; 38 volatile byte data[QUEUESIZE]; 39 } queue; 40 41 #define FULL(q) (q.head == ((q.tail-1) & QUEUEMASK)) 42 #define EMPTY(q) (q.tail == q.head) 43 #define ENQUEUE(q,b) (q.data[q.head] = b, q.head = (q.head + 1) & QUEUEMASK) 44 #define DEQUEUE(q,b) (b = q.data[q.tail], q.tail = (q.tail + 1) & QUEUEMASK) 45 46 extern cvar_t config_com_port; 47 extern cvar_t config_com_irq; 48 extern cvar_t config_com_baud; 49 extern cvar_t config_com_modem; 50 extern cvar_t config_modem_dialtype; 51 extern cvar_t config_modem_clear; 52 extern cvar_t config_modem_init; 53 extern cvar_t config_modem_hangup; 54 55 extern int m_return_state; 56 extern int m_state; 57 extern qboolean m_return_onerror; 58 extern char m_return_reason[32]; 59 60 // 8250, 16550 definitions 61 #define TRANSMIT_HOLDING_REGISTER 0x00 62 #define RECEIVE_BUFFER_REGISTER 0x00 63 #define INTERRUPT_ENABLE_REGISTER 0x01 64 #define IER_RX_DATA_READY 0x01 65 #define IER_TX_HOLDING_REGISTER_EMPTY 0x02 66 #define IER_LINE_STATUS 0x04 67 #define IER_MODEM_STATUS 0x08 68 #define INTERRUPT_ID_REGISTER 0x02 69 #define IIR_MODEM_STATUS_INTERRUPT 0x00 70 #define IIR_TX_HOLDING_REGISTER_INTERRUPT 0x02 71 #define IIR_RX_DATA_READY_INTERRUPT 0x04 72 #define IIR_LINE_STATUS_INTERRUPT 0x06 73 #define IIR_FIFO_TIMEOUT 0x0c 74 #define IIR_FIFO_ENABLED 0xc0 75 #define FIFO_CONTROL_REGISTER 0x02 76 #define FCR_FIFO_ENABLE 0x01 77 #define FCR_RCVR_FIFO_RESET 0x02 78 #define FCR_XMIT_FIFO_RESET 0x04 79 #define FCR_TRIGGER_01 0x00 80 #define FCR_TRIGGER_04 0x40 81 #define FCR_TRIGGER_08 0x80 82 #define FCR_TRIGGER_16 0xc0 83 #define LINE_CONTROL_REGISTER 0x03 84 #define LCR_DATA_BITS_5 0x00 85 #define LCR_DATA_BITS_6 0x01 86 #define LCR_DATA_BITS_7 0x02 87 #define LCR_DATA_BITS_8 0x03 88 #define LCR_STOP_BITS_1 0x00 89 #define LCR_STOP_BITS_2 0x04 90 #define LCR_PARITY_NONE 0x00 91 #define LCR_PARITY_ODD 0x08 92 #define LCR_PARITY_EVEN 0x18 93 #define LCR_PARITY_MARK 0x28 94 #define LCR_PARITY_SPACE 0x38 95 #define LCR_SET_BREAK 0x40 96 #define LCR_DLAB 0x80 97 #define MODEM_CONTROL_REGISTER 0x04 98 #define MCR_DTR 0x01 99 #define MCR_RTS 0x02 100 #define MCR_OUT1 0x04 101 #define MCR_OUT2 0x08 102 #define MCR_LOOPBACK 0x10 103 #define LINE_STATUS_REGISTER 0x05 104 #define LSR_DATA_READY 0x01 105 #define LSR_OVERRUN_ERROR 0x02 106 #define LSR_PARITY_ERROR 0x04 107 #define LSR_FRAMING_ERROR 0x08 108 #define LSR_BREAK_DETECT 0x10 109 #define LSR_TRANSMITTER_BUFFER_EMPTY 0x20 110 #define LSR_TRANSMITTER_EMPTY 0x40 111 #define LSR_FIFO_DIRTY 0x80 112 #define MODEM_STATUS_REGISTER 0x06 113 #define MSR_DELTA_CTS 0x01 114 #define MSR_DELTA_DSR 0x02 115 #define MSR_DELTA_RI 0x04 116 #define MSR_DELTA_CD 0x08 117 #define MSR_CTS 0x10 118 #define MSR_DSR 0x20 119 #define MSR_RI 0x40 120 #define MSR_CD 0x80 121 #define DIVISOR_LATCH_LOW 0x00 122 #define DIVISOR_LATCH_HIGH 0x01 123 124 #define MODEM_STATUS_MASK (MSR_CTS | MSR_DSR | MSR_CD) 125 126 #define UART_AUTO 0 127 #define UART_8250 1 128 #define UART_16550 2 129 130 static int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8}; 131 static int ISA_IRQs[] = {4,3,4,3}; 132 133 typedef struct ComPort_s 134 { 135 struct ComPort_s *next; 136 _go32_dpmi_seginfo protectedModeInfo; 137 _go32_dpmi_seginfo protectedModeSaveInfo; 138 int uart; 139 volatile byte modemStatus; 140 byte modemStatusIgnore; 141 byte lineStatus; 142 byte bufferUsed; 143 qboolean enabled; 144 volatile qboolean statusUpdated; 145 qboolean useModem; 146 qboolean modemInitialized; 147 qboolean modemRang; 148 qboolean modemConnected; 149 queue inputQueue; 150 queue outputQueue; 151 char clear[16]; 152 char startup[32]; 153 char shutdown[16]; 154 char buffer[128]; 155 PollProcedure poll; 156 double timestamp; 157 byte uartType; 158 byte irq; 159 byte baudBits; 160 byte lineControl; 161 byte portNumber; 162 char dialType; 163 char name[4]; 164 } ComPort; 165 166 ComPort *portList = NULL; 167 ComPort *handleToPort [NUM_COM_PORTS]; 168 169 static int Modem_Command(ComPort *p, char *commandString); 170 static char *Modem_Response(ComPort *p); 171 static void Modem_Hangup(ComPort *p); 172 173 int TTY_Init(void); 174 void TTY_Shutdown(void); 175 int TTY_Open(int serialPortNumber); 176 void TTY_Close(int handle); 177 int TTY_ReadByte(int handle); 178 int TTY_WriteByte(int handle, byte data); 179 void TTY_Flush(int handle); 180 int TTY_Connect(int handle, char *host); 181 void TTY_Disconnect(int handle); 182 qboolean TTY_CheckForConnection(int handle); 183 qboolean TTY_IsEnabled(int serialPortNumber); 184 qboolean TTY_IsModem(int serialPortNumber); 185 qboolean TTY_OutputQueueIsEmpty(int handle); 186 187 static void ISR_8250 (ComPort *p) 188 { 189 byte source = 0; 190 byte b; 191 192 disable(); 193 194 while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) 195 { 196 switch (source) 197 { 198 case IIR_RX_DATA_READY_INTERRUPT: 199 b = inportb (p->uart + RECEIVE_BUFFER_REGISTER); 200 if (! FULL(p->inputQueue)) 201 { 202 ENQUEUE (p->inputQueue, b); 203 } 204 else 205 { 206 p->lineStatus |= LSR_OVERRUN_ERROR; 207 p->statusUpdated = true; 208 } 209 break; 210 211 case IIR_TX_HOLDING_REGISTER_INTERRUPT: 212 if (! EMPTY(p->outputQueue)) 213 { 214 DEQUEUE (p->outputQueue, b); 215 outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); 216 } 217 break; 218 219 case IIR_MODEM_STATUS_INTERRUPT: 220 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; 221 p->statusUpdated = true; 222 break; 223 224 case IIR_LINE_STATUS_INTERRUPT: 225 p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); 226 p->statusUpdated = true; 227 break; 228 } 229 source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07; 230 } 231 outportb (0x20, 0x20); 232 } 233 234 static void COM1_ISR_8250 (void) 235 { 236 ISR_8250 (handleToPort[0]); 237 } 238 239 static void COM2_ISR_8250 (void) 240 { 241 ISR_8250 (handleToPort[1]); 242 } 243 244 245 246 static void ISR_16550 (ComPort *p) 247 { 248 int count; 249 byte source; 250 byte b; 251 252 disable(); 253 while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) 254 { 255 switch (source) 256 { 257 case IIR_RX_DATA_READY_INTERRUPT: 258 do 259 { 260 b = inportb (p->uart + RECEIVE_BUFFER_REGISTER); 261 if (!FULL(p->inputQueue)) 262 { 263 ENQUEUE (p->inputQueue, b); 264 } 265 else 266 { 267 p->lineStatus |= LSR_OVERRUN_ERROR; 268 p->statusUpdated = true; 269 } 270 } while (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_DATA_READY); 271 break; 272 273 case IIR_TX_HOLDING_REGISTER_INTERRUPT: 274 count = 16; 275 while ((! EMPTY(p->outputQueue)) && count--) 276 { 277 DEQUEUE (p->outputQueue, b); 278 outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); 279 } 280 break; 281 282 case IIR_MODEM_STATUS_INTERRUPT: 283 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; 284 p->statusUpdated = true; 285 break; 286 287 case IIR_LINE_STATUS_INTERRUPT: 288 p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); 289 p->statusUpdated = true; 290 break; 291 } 292 source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07; 293 } 294 295 // check for lost IIR_TX_HOLDING_REGISTER_INTERRUPT on 16550a! 296 if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY) 297 { 298 count = 16; 299 while ((! EMPTY(p->outputQueue)) && count--) 300 { 301 DEQUEUE (p->outputQueue, b); 302 outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); 303 } 304 } 305 306 outportb (0x20, 0x20); 307 } 308 309 static void COM1_ISR_16550 (void) 310 { 311 ISR_16550 (handleToPort[0]); 312 } 313 314 static void COM2_ISR_16550 (void) 315 { 316 ISR_16550 (handleToPort[1]); 317 } 318 319 320 void TTY_GetComPortConfig (int portNumber, int *port, int *irq, int *baud, qboolean *useModem) 321 { 322 ComPort *p; 323 324 p = handleToPort[portNumber]; 325 *port = p->uart; 326 *irq = p->irq; 327 *baud = 115200 / p->baudBits; 328 *useModem = p->useModem; 329 } 330 331 void TTY_SetComPortConfig (int portNumber, int port, int irq, int baud, qboolean useModem) 332 { 333 ComPort *p; 334 float temp; 335 336 if (useModem) 337 { 338 if (baud == 14400) 339 baud = 19200; 340 if (baud == 28800) 341 baud = 38400; 342 } 343 344 p = handleToPort[portNumber]; 345 p->uart = port; 346 p->irq = irq; 347 p->baudBits = 115200 / baud; 348 p->useModem = useModem; 349 350 if (useModem) 351 temp = 1.0; 352 else 353 temp = 0.0; 354 355 Cvar_SetValue ("_config_com_port", (float)port); 356 Cvar_SetValue ("_config_com_irq", (float)irq); 357 Cvar_SetValue ("_config_com_baud", (float)baud); 358 Cvar_SetValue ("_config_com_modem", temp); 359 } 360 361 void TTY_GetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup) 362 { 363 ComPort *p; 364 365 p = handleToPort[portNumber]; 366 *dialType = p->dialType; 367 Q_strcpy(clear, p->clear); 368 Q_strcpy(init, p->startup); 369 Q_strcpy(hangup, p->shutdown); 370 } 371 372 void TTY_SetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup) 373 { 374 ComPort *p; 375 376 p = handleToPort[portNumber]; 377 p->dialType = dialType[0]; 378 Q_strcpy(p->clear, clear); 379 Q_strcpy(p->startup, init); 380 Q_strcpy(p->shutdown, hangup); 381 382 p->modemInitialized = false; 383 384 Cvar_Set ("_config_modem_dialtype", dialType); 385 Cvar_Set ("_config_modem_clear", clear); 386 Cvar_Set ("_config_modem_init", init); 387 Cvar_Set ("_config_modem_hangup", hangup); 388 } 389 390 391 static void ResetComPortConfig (ComPort *p) 392 { 393 p->useModem = false; 394 p->uartType = UART_AUTO; 395 p->uart = ISA_uarts[p->portNumber]; 396 p->irq = ISA_IRQs[p->portNumber]; 397 p->modemStatusIgnore = MSR_CD | MSR_CTS | MSR_DSR; 398 p->baudBits = 115200 / 57600; 399 p->lineControl = LCR_DATA_BITS_8 | LCR_STOP_BITS_1 | LCR_PARITY_NONE; 400 Q_strcpy(p->clear, "ATZ"); 401 Q_strcpy(p->startup, ""); 402 Q_strcpy(p->shutdown, "AT H"); 403 p->modemRang = false; 404 p->modemConnected = false; 405 p->statusUpdated = false; 406 p->outputQueue.head = p->outputQueue.tail = 0; 407 p->inputQueue.head = p->inputQueue.tail = 0; 408 } 409 410 411 static void ComPort_Enable(ComPort *p) 412 { 413 void (*isr)(void); 414 int n; 415 byte b; 416 417 if (p->enabled) 418 { 419 Con_Printf("Already enabled\n"); 420 return; 421 } 422 423 // disable all UART interrupts 424 outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0); 425 426 // clear out any buffered uncoming data 427 while((inportb (p->uart + LINE_STATUS_REGISTER)) & LSR_DATA_READY) 428 inportb (p->uart + RECEIVE_BUFFER_REGISTER); 429 430 // get the current line and modem status 431 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; 432 p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); 433 434 // clear any UART interrupts 435 do 436 { 437 n = inportb (p->uart + INTERRUPT_ID_REGISTER) & 7; 438 if (n == IIR_RX_DATA_READY_INTERRUPT) 439 inportb (p->uart + RECEIVE_BUFFER_REGISTER); 440 } while (!(n & 1)); 441 442 if (p->uartType == UART_AUTO) 443 { 444 outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE); 445 b = inportb (p->uart + INTERRUPT_ID_REGISTER); 446 if ((b & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) 447 p->uartType = UART_16550; 448 else 449 p->uartType = UART_8250; 450 } 451 452 // save the old interrupt handler 453 _go32_dpmi_get_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo); 454 455 if (p->uartType == UART_8250) 456 { 457 outportb (p->uart + FIFO_CONTROL_REGISTER, 0); 458 if (p == handleToPort[0]) 459 isr = COM1_ISR_8250; 460 else 461 isr = COM2_ISR_8250; 462 } 463 else 464 { 465 outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE | FCR_RCVR_FIFO_RESET | FCR_XMIT_FIFO_RESET | FCR_TRIGGER_08); 466 if (p == handleToPort[0]) 467 isr = COM1_ISR_16550; 468 else 469 isr = COM2_ISR_16550; 470 } 471 472 p->protectedModeInfo.pm_offset = (int)isr; 473 474 n = _go32_dpmi_allocate_iret_wrapper(&p->protectedModeInfo); 475 if (n) 476 { 477 Con_Printf("serial: protected mode callback allocation failed\n"); 478 return; 479 } 480 481 // disable interrupts at the processor 482 disable(); 483 484 // install our interrupt handlers now 485 _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeInfo); 486 487 // enable our interrupt at the PIC 488 outportb (0x21, inportb (0x21) & ~(1<<p->irq)); 489 490 // enable interrupts at the processor 491 enable(); 492 493 // enable interrupts at the PIC 494 outportb (0x20, 0xc2); 495 496 // set baud rate & line control 497 outportb (p->uart + LINE_CONTROL_REGISTER, LCR_DLAB | p->lineControl); 498 outportb (p->uart, p->baudBits); 499 outportb (p->uart + 1, 0); 500 outportb (p->uart + LINE_CONTROL_REGISTER, p->lineControl); 501 502 // set modem control register & enable uart interrupt generation 503 outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR); 504 505 // enable the individual interrupts at the uart 506 outportb (p->uart + INTERRUPT_ENABLE_REGISTER, IER_RX_DATA_READY | IER_TX_HOLDING_REGISTER_EMPTY | IER_LINE_STATUS | IER_MODEM_STATUS); 507 508 p->enabled = true; 509 } 510 511 512 static void ComPort_Disable(ComPort *p) 513 { 514 if (!p->enabled) 515 { 516 Con_Printf("Already disabled\n"); 517 return; 518 } 519 520 // disable interrupts at the uart 521 outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0); 522 523 // disable our interrupt at the PIC 524 outportb (0x21, inportb (0x21) | (1<<p->irq)); 525 526 // disable interrupts at the processor 527 disable(); 528 529 // restore the old interrupt handler 530 _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo); 531 _go32_dpmi_free_iret_wrapper(&p->protectedModeInfo); 532 533 // enable interrupts at the processor 534 enable(); 535 536 p->enabled = false; 537 } 538 539 540 static int CheckStatus (ComPort *p) 541 { 542 int ret = 0; 543 544 if (p->statusUpdated) 545 { 546 p->statusUpdated = false; 547 548 if (p->lineStatus & (LSR_OVERRUN_ERROR | LSR_PARITY_ERROR | LSR_FRAMING_ERROR | LSR_BREAK_DETECT)) 549 { 550 if (p->lineStatus & LSR_OVERRUN_ERROR) 551 Con_DPrintf ("Serial overrun error\n"); 552 if (p->lineStatus & LSR_PARITY_ERROR) 553 Con_DPrintf ("Serial parity error\n"); 554 if (p->lineStatus & LSR_FRAMING_ERROR) 555 Con_DPrintf ("Serial framing error\n"); 556 if (p->lineStatus & LSR_BREAK_DETECT) 557 Con_DPrintf ("Serial break detect\n"); 558 ret = ERR_TTY_LINE_STATUS; 559 } 560 561 if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) 562 { 563 if (!(p->modemStatus & MSR_CTS)) 564 Con_Printf ("Serial lost CTS\n"); 565 if (!(p->modemStatus & MSR_DSR)) 566 Con_Printf ("Serial lost DSR\n"); 567 if (!(p->modemStatus & MSR_CD)) 568 Con_Printf ("Serial lost Carrier\n"); 569 ret = ERR_TTY_MODEM_STATUS; 570 } 571 } 572 573 return ret; 574 } 575 576 577 static void Modem_Init(ComPort *p) 578 { 579 double start; 580 char *response; 581 582 Con_Printf ("Initializing modem...\n"); 583 584 // write 0 to MCR, wait 1/2 sec, then write the real value back again 585 // I got this from the guys at head-to-head who say it's necessary. 586 outportb(p->uart + MODEM_CONTROL_REGISTER, 0); 587 start = Sys_FloatTime(); 588 while ((Sys_FloatTime() - start) < 0.5) 589 ; 590 outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR); 591 start = Sys_FloatTime(); 592 while ((Sys_FloatTime() - start) < 0.25) 593 ; 594 595 if (*p->clear) 596 { 597 Modem_Command (p, p->clear); 598 start = Sys_FloatTime(); 599 while(1) 600 { 601 if ((Sys_FloatTime() - start) > 3.0) 602 { 603 Con_Printf("No response - clear failed\n"); 604 p->enabled = false; 605 goto failed; 606 } 607 response = Modem_Response(p); 608 if (!response) 609 continue; 610 if (Q_strncmp(response, "OK", 2) == 0) 611 break; 612 if (Q_strncmp(response, "ERROR", 5) == 0) 613 { 614 p->enabled = false; 615 goto failed; 616 } 617 } 618 } 619 620 if (*p->startup) 621 { 622 Modem_Command (p, p->startup); 623 start = Sys_FloatTime(); 624 while(1) 625 { 626 if ((Sys_FloatTime() - start) > 3.0) 627 { 628 Con_Printf("No response - init failed\n"); 629 p->enabled = false; 630 goto failed; 631 } 632 response = Modem_Response(p); 633 if (!response) 634 continue; 635 if (Q_strncmp(response, "OK", 2) == 0) 636 break; 637 if (Q_strncmp(response, "ERROR", 5) == 0) 638 { 639 p->enabled = false; 640 goto failed; 641 } 642 } 643 } 644 645 p->modemInitialized = true; 646 return; 647 648 failed: 649 if (m_return_onerror) 650 { 651 key_dest = key_menu; 652 m_state = m_return_state; 653 m_return_onerror = false; 654 Q_strcpy(m_return_reason, "Initialization Failed"); 655 } 656 return; 657 } 658 659 660 void TTY_Enable(int handle) 661 { 662 ComPort *p; 663 664 p = handleToPort [handle]; 665 if (p->enabled) 666 return; 667 668 ComPort_Enable(p); 669 670 if (p->useModem && !p->modemInitialized) 671 Modem_Init (p); 672 } 673 674 675 int TTY_Open(int serialPortNumber) 676 { 677 return serialPortNumber; 678 } 679 680 681 void TTY_Close(int handle) 682 { 683 ComPort *p; 684 double startTime; 685 686 p = handleToPort [handle]; 687 688 startTime = Sys_FloatTime(); 689 while ((Sys_FloatTime() - startTime) < 1.0) 690 if (EMPTY(p->outputQueue)) 691 break; 692 693 if (p->useModem) 694 { 695 if (p->modemConnected) 696 Modem_Hangup(p); 697 } 698 } 699 700 701 int TTY_ReadByte(int handle) 702 { 703 int ret; 704 ComPort *p; 705 706 p = handleToPort [handle]; 707 708 if ((ret = CheckStatus (p)) != 0) 709 return ret; 710 711 if (EMPTY (p->inputQueue)) 712 return ERR_TTY_NODATA; 713 714 DEQUEUE (p->inputQueue, ret); 715 return (ret & 0xff); 716 } 717 718 719 int TTY_WriteByte(int handle, byte data) 720 { 721 ComPort *p; 722 723 p = handleToPort [handle]; 724 if (FULL(p->outputQueue)) 725 return -1; 726 727 ENQUEUE (p->outputQueue, data); 728 return 0; 729 } 730 731 732 void TTY_Flush(int handle) 733 { 734 byte b; 735 ComPort *p; 736 737 p = handleToPort [handle]; 738 739 if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY) 740 { 741 DEQUEUE (p->outputQueue, b); 742 outportb(p->uart, b); 743 } 744 } 745 746 747 int TTY_Connect(int handle, char *host) 748 { 749 double start; 750 ComPort *p; 751 char *response = NULL; 752 keydest_t save_key_dest; 753 byte dialstring[64]; 754 byte b; 755 756 p = handleToPort[handle]; 757 758 if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) 759 { 760 Con_Printf ("Serial: line not ready ("); 761 if ((p->modemStatus & MSR_CTS) == 0) 762 Con_Printf(" CTS"); 763 if ((p->modemStatus & MSR_DSR) == 0) 764 Con_Printf(" DSR"); 765 if ((p->modemStatus & MSR_CD) == 0) 766 Con_Printf(" CD"); 767 Con_Printf(" )"); 768 return -1; 769 } 770 771 // discard any scraps in the input buffer 772 while (! EMPTY (p->inputQueue)) 773 DEQUEUE (p->inputQueue, b); 774 775 CheckStatus (p); 776 777 if (p->useModem) 778 { 779 save_key_dest = key_dest; 780 key_dest = key_console; 781 key_count = -2; 782 783 Con_Printf ("Dialing...\n"); 784 sprintf(dialstring, "AT D%c %s\r", p->dialType, host); 785 Modem_Command (p, dialstring); 786 start = Sys_FloatTime(); 787 while(1) 788 { 789 if ((Sys_FloatTime() - start) > 60.0) 790 { 791 Con_Printf("Dialing failure!\n"); 792 break; 793 } 794 795 Sys_SendKeyEvents (); 796 if (key_count == 0) 797 { 798 if (key_lastpress != K_ESCAPE) 799 { 800 key_count = -2; 801 continue; 802 } 803 Con_Printf("Aborting...\n"); 804 while ((Sys_FloatTime() - start) < 5.0) 805 ; 806 disable(); 807 p->outputQueue.head = p->outputQueue.tail = 0; 808 p->inputQueue.head = p->inputQueue.tail = 0; 809 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR); 810 enable(); 811 start = Sys_FloatTime(); 812 while ((Sys_FloatTime() - start) < 0.75) 813 ; 814 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR); 815 response = "Aborted"; 816 break; 817 } 818 819 response = Modem_Response(p); 820 if (!response) 821 continue; 822 if (Q_strncmp(response, "CONNECT", 7) == 0) 823 { 824 disable(); 825 p->modemRang = true; 826 p->modemConnected = true; 827 p->outputQueue.head = p->outputQueue.tail = 0; 828 p->inputQueue.head = p->inputQueue.tail = 0; 829 enable(); 830 key_dest = save_key_dest; 831 key_count = 0; 832 m_return_onerror = false; 833 return 0; 834 } 835 if (Q_strncmp(response, "NO CARRIER", 10) == 0) 836 break; 837 if (Q_strncmp(response, "NO DIALTONE", 11) == 0) 838 break; 839 if (Q_strncmp(response, "NO DIAL TONE", 12) == 0) 840 break; 841 if (Q_strncmp(response, "NO ANSWER", 9) == 0) 842 break; 843 if (Q_strncmp(response, "BUSY", 4) == 0) 844 break; 845 if (Q_strncmp(response, "ERROR", 5) == 0) 846 break; 847 } 848 key_dest = save_key_dest; 849 key_count = 0; 850 if (m_return_onerror) 851 { 852 key_dest = key_menu; 853 m_state = m_return_state; 854 m_return_onerror = false; 855 Q_strncpy(m_return_reason, response, 31); 856 } 857 return -1; 858 } 859 m_return_onerror = false; 860 return 0; 861 } 862 863 864 void TTY_Disconnect(int handle) 865 { 866 ComPort *p; 867 868 p = handleToPort[handle]; 869 870 if (p->useModem && p->modemConnected) 871 Modem_Hangup(p); 872 } 873 874 875 qboolean TTY_CheckForConnection(int handle) 876 { 877 ComPort *p; 878 879 p = handleToPort[handle]; 880 881 CheckStatus (p); 882 883 if (p->useModem) 884 { 885 if (!p->modemRang) 886 { 887 if (!Modem_Response(p)) 888 return false; 889 890 if (Q_strncmp(p->buffer, "RING", 4) == 0) 891 { 892 Modem_Command (p, "ATA"); 893 p->modemRang = true; 894 p->timestamp = net_time; 895 } 896 return false; 897 } 898 if (!p->modemConnected) 899 { 900 if ((net_time - p->timestamp) > 35.0) 901 { 902 Con_Printf("Unable to establish modem connection\n"); 903 p->modemRang = false; 904 return false; 905 } 906 907 if (!Modem_Response(p)) 908 return false; 909 910 if (Q_strncmp (p->buffer, "CONNECT", 7) != 0) 911 return false; 912 913 disable(); 914 p->modemConnected = true; 915 p->outputQueue.head = p->outputQueue.tail = 0; 916 p->inputQueue.head = p->inputQueue.tail = 0; 917 enable(); 918 Con_Printf("Modem Connect\n"); 919 return true; 920 } 921 return true; 922 } 923 924 // direct connect case 925 if (EMPTY (p->inputQueue)) 926 return false; 927 return true; 928 } 929 930 931 qboolean TTY_IsEnabled(int serialPortNumber) 932 { 933 return handleToPort[serialPortNumber]->enabled; 934 } 935 936 937 qboolean TTY_IsModem(int serialPortNumber) 938 { 939 return handleToPort[serialPortNumber]->useModem; 940 } 941 942 943 qboolean TTY_OutputQueueIsEmpty(int handle) 944 { 945 return EMPTY(handleToPort[handle]->outputQueue); 946 } 947 948 949 void Com_f (void) 950 { 951 ComPort *p; 952 int portNumber; 953 int i; 954 int n; 955 956 // first, determine which port they're messing with 957 portNumber = Q_atoi(Cmd_Argv (0) + 3) - 1; 958 if (portNumber > 1) 959 return; 960 p = handleToPort[portNumber]; 961 962 if (Cmd_Argc() == 1) 963 { 964 Con_Printf("Settings for COM%i\n", portNumber + 1); 965 Con_Printf("enabled: %s\n", p->enabled ? "true" : "false"); 966 Con_Printf("uart: "); 967 if (p->uartType == UART_AUTO) 968 Con_Printf("auto\n"); 969 else if (p->uartType == UART_8250) 970 Con_Printf("8250\n"); 971 else 972 Con_Printf("16550\n"); 973 Con_Printf("port: %x\n", p->uart); 974 Con_Printf("irq: %i\n", p->irq); 975 Con_Printf("baud: %i\n", 115200 / p->baudBits); 976 Con_Printf("CTS: %s\n", (p->modemStatusIgnore & MSR_CTS) ? "ignored" : "honored"); 977 Con_Printf("DSR: %s\n", (p->modemStatusIgnore & MSR_DSR) ? "ignored" : "honored"); 978 Con_Printf("CD: %s\n", (p->modemStatusIgnore & MSR_CD) ? "ignored" : "honored"); 979 if (p->useModem) 980 { 981 Con_Printf("type: Modem\n"); 982 Con_Printf("clear: %s\n", p->clear); 983 Con_Printf("startup: %s\n", p->startup); 984 Con_Printf("shutdown: %s\n", p->shutdown); 985 } 986 else 987 Con_Printf("type: Direct connect\n"); 988 989 return; 990 } 991 992 993 if (Cmd_CheckParm ("disable")) 994 { 995 if (p->enabled) 996 ComPort_Disable(p); 997 p->modemInitialized = false; 998 return; 999 } 1000 1001 if (Cmd_CheckParm ("reset")) 1002 { 1003 ComPort_Disable(p); 1004 ResetComPortConfig (p); 1005 return; 1006 } 1007 1008 if ((i = Cmd_CheckParm ("port")) != 0) 1009 { 1010 if (p->enabled) 1011 { 1012 Con_Printf("COM port must be disabled to change port\n"); 1013 return; 1014 } 1015 p->uart = Q_atoi (Cmd_Argv (i+1)); 1016 } 1017 1018 if ((i = Cmd_CheckParm ("irq")) != 0) 1019 { 1020 if (p->enabled) 1021 { 1022 Con_Printf("COM port must be disabled to change irq\n"); 1023 return; 1024 } 1025 p->irq = Q_atoi (Cmd_Argv (i+1)); 1026 } 1027 1028 if ((i = Cmd_CheckParm ("baud")) != 0) 1029 { 1030 if (p->enabled) 1031 { 1032 Con_Printf("COM port must be disabled to change baud\n"); 1033 return; 1034 } 1035 n = Q_atoi (Cmd_Argv (i+1)); 1036 if (n == 0) 1037 Con_Printf("Invalid baud rate specified\n"); 1038 else 1039 p->baudBits = 115200 / n; 1040 } 1041 1042 if (Cmd_CheckParm ("8250")) 1043 { 1044 if (p->enabled) 1045 { 1046 Con_Printf("COM port must be disabled to change uart\n"); 1047 return; 1048 } 1049 p->uartType = UART_8250; 1050 } 1051 if (Cmd_CheckParm ("16550")) 1052 { 1053 if (p->enabled) 1054 { 1055 Con_Printf("COM port must be disabled to change uart\n"); 1056 return; 1057 } 1058 p->uartType = UART_16550; 1059 } 1060 if (Cmd_CheckParm ("auto")) 1061 { 1062 if (p->enabled) 1063 { 1064 Con_Printf("COM port must be disabled to change uart\n"); 1065 return; 1066 } 1067 p->uartType = UART_AUTO; 1068 } 1069 1070 if (Cmd_CheckParm ("pulse")) 1071 p->dialType = 'P'; 1072 if (Cmd_CheckParm ("tone")) 1073 p->dialType = 'T'; 1074 1075 if (Cmd_CheckParm ("direct")) 1076 p->useModem = false; 1077 if (Cmd_CheckParm ("modem")) 1078 p->useModem = true; 1079 1080 if ((i = Cmd_CheckParm ("clear")) != 0) 1081 { 1082 Q_strncpy (p->clear, Cmd_Argv (i+1), 16); 1083 } 1084 1085 if ((i = Cmd_CheckParm ("startup")) != 0) 1086 { 1087 Q_strncpy (p->startup, Cmd_Argv (i+1), 32); 1088 p->modemInitialized = false; 1089 } 1090 1091 if ((i = Cmd_CheckParm ("shutdown")) != 0) 1092 { 1093 Q_strncpy (p->shutdown, Cmd_Argv (i+1), 16); 1094 } 1095 1096 if (Cmd_CheckParm ("-cts")) 1097 { 1098 p->modemStatusIgnore |= MSR_CTS; 1099 p->modemStatus |= MSR_CTS; 1100 } 1101 1102 if (Cmd_CheckParm ("+cts")) 1103 { 1104 p->modemStatusIgnore &= (~MSR_CTS); 1105 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; 1106 } 1107 1108 if (Cmd_CheckParm ("-dsr")) 1109 { 1110 p->modemStatusIgnore |= MSR_DSR; 1111 p->modemStatus |= MSR_DSR; 1112 } 1113 1114 if (Cmd_CheckParm ("+dsr")) 1115 { 1116 p->modemStatusIgnore &= (~MSR_DSR); 1117 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; 1118 } 1119 1120 if (Cmd_CheckParm ("-cd")) 1121 { 1122 p->modemStatusIgnore |= MSR_CD; 1123 p->modemStatus |= MSR_CD; 1124 } 1125 1126 if (Cmd_CheckParm ("+cd")) 1127 { 1128 p->modemStatusIgnore &= (~MSR_CD); 1129 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; 1130 } 1131 1132 if (Cmd_CheckParm ("enable")) 1133 { 1134 if (!p->enabled) 1135 ComPort_Enable(p); 1136 if (p->useModem && !p->modemInitialized) 1137 Modem_Init (p); 1138 } 1139 } 1140 1141 1142 int TTY_Init(void) 1143 { 1144 int n; 1145 ComPort *p; 1146 1147 for (n = 0; n < NUM_COM_PORTS; n++) 1148 { 1149 p = (ComPort *)Hunk_AllocName(sizeof(ComPort), "comport"); 1150 if (p == NULL) 1151 Sys_Error("Hunk alloc failed for com port\n"); 1152 p->next = portList; 1153 portList = p; 1154 handleToPort[n] = p; 1155 p->portNumber = n; 1156 p->dialType = 'T'; 1157 sprintf(p->name, "com%u", n+1); 1158 Cmd_AddCommand (p->name, Com_f); 1159 ResetComPortConfig (p); 1160 } 1161 1162 GetComPortConfig = TTY_GetComPortConfig; 1163 SetComPortConfig = TTY_SetComPortConfig; 1164 GetModemConfig = TTY_GetModemConfig; 1165 SetModemConfig = TTY_SetModemConfig; 1166 1167 return 0; 1168 } 1169 1170 1171 void TTY_Shutdown(void) 1172 { 1173 int n; 1174 ComPort *p; 1175 1176 for (n = 0; n < NUM_COM_PORTS; n++) 1177 { 1178 p = handleToPort[n]; 1179 if (p->enabled) 1180 { 1181 while (p->modemConnected) 1182 NET_Poll(); 1183 ComPort_Disable (p); 1184 } 1185 } 1186 } 1187 1188 1189 static int Modem_Command(ComPort *p, char *commandString) 1190 { 1191 byte b; 1192 1193 if (CheckStatus (p)) 1194 return -1; 1195 1196 disable(); 1197 p->outputQueue.head = p->outputQueue.tail = 0; 1198 p->inputQueue.head = p->inputQueue.tail = 0; 1199 enable(); 1200 p->bufferUsed = 0; 1201 1202 while (*commandString) 1203 ENQUEUE (p->outputQueue, *commandString++); 1204 ENQUEUE (p->outputQueue, '\r'); 1205 1206 // get the transmit rolling 1207 DEQUEUE (p->outputQueue, b); 1208 outportb(p->uart, b); 1209 1210 return 0; 1211 } 1212 1213 1214 static char *Modem_Response(ComPort *p) 1215 { 1216 byte b; 1217 1218 if (CheckStatus (p)) 1219 return NULL; 1220 1221 while (! EMPTY(p->inputQueue)) 1222 { 1223 DEQUEUE (p->inputQueue, b); 1224 1225 if (p->bufferUsed == (sizeof(p->buffer) - 1)) 1226 b = '\r'; 1227 1228 if (b == '\r' && p->bufferUsed) 1229 { 1230 p->buffer[p->bufferUsed] = 0; 1231 Con_Printf("%s\n", p->buffer); 1232 SCR_UpdateScreen (); 1233 p->bufferUsed = 0; 1234 return p->buffer; 1235 } 1236 1237 if (b < ' ' || b > 'z') 1238 continue; 1239 p->buffer[p->bufferUsed] = b; 1240 p->bufferUsed++; 1241 } 1242 1243 return NULL; 1244 } 1245 1246 1247 static void Modem_Hangup2(ComPort *p); 1248 static void Modem_Hangup3(ComPort *p); 1249 static void Modem_Hangup4(ComPort *p); 1250 1251 static void Modem_Hangup(ComPort *p) 1252 { 1253 Con_Printf("Hanging up modem...\n"); 1254 disable(); 1255 p->modemRang = false; 1256 p->outputQueue.head = p->outputQueue.tail = 0; 1257 p->inputQueue.head = p->inputQueue.tail = 0; 1258 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR); 1259 enable(); 1260 p->poll.procedure = Modem_Hangup2; 1261 p->poll.arg = p; 1262 SchedulePollProcedure(&p->poll, 1.5); 1263 } 1264 1265 static void Modem_Hangup2(ComPort *p) 1266 { 1267 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR); 1268 Modem_Command(p, "+++"); 1269 p->poll.procedure = Modem_Hangup3; 1270 SchedulePollProcedure(&p->poll, 1.5); 1271 } 1272 1273 static void Modem_Hangup3(ComPort *p) 1274 { 1275 Modem_Command(p, p->shutdown); 1276 p->poll.procedure = Modem_Hangup4; 1277 SchedulePollProcedure(&p->poll, 1.5); 1278 } 1279 1280 static void Modem_Hangup4(ComPort *p) 1281 { 1282 Modem_Response(p); 1283 Con_Printf("Hangup complete\n"); 1284 p->modemConnected = false; 1285 } 1286