• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ISR_8250(ComPort * p)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 
COM1_ISR_8250(void)234 static void COM1_ISR_8250 (void)
235 {
236 	ISR_8250 (handleToPort[0]);
237 }
238 
COM2_ISR_8250(void)239 static void COM2_ISR_8250 (void)
240 {
241 	ISR_8250 (handleToPort[1]);
242 }
243 
244 
245 
ISR_16550(ComPort * p)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 
COM1_ISR_16550(void)309 static void COM1_ISR_16550 (void)
310 {
311 	ISR_16550 (handleToPort[0]);
312 }
313 
COM2_ISR_16550(void)314 static void COM2_ISR_16550 (void)
315 {
316 	ISR_16550 (handleToPort[1]);
317 }
318 
319 
TTY_GetComPortConfig(int portNumber,int * port,int * irq,int * baud,qboolean * useModem)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 
TTY_SetComPortConfig(int portNumber,int port,int irq,int baud,qboolean useModem)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 
TTY_GetModemConfig(int portNumber,char * dialType,char * clear,char * init,char * hangup)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 
TTY_SetModemConfig(int portNumber,char * dialType,char * clear,char * init,char * hangup)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 
ResetComPortConfig(ComPort * p)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 
ComPort_Enable(ComPort * p)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 
ComPort_Disable(ComPort * p)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 
CheckStatus(ComPort * p)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 
Modem_Init(ComPort * p)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 
TTY_Enable(int handle)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 
TTY_Open(int serialPortNumber)675 int TTY_Open(int serialPortNumber)
676 {
677 	return serialPortNumber;
678 }
679 
680 
TTY_Close(int handle)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 
TTY_ReadByte(int handle)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 
TTY_WriteByte(int handle,byte data)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 
TTY_Flush(int handle)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 
TTY_Connect(int handle,char * host)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 
TTY_Disconnect(int handle)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 
TTY_CheckForConnection(int handle)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 
TTY_IsEnabled(int serialPortNumber)931 qboolean TTY_IsEnabled(int serialPortNumber)
932 {
933 	return handleToPort[serialPortNumber]->enabled;
934 }
935 
936 
TTY_IsModem(int serialPortNumber)937 qboolean TTY_IsModem(int serialPortNumber)
938 {
939 	return handleToPort[serialPortNumber]->useModem;
940 }
941 
942 
TTY_OutputQueueIsEmpty(int handle)943 qboolean TTY_OutputQueueIsEmpty(int handle)
944 {
945 	return EMPTY(handleToPort[handle]->outputQueue);
946 }
947 
948 
Com_f(void)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 
TTY_Init(void)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 
TTY_Shutdown(void)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 
Modem_Command(ComPort * p,char * commandString)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 
Modem_Response(ComPort * p)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 
Modem_Hangup(ComPort * p)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 
Modem_Hangup2(ComPort * p)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 
Modem_Hangup3(ComPort * p)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 
Modem_Hangup4(ComPort * p)1280 static void Modem_Hangup4(ComPort *p)
1281 {
1282 	Modem_Response(p);
1283 	Con_Printf("Hangup complete\n");
1284 	p->modemConnected = false;
1285 }
1286