From a16aab69797d5c8aa9072b93485a1e687ed89d1f Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 26 May 2022 17:31:22 +0200 Subject: [PATCH] feat(esp_modem): Add support to CMUX exit Closes https://github.com/espressif/esp-protocols/issues/37 --- .../include/cxx_include/esp_modem_cmux.hpp | 11 ++++ .../include/cxx_include/esp_modem_dte.hpp | 1 + components/esp_modem/src/esp_modem_cmux.cpp | 52 +++++++++++++++++++ components/esp_modem/src/esp_modem_dce.cpp | 21 +++++++- components/esp_modem/src/esp_modem_dte.cpp | 28 +++++++++- 5 files changed, 111 insertions(+), 2 deletions(-) diff --git a/components/esp_modem/include/cxx_include/esp_modem_cmux.hpp b/components/esp_modem/include/cxx_include/esp_modem_cmux.hpp index f88a85ac2..d3bac0933 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_cmux.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_cmux.hpp @@ -64,6 +64,13 @@ public: */ [[nodiscard]] bool init(); + /** + * @brief Closes CMux protocol and ejects attached terminal and buffer + * @return nullptr on failure + * tuple of the original terminal and buffer on success + */ + std::tuple, std::unique_ptr, size_t> deinit_and_eject(); + /** * @brief Sets read callback for the appropriate terminal * @param inst Index of the terminal @@ -84,6 +91,9 @@ private: static uint8_t fcs_crc(const uint8_t frame[6]); /*!< Utility to calculate FCS CRC */ void data_available(uint8_t *data, size_t len); /*!< Called when valid data available */ void send_sabm(size_t i); /*!< Sending initial SABM */ + void send_disc(size_t i); /*!< Sending closing request for each virtual terminal */ + void close_down(); /*!< Close up the control terminla (term=0) */ + bool exit_cmux_protocol(); /*!< Sequence of exit of all virtual terms and control term */ bool on_cmux(uint8_t *data, size_t len); /*!< Called from terminal layer when raw CMUX protocol data available */ struct CMuxFrame; /*!< Forward declare the Frame struct, used in protocol decoders */ @@ -147,6 +157,7 @@ public: } void start() override { } void stop() override { } + CMux* get_cmux() { return cmux.get(); } private: std::shared_ptr cmux; size_t instance; diff --git a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp index c505b2ed1..09fc59d6d 100644 --- a/components/esp_modem/include/cxx_include/esp_modem_dte.hpp +++ b/components/esp_modem/include/cxx_include/esp_modem_dte.hpp @@ -98,6 +98,7 @@ private: static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */ [[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */ + [[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode */ Lock lock{}; /*!< Locks DTE operations */ size_t buffer_size; /*!< Size of available DTE buffer */ diff --git a/components/esp_modem/src/esp_modem_cmux.cpp b/components/esp_modem/src/esp_modem_cmux.cpp index f37bd8512..0838db27d 100644 --- a/components/esp_modem/src/esp_modem_cmux.cpp +++ b/components/esp_modem/src/esp_modem_cmux.cpp @@ -78,6 +78,25 @@ uint8_t CMux::fcs_crc(const uint8_t frame[6]) return crc; } +void CMux::close_down() +{ + uint8_t frame[] = { + SOF_MARKER, 0x3, 0xFF, 0x5, 0xC3, 0x1, 0xE7, SOF_MARKER }; + term->write(frame, 8); +} + +void CMux::send_disc(size_t i) +{ + uint8_t frame[6]; + frame[0] = SOF_MARKER; + frame[1] = (i << 2) | 0x3; + frame[2] = FT_DISC | PF; + frame[3] = 1; + frame[4] = 0xFF - fcs_crc(frame); + frame[5] = SOF_MARKER; + term->write(frame, 6); +} + void CMux::send_sabm(size_t i) { uint8_t frame[6]; @@ -305,6 +324,31 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len) return true; } +bool CMux::exit_cmux_protocol() +{ + sabm_ack = -1; + for (size_t i = 1; i < 3; i++) { + int timeout = 0; + send_disc(i); + while (true) { + usleep(10'000); + Scoped l(lock); + if (sabm_ack == i) { + sabm_ack = -1; + break; + } + if (timeout++ > 100) { + return false; + } + } + } + close_down(); + usleep(100'000); + term->set_read_cb(nullptr); + return true; + +} + bool CMux::init() { frame_header_offset = 0; @@ -370,3 +414,11 @@ void CMux::set_read_cb(int inst, std::function f) read_cb[inst] = std::move(f); } } + +std::tuple, std::unique_ptr, size_t> esp_modem::CMux::deinit_and_eject() +{ + if (exit_cmux_protocol()) { + return std::make_tuple(std::move(term), std::move(buffer), buffer_size); + } + return std::tuple(nullptr, nullptr, 0); +} diff --git a/components/esp_modem/src/esp_modem_dce.cpp b/components/esp_modem/src/esp_modem_dce.cpp index 735c5c2ee..14a9f9b02 100644 --- a/components/esp_modem/src/esp_modem_dce.cpp +++ b/components/esp_modem/src/esp_modem_dce.cpp @@ -33,6 +33,15 @@ bool DCE_Mode::set(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m) if (mode == modem_mode::COMMAND_MODE) { return false; } + if (mode == modem_mode::CMUX_MODE) { + netif.stop(); + netif.wait_until_ppp_exits(); + if (!dte->set_mode(modem_mode::COMMAND_MODE)) { + return false; + } + mode = m; + return true; + } netif.stop(); SignalGroup signal; dte->set_read_cb([&](uint8_t *data, size_t len) -> bool { @@ -90,7 +99,17 @@ bool DCE_Mode::set(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m) if (!dte->set_mode(modem_mode::CMUX_MODE)) { return false; } - mode = modem_mode::COMMAND_MODE; + mode = modem_mode::CMUX_MODE; + if (!device->setup_data_mode()) { + return false; + } + if (!device->set_mode(modem_mode::DATA_MODE)) { + return false; + } + if (!dte->set_mode(modem_mode::DATA_MODE)) { + return false; + } + netif.start(); return true; } return false; diff --git a/components/esp_modem/src/esp_modem_dte.cpp b/components/esp_modem/src/esp_modem_dte.cpp index 26a89868f..d8578df0a 100644 --- a/components/esp_modem/src/esp_modem_dte.cpp +++ b/components/esp_modem/src/esp_modem_dte.cpp @@ -69,6 +69,23 @@ command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32 return command(cmd, got_line, time_ms, '\n'); } +bool DTE::exit_cmux() +{ + auto cmux_term = static_cast(term.get())->get_cmux(); + auto ejected = cmux_term->deinit_and_eject(); + if (ejected == std::tuple(nullptr, nullptr, 0)) { + return false; + } + // deinit succeeded -> swap the internal terminals with those ejected from cmux + auto term_orig = std::move(term); + auto other_term_orig = std::move(other_term); + term = std::move(std::get<0>(ejected)); + buffer = std::move(std::get<1>(ejected)); + buffer_size = std::get<2>(ejected); + command_term = term.get(); // use command terminal as previously + return true; +} + bool DTE::setup_cmux() { auto original_term = std::move(term); @@ -94,7 +111,16 @@ bool DTE::setup_cmux() bool DTE::set_mode(modem_mode m) { - mode = m; + if (mode == modem_mode::CMUX_MODE && m == modem_mode::COMMAND_MODE) { + if (exit_cmux()) { + mode = m; + return true; + } + return false; + } + if (mode != modem_mode::CMUX_MODE) { // keep CMUX internally, it's CMD+PPP + mode = m; + } if (m == modem_mode::DATA_MODE) { term->set_read_cb(on_data); if (other_term) { // if we have the other terminal, let's use it for commands