2021-03-29 19:34:45 +02:00
|
|
|
/*
|
2023-09-20 12:06:47 +02:00
|
|
|
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
2022-10-11 16:31:57 +02:00
|
|
|
*
|
2021-03-29 19:34:45 +02:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2021-05-26 15:57:25 +02:00
|
|
|
#pragma once
|
2021-03-29 19:34:45 +02:00
|
|
|
|
2021-02-26 18:32:15 +01:00
|
|
|
#include <memory>
|
2022-06-10 18:04:10 +02:00
|
|
|
#include <utility>
|
2021-02-26 18:32:15 +01:00
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdint>
|
2021-03-07 19:43:45 +01:00
|
|
|
#include "cxx_include/esp_modem_primitives.hpp"
|
|
|
|
#include "cxx_include/esp_modem_terminal.hpp"
|
|
|
|
#include "cxx_include/esp_modem_types.hpp"
|
2022-06-10 18:04:10 +02:00
|
|
|
#include "cxx_include/esp_modem_buffer.hpp"
|
2021-03-05 21:18:38 +01:00
|
|
|
|
2021-04-18 19:14:22 +02:00
|
|
|
struct esp_modem_dte_config;
|
|
|
|
|
2021-03-29 19:34:45 +02:00
|
|
|
namespace esp_modem {
|
2021-03-03 20:35:08 +01:00
|
|
|
|
2022-06-10 18:04:10 +02:00
|
|
|
class CMux;
|
|
|
|
|
2021-04-14 17:57:42 +02:00
|
|
|
/**
|
|
|
|
* @defgroup ESP_MODEM_DTE
|
|
|
|
* @brief Definition of DTE and related classes
|
|
|
|
*/
|
|
|
|
/** @addtogroup ESP_MODEM_DTE
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
2023-03-16 19:17:53 +01:00
|
|
|
struct DTE_Command {
|
|
|
|
DTE_Command(const std::string &cmd): data((uint8_t *)cmd.c_str()), len(cmd.length()) {}
|
|
|
|
|
|
|
|
uint8_t *data;
|
|
|
|
size_t len;
|
|
|
|
};
|
|
|
|
|
2021-04-14 17:57:42 +02:00
|
|
|
/**
|
|
|
|
* DTE (Data Terminal Equipment) class
|
|
|
|
*/
|
2021-03-29 19:34:45 +02:00
|
|
|
class DTE : public CommandableIf {
|
|
|
|
public:
|
2021-04-18 19:34:58 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Creates a DTE instance from the terminal
|
|
|
|
* @param config DTE config structure
|
|
|
|
* @param t unique-ptr to Terminal
|
2023-03-07 13:35:34 +01:00
|
|
|
* @param s unique-ptr to secondary Terminal
|
2021-04-18 19:34:58 +02:00
|
|
|
*/
|
2021-04-18 19:14:22 +02:00
|
|
|
explicit DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t);
|
2021-04-19 11:28:53 +02:00
|
|
|
explicit DTE(std::unique_ptr<Terminal> t);
|
2023-03-07 13:35:34 +01:00
|
|
|
explicit DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s);
|
|
|
|
explicit DTE(std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s);
|
2021-02-26 18:32:15 +01:00
|
|
|
|
2021-03-29 19:34:45 +02:00
|
|
|
~DTE() = default;
|
2021-02-26 18:32:15 +01:00
|
|
|
|
2021-04-14 17:57:42 +02:00
|
|
|
/**
|
|
|
|
* @brief Writing to the underlying terminal
|
|
|
|
* @param data Data pointer to write
|
|
|
|
* @param len Data len to write
|
|
|
|
* @return number of bytes written
|
|
|
|
*/
|
2023-03-16 19:17:53 +01:00
|
|
|
int write(uint8_t *data, size_t len) override;
|
|
|
|
|
|
|
|
int write(DTE_Command command);
|
2021-03-29 19:34:45 +02:00
|
|
|
|
2021-04-14 17:57:42 +02:00
|
|
|
/**
|
|
|
|
* @brief Reading from the underlying terminal
|
|
|
|
* @param d Returning the data pointer of the received payload
|
|
|
|
* @param len Length of the data payload
|
|
|
|
* @return number of bytes read
|
|
|
|
*/
|
2021-04-18 19:34:58 +02:00
|
|
|
int read(uint8_t **d, size_t len);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Sets read callback with valid data and length
|
|
|
|
* @param f Function to be called on data available
|
|
|
|
*/
|
|
|
|
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f);
|
2021-02-26 18:32:15 +01:00
|
|
|
|
2023-06-23 19:35:49 +02:00
|
|
|
/**
|
|
|
|
* @brief Sets read callback for manual command processing
|
|
|
|
* Note that this API also locks the command API, which can only be used
|
|
|
|
* after you remove the callback by dte->on_read(nullptr)
|
|
|
|
*
|
|
|
|
* @param on_data Function to be called when a command response is available
|
|
|
|
*/
|
2023-03-16 19:17:53 +01:00
|
|
|
void on_read(got_line_cb on_data) override;
|
|
|
|
|
2022-09-02 09:14:38 +02:00
|
|
|
/**
|
|
|
|
* @brief Sets DTE error callback
|
|
|
|
* @param f Function to be called on DTE error
|
|
|
|
*/
|
|
|
|
void set_error_cb(std::function<void(terminal_error err)> f);
|
|
|
|
|
2021-04-18 19:34:58 +02:00
|
|
|
/**
|
|
|
|
* @brief Sets the DTE to desired mode (Command/Data/Cmux)
|
|
|
|
* @param m Desired operation mode
|
|
|
|
* @return true on success
|
|
|
|
*/
|
|
|
|
[[nodiscard]] bool set_mode(modem_mode m);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Sends command and provides callback with responding line
|
|
|
|
* @param command String parameter representing command
|
|
|
|
* @param got_line Function to be called after line available as a response
|
|
|
|
* @param time_ms Time in ms to wait for the answer
|
|
|
|
* @return OK, FAIL, TIMEOUT
|
|
|
|
*/
|
2021-03-29 19:34:45 +02:00
|
|
|
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms) override;
|
|
|
|
|
2021-04-18 19:34:58 +02:00
|
|
|
/**
|
|
|
|
* @brief Sends the command (same as above) but with a specific separator
|
|
|
|
*/
|
|
|
|
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override;
|
2021-03-29 19:34:45 +02:00
|
|
|
|
2023-09-20 12:06:47 +02:00
|
|
|
/**
|
|
|
|
* @brief Allows this DTE to recover from a generic connection issue
|
|
|
|
*
|
|
|
|
* @return true if success
|
|
|
|
*/
|
|
|
|
bool recover();
|
|
|
|
|
2022-06-08 17:16:15 +02:00
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* @brief Allows for locking the DTE
|
|
|
|
*/
|
|
|
|
void lock()
|
|
|
|
{
|
|
|
|
internal_lock.lock();
|
|
|
|
}
|
|
|
|
void unlock()
|
|
|
|
{
|
|
|
|
internal_lock.unlock();
|
|
|
|
}
|
|
|
|
friend class Scoped<DTE>; /*!< Declaring "Scoped<DTE> lock(dte)" locks this instance */
|
2021-02-26 18:32:15 +01:00
|
|
|
private:
|
2021-04-18 19:34:58 +02:00
|
|
|
|
2023-09-20 12:06:47 +02:00
|
|
|
void handle_error(terminal_error err); /*!< Performs internal error handling */
|
2022-06-10 18:04:10 +02:00
|
|
|
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
|
2023-08-18 12:44:37 +03:00
|
|
|
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
|
|
|
|
void exit_cmux_internal(); /*!< Cleanup CMUX */
|
2021-04-18 19:34:58 +02:00
|
|
|
|
2022-06-10 18:04:10 +02:00
|
|
|
Lock internal_lock{}; /*!< Locks DTE operations */
|
|
|
|
unique_buffer buffer; /*!< DTE buffer */
|
2022-06-08 17:16:15 +02:00
|
|
|
std::shared_ptr<CMux> cmux_term; /*!< Primary terminal for this DTE */
|
2022-10-25 16:50:34 +02:00
|
|
|
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 */
|
2022-06-10 18:04:10 +02:00
|
|
|
modem_mode mode; /*!< DTE operation mode */
|
|
|
|
std::function<bool(uint8_t *data, size_t len)> on_data; /*!< on data callback for current terminal */
|
2023-09-20 12:06:47 +02:00
|
|
|
std::function<void(terminal_error err)> user_error_cb; /*!< user callback on error event from attached terminals */
|
2023-06-23 19:35:49 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
|
|
|
/**
|
|
|
|
* @brief Implements an extra buffer that is used to capture partial reads from underlying terminals
|
|
|
|
* when we run out of the standard buffer
|
|
|
|
*/
|
|
|
|
struct extra_buffer {
|
|
|
|
extra_buffer(): buffer(nullptr) {}
|
|
|
|
~extra_buffer()
|
|
|
|
{
|
|
|
|
delete buffer;
|
|
|
|
}
|
|
|
|
std::vector<uint8_t> *buffer;
|
|
|
|
size_t consumed{0};
|
|
|
|
void grow(size_t need_size);
|
|
|
|
void deflate()
|
|
|
|
{
|
|
|
|
grow(0);
|
|
|
|
consumed = 0;
|
|
|
|
}
|
|
|
|
[[nodiscard]] uint8_t *begin() const
|
|
|
|
{
|
|
|
|
return &buffer->at(0);
|
|
|
|
}
|
|
|
|
[[nodiscard]] uint8_t *current() const
|
|
|
|
{
|
|
|
|
return &buffer->at(0) + consumed;
|
|
|
|
}
|
|
|
|
} inflatable;
|
|
|
|
#endif // CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set internal command callbacks to the underlying terminal.
|
|
|
|
* Here we capture command replies to be processed by supplied command callbacks in struct command_cb.
|
|
|
|
*/
|
|
|
|
void set_command_callbacks();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief This abstracts command callback processing and implements its locking, signaling of completion and timeouts.
|
|
|
|
*/
|
|
|
|
struct command_cb {
|
|
|
|
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
|
|
|
got_line_cb got_line; /*!< Supplied command callback */
|
|
|
|
Lock line_lock{}; /*!< Command callback locking mechanism */
|
|
|
|
char separator{}; /*!< Command reply separator (end of line/processing unit) */
|
|
|
|
command_result result{}; /*!< Command return code */
|
|
|
|
SignalGroup signal; /*!< Event group used to signal request-response operations */
|
|
|
|
bool process_line(uint8_t *data, size_t consumed, size_t len); /*!< Lets the processing callback handle one line (processing unit) */
|
2023-09-20 12:06:47 +02:00
|
|
|
bool wait_for_line(uint32_t time_ms) /*!< Waiting for command processing */
|
|
|
|
{
|
|
|
|
return signal.wait_any(command_cb::GOT_LINE, time_ms);
|
|
|
|
}
|
2023-06-23 19:35:49 +02:00
|
|
|
void set(got_line_cb l, char s = '\n') /*!< Sets the command callback atomically */
|
|
|
|
{
|
|
|
|
Scoped<Lock> lock(line_lock);
|
|
|
|
if (l) {
|
|
|
|
// if we set the line callback, we have to reset the signal and the result
|
|
|
|
signal.clear(GOT_LINE);
|
|
|
|
result = command_result::TIMEOUT;
|
2023-09-20 12:06:47 +02:00
|
|
|
} else {
|
|
|
|
// if we clear the line callback, we check consistency (since we've locked the line processing)
|
|
|
|
if (signal.is_any(command_cb::GOT_LINE) && result == command_result::TIMEOUT) {
|
|
|
|
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
|
|
|
|
}
|
2023-06-23 19:35:49 +02:00
|
|
|
}
|
|
|
|
got_line = std::move(l);
|
|
|
|
separator = s;
|
|
|
|
}
|
|
|
|
void give_up() /*!< Reports other than timeout error when processing replies (out of buffer) */
|
|
|
|
{
|
|
|
|
result = command_result::FAIL;
|
|
|
|
signal.set(GOT_LINE);
|
|
|
|
}
|
|
|
|
} command_cb; /*!< Command callback utility class */
|
2021-02-26 18:32:15 +01:00
|
|
|
};
|
|
|
|
|
2021-04-14 17:57:42 +02:00
|
|
|
/**
|
|
|
|
* @}
|
|
|
|
*/
|
2021-04-06 08:33:40 +02:00
|
|
|
|
2021-03-29 19:34:45 +02:00
|
|
|
} // namespace esp_modem
|