Skip to content

Commit 85abac4

Browse files
committed
feat(modem): Add support for guessing mode
1 parent b2272b4 commit 85abac4

File tree

6 files changed

+206
-9
lines changed

6 files changed

+206
-9
lines changed

components/esp_modem/include/cxx_include/esp_modem_dce.hpp

Lines changed: 8 additions & 1 deletion
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-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -30,9 +30,11 @@ class DCE_Mode {
3030
~DCE_Mode() = default;
3131
bool set(DTE *dte, ModuleIf *module, Netif &netif, modem_mode m);
3232
modem_mode get();
33+
modem_mode guess(DTE *dte, bool with_cmux = false);
3334

3435
private:
3536
bool set_unsafe(DTE *dte, ModuleIf *module, Netif &netif, modem_mode m);
37+
modem_mode guess_unsafe(DTE *dte, bool with_cmux);
3638
modem_mode mode;
3739

3840
};
@@ -79,6 +81,11 @@ class DCE_T {
7981
return dte->command(command, std::move(got_line), time_ms);
8082
}
8183

84+
modem_mode guess_mode(bool with_cmux = false)
85+
{
86+
return mode.guess(dte.get(), with_cmux);
87+
}
88+
8289
bool set_mode(modem_mode m)
8390
{
8491
return mode.set(dte.get(), device.get(), netif, m);

components/esp_modem/include/cxx_include/esp_modem_dte.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class DTE : public CommandableIf {
6464
int write(uint8_t *data, size_t len) override;
6565

6666
int write(DTE_Command command);
67+
int send(uint8_t *data, size_t len, int term_id = 1);
6768

6869
/**
6970
* @brief Reading from the underlying terminal

components/esp_modem/include/cxx_include/esp_modem_types.hpp

Lines changed: 7 additions & 1 deletion
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-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -37,6 +37,11 @@ enum class modem_mode {
3737
CMUX_MANUAL_DATA, /*!< Sets the primary terminal to DATA mode in manual CMUX */
3838
CMUX_MANUAL_COMMAND, /*!< Sets the primary terminal to COMMAND mode in manual CMUX */
3939
CMUX_MANUAL_SWAP, /*!< Swaps virtual terminals in manual CMUX mode (primary <-> secondary) */
40+
RESUME_DATA_MODE,
41+
RESUME_COMMAND_MODE,
42+
RESUME_CMUX_MANUAL_MODE,
43+
RESUME_CMUX_MANUAL_DATA,
44+
AUTODETECT,
4045
};
4146

4247
/**
@@ -57,6 +62,7 @@ struct PdpContext {
5762
explicit PdpContext(std::string apn) : apn(std::move(apn)) {}
5863
size_t context_id = 1;
5964
std::string protocol_type = "IP";
65+
// std::string protocol_type = "IPV6";
6066
std::string apn;
6167
};
6268

components/esp_modem/src/esp_modem_dce.cpp

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -103,6 +103,49 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
103103
return true;
104104
case modem_mode::DUAL_MODE: // Only DTE can be in Dual mode
105105
break;
106+
case modem_mode::AUTODETECT: {
107+
auto guessed = guess_unsafe(dte, true);
108+
if (guessed == modem_mode::UNDEF) {
109+
return false;
110+
}
111+
// prepare the undefined mode before to allow all possible transitions
112+
if (!dte->set_mode(modem_mode::UNDEF)) {
113+
return false;
114+
}
115+
mode = modem_mode::UNDEF;
116+
ESP_LOGI("DCE mode", "Detected mode: %d", static_cast<int>(guessed));
117+
if (guessed == modem_mode::DATA_MODE) {
118+
return set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_DATA_MODE);
119+
} else if (guessed == esp_modem::modem_mode::COMMAND_MODE) {
120+
return set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_COMMAND_MODE);
121+
} else if (guessed == esp_modem::modem_mode::CMUX_MODE) {
122+
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_MODE)) {
123+
return false;
124+
}
125+
// now we guess the mode for each terminal
126+
guessed = guess_unsafe(dte, false);
127+
ESP_LOGI("DCE mode", "Detected mode on primary term: %d", static_cast<int>(guessed));
128+
if (guessed == modem_mode::DATA_MODE) {
129+
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_DATA)) {
130+
return false;
131+
}
132+
// vTaskDelay(pdMS_TO_TICKS(1000));
133+
} else {
134+
// swap terminals, and do the same for the secondary terminal
135+
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::CMUX_MANUAL_SWAP)) {
136+
return false;
137+
}
138+
}
139+
guessed = guess_unsafe(dte, false);
140+
ESP_LOGI("DCE mode", "Detected mode on secondary term: %d", static_cast<int>(guessed));
141+
if (guessed == modem_mode::DATA_MODE) {
142+
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_DATA)) {
143+
return false;
144+
}
145+
}
146+
}
147+
return true;
148+
}
106149
case modem_mode::COMMAND_MODE:
107150
if (mode == modem_mode::COMMAND_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
108151
return false;
@@ -122,6 +165,35 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
122165
}
123166
mode = m;
124167
return true;
168+
case modem_mode::RESUME_DATA_MODE:
169+
if (!dte->set_mode(modem_mode::DATA_MODE)) {
170+
return false;
171+
}
172+
netif.start();
173+
mode = modem_mode::DATA_MODE;
174+
return true;
175+
case modem_mode::RESUME_COMMAND_MODE:
176+
if (!dte->set_mode(modem_mode::COMMAND_MODE)) {
177+
return false;
178+
}
179+
mode = modem_mode::COMMAND_MODE;
180+
return true;
181+
case modem_mode::RESUME_CMUX_MANUAL_MODE:
182+
if (!dte->set_mode(modem_mode::CMUX_MANUAL_MODE)) {
183+
return false;
184+
}
185+
mode = modem_mode::CMUX_MANUAL_MODE;
186+
return true;
187+
case modem_mode::RESUME_CMUX_MANUAL_DATA:
188+
if (!dte->set_mode(modem_mode::CMUX_MANUAL_SWAP)) {
189+
return false;
190+
}
191+
netif.start();
192+
// if (!dte->set_mode(modem_mode::CMUX_MANUAL_SWAP)) {
193+
// return false;
194+
// }
195+
mode = modem_mode::CMUX_MANUAL_MODE;
196+
return true;
125197
case modem_mode::DATA_MODE:
126198
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
127199
return false;
@@ -191,4 +263,107 @@ modem_mode DCE_Mode::get()
191263
return mode;
192264
}
193265

266+
modem_mode DCE_Mode::guess(DTE *dte, bool with_cmux)
267+
{
268+
Scoped<DTE> lock(*dte);
269+
return guess_unsafe(dte, with_cmux);
270+
}
271+
272+
namespace probe {
273+
274+
namespace ppp {
275+
const uint8_t lcp_echo_request[] = {0x7e, 0xff, 0x03, 0xc0, 0x21, 0x09, 0x01, 0x00, 0x08, 0x99, 0xd1, 0x35, 0xc1, 0x8e, 0x2c, 0x7e };
276+
const uint8_t lcp_echo_reply_head[] = {0x7e, 0xff, 0x7d, 0x23, 0xc0};
277+
const size_t mode = 1 << 0;
278+
const int timeout = 200;
279+
}
280+
281+
namespace cmd {
282+
const char at[] = "\r\nAT\r\n";
283+
size_t max_at_reply = 16; // account for some whitespaces and/or CMUX encapsulation
284+
const char reply[] = { 'O', 'K' };
285+
const int mode = 1 << 1;
286+
const int timeout = 500;
287+
}
288+
289+
namespace cmux {
290+
const uint8_t sabm0_reqest[] = {0xf9, 0x03, 0x3f, 0x01, 0x1c, 0xf9};
291+
const uint8_t sabm0_reply[] = {0xf9, 0x03, 0x73, 0x01};
292+
const int mode = 1 << 0;
293+
const int timeout = 200;
294+
}
295+
};
296+
297+
modem_mode DCE_Mode::guess_unsafe(DTE *dte, bool with_cmux)
298+
{
299+
// placeholder for reply and its size, since it could come in pieces, and we have to cache
300+
// this is passed to the lambda as references.
301+
// must make sure the lambda is cleared before exiting this function (done by dte->on_read(nullptr))
302+
uint8_t reply[std::max(probe::cmd::max_at_reply, std::max(sizeof(probe::ppp::lcp_echo_request), sizeof(probe::cmux::sabm0_reply)))];
303+
size_t reply_pos = 0;
304+
auto signal = std::make_shared<SignalGroup>();
305+
std::weak_ptr<SignalGroup> weak_signal = signal;
306+
dte->on_read([weak_signal, with_cmux, &reply, &reply_pos](uint8_t *data, size_t len) {
307+
// storing the response in the `reply` array and de-fragmenting
308+
if (reply_pos >= sizeof(reply)) {
309+
return command_result::TIMEOUT;
310+
}
311+
auto reply_size = std::min((size_t)sizeof(reply) - reply_pos, len);
312+
::memcpy(reply + reply_pos, data, reply_size);
313+
reply_pos += reply_size;
314+
ESP_LOG_BUFFER_HEXDUMP("esp-modem: guess mode data:", reply, reply_pos, ESP_LOG_ERROR);
315+
316+
// Check whether the response resembles the "golden" reply (for these 3 protocols)
317+
if (reply_pos >= sizeof(probe::ppp::lcp_echo_reply_head)) {
318+
// check for initial 2 bytes
319+
uint8_t *ptr = static_cast<uint8_t *>(memmem(reply, reply_pos, probe::ppp::lcp_echo_reply_head, 2));
320+
// and check the other two bytes for protocol ID: LCP
321+
if (ptr && ptr[3] == probe::ppp::lcp_echo_reply_head[3] && ptr[4] == probe::ppp::lcp_echo_reply_head[4]) {
322+
if (auto signal = weak_signal.lock()) {
323+
signal->set(probe::ppp::mode);
324+
}
325+
}
326+
}
327+
if (reply_pos >= 4 && memmem(reply, reply_pos, probe::cmd::reply, sizeof(probe::cmd::reply))) {
328+
if (reply[0] != 0xf9) { // double check that the reply is not wrapped in CMUX headers
329+
if (auto signal = weak_signal.lock()) {
330+
signal->set(probe::cmd::mode);
331+
}
332+
}
333+
}
334+
if (with_cmux && reply_pos >= sizeof(probe::cmux::sabm0_reply)) {
335+
// checking the initial 3 bytes
336+
uint8_t *ptr = static_cast<uint8_t *>(memmem(reply, reply_pos, probe::cmux::sabm0_reply, 3));
337+
// and checking that DLCI is 0 (control frame)
338+
if (ptr && (ptr[3] >> 2) == 0) {
339+
if (auto signal = weak_signal.lock()) {
340+
signal->set(probe::cmux::mode);
341+
}
342+
}
343+
}
344+
return command_result::TIMEOUT;
345+
});
346+
auto guessed = modem_mode::UNDEF;
347+
// Check the PPP mode fist by sending LCP echo request
348+
dte->send((uint8_t *)probe::ppp::lcp_echo_request, sizeof(probe::ppp::lcp_echo_request), 0);
349+
if (signal->wait(probe::ppp::mode, probe::ppp::timeout)) {
350+
guessed = modem_mode::DATA_MODE;
351+
} else { // LCP echo timeout
352+
// now check for AT mode
353+
reply_pos = 0;
354+
dte->send((uint8_t *)probe::cmd::at, sizeof(probe::cmd::at), 0);
355+
if (signal->wait(probe::cmd::mode, probe::cmd::timeout)) {
356+
guessed = modem_mode::COMMAND_MODE;
357+
} else if (with_cmux) { // no AT reply, check for CMUX mode (if requested)
358+
reply_pos = 0;
359+
dte->send((uint8_t *) probe::cmux::sabm0_reqest, sizeof(probe::cmux::sabm0_reqest), 0);
360+
if (signal->wait(probe::cmux::mode, probe::cmux::timeout)) {
361+
guessed = modem_mode::CMUX_MODE;
362+
}
363+
}
364+
}
365+
dte->on_read(nullptr);
366+
return guessed;
367+
}
368+
194369
} // esp_modem

components/esp_modem/src/esp_modem_dte.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -223,13 +223,13 @@ bool DTE::set_mode(modem_mode m)
223223
}
224224
}
225225
// transitions (COMMAND|DUAL|CMUX|UNDEF) -> DATA
226-
if (m == modem_mode::DATA_MODE) {
226+
if (m == modem_mode::DATA_MODE || m == modem_mode::RESUME_DATA_MODE) {
227227
if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::DUAL_MODE) {
228228
// mode stays the same, but need to swap terminals (as command has been switched)
229229
secondary_term.swap(primary_term);
230230
set_command_callbacks();
231231
} else {
232-
mode = m;
232+
mode = modem_mode::DATA_MODE;
233233
}
234234
return true;
235235
}
@@ -316,6 +316,12 @@ int DTE::write(uint8_t *data, size_t len)
316316
return secondary_term->write(data, len);
317317
}
318318

319+
int DTE::send(uint8_t *data, size_t len, int term_id)
320+
{
321+
Terminal *term = term_id == 0 ? primary_term.get() : secondary_term.get();
322+
return term->write(data, len);
323+
}
324+
319325
int DTE::write(DTE_Command command)
320326
{
321327
return primary_term->write(command.data, command.len);

components/esp_modem/src/esp_modem_netif.cpp

Lines changed: 5 additions & 3 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-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -87,8 +87,10 @@ void Netif::start()
8787
receive(data, len);
8888
return true;
8989
});
90-
signal.set(PPP_STARTED);
91-
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
90+
if (!signal.is_any(PPP_STARTED)) {
91+
signal.set(PPP_STARTED);
92+
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
93+
}
9294
}
9395

9496
void Netif::stop()

0 commit comments

Comments
 (0)