// 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 #include #include "cxx_include/esp_modem_dte.hpp" #include "esp_log.h" #include "esp_modem_config.h" #include "exception_stub.hpp" #include "uart_resource.hpp" // In case of VFS using UART static const char *TAG = "fs_terminal"; namespace esp_modem { class Resource { public: explicit Resource(const esp_modem_dte_config *config, int fd): uart(config->vfs_config.resource == ESP_MODEM_VFS_IS_EXTERN? std::nullopt : std::make_optional(config, nullptr, fd)) {} std::optional uart; }; struct File { explicit File(const char *name): fd(-1) { fd = open(name, O_RDWR); throw_if_false(fd >= 0, "Cannot open the fd"); // Set the FD to non-blocking mode int flags = fcntl(fd, F_GETFL, nullptr) | O_NONBLOCK; fcntl(fd, F_SETFL, flags); } ~File() { if (fd >= 0) { close(fd); } } int fd; }; class FdTerminal : public Terminal { public: explicit FdTerminal(const esp_modem_dte_config *config); ~FdTerminal() override; void start() override { signal.set(TASK_START); } void stop() override { signal.clear(TASK_START); } 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: void task(); static const size_t TASK_INIT = SignalGroup::bit0; static const size_t TASK_START = SignalGroup::bit1; static const size_t TASK_STOP = SignalGroup::bit2; static const size_t TASK_PARAMS = SignalGroup::bit3; File f; Resource resource; SignalGroup signal; 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; ) } FdTerminal::FdTerminal(const esp_modem_dte_config *config) : f(config->vfs_config.dev_name), resource(config, f.fd), signal(), task_handle(config->task_stack_size, config->task_priority, this, [](void* p){ auto t = static_cast(p); t->task(); Task::Delete(); }) {} void FdTerminal::task() { std::function on_data_priv = nullptr; 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 } while (signal.is_any(TASK_START)) { int s; fd_set rfds; struct timeval tv = { .tv_sec = 1, .tv_usec = 0, }; FD_ZERO(&rfds); FD_SET(f.fd, &rfds); s = select(f.fd + 1, &rfds, nullptr, nullptr, &tv); if (signal.is_any(TASK_PARAMS)) { on_data_priv = on_data; signal.clear(TASK_PARAMS); } if (s < 0) { break; } else if (s == 0) { // ESP_LOGV(TAG, "Select exited with timeout"); } else { if (FD_ISSET(f.fd, &rfds)) { if (on_data_priv) { if (on_data_priv(nullptr, 0)) { on_data_priv = nullptr; } } } } Task::Relinquish(); } } int FdTerminal::read(uint8_t *data, size_t len) { int size = ::read(f.fd, data, len); if (size < 0) { if (errno != EAGAIN) { ESP_LOGE(TAG, "Error occurred during read: %d", errno); } return 0; } return size; } int FdTerminal::write(uint8_t *data, size_t len) { int size = ::write(f.fd, data, len); if (size < 0) { ESP_LOGE(TAG, "Error occurred during read: %d", errno); return 0; } return size; } FdTerminal::~FdTerminal() { stop(); } } // namespace esp_modem