2022-10-11 16:31:57 +02:00
|
|
|
/*
|
2025-04-15 17:14:57 +02:00
|
|
|
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
2022-10-11 16:31:57 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
2021-03-24 21:06:27 +01:00
|
|
|
|
2022-06-05 15:43:56 +02:00
|
|
|
#include <list>
|
2022-05-10 17:55:55 +02:00
|
|
|
#include <unistd.h>
|
2022-06-05 15:43:56 +02:00
|
|
|
#include <cstring>
|
2022-05-10 17:55:55 +02:00
|
|
|
|
2021-03-24 21:06:27 +01:00
|
|
|
#include "cxx_include/esp_modem_dte.hpp"
|
|
|
|
|
#include "cxx_include/esp_modem_dce.hpp"
|
|
|
|
|
#include "esp_log.h"
|
|
|
|
|
|
2021-03-29 19:34:45 +02:00
|
|
|
namespace esp_modem {
|
2021-03-24 21:06:27 +01:00
|
|
|
|
2022-10-25 15:41:48 +02:00
|
|
|
namespace transitions {
|
|
|
|
|
|
|
|
|
|
static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
|
|
|
|
|
{
|
|
|
|
|
auto signal = std::make_shared<SignalGroup>();
|
|
|
|
|
std::weak_ptr<SignalGroup> weak_signal = signal;
|
2023-06-27 14:59:08 +02:00
|
|
|
dte.set_read_cb([&netif, weak_signal](uint8_t *data, size_t len) -> bool {
|
|
|
|
|
// post the transitioning data to the network layers if it contains PPP SOF marker
|
|
|
|
|
if (memchr(data, 0x7E, len))
|
|
|
|
|
{
|
|
|
|
|
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (PPP)", data, len, ESP_LOG_DEBUG);
|
|
|
|
|
netif.receive(data, len);
|
|
|
|
|
}
|
|
|
|
|
// treat the transitioning data as a textual message if it contains a newline char
|
2022-10-25 15:41:48 +02:00
|
|
|
if (memchr(data, '\n', len))
|
|
|
|
|
{
|
2023-06-27 14:59:08 +02:00
|
|
|
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (CMD)", data, len, ESP_LOG_DEBUG);
|
2024-11-28 21:34:15 +01:00
|
|
|
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED", "OK"});
|
2022-10-25 15:41:48 +02:00
|
|
|
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;
|
|
|
|
|
});
|
2024-11-28 21:34:15 +01:00
|
|
|
netif.stop();
|
2022-10-25 15:41:48 +02:00
|
|
|
netif.wait_until_ppp_exits();
|
2024-11-28 21:34:15 +01:00
|
|
|
#ifdef ESP_MODEM_PPP_ESCAPE_BEFORE_EXIT
|
|
|
|
|
std::array<uint8_t, 3> ppp_escape = {'+', '+', '+'};
|
|
|
|
|
dte.write(ppp_escape.data(), ppp_escape.size());
|
|
|
|
|
#endif
|
|
|
|
|
if (!signal->wait(1, 2000)) { // wait for any of the disconnection messages
|
|
|
|
|
// if no reply -> set device to command mode
|
2023-06-23 19:35:49 +02:00
|
|
|
dte.set_read_cb(nullptr);
|
2022-10-25 15:41:48 +02:00
|
|
|
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
|
|
|
|
|
|
2022-06-08 17:16:15 +02:00
|
|
|
/**
|
|
|
|
|
* Set mode while the entire DTE is locked
|
|
|
|
|
*/
|
2021-03-29 19:34:45 +02:00
|
|
|
bool DCE_Mode::set(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m)
|
2022-06-08 17:16:15 +02:00
|
|
|
{
|
|
|
|
|
Scoped<DTE> lock(*dte);
|
|
|
|
|
return set_unsafe(dte, device, netif, m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* state machine:
|
|
|
|
|
*
|
|
|
|
|
* COMMAND_MODE <----> DATA_MODE
|
|
|
|
|
* COMMAND_MODE <----> CMUX_MODE
|
|
|
|
|
*
|
|
|
|
|
* UNDEF <----> any
|
|
|
|
|
*/
|
|
|
|
|
bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m)
|
2021-03-24 21:06:27 +01:00
|
|
|
{
|
|
|
|
|
switch (m) {
|
2021-06-01 10:21:51 +02:00
|
|
|
case modem_mode::UNDEF:
|
2023-11-02 20:47:02 +01:00
|
|
|
if (!dte->set_mode(m)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = m;
|
|
|
|
|
return true;
|
2023-03-07 13:35:34 +01:00
|
|
|
case modem_mode::DUAL_MODE: // Only DTE can be in Dual mode
|
2021-06-01 10:21:51 +02:00
|
|
|
break;
|
2024-07-10 20:13:34 +02:00
|
|
|
case modem_mode::AUTODETECT: {
|
|
|
|
|
auto guessed = guess_unsafe(dte, true);
|
|
|
|
|
if (guessed == modem_mode::UNDEF) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// prepare the undefined mode before to allow all possible transitions
|
|
|
|
|
if (!dte->set_mode(modem_mode::UNDEF)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = modem_mode::UNDEF;
|
|
|
|
|
ESP_LOGD("DCE mode", "Detected mode: %d", static_cast<int>(guessed));
|
|
|
|
|
if (guessed == modem_mode::DATA_MODE) {
|
|
|
|
|
return set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_DATA_MODE);
|
|
|
|
|
} else if (guessed == esp_modem::modem_mode::COMMAND_MODE) {
|
|
|
|
|
return set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_COMMAND_MODE);
|
|
|
|
|
} else if (guessed == esp_modem::modem_mode::CMUX_MODE) {
|
|
|
|
|
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_MODE)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// now we guess the mode for each terminal
|
|
|
|
|
guessed = guess_unsafe(dte, false);
|
|
|
|
|
ESP_LOGD("DCE mode", "Detected mode on primary term: %d", static_cast<int>(guessed));
|
|
|
|
|
// now we need to access the second terminal, so we could simply send a SWAP command
|
|
|
|
|
// (switching to data mode does the swapping internally, so we only swap if we're in CMD mode)
|
|
|
|
|
if (guessed == modem_mode::DATA_MODE) {
|
|
|
|
|
// switch to DATA on the primary terminal and swap terminals
|
|
|
|
|
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_DATA)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// swap terminals
|
|
|
|
|
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::CMUX_MANUAL_SWAP)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
guessed = guess_unsafe(dte, false);
|
|
|
|
|
ESP_LOGD("DCE mode", "Detected mode on secondary term: %d", static_cast<int>(guessed));
|
|
|
|
|
if (guessed == modem_mode::DATA_MODE) {
|
|
|
|
|
if (!set_unsafe(dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_DATA)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-10-25 15:41:48 +02:00
|
|
|
case modem_mode::COMMAND_MODE:
|
|
|
|
|
if (mode == modem_mode::COMMAND_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
|
2022-10-11 16:31:57 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (mode == modem_mode::CMUX_MODE) {
|
2022-06-05 15:43:56 +02:00
|
|
|
netif.stop();
|
|
|
|
|
netif.wait_until_ppp_exits();
|
|
|
|
|
if (!dte->set_mode(modem_mode::COMMAND_MODE)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = m;
|
|
|
|
|
return true;
|
2021-06-01 10:21:51 +02:00
|
|
|
}
|
2022-10-25 15:41:48 +02:00
|
|
|
if (!transitions::exit_data(*dte, *device, netif)) {
|
2022-10-11 16:31:57 +02:00
|
|
|
mode = modem_mode::UNDEF;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = m;
|
|
|
|
|
return true;
|
2024-07-10 20:13:34 +02:00
|
|
|
case modem_mode::RESUME_DATA_MODE:
|
|
|
|
|
if (!dte->set_mode(modem_mode::DATA_MODE)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
netif.start();
|
|
|
|
|
mode = modem_mode::DATA_MODE;
|
|
|
|
|
return true;
|
|
|
|
|
case modem_mode::RESUME_COMMAND_MODE:
|
|
|
|
|
if (!dte->set_mode(modem_mode::COMMAND_MODE)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = modem_mode::COMMAND_MODE;
|
|
|
|
|
return true;
|
|
|
|
|
case modem_mode::RESUME_CMUX_MANUAL_MODE:
|
|
|
|
|
if (!dte->set_mode(modem_mode::CMUX_MANUAL_MODE)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = modem_mode::CMUX_MANUAL_MODE;
|
|
|
|
|
return true;
|
|
|
|
|
case modem_mode::RESUME_CMUX_MANUAL_DATA:
|
|
|
|
|
if (!dte->set_mode(modem_mode::CMUX_MANUAL_SWAP)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
netif.start();
|
|
|
|
|
mode = modem_mode::CMUX_MANUAL_MODE;
|
|
|
|
|
return true;
|
2021-06-01 10:21:51 +02:00
|
|
|
case modem_mode::DATA_MODE:
|
2022-10-25 15:41:48 +02:00
|
|
|
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
|
2021-06-01 10:21:51 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-10-25 15:41:48 +02:00
|
|
|
if (!transitions::enter_data(*dte, *device, netif)) {
|
2021-06-01 10:21:51 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = m;
|
|
|
|
|
return true;
|
|
|
|
|
case modem_mode::CMUX_MODE:
|
2022-10-25 15:41:48 +02:00
|
|
|
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
|
2021-06-01 10:21:51 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-05-10 17:55:55 +02:00
|
|
|
device->set_mode(modem_mode::CMUX_MODE); // switch the device into CMUX mode
|
2022-06-10 18:04:10 +02:00
|
|
|
usleep(100'000); // some devices need a few ms to switch
|
2022-05-10 17:55:55 +02:00
|
|
|
|
2021-06-01 10:21:51 +02:00
|
|
|
if (!dte->set_mode(modem_mode::CMUX_MODE)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-05-26 17:31:22 +02:00
|
|
|
mode = modem_mode::CMUX_MODE;
|
2022-10-25 15:41:48 +02:00
|
|
|
return transitions::enter_data(*dte, *device, netif);
|
|
|
|
|
case modem_mode::CMUX_MANUAL_MODE:
|
2022-10-25 16:50:34 +02:00
|
|
|
if (mode != modem_mode::COMMAND_MODE && mode != modem_mode::UNDEF) {
|
2022-10-25 15:41:48 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
device->set_mode(modem_mode::CMUX_MODE);
|
|
|
|
|
usleep(100'000);
|
|
|
|
|
|
|
|
|
|
if (!dte->set_mode(m)) {
|
2022-05-26 17:31:22 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-10-25 15:41:48 +02:00
|
|
|
mode = modem_mode::CMUX_MANUAL_MODE;
|
|
|
|
|
return true;
|
|
|
|
|
case modem_mode::CMUX_MANUAL_EXIT:
|
2023-11-02 20:47:02 +01:00
|
|
|
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
|
2022-10-25 15:41:48 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!dte->set_mode(m)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mode = modem_mode::COMMAND_MODE;
|
|
|
|
|
return true;
|
|
|
|
|
case modem_mode::CMUX_MANUAL_SWAP:
|
2023-11-02 20:47:02 +01:00
|
|
|
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
|
2022-05-26 17:31:22 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-10-25 15:41:48 +02:00
|
|
|
if (!dte->set_mode(m)) {
|
2022-05-26 17:31:22 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2021-06-01 10:21:51 +02:00
|
|
|
return true;
|
2022-10-25 15:41:48 +02:00
|
|
|
case modem_mode::CMUX_MANUAL_DATA:
|
2023-11-02 20:47:02 +01:00
|
|
|
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
|
2022-10-25 15:41:48 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return transitions::enter_data(*dte, *device, netif);
|
|
|
|
|
case modem_mode::CMUX_MANUAL_COMMAND:
|
2023-11-02 20:47:02 +01:00
|
|
|
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
|
2022-10-25 15:41:48 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return transitions::exit_data(*dte, *device, netif);
|
2021-03-24 21:06:27 +01:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 19:34:45 +02:00
|
|
|
modem_mode DCE_Mode::get()
|
2021-03-24 21:06:27 +01:00
|
|
|
{
|
|
|
|
|
return mode;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 20:13:34 +02:00
|
|
|
modem_mode DCE_Mode::guess(DTE *dte, bool with_cmux)
|
|
|
|
|
{
|
|
|
|
|
Scoped<DTE> lock(*dte);
|
|
|
|
|
return guess_unsafe(dte, with_cmux);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This namespace contains probe packets and expected replies on 3 different protocols,
|
|
|
|
|
* the modem device could use (as well as timeouts and mode ids for synchronisation)
|
|
|
|
|
*/
|
|
|
|
|
namespace probe {
|
|
|
|
|
|
|
|
|
|
namespace ppp {
|
|
|
|
|
// Test that we're in the PPP mode by sending an LCP protocol echo request and expecting LCP echo reply
|
|
|
|
|
constexpr std::array<uint8_t, 16> lcp_echo_request = {0x7e, 0xff, 0x03, 0xc0, 0x21, 0x09, 0x01, 0x00, 0x08, 0x99, 0xd1, 0x35, 0xc1, 0x8e, 0x2c, 0x7e };
|
|
|
|
|
constexpr std::array<uint8_t, 5> lcp_echo_reply_head = {0x7e, 0xff, 0x7d, 0x23, 0xc0};
|
2025-04-15 17:14:57 +02:00
|
|
|
// PPP frame delimiter
|
|
|
|
|
constexpr uint8_t ppp_delimiter = 0x7e;
|
|
|
|
|
// LCP protocol ID
|
|
|
|
|
constexpr std::array<uint8_t, 2> lcp_protocol_id = {0xc0, 0x21};
|
2024-07-10 20:13:34 +02:00
|
|
|
const size_t mode = 1 << 0;
|
|
|
|
|
const int timeout = 200;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace cmd {
|
|
|
|
|
// For command mode, we just send a simple AT command
|
|
|
|
|
const char at[] = "\r\nAT\r\n";
|
|
|
|
|
const size_t max_at_reply = 16; // account for some whitespaces and/or CMUX encapsulation
|
|
|
|
|
const char reply[] = { 'O', 'K' };
|
|
|
|
|
const int mode = 1 << 1;
|
|
|
|
|
const int timeout = 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace cmux {
|
|
|
|
|
// For CMUX mode, we send an SABM on control terminal (0)
|
|
|
|
|
const uint8_t sabm0_reqest[] = {0xf9, 0x03, 0x3f, 0x01, 0x1c, 0xf9};
|
|
|
|
|
const uint8_t sabm0_reply[] = {0xf9, 0x03, 0x73, 0x01};
|
|
|
|
|
const int mode = 1 << 0;
|
|
|
|
|
const int timeout = 200;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
modem_mode DCE_Mode::guess_unsafe(DTE *dte, bool with_cmux)
|
|
|
|
|
{
|
|
|
|
|
// placeholder for reply and its size, since it could come in pieces, and we have to cache
|
|
|
|
|
// this is captured by the lambda by reference.
|
|
|
|
|
// must make sure the lambda is cleared before exiting this function (done by dte->on_read(nullptr))
|
|
|
|
|
uint8_t reply[std::max(probe::cmd::max_at_reply, std::max(sizeof(probe::ppp::lcp_echo_request), sizeof(probe::cmux::sabm0_reply)))];
|
|
|
|
|
size_t reply_pos = 0;
|
|
|
|
|
auto signal = std::make_shared<SignalGroup>();
|
|
|
|
|
std::weak_ptr<SignalGroup> weak_signal = signal;
|
|
|
|
|
dte->on_read([weak_signal, with_cmux, &reply, &reply_pos](uint8_t *data, size_t len) {
|
|
|
|
|
// storing the response in the `reply` array and de-fragmenting
|
|
|
|
|
if (reply_pos >= sizeof(reply)) {
|
|
|
|
|
return command_result::TIMEOUT;
|
|
|
|
|
}
|
|
|
|
|
auto reply_size = std::min((size_t)sizeof(reply) - reply_pos, len);
|
|
|
|
|
::memcpy(reply + reply_pos, data, reply_size);
|
|
|
|
|
reply_pos += reply_size;
|
|
|
|
|
ESP_LOG_BUFFER_HEXDUMP("esp-modem: guess mode data:", reply, reply_pos, ESP_LOG_DEBUG);
|
|
|
|
|
|
|
|
|
|
// Check whether the response resembles the "golden" reply (for these 3 protocols)
|
2025-04-15 17:14:57 +02:00
|
|
|
if (reply_pos >= 3) { // Minimum size needed for basic PPP detection
|
|
|
|
|
// Check for PPP detection - look for both traditional format and alternative formats
|
|
|
|
|
|
|
|
|
|
// Look for PPP frame delimiter (0x7E)
|
|
|
|
|
auto *ppp_start = static_cast<uint8_t *>(memchr(reply, probe::ppp::ppp_delimiter, reply_pos));
|
|
|
|
|
if (ppp_start) {
|
|
|
|
|
size_t remaining = reply_pos - (ppp_start - reply);
|
|
|
|
|
|
|
|
|
|
// Look for LCP protocol ID anywhere within reasonable distance after delimiter
|
|
|
|
|
if (remaining >= 5) { // Reasonable minimum for finding protocol ID
|
|
|
|
|
// Check for original expected pattern
|
|
|
|
|
if (memmem(ppp_start, remaining, probe::ppp::lcp_echo_reply_head.data(), 2)) {
|
|
|
|
|
// Check for protocol ID in original format position
|
|
|
|
|
uint8_t *protocol_pos = ppp_start + 3;
|
|
|
|
|
if ((protocol_pos + 1) < (reply + reply_pos) &&
|
|
|
|
|
(protocol_pos[0] == probe::ppp::lcp_echo_reply_head[3] &&
|
|
|
|
|
protocol_pos[1] == probe::ppp::lcp_echo_reply_head[4])) {
|
|
|
|
|
if (auto signal = weak_signal.lock()) {
|
|
|
|
|
signal->set(probe::ppp::mode);
|
|
|
|
|
return command_result::OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Also check for direct LCP protocol ID after delimiter (alternative format)
|
|
|
|
|
if (remaining >= 3 && memmem(ppp_start + 1, remaining - 1,
|
|
|
|
|
probe::ppp::lcp_protocol_id.data(),
|
|
|
|
|
probe::ppp::lcp_protocol_id.size())) {
|
|
|
|
|
if (auto signal = weak_signal.lock()) {
|
|
|
|
|
signal->set(probe::ppp::mode);
|
|
|
|
|
return command_result::OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-10 20:13:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-15 17:14:57 +02:00
|
|
|
|
2024-07-10 20:13:34 +02:00
|
|
|
if (reply_pos >= 4 && memmem(reply, reply_pos, probe::cmd::reply, sizeof(probe::cmd::reply))) {
|
|
|
|
|
if (reply[0] != 0xf9) { // double check that the reply is not wrapped in CMUX headers
|
|
|
|
|
if (auto signal = weak_signal.lock()) {
|
|
|
|
|
signal->set(probe::cmd::mode);
|
2025-04-15 17:14:57 +02:00
|
|
|
return command_result::OK;
|
2024-07-10 20:13:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (with_cmux && reply_pos >= sizeof(probe::cmux::sabm0_reply)) {
|
|
|
|
|
// checking the initial 3 bytes
|
|
|
|
|
auto *ptr = static_cast<uint8_t *>(memmem(reply, reply_pos, probe::cmux::sabm0_reply, 3));
|
|
|
|
|
// and checking that DLCI is 0 (control frame)
|
|
|
|
|
if (ptr && (ptr[3] >> 2) == 0) {
|
|
|
|
|
if (auto signal = weak_signal.lock()) {
|
|
|
|
|
signal->set(probe::cmux::mode);
|
2025-04-15 17:14:57 +02:00
|
|
|
return command_result::OK;
|
2024-07-10 20:13:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return command_result::TIMEOUT;
|
|
|
|
|
});
|
|
|
|
|
auto guessed = modem_mode::UNDEF;
|
|
|
|
|
// Check the PPP mode fist by sending LCP echo request
|
|
|
|
|
dte->send((uint8_t *)probe::ppp::lcp_echo_request.data(), sizeof(probe::ppp::lcp_echo_request), 0);
|
|
|
|
|
if (signal->wait(probe::ppp::mode, probe::ppp::timeout)) {
|
|
|
|
|
guessed = modem_mode::DATA_MODE;
|
|
|
|
|
} else { // LCP echo timeout
|
|
|
|
|
// now check for AT mode
|
|
|
|
|
reply_pos = 0;
|
|
|
|
|
dte->send((uint8_t *)probe::cmd::at, sizeof(probe::cmd::at), 0);
|
|
|
|
|
if (signal->wait(probe::cmd::mode, probe::cmd::timeout)) {
|
|
|
|
|
guessed = modem_mode::COMMAND_MODE;
|
|
|
|
|
} else if (with_cmux) { // no AT reply, check for CMUX mode (if requested)
|
|
|
|
|
reply_pos = 0;
|
|
|
|
|
dte->send((uint8_t *) probe::cmux::sabm0_reqest, sizeof(probe::cmux::sabm0_reqest), 0);
|
|
|
|
|
if (signal->wait(probe::cmux::mode, probe::cmux::timeout)) {
|
|
|
|
|
guessed = modem_mode::CMUX_MODE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dte->on_read(nullptr);
|
|
|
|
|
return guessed;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-11 16:31:57 +02:00
|
|
|
} // esp_modem
|