/* | |
Copyright (C) 1996-1997 Id Software, Inc. | |
This program is free software; you can redistribute it and/or | |
modify it under the terms of the GNU General Public License | |
as published by the Free Software Foundation; either version 2 | |
of the License, or (at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
See the GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
*/ | |
// net_comx.c | |
#include <dos.h> | |
#include <dpmi.h> | |
#define NUM_COM_PORTS 2 | |
#define ERR_TTY_LINE_STATUS -1 | |
#define ERR_TTY_MODEM_STATUS -2 | |
#define ERR_TTY_NODATA -3 | |
#define QUEUESIZE 8192 | |
#define QUEUEMASK (QUEUESIZE - 1) | |
typedef struct | |
{ | |
volatile int head; | |
volatile int tail; | |
volatile byte data[QUEUESIZE]; | |
} queue; | |
#define FULL(q) (q.head == ((q.tail-1) & QUEUEMASK)) | |
#define EMPTY(q) (q.tail == q.head) | |
#define ENQUEUE(q,b) (q.data[q.head] = b, q.head = (q.head + 1) & QUEUEMASK) | |
#define DEQUEUE(q,b) (b = q.data[q.tail], q.tail = (q.tail + 1) & QUEUEMASK) | |
extern cvar_t config_com_port; | |
extern cvar_t config_com_irq; | |
extern cvar_t config_com_baud; | |
extern cvar_t config_com_modem; | |
extern cvar_t config_modem_dialtype; | |
extern cvar_t config_modem_clear; | |
extern cvar_t config_modem_init; | |
extern cvar_t config_modem_hangup; | |
extern int m_return_state; | |
extern int m_state; | |
extern qboolean m_return_onerror; | |
extern char m_return_reason[32]; | |
// 8250, 16550 definitions | |
#define TRANSMIT_HOLDING_REGISTER 0x00 | |
#define RECEIVE_BUFFER_REGISTER 0x00 | |
#define INTERRUPT_ENABLE_REGISTER 0x01 | |
#define IER_RX_DATA_READY 0x01 | |
#define IER_TX_HOLDING_REGISTER_EMPTY 0x02 | |
#define IER_LINE_STATUS 0x04 | |
#define IER_MODEM_STATUS 0x08 | |
#define INTERRUPT_ID_REGISTER 0x02 | |
#define IIR_MODEM_STATUS_INTERRUPT 0x00 | |
#define IIR_TX_HOLDING_REGISTER_INTERRUPT 0x02 | |
#define IIR_RX_DATA_READY_INTERRUPT 0x04 | |
#define IIR_LINE_STATUS_INTERRUPT 0x06 | |
#define IIR_FIFO_TIMEOUT 0x0c | |
#define IIR_FIFO_ENABLED 0xc0 | |
#define FIFO_CONTROL_REGISTER 0x02 | |
#define FCR_FIFO_ENABLE 0x01 | |
#define FCR_RCVR_FIFO_RESET 0x02 | |
#define FCR_XMIT_FIFO_RESET 0x04 | |
#define FCR_TRIGGER_01 0x00 | |
#define FCR_TRIGGER_04 0x40 | |
#define FCR_TRIGGER_08 0x80 | |
#define FCR_TRIGGER_16 0xc0 | |
#define LINE_CONTROL_REGISTER 0x03 | |
#define LCR_DATA_BITS_5 0x00 | |
#define LCR_DATA_BITS_6 0x01 | |
#define LCR_DATA_BITS_7 0x02 | |
#define LCR_DATA_BITS_8 0x03 | |
#define LCR_STOP_BITS_1 0x00 | |
#define LCR_STOP_BITS_2 0x04 | |
#define LCR_PARITY_NONE 0x00 | |
#define LCR_PARITY_ODD 0x08 | |
#define LCR_PARITY_EVEN 0x18 | |
#define LCR_PARITY_MARK 0x28 | |
#define LCR_PARITY_SPACE 0x38 | |
#define LCR_SET_BREAK 0x40 | |
#define LCR_DLAB 0x80 | |
#define MODEM_CONTROL_REGISTER 0x04 | |
#define MCR_DTR 0x01 | |
#define MCR_RTS 0x02 | |
#define MCR_OUT1 0x04 | |
#define MCR_OUT2 0x08 | |
#define MCR_LOOPBACK 0x10 | |
#define LINE_STATUS_REGISTER 0x05 | |
#define LSR_DATA_READY 0x01 | |
#define LSR_OVERRUN_ERROR 0x02 | |
#define LSR_PARITY_ERROR 0x04 | |
#define LSR_FRAMING_ERROR 0x08 | |
#define LSR_BREAK_DETECT 0x10 | |
#define LSR_TRANSMITTER_BUFFER_EMPTY 0x20 | |
#define LSR_TRANSMITTER_EMPTY 0x40 | |
#define LSR_FIFO_DIRTY 0x80 | |
#define MODEM_STATUS_REGISTER 0x06 | |
#define MSR_DELTA_CTS 0x01 | |
#define MSR_DELTA_DSR 0x02 | |
#define MSR_DELTA_RI 0x04 | |
#define MSR_DELTA_CD 0x08 | |
#define MSR_CTS 0x10 | |
#define MSR_DSR 0x20 | |
#define MSR_RI 0x40 | |
#define MSR_CD 0x80 | |
#define DIVISOR_LATCH_LOW 0x00 | |
#define DIVISOR_LATCH_HIGH 0x01 | |
#define MODEM_STATUS_MASK (MSR_CTS | MSR_DSR | MSR_CD) | |
#define UART_AUTO 0 | |
#define UART_8250 1 | |
#define UART_16550 2 | |
static int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8}; | |
static int ISA_IRQs[] = {4,3,4,3}; | |
typedef struct ComPort_s | |
{ | |
struct ComPort_s *next; | |
_go32_dpmi_seginfo protectedModeInfo; | |
_go32_dpmi_seginfo protectedModeSaveInfo; | |
int uart; | |
volatile byte modemStatus; | |
byte modemStatusIgnore; | |
byte lineStatus; | |
byte bufferUsed; | |
qboolean enabled; | |
volatile qboolean statusUpdated; | |
qboolean useModem; | |
qboolean modemInitialized; | |
qboolean modemRang; | |
qboolean modemConnected; | |
queue inputQueue; | |
queue outputQueue; | |
char clear[16]; | |
char startup[32]; | |
char shutdown[16]; | |
char buffer[128]; | |
PollProcedure poll; | |
double timestamp; | |
byte uartType; | |
byte irq; | |
byte baudBits; | |
byte lineControl; | |
byte portNumber; | |
char dialType; | |
char name[4]; | |
} ComPort; | |
ComPort *portList = NULL; | |
ComPort *handleToPort [NUM_COM_PORTS]; | |
static int Modem_Command(ComPort *p, char *commandString); | |
static char *Modem_Response(ComPort *p); | |
static void Modem_Hangup(ComPort *p); | |
int TTY_Init(void); | |
void TTY_Shutdown(void); | |
int TTY_Open(int serialPortNumber); | |
void TTY_Close(int handle); | |
int TTY_ReadByte(int handle); | |
int TTY_WriteByte(int handle, byte data); | |
void TTY_Flush(int handle); | |
int TTY_Connect(int handle, char *host); | |
void TTY_Disconnect(int handle); | |
qboolean TTY_CheckForConnection(int handle); | |
qboolean TTY_IsEnabled(int serialPortNumber); | |
qboolean TTY_IsModem(int serialPortNumber); | |
qboolean TTY_OutputQueueIsEmpty(int handle); | |
static void ISR_8250 (ComPort *p) | |
{ | |
byte source = 0; | |
byte b; | |
disable(); | |
while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) | |
{ | |
switch (source) | |
{ | |
case IIR_RX_DATA_READY_INTERRUPT: | |
b = inportb (p->uart + RECEIVE_BUFFER_REGISTER); | |
if (! FULL(p->inputQueue)) | |
{ | |
ENQUEUE (p->inputQueue, b); | |
} | |
else | |
{ | |
p->lineStatus |= LSR_OVERRUN_ERROR; | |
p->statusUpdated = true; | |
} | |
break; | |
case IIR_TX_HOLDING_REGISTER_INTERRUPT: | |
if (! EMPTY(p->outputQueue)) | |
{ | |
DEQUEUE (p->outputQueue, b); | |
outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); | |
} | |
break; | |
case IIR_MODEM_STATUS_INTERRUPT: | |
p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; | |
p->statusUpdated = true; | |
break; | |
case IIR_LINE_STATUS_INTERRUPT: | |
p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); | |
p->statusUpdated = true; | |
break; | |
} | |
source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07; | |
} | |
outportb (0x20, 0x20); | |
} | |
static void COM1_ISR_8250 (void) | |
{ | |
ISR_8250 (handleToPort[0]); | |
} | |
static void COM2_ISR_8250 (void) | |
{ | |
ISR_8250 (handleToPort[1]); | |
} | |
static void ISR_16550 (ComPort *p) | |
{ | |
int count; | |
byte source; | |
byte b; | |
disable(); | |
while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) | |
{ | |
switch (source) | |
{ | |
case IIR_RX_DATA_READY_INTERRUPT: | |
do | |
{ | |
b = inportb (p->uart + RECEIVE_BUFFER_REGISTER); | |
if (!FULL(p->inputQueue)) | |
{ | |
ENQUEUE (p->inputQueue, b); | |
} | |
else | |
{ | |
p->lineStatus |= LSR_OVERRUN_ERROR; | |
p->statusUpdated = true; | |
} | |
} while (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_DATA_READY); | |
break; | |
case IIR_TX_HOLDING_REGISTER_INTERRUPT: | |
count = 16; | |
while ((! EMPTY(p->outputQueue)) && count--) | |
{ | |
DEQUEUE (p->outputQueue, b); | |
outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); | |
} | |
break; | |
case IIR_MODEM_STATUS_INTERRUPT: | |
p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; | |
p->statusUpdated = true; | |
break; | |
case IIR_LINE_STATUS_INTERRUPT: | |
p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); | |
p->statusUpdated = true; | |
break; | |
} | |
source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07; | |
} | |
// check for lost IIR_TX_HOLDING_REGISTER_INTERRUPT on 16550a! | |
if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY) | |
{ | |
count = 16; | |
while ((! EMPTY(p->outputQueue)) && count--) | |
{ | |
DEQUEUE (p->outputQueue, b); | |
outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); | |
} | |
} | |
outportb (0x20, 0x20); | |
} | |
static void COM1_ISR_16550 (void) | |
{ | |
ISR_16550 (handleToPort[0]); | |
} | |
static void COM2_ISR_16550 (void) | |
{ | |
ISR_16550 (handleToPort[1]); | |
} | |
void TTY_GetComPortConfig (int portNumber, int *port, int *irq, int *baud, qboolean *useModem) | |
{ | |
ComPort *p; | |
p = handleToPort[portNumber]; | |
*port = p->uart; | |
*irq = p->irq; | |
*baud = 115200 / p->baudBits; | |
*useModem = p->useModem; | |
} | |
void TTY_SetComPortConfig (int portNumber, int port, int irq, int baud, qboolean useModem) | |
{ | |
ComPort *p; | |
float temp; | |
if (useModem) | |
{ | |
if (baud == 14400) | |
baud = 19200; | |
if (baud == 28800) | |
baud = 38400; | |
} | |
p = handleToPort[portNumber]; | |
p->uart = port; | |
p->irq = irq; | |
p->baudBits = 115200 / baud; | |
p->useModem = useModem; | |
if (useModem) | |
temp = 1.0; | |
else | |
temp = 0.0; | |
Cvar_SetValue ("_config_com_port", (float)port); | |
Cvar_SetValue ("_config_com_irq", (float)irq); | |
Cvar_SetValue ("_config_com_baud", (float)baud); | |
Cvar_SetValue ("_config_com_modem", temp); | |
} | |
void TTY_GetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup) | |
{ | |
ComPort *p; | |
p = handleToPort[portNumber]; | |
*dialType = p->dialType; | |
Q_strcpy(clear, p->clear); | |
Q_strcpy(init, p->startup); | |
Q_strcpy(hangup, p->shutdown); | |
} | |
void TTY_SetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup) | |
{ | |
ComPort *p; | |
p = handleToPort[portNumber]; | |
p->dialType = dialType[0]; | |
Q_strcpy(p->clear, clear); | |
Q_strcpy(p->startup, init); | |
Q_strcpy(p->shutdown, hangup); | |
p->modemInitialized = false; | |
Cvar_Set ("_config_modem_dialtype", dialType); | |
Cvar_Set ("_config_modem_clear", clear); | |
Cvar_Set ("_config_modem_init", init); | |
Cvar_Set ("_config_modem_hangup", hangup); | |
} | |
static void ResetComPortConfig (ComPort *p) | |
{ | |
p->useModem = false; | |
p->uartType = UART_AUTO; | |
p->uart = ISA_uarts[p->portNumber]; | |
p->irq = ISA_IRQs[p->portNumber]; | |
p->modemStatusIgnore = MSR_CD | MSR_CTS | MSR_DSR; | |
p->baudBits = 115200 / 57600; | |
p->lineControl = LCR_DATA_BITS_8 | LCR_STOP_BITS_1 | LCR_PARITY_NONE; | |
Q_strcpy(p->clear, "ATZ"); | |
Q_strcpy(p->startup, ""); | |
Q_strcpy(p->shutdown, "AT H"); | |
p->modemRang = false; | |
p->modemConnected = false; | |
p->statusUpdated = false; | |
p->outputQueue.head = p->outputQueue.tail = 0; | |
p->inputQueue.head = p->inputQueue.tail = 0; | |
} | |
static void ComPort_Enable(ComPort *p) | |
{ | |
void (*isr)(void); | |
int n; | |
byte b; | |
if (p->enabled) | |
{ | |
Con_Printf("Already enabled\n"); | |
return; | |
} | |
// disable all UART interrupts | |
outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0); | |
// clear out any buffered uncoming data | |
while((inportb (p->uart + LINE_STATUS_REGISTER)) & LSR_DATA_READY) | |
inportb (p->uart + RECEIVE_BUFFER_REGISTER); | |
// get the current line and modem status | |
p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; | |
p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); | |
// clear any UART interrupts | |
do | |
{ | |
n = inportb (p->uart + INTERRUPT_ID_REGISTER) & 7; | |
if (n == IIR_RX_DATA_READY_INTERRUPT) | |
inportb (p->uart + RECEIVE_BUFFER_REGISTER); | |
} while (!(n & 1)); | |
if (p->uartType == UART_AUTO) | |
{ | |
outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE); | |
b = inportb (p->uart + INTERRUPT_ID_REGISTER); | |
if ((b & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) | |
p->uartType = UART_16550; | |
else | |
p->uartType = UART_8250; | |
} | |
// save the old interrupt handler | |
_go32_dpmi_get_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo); | |
if (p->uartType == UART_8250) | |
{ | |
outportb (p->uart + FIFO_CONTROL_REGISTER, 0); | |
if (p == handleToPort[0]) | |
isr = COM1_ISR_8250; | |
else | |
isr = COM2_ISR_8250; | |
} | |
else | |
{ | |
outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE | FCR_RCVR_FIFO_RESET | FCR_XMIT_FIFO_RESET | FCR_TRIGGER_08); | |
if (p == handleToPort[0]) | |
isr = COM1_ISR_16550; | |
else | |
isr = COM2_ISR_16550; | |
} | |
p->protectedModeInfo.pm_offset = (int)isr; | |
n = _go32_dpmi_allocate_iret_wrapper(&p->protectedModeInfo); | |
if (n) | |
{ | |
Con_Printf("serial: protected mode callback allocation failed\n"); | |
return; | |
} | |
// disable interrupts at the processor | |
disable(); | |
// install our interrupt handlers now | |
_go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeInfo); | |
// enable our interrupt at the PIC | |
outportb (0x21, inportb (0x21) & ~(1<<p->irq)); | |
// enable interrupts at the processor | |
enable(); | |
// enable interrupts at the PIC | |
outportb (0x20, 0xc2); | |
// set baud rate & line control | |
outportb (p->uart + LINE_CONTROL_REGISTER, LCR_DLAB | p->lineControl); | |
outportb (p->uart, p->baudBits); | |
outportb (p->uart + 1, 0); | |
outportb (p->uart + LINE_CONTROL_REGISTER, p->lineControl); | |
// set modem control register & enable uart interrupt generation | |
outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR); | |
// enable the individual interrupts at the uart | |
outportb (p->uart + INTERRUPT_ENABLE_REGISTER, IER_RX_DATA_READY | IER_TX_HOLDING_REGISTER_EMPTY | IER_LINE_STATUS | IER_MODEM_STATUS); | |
p->enabled = true; | |
} | |
static void ComPort_Disable(ComPort *p) | |
{ | |
if (!p->enabled) | |
{ | |
Con_Printf("Already disabled\n"); | |
return; | |
} | |
// disable interrupts at the uart | |
outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0); | |
// disable our interrupt at the PIC | |
outportb (0x21, inportb (0x21) | (1<<p->irq)); | |
// disable interrupts at the processor | |
disable(); | |
// restore the old interrupt handler | |
_go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo); | |
_go32_dpmi_free_iret_wrapper(&p->protectedModeInfo); | |
// enable interrupts at the processor | |
enable(); | |
p->enabled = false; | |
} | |
static int CheckStatus (ComPort *p) | |
{ | |
int ret = 0; | |
if (p->statusUpdated) | |
{ | |
p->statusUpdated = false; | |
if (p->lineStatus & (LSR_OVERRUN_ERROR | LSR_PARITY_ERROR | LSR_FRAMING_ERROR | LSR_BREAK_DETECT)) | |
{ | |
if (p->lineStatus & LSR_OVERRUN_ERROR) | |
Con_DPrintf ("Serial overrun error\n"); | |
if (p->lineStatus & LSR_PARITY_ERROR) | |
Con_DPrintf ("Serial parity error\n"); | |
if (p->lineStatus & LSR_FRAMING_ERROR) | |
Con_DPrintf ("Serial framing error\n"); | |
if (p->lineStatus & LSR_BREAK_DETECT) | |
Con_DPrintf ("Serial break detect\n"); | |
ret = ERR_TTY_LINE_STATUS; | |
} | |
if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) | |
{ | |
if (!(p->modemStatus & MSR_CTS)) | |
Con_Printf ("Serial lost CTS\n"); | |
if (!(p->modemStatus & MSR_DSR)) | |
Con_Printf ("Serial lost DSR\n"); | |
if (!(p->modemStatus & MSR_CD)) | |
Con_Printf ("Serial lost Carrier\n"); | |
ret = ERR_TTY_MODEM_STATUS; | |
} | |
} | |
return ret; | |
} | |
static void Modem_Init(ComPort *p) | |
{ | |
double start; | |
char *response; | |
Con_Printf ("Initializing modem...\n"); | |
// write 0 to MCR, wait 1/2 sec, then write the real value back again | |
// I got this from the guys at head-to-head who say it's necessary. | |
outportb(p->uart + MODEM_CONTROL_REGISTER, 0); | |
start = Sys_FloatTime(); | |
while ((Sys_FloatTime() - start) < 0.5) | |
; | |
outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR); | |
start = Sys_FloatTime(); | |
while ((Sys_FloatTime() - start) < 0.25) | |
; | |
if (*p->clear) | |
{ | |
Modem_Command (p, p->clear); | |
start = Sys_FloatTime(); | |
while(1) | |
{ | |
if ((Sys_FloatTime() - start) > 3.0) | |
{ | |
Con_Printf("No response - clear failed\n"); | |
p->enabled = false; | |
goto failed; | |
} | |
response = Modem_Response(p); | |
if (!response) | |
continue; | |
if (Q_strncmp(response, "OK", 2) == 0) | |
break; | |
if (Q_strncmp(response, "ERROR", 5) == 0) | |
{ | |
p->enabled = false; | |
goto failed; | |
} | |
} | |
} | |
if (*p->startup) | |
{ | |
Modem_Command (p, p->startup); | |
start = Sys_FloatTime(); | |
while(1) | |
{ | |
if ((Sys_FloatTime() - start) > 3.0) | |
{ | |
Con_Printf("No response - init failed\n"); | |
p->enabled = false; | |
goto failed; | |
} | |
response = Modem_Response(p); | |
if (!response) | |
continue; | |
if (Q_strncmp(response, "OK", 2) == 0) | |
break; | |
if (Q_strncmp(response, "ERROR", 5) == 0) | |
{ | |
p->enabled = false; | |
goto failed; | |
} | |
} | |
} | |
p->modemInitialized = true; | |
return; | |
failed: | |
if (m_return_onerror) | |
{ | |
key_dest = key_menu; | |
m_state = m_return_state; | |
m_return_onerror = false; | |
Q_strcpy(m_return_reason, "Initialization Failed"); | |
} | |
return; | |
} | |
void TTY_Enable(int handle) | |
{ | |
ComPort *p; | |
p = handleToPort [handle]; | |
if (p->enabled) | |
return; | |
ComPort_Enable(p); | |
if (p->useModem && !p->modemInitialized) | |
Modem_Init (p); | |
} | |
int TTY_Open(int serialPortNumber) | |
{ | |
return serialPortNumber; | |
} | |
void TTY_Close(int handle) | |
{ | |
ComPort *p; | |
double startTime; | |
p = handleToPort [handle]; | |
startTime = Sys_FloatTime(); | |
while ((Sys_FloatTime() - startTime) < 1.0) | |
if (EMPTY(p->outputQueue)) | |
break; | |
if (p->useModem) | |
{ | |
if (p->modemConnected) | |
Modem_Hangup(p); | |
} | |
} | |
int TTY_ReadByte(int handle) | |
{ | |
int ret; | |
ComPort *p; | |
p = handleToPort [handle]; | |
if ((ret = CheckStatus (p)) != 0) | |
return ret; | |
if (EMPTY (p->inputQueue)) | |
return ERR_TTY_NODATA; | |
DEQUEUE (p->inputQueue, ret); | |
return (ret & 0xff); | |
} | |
int TTY_WriteByte(int handle, byte data) | |
{ | |
ComPort *p; | |
p = handleToPort [handle]; | |
if (FULL(p->outputQueue)) | |
return -1; | |
ENQUEUE (p->outputQueue, data); | |
return 0; | |
} | |
void TTY_Flush(int handle) | |
{ | |
byte b; | |
ComPort *p; | |
p = handleToPort [handle]; | |
if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY) | |
{ | |
DEQUEUE (p->outputQueue, b); | |
outportb(p->uart, b); | |
} | |
} | |
int TTY_Connect(int handle, char *host) | |
{ | |
double start; | |
ComPort *p; | |
char *response = NULL; | |
keydest_t save_key_dest; | |
byte dialstring[64]; | |
byte b; | |
p = handleToPort[handle]; | |
if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) | |
{ | |
Con_Printf ("Serial: line not ready ("); | |
if ((p->modemStatus & MSR_CTS) == 0) | |
Con_Printf(" CTS"); | |
if ((p->modemStatus & MSR_DSR) == 0) | |
Con_Printf(" DSR"); | |
if ((p->modemStatus & MSR_CD) == 0) | |
Con_Printf(" CD"); | |
Con_Printf(" )"); | |
return -1; | |
} | |
// discard any scraps in the input buffer | |
while (! EMPTY (p->inputQueue)) | |
DEQUEUE (p->inputQueue, b); | |
CheckStatus (p); | |
if (p->useModem) | |
{ | |
save_key_dest = key_dest; | |
key_dest = key_console; | |
key_count = -2; | |
Con_Printf ("Dialing...\n"); | |
sprintf(dialstring, "AT D%c %s\r", p->dialType, host); | |
Modem_Command (p, dialstring); | |
start = Sys_FloatTime(); | |
while(1) | |
{ | |
if ((Sys_FloatTime() - start) > 60.0) | |
{ | |
Con_Printf("Dialing failure!\n"); | |
break; | |
} | |
Sys_SendKeyEvents (); | |
if (key_count == 0) | |
{ | |
if (key_lastpress != K_ESCAPE) | |
{ | |
key_count = -2; | |
continue; | |
} | |
Con_Printf("Aborting...\n"); | |
while ((Sys_FloatTime() - start) < 5.0) | |
; | |
disable(); | |
p->outputQueue.head = p->outputQueue.tail = 0; | |
p->inputQueue.head = p->inputQueue.tail = 0; | |
outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR); | |
enable(); | |
start = Sys_FloatTime(); | |
while ((Sys_FloatTime() - start) < 0.75) | |
; | |
outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR); | |
response = "Aborted"; | |
break; | |
} | |
response = Modem_Response(p); | |
if (!response) | |
continue; | |
if (Q_strncmp(response, "CONNECT", 7) == 0) | |
{ | |
disable(); | |
p->modemRang = true; | |
p->modemConnected = true; | |
p->outputQueue.head = p->outputQueue.tail = 0; | |
p->inputQueue.head = p->inputQueue.tail = 0; | |
enable(); | |
key_dest = save_key_dest; | |
key_count = 0; | |
m_return_onerror = false; | |
return 0; | |
} | |
if (Q_strncmp(response, "NO CARRIER", 10) == 0) | |
break; | |
if (Q_strncmp(response, "NO DIALTONE", 11) == 0) | |
break; | |
if (Q_strncmp(response, "NO DIAL TONE", 12) == 0) | |
break; | |
if (Q_strncmp(response, "NO ANSWER", 9) == 0) | |
break; | |
if (Q_strncmp(response, "BUSY", 4) == 0) | |
break; | |
if (Q_strncmp(response, "ERROR", 5) == 0) | |
break; | |
} | |
key_dest = save_key_dest; | |
key_count = 0; | |
if (m_return_onerror) | |
{ | |
key_dest = key_menu; | |
m_state = m_return_state; | |
m_return_onerror = false; | |
Q_strncpy(m_return_reason, response, 31); | |
} | |
return -1; | |
} | |
m_return_onerror = false; | |
return 0; | |
} | |
void TTY_Disconnect(int handle) | |
{ | |
ComPort *p; | |
p = handleToPort[handle]; | |
if (p->useModem && p->modemConnected) | |
Modem_Hangup(p); | |
} | |
qboolean TTY_CheckForConnection(int handle) | |
{ | |
ComPort *p; | |
p = handleToPort[handle]; | |
CheckStatus (p); | |
if (p->useModem) | |
{ | |
if (!p->modemRang) | |
{ | |
if (!Modem_Response(p)) | |
return false; | |
if (Q_strncmp(p->buffer, "RING", 4) == 0) | |
{ | |
Modem_Command (p, "ATA"); | |
p->modemRang = true; | |
p->timestamp = net_time; | |
} | |
return false; | |
} | |
if (!p->modemConnected) | |
{ | |
if ((net_time - p->timestamp) > 35.0) | |
{ | |
Con_Printf("Unable to establish modem connection\n"); | |
p->modemRang = false; | |
return false; | |
} | |
if (!Modem_Response(p)) | |
return false; | |
if (Q_strncmp (p->buffer, "CONNECT", 7) != 0) | |
return false; | |
disable(); | |
p->modemConnected = true; | |
p->outputQueue.head = p->outputQueue.tail = 0; | |
p->inputQueue.head = p->inputQueue.tail = 0; | |
enable(); | |
Con_Printf("Modem Connect\n"); | |
return true; | |
} | |
return true; | |
} | |
// direct connect case | |
if (EMPTY (p->inputQueue)) | |
return false; | |
return true; | |
} | |
qboolean TTY_IsEnabled(int serialPortNumber) | |
{ | |
return handleToPort[serialPortNumber]->enabled; | |
} | |
qboolean TTY_IsModem(int serialPortNumber) | |
{ | |
return handleToPort[serialPortNumber]->useModem; | |
} | |
qboolean TTY_OutputQueueIsEmpty(int handle) | |
{ | |
return EMPTY(handleToPort[handle]->outputQueue); | |
} | |
void Com_f (void) | |
{ | |
ComPort *p; | |
int portNumber; | |
int i; | |
int n; | |
// first, determine which port they're messing with | |
portNumber = Q_atoi(Cmd_Argv (0) + 3) - 1; | |
if (portNumber > 1) | |
return; | |
p = handleToPort[portNumber]; | |
if (Cmd_Argc() == 1) | |
{ | |
Con_Printf("Settings for COM%i\n", portNumber + 1); | |
Con_Printf("enabled: %s\n", p->enabled ? "true" : "false"); | |
Con_Printf("uart: "); | |
if (p->uartType == UART_AUTO) | |
Con_Printf("auto\n"); | |
else if (p->uartType == UART_8250) | |
Con_Printf("8250\n"); | |
else | |
Con_Printf("16550\n"); | |
Con_Printf("port: %x\n", p->uart); | |
Con_Printf("irq: %i\n", p->irq); | |
Con_Printf("baud: %i\n", 115200 / p->baudBits); | |
Con_Printf("CTS: %s\n", (p->modemStatusIgnore & MSR_CTS) ? "ignored" : "honored"); | |
Con_Printf("DSR: %s\n", (p->modemStatusIgnore & MSR_DSR) ? "ignored" : "honored"); | |
Con_Printf("CD: %s\n", (p->modemStatusIgnore & MSR_CD) ? "ignored" : "honored"); | |
if (p->useModem) | |
{ | |
Con_Printf("type: Modem\n"); | |
Con_Printf("clear: %s\n", p->clear); | |
Con_Printf("startup: %s\n", p->startup); | |
Con_Printf("shutdown: %s\n", p->shutdown); | |
} | |
else | |
Con_Printf("type: Direct connect\n"); | |
return; | |
} | |
if (Cmd_CheckParm ("disable")) | |
{ | |
if (p->enabled) | |
ComPort_Disable(p); | |
p->modemInitialized = false; | |
return; | |
} | |
if (Cmd_CheckParm ("reset")) | |
{ | |
ComPort_Disable(p); | |
ResetComPortConfig (p); | |
return; | |
} | |
if ((i = Cmd_CheckParm ("port")) != 0) | |
{ | |
if (p->enabled) | |
{ | |
Con_Printf("COM port must be disabled to change port\n"); | |
return; | |
} | |
p->uart = Q_atoi (Cmd_Argv (i+1)); | |
} | |
if ((i = Cmd_CheckParm ("irq")) != 0) | |
{ | |
if (p->enabled) | |
{ | |
Con_Printf("COM port must be disabled to change irq\n"); | |
return; | |
} | |
p->irq = Q_atoi (Cmd_Argv (i+1)); | |
} | |
if ((i = Cmd_CheckParm ("baud")) != 0) | |
{ | |
if (p->enabled) | |
{ | |
Con_Printf("COM port must be disabled to change baud\n"); | |
return; | |
} | |
n = Q_atoi (Cmd_Argv (i+1)); | |
if (n == 0) | |
Con_Printf("Invalid baud rate specified\n"); | |
else | |
p->baudBits = 115200 / n; | |
} | |
if (Cmd_CheckParm ("8250")) | |
{ | |
if (p->enabled) | |
{ | |
Con_Printf("COM port must be disabled to change uart\n"); | |
return; | |
} | |
p->uartType = UART_8250; | |
} | |
if (Cmd_CheckParm ("16550")) | |
{ | |
if (p->enabled) | |
{ | |
Con_Printf("COM port must be disabled to change uart\n"); | |
return; | |
} | |
p->uartType = UART_16550; | |
} | |
if (Cmd_CheckParm ("auto")) | |
{ | |
if (p->enabled) | |
{ | |
Con_Printf("COM port must be disabled to change uart\n"); | |
return; | |
} | |
p->uartType = UART_AUTO; | |
} | |
if (Cmd_CheckParm ("pulse")) | |
p->dialType = 'P'; | |
if (Cmd_CheckParm ("tone")) | |
p->dialType = 'T'; | |
if (Cmd_CheckParm ("direct")) | |
p->useModem = false; | |
if (Cmd_CheckParm ("modem")) | |
p->useModem = true; | |
if ((i = Cmd_CheckParm ("clear")) != 0) | |
{ | |
Q_strncpy (p->clear, Cmd_Argv (i+1), 16); | |
} | |
if ((i = Cmd_CheckParm ("startup")) != 0) | |
{ | |
Q_strncpy (p->startup, Cmd_Argv (i+1), 32); | |
p->modemInitialized = false; | |
} | |
if ((i = Cmd_CheckParm ("shutdown")) != 0) | |
{ | |
Q_strncpy (p->shutdown, Cmd_Argv (i+1), 16); | |
} | |
if (Cmd_CheckParm ("-cts")) | |
{ | |
p->modemStatusIgnore |= MSR_CTS; | |
p->modemStatus |= MSR_CTS; | |
} | |
if (Cmd_CheckParm ("+cts")) | |
{ | |
p->modemStatusIgnore &= (~MSR_CTS); | |
p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; | |
} | |
if (Cmd_CheckParm ("-dsr")) | |
{ | |
p->modemStatusIgnore |= MSR_DSR; | |
p->modemStatus |= MSR_DSR; | |
} | |
if (Cmd_CheckParm ("+dsr")) | |
{ | |
p->modemStatusIgnore &= (~MSR_DSR); | |
p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; | |
} | |
if (Cmd_CheckParm ("-cd")) | |
{ | |
p->modemStatusIgnore |= MSR_CD; | |
p->modemStatus |= MSR_CD; | |
} | |
if (Cmd_CheckParm ("+cd")) | |
{ | |
p->modemStatusIgnore &= (~MSR_CD); | |
p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; | |
} | |
if (Cmd_CheckParm ("enable")) | |
{ | |
if (!p->enabled) | |
ComPort_Enable(p); | |
if (p->useModem && !p->modemInitialized) | |
Modem_Init (p); | |
} | |
} | |
int TTY_Init(void) | |
{ | |
int n; | |
ComPort *p; | |
for (n = 0; n < NUM_COM_PORTS; n++) | |
{ | |
p = (ComPort *)Hunk_AllocName(sizeof(ComPort), "comport"); | |
if (p == NULL) | |
Sys_Error("Hunk alloc failed for com port\n"); | |
p->next = portList; | |
portList = p; | |
handleToPort[n] = p; | |
p->portNumber = n; | |
p->dialType = 'T'; | |
sprintf(p->name, "com%u", n+1); | |
Cmd_AddCommand (p->name, Com_f); | |
ResetComPortConfig (p); | |
} | |
GetComPortConfig = TTY_GetComPortConfig; | |
SetComPortConfig = TTY_SetComPortConfig; | |
GetModemConfig = TTY_GetModemConfig; | |
SetModemConfig = TTY_SetModemConfig; | |
return 0; | |
} | |
void TTY_Shutdown(void) | |
{ | |
int n; | |
ComPort *p; | |
for (n = 0; n < NUM_COM_PORTS; n++) | |
{ | |
p = handleToPort[n]; | |
if (p->enabled) | |
{ | |
while (p->modemConnected) | |
NET_Poll(); | |
ComPort_Disable (p); | |
} | |
} | |
} | |
static int Modem_Command(ComPort *p, char *commandString) | |
{ | |
byte b; | |
if (CheckStatus (p)) | |
return -1; | |
disable(); | |
p->outputQueue.head = p->outputQueue.tail = 0; | |
p->inputQueue.head = p->inputQueue.tail = 0; | |
enable(); | |
p->bufferUsed = 0; | |
while (*commandString) | |
ENQUEUE (p->outputQueue, *commandString++); | |
ENQUEUE (p->outputQueue, '\r'); | |
// get the transmit rolling | |
DEQUEUE (p->outputQueue, b); | |
outportb(p->uart, b); | |
return 0; | |
} | |
static char *Modem_Response(ComPort *p) | |
{ | |
byte b; | |
if (CheckStatus (p)) | |
return NULL; | |
while (! EMPTY(p->inputQueue)) | |
{ | |
DEQUEUE (p->inputQueue, b); | |
if (p->bufferUsed == (sizeof(p->buffer) - 1)) | |
b = '\r'; | |
if (b == '\r' && p->bufferUsed) | |
{ | |
p->buffer[p->bufferUsed] = 0; | |
Con_Printf("%s\n", p->buffer); | |
SCR_UpdateScreen (); | |
p->bufferUsed = 0; | |
return p->buffer; | |
} | |
if (b < ' ' || b > 'z') | |
continue; | |
p->buffer[p->bufferUsed] = b; | |
p->bufferUsed++; | |
} | |
return NULL; | |
} | |
static void Modem_Hangup2(ComPort *p); | |
static void Modem_Hangup3(ComPort *p); | |
static void Modem_Hangup4(ComPort *p); | |
static void Modem_Hangup(ComPort *p) | |
{ | |
Con_Printf("Hanging up modem...\n"); | |
disable(); | |
p->modemRang = false; | |
p->outputQueue.head = p->outputQueue.tail = 0; | |
p->inputQueue.head = p->inputQueue.tail = 0; | |
outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR); | |
enable(); | |
p->poll.procedure = Modem_Hangup2; | |
p->poll.arg = p; | |
SchedulePollProcedure(&p->poll, 1.5); | |
} | |
static void Modem_Hangup2(ComPort *p) | |
{ | |
outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR); | |
Modem_Command(p, "+++"); | |
p->poll.procedure = Modem_Hangup3; | |
SchedulePollProcedure(&p->poll, 1.5); | |
} | |
static void Modem_Hangup3(ComPort *p) | |
{ | |
Modem_Command(p, p->shutdown); | |
p->poll.procedure = Modem_Hangup4; | |
SchedulePollProcedure(&p->poll, 1.5); | |
} | |
static void Modem_Hangup4(ComPort *p) | |
{ | |
Modem_Response(p); | |
Con_Printf("Hangup complete\n"); | |
p->modemConnected = false; | |
} |