Skip to content

Commit afbca53

Browse files
authored
Merge pull request #416 from david-cermak/fix/modem_undef_mode
fix(modem): Fixed mode transitions between any state and UNDEF
2 parents 3f49583 + 5396de4 commit afbca53

File tree

5 files changed

+170
-13
lines changed

5 files changed

+170
-13
lines changed

components/esp_modem/examples/modem_console/main/modem_console_main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,9 @@ extern "C" void app_main(void)
235235
if (c->get_count_of(&SetModeArgs::mode)) {
236236
auto mode = c->get_string_of(&SetModeArgs::mode);
237237
modem_mode dev_mode;
238-
if (mode == "CMUX1") {
238+
if (mode == "UNDEF") {
239+
dev_mode = esp_modem::modem_mode::UNDEF;
240+
} else if (mode == "CMUX1") {
239241
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_MODE;
240242
} else if (mode == "CMUX2") {
241243
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_EXIT;

components/esp_modem/src/esp_modem_dce.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -96,6 +96,11 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
9696
{
9797
switch (m) {
9898
case modem_mode::UNDEF:
99+
if (!dte->set_mode(m)) {
100+
return false;
101+
}
102+
mode = m;
103+
return true;
99104
case modem_mode::DUAL_MODE: // Only DTE can be in Dual mode
100105
break;
101106
case modem_mode::COMMAND_MODE:
@@ -151,7 +156,7 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
151156
mode = modem_mode::CMUX_MANUAL_MODE;
152157
return true;
153158
case modem_mode::CMUX_MANUAL_EXIT:
154-
if (mode != modem_mode::CMUX_MANUAL_MODE) {
159+
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
155160
return false;
156161
}
157162
if (!dte->set_mode(m)) {
@@ -160,20 +165,20 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
160165
mode = modem_mode::COMMAND_MODE;
161166
return true;
162167
case modem_mode::CMUX_MANUAL_SWAP:
163-
if (mode != modem_mode::CMUX_MANUAL_MODE) {
168+
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
164169
return false;
165170
}
166171
if (!dte->set_mode(m)) {
167172
return false;
168173
}
169174
return true;
170175
case modem_mode::CMUX_MANUAL_DATA:
171-
if (mode != modem_mode::CMUX_MANUAL_MODE) {
176+
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
172177
return false;
173178
}
174179
return transitions::enter_data(*dte, *device, netif);
175180
case modem_mode::CMUX_MANUAL_COMMAND:
176-
if (mode != modem_mode::CMUX_MANUAL_MODE) {
181+
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
177182
return false;
178183
}
179184
return transitions::exit_data(*dte, *device, netif);

components/esp_modem/src/esp_modem_dte.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,14 @@ command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32
151151

152152
bool DTE::exit_cmux()
153153
{
154+
if (!cmux_term) {
155+
return false;
156+
}
154157
if (!cmux_term->deinit()) {
155158
return false;
156159
}
157160
exit_cmux_internal();
161+
cmux_term.reset();
158162
return true;
159163
}
160164

@@ -174,6 +178,10 @@ void DTE::exit_cmux_internal()
174178

175179
bool DTE::setup_cmux()
176180
{
181+
if (cmux_term) {
182+
ESP_LOGE("esp_modem_dte", "Cannot setup_cmux(), cmux_term already exists");
183+
return false;
184+
}
177185
cmux_term = std::make_shared<CMux>(primary_term, std::move(buffer));
178186
if (cmux_term == nullptr) {
179187
return false;
@@ -198,6 +206,11 @@ bool DTE::setup_cmux()
198206

199207
bool DTE::set_mode(modem_mode m)
200208
{
209+
// transitions (any) -> UNDEF
210+
if (m == modem_mode::UNDEF) {
211+
mode = m;
212+
return true;
213+
}
201214
// transitions (COMMAND|UNDEF) -> CMUX
202215
if (m == modem_mode::CMUX_MODE) {
203216
if (mode == modem_mode::UNDEF || mode == modem_mode::COMMAND_MODE) {
@@ -246,7 +259,7 @@ bool DTE::set_mode(modem_mode m)
246259
return false;
247260
}
248261
// manual CMUX transitions: Exit CMUX
249-
if (m == modem_mode::CMUX_MANUAL_EXIT && mode == modem_mode::CMUX_MANUAL_MODE) {
262+
if (m == modem_mode::CMUX_MANUAL_EXIT && (mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::UNDEF)) {
250263
if (exit_cmux()) {
251264
mode = modem_mode::COMMAND_MODE;
252265
return true;
@@ -255,7 +268,7 @@ bool DTE::set_mode(modem_mode m)
255268
return false;
256269
}
257270
// manual CMUX transitions: Swap terminals
258-
if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) {
271+
if (m == modem_mode::CMUX_MANUAL_SWAP && (mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::UNDEF)) {
259272
secondary_term.swap(primary_term);
260273
set_command_callbacks();
261274
return true;

components/esp_modem/test/host_test/main/test_modem.cpp

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Unlicense OR CC0-1.0
55
*/
@@ -247,3 +247,88 @@ TEST_CASE("Test CMUX protocol by injecting payloads", "[esp_modem]")
247247
CHECK(ret == command_result::OK);
248248
}
249249
}
250+
251+
TEST_CASE("Command and Data mode transitions", "[esp_modem][transitions]")
252+
{
253+
auto term = std::make_unique<LoopbackTerm>();
254+
auto loopback = term.get();
255+
auto dte = std::make_shared<DTE>(std::move(term));
256+
CHECK(term == nullptr);
257+
258+
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
259+
esp_netif_t netif{};
260+
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
261+
CHECK(dce != nullptr);
262+
263+
// UNDEF -> CMD (OK)
264+
uint8_t resp[] = "DISCONNECTED\n";
265+
loopback->inject(&resp[0], sizeof(resp), sizeof(resp), /* 10ms before injecting reply */100, 0);
266+
loopback->write(nullptr, 0); /* this triggers sending the injected response */
267+
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
268+
loopback->inject(nullptr, 0, 0, 0, 0); /* reset injection, use synchronous replies now */
269+
// CMD -> CMD (Fail)
270+
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == false);
271+
272+
// Forcing transition to CMD (via UNDEF)
273+
// CMD -> UNDEF (OK)
274+
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
275+
// UNDEF -> CMD (OK)
276+
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
277+
278+
// CMD -> DATA (OK)
279+
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == true);
280+
// DATA -> CMD (OK)
281+
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
282+
}
283+
284+
TEST_CASE("CMUX mode transitions", "[esp_modem][transitions]")
285+
{
286+
auto term = std::make_unique<LoopbackTerm>();
287+
auto dte = std::make_shared<DTE>(std::move(term));
288+
CHECK(term == nullptr);
289+
290+
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
291+
esp_netif_t netif{};
292+
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
293+
CHECK(dce != nullptr);
294+
// UNDEF -> CMUX (OK)
295+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MODE) == true);
296+
// CMUX -> DATA (Fail)
297+
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == false);
298+
// CMUX back -> CMD (OK)
299+
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
300+
}
301+
302+
TEST_CASE("CMUX manual mode transitions", "[esp_modem][transitions]")
303+
{
304+
auto term = std::make_unique<LoopbackTerm>();
305+
auto dte = std::make_shared<DTE>(std::move(term));
306+
CHECK(term == nullptr);
307+
308+
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
309+
esp_netif_t netif{};
310+
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
311+
CHECK(dce != nullptr);
312+
313+
// Happy flow transitions of Manual CMUX transitions
314+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true);
315+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true);
316+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true);
317+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == true);
318+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_DATA) == true);
319+
// Cannot test CMUX_MANUAL_DATA -> CMUX_MANUAL_COMMAND with our mocked terminal for now
320+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true);
321+
322+
// Check some out of order manual transitions, most of them are allowed,
323+
// but some fail as modem layers report issues with specific steps
324+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == false); // cannot go directly to SWAP
325+
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
326+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == true); // can go via UNDEF
327+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == false); // EXIT is allowed, but CMUX terms don't exist
328+
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
329+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true); // Enter CMUX (via UNDEF)
330+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_DATA) == true); // Go directly to DATA mode
331+
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true); // Exit CMUX
332+
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true); // Succeeds from any state
333+
334+
}

docs/esp_modem/en/README.rst

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,68 @@ Common use cases of the esp-modem are also listed as the examples:
8181
- ``examples/modem_console`` is an example to exercise all possible module commands in a console application.
8282
- ``examples/ap_to_pppos`` this example focuses on the network connectivity of the esp-modem and provides a WiFi AP that forwards packets (and uses NAT) to and from the PPPoS connection.
8383

84+
Working modes
85+
~~~~~~~~~~~~~
86+
87+
Modem devices could work in multiple different modes, esp-modem library
88+
uses these states to describe them:
89+
- Standard modes:
90+
- Command mode -- This mode is used for sending AT commands
91+
- Data or PPP mode -- This mode is used for data communication (to create PPPoS tunnel between the device and the library)
92+
- Multiplexing modes:
93+
- CMUX mode -- This mode creates two virtual channels and uses one for sending AT commands and the other one for data communication.
94+
- DUAL mode -- This mode uses two physical channels the same way as CMUX. This mode is supported only by certain devices, usually with USB interface.
95+
- Manual CMUX modes -- These modes are designed for applications to have better control over CMUX mode transitions. It allows setting up the virtual channels,
96+
switching between channels, transitioning between data and command modes for each channel separately, and exiting the CMUX.
97+
98+
Switching between common modes
99+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
100+
101+
The diagram below depicts allowed transitions between the most common modes
102+
103+
::
104+
105+
+---------+ +---------+
106+
| COMMAND |<-->| DATA |
107+
+---------+ +---------+
108+
^
109+
|
110+
v
111+
+-------+
112+
| CMUX |
113+
+-------+
114+
115+
Note that it is possible to switch from any mode to the "UNDEF" mode and vice-versa.
116+
117+
Switching between manual modes
118+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119+
120+
The diagram below depicts allowed transitions between manual CMUX modes
121+
122+
::
123+
+------------------------------------
124+
| |
125+
+----------+ +-------------+ +------------+ +----------+
126+
| |<-->| MANUAL_DATA |<-->| MANUAL_CMD |<-->| COMMAND |
127+
| CMUX | +-------------+ +------------+ | (CMUX |
128+
| MANUAL | | | MANUAL |
129+
| | +-------------+ | EXIT) |
130+
| |<-->| MANUAL_SWAP |<-------------------->| |
131+
+----------+ +-------------+ +----------+
132+
| |
133+
+-----------------------------------------------------+
134+
135+
Note that transitioning between "MANUAL_DATA" and "MANUAL_CMD" switches the secondary terminal (dedicated to PPP session) and could be used for recovering data communication if PPP session gets dropped.
136+
84137
Extensibility
85138
-------------
86139

87140
CMUX
88141
~~~~
89142

90-
Implementation of virtual terminals is an experimental feature, which
91-
allows users to also issue commands in the data mode, after creating
92-
multiple virtual terminals, designating some of them solely to data
93-
mode, others solely to command mode.
143+
Implements virtual terminals which allow users to also issue commands in the data mode;
144+
after creating two virtual terminals, designating one of them solely to data mode, and
145+
another one solely to command mode.
94146

95147
DTE
96148
~~~

0 commit comments

Comments
 (0)