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