Home | History | Annotate | Download | only in WinQuake
      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