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