Add patch 1151453 from h-a-l-9000. Improves softmodem and directserial supports. Adds a dummy serial class as well and makes the serial ports runtime changable
Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@2257
This commit is contained in:
parent
4b6fd3c481
commit
a5252330f7
19 changed files with 3329 additions and 1484 deletions
|
@ -1,5 +1,7 @@
|
|||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
SUBDIRS = serialport
|
||||
|
||||
EXTRA_DIST = fmopl.c fmopl.h ymf262.h ymf262.c
|
||||
|
||||
noinst_LIBRARIES = libhardware.a
|
||||
|
@ -8,9 +10,6 @@ libhardware_a_SOURCES = adlib.cpp dma.cpp gameblaster.cpp hardware.cpp iohandler
|
|||
memory.cpp mixer.cpp pcspeaker.cpp pic.cpp sblaster.cpp tandy_sound.cpp timer.cpp \
|
||||
vga.cpp vga_attr.cpp vga_crtc.cpp vga_dac.cpp vga_draw.cpp vga_gfx.cpp vga_other.cpp \
|
||||
vga_memory.cpp vga_misc.cpp vga_seq.cpp vga_xga.cpp cmos.cpp disney.cpp \
|
||||
gus.cpp mpu401.cpp serialport.cpp softmodem.cpp ipx.cpp ipxserver.cpp \
|
||||
directserial_win32.cpp
|
||||
|
||||
|
||||
gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp
|
||||
|
||||
|
||||
|
|
|
@ -1,198 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* $Id: directserial_win32.cpp,v 1.5 2005-02-10 10:21:08 qbix79 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
|
||||
/* Windows version */
|
||||
#if defined (WIN32)
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "setup.h"
|
||||
#include "serialport.h"
|
||||
|
||||
// Win32 related headers
|
||||
#include <windows.h>
|
||||
|
||||
/* This is a serial passthrough class. Its amazingly simple to */
|
||||
/* write now that the serial ports themselves were abstracted out */
|
||||
|
||||
|
||||
class CDirectSerial : public CSerial {
|
||||
public:
|
||||
HANDLE hCom;
|
||||
DCB dcb;
|
||||
BOOL fSuccess;
|
||||
|
||||
CDirectSerial(char * realPort, Bit16u baseAddr, Bit8u initIrq, Bit32u initBps, Bit16u bytesize, char *parity, Bit16u stopbits ) : CSerial(baseAddr, initIrq, initBps) {
|
||||
LOG_MSG("Opening Windows serial port");
|
||||
hCom = CreateFile(realPort, GENERIC_READ | GENERIC_WRITE,
|
||||
0, // must be opened with exclusive-access
|
||||
NULL, // no security attributes
|
||||
OPEN_EXISTING, // must use OPEN_EXISTING
|
||||
0, // non overlapped I/O
|
||||
NULL // hTemplate must be NULL for comm devices
|
||||
);
|
||||
|
||||
if (hCom == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
LOG_MSG("CreateFile failed with error %d.\n", GetLastError());
|
||||
hCom = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
fSuccess = GetCommState(hCom, &dcb);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
// Handle the error.
|
||||
LOG_MSG("GetCommState failed with error %d.\n", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
dcb.BaudRate = initBps; // set the baud rate
|
||||
dcb.ByteSize = bytesize; // data size, xmit, and rcv
|
||||
if(parity[0] == 'N')
|
||||
dcb.Parity = NOPARITY; // no parity bit
|
||||
if(parity[1] == 'E')
|
||||
dcb.Parity = EVENPARITY; // even parity bit
|
||||
if(parity[2] == 'O')
|
||||
dcb.Parity = ODDPARITY; // odd parity bit
|
||||
|
||||
|
||||
if(stopbits == 1)
|
||||
dcb.StopBits = ONESTOPBIT; // one stop bit
|
||||
if(stopbits == 2)
|
||||
dcb.StopBits = TWOSTOPBITS; // two stop bits
|
||||
|
||||
fSuccess = SetCommState(hCom, &dcb);
|
||||
|
||||
// Configure timeouts to effectively use polling
|
||||
COMMTIMEOUTS ct;
|
||||
ct.ReadIntervalTimeout = MAXDWORD;
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.ReadTotalTimeoutMultiplier = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts(hCom, &ct);
|
||||
|
||||
}
|
||||
|
||||
~CDirectSerial() {
|
||||
if(hCom != INVALID_HANDLE_VALUE) CloseHandle(hCom);
|
||||
}
|
||||
|
||||
bool CanRecv(void) { return true; }
|
||||
bool CanSend(void) { return true; }
|
||||
|
||||
void Send(Bit8u val) { tqueue->addb(val); }
|
||||
|
||||
Bit8u Recv(Bit8u val) { return rqueue->getb(); }
|
||||
|
||||
void updatestatus(void) {
|
||||
Bit8u ms=0;
|
||||
DWORD stat = 0;
|
||||
GetCommModemStatus(hCom, &stat);
|
||||
|
||||
//Check for data carrier
|
||||
if(stat & MS_RLSD_ON) ms|=MS_DCD;
|
||||
if (stat & MS_RING_ON) ms|=MS_RI;
|
||||
if (stat & MS_DSR_ON) ms|=MS_DSR;
|
||||
if (stat & MS_CTS_ON) ms|=MS_CTS;
|
||||
SetModemStatus(ms);
|
||||
}
|
||||
|
||||
void Timer(void) {
|
||||
DWORD dwRead;
|
||||
Bit8u chRead;
|
||||
|
||||
if (ReadFile(hCom, &chRead, 1, &dwRead, NULL)) {
|
||||
if(dwRead != 0) {
|
||||
if(!rqueue->isFull()) rqueue->addb(chRead);
|
||||
}
|
||||
}
|
||||
|
||||
updatestatus();
|
||||
|
||||
Bit8u txval;
|
||||
|
||||
Bitu tx_size=tqueue->inuse();
|
||||
while (tx_size--) {
|
||||
txval = tqueue->getb();
|
||||
DWORD bytesWritten;
|
||||
BOOL result;
|
||||
result = WriteFile(hCom, &txval, 1, &bytesWritten, NULL);
|
||||
if (!result)
|
||||
{
|
||||
// Handle the error.
|
||||
LOG_MSG("WriteFile failed with error %d.\n", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CDirectSerial *cds;
|
||||
|
||||
void DIRECTSERIAL_Init(Section* sec) {
|
||||
|
||||
unsigned long args = 1;
|
||||
Section_prop * section=static_cast<Section_prop *>(sec);
|
||||
|
||||
|
||||
if(!section->Get_bool("directserial")) return;
|
||||
|
||||
Bit16u comport = section->Get_int("comport");
|
||||
Bit32u bps = section->Get_int("defaultbps");
|
||||
switch (comport) {
|
||||
case 1:
|
||||
cds = new CDirectSerial((char *)section->Get_string("realport"), 0x3f0, 4, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit"));
|
||||
break;
|
||||
case 2:
|
||||
cds = new CDirectSerial((char *)section->Get_string("realport"), 0x2f0, 3, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit"));
|
||||
break;
|
||||
case 3:
|
||||
cds = new CDirectSerial((char *)section->Get_string("realport"), 0x3e0, 4, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit"));
|
||||
break;
|
||||
case 4:
|
||||
cds = new CDirectSerial((char *)section->Get_string("realport"), 0x2e0, 3, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit"));
|
||||
break;
|
||||
default:
|
||||
cds = new CDirectSerial((char *)section->Get_string("realport"), 0x3f0, 4, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit"));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
seriallist.push_back(cds);
|
||||
}
|
||||
#else /*linux and others oneday maybe */
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <list>
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "inout.h"
|
||||
#include "mixer.h"
|
||||
#include "pic.h"
|
||||
#include "setup.h"
|
||||
#include "timer.h"
|
||||
#include "math.h"
|
||||
#include "regs.h"
|
||||
#include "serialport.h"
|
||||
|
||||
#define SERIALBASERATE 115200
|
||||
#define LOG_UART LOG_MSG
|
||||
|
||||
CSerialList seriallist;
|
||||
|
||||
void CSerial::UpdateBaudrate(void) {
|
||||
Bitu divsize=(divisor_msb << 8) | divisor_lsb;
|
||||
if (divsize) {
|
||||
bps = SERIALBASERATE / divsize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSerial::Timer(void) {
|
||||
dotxint=true;
|
||||
checkint();
|
||||
}
|
||||
|
||||
void CSerial::checkint(void) {
|
||||
/* Check which irq to activate */
|
||||
//TODO Check for line status changes, but we can't get errors anyway
|
||||
//Since we only check once every millisecond, we just ignore the fifosize parameter
|
||||
if ((ier & 0x1) && rqueue->inuse()) {
|
||||
// LOG_MSG("RX IRQ with %d",rqueue->inuse());
|
||||
iir=0x4;
|
||||
} else if ((ier & 0x2) && !tqueue->inuse() && dotxint) {
|
||||
iir=0x2;
|
||||
} else if ((ier & 0x8) && (mstatus & 0x0f)) {
|
||||
iir=0x0;
|
||||
} else {
|
||||
iir=0x1;
|
||||
PIC_DeActivateIRQ(irq);
|
||||
return;
|
||||
}
|
||||
if (mctrl & 0x8) PIC_ActivateIRQ(irq);
|
||||
else PIC_DeActivateIRQ(irq);
|
||||
}
|
||||
|
||||
void CSerial::write_reg(Bitu reg, Bitu val) {
|
||||
// if (port!=9 && port!=8) LOG_MSG("Serial write %X val %x %c",port,val,val);
|
||||
switch(reg) {
|
||||
case 0x8: // Transmit holding buffer + Divisor LSB
|
||||
if (dlab) {
|
||||
divisor_lsb = val;
|
||||
UpdateBaudrate();
|
||||
return;
|
||||
}
|
||||
if (local_loopback) rqueue->addb(val);
|
||||
else tqueue->addb(val);
|
||||
break;
|
||||
case 0x9: // Interrupt enable register + Divisor MSB
|
||||
if (dlab) {
|
||||
divisor_msb = val;
|
||||
UpdateBaudrate();
|
||||
return;
|
||||
} else {
|
||||
ier = val;
|
||||
dotxint=true;
|
||||
}
|
||||
break;
|
||||
case 0xa: // FIFO Control register
|
||||
FIFOenabled = (val & 0x1) > 0;
|
||||
if (val & 0x2) rqueue->clear(); //Clear receiver FIFO
|
||||
if (val & 0x4) tqueue->clear(); //Clear transmit FIFO
|
||||
if (val & 0x8) LOG(LOG_MISC,LOG_WARN)("UART:Enabled DMA mode");
|
||||
switch (val >> 6) {
|
||||
case 0:FIFOsize = 1;break;
|
||||
case 1:FIFOsize = 4;break;
|
||||
case 2:FIFOsize = 8;break;
|
||||
case 3:FIFOsize = 14;break;
|
||||
}
|
||||
break;
|
||||
case 0xb: // Line control register
|
||||
linectrl = val;
|
||||
dlab = (val & 0x80);
|
||||
break;
|
||||
case 0xc: // Modem control register
|
||||
mctrl=val;
|
||||
local_loopback = (val & 0x10);
|
||||
break;
|
||||
case 0xf: // Scratch register
|
||||
scratch = val;
|
||||
break;
|
||||
default:
|
||||
LOG_UART("Modem: Write to 0x%x, with 0x%x '%c'\n", reg,val,val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteSerial(Bitu port,Bitu val,Bitu iolen) {
|
||||
Bitu check=port&~0xf;
|
||||
for (CSerial_it it=seriallist.begin();it!=seriallist.end();it++){
|
||||
CSerial * serial=(*it);
|
||||
if (check==serial->base) {
|
||||
serial->write_reg(port&0xf,val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Bitu ReadSerial(Bitu port,Bitu iolen) {
|
||||
Bitu check=port&~0xf;
|
||||
|
||||
for (CSerial_it it=seriallist.begin();it!=seriallist.end();it++){
|
||||
CSerial * serial=(*it);
|
||||
if (check==serial->base) {
|
||||
|
||||
return serial->read_reg(port&0xf);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SERIAL_Update(void) {
|
||||
for (CSerial_it it=seriallist.begin();it!=seriallist.end();it++){
|
||||
CSerial * serial=(*it);
|
||||
serial->Timer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Bitu CSerial::read_reg(Bitu reg) {
|
||||
Bitu retval;
|
||||
// if (port!=0xd && port!=0xa) LOG_MSG("REad from port %x",reg);
|
||||
switch(reg) {
|
||||
case 0x8: // Receive buffer + Divisor LSB
|
||||
if (dlab) {
|
||||
return divisor_lsb ;
|
||||
} else {
|
||||
retval=rqueue->getb();
|
||||
//LOG_MSG("Received char %x %c",retval,retval);
|
||||
checkint();
|
||||
return retval;
|
||||
}
|
||||
case 0x9: // Interrupt enable register + Divisor MSB
|
||||
if (dlab) {
|
||||
return divisor_msb ;
|
||||
} else {
|
||||
return ier;
|
||||
}
|
||||
case 0xa: // Interrupt identification register
|
||||
retval=iir;
|
||||
if (iir==2) {
|
||||
dotxint=false;
|
||||
iir=1;
|
||||
}
|
||||
// LOG_MSG("Read iir %d after %d",retval,iir);
|
||||
return retval | ((FIFOenabled) ? (3 << 6) : 0);
|
||||
case 0xb: // Line control register
|
||||
return linectrl;
|
||||
case 0xC: // Modem control register
|
||||
return mctrl;
|
||||
case 0xD: // Line status register
|
||||
retval = 0x40;
|
||||
if (!tqueue->inuse()) retval|=0x20;
|
||||
if (rqueue->inuse()) retval|= 1;
|
||||
// LOG_MSG("Read from line status %x",retval);
|
||||
return retval;
|
||||
case 0xE: // modem status register
|
||||
retval=mstatus;
|
||||
mstatus&=0xf0;
|
||||
checkint();
|
||||
return retval;
|
||||
case 0xF: // Scratch register
|
||||
return scratch;
|
||||
default:
|
||||
//LOG_DEBUG("Modem: Read from 0x%x\n", port);
|
||||
break;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
|
||||
void CSerial::SetModemStatus(Bit8u status) {
|
||||
status&=0xf;
|
||||
Bit8u oldstatus=mstatus >> 4;
|
||||
if (oldstatus ^ status ) {
|
||||
mstatus=(mstatus & 0xf) | status << 4;
|
||||
mstatus|=(oldstatus ^ status) & ((status & 0x4) | (0xf-0x4));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSerial::CSerial (Bit16u initbase, Bit8u initirq, Bit32u initbps) {
|
||||
|
||||
Bitu i;
|
||||
Bit16u initdiv;
|
||||
|
||||
base=initbase;
|
||||
irq=initirq;
|
||||
bps=initbps;
|
||||
|
||||
local_loopback = 0;
|
||||
ier = 0;
|
||||
iir = 1;
|
||||
|
||||
FIFOenabled = false;
|
||||
FIFOsize = 1;
|
||||
dlab = 0;
|
||||
mstatus = 0;
|
||||
|
||||
initdiv = SERIALBASERATE / bps;
|
||||
UpdateBaudrate();
|
||||
|
||||
for (i=0;i<=8;i++) {
|
||||
WriteHandler[i].Install(base+i+8,WriteSerial,IO_MB);
|
||||
ReadHandler[i].Install(base+i+8,ReadSerial,IO_MB);
|
||||
}
|
||||
|
||||
rqueue=new CFifo(QUEUE_SIZE);
|
||||
tqueue=new CFifo(QUEUE_SIZE);
|
||||
};
|
||||
|
||||
CSerial::~CSerial(void)
|
||||
{
|
||||
//Remove pointer in list if present
|
||||
for (CSerial_it it=seriallist.begin();it!=seriallist.end();)
|
||||
if(this==*it) it=seriallist.erase(it); else it++;
|
||||
|
||||
delete rqueue;
|
||||
delete tqueue;
|
||||
|
||||
};
|
||||
|
||||
class SERIALPORTS:public Module_base{
|
||||
public:
|
||||
SERIALPORTS(Section* configuration):Module_base(configuration){
|
||||
TIMER_AddTickHandler(&SERIAL_Update);
|
||||
}
|
||||
~SERIALPORTS(){
|
||||
TIMER_DelTickHandler(&SERIAL_Update);
|
||||
}
|
||||
};
|
||||
|
||||
static SERIALPORTS* test;
|
||||
|
||||
void SERIAL_Destroy(Section* sec){
|
||||
delete test;
|
||||
}
|
||||
|
||||
void SERIAL_Init(Section* sec) {
|
||||
test = new SERIALPORTS(sec);
|
||||
sec->AddDestroyFunction(&SERIAL_Destroy,false);
|
||||
}
|
7
src/hardware/serialport/Makefile.am
Normal file
7
src/hardware/serialport/Makefile.am
Normal file
|
@ -0,0 +1,7 @@
|
|||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
noinst_LIBRARIES = libserial.a
|
||||
|
||||
libserial_a_SOURCES = directserial_win32.cpp directserial_win32.h \
|
||||
serialdummy.cpp serialdummy.h serialport.cpp \
|
||||
softmodem.cpp softmodem.h
|
377
src/hardware/serialport/directserial_win32.cpp
Normal file
377
src/hardware/serialport/directserial_win32.cpp
Normal file
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* $Id: directserial_win32.cpp,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
|
||||
/* Windows version */
|
||||
#if defined (WIN32)
|
||||
|
||||
#include "serialport.h"
|
||||
#include "directserial_win32.h"
|
||||
|
||||
// Win32 related headers
|
||||
#include <windows.h>
|
||||
|
||||
/* This is a serial passthrough class. Its amazingly simple to */
|
||||
/* write now that the serial ports themselves were abstracted out */
|
||||
|
||||
CDirectSerial::CDirectSerial (IO_ReadHandler * rh, IO_WriteHandler * wh,
|
||||
TIMER_TickHandler th, Bit16u baseAddr, Bit8u initIrq,
|
||||
Bit32u initBps, Bit8u bytesize, const char *parity,
|
||||
Bit8u stopbits,const char *realPort)
|
||||
:CSerial (rh, wh, th,baseAddr,initIrq, initBps,
|
||||
bytesize, parity,stopbits) {
|
||||
InstallationSuccessful = false;
|
||||
lastChance = 0;
|
||||
LOG_MSG ("Serial port at %x: Opening %s", base, realPort);
|
||||
hCom = CreateFile (realPort, GENERIC_READ | GENERIC_WRITE, 0, // must be opened with exclusive-access
|
||||
NULL, // no security attributes
|
||||
OPEN_EXISTING, // must use OPEN_EXISTING
|
||||
0, // non overlapped I/O
|
||||
NULL // hTemplate must be NULL for comm devices
|
||||
);
|
||||
|
||||
if (hCom == INVALID_HANDLE_VALUE) {
|
||||
int error = GetLastError ();
|
||||
LOG_MSG ("Serial port \"%s\" could not be opened.", realPort);
|
||||
if (error == 2) {
|
||||
LOG_MSG ("The specified port does not exist.");
|
||||
} else if (error == 5) {
|
||||
LOG_MSG ("The specified port is already in use.");
|
||||
} else {
|
||||
LOG_MSG ("Windows error %d occurred.", error);
|
||||
}
|
||||
|
||||
hCom = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
fSuccess = GetCommState (hCom, &dcb);
|
||||
|
||||
if (!fSuccess) {
|
||||
// Handle the error.
|
||||
LOG_MSG ("GetCommState failed with error %d.\n", GetLastError ());
|
||||
hCom = 0;
|
||||
return;
|
||||
}
|
||||
// Configure timeouts to effectively use polling
|
||||
COMMTIMEOUTS ct;
|
||||
ct.ReadIntervalTimeout = MAXDWORD;
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.ReadTotalTimeoutMultiplier = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts (hCom, &ct);
|
||||
|
||||
CSerial::Init_Registers (initBps, bytesize, parity, stopbits);
|
||||
InstallationSuccessful = true;
|
||||
//LOG_MSG("InstSuccess");
|
||||
}
|
||||
|
||||
CDirectSerial::~CDirectSerial () {
|
||||
if (hCom != INVALID_HANDLE_VALUE)
|
||||
CloseHandle (hCom);
|
||||
}
|
||||
|
||||
Bitu lastChance;
|
||||
|
||||
void CDirectSerial::RXBufferEmpty () {
|
||||
DWORD dwRead;
|
||||
DWORD errors;
|
||||
Bit8u chRead;
|
||||
if (lastChance > 0) {
|
||||
receiveByte (ChanceChar);
|
||||
lastChance = 0;
|
||||
} else {
|
||||
// update RX
|
||||
if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) {
|
||||
if (dwRead != 0) {
|
||||
//LOG_MSG("UART 0x%x: RX 0x%x", base,chRead);
|
||||
receiveByte (chRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check for errors
|
||||
if (ClearCommError (hCom, &errors, NULL))
|
||||
if (errors & (CE_BREAK | CE_FRAME | CE_RXPARITY)) {
|
||||
Bit8u errreg = 0;
|
||||
if (errors & CE_BREAK) {
|
||||
LOG_MSG ("Serial port at 0x%x: line error: break received.", base);
|
||||
errreg |= LSR_RX_BREAK_MASK;
|
||||
}
|
||||
if (errors & CE_FRAME) {
|
||||
LOG_MSG ("Serial port at 0x%x: line error: framing error.", base);
|
||||
errreg |= LSR_FRAMING_ERROR_MASK;
|
||||
}
|
||||
if (errors & CE_RXPARITY) {
|
||||
LOG_MSG ("Serial port at 0x%x: line error: parity error.", base);
|
||||
errreg |= LSR_PARITY_ERROR_MASK;
|
||||
}
|
||||
receiveError (errreg);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* updatePortConfig is called when emulated app changes the serial port **/
|
||||
/* parameters baudrate, stopbits, number of databits, parity. **/
|
||||
/*****************************************************************************/
|
||||
void CDirectSerial::updatePortConfig (Bit8u dll, Bit8u dlm, Bit8u lcr) {
|
||||
Bit8u parity = 0;
|
||||
Bit8u bytelength = 0;
|
||||
Bit16u baudrate = 0;
|
||||
|
||||
// baud
|
||||
baudrate = dlm;
|
||||
baudrate = baudrate << 8;
|
||||
baudrate |= dll;
|
||||
if (baudrate <= 0x1)
|
||||
dcb.BaudRate = CBR_115200;
|
||||
else if (baudrate <= 0x2)
|
||||
dcb.BaudRate = CBR_57600;
|
||||
else if (baudrate <= 0x3)
|
||||
dcb.BaudRate = CBR_38400;
|
||||
else if (baudrate <= 0x6)
|
||||
dcb.BaudRate = CBR_19200;
|
||||
else if (baudrate <= 0xc)
|
||||
dcb.BaudRate = CBR_9600;
|
||||
else if (baudrate <= 0x18)
|
||||
dcb.BaudRate = CBR_4800;
|
||||
else if (baudrate <= 0x30)
|
||||
dcb.BaudRate = CBR_2400;
|
||||
else if (baudrate <= 0x60)
|
||||
dcb.BaudRate = CBR_1200;
|
||||
else if (baudrate <= 0xc0)
|
||||
dcb.BaudRate = CBR_600;
|
||||
else if (baudrate <= 0x180)
|
||||
dcb.BaudRate = CBR_300;
|
||||
else if (baudrate <= 0x417)
|
||||
dcb.BaudRate = CBR_110;
|
||||
|
||||
// I read that windows can handle nonstandard baudrates:
|
||||
else
|
||||
dcb.BaudRate = 115200 / baudrate;
|
||||
|
||||
#ifdef SERIALPORT_DEBUGMSG
|
||||
LOG_MSG ("Serial port at %x: new baud rate: %d", base, dcb.BaudRate);
|
||||
#endif
|
||||
|
||||
// byte length
|
||||
bytelength = lcr & 0x3;
|
||||
bytelength += 5;
|
||||
dcb.ByteSize = bytelength;
|
||||
|
||||
// parity
|
||||
parity = lcr & 0x38;
|
||||
parity = parity >> 3;
|
||||
switch (parity) {
|
||||
case 0x1:
|
||||
dcb.Parity = ODDPARITY;
|
||||
break;
|
||||
case 0x3:
|
||||
dcb.Parity = EVENPARITY;
|
||||
break;
|
||||
case 0x5:
|
||||
dcb.Parity = MARKPARITY;
|
||||
break;
|
||||
case 0x7:
|
||||
dcb.Parity = SPACEPARITY;
|
||||
break;
|
||||
default:
|
||||
dcb.Parity = NOPARITY;
|
||||
break;
|
||||
}
|
||||
|
||||
// stopbits
|
||||
if (lcr & 0x4) {
|
||||
if (bytelength == 5)
|
||||
dcb.StopBits = ONE5STOPBITS;
|
||||
else
|
||||
dcb.StopBits = TWOSTOPBITS;
|
||||
} else {
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
}
|
||||
|
||||
if (!SetCommState (hCom, &dcb))
|
||||
LOG_MSG ("Serial port at 0x%x: API did not like the new values.", base);
|
||||
//LOG_MSG("Serial port at 0x%x: Port params changed: %d Baud", base,dcb.BaudRate);
|
||||
}
|
||||
|
||||
void CDirectSerial::updateMSR () {
|
||||
Bit8u newmsr = 0;
|
||||
DWORD dptr = 0;
|
||||
|
||||
if (!GetCommModemStatus (hCom, &dptr)) {
|
||||
#ifdef SERIALPORT_DEBUGMSG
|
||||
LOG_MSG ("Serial port at %x: GetCommModemStatus failed!", base);
|
||||
#endif
|
||||
//return;
|
||||
}
|
||||
if (dptr & MS_CTS_ON)
|
||||
newmsr |= MSR_CTS_MASK;
|
||||
if (dptr & MS_DSR_ON)
|
||||
newmsr |= MSR_DSR_MASK;
|
||||
if (dptr & MS_RING_ON)
|
||||
newmsr |= MSR_RI_MASK;
|
||||
if (dptr & MS_RLSD_ON)
|
||||
newmsr |= MSR_CD_MASK;
|
||||
changeMSR (newmsr);
|
||||
}
|
||||
|
||||
void CDirectSerial::transmitByte (Bit8u val) {
|
||||
// mean bug: with break = 1, WriteFile will never return.
|
||||
if((LCR&LCR_BREAK_MASK) == 0) {
|
||||
|
||||
DWORD bytesWritten = 0;
|
||||
WriteFile (hCom, &val, 1, &bytesWritten, NULL);
|
||||
if (bytesWritten > 0) {
|
||||
ByteTransmitted ();
|
||||
//LOG_MSG("UART 0x%x: TX 0x%x", base,val);
|
||||
} else {
|
||||
LOG_MSG ("UART 0x%x: NO BYTE WRITTEN! PORT HANGS NOW!", base);
|
||||
}
|
||||
} else {
|
||||
// have a delay here, it's the only sense of sending
|
||||
// data with break=1
|
||||
Bitu ticks;
|
||||
Bitu elapsed = 0;
|
||||
ticks = GetTicks();
|
||||
|
||||
while(elapsed < 10) {
|
||||
elapsed = GetTicks() - ticks;
|
||||
}
|
||||
ByteTransmitted();
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* setBreak(val) switches break on or off **/
|
||||
/*****************************************************************************/
|
||||
|
||||
void CDirectSerial::setBreak (bool value) {
|
||||
//#ifdef SERIALPORT_DEBUGMSG
|
||||
//LOG_MSG("UART 0x%x: Break toggeled: %d", base, value);
|
||||
//#endif
|
||||
if (value)
|
||||
SetCommBreak (hCom);
|
||||
else
|
||||
ClearCommBreak (hCom);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* updateModemControlLines(mcr) sets DTR and RTS. **/
|
||||
/*****************************************************************************/
|
||||
void CDirectSerial::updateModemControlLines ( /*Bit8u mcr */ ) {
|
||||
bool change = false;
|
||||
|
||||
/*** DTR ***/
|
||||
if (CSerial::getDTR ()) { // DTR on
|
||||
if (dcb.fDtrControl == DTR_CONTROL_DISABLE) {
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
change = true;
|
||||
}
|
||||
} else {
|
||||
if (dcb.fDtrControl == DTR_CONTROL_ENABLE) {
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
/*** RTS ***/
|
||||
if (CSerial::getRTS ()) { // RTS on
|
||||
if (dcb.fRtsControl == RTS_CONTROL_DISABLE) {
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
change = true;
|
||||
}
|
||||
} else {
|
||||
if (dcb.fRtsControl == RTS_CONTROL_ENABLE) {
|
||||
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
if (change)
|
||||
SetCommState (hCom, &dcb);
|
||||
}
|
||||
|
||||
void CDirectSerial::Timer2(void) {
|
||||
DWORD dwRead = 0;
|
||||
DWORD errors = 0;
|
||||
Bit8u chRead = 0;
|
||||
|
||||
|
||||
if (lastChance == 0) { // lastChance = 0
|
||||
if (CanReceiveByte ()) {
|
||||
if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) {
|
||||
if (dwRead)
|
||||
receiveByte (chRead);
|
||||
}
|
||||
} else {
|
||||
if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) {
|
||||
if (dwRead) {
|
||||
ChanceChar = chRead;
|
||||
lastChance++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (lastChance > 10) {
|
||||
receiveByte (0); // this causes RX Overrun now
|
||||
lastChance = 0;
|
||||
// empty serial buffer
|
||||
dwRead = 1;
|
||||
while (dwRead > 0) { // throw away bytes in buffer
|
||||
ReadFile (hCom, &chRead, 1, &dwRead, NULL);
|
||||
}
|
||||
} else { // lastChance>0 // already one waiting
|
||||
if (CanReceiveByte ()) { // chance used
|
||||
receiveByte (ChanceChar);
|
||||
lastChance = 0;
|
||||
} else
|
||||
lastChance++;
|
||||
}
|
||||
|
||||
// check for errors
|
||||
if (ClearCommError (hCom, &errors, NULL))
|
||||
if (errors & (CE_BREAK | CE_FRAME | CE_RXPARITY)) {
|
||||
Bit8u errreg = 0;
|
||||
|
||||
if (errors & CE_BREAK) {
|
||||
LOG_MSG ("Serial port at 0x%x: line error: break received.", base);
|
||||
errreg |= LSR_RX_BREAK_MASK;
|
||||
}
|
||||
if (errors & CE_FRAME) {
|
||||
LOG_MSG ("Serial port at 0x%x: line error: framing error.", base);
|
||||
errreg |= LSR_FRAMING_ERROR_MASK;
|
||||
}
|
||||
if (errors & CE_RXPARITY) {
|
||||
LOG_MSG ("Serial port at 0x%x: line error: parity error.", base);
|
||||
errreg |= LSR_PARITY_ERROR_MASK;
|
||||
}
|
||||
|
||||
receiveError (errreg);
|
||||
}
|
||||
// update Modem input line states
|
||||
updateMSR ();
|
||||
}
|
||||
|
||||
|
||||
#else /*linux and others oneday maybe */
|
||||
|
||||
#endif
|
||||
#endif
|
84
src/hardware/serialport/directserial_win32.h
Normal file
84
src/hardware/serialport/directserial_win32.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* $Id: directserial_win32.h,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */
|
||||
|
||||
// include guard
|
||||
#ifndef DOSBOX_DIRECTSERIAL_WIN32_H
|
||||
#define DOSBOX_DIRECTSERIAL_WIN32_H
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
#ifdef WIN32
|
||||
|
||||
|
||||
|
||||
#define DIRECTSERIAL_AVAILIBLE
|
||||
#include "serialport.h"
|
||||
#include <windows.h>
|
||||
|
||||
class CDirectSerial : public CSerial {
|
||||
public:
|
||||
HANDLE hCom;
|
||||
DCB dcb;
|
||||
BOOL fSuccess;
|
||||
|
||||
CDirectSerial(
|
||||
IO_ReadHandler* rh,
|
||||
IO_WriteHandler* wh,
|
||||
TIMER_TickHandler th,
|
||||
Bit16u baseAddr,
|
||||
Bit8u initIrq,
|
||||
Bit32u initBps,
|
||||
Bit8u bytesize,
|
||||
const char *parity,
|
||||
Bit8u stopbits,
|
||||
const char * realPort
|
||||
);
|
||||
|
||||
|
||||
~CDirectSerial();
|
||||
|
||||
|
||||
Bitu lastChance; // If there is no space for new
|
||||
// received data, it gets a little chance
|
||||
Bit8u ChanceChar;
|
||||
|
||||
bool CanRecv(void);
|
||||
bool CanSend(void);
|
||||
|
||||
bool InstallationSuccessful; // check after constructing. If
|
||||
// something was wrong, delete it right away.
|
||||
|
||||
void RXBufferEmpty();
|
||||
|
||||
|
||||
void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr);
|
||||
void updateMSR();
|
||||
void transmitByte(Bit8u val);
|
||||
void setBreak(bool value);
|
||||
void updateModemControlLines(/*Bit8u mcr*/);
|
||||
void Timer2(void);
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // WIN32
|
||||
#endif // C_DIRECTSERIAL
|
||||
#endif // include guard
|
85
src/hardware/serialport/serialdummy.cpp
Normal file
85
src/hardware/serialport/serialdummy.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* $Id: serialdummy.cpp,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#include "setup.h"
|
||||
#include "serialdummy.h"
|
||||
#include "serialport.h"
|
||||
|
||||
|
||||
CSerialDummy::CSerialDummy(
|
||||
IO_ReadHandler* rh,
|
||||
IO_WriteHandler* wh,
|
||||
TIMER_TickHandler th,
|
||||
Bit16u baseAddr,
|
||||
Bit8u initIrq,
|
||||
Bit32u initBps,
|
||||
Bit8u bytesize,
|
||||
const char* parity,
|
||||
Bit8u stopbits
|
||||
) : CSerial(
|
||||
rh, wh, th,
|
||||
baseAddr,initIrq,initBps,bytesize,parity,stopbits)
|
||||
{
|
||||
CSerial::Init_Registers(initBps,bytesize,parity,stopbits);
|
||||
}
|
||||
|
||||
CSerialDummy::~CSerialDummy() {
|
||||
}
|
||||
|
||||
void CSerialDummy::RXBufferEmpty() {
|
||||
// no external buffer, not used here
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* updatePortConfig is called when emulated app changes the serial port **/
|
||||
/* parameters baudrate, stopbits, number of databits, parity. **/
|
||||
/*****************************************************************************/
|
||||
void CSerialDummy::updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr) {
|
||||
//LOG_MSG("Serial port at 0x%x: Port params changed: %d Baud", base,dcb.BaudRate);
|
||||
}
|
||||
|
||||
void CSerialDummy::updateMSR() {
|
||||
changeMSR(0);
|
||||
}
|
||||
|
||||
void CSerialDummy::transmitByte(Bit8u val) {
|
||||
ByteTransmitted();
|
||||
//LOG_MSG("UART 0x%x: TX 0x%x", base,val);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* setBreak(val) switches break on or off **/
|
||||
/*****************************************************************************/
|
||||
|
||||
void CSerialDummy::setBreak(bool value) {
|
||||
//LOG_MSG("UART 0x%x: Break toggeled: %d", base, value);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* updateModemControlLines(mcr) sets DTR and RTS. **/
|
||||
/*****************************************************************************/
|
||||
void CSerialDummy::updateModemControlLines(/*Bit8u mcr*/) {
|
||||
}
|
||||
|
||||
void CSerialDummy::Timer2(void) {
|
||||
}
|
||||
|
54
src/hardware/serialport/serialdummy.h
Normal file
54
src/hardware/serialport/serialdummy.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* $Id: serialdummy.h,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */
|
||||
|
||||
#ifndef INCLUDEGUARD_SERIALDUMMY_H
|
||||
#define INCLUDEGUARD_SERIALDUMMY_H
|
||||
|
||||
#include "serialport.h"
|
||||
|
||||
class CSerialDummy : public CSerial {
|
||||
public:
|
||||
|
||||
CSerialDummy(
|
||||
IO_ReadHandler* rh,
|
||||
IO_WriteHandler* wh,
|
||||
TIMER_TickHandler th,
|
||||
Bit16u baseAddr,
|
||||
Bit8u initIrq,
|
||||
Bit32u initBps,
|
||||
Bit8u bytesize,
|
||||
const char* parity,
|
||||
Bit8u stopbits
|
||||
);
|
||||
|
||||
|
||||
~CSerialDummy();
|
||||
bool CanRecv(void);
|
||||
bool CanSend(void);
|
||||
void RXBufferEmpty();
|
||||
void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr);
|
||||
void updateMSR();
|
||||
void transmitByte(Bit8u val);
|
||||
void setBreak(bool value);
|
||||
void updateModemControlLines(/*Bit8u mcr*/);
|
||||
void Timer2(void);
|
||||
};
|
||||
|
||||
#endif // INCLUDEGUARD
|
1089
src/hardware/serialport/serialport.cpp
Normal file
1089
src/hardware/serialport/serialport.cpp
Normal file
File diff suppressed because it is too large
Load diff
826
src/hardware/serialport/softmodem.cpp
Normal file
826
src/hardware/serialport/softmodem.cpp
Normal file
|
@ -0,0 +1,826 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* $Id: softmodem.cpp,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_MODEM
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "SDL_net.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "timer.h"
|
||||
#include "serialport.h"
|
||||
#include "softmodem.h"
|
||||
|
||||
//#include "mixer.h"
|
||||
|
||||
|
||||
CSerialModem::CSerialModem(
|
||||
IO_ReadHandler* rh,
|
||||
IO_WriteHandler* wh,
|
||||
TIMER_TickHandler th,
|
||||
Bit16u baseAddr,
|
||||
Bit8u initIrq,
|
||||
Bit32u initBps,
|
||||
Bit8u bytesize,
|
||||
const char* parity,
|
||||
Bit8u stopbits,
|
||||
const char *remotestr,
|
||||
Bit16u lport)
|
||||
: CSerial(rh, wh, th,
|
||||
baseAddr, initIrq, initBps, bytesize, parity, stopbits) {
|
||||
socket=0;
|
||||
incomingsocket=0;
|
||||
|
||||
if(!SDLNetInited) {
|
||||
if(SDLNet_Init()==-1) {
|
||||
LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
|
||||
return;
|
||||
}
|
||||
SDLNetInited = true;
|
||||
}
|
||||
rqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE);
|
||||
tqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE);
|
||||
|
||||
//cmdpos = 0;
|
||||
|
||||
//plusinc = 0;
|
||||
//incomingsocket = 0;
|
||||
// answermode = false;
|
||||
//memset(®,0,sizeof(reg));
|
||||
//cmdpause = 0;
|
||||
//echo = true;
|
||||
//doresponse = true;
|
||||
//numericresponse = false;
|
||||
|
||||
/* Default to direct null modem connection. Telnet mode interprets IAC codes */
|
||||
telnetmode = false;
|
||||
|
||||
/* Initialize the sockets and setup the listening port */
|
||||
socketset = SDLNet_AllocSocketSet(1);
|
||||
listensocketset = SDLNet_AllocSocketSet(1);
|
||||
if (!socketset || !listensocketset) {
|
||||
LOG_MSG("MODEM:Can't open socketset:%s",SDLNet_GetError());
|
||||
//TODO Should probably just exit
|
||||
return;
|
||||
}
|
||||
socket=0;
|
||||
listenport=lport;
|
||||
if (listenport) {
|
||||
IPaddress listen_ip;
|
||||
SDLNet_ResolveHost(&listen_ip, NULL, listenport);
|
||||
listensocket=SDLNet_TCP_Open(&listen_ip);
|
||||
if (!listensocket) LOG_MSG("MODEM:Can't open listen port: %s",SDLNet_GetError());
|
||||
|
||||
else LOG_MSG("MODEM: Port listener installed at port %d",listenport);
|
||||
|
||||
}
|
||||
else listensocket=0;
|
||||
|
||||
// TODO: Fix dialtones if requested
|
||||
//mhd.chan=MIXER_AddChannel((MIXER_MixHandler)this->MODEM_CallBack,8000,"MODEM");
|
||||
//MIXER_Enable(mhd.chan,false);
|
||||
//MIXER_SetMode(mhd.chan,MIXER_16MONO);
|
||||
|
||||
Reset();
|
||||
//EnterIdleState();
|
||||
CSerial::Init_Registers(initBps,bytesize,parity,stopbits);
|
||||
}
|
||||
|
||||
CSerialModem::~CSerialModem() {
|
||||
if(socket) {
|
||||
SDLNet_TCP_DelSocket(socketset,socket);
|
||||
SDLNet_TCP_Close(socket);
|
||||
}
|
||||
|
||||
if(listensocket) SDLNet_TCP_Close(listensocket);
|
||||
if(socketset) SDLNet_FreeSocketSet(socketset);
|
||||
|
||||
delete rqueue;
|
||||
delete tqueue;
|
||||
}
|
||||
|
||||
void CSerialModem::SendLine(const char *line) {
|
||||
rqueue->addb(0xd);
|
||||
rqueue->addb(0xa);
|
||||
rqueue->adds((Bit8u *)line,strlen(line));
|
||||
rqueue->addb(0xd);
|
||||
rqueue->addb(0xa);
|
||||
}
|
||||
|
||||
// only for numbers < 1000...
|
||||
void CSerialModem::SendNumber(Bitu val) {
|
||||
rqueue->addb(0xd);
|
||||
rqueue->addb(0xa);
|
||||
|
||||
rqueue->addb(val/100+'0');
|
||||
val = val%100;
|
||||
rqueue->addb(val/10+'0');
|
||||
val = val%10;
|
||||
rqueue->addb(val+'0');
|
||||
|
||||
rqueue->addb(0xd);
|
||||
rqueue->addb(0xa);
|
||||
}
|
||||
|
||||
void CSerialModem::SendRes(ResTypes response) {
|
||||
char * string;Bitu /*char **/ code;
|
||||
switch (response)
|
||||
{
|
||||
case ResNONE: return;
|
||||
case ResOK: string="OK"; code=0; break;
|
||||
case ResERROR: string="ERROR"; code=4; break;
|
||||
case ResRING: string="RING"; code=2; break;
|
||||
case ResNODIALTONE: string="NO DIALTONE"; code=6; break;
|
||||
case ResNOCARRIER: string="NO CARRIER" ;code=3; break;
|
||||
case ResCONNECT: string="CONNECT 57600"; code=1; break;
|
||||
}
|
||||
|
||||
if(doresponse!=1) {
|
||||
if(doresponse==2 && (response==ResRING ||
|
||||
response == ResCONNECT || response==ResNOCARRIER)) return;
|
||||
if(numericresponse)
|
||||
//{
|
||||
SendNumber(code);
|
||||
// rqueue->addb(*code+'0');
|
||||
// rqueue->addb(0xd);rqueue->addb(0xa);
|
||||
//}
|
||||
else
|
||||
//{
|
||||
SendLine(string);
|
||||
//rqueue->addb(0xd);rqueue->addb(0xa);
|
||||
//rqueue->adds((Bit8u *)string,strlen(string));
|
||||
//rqueue->addb(0xd);rqueue->addb(0xa);
|
||||
//}
|
||||
//if(CSerial::CanReceiveByte()) // very fast response
|
||||
// if(rqueue->inuse() && CSerial::getRTS())
|
||||
// { Bit8u rbyte =rqueue->getb();
|
||||
// CSerial::receiveByte(rbyte);
|
||||
// LOG_MSG("Modem: sending byte %2x back to UART2",rbyte);
|
||||
// }
|
||||
|
||||
LOG_MSG("Modem response: %s", string);
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialModem::openConnection(void) {
|
||||
if (socket) {
|
||||
LOG_MSG("Huh? already connected");
|
||||
SDLNet_TCP_DelSocket(socketset,socket);
|
||||
SDLNet_TCP_Close(socket);
|
||||
}
|
||||
socket = SDLNet_TCP_Open(&openip);
|
||||
//if (!socket)
|
||||
|
||||
}
|
||||
|
||||
bool CSerialModem::Dial(char * host) {
|
||||
char* helper;
|
||||
// scan for and remove spaces; weird bug: with leading spaces in the string,
|
||||
// SDLNet_ResolveHost will return no error but not work anyway (win)
|
||||
while(host[0]==' ') host++;
|
||||
helper=host;
|
||||
helper+=strlen(host);
|
||||
while(helper[0]==' ') {
|
||||
helper[0]=0;
|
||||
helper--;
|
||||
}
|
||||
|
||||
/* Scan host for port */
|
||||
Bit16u port;
|
||||
char * hasport=strrchr(host,':');
|
||||
if (hasport) {
|
||||
*hasport++=0;
|
||||
port=(Bit16u)atoi(hasport);
|
||||
}
|
||||
else port=MODEM_DEFAULT_PORT;
|
||||
/* Resolve host we're gonna dial */
|
||||
LOG_MSG("Connecting to host %s port %d",host,port);
|
||||
if (!SDLNet_ResolveHost(&openip,host,port)) {
|
||||
openConnection();
|
||||
EnterConnectedState();
|
||||
return true;
|
||||
} else {
|
||||
LOG_MSG("Failed to resolve host %s: %s",host,SDLNet_GetError());
|
||||
SendRes(ResNODIALTONE);
|
||||
EnterIdleState();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialModem::AcceptIncomingCall(void) {
|
||||
// assert(!socket);
|
||||
socket=incomingsocket;
|
||||
SDLNet_TCP_AddSocket(socketset,socket);
|
||||
incomingsocket = 0;
|
||||
EnterConnectedState();
|
||||
}
|
||||
|
||||
Bitu CSerialModem::ScanNumber(char * & scan) {
|
||||
Bitu ret=0;
|
||||
while (char c=*scan) {
|
||||
if (c>='0' && c<='9') {
|
||||
ret*=10;
|
||||
ret+=c-'0';
|
||||
scan++;
|
||||
} else break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CSerialModem::Reset(){
|
||||
EnterIdleState();
|
||||
cmdpos = 0;
|
||||
cmdbuf[0]=0;
|
||||
oldDTRstate = getDTR();
|
||||
|
||||
plusinc = 0;
|
||||
incomingsocket = 0;
|
||||
//answermode = false; // no autoanswer
|
||||
memset(®,0,sizeof(reg));
|
||||
reg[MREG_AUTOANSWER_COUNT]=0; // no autoanswer
|
||||
reg[MREG_RING_COUNT] = 1;
|
||||
reg[MREG_ESCAPE_CHAR]='+';
|
||||
reg[MREG_CR_CHAR]='\r';
|
||||
reg[MREG_LF_CHAR]='\n';
|
||||
reg[MREG_BACKSPACE_CHAR]='\b';
|
||||
|
||||
cmdpause = 0;
|
||||
echo = true;
|
||||
doresponse = 0;
|
||||
numericresponse = false;
|
||||
|
||||
/* Default to direct null modem connection. Telnet mode interprets IAC codes */
|
||||
telnetmode = false;
|
||||
}
|
||||
|
||||
void CSerialModem::EnterIdleState(void){
|
||||
connected=false;
|
||||
ringing=false;
|
||||
txbufferfull=false;
|
||||
|
||||
if(socket) { // clear current socket
|
||||
SDLNet_TCP_DelSocket(socketset,socket);
|
||||
SDLNet_TCP_Close(socket);
|
||||
socket=0;
|
||||
}
|
||||
if(incomingsocket) { // clear current incoming socket
|
||||
SDLNet_TCP_DelSocket(socketset,incomingsocket);
|
||||
SDLNet_TCP_Close(incomingsocket);
|
||||
}
|
||||
// get rid of everything
|
||||
while(incomingsocket=SDLNet_TCP_Accept(listensocket)) {
|
||||
SDLNet_TCP_DelSocket(socketset,incomingsocket);
|
||||
SDLNet_TCP_Close(incomingsocket);
|
||||
}
|
||||
incomingsocket=0;
|
||||
|
||||
commandmode = true;
|
||||
CSerial::setCD(false);
|
||||
CSerial::setRI(false);
|
||||
tqueue->clear();
|
||||
}
|
||||
|
||||
void CSerialModem::EnterConnectedState(void) {
|
||||
if(socket) {
|
||||
SDLNet_TCP_AddSocket(socketset,socket);
|
||||
SendRes(ResCONNECT);
|
||||
commandmode = false;
|
||||
memset(&telClient, 0, sizeof(telClient));
|
||||
connected = true;
|
||||
ringing = false;
|
||||
CSerial::setCD(true);
|
||||
CSerial::setRI(false);
|
||||
} else {
|
||||
SendRes(ResNOCARRIER);
|
||||
EnterIdleState();
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialModem::DoCommand() {
|
||||
cmdbuf[cmdpos] = 0;
|
||||
cmdpos = 0; //Reset for next command
|
||||
upcase(cmdbuf);
|
||||
LOG_MSG("Command sent to modem: ->%s<-\n", cmdbuf);
|
||||
/* Check for empty line, stops dialing and autoanswer */
|
||||
if (!cmdbuf[0]) {
|
||||
reg[0]=0; // autoanswer off
|
||||
//answermode = false;
|
||||
return;//goto ret_none;
|
||||
// }
|
||||
//else {
|
||||
//MIXER_Enable(mhd.chan,false);
|
||||
// dialing = false;
|
||||
// SendRes(ResNOCARRIER);
|
||||
// goto ret_none;
|
||||
// }
|
||||
}
|
||||
/* AT command set interpretation */
|
||||
|
||||
if ((cmdbuf[0] != 'A') || (cmdbuf[1] != 'T')) {
|
||||
SendRes(ResERROR);
|
||||
return;//goto ret_error;
|
||||
}
|
||||
|
||||
if (strstr(cmdbuf,"NET0")) {
|
||||
telnetmode = false;
|
||||
SendRes(ResOK);
|
||||
return;
|
||||
}
|
||||
else if (strstr(cmdbuf,"NET1")) {
|
||||
telnetmode = true;
|
||||
SendRes(ResOK);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for dial command */
|
||||
if(strncmp(cmdbuf,"ATD3",3)==0) {
|
||||
char * foundstr=&cmdbuf[3];
|
||||
if (*foundstr=='T' || *foundstr=='P') foundstr++;
|
||||
/* Small protection against empty line */
|
||||
if (!foundstr[0]) {
|
||||
SendRes(ResERROR);//goto ret_error;
|
||||
return;
|
||||
}
|
||||
if (strlen(foundstr) >= 12) {
|
||||
// Check if supplied parameter only consists of digits
|
||||
bool isNum = true;
|
||||
for (Bitu i=0; i<strlen(foundstr); i++)
|
||||
if (foundstr[i] < '0' || foundstr[i] > '9')
|
||||
isNum = false;
|
||||
if (isNum) {
|
||||
// Parameter is a number with at least 12 digits => this cannot be a valid IP/name
|
||||
// Transform by adding dots
|
||||
char buffer[128];
|
||||
Bitu j = 0;
|
||||
for (Bitu i=0; i<strlen(foundstr); i++) {
|
||||
buffer[j++] = foundstr[i];
|
||||
// Add a dot after the third, sixth and ninth number
|
||||
if (i == 2 || i == 5 || i == 8)
|
||||
buffer[j++] = '.';
|
||||
// If the string is longer than 12 digits, interpret the rest as port
|
||||
if (i == 11 && strlen(foundstr)>12)
|
||||
buffer[j++] = ':';
|
||||
}
|
||||
buffer[j] = 0;
|
||||
foundstr = buffer;
|
||||
}
|
||||
}
|
||||
Dial(foundstr);
|
||||
return;//goto ret_none;
|
||||
}
|
||||
char * scanbuf;
|
||||
scanbuf=&cmdbuf[2];
|
||||
char chr;
|
||||
Bitu num;
|
||||
while (chr=*scanbuf++) {
|
||||
switch (chr) {
|
||||
case 'I': //Some strings about firmware
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 3:SendLine("DosBox Emulated Modem Firmware V1.00");break;
|
||||
case 4:SendLine("Modem compiled for DosBox version " VERSION);break;
|
||||
};break;
|
||||
case 'E': //Echo on/off
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 0:echo = false;break;
|
||||
case 1:echo = true;break;
|
||||
};break;
|
||||
case 'V':
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 0:numericresponse = true;break;
|
||||
case 1:numericresponse = false;break;
|
||||
};break;
|
||||
case 'H': //Hang up
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 0:
|
||||
if (connected) {
|
||||
SendRes(ResNOCARRIER);
|
||||
EnterIdleState();
|
||||
return;//goto ret_none;
|
||||
}
|
||||
//Else return ok
|
||||
};break;
|
||||
case 'O': //Return to data mode
|
||||
switch (num=ScanNumber(scanbuf))
|
||||
{
|
||||
case 0:
|
||||
if (socket) {
|
||||
commandmode = false;
|
||||
return;//goto ret_none;
|
||||
} else {
|
||||
SendRes(ResERROR);
|
||||
return;//goto ret_none;
|
||||
}
|
||||
};break;
|
||||
case 'T': //Tone Dial
|
||||
case 'P': //Pulse Dial
|
||||
break;
|
||||
case 'M': //Monitor
|
||||
case 'L': //Volume
|
||||
ScanNumber(scanbuf);
|
||||
break;
|
||||
case 'A': //Answer call
|
||||
if (incomingsocket) {
|
||||
AcceptIncomingCall();
|
||||
} else {
|
||||
SendRes(ResERROR);
|
||||
return;//goto ret_none;
|
||||
}
|
||||
return;//goto ret_none;
|
||||
case 'Z': //Reset and load profiles
|
||||
{
|
||||
// scan the number away, if any
|
||||
ScanNumber(scanbuf);
|
||||
if (socket) SendRes(ResNOCARRIER);
|
||||
Reset();
|
||||
break;
|
||||
}
|
||||
case ' ': //Space just skip
|
||||
break;
|
||||
case 'Q': // Response options
|
||||
{ // 0 = all on, 1 = all off,
|
||||
// 2 = no ring and no connect/carrier in answermode
|
||||
Bitu val = ScanNumber(scanbuf);
|
||||
if(!(val>2)) {
|
||||
doresponse=val;
|
||||
break;
|
||||
} else {
|
||||
SendRes(ResERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
case 'S': //Registers
|
||||
{
|
||||
Bitu index=ScanNumber(scanbuf);
|
||||
if(index>=SREGS) {
|
||||
SendRes(ResERROR);
|
||||
return; //goto ret_none;
|
||||
}
|
||||
|
||||
while(scanbuf[0]==' ') scanbuf++; // skip spaces
|
||||
|
||||
if(scanbuf[0]=='=') { // set register
|
||||
scanbuf++;
|
||||
while(scanbuf[0]==' ') scanbuf++; // skip spaces
|
||||
Bitu val = ScanNumber(scanbuf);
|
||||
reg[index]=val;
|
||||
break;
|
||||
}
|
||||
else if(scanbuf[0]=='?') { // get register
|
||||
SendNumber(reg[index]);
|
||||
scanbuf++;
|
||||
break;
|
||||
}
|
||||
//else LOG_MSG("print reg %d with %d",index,reg[index]);
|
||||
}
|
||||
break;
|
||||
case '&':
|
||||
{
|
||||
if(scanbuf[0]!=0) {
|
||||
char ch = scanbuf[0];
|
||||
//switch(scanbuf[0]) Maybe You want to implement it?
|
||||
scanbuf++;
|
||||
LOG_MSG("Modem: Unhandled command: &%c%d",ch,ScanNumber(scanbuf));
|
||||
} else {
|
||||
SendRes(ResERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_MSG("Modem: Unhandled command: %c%d",chr,ScanNumber(scanbuf));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
}
|
||||
|
||||
if (strstr(mhd.cmdbuf,"NET0"))
|
||||
{
|
||||
telnetmode = false;
|
||||
}
|
||||
if (strstr(mhd.cmdbuf,"NET1"))
|
||||
{
|
||||
telnetmode = true;
|
||||
}
|
||||
#endif*/
|
||||
//ret_ok:
|
||||
SendRes(ResOK);
|
||||
return;
|
||||
//ret_error:
|
||||
//SendRes(ResERROR);
|
||||
//ret_none:
|
||||
// return;
|
||||
|
||||
}
|
||||
|
||||
void CSerialModem::TelnetEmulation(Bit8u * data, Bitu size) {
|
||||
Bitu i;
|
||||
Bit8u c;
|
||||
for(i=0;i<size;i++) {
|
||||
c = data[i];
|
||||
if(telClient.inIAC) {
|
||||
if(telClient.recCommand) {
|
||||
if((c != 0) && (c != 1) && (c != 3)) {
|
||||
LOG_MSG("MODEM: Unrecognized option %d", c);
|
||||
if(telClient.command>250) {
|
||||
/* Reject anything we don't recognize */
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(c); /* We won't do crap! */
|
||||
}
|
||||
}
|
||||
switch(telClient.command) {
|
||||
case 251: /* Will */
|
||||
if(c == 0) telClient.binary[TEL_SERVER] = true;
|
||||
if(c == 1) telClient.echo[TEL_SERVER] = true;
|
||||
if(c == 3) telClient.supressGA[TEL_SERVER] = true;
|
||||
break;
|
||||
case 252: /* Won't */
|
||||
if(c == 0) telClient.binary[TEL_SERVER] = false;
|
||||
if(c == 1) telClient.echo[TEL_SERVER] = false;
|
||||
if(c == 3) telClient.supressGA[TEL_SERVER] = false;
|
||||
break;
|
||||
case 253: /* Do */
|
||||
if(c == 0) {
|
||||
telClient.binary[TEL_CLIENT] = true;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(251);
|
||||
tqueue->addb(0); /* Will do binary transfer */
|
||||
}
|
||||
if(c == 1) {
|
||||
telClient.echo[TEL_CLIENT] = false;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(1); /* Won't echo (too lazy) */
|
||||
}
|
||||
if(c == 3) {
|
||||
telClient.supressGA[TEL_CLIENT] = true;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(251);
|
||||
tqueue->addb(3); /* Will Suppress GA */
|
||||
}
|
||||
break;
|
||||
case 254: /* Don't */
|
||||
if(c == 0) {
|
||||
telClient.binary[TEL_CLIENT] = false;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(0); /* Won't do binary transfer */
|
||||
}
|
||||
if(c == 1) {
|
||||
telClient.echo[TEL_CLIENT] = false;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(1); /* Won't echo (fine by me) */
|
||||
}
|
||||
if(c == 3) {
|
||||
telClient.supressGA[TEL_CLIENT] = true;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(251);
|
||||
tqueue->addb(3); /* Will Suppress GA (too lazy) */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command);
|
||||
break;
|
||||
}
|
||||
|
||||
telClient.inIAC = false;
|
||||
telClient.recCommand = false;
|
||||
continue;
|
||||
|
||||
} else {
|
||||
if(c==249) {
|
||||
/* Go Ahead received */
|
||||
telClient.inIAC = false;
|
||||
continue;
|
||||
}
|
||||
telClient.command = c;
|
||||
telClient.recCommand = true;
|
||||
|
||||
if((telClient.binary[TEL_SERVER]) && (c == 0xff)) {
|
||||
/* Binary data with value of 255 */
|
||||
telClient.inIAC = false;
|
||||
telClient.recCommand = false;
|
||||
rqueue->addb(0xff);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if(c == 0xff) {
|
||||
telClient.inIAC = true;
|
||||
continue;
|
||||
}
|
||||
rqueue->addb(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialModem::Timer2(void) {
|
||||
int result =0;
|
||||
unsigned long args = 1;
|
||||
bool sendbyte = true;
|
||||
Bitu usesize;
|
||||
Bit8u txval;
|
||||
Bitu txbuffersize =0;
|
||||
Bitu testres = 0;
|
||||
|
||||
// check for bytes to be sent to port
|
||||
if(CSerial::CanReceiveByte())
|
||||
if(rqueue->inuse() && CSerial::getRTS()) {
|
||||
Bit8u rbyte = rqueue->getb();
|
||||
//LOG_MSG("Modem: sending byte %2x back to UART3",rbyte);
|
||||
CSerial::receiveByte(rbyte);
|
||||
}
|
||||
/* Check for eventual break command */
|
||||
if (!commandmode) cmdpause++;
|
||||
/* Handle incoming data from serial port, read as much as available */
|
||||
//Bitu tx_size=tqueue->inuse();
|
||||
//Bitu tx_first = tx_size; // TODO:comment out
|
||||
CSerial::setCTS(true); // buffer will get 'emptier', new data can be received
|
||||
while (/*tx_size--*/tqueue->inuse()) {
|
||||
txval = tqueue->getb();
|
||||
if (commandmode) {
|
||||
if (echo) {
|
||||
rqueue->addb(txval);
|
||||
//LOG_MSG("Echo back to queue: %x",txval);
|
||||
}
|
||||
if (txval==0xa) continue; //Real modem doesn't seem to skip this?
|
||||
else if (txval==0x8 && (cmdpos > 0)) --cmdpos; // backspace
|
||||
else if (txval==0xd) DoCommand(); // return
|
||||
else if (txval != '+') {
|
||||
if(cmdpos<99) {
|
||||
cmdbuf[cmdpos] = txval;
|
||||
cmdpos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {// + character
|
||||
/* 1000 ticks have passed, can check for pause command */
|
||||
if (cmdpause > 1000) {
|
||||
if(txval ==/* '+')*/reg[MREG_ESCAPE_CHAR])
|
||||
{
|
||||
plusinc++;
|
||||
if(plusinc>=3) {
|
||||
LOG_MSG("Modem: Entering command mode(escape sequence)");
|
||||
commandmode = true;
|
||||
SendRes(ResOK);
|
||||
plusinc = 0;
|
||||
}
|
||||
sendbyte=false;
|
||||
} else {
|
||||
plusinc=0;
|
||||
}
|
||||
//If not a special pause command, should go for bigger blocks to send
|
||||
}
|
||||
tmpbuf[txbuffersize] = txval;
|
||||
txbuffersize++;
|
||||
}
|
||||
} // while loop
|
||||
|
||||
if (socket && sendbyte && txbuffersize) {
|
||||
// down here it saves a lot of network traffic
|
||||
SDLNet_TCP_Send(socket, tmpbuf,txbuffersize);
|
||||
//TODO error testing
|
||||
}
|
||||
SDLNet_CheckSockets(socketset,0);
|
||||
/* Handle incoming to the serial port */
|
||||
if(!commandmode && socket) {
|
||||
if(rqueue->left() && SDLNet_SocketReady(socket) /*&& CSerial::getRTS()*/)
|
||||
{
|
||||
usesize = rqueue->left();
|
||||
if (usesize>16) usesize=16;
|
||||
result = SDLNet_TCP_Recv(socket, tmpbuf, usesize);
|
||||
if (result>0) {
|
||||
if(telnetmode) {
|
||||
/* Filter telnet commands */
|
||||
TelnetEmulation(tmpbuf, result);
|
||||
} else {
|
||||
rqueue->adds(tmpbuf,result);
|
||||
}
|
||||
cmdpause = 0;
|
||||
} else {
|
||||
SendRes(ResNOCARRIER);
|
||||
EnterIdleState();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Check for incoming calls */
|
||||
if (!connected && !incomingsocket && listensocket) {
|
||||
incomingsocket = SDLNet_TCP_Accept(listensocket);
|
||||
if (incomingsocket) {
|
||||
SDLNet_TCP_AddSocket(listensocketset, incomingsocket);
|
||||
|
||||
if(!CSerial::getDTR()) {
|
||||
// accept no calls with DTR off; TODO: AT &Dn
|
||||
EnterIdleState();
|
||||
} else {
|
||||
ringing=true;
|
||||
SendRes(ResRING);
|
||||
CSerial::setRI(!CSerial::getRI());
|
||||
//MIXER_Enable(mhd.chan,true);
|
||||
ringtimer = 3000;
|
||||
reg[1] = 0; //Reset ring counter reg
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ringing) {
|
||||
if (ringtimer <= 0) {
|
||||
reg[1]++;
|
||||
if ((reg[0]>0) && (reg[0]>=reg[1])) {
|
||||
AcceptIncomingCall();
|
||||
return;
|
||||
}
|
||||
SendRes(ResRING);
|
||||
CSerial::setRI(!CSerial::getRI());
|
||||
|
||||
//MIXER_Enable(mhd.chan,true);
|
||||
ringtimer = 3000;
|
||||
}
|
||||
--ringtimer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//TODO
|
||||
void CSerialModem::RXBufferEmpty() {
|
||||
// see if rqueue has some more bytes
|
||||
if(rqueue->inuse() && CSerial::getRTS()){
|
||||
Bit8u rbyte = rqueue->getb();
|
||||
//LOG_MSG("Modem: sending byte %2x back to UART1",rbyte);
|
||||
CSerial::receiveByte(rbyte);
|
||||
|
||||
//CSerial::receiveByte(rqueue->getb());
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialModem::transmitByte(Bit8u val) {
|
||||
//LOG_MSG("MODEM: Byte %x to be transmitted",val);
|
||||
if(tqueue->left()) {
|
||||
tqueue->addb(val);
|
||||
if(!tqueue->left()) {
|
||||
CSerial::setCTS(false);
|
||||
txbufferfull=true;
|
||||
}
|
||||
} else LOG_MSG("MODEM: TX Buffer overflow!");
|
||||
CSerial::ByteTransmitted();
|
||||
}
|
||||
|
||||
void CSerialModem::updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr) {
|
||||
// nothing to do here right?
|
||||
}
|
||||
|
||||
void CSerialModem::updateMSR() {
|
||||
// think it is not needed
|
||||
}
|
||||
|
||||
void CSerialModem::setBreak(bool) {
|
||||
// TODO: handle this
|
||||
}
|
||||
|
||||
void CSerialModem::updateModemControlLines(/*Bit8u mcr*/) {
|
||||
//if(!txbufferfull)
|
||||
//{
|
||||
// if(CSerial::getRTS()) CSerial::setCTS(true);
|
||||
// else CSerial::setCTS(false);
|
||||
//}
|
||||
|
||||
// If DTR goes low, hang up.
|
||||
if(connected)
|
||||
if(oldDTRstate)
|
||||
if(!getDTR()) {
|
||||
SendRes(ResNOCARRIER);
|
||||
EnterIdleState();
|
||||
LOG_MSG("Modem: Hang up due to dropped DTR.");
|
||||
}
|
||||
|
||||
oldDTRstate = getDTR();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
243
src/hardware/serialport/softmodem.h
Normal file
243
src/hardware/serialport/softmodem.h
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* $Id: softmodem.h,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */
|
||||
|
||||
#ifndef DOSBOX_SERIALMODEM_H
|
||||
#define DOSBOX_SERIALMODEM_H
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "SDL_net.h"
|
||||
#include "serialport.h"
|
||||
|
||||
#define MODEMSPD 57600
|
||||
#define SREGS 100
|
||||
|
||||
//If it's too high you overflow terminal clients buffers i think
|
||||
#define MODEM_BUFFER_QUEUE_SIZE 1024
|
||||
|
||||
#define MODEM_DEFAULT_PORT 23
|
||||
|
||||
enum ResTypes {
|
||||
ResNONE,
|
||||
ResOK,ResERROR,
|
||||
ResCONNECT,ResRING,
|
||||
ResBUSY,ResNODIALTONE,ResNOCARRIER,
|
||||
};
|
||||
|
||||
#define TEL_CLIENT 0
|
||||
#define TEL_SERVER 1
|
||||
|
||||
class CFifo {
|
||||
public:
|
||||
CFifo(Bitu _size) {
|
||||
size=_size;
|
||||
pos=used=0;
|
||||
data=new Bit8u[size];
|
||||
}
|
||||
~CFifo() {
|
||||
delete[] data;
|
||||
}
|
||||
INLINE Bitu left(void) {
|
||||
return size-used;
|
||||
}
|
||||
INLINE Bitu inuse(void) {
|
||||
return used;
|
||||
}
|
||||
void clear(void) {
|
||||
used=pos=0;
|
||||
}
|
||||
|
||||
void addb(Bit8u _val) {
|
||||
if(used>=size) {
|
||||
LOG_MSG("FIFO Overflow!");
|
||||
return;
|
||||
}
|
||||
//assert(used<size);
|
||||
Bitu where=pos+used;
|
||||
if (where>=size) where-=size;
|
||||
data[where]=_val;
|
||||
//LOG_MSG("+%x",_val);
|
||||
used++;
|
||||
}
|
||||
void adds(Bit8u * _str,Bitu _len) {
|
||||
if((used+_len)>size) {
|
||||
LOG_MSG("FIFO Overflow!");
|
||||
return;
|
||||
}
|
||||
|
||||
//assert((used+_len)<=size);
|
||||
Bitu where=pos+used;
|
||||
used+=_len;
|
||||
while (_len--) {
|
||||
if (where>=size) where-=size;
|
||||
//LOG_MSG("+%x",*_str);
|
||||
data[where++]=*_str++;
|
||||
}
|
||||
}
|
||||
Bit8u getb(void) {
|
||||
if (!used) {
|
||||
LOG_MSG("MODEM: FIFO UNDERFLOW!");
|
||||
return data[pos];
|
||||
}
|
||||
Bitu where=pos;
|
||||
if (++pos>=size) pos-=size;
|
||||
used--;
|
||||
//LOG_MSG("-%x",data[where]);
|
||||
return data[where];
|
||||
}
|
||||
void gets(Bit8u * _str,Bitu _len) {
|
||||
if (!used) {
|
||||
LOG_MSG("MODEM: FIFO UNDERFLOW!");
|
||||
return;
|
||||
}
|
||||
//assert(used>=_len);
|
||||
used-=_len;
|
||||
while (_len--) {
|
||||
//LOG_MSG("-%x",data[pos]);
|
||||
*_str++=data[pos];
|
||||
if (++pos>=size) pos-=size;
|
||||
}
|
||||
}
|
||||
private:
|
||||
Bit8u * data;
|
||||
Bitu size,pos,used;
|
||||
//Bit8u tmpbuf[MODEM_BUFFER_QUEUE_SIZE];
|
||||
|
||||
|
||||
};
|
||||
#define MREG_AUTOANSWER_COUNT 0
|
||||
#define MREG_RING_COUNT 1
|
||||
#define MREG_ESCAPE_CHAR 2
|
||||
#define MREG_CR_CHAR 3
|
||||
#define MREG_LF_CHAR 4
|
||||
#define MREG_BACKSPACE_CHAR 5
|
||||
|
||||
|
||||
class CSerialModem : public CSerial {
|
||||
public:
|
||||
|
||||
CFifo *rqueue;
|
||||
CFifo *tqueue;
|
||||
|
||||
CSerialModem(
|
||||
IO_ReadHandler* rh,
|
||||
IO_WriteHandler* wh,
|
||||
TIMER_TickHandler th,
|
||||
Bit16u baseAddr,
|
||||
Bit8u initIrq,
|
||||
Bit32u initBps,
|
||||
Bit8u bytesize,
|
||||
const char* parity,
|
||||
Bit8u stopbits,
|
||||
|
||||
const char *remotestr = NULL,
|
||||
Bit16u lport = 23);
|
||||
|
||||
~CSerialModem();
|
||||
|
||||
void Reset();
|
||||
|
||||
void SendLine(const char *line);
|
||||
void SendRes(ResTypes response);
|
||||
void SendNumber(Bitu val);
|
||||
|
||||
void EnterIdleState();
|
||||
void EnterConnectedState();
|
||||
|
||||
void openConnection(void);
|
||||
bool Dial(char * host);
|
||||
void AcceptIncomingCall(void);
|
||||
Bitu ScanNumber(char * & scan);
|
||||
|
||||
void DoCommand();
|
||||
|
||||
void MC_Changed(Bitu new_mc);
|
||||
|
||||
void TelnetEmulation(Bit8u * data, Bitu size);
|
||||
|
||||
void Timer2(void);
|
||||
|
||||
void RXBufferEmpty();
|
||||
|
||||
void transmitByte(Bit8u val);
|
||||
void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr);
|
||||
void updateMSR();
|
||||
|
||||
void setBreak(bool);
|
||||
|
||||
void updateModemControlLines(/*Bit8u mcr*/);
|
||||
|
||||
protected:
|
||||
char cmdbuf[255];
|
||||
bool commandmode; // true: interpret input as commands
|
||||
//bool answermode;
|
||||
bool echo; // local echo on or off
|
||||
|
||||
bool oldDTRstate;
|
||||
bool ringing;
|
||||
//bool response;
|
||||
bool numericresponse; // true: send control response as number.
|
||||
// false: send text (i.e. NO DIALTONE)
|
||||
bool telnetmode; // true: process IAC commands.
|
||||
|
||||
bool connected;
|
||||
bool txbufferfull;
|
||||
Bitu doresponse;
|
||||
|
||||
|
||||
|
||||
Bitu cmdpause;
|
||||
Bits ringtimer;
|
||||
Bits ringcount;
|
||||
Bitu plusinc;
|
||||
Bitu cmdpos;
|
||||
|
||||
|
||||
//Bit8u mctrl;
|
||||
Bit8u tmpbuf[MODEM_BUFFER_QUEUE_SIZE];
|
||||
|
||||
Bitu listenport;
|
||||
Bit8u reg[SREGS];
|
||||
IPaddress openip;
|
||||
TCPsocket incomingsocket;
|
||||
TCPsocket socket;
|
||||
|
||||
TCPsocket listensocket;
|
||||
SDLNet_SocketSet socketset;
|
||||
SDLNet_SocketSet listensocketset;
|
||||
|
||||
struct {
|
||||
bool binary[2];
|
||||
bool echo[2];
|
||||
bool supressGA[2];
|
||||
bool timingMark[2];
|
||||
|
||||
bool inIAC;
|
||||
bool recCommand;
|
||||
Bit8u command;
|
||||
} telClient;
|
||||
struct {
|
||||
bool active;
|
||||
double f1, f2;
|
||||
Bitu len,pos;
|
||||
char str[256];
|
||||
} dial;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,820 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2005 The DOSBox Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_MODEM
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "SDL_net.h"
|
||||
|
||||
#include "inout.h"
|
||||
#include "mixer.h"
|
||||
#include "pic.h"
|
||||
#include "setup.h"
|
||||
#include "programs.h"
|
||||
#include "debug.h"
|
||||
#include "timer.h"
|
||||
#include "callback.h"
|
||||
#include "math.h"
|
||||
#include "regs.h"
|
||||
#include "serialport.h"
|
||||
|
||||
#define MODEMSPD 57600
|
||||
#define SREGS 100
|
||||
|
||||
|
||||
static Bit8u tmpbuf[QUEUE_SIZE];
|
||||
|
||||
struct ModemHd {
|
||||
char cmdbuf[QUEUE_SIZE];
|
||||
bool commandmode;
|
||||
bool answermode;
|
||||
bool echo,response,numericresponse;
|
||||
bool telnetmode;
|
||||
Bitu cmdpause;
|
||||
Bits ringtimer;
|
||||
Bits ringcount;
|
||||
Bitu plusinc;
|
||||
Bitu cmdpos;
|
||||
|
||||
Bit8u reg[SREGS];
|
||||
TCPsocket incomingsocket;
|
||||
TCPsocket socket;
|
||||
TCPsocket listensocket;
|
||||
SDLNet_SocketSet socketset;
|
||||
|
||||
IPaddress openip;
|
||||
|
||||
Bitu comport;
|
||||
Bitu listenport;
|
||||
|
||||
char remotestr[4096];
|
||||
|
||||
bool dialing;
|
||||
double f1, f2;
|
||||
Bitu diallen;
|
||||
Bitu dialpos;
|
||||
char dialstr[256];
|
||||
// TODO: Re-enable dialtons
|
||||
//MIXER_Channel * chan;
|
||||
};
|
||||
|
||||
enum ResTypes {
|
||||
ResNONE,
|
||||
ResOK,ResERROR,
|
||||
ResCONNECT,ResRING,
|
||||
ResBUSY,ResNODIALTONE,ResNOCARRIER,
|
||||
};
|
||||
|
||||
#define TEL_CLIENT 0
|
||||
#define TEL_SERVER 1
|
||||
|
||||
struct telnetClient {
|
||||
bool binary[2];
|
||||
bool echo[2];
|
||||
bool supressGA[2];
|
||||
bool timingMark[2];
|
||||
|
||||
bool inIAC;
|
||||
bool recCommand;
|
||||
Bit8u command;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#if 1
|
||||
|
||||
static void toUpcase(char *buffer) {
|
||||
Bitu i=0;
|
||||
while (buffer[i] != 0) {
|
||||
buffer[i] = toupper(buffer[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CSerialModem : public CSerial {
|
||||
public:
|
||||
ModemHd mhd;
|
||||
|
||||
CSerialModem(Bit16u baseAddr, Bit8u initIrq, Bit32u initBps, const char *remotestr = NULL, Bit16u lport = 27)
|
||||
: CSerial(baseAddr, initIrq, initBps)
|
||||
{
|
||||
|
||||
|
||||
mhd.cmdpos = 0;
|
||||
mhd.commandmode = true;
|
||||
mhd.plusinc = 0;
|
||||
mhd.incomingsocket = 0;
|
||||
mhd.answermode = false;
|
||||
memset(&mhd.reg,0,sizeof(mhd.reg));
|
||||
mhd.cmdpause = 0;
|
||||
mhd.echo = true;
|
||||
mhd.response = true;
|
||||
mhd.numericresponse = false;
|
||||
|
||||
/* Default to direct null modem connection. Telnet mode interprets IAC codes */
|
||||
mhd.telnetmode = false;
|
||||
|
||||
/* Bind the modem to the correct serial port */
|
||||
//strcpy(mhd.remotestr, remotestr);
|
||||
|
||||
/* Initialize the sockets and setup the listening port */
|
||||
mhd.socketset = SDLNet_AllocSocketSet(1);
|
||||
if (!mhd.socketset) {
|
||||
LOG_MSG("MODEM:Can't open socketset:%s",SDLNet_GetError());
|
||||
//TODO Should probably just exit
|
||||
return;
|
||||
}
|
||||
mhd.socket=0;
|
||||
mhd.listenport=lport;
|
||||
if (mhd.listenport) {
|
||||
IPaddress listen_ip;
|
||||
SDLNet_ResolveHost(&listen_ip, NULL, mhd.listenport);
|
||||
mhd.listensocket=SDLNet_TCP_Open(&listen_ip);
|
||||
if (!mhd.listensocket) LOG_MSG("MODEM:Can't open listen port:%s",SDLNet_GetError());
|
||||
} else mhd.listensocket=0;
|
||||
|
||||
// TODO: Fix dialtones if requested
|
||||
//mhd.chan=MIXER_AddChannel((MIXER_MixHandler)this->MODEM_CallBack,8000,"MODEM");
|
||||
//MIXER_Enable(mhd.chan,false);
|
||||
//MIXER_SetMode(mhd.chan,MIXER_16MONO);
|
||||
|
||||
}
|
||||
|
||||
~CSerialModem(){
|
||||
if (mhd.socket) {
|
||||
SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket);
|
||||
SDLNet_TCP_Close(mhd.socket);
|
||||
}
|
||||
if(mhd.listensocket) SDLNet_TCP_Close(mhd.listensocket);
|
||||
if(mhd.socketset) SDLNet_FreeSocketSet(mhd.socketset);
|
||||
}
|
||||
|
||||
void SendLine(const char *line) {
|
||||
rqueue->addb(0xd);
|
||||
rqueue->addb(0xa);
|
||||
rqueue->adds((Bit8u *)line,strlen(line));
|
||||
rqueue->addb(0xd);
|
||||
rqueue->addb(0xa);
|
||||
|
||||
}
|
||||
|
||||
void SendRes(ResTypes response) {
|
||||
char * string;char * code;
|
||||
switch (response) {
|
||||
case ResNONE:
|
||||
return;
|
||||
case ResOK:string="OK";code="0";break;
|
||||
case ResERROR:string="ERROR";code="4";break;
|
||||
case ResRING:string="RING";code="2";
|
||||
case ResNODIALTONE:string="NO DIALTONE";code="6";break;
|
||||
case ResNOCARRIER:string="NO CARRIER";code="3";break;
|
||||
case ResCONNECT:string="CONNECT 57600";code="1";break;
|
||||
}
|
||||
|
||||
rqueue->addb(0xd);rqueue->addb(0xa);
|
||||
rqueue->adds((Bit8u *)string,strlen(string));
|
||||
rqueue->addb(0xd);rqueue->addb(0xa);
|
||||
}
|
||||
|
||||
void Send(Bit8u val) {
|
||||
tqueue->addb(val);
|
||||
}
|
||||
|
||||
Bit8u Recv(Bit8u val) {
|
||||
return rqueue->getb();
|
||||
|
||||
}
|
||||
|
||||
void openConnection(void) {
|
||||
if (mhd.socket) {
|
||||
LOG_MSG("Huh? already connected");
|
||||
SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket);
|
||||
SDLNet_TCP_Close(mhd.socket);
|
||||
}
|
||||
mhd.socket = SDLNet_TCP_Open(&openip);
|
||||
if (mhd.socket) {
|
||||
SDLNet_TCP_AddSocket(mhd.socketset,mhd.socket);
|
||||
SendRes(ResCONNECT);
|
||||
mhd.commandmode = false;
|
||||
memset(&telClient, 0, sizeof(telClient));
|
||||
updatemstatus();
|
||||
} else {
|
||||
SendRes(ResNODIALTONE);
|
||||
}
|
||||
}
|
||||
|
||||
void updatemstatus(void) {
|
||||
Bit8u ms=0;
|
||||
//Check for data carrier, a connection that is
|
||||
if (mhd.incomingsocket) ms|=MS_RI;
|
||||
if (mhd.socket) ms|=MS_DCD;
|
||||
if (!mhd.commandmode) ms|=MS_DSR;
|
||||
//Check for DTR reply with DSR
|
||||
// if (cport->mctrl & MC_DTR) ms|=MS_DSR;
|
||||
//Check for RTS reply with CTS
|
||||
if (mctrl & MC_RTS) ms|=MS_CTS;
|
||||
SetModemStatus(ms);
|
||||
}
|
||||
|
||||
bool Dial(char * host) {
|
||||
/* Scan host for port */
|
||||
Bit16u port;
|
||||
char * hasport=strrchr(host,':');
|
||||
if (hasport) {
|
||||
*hasport++=0;
|
||||
port=(Bit16u)atoi(hasport);
|
||||
} else port=23;
|
||||
/* Resolve host we're gonna dial */
|
||||
LOG_MSG("host %s port %x",host,port);
|
||||
if (!SDLNet_ResolveHost(&openip,host,port)) {
|
||||
openConnection();
|
||||
return true;
|
||||
} else {
|
||||
LOG_MSG("Failed to resolve host %s:%s",host,SDLNet_GetError());
|
||||
SendRes(ResNOCARRIER);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AcceptIncomingCall(void) {
|
||||
assert(!mhd.socket);
|
||||
mhd.socket=mhd.incomingsocket;
|
||||
SDLNet_TCP_AddSocket(mhd.socketset,mhd.socket);
|
||||
SendRes(ResCONNECT);
|
||||
LOG_MSG("Connected!\n");
|
||||
|
||||
mhd.incomingsocket = 0;
|
||||
mhd.commandmode = false;
|
||||
}
|
||||
|
||||
Bitu ScanNumber(char * & scan) {
|
||||
Bitu ret=0;
|
||||
while (char c=*scan) {
|
||||
if (c>='0' && c<='9') {
|
||||
ret*=10;
|
||||
ret+=c-'0';
|
||||
scan++;
|
||||
} else break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void HangUp(void) {
|
||||
SendRes(ResNOCARRIER);
|
||||
SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket);
|
||||
SDLNet_TCP_Close(mhd.socket);
|
||||
mhd.socket=0;
|
||||
mhd.commandmode = true;
|
||||
updatemstatus();
|
||||
}
|
||||
|
||||
void DoCommand() {
|
||||
mhd.cmdbuf[mhd.cmdpos] = 0;
|
||||
mhd.cmdpos = 0; //Reset for next command
|
||||
toUpcase(mhd.cmdbuf);
|
||||
LOG_MSG("Modem Sent Command: %s\n", mhd.cmdbuf);
|
||||
/* Check for empty line, stops dialing and autoanswer */
|
||||
if (!mhd.cmdbuf[0]) {
|
||||
if(!mhd.dialing) {
|
||||
mhd.answermode = false;
|
||||
goto ret_none;
|
||||
} else {
|
||||
//MIXER_Enable(mhd.chan,false);
|
||||
mhd.dialing = false;
|
||||
SendRes(ResNOCARRIER);
|
||||
goto ret_none;
|
||||
}
|
||||
}
|
||||
/* AT command set interpretation */
|
||||
if ((mhd.cmdbuf[0] != 'A') || (mhd.cmdbuf[1] != 'T')) goto ret_error;
|
||||
/* Check for dial command */
|
||||
if(strncmp(mhd.cmdbuf,"ATD3",3)==0) {
|
||||
char * foundstr=&mhd.cmdbuf[3];
|
||||
if (*foundstr=='T' || *foundstr=='P') foundstr++;
|
||||
/* Small protection against empty line */
|
||||
if (!foundstr[0]) goto ret_error;
|
||||
if (strlen(foundstr) >= 12){
|
||||
// Check if supplied parameter only consists of digits
|
||||
bool isNum = true;
|
||||
for (Bitu i=0; i<strlen(foundstr); i++)
|
||||
if (foundstr[i] < '0' || foundstr[i] > '9')
|
||||
isNum = false;
|
||||
if (isNum) {
|
||||
// Parameter is a number with at least 12 digits => this cannot be a valid IP/name
|
||||
// Transform by adding dots
|
||||
char buffer[128];
|
||||
Bitu j = 0;
|
||||
for (Bitu i=0; i<strlen(foundstr); i++) {
|
||||
buffer[j++] = foundstr[i];
|
||||
// Add a dot after the third, sixth and ninth number
|
||||
if (i == 2 || i == 5 || i == 8)
|
||||
buffer[j++] = '.';
|
||||
// If the string is longer than 12 digits, interpret the rest as port
|
||||
if (i == 11 && strlen(foundstr)>12)
|
||||
buffer[j++] = ':';
|
||||
}
|
||||
buffer[j] = 0;
|
||||
foundstr = buffer;
|
||||
}
|
||||
}
|
||||
Dial(foundstr);
|
||||
goto ret_none;
|
||||
}
|
||||
char * scanbuf;
|
||||
scanbuf=&mhd.cmdbuf[2];char chr;Bitu num;
|
||||
while (chr=*scanbuf++) {
|
||||
switch (chr) {
|
||||
case 'I': //Some strings about firmware
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 3:SendLine("DosBox Emulated Modem Firmware V1.00");break;
|
||||
case 4:SendLine("Modem compiled for DosBox version " VERSION);break;
|
||||
};break;
|
||||
case 'E': //Echo on/off
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 0:mhd.echo = false;break;
|
||||
case 1:mhd.echo = true;break;
|
||||
};break;
|
||||
case 'V':
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 0:mhd.numericresponse = true;break;
|
||||
case 1:mhd.numericresponse = false;break;
|
||||
};break;
|
||||
case 'H': //Hang up
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 0:
|
||||
if (mhd.socket) {
|
||||
HangUp();
|
||||
goto ret_none;
|
||||
}
|
||||
//Else return ok
|
||||
};break;
|
||||
case 'O': //Return to data mode
|
||||
switch (num=ScanNumber(scanbuf)) {
|
||||
case 0:
|
||||
if (mhd.socket) {
|
||||
mhd.commandmode = false;
|
||||
goto ret_none;
|
||||
} else {
|
||||
goto ret_error;
|
||||
}
|
||||
};break;
|
||||
case 'T': //Tone Dial
|
||||
case 'P': //Pulse Dial
|
||||
break;
|
||||
case 'M': //Monitor
|
||||
case 'L': //Volume
|
||||
ScanNumber(scanbuf);
|
||||
break;
|
||||
case 'A': //Answer call
|
||||
if (mhd.incomingsocket) {
|
||||
AcceptIncomingCall();
|
||||
} else {
|
||||
mhd.answermode = true;
|
||||
}
|
||||
goto ret_none;
|
||||
case 'Z': //Reset and load profiles
|
||||
num=ScanNumber(scanbuf);
|
||||
break;
|
||||
case ' ': //Space just skip
|
||||
break;
|
||||
case 'S': //Registers
|
||||
{
|
||||
Bitu index=ScanNumber(scanbuf);
|
||||
bool hasequal=(*scanbuf == '=');
|
||||
if (hasequal) scanbuf++;
|
||||
Bitu val=ScanNumber(scanbuf);
|
||||
if (index>=SREGS) goto ret_error;
|
||||
if (hasequal) mhd.reg[index]=val;
|
||||
else LOG_MSG("print reg %d with %d",index,mhd.reg[index]);
|
||||
};break;
|
||||
default:
|
||||
LOG_MSG("Unhandled cmd %c%d",chr,ScanNumber(scanbuf));
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
if (strstr(mhd.cmdbuf,"S0=1")) {
|
||||
mhd.autoanswer = true;
|
||||
}
|
||||
if (strstr(mhd.cmdbuf,"S0=0")) {
|
||||
mhd.autoanswer = false;
|
||||
}
|
||||
|
||||
if (strstr(mhd.cmdbuf,"NET0")) {
|
||||
mhd.telnetmode = false;
|
||||
}
|
||||
if (strstr(mhd.cmdbuf,"NET1")) {
|
||||
mhd.telnetmode = true;
|
||||
}
|
||||
#endif
|
||||
LOG_MSG("Sending OK");
|
||||
SendRes(ResOK);
|
||||
return;
|
||||
ret_error:
|
||||
LOG_MSG("Sending ERROR");
|
||||
SendRes(ResERROR);
|
||||
ret_none:
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void MC_Changed(Bitu new_mc) {
|
||||
LOG_MSG("New MC %x",new_mc );
|
||||
if (!(new_mc & 1) && mhd.socket) HangUp();
|
||||
updatemstatus();
|
||||
}
|
||||
|
||||
void TelnetEmulation(Bit8u * data, Bitu size) {
|
||||
Bitu i;
|
||||
Bit8u c;
|
||||
for(i=0;i<size;i++) {
|
||||
c = data[i];
|
||||
if(telClient.inIAC) {
|
||||
if(telClient.recCommand) {
|
||||
if((c != 0) && (c != 1) && (c != 3)) {
|
||||
LOG_MSG("MODEM: Unrecognized option %d", c);
|
||||
if(telClient.command>250) {
|
||||
/* Reject anything we don't recognize */
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(c); /* We won't do crap! */
|
||||
}
|
||||
}
|
||||
switch(telClient.command) {
|
||||
case 251: /* Will */
|
||||
if(c == 0) telClient.binary[TEL_SERVER] = true;
|
||||
if(c == 1) telClient.echo[TEL_SERVER] = true;
|
||||
if(c == 3) telClient.supressGA[TEL_SERVER] = true;
|
||||
break;
|
||||
case 252: /* Won't */
|
||||
if(c == 0) telClient.binary[TEL_SERVER] = false;
|
||||
if(c == 1) telClient.echo[TEL_SERVER] = false;
|
||||
if(c == 3) telClient.supressGA[TEL_SERVER] = false;
|
||||
break;
|
||||
case 253: /* Do */
|
||||
if(c == 0) {
|
||||
telClient.binary[TEL_CLIENT] = true;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(251);
|
||||
tqueue->addb(0); /* Will do binary transfer */
|
||||
}
|
||||
if(c == 1) {
|
||||
telClient.echo[TEL_CLIENT] = false;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(1); /* Won't echo (too lazy) */
|
||||
}
|
||||
if(c == 3) {
|
||||
telClient.supressGA[TEL_CLIENT] = true;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(251);
|
||||
tqueue->addb(3); /* Will Suppress GA */
|
||||
}
|
||||
break;
|
||||
case 254: /* Don't */
|
||||
if(c == 0) {
|
||||
telClient.binary[TEL_CLIENT] = false;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(0); /* Won't do binary transfer */
|
||||
}
|
||||
if(c == 1) {
|
||||
telClient.echo[TEL_CLIENT] = false;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(252);
|
||||
tqueue->addb(1); /* Won't echo (fine by me) */
|
||||
}
|
||||
if(c == 3) {
|
||||
telClient.supressGA[TEL_CLIENT] = true;
|
||||
tqueue->addb(0xff);
|
||||
tqueue->addb(251);
|
||||
tqueue->addb(3); /* Will Suppress GA (too lazy) */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command);
|
||||
break;
|
||||
}
|
||||
|
||||
telClient.inIAC = false;
|
||||
telClient.recCommand = false;
|
||||
continue;
|
||||
|
||||
} else {
|
||||
if(c==249) {
|
||||
/* Go Ahead received */
|
||||
telClient.inIAC = false;
|
||||
continue;
|
||||
}
|
||||
telClient.command = c;
|
||||
telClient.recCommand = true;
|
||||
|
||||
if((telClient.binary[TEL_SERVER]) && (c == 0xff)) {
|
||||
/* Binary data with value of 255 */
|
||||
telClient.inIAC = false;
|
||||
telClient.recCommand = false;
|
||||
rqueue->addb(0xff);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if(c == 0xff) {
|
||||
telClient.inIAC = true;
|
||||
continue;
|
||||
}
|
||||
rqueue->addb(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Timer(void) {
|
||||
int result =0;
|
||||
unsigned long args = 1;
|
||||
bool sendbyte = true;
|
||||
Bitu usesize;
|
||||
Bit8u txval;
|
||||
|
||||
|
||||
/* Check for eventual break command */
|
||||
if (!mhd.commandmode) mhd.cmdpause++;
|
||||
/* Handle incoming data from serial port, read as much as available */
|
||||
Bitu tx_size=tqueue->inuse();
|
||||
while (tx_size--) {
|
||||
txval = tqueue->getb();
|
||||
if (mhd.commandmode) {
|
||||
if (mhd.echo) rqueue->addb(txval);
|
||||
if (txval==0xa) continue; //Real modem doesn't seem to skip this?
|
||||
else if (txval==0x8 && (mhd.cmdpos > 0)) --mhd.cmdpos;
|
||||
else if (txval==0xd) DoCommand();
|
||||
else if (txval != '+') {
|
||||
if(mhd.cmdpos<QUEUE_SIZE) {
|
||||
mhd.cmdbuf[mhd.cmdpos] = txval;
|
||||
mhd.cmdpos++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* 1000 ticks have passed, can check for pause command */
|
||||
if (mhd.cmdpause > 1000) {
|
||||
if(txval == '+') {
|
||||
mhd.plusinc++;
|
||||
if(mhd.plusinc>=3) {
|
||||
LOG_MSG("Entering command mode");
|
||||
mhd.commandmode = true;
|
||||
SendRes(ResOK);
|
||||
mhd.plusinc = 0;
|
||||
}
|
||||
sendbyte=false;
|
||||
} else {
|
||||
mhd.plusinc=0;
|
||||
}
|
||||
//If not a special pause command, should go for bigger blocks to send
|
||||
}
|
||||
|
||||
tmpbuf[0] = txval;
|
||||
tmpbuf[1] = 0x0;
|
||||
|
||||
if (mhd.socket && sendbyte) {
|
||||
SDLNet_TCP_Send(mhd.socket, tmpbuf,1);
|
||||
//TODO error testing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDLNet_CheckSockets(mhd.socketset,0);
|
||||
/* Handle incoming to the serial port */
|
||||
if(!mhd.commandmode && mhd.socket) {
|
||||
if(rqueue->left() && SDLNet_SocketReady(mhd.socket) && (mctrl & MC_RTS)) {
|
||||
usesize = rqueue->left();
|
||||
if (usesize>16) usesize=16;
|
||||
result = SDLNet_TCP_Recv(mhd.socket, tmpbuf, usesize);
|
||||
if (result>0) {
|
||||
if(mhd.telnetmode) {
|
||||
/* Filter telnet commands */
|
||||
TelnetEmulation(tmpbuf, result);
|
||||
} else {
|
||||
|
||||
rqueue->adds(tmpbuf,result);
|
||||
}
|
||||
mhd.cmdpause = 0;
|
||||
} else HangUp();
|
||||
}
|
||||
}
|
||||
/* Check for incoming calls */
|
||||
if (!mhd.socket && !mhd.incomingsocket && mhd.listensocket) {
|
||||
mhd.incomingsocket = SDLNet_TCP_Accept(mhd.listensocket);
|
||||
if (mhd.incomingsocket) {
|
||||
mhd.diallen = 12000;
|
||||
mhd.dialpos = 0;
|
||||
SendRes(ResRING);
|
||||
//MIXER_Enable(mhd.chan,true);
|
||||
mhd.ringtimer = 3000;
|
||||
mhd.reg[1] = 0; //Reset ring counter reg
|
||||
}
|
||||
}
|
||||
if (mhd.incomingsocket) {
|
||||
if (mhd.ringtimer <= 0) {
|
||||
mhd.reg[1]++;
|
||||
if (mhd.answermode || (mhd.reg[0]==mhd.reg[1])) {
|
||||
AcceptIncomingCall();
|
||||
return;
|
||||
}
|
||||
SendRes(ResRING);
|
||||
mhd.diallen = 12000;
|
||||
mhd.dialpos = 0;
|
||||
//MIXER_Enable(mhd.chan,true);
|
||||
mhd.ringtimer = 3000;
|
||||
}
|
||||
--mhd.ringtimer;
|
||||
}
|
||||
updatemstatus();
|
||||
}
|
||||
|
||||
bool CanSend(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanRecv(void) {
|
||||
if(rqueue->inuse()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
char cmdbuf[QUEUE_SIZE];
|
||||
bool commandmode;
|
||||
bool answermode;
|
||||
bool echo;
|
||||
bool telnetmode;
|
||||
Bitu cmdpause;
|
||||
Bits ringtimer;
|
||||
Bits ringcount;
|
||||
Bitu plusinc;
|
||||
Bitu cmdpos;
|
||||
|
||||
Bit8u reg[SREGS];
|
||||
IPaddress openip;
|
||||
TCPsocket incomingsocket;
|
||||
TCPsocket socket;
|
||||
TCPsocket listensocket;
|
||||
SDLNet_SocketSet socketset;
|
||||
|
||||
struct {
|
||||
bool binary[2];
|
||||
bool echo[2];
|
||||
bool supressGA[2];
|
||||
bool timingMark[2];
|
||||
|
||||
bool inIAC;
|
||||
bool recCommand;
|
||||
Bit8u command;
|
||||
} telClient;
|
||||
struct {
|
||||
bool active;
|
||||
double f1, f2;
|
||||
Bitu len,pos;
|
||||
char str[256];
|
||||
} dial;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
CSerialModem *csm;
|
||||
|
||||
|
||||
static Bitu INT14_FOSSIL(void) {
|
||||
switch (reg_ah) {
|
||||
case 0x01:
|
||||
// Serial - Write character to port
|
||||
csm->tqueue->addb(reg_al);
|
||||
reg_ah = csm->read_reg(0xd) & 0x7f;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
// FOSSIL - Receive character with wait
|
||||
//LOG_MSG("FOSSIL: Calling get to receive character", reg_ah);
|
||||
csm->mctrl |= MC_RTS;
|
||||
while(!csm->rqueue->inuse()) {
|
||||
// Wait for byte. Yes, I realize that this locks up DosBox, but
|
||||
// it would an oldskool system as well.
|
||||
csm->Timer();
|
||||
}
|
||||
reg_al = csm->rqueue->getb();
|
||||
reg_ah = 0;
|
||||
break;
|
||||
case 0x03:
|
||||
//LOG_MSG("FOSSIL: Calling get port status", reg_ah);
|
||||
// Serial - Get port status
|
||||
csm->mctrl |= MC_RTS;
|
||||
reg_ah = csm->read_reg(0xd) ; // Line status
|
||||
if(csm->rqueue->inuse()) reg_ah |= 0x1;
|
||||
|
||||
reg_al = csm->read_reg(0xe); // Modem status
|
||||
break;
|
||||
default:
|
||||
LOG_MSG("FOSSIL: Func 0x%x not handled", reg_ah);
|
||||
break;
|
||||
}
|
||||
return CBRET_NONE;
|
||||
}
|
||||
|
||||
|
||||
class VIRTUAL_MODEM:public Module_base {
|
||||
private:
|
||||
CALLBACK_HandlerObject callback;
|
||||
public:
|
||||
VIRTUAL_MODEM(Section* configuration):Module_base(configuration){
|
||||
Section_prop * section=static_cast<Section_prop *>(configuration);
|
||||
csm = NULL;
|
||||
|
||||
if(!section->Get_bool("modem")) return;
|
||||
|
||||
if(SDLNet_Init()==-1) {
|
||||
LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if(!SDLNetInited) {
|
||||
if(SDLNet_Init()==-1) {
|
||||
LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
|
||||
return;
|
||||
}
|
||||
SDLNetInited = true;
|
||||
}
|
||||
|
||||
Bit16u comport = section->Get_int("comport");
|
||||
|
||||
|
||||
|
||||
switch (comport) {
|
||||
case 1:
|
||||
csm = new CSerialModem(0x3f0, 4, 57600, section->Get_string("remote"), section->Get_int("listenport"));
|
||||
break;
|
||||
case 2:
|
||||
csm = new CSerialModem(0x2f0, 3, 57600, section->Get_string("remote"), section->Get_int("listenport"));
|
||||
break;
|
||||
case 3:
|
||||
csm = new CSerialModem(0x3e0, 4, 57600, section->Get_string("remote"), section->Get_int("listenport"));
|
||||
break;
|
||||
case 4:
|
||||
csm = new CSerialModem(0x2e0, 3, 57600, section->Get_string("remote"), section->Get_int("listenport"));
|
||||
break;
|
||||
default:
|
||||
// Default to COM2
|
||||
csm = new CSerialModem(0x2f0, 3, 57600, section->Get_string("remote"), section->Get_int("listenport"));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if(csm != NULL) seriallist.push_back(csm);
|
||||
|
||||
//Enable FOSSIL support (GTERM, etc)
|
||||
callback.Install(&INT14_FOSSIL,CB_IRET,"int14 fossil");
|
||||
callback.Set_RealVec(0x14);
|
||||
}
|
||||
~VIRTUAL_MODEM(){
|
||||
if(!csm) return;
|
||||
delete csm;
|
||||
}
|
||||
};
|
||||
|
||||
static VIRTUAL_MODEM* test;
|
||||
|
||||
void MODEM_Destroy(Section* sec){
|
||||
delete test;
|
||||
}
|
||||
|
||||
|
||||
void MODEM_Init(Section* sec) {
|
||||
test = new VIRTUAL_MODEM(sec);
|
||||
sec->AddDestroyFunction(&MODEM_Destroy,true);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue