Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(esp-modem): Extend CMUX mode to support manual transitions #169

Merged
merged 2 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,17 @@ extern "C" void app_main(void)
if (c->get_count_of(&SetModeArgs::mode)) {
auto mode = c->get_string_of(&SetModeArgs::mode);
modem_mode dev_mode;
if (mode == "CMD") {
if (mode == "CMUX1") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_MODE;
} else if (mode == "CMUX2") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_EXIT;
} else if (mode == "CMUX3") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_SWAP;
} else if (mode == "CMUX4") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_DATA;
} else if (mode == "CMUX5") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_COMMAND;
} else if (mode == "CMD") {
dev_mode = esp_modem::modem_mode::COMMAND_MODE;
} else if (mode == "PPP") {
dev_mode = esp_modem::modem_mode::DATA_MODE;
Expand Down
4 changes: 2 additions & 2 deletions components/esp_modem/include/cxx_include/esp_modem_dte.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ class DTE : public CommandableIf {
Lock internal_lock{}; /*!< Locks DTE operations */
unique_buffer buffer; /*!< DTE buffer */
std::shared_ptr<CMux> cmux_term; /*!< Primary terminal for this DTE */
std::shared_ptr<Terminal> command_term; /*!< Reference to the terminal used for sending commands */
std::shared_ptr<Terminal> data_term; /*!< Secondary terminal for this DTE */
std::shared_ptr<Terminal> primary_term; /*!< Reference to the primary terminal (mostly for sending commands) */
std::shared_ptr<Terminal> secondary_term; /*!< Secondary terminal for this DTE */
modem_mode mode; /*!< DTE operation mode */
SignalGroup signal; /*!< Event group used to signal request-response operations */
command_result result; /*!< Command result of the currently exectuted command */
Expand Down
7 changes: 6 additions & 1 deletion components/esp_modem/include/cxx_include/esp_modem_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ enum class modem_mode {
UNDEF,
COMMAND_MODE, /*!< Command mode -- the modem is supposed to send AT commands in this mode */
DATA_MODE, /*!< Data mode -- the modem communicates with network interface on PPP protocol */
CMUX_MODE /*!< CMUX (Multiplex mode) -- Simplified CMUX mode, which creates two virtual terminals,
CMUX_MODE, /*!< CMUX (Multiplex mode) -- Simplified CMUX mode, which creates two virtual terminals,
* assigning one solely to command interface and the other to the data mode */
CMUX_MANUAL_MODE, /*!< Enter CMUX mode manually -- just creates two virtual terminals */
CMUX_MANUAL_EXIT, /*!< Exits CMUX mode manually -- just destroys two virtual terminals */
CMUX_MANUAL_DATA, /*!< Sets the primary terminal to DATA mode in manual CMUX */
CMUX_MANUAL_COMMAND, /*!< Sets the primary terminal to COMMAND mode in manual CMUX */
CMUX_MANUAL_SWAP, /*!< Swaps virtual terminals in manual CMUX mode (primary <-> secondary) */
};

/**
Expand Down
7 changes: 7 additions & 0 deletions components/esp_modem/include/esp_modem_c_api_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ typedef enum esp_modem_dce_mode {
ESP_MODEM_MODE_COMMAND, /**< Default mode after modem startup, used for sending AT commands */
ESP_MODEM_MODE_DATA, /**< Used for switching to PPP mode for the modem to connect to a network */
ESP_MODEM_MODE_CMUX, /**< Multiplexed terminal mode */
ESP_MODEM_MODE_CMUX_MANUAL, /**< CMUX manual mode */
ESP_MODEM_MODE_CMUX_MANUAL_EXIT, /**< Exit CMUX manual mode */
ESP_MODEM_MODE_CMUX_MANUAL_SWAP, /**< Swap terminals in CMUX manual mode */
ESP_MODEM_MODE_CMUX_MANUAL_DATA, /**< Set DATA mode in CMUX manual mode */
ESP_MODEM_MODE_CMUX_MANUAL_COMMAND, /**< Set COMMAND mode in CMUX manual mode */
} esp_modem_dce_mode_t;

/**
Expand Down Expand Up @@ -113,6 +118,8 @@ esp_err_t esp_modem_set_error_cb(esp_modem_dce_t *dce, esp_modem_terminal_error_
*/
esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce, esp_modem_dce_mode_t mode);

esp_err_t esp_modem_command(esp_modem_dce_t *dce, const char *command, esp_err_t(*got_line_cb)(uint8_t *data, size_t len), uint32_t timeout_ms);

/**
* @}
*/
Expand Down
37 changes: 32 additions & 5 deletions components/esp_modem/src/esp_modem_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,23 @@ extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
if (mode == ESP_MODEM_MODE_DATA) {
switch (mode) {
case ESP_MODEM_MODE_DATA:
return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL;
}
if (mode == ESP_MODEM_MODE_COMMAND) {
case ESP_MODEM_MODE_COMMAND:
return dce_wrap->dce->set_mode(modem_mode::COMMAND_MODE) ? ESP_OK : ESP_FAIL;
}
if (mode == ESP_MODEM_MODE_CMUX) {
case ESP_MODEM_MODE_CMUX:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_EXIT:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_EXIT) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_SWAP:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_SWAP) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_DATA:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL;
}
return ESP_ERR_NOT_SUPPORTED;
}
Expand Down Expand Up @@ -391,3 +400,21 @@ extern "C" esp_err_t esp_modem_set_pdp_context(esp_modem_dce_t *dce_wrap, esp_mo
pdp.protocol_type = c_api_pdp->protocol_type;
return command_response_to_esp_err(dce_wrap->dce->set_pdp_context(pdp));
}

extern "C" esp_err_t esp_modem_command(esp_modem_dce_t *dce_wrap, const char *command, esp_err_t(*got_line_fn)(uint8_t *data, size_t len), uint32_t timeout_ms)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || command == nullptr || got_line_fn == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string cmd(command);
return command_response_to_esp_err(dce_wrap->dce->command(cmd, [got_line_fn](uint8_t *data, size_t len) {
switch (got_line_fn(data, len)) {
case ESP_OK:
return command_result::OK;
case ESP_FAIL:
return command_result::FAIL;
default:
return command_result::TIMEOUT;
}
}, timeout_ms));
}
138 changes: 92 additions & 46 deletions components/esp_modem/src/esp_modem_dce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,59 @@

namespace esp_modem {

namespace transitions {

static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
{
netif.stop();
auto signal = std::make_shared<SignalGroup>();
std::weak_ptr<SignalGroup> weak_signal = signal;
dte.set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool {
if (memchr(data, '\n', len))
{
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG);
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED"});
std::string_view response((char *) data, len);
for (auto &it : pass)
if (response.find(it) != std::string::npos) {
if (auto signal = weak_signal.lock()) {
signal->set(1);
}
return true;
}
}
return false;
});
netif.wait_until_ppp_exits();
if (!signal->wait(1, 2000)) {
if (!device.set_mode(modem_mode::COMMAND_MODE)) {
return false;
}
}
dte.set_read_cb(nullptr);
if (!dte.set_mode(modem_mode::COMMAND_MODE)) {
return false;
}
return true;
}

static bool enter_data(DTE &dte, ModuleIf &device, Netif &netif)
{
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;
}

} // namespace transitions

/**
* Set mode while the entire DTE is locked
*/
Expand All @@ -36,8 +89,8 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
switch (m) {
case modem_mode::UNDEF:
break;
case modem_mode::COMMAND_MODE: {
if (mode == modem_mode::COMMAND_MODE) {
case modem_mode::COMMAND_MODE:
if (mode == modem_mode::COMMAND_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
return false;
}
if (mode == modem_mode::CMUX_MODE) {
Expand All @@ -49,59 +102,23 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
mode = m;
return true;
}
netif.stop();
auto signal = std::make_shared<SignalGroup>();
std::weak_ptr<SignalGroup> weak_signal = signal;
dte->set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool {
if (memchr(data, '\n', len))
{
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG);
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED"});
std::string_view response((char *) data, len);
for (auto &it : pass)
if (response.find(it) != std::string::npos) {
if (auto signal = weak_signal.lock()) {
signal->set(1);
}
return true;
}
}
return false;
});
netif.wait_until_ppp_exits();
if (!signal->wait(1, 2000)) {
if (!device->set_mode(modem_mode::COMMAND_MODE)) {
mode = modem_mode::UNDEF;
return false;
}
}
dte->set_read_cb(nullptr);
if (!dte->set_mode(modem_mode::COMMAND_MODE)) {
if (!transitions::exit_data(*dte, *device, netif)) {
mode = modem_mode::UNDEF;
return false;
}
mode = m;
return true;
}
break;
case modem_mode::DATA_MODE:
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE) {
return false;
}
if (!device->setup_data_mode()) {
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
return false;
}
if (!device->set_mode(modem_mode::DATA_MODE)) {
if (!transitions::enter_data(*dte, *device, netif)) {
return false;
}
if (!dte->set_mode(modem_mode::DATA_MODE)) {
return false;
}
netif.start();
mode = m;
return true;
case modem_mode::CMUX_MODE:
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE) {
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
return false;
}
device->set_mode(modem_mode::CMUX_MODE); // switch the device into CMUX mode
Expand All @@ -111,17 +128,46 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
return false;
}
mode = modem_mode::CMUX_MODE;
if (!device->setup_data_mode()) {
return transitions::enter_data(*dte, *device, netif);
case modem_mode::CMUX_MANUAL_MODE:
if (mode != modem_mode::COMMAND_MODE && mode != modem_mode::UNDEF) {
return false;
}
device->set_mode(modem_mode::CMUX_MODE);
usleep(100'000);

if (!dte->set_mode(m)) {
return false;
}
if (!device->set_mode(modem_mode::DATA_MODE)) {
mode = modem_mode::CMUX_MANUAL_MODE;
return true;
case modem_mode::CMUX_MANUAL_EXIT:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
return false;
}
if (!dte->set_mode(m)) {
return false;
}
mode = modem_mode::COMMAND_MODE;
return true;
case modem_mode::CMUX_MANUAL_SWAP:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
return false;
}
if (!dte->set_mode(modem_mode::DATA_MODE)) {
if (!dte->set_mode(m)) {
return false;
}
netif.start();
return true;
case modem_mode::CMUX_MANUAL_DATA:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
return false;
}
return transitions::enter_data(*dte, *device, netif);
case modem_mode::CMUX_MANUAL_COMMAND:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
return false;
}
return transitions::exit_data(*dte, *device, netif);
}
return false;
}
Expand Down
Loading