diff --git a/esp_modem/CMakeLists.txt b/esp_modem/CMakeLists.txt index 59149ff51..a5ee3a5e2 100644 --- a/esp_modem/CMakeLists.txt +++ b/esp_modem/CMakeLists.txt @@ -20,6 +20,7 @@ set(srcs ${platform_srcs} "src/esp_modem_factory.cpp" "src/esp_modem_cmux.cpp" "src/esp_modem_command_library.cpp" + "src/esp_modem_term_fs.cpp" "src/esp_modem_modules.cpp") set(include_dirs "include") diff --git a/esp_modem/examples/modem_console/main/modem_console_main.cpp b/esp_modem/examples/modem_console/main/modem_console_main.cpp index 453604aa6..b47ac8354 100644 --- a/esp_modem/examples/modem_console/main/modem_console_main.cpp +++ b/esp_modem/examples/modem_console/main/modem_console_main.cpp @@ -47,14 +47,24 @@ extern "C" void app_main(void) ESP_ERROR_CHECK(esp_event_loop_create_default()); // init the DTE - esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); - dte_config.uart_config.event_task_stack_size = 4096; - dte_config.uart_config.event_task_priority = 15; + esp_modem_dte_config_t dte_config = { + .dte_buffer_size = 512, + .vfs_config = {.port_num = UART_NUM_1, + .dev_name = "/dev/uart/1", + .rx_buffer_size = 512, + .tx_buffer_size = 512, + .baud_rate = 115200, + .tx_io_num = 25, + .rx_io_num = 26, + .task_stack_size = 4096, + .task_prio = 5} + }; + esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP(); esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config); assert(esp_netif); - auto uart_dte = create_uart_dte(&dte_config); + auto uart_dte = create_vfs_dte(&dte_config); esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet"); diff --git a/esp_modem/include/cxx_include/esp_modem_api.hpp b/esp_modem/include/cxx_include/esp_modem_api.hpp index 528bb06da..5ff6e496a 100644 --- a/esp_modem/include/cxx_include/esp_modem_api.hpp +++ b/esp_modem/include/cxx_include/esp_modem_api.hpp @@ -48,6 +48,11 @@ using dte_config = ::esp_modem_dte_config; * if exceptions are disabled the API abort()'s on error */ std::shared_ptr create_uart_dte(const dte_config *config); + + +std::shared_ptr create_vfs_dte(const dte_config *config); + + /** * @} */ diff --git a/esp_modem/include/esp_modem_config.h b/esp_modem/include/esp_modem_config.h index a2ef11037..4af3518bc 100644 --- a/esp_modem/include/esp_modem_config.h +++ b/esp_modem/include/esp_modem_config.h @@ -58,9 +58,24 @@ struct esp_modem_uart_term_config { int event_task_priority; /*!< UART Event Task Priority */ }; +struct esp_modem_vfs_term_config { + int port_num; + const char* dev_name; + int rx_buffer_size; + int tx_buffer_size; + int baud_rate; + int tx_io_num; + int rx_io_num; + uint32_t task_stack_size; + int task_prio; +}; + struct esp_modem_dte_config { size_t dte_buffer_size; - struct esp_modem_uart_term_config uart_config; + union { + struct esp_modem_uart_term_config uart_config; + struct esp_modem_vfs_term_config vfs_config; + }; }; diff --git a/esp_modem/private_include/vfs_termial.hpp b/esp_modem/private_include/vfs_termial.hpp new file mode 100644 index 000000000..ac29ebc92 --- /dev/null +++ b/esp_modem/private_include/vfs_termial.hpp @@ -0,0 +1,28 @@ +// Copyright 2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _VFS_TERMINAL_HPP_ +#define _VFS_TERMINAL_HPP_ + +#include "cxx_include/esp_modem_dte.hpp" + +struct esp_modem_dte_config; + +namespace esp_modem::terminal { + +std::unique_ptr create_vfs_terminal(const esp_modem_dte_config *config); + +} // namespace esp_modem::terminal + +#endif // _VFS_TERMINAL_HPP_ diff --git a/esp_modem/src/esp_modem_api.cpp b/esp_modem/src/esp_modem_api.cpp index 7b29f61eb..fb86f37d6 100644 --- a/esp_modem/src/esp_modem_api.cpp +++ b/esp_modem/src/esp_modem_api.cpp @@ -13,9 +13,10 @@ // limitations under the License. #include +#include "esp_log.h" #include "cxx_include/esp_modem_dte.hpp" #include "uart_terminal.hpp" -#include "esp_log.h" +#include "vfs_termial.hpp" #include "cxx_include/esp_modem_api.hpp" #include "cxx_include/esp_modem_dce_factory.hpp" #include "esp_modem_config.h" @@ -36,6 +37,14 @@ std::shared_ptr create_uart_dte(const dte_config *config) { ) } +std::shared_ptr create_vfs_dte(const dte_config *config) { + TRY_CATCH_RET_NULL( + auto term = terminal::create_vfs_terminal(config); + return std::make_shared(config, std::move(term)); + ) +} + + static inline std::unique_ptr create_modem_dce(dce_factory::Modem m, const dce_config *config, std::shared_ptr dte, esp_netif_t *netif) { dce_factory::Factory f(m); diff --git a/esp_modem/src/esp_modem_term_fs.cpp b/esp_modem/src/esp_modem_term_fs.cpp new file mode 100644 index 000000000..390705b04 --- /dev/null +++ b/esp_modem/src/esp_modem_term_fs.cpp @@ -0,0 +1,199 @@ +// Copyright 2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "cxx_include/esp_modem_dte.hpp" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/uart.h" +#include "esp_modem_config.h" +#include "exception_stub.hpp" +#include "esp_vfs_dev.h" + +static const char *TAG = "fs_terminal"; + +namespace esp_modem::terminal { + +struct uart_resource { + explicit uart_resource(const esp_modem_dte_config *config); + ~uart_resource(); + uart_port_t port; + int fd; +}; + +struct vfs_task { + explicit vfs_task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) : + task_handle(nullptr) { + BaseType_t ret = xTaskCreate(task_function, "vfs_task" + "", stack_size, task_param, priority, &task_handle); + throw_if_false(ret == pdTRUE, "create uart event task failed"); + } + + ~vfs_task() { + if (task_handle) vTaskDelete(task_handle); + } + + TaskHandle_t task_handle; /*!< UART event task handle */ +}; + +uart_resource::~uart_resource() +{ + if (port >= UART_NUM_0 && port < UART_NUM_MAX) { + uart_driver_delete(port); + } + if (fd >= 0) { + close(fd); + } +} + +uart_resource::uart_resource(const esp_modem_dte_config *config) : + port(-1), fd(-1) +{ + /* Config UART */ + uart_config_t uart_config = {}; + uart_config.baud_rate = config->vfs_config.baud_rate; + uart_config.data_bits = UART_DATA_8_BITS; + uart_config.parity = UART_PARITY_DISABLE; + uart_config.stop_bits = UART_STOP_BITS_1; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.source_clk = UART_SCLK_REF_TICK; + + throw_if_esp_fail(uart_param_config(config->vfs_config.port_num, &uart_config), "config uart parameter failed"); + + throw_if_esp_fail(uart_set_pin(config->vfs_config.port_num, config->vfs_config.tx_io_num, config->vfs_config.rx_io_num, + UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), "config uart gpio failed"); + + throw_if_esp_fail(uart_driver_install(config->vfs_config.port_num, config->vfs_config.rx_buffer_size, config->vfs_config.tx_buffer_size, + 0, nullptr, 0), "install uart driver failed"); + + throw_if_esp_fail(uart_set_rx_timeout(config->vfs_config.port_num, 1), "set rx timeout failed"); + + throw_if_esp_fail(uart_set_rx_full_threshold(config->uart_config.port_num, 64), "config rx full threshold failed"); + + /* mark UART as initialized */ + port = config->vfs_config.port_num; + + fd = open(config->vfs_config.dev_name, O_RDWR); + + throw_if_false(fd >= 0, "Cannot open the fd"); +} + +class vfs_terminal : public Terminal { +public: + explicit vfs_terminal(const esp_modem_dte_config *config) : + uart(config), signal(), + task_handle(config->vfs_config.task_stack_size, config->vfs_config.task_prio, this, s_task) {} + + ~vfs_terminal() override = default; + + void start() override { + signal.set(TASK_START); + } + + void stop() override { + signal.set(TASK_STOP); + } + + int write(uint8_t *data, size_t len) override; + + int read(uint8_t *data, size_t len) override; + + void set_read_cb(std::function f) override { + on_data = std::move(f); + signal.set(TASK_PARAMS); + } + +private: + static void s_task(void *task_param) { + auto t = static_cast(task_param); + t->task(); + vTaskDelete(NULL); + } + + void task(); + + static const size_t TASK_INIT = BIT0; + static const size_t TASK_START = BIT1; + static const size_t TASK_STOP = BIT2; + static const size_t TASK_PARAMS = BIT3; + + uart_resource uart; + signal_group signal; + vfs_task task_handle; +}; + +std::unique_ptr create_vfs_terminal(const esp_modem_dte_config *config) { + TRY_CATCH_RET_NULL( + auto term = std::make_unique(config); + term->start(); + return term; + ) +} + +void vfs_terminal::task() { + std::function on_data_priv = nullptr; + size_t len; + signal.set(TASK_INIT); + signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY); + if (signal.is_any(TASK_STOP)) { + return; // exits to the static method where the task gets deleted + } + esp_vfs_dev_uart_use_driver(uart.port); + + while (signal.is_any(TASK_START)) { + int s; + fd_set rfds; + struct timeval tv = { + .tv_sec = 5, + .tv_usec = 0, + }; + FD_ZERO(&rfds); + FD_SET(uart.fd, &rfds); + + s = select(uart.fd + 1, &rfds, NULL, NULL, &tv); +// if (signal.is_any(TASK_PARAMS)) { + on_data_priv = on_data; +// signal.clear(TASK_PARAMS); +// } + + if (s < 0) { + ESP_LOGE(TAG, "Select failed: errno %d", errno); + break; + } else if (s == 0) { + ESP_LOGI(TAG, "Timeout has been reached and nothing has been received"); + } else { + if (FD_ISSET(uart.fd, &rfds)) { + uart_get_buffered_data_len(uart.port, &len); + if (len && on_data_priv) { + if (on_data_priv(nullptr, len)) { + on_data_priv = nullptr; + } + } + } + } + vTaskDelay(1); + } +} + +int vfs_terminal::read(uint8_t *data, size_t len) { + return ::read(uart.fd, data, len); +} + +int vfs_terminal::write(uint8_t *data, size_t len) { + return ::write(uart.fd, data, len); +} + +} // namespace esp_modem + diff --git a/esp_modem/src/esp_modem_uart.cpp b/esp_modem/src/esp_modem_uart.cpp index 03864f4f5..9155fbf61 100644 --- a/esp_modem/src/esp_modem_uart.cpp +++ b/esp_modem/src/esp_modem_uart.cpp @@ -97,14 +97,15 @@ uart_resource::uart_resource(const esp_modem_dte_config *config) : res = uart_set_sw_flow_ctrl(config->uart_config.port_num, true, 8, UART_FIFO_LEN - 8); } throw_if_esp_fail(res, "config uart flow control failed"); + /* Install UART driver and get event queue used inside driver */ res = uart_driver_install(config->uart_config.port_num, config->uart_config.rx_buffer_size, config->uart_config.tx_buffer_size, config->uart_config.event_queue_size, &(event_queue), 0); throw_if_esp_fail(res, "install uart driver failed"); throw_if_esp_fail(uart_set_rx_timeout(config->uart_config.port_num, 1), "set rx timeout failed"); - uart_set_rx_full_threshold(config->uart_config.port_num, 64); - throw_if_esp_fail(res, "config uart pattern failed"); + throw_if_esp_fail(uart_set_rx_full_threshold(config->uart_config.port_num, 64), "config rx full threshold failed"); + /* mark UART as initialized */ port = config->uart_config.port_num; }