Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

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