From 880d2d2ceb6bcc27c3c5103fa79d04314f4280c8 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 19 Mar 2020 20:13:58 +0300 Subject: [PATCH] Implement DTR drop actions (&Dn) and DTR drop delay (S25) in modem &D0 may be needed for old apps which don't set DTR or set it to garbage. DTR drop delay is required for Quake otherwise it doesn't disconnect properly. --- src/hardware/serialport/softmodem.cpp | 65 ++++++++++++++++++++++++--- src/hardware/serialport/softmodem.h | 3 ++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/hardware/serialport/softmodem.cpp b/src/hardware/serialport/softmodem.cpp index d5a97867..82f8c531 100644 --- a/src/hardware/serialport/softmodem.cpp +++ b/src/hardware/serialport/softmodem.cpp @@ -246,9 +246,10 @@ void CSerialModem::Reset(){ EnterIdleState(); cmdpos = 0; cmdbuf[0] = 0; - oldDTRstate = getDTR(); flowcontrol = 0; plusinc = 0; + oldDTRstate = getDTR(); + dtrmode = 2; clientsocket.reset(nullptr); memset(®,0,sizeof(reg)); @@ -259,6 +260,7 @@ void CSerialModem::Reset(){ reg[MREG_LF_CHAR] = '\n'; reg[MREG_BACKSPACE_CHAR] = '\b'; reg[MREG_GUARD_TIME] = 50; + reg[MREG_DTR_DELAY] = 5; cmdpause = 0; echo = true; @@ -272,6 +274,7 @@ void CSerialModem::Reset(){ void CSerialModem::EnterIdleState(void){ connected = false; ringing = false; + dtrofftimer = -1; clientsocket.reset(nullptr); waitingclientsocket.reset(nullptr); @@ -313,6 +316,7 @@ void CSerialModem::EnterConnectedState(void) { memset(&telClient, 0, sizeof(telClient)); connected = true; ringing = false; + dtrofftimer = -1; CSerial::setCD(true); CSerial::setRI(false); } @@ -541,6 +545,15 @@ void CSerialModem::DoCommand() { } break; } + case 'D': { + Bitu val = ScanNumber(scanbuf); + if (val<4) dtrmode=val; + else { + SendRes(ResERROR); + return; + } + break; + } case '\0': // end of string SendRes(ResERROR); @@ -773,7 +786,7 @@ void CSerialModem::Timer2(void) { if (!connected && !waitingclientsocket && serversocket) { waitingclientsocket.reset(serversocket->Accept()); if (waitingclientsocket) { - if (!CSerial::getDTR()) { + if (!CSerial::getDTR() && dtrmode != 0) { // accept no calls with DTR off; TODO: AT &Dn EnterIdleState(); } else { @@ -801,6 +814,40 @@ void CSerialModem::Timer2(void) { } --ringtimer; } + + if (connected && !getDTR()) { + if (dtrofftimer == 0) { + switch (dtrmode) { + case 0: + // Do nothing. + //LOG_MSG("Modem: Dropped DTR."); + break; + case 1: + // Go back to command mode. + LOG_MSG("Modem: Entering command mode due to dropped DTR."); + commandmode = true; + SendRes(ResOK); + break; + case 2: + // Hang up. + LOG_MSG("Modem: Hanging up due to dropped DTR."); + SendRes(ResNOCARRIER); + EnterIdleState(); + break; + case 3: + // Reset. + LOG_MSG("Modem: Resetting due to dropped DTR."); + SendRes(ResNOCARRIER); + Reset(); + break; + } + } + + // Set the timer to -1 once it's expired to turn it off. + if (dtrofftimer >= 0) { + dtrofftimer--; + } + } } @@ -842,12 +889,16 @@ void CSerialModem::setRTS(bool val) { (void) val; // deliberately unused but but needed for API compliance } void CSerialModem::setDTR(bool val) { - if (!val && connected) { - // If DTR goes low, hang up. - SendRes(ResNOCARRIER); - EnterIdleState(); - LOG_MSG("Modem: Hang up due to dropped DTR."); + if (val != oldDTRstate) { + if (connected && !val) { + // Start the timer upon losing DTR. + dtrofftimer = reg[MREG_DTR_DELAY]; + } else { + dtrofftimer = -1; + } } + + oldDTRstate = val; } /* void CSerialModem::updateModemControlLines() { diff --git a/src/hardware/serialport/softmodem.h b/src/hardware/serialport/softmodem.h index 8fa6e4a5..8741b05f 100644 --- a/src/hardware/serialport/softmodem.h +++ b/src/hardware/serialport/softmodem.h @@ -166,6 +166,7 @@ private: #define MREG_LF_CHAR 4 #define MREG_BACKSPACE_CHAR 5 #define MREG_GUARD_TIME 12 +#define MREG_DTR_DELAY 25 class CSerialModem : public CSerial { @@ -230,6 +231,8 @@ protected: Bitu plusinc; Bitu cmdpos; Bitu flowcontrol; + Bitu dtrmode; + Bits dtrofftimer; Bit8u tmpbuf[MODEM_BUFFER_QUEUE_SIZE]; Bitu listenport; Bit8u reg[SREGS];