diff options
author | NicknineTheEagle <nik18.1995@gmail.com> | 2021-12-28 22:14:23 +0300 |
---|---|---|
committer | NicknineTheEagle <nik18.1995@gmail.com> | 2022-01-02 18:50:37 +0300 |
commit | 73bafe20ea57bbd9bc2eb4b5299c0cc6b18f40ec (patch) | |
tree | b1c702c7787bd38b611783f3b5785fa907c1636a | |
parent | 4499e4566d2e488be1c0e0a20328b13652ed14cb (diff) |
Implement new modem communication modenn/modem
Only has basic answer confirmation for now, to be expanded in the future
-rw-r--r-- | src/hardware/serialport/softmodem.cpp | 99 | ||||
-rw-r--r-- | src/hardware/serialport/softmodem.h | 14 |
2 files changed, 88 insertions, 25 deletions
diff --git a/src/hardware/serialport/softmodem.cpp b/src/hardware/serialport/softmodem.cpp index 6556538ab..d2c89ae17 100644 --- a/src/hardware/serialport/softmodem.cpp +++ b/src/hardware/serialport/softmodem.cpp @@ -143,11 +143,13 @@ CSerialModem::CSerialModem(const uint8_t port_idx, CommandLine *cmd) Reset(); // reset calls EnterIdleState setEvent(SERIAL_POLLING_EVENT, MODEM_TICKTIME); - // Enable telnet-mode if configured - if (getUintFromString("telnet:", val, cmd)) { - telnet_mode = (val == 1); - LOG_MSG("SERIAL: Port %" PRIu8 " telnet-mode %s", - GetPortNumber(), telnet_mode ? "enabled" : "disabled"); + // Select communication mode. + if (getUintFromString("telnet:", val, cmd) && val) { + SetNetMode(MODEM_NET_TELNET); + } else if (getUintFromString("netmode:", val, cmd) && val < MODEM_NET_COUNT) { + SetNetMode((ModemNetMode)val); + } else { + SetNetMode(MODEM_NET_RAW); } InstallationSuccessful=true; @@ -158,6 +160,22 @@ CSerialModem::~CSerialModem() { for (uint32_t i = SERIAL_BASE_EVENT_COUNT + 1; i <= SERIAL_MODEM_EVENT_COUNT; i++) removeEvent(i); + +} + +void CSerialModem::SetNetMode(ModemNetMode val) +{ + netmode = val; + + const char *modename; + switch (netmode) { + case MODEM_NET_RAW: modename = "raw"; break; + case MODEM_NET_TELNET: modename = "telnet"; break; + case MODEM_NET_AUTH: modename = "auth"; break; + default: modename = "ERROR"; break; + } + LOG_MSG("SERIAL: Port %" PRIu8 " modem set to %s mode", GetPortNumber(), + modename); } void CSerialModem::handleUpperEvent(uint16_t type) @@ -296,11 +314,20 @@ bool CSerialModem::Dial(const char * host) { if (!clientsocket->isopen) { clientsocket.reset(nullptr); LOG_MSG("SERIAL: Port %" PRIu8 " failed to connect.", GetPortNumber()); - SendRes(ResNOCARRIER); + SendRes(ResNODIALTONE); EnterIdleState(); return false; } else { - EnterConnectedState(); + if (netmode == MODEM_NET_AUTH) { + // Don't accept calls while dialing. + serversocket.reset(); + // Wait for answer confirmation. + assert(!dialing); + dialing = true; + } else { + // Enter data mode immediately. + EnterConnectedState(); + } return true; } } @@ -309,6 +336,11 @@ void CSerialModem::AcceptIncomingCall() { if (waitingclientsocket) { clientsocket = std::move(waitingclientsocket); EnterConnectedState(); + + if (netmode == MODEM_NET_AUTH) + // Send answer confirmation. + assert(!dialing); + clientsocket->Putchar(MODEM_ANSWER_MAGIC); } else { EnterIdleState(); } @@ -363,16 +395,16 @@ void CSerialModem::Reset(){ void CSerialModem::EnterIdleState(){ connected = false; ringing = false; + dialing = false; dtrofftimer = -1; clientsocket.reset(nullptr); waitingclientsocket.reset(nullptr); // get rid of everything if (serversocket) { - waitingclientsocket.reset(serversocket->Accept()); - while (waitingclientsocket) { + do { waitingclientsocket.reset(serversocket->Accept()); - } + } while (waitingclientsocket); } if (listenport) { serversocket.reset(nullptr); @@ -407,6 +439,7 @@ void CSerialModem::EnterConnectedState() { telClient = {}; // reset values connected = true; ringing = false; + dialing = false; dtrofftimer = -1; CSerial::setCD(true); CSerial::setRI(false); @@ -458,16 +491,12 @@ void CSerialModem::DoCommand() const uint32_t requested_mode = ScanNumber(scanbuf); // If the mode isn't valid then stop parsing - if (requested_mode != 1 && requested_mode != 0) { + if (requested_mode >= MODEM_NET_COUNT) { SendRes(ResERROR); return; } - // Inform the user on changes - if (telnet_mode != static_cast<bool>(requested_mode)) { - telnet_mode = requested_mode; - LOG_MSG("SERIAL: Port %" PRIu8 " telnet-mode %s", - GetPortNumber(), - telnet_mode ? "enabled" : "disabled"); + if (netmode != requested_mode) { + SetNetMode((ModemNetMode)requested_mode); } break; } @@ -581,7 +610,8 @@ void CSerialModem::DoCommand() case 'H': // Hang up switch (ScanNumber(scanbuf)) { case 0: - if (connected) { + if (connected || dialing) { + assert(clientsocket); SendRes(ResNOCARRIER); EnterIdleState(); return; @@ -592,7 +622,8 @@ void CSerialModem::DoCommand() case 'O': // Return to data mode switch (ScanNumber(scanbuf)) { case 0: - if (clientsocket) { + if (connected) { + assert(clientsocket); commandmode = false; return; } else { @@ -619,7 +650,7 @@ void CSerialModem::DoCommand() case 'Z': { // Reset and load profiles // scan the number away, if any ScanNumber(scanbuf); - if (clientsocket) + if (connected) SendRes(ResNOCARRIER); Reset(); break; @@ -852,7 +883,25 @@ void CSerialModem::Echo(uint8_t ch) void CSerialModem::Timer2() { - uint32_t txbuffersize = 0; + if (dialing && clientsocket) { + // Don't enter connected state and data mode until we get answer + // confirmation. + assert(netmode == MODEM_NET_AUTH); + size_t usesize = 1; + if (!clientsocket->ReceiveArray(tmpbuf, usesize)) { + LOG_MSG("SERIAL: Remote host hung up without answering."); + SendRes(ResNOANSWER); + EnterIdleState(); + } else if (usesize) { + if (tmpbuf[0] == MODEM_ANSWER_MAGIC) { + EnterConnectedState(); + } else { + LOG_MSG("SERIAL: Wrong answer packet from remote host."); + SendRes(ResNOANSWER); + EnterIdleState(); + } + } + } // Check for eventual break command if (!commandmode) { @@ -873,6 +922,8 @@ void CSerialModem::Timer2() } } + uint32_t txbuffersize = 0; + // Handle incoming data from serial port, read as much as available CSerial::setCTS(true); // buffer will get 'emptier', new data can be received while (tqueue->inuse()) { @@ -927,7 +978,7 @@ void CSerialModem::Timer2() } } // while loop - if (clientsocket && txbuffersize) { + if (connected && clientsocket && txbuffersize) { // down here it saves a lot of network traffic if (!clientsocket->SendArray(tmpbuf,txbuffersize)) { SendRes(ResNOCARRIER); @@ -945,12 +996,13 @@ void CSerialModem::Timer2() EnterIdleState(); } else if (usesize) { // Filter telnet commands - if (telnet_mode) + if (netmode == MODEM_NET_TELNET) TelnetEmulation(tmpbuf, usesize); else rqueue->adds(tmpbuf,usesize); } } + // Check for incoming calls if (!connected && !waitingclientsocket && serversocket) { waitingclientsocket.reset(serversocket->Accept()); @@ -985,6 +1037,7 @@ void CSerialModem::Timer2() --ringtimer; } + // Handle DTR drop actions if (connected && !getDTR()) { if (dtrofftimer == 0) { switch (dtrmode) { diff --git a/src/hardware/serialport/softmodem.h b/src/hardware/serialport/softmodem.h index 6d0b2578e..c82546402 100644 --- a/src/hardware/serialport/softmodem.h +++ b/src/hardware/serialport/softmodem.h @@ -37,6 +37,8 @@ #define MODEM_DEFAULT_PORT 23 +#define MODEM_ANSWER_MAGIC 0x01 + #define MODEM_TX_EVENT SERIAL_BASE_EVENT_COUNT + 1 #define MODEM_RX_POLLING SERIAL_BASE_EVENT_COUNT + 2 #define MODEM_RING_EVENT SERIAL_BASE_EVENT_COUNT + 3 @@ -59,6 +61,13 @@ enum ResTypes { ResNOANSWER }; +enum ModemNetMode { + MODEM_NET_RAW = 0, // Raw null-modem like connection + MODEM_NET_TELNET, // Interpret IAC + MODEM_NET_AUTH, // Custom protocol for modem emulation + MODEM_NET_COUNT +}; + #define TEL_CLIENT 0 #define TEL_SERVER 1 @@ -179,6 +188,7 @@ public: CSerialModem(const uint8_t port_idx, CommandLine *cmd); ~CSerialModem(); void Reset(); + void SetNetMode(const ModemNetMode val); void SendLine(const char *line); void SendRes(const ResTypes response); @@ -229,9 +239,9 @@ protected: bool ringing = false; bool numericresponse = false; // true: send control response as number. // false: send text (i.e. NO DIALTONE) - bool telnet_mode = false; // Default to direct null modem connection; - // Telnet mode interprets IAC + ModemNetMode netmode = MODEM_NET_RAW; bool connected = false; + bool dialing = false; uint32_t doresponse = 0; uint8_t waiting_tx_character = 0; uint32_t cmdpause = 0; |