feat(esp_modem): Add support to CMUX exit

Closes https://github.com/espressif/esp-protocols/issues/37
This commit is contained in:
David Cermak
2022-05-26 17:31:22 +02:00
parent 1e0aefd72a
commit a16aab6979
5 changed files with 111 additions and 2 deletions

View File

@ -64,6 +64,13 @@ public:
*/ */
[[nodiscard]] bool init(); [[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<Terminal>, std::unique_ptr<uint8_t[]>, size_t> deinit_and_eject();
/** /**
* @brief Sets read callback for the appropriate terminal * @brief Sets read callback for the appropriate terminal
* @param inst Index of the 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 */ 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 data_available(uint8_t *data, size_t len); /*!< Called when valid data available */
void send_sabm(size_t i); /*!< Sending initial SABM */ 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 */ 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 */ struct CMuxFrame; /*!< Forward declare the Frame struct, used in protocol decoders */
@ -147,6 +157,7 @@ public:
} }
void start() override { } void start() override { }
void stop() override { } void stop() override { }
CMux* get_cmux() { return cmux.get(); }
private: private:
std::shared_ptr<CMux> cmux; std::shared_ptr<CMux> cmux;
size_t instance; size_t instance;

View File

@ -98,6 +98,7 @@ private:
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */ static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */ [[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode */
Lock lock{}; /*!< Locks DTE operations */ Lock lock{}; /*!< Locks DTE operations */
size_t buffer_size; /*!< Size of available DTE buffer */ size_t buffer_size; /*!< Size of available DTE buffer */

View File

@ -78,6 +78,25 @@ uint8_t CMux::fcs_crc(const uint8_t frame[6])
return crc; 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) void CMux::send_sabm(size_t i)
{ {
uint8_t frame[6]; uint8_t frame[6];
@ -305,6 +324,31 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len)
return true; 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<Lock> 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() bool CMux::init()
{ {
frame_header_offset = 0; frame_header_offset = 0;
@ -370,3 +414,11 @@ void CMux::set_read_cb(int inst, std::function<bool(uint8_t *, size_t)> f)
read_cb[inst] = std::move(f); read_cb[inst] = std::move(f);
} }
} }
std::tuple<std::unique_ptr<Terminal>, std::unique_ptr<uint8_t[]>, 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);
}

View File

@ -33,6 +33,15 @@ bool DCE_Mode::set(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m)
if (mode == modem_mode::COMMAND_MODE) { if (mode == modem_mode::COMMAND_MODE) {
return false; 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(); netif.stop();
SignalGroup signal; SignalGroup signal;
dte->set_read_cb([&](uint8_t *data, size_t len) -> bool { 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)) { if (!dte->set_mode(modem_mode::CMUX_MODE)) {
return false; 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 true;
} }
return false; return false;

View File

@ -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'); return command(cmd, got_line, time_ms, '\n');
} }
bool DTE::exit_cmux()
{
auto cmux_term = static_cast<CMuxInstance*>(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() bool DTE::setup_cmux()
{ {
auto original_term = std::move(term); auto original_term = std::move(term);
@ -94,7 +111,16 @@ bool DTE::setup_cmux()
bool DTE::set_mode(modem_mode m) 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) { if (m == modem_mode::DATA_MODE) {
term->set_read_cb(on_data); term->set_read_cb(on_data);
if (other_term) { // if we have the other terminal, let's use it for commands if (other_term) { // if we have the other terminal, let's use it for commands