From 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 23 Aug 2018 17:08:59 +0200 Subject: o Initial import. --- .../libraries/cli/ble_uart/nrf_cli_ble_uart.c | 317 ++ .../libraries/cli/ble_uart/nrf_cli_ble_uart.h | 123 + .../libraries/cli/cdc_acm/nrf_cli_cdc_acm.c | 242 ++ .../libraries/cli/cdc_acm/nrf_cli_cdc_acm.h | 95 + .../components/libraries/cli/cli_utils_cmds.c | 164 + .../libraries/cli/libuarte/nrf_cli_libuarte.c | 247 ++ .../libraries/cli/libuarte/nrf_cli_libuarte.h | 112 + .../components/libraries/cli/nrf_cli.c | 3500 ++++++++++++++++++++ .../components/libraries/cli/nrf_cli.h | 636 ++++ .../components/libraries/cli/nrf_cli_types.h | 102 + .../components/libraries/cli/nrf_cli_vt100.h | 625 ++++ .../components/libraries/cli/rtt/nrf_cli_rtt.c | 223 ++ .../components/libraries/cli/rtt/nrf_cli_rtt.h | 100 + .../components/libraries/cli/uart/nrf_cli_uart.c | 308 ++ .../components/libraries/cli/uart/nrf_cli_uart.h | 115 + 15 files changed, 6909 insertions(+) create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cli_utils_cmds.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_types.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_vt100.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.h (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli') diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.c new file mode 100644 index 0000000..e469853 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.c @@ -0,0 +1,317 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_CLI_BLE_UART) +#include "nrf_cli_ble_uart.h" +#include "ble_nus.h" +#include "nrf_ringbuf.h" +#include "app_timer.h" +#include "nrf_assert.h" +#include "nrf_ble_gatt.h" +#define NRF_LOG_MODULE_NAME cli_ble + +#if NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NRF_CLI_BLE_UART_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR + +#else //NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#endif //NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#if NRF_CLI_BLE_UART_MAX_CLIENTS > NRF_SDH_BLE_TOTAL_LINK_COUNT + #error "Too few BLE peripheral links are supported by the BLE SDH module for the maximal number \ + of BLE transport instances" +#endif + +#define NRF_CLI_BLE_UART_TIMEOUT_MS 50 +#define OVERHEAD_LENGTH (OPCODE_LENGTH + HANDLE_LENGTH) + +BLE_NUS_DEF(m_nus, NRF_CLI_BLE_UART_MAX_CLIENTS); +BLE_LINK_CTX_MANAGER_DEF(m_link_ctx_storage, + NRF_CLI_BLE_UART_MAX_CLIENTS, + sizeof(nrf_cli_ble_uart_internal_t *)); + +static void tx_try(nrf_cli_ble_uart_internal_t * p_instance, uint32_t threshold) +{ + uint8_t * p_out_data; + size_t out_data_len = + nrf_ble_gatt_eff_mtu_get(p_instance->p_gatt, p_instance->p_cb->conn_handle) - + OVERHEAD_LENGTH; + + ret_code_t err_code = nrf_ringbuf_get(p_instance->p_tx_ringbuf, + &p_out_data, + &out_data_len, + true); + + if (err_code == NRF_SUCCESS) + { + if (out_data_len >= threshold) + { + size_t req_data_len = out_data_len; + err_code = ble_nus_data_send(&m_nus, + p_out_data, + (uint16_t*)&out_data_len, + p_instance->p_cb->conn_handle); + + if ((err_code == NRF_ERROR_BUSY) || (err_code == NRF_ERROR_RESOURCES)) + { + out_data_len = 0; + } + NRF_LOG_INFO("Conn_handle:%d TX req_len: %d, len: %d", + p_instance->p_cb->conn_handle, req_data_len, out_data_len); + NRF_LOG_HEXDUMP_DEBUG(p_out_data, out_data_len); + err_code = nrf_ringbuf_free(p_instance->p_tx_ringbuf, out_data_len); + ASSERT(err_code == NRF_SUCCESS); + } + else + { + err_code = nrf_ringbuf_free(p_instance->p_tx_ringbuf, 0); + ASSERT(err_code == NRF_SUCCESS); + } + } +} + +static void nus_data_handler(ble_nus_evt_t * p_nus_evt) +{ + ret_code_t err_code = NRF_SUCCESS; + nrf_cli_ble_uart_internal_t * p_instance; + nrf_cli_ble_uart_internal_t ** pp_instance; + + err_code = blcm_link_ctx_get(&m_link_ctx_storage, p_nus_evt->conn_handle, (void *) &pp_instance); + ASSERT(err_code == NRF_SUCCESS); + + p_instance = *pp_instance; + + switch (p_nus_evt->type) + { + case BLE_NUS_EVT_RX_DATA: + { + NRF_LOG_INFO("Conn_handle:%d, Received: %d", + p_instance->p_cb->conn_handle, p_nus_evt->params.rx_data.length); + NRF_LOG_HEXDUMP_DEBUG(p_nus_evt->params.rx_data.p_data, p_nus_evt->params.rx_data.length); + size_t len = ((size_t) p_nus_evt->params.rx_data.length) & 0x0000FFFF; + err_code = nrf_ringbuf_cpy_put(p_instance->p_rx_ringbuf, + p_nus_evt->params.rx_data.p_data, + (size_t *)&len); + ASSERT(err_code == NRF_SUCCESS); + + p_instance->p_cb->handler(NRF_CLI_TRANSPORT_EVT_RX_RDY, p_instance->p_cb->p_context); + break; + } + case BLE_NUS_EVT_TX_RDY: + { + //TX_Complete + uint32_t max_tx_len = nrf_ble_gatt_eff_mtu_get(p_instance->p_gatt, + p_instance->p_cb->conn_handle) - + OVERHEAD_LENGTH; + tx_try(p_instance, max_tx_len); + p_instance->p_cb->handler(NRF_CLI_TRANSPORT_EVT_TX_RDY, p_instance->p_cb->p_context); + break; + } + case BLE_NUS_EVT_COMM_STARTED: + p_instance->p_cb->service_started = true; + err_code = app_timer_start(*p_instance->p_timer, + APP_TIMER_TICKS(NRF_CLI_BLE_UART_TIMEOUT_MS), + p_instance); + ASSERT(err_code == NRF_SUCCESS); + NRF_LOG_INFO("Conn_handle:%d, communication started", p_instance->p_cb->conn_handle); + break; + case BLE_NUS_EVT_COMM_STOPPED: + (void)app_timer_stop(*p_instance->p_timer); + p_instance->p_cb->service_started = false; + NRF_LOG_INFO("Conn_handle:%d, communication stopped", p_instance->p_cb->conn_handle); + break; + default: + break; + } +} + +static void timer_handler(void * p_context) +{ + nrf_cli_ble_uart_internal_t * p_instance = (nrf_cli_ble_uart_internal_t *) p_context; + tx_try(p_instance, 1); + + ret_code_t err_code = app_timer_start(*p_instance->p_timer, + APP_TIMER_TICKS(NRF_CLI_BLE_UART_TIMEOUT_MS), (void *)p_instance); + ASSERT(err_code == NRF_SUCCESS); + UNUSED_VARIABLE(err_code); + UNUSED_PARAMETER(p_context); +} + +ret_code_t nrf_cli_ble_uart_service_init(void) +{ + ble_nus_init_t nus_init; + + memset(&nus_init, 0, sizeof(nus_init)); + nus_init.data_handler = nus_data_handler; + + return ble_nus_init(&m_nus, &nus_init); +} + +static ret_code_t cli_ble_uart_init(nrf_cli_transport_t const * p_transport, + void const * p_config, + nrf_cli_transport_handler_t evt_handler, + void * p_context) +{ + ret_code_t err_code; + nrf_cli_ble_uart_internal_t ** pp_instance; + nrf_cli_ble_uart_internal_t * p_instance = + CONTAINER_OF(p_transport, nrf_cli_ble_uart_internal_t, transport); + nrf_cli_ble_uart_config_t * p_ble_uart_config = (nrf_cli_ble_uart_config_t *)p_config; + + p_instance->p_cb->handler = evt_handler; + p_instance->p_cb->p_context = p_context; + p_instance->p_cb->service_started = false; + p_instance->p_cb->conn_handle = p_ble_uart_config->conn_handle; + + NRF_LOG_INFO("Conn_handle:%d init.", p_ble_uart_config->conn_handle); + nrf_ringbuf_init(p_instance->p_rx_ringbuf); + nrf_ringbuf_init(p_instance->p_tx_ringbuf); + + err_code = blcm_link_ctx_get(&m_link_ctx_storage, + p_ble_uart_config->conn_handle, + (void *) &pp_instance); + VERIFY_SUCCESS(err_code); + + *pp_instance = p_instance; + + return NRF_SUCCESS; +} + +static ret_code_t cli_ble_uart_uninit(nrf_cli_transport_t const * p_transport) +{ + nrf_cli_ble_uart_internal_t * p_instance = + CONTAINER_OF(p_transport, nrf_cli_ble_uart_internal_t, transport); + + NRF_LOG_INFO("Conn_handle:%d uninit.", p_instance->p_cb->conn_handle); + ret_code_t ret = app_timer_stop(*p_instance->p_timer); + + return ret; +} + +static ret_code_t cli_ble_uart_enable(nrf_cli_transport_t const * p_transport, bool blocking) +{ + nrf_cli_ble_uart_internal_t * p_instance = + CONTAINER_OF(p_transport, nrf_cli_ble_uart_internal_t, transport); + + + NRF_LOG_INFO("Conn_handle:%d, enable blocking:%d", blocking, p_instance->p_cb->conn_handle); + if (blocking) + { + return NRF_ERROR_NOT_SUPPORTED; + } + else + { + ret_code_t err_code = NRF_SUCCESS; + if (!p_instance->p_cb->timer_created) + { + err_code = app_timer_create(p_instance->p_timer, + APP_TIMER_MODE_SINGLE_SHOT, + timer_handler); + p_instance->p_cb->timer_created = true; + } + return err_code; + } +} + +static ret_code_t cli_ble_uart_read(nrf_cli_transport_t const * p_transport, + void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + nrf_cli_ble_uart_internal_t * p_instance = + CONTAINER_OF(p_transport, nrf_cli_ble_uart_internal_t, transport); + *p_cnt = length; + ret_code_t err_code = nrf_ringbuf_cpy_get(p_instance->p_rx_ringbuf, p_data, p_cnt); + + if (*p_cnt) + { + NRF_LOG_INFO("Conn_handle:%d, read req_len:%d read_len: %d", + p_instance->p_cb->conn_handle, + length, + *p_cnt); + NRF_LOG_HEXDUMP_DEBUG(p_data, *p_cnt); + } + + return err_code; +} + +static ret_code_t cli_ble_uart_write(nrf_cli_transport_t const * p_transport, + const void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + nrf_cli_ble_uart_internal_t * p_instance = + CONTAINER_OF(p_transport, nrf_cli_ble_uart_internal_t, transport); + ret_code_t err_code = NRF_SUCCESS; + if (p_instance->p_cb->service_started) + { + *p_cnt = length; + err_code = nrf_ringbuf_cpy_put(p_instance->p_tx_ringbuf, p_data, p_cnt); + + NRF_LOG_INFO("Conn_handle:%d, write req:%d, buffered:%d", + p_instance->p_cb->conn_handle, length, *p_cnt); + NRF_LOG_HEXDUMP_DEBUG(p_data, *p_cnt); + } + else + { + NRF_LOG_INFO("Conn_handle:%d, write req:%d. Notifications not enabled", + p_instance->p_cb->conn_handle, length); + *p_cnt = length; + p_instance->p_cb->handler(NRF_CLI_TRANSPORT_EVT_TX_RDY, p_instance->p_cb->p_context); + } + return err_code; +} + +const nrf_cli_transport_api_t nrf_cli_ble_uart_transport_api = { + .init = cli_ble_uart_init, + .uninit = cli_ble_uart_uninit, + .enable = cli_ble_uart_enable, + .read = cli_ble_uart_read, + .write = cli_ble_uart_write, +}; + +#endif //NRF_MODULE_ENABLED(NRF_CLI_BLE_UART) diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.h new file mode 100644 index 0000000..6111940 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/ble_uart/nrf_cli_ble_uart.h @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_CLI_BLE_UART_H__ +#define NRF_CLI_BLE_UART_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "nrf_cli.h" +#include "ble.h" +#include "app_timer.h" +#include "nrf_ringbuf.h" +#include "nordic_common.h" +#include "nrf_ble_gatt.h" +/**@file + * + * @defgroup nrf_cli_ble_uart BLE UART command line interface transport layer + * @ingroup nrf_cli + * + * @{ + * + */ + +/** + * @brief Command line interface transport. + */ + +ret_code_t nrf_cli_ble_uart_service_init(void); + +extern const nrf_cli_transport_api_t nrf_cli_ble_uart_transport_api; + +typedef struct nrf_cli_ble_uart_internal_s nrf_cli_ble_uart_internal_t; + +typedef struct { + nrf_cli_transport_handler_t handler; + void * p_context; + uint16_t conn_handle; + bool timer_created; + bool service_started; +} nrf_cli_ble_uart_internal_cb_t; + + +struct nrf_cli_ble_uart_internal_s { + nrf_cli_transport_t transport; + nrf_cli_ble_uart_internal_cb_t * p_cb; + app_timer_id_t const * p_timer; + nrf_ringbuf_t const * p_rx_ringbuf; + nrf_ringbuf_t const * p_tx_ringbuf; + nrf_ble_gatt_t const * p_gatt; +}; + +typedef struct { + uint16_t conn_handle; +} nrf_cli_ble_uart_config_t; + +/**@brief CLI Bluetooth transport definition. + * + * @param _name Name of the instance. + * @param _p_gatt Pointer to the nrf_ble_gatt module. + * @param _tx_buf_sz Size of TX ring buffer. + * @param _rx_buf_sz Size of RX ring buffer. + */ +#define NRF_CLI_BLE_UART_DEF(_name, _p_gatt, _tx_buf_sz, _rx_buf_sz) \ + APP_TIMER_DEF(CONCAT_2(_name, _timer)); \ + NRF_RINGBUF_DEF(CONCAT_2(_name,_tx_ringbuf), _tx_buf_sz); \ + NRF_RINGBUF_DEF(CONCAT_2(_name,_rx_ringbuf), _rx_buf_sz); \ + static nrf_cli_ble_uart_internal_cb_t CONCAT_2(_name, _cb); \ + static const nrf_cli_ble_uart_internal_t _name = { \ + .transport = {.p_api = &nrf_cli_ble_uart_transport_api}, \ + .p_cb = &CONCAT_2(_name, _cb), \ + .p_timer = &CONCAT_2(_name, _timer), \ + .p_rx_ringbuf = &CONCAT_2(_name,_rx_ringbuf), \ + .p_tx_ringbuf = &CONCAT_2(_name,_tx_ringbuf), \ + .p_gatt = _p_gatt, \ + } + + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_CLI_BLE_UART_H__ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.c new file mode 100644 index 0000000..42adb75 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.c @@ -0,0 +1,242 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_CLI_CDC_ACM) +#include "nrf_cli_cdc_acm.h" +#include "nrf_queue.h" +#include "app_error.h" +#include "nrf_assert.h" + + +#if APP_USBD_CONFIG_EVENT_QUEUE_ENABLE +#error "Current CLI CDC implementation supports only USB with event queue disabled (see APP_USBD_CONFIG_EVENT_QUEUE_ENABLE)" +#endif + +static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst, + app_usbd_cdc_acm_user_event_t event); + +/*lint -save -e26 -e40 -e64 -e123 -e505 -e651*/ + +/** + * @brief CDC_ACM class instance. + * */ +APP_USBD_CDC_ACM_GLOBAL_DEF(nrf_cli_cdc_acm, + cdc_acm_user_ev_handler, + NRF_CLI_CDC_ACM_COMM_INTERFACE, + NRF_CLI_CDC_ACM_DATA_INTERFACE, + NRF_CLI_CDC_ACM_COMM_EPIN, + NRF_CLI_CDC_ACM_DATA_EPIN, + NRF_CLI_CDC_ACM_DATA_EPOUT, + APP_USBD_CDC_COMM_PROTOCOL_AT_V250 +); + +/*lint -restore*/ + +NRF_QUEUE_DEF(uint8_t, + m_rx_queue, + 2*NRF_DRV_USBD_EPSIZE, + NRF_QUEUE_MODE_OVERFLOW); + +static char m_rx_buffer[NRF_DRV_USBD_EPSIZE]; + +static nrf_cli_cdc_acm_internal_t * mp_internal; + +/** + * @brief Set new buffer and process any data if already present + * + * This is internal function. + * The result of its execution is the library waiting for the event of the new data. + * If there is already any data that was returned from the CDC internal buffer + * it would be processed here. + */ +static void cdc_acm_process_and_prepare_buffer(app_usbd_cdc_acm_t const * p_cdc_acm) +{ + for (;;) + { + if (!nrf_queue_is_empty(&m_rx_queue)) + { + mp_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_RX_RDY, mp_internal->p_cb->p_context); + } + ret_code_t ret = app_usbd_cdc_acm_read_any(&nrf_cli_cdc_acm, + m_rx_buffer, + sizeof(m_rx_buffer)); + if (ret == NRF_SUCCESS) + { + size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm); + size_t qsize = nrf_queue_in(&m_rx_queue, m_rx_buffer, size); + ASSERT(size == qsize); + UNUSED_VARIABLE(qsize); + } + else if (ret == NRF_ERROR_IO_PENDING) + { + break; + } + else + { + APP_ERROR_CHECK(ret); + break; + } + } +} + +/** + * @brief User event handler. + * */ +static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst, + app_usbd_cdc_acm_user_event_t event) +{ + app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst); + + + switch (event) + { + case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN: + { + /*Setup first transfer*/ + cdc_acm_process_and_prepare_buffer(p_cdc_acm); + break; + } + case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE: + break; + case APP_USBD_CDC_ACM_USER_EVT_TX_DONE: + mp_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_TX_RDY, mp_internal->p_cb->p_context); + break; + case APP_USBD_CDC_ACM_USER_EVT_RX_DONE: + { + /*Get amount of data transfered*/ + size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm); + size_t qsize = nrf_queue_in(&m_rx_queue, m_rx_buffer, size); + ASSERT(size == qsize); + UNUSED_VARIABLE(qsize); + + /*Setup next transfer*/ + cdc_acm_process_and_prepare_buffer(p_cdc_acm); + break; + } + default: + break; + } +} + +static ret_code_t cli_cdc_acm_init(nrf_cli_transport_t const * p_transport, + void const * p_config, + nrf_cli_transport_handler_t evt_handler, + void * p_context) +{ + UNUSED_PARAMETER(p_config); + nrf_cli_cdc_acm_internal_t * p_internal = + CONTAINER_OF(p_transport, nrf_cli_cdc_acm_internal_t, transport); + p_internal->p_cb->handler = evt_handler; + p_internal->p_cb->p_context = p_context; + + mp_internal = p_internal; + return NRF_SUCCESS; +} + +static ret_code_t cli_cdc_acm_uninit(nrf_cli_transport_t const * p_transport) +{ + UNUSED_PARAMETER(p_transport); + return NRF_SUCCESS; +} + +static ret_code_t cli_cdc_acm_enable(nrf_cli_transport_t const * p_transport, + bool blocking) +{ + UNUSED_PARAMETER(p_transport); + if (blocking) + { + return NRF_ERROR_NOT_SUPPORTED; + } + else + { + return NRF_SUCCESS; + } +} + +static ret_code_t cli_cdc_acm_read(nrf_cli_transport_t const * p_transport, + void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + UNUSED_PARAMETER(p_transport); + size_t size = nrf_queue_out(&m_rx_queue, p_data, length); + *p_cnt = size; + + return NRF_SUCCESS; +} + +static ret_code_t cli_cdc_acm_write(nrf_cli_transport_t const * p_transport, + void const * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + UNUSED_PARAMETER(p_transport); + ret_code_t ret; + + ret = app_usbd_cdc_acm_write(&nrf_cli_cdc_acm, p_data, length); + if (ret == NRF_SUCCESS || ret == NRF_ERROR_INVALID_STATE) + { + *p_cnt = length; + ret = NRF_SUCCESS; + } + else if (ret == NRF_ERROR_BUSY) + { + *p_cnt = 0; + ret = NRF_SUCCESS; + } + else + { + /* Nothing to do */ + } + + return ret; +} + +const nrf_cli_transport_api_t nrf_cli_cdc_acm_transport_api = { + .init = cli_cdc_acm_init, + .uninit = cli_cdc_acm_uninit, + .enable = cli_cdc_acm_enable, + .read = cli_cdc_acm_read, + .write = cli_cdc_acm_write, +}; + +#endif // NRF_MODULE_ENABLED(NRF_CLI_CDC_ACM) diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.h new file mode 100644 index 0000000..a6d8829 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cdc_acm/nrf_cli_cdc_acm.h @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_CLI_CDC_ACM_H__ +#define NRF_CLI_CDC_ACM_H__ + +#include + +#include "nrf_cli.h" +#include "app_usbd_cdc_acm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nrf_cli_cdc_acm CDC ACM command line interface transport layer + * @ingroup nrf_cli + * + * @{ + * + */ + +/** + * @brief Command line interface transport. + */ +extern const nrf_cli_transport_api_t nrf_cli_cdc_acm_transport_api; + +/** + * @brief Command line interface class instance. + */ +extern const app_usbd_cdc_acm_t nrf_cli_cdc_acm; + +typedef struct { + nrf_cli_transport_handler_t handler; + void * p_context; +} nrf_cli_cdc_acm_internal_cb_t; + +typedef struct { + nrf_cli_transport_t transport; + nrf_cli_cdc_acm_internal_cb_t * p_cb; +} nrf_cli_cdc_acm_internal_t; + +/**@brief CLI USB transport definition */ +#define NRF_CLI_CDC_ACM_DEF(_name_) \ + static nrf_cli_cdc_acm_internal_cb_t CONCAT_2(_name_, _cb); \ + static const nrf_cli_cdc_acm_internal_t _name_ = { \ + .transport = {.p_api = &nrf_cli_cdc_acm_transport_api}, \ + .p_cb = &CONCAT_2(_name_, _cb), \ + } + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_CLI_CDC_ACM_H__ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cli_utils_cmds.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cli_utils_cmds.c new file mode 100644 index 0000000..9c0805d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/cli_utils_cmds.c @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2018 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nrf_cli.h" +#include "nrf_log.h" + +static void cmd_reset(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + NVIC_SystemReset(); +} + + +static void cmd_error(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + APP_ERROR_CHECK(NRF_ERROR_INTERNAL); +} + + +static void cmd_app_size(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "Application address:%d (0x%08X), size: %d (0x%08X)\r\n", + CODE_START, + CODE_START, + CODE_SIZE, + CODE_SIZE); +} + + +static void cmd_log_msg_error(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + switch (argc-1) + { + case 0: + NRF_LOG_ERROR("test error message"); + break; + case 1: + NRF_LOG_ERROR("test error message: %d", strtol(argv[1], NULL, 10)); + break; + case 2: + NRF_LOG_ERROR("test error message: %d %d", strtol(argv[1], NULL, 10), + strtol(argv[2], NULL, 10)); + break; + default: + NRF_LOG_ERROR("test error message with more than 3 arguments"); + break; + } +} + + +static void cmd_log_msg_warning(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + switch (argc-1) + { + case 0: + NRF_LOG_WARNING("test warning message"); + break; + case 1: + NRF_LOG_WARNING("test warning message: %d", strtol(argv[1], NULL, 10)); + break; + case 2: + NRF_LOG_WARNING("test warning message: %d %d", strtol(argv[1], NULL, 10), + strtol(argv[2], NULL, 10)); + break; + default: + NRF_LOG_WARNING("test warning message with more than 3 arguments"); + break; + } +} + +/** + * @brief Command set array + * */ + +NRF_CLI_CMD_REGISTER(reset, NULL, "System reset.", cmd_reset); + +NRF_CLI_CMD_REGISTER(error, NULL, "Trigger error.", cmd_error); + +NRF_CLI_CMD_REGISTER(app_size, NULL, "Print application size.", cmd_app_size); + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_log_msg) +{ + NRF_CLI_CMD(error, NULL, "Error log message with parameters", cmd_log_msg_error), + NRF_CLI_CMD(warning, NULL, "Warning log message with parameters", cmd_log_msg_warning), + NRF_CLI_SUBCMD_SET_END +}; + +NRF_CLI_CMD_REGISTER(log_msg, &m_sub_log_msg, "Trigger log message with decimal arguments", NULL); diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.c new file mode 100644 index 0000000..d64e141 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.c @@ -0,0 +1,247 @@ +/** + * Copyright (c) 2018 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#include "nrf_cli_libuarte.h" +#include "nrf_libuarte_async.h" +#include "nrf_assert.h" + +#define NRF_LOG_MODULE_NAME cli_libuarte +#if NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR +#else //NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#endif //NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +static cli_libuarte_internal_t * mp_internal; +static bool m_uart_busy; + +static void uart_event_handler(nrf_libuarte_async_evt_t * p_event) +{ + cli_libuarte_internal_t * p_internal = mp_internal; + ret_code_t err_code = NRF_SUCCESS; + size_t len; + UNUSED_VARIABLE(err_code); + switch (p_event->type) + { + case NRF_LIBUARTE_ASYNC_EVT_ERROR: + NRF_LOG_WARNING("(evt) ERROR"); + + break; + + case NRF_LIBUARTE_ASYNC_EVT_RX_DATA: + { + len = (size_t)((uint32_t)p_event->data.rxtx.length & 0x0000FFFF); + err_code = nrf_ringbuf_cpy_put(p_internal->p_rx_ringbuf, + p_event->data.rxtx.p_data, + &len); + ASSERT(err_code == NRF_SUCCESS); + + if (len != p_event->data.rxtx.length) + { + NRF_LOG_WARNING("Data lost, no room in RX ringbuf"); + } + nrf_libuarte_async_rx_free(p_event->data.rxtx.p_data, p_event->data.rxtx.length); + + if (p_event->data.rxtx.length) + { + NRF_LOG_DEBUG("(evt) RXRDY length:%d", p_event->data.rxtx.length); + NRF_LOG_HEXDUMP_DEBUG(p_event->data.rxtx.p_data, p_event->data.rxtx.length); + p_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_RX_RDY, + p_internal->p_cb->p_context); + } + + break; + } + + case NRF_LIBUARTE_ASYNC_EVT_TX_DONE: + err_code = nrf_ringbuf_free(p_internal->p_tx_ringbuf, p_event->data.rxtx.length); + ASSERT(err_code == NRF_SUCCESS); + uint8_t * p_data; + len = 255; + err_code = nrf_ringbuf_get(p_internal->p_tx_ringbuf, &p_data, &len, true); + ASSERT(err_code == NRF_SUCCESS); + if (len) + { + NRF_LOG_DEBUG("(evt) Started TX (%d).", len); + err_code = nrf_libuarte_async_tx(p_data, len); + ASSERT(err_code == NRF_SUCCESS); + } + else + { + m_uart_busy = false; + } + p_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_TX_RDY, p_internal->p_cb->p_context); + NRF_LOG_DEBUG("(evt) TX completed (%d)", p_event->data.rxtx.length); + break; + + default: + NRF_LOG_ERROR("(evt) Unknown event"); + ASSERT(false); + break; + } +} + +static ret_code_t cli_libuarte_init(nrf_cli_transport_t const * p_transport, + void const * p_config, + nrf_cli_transport_handler_t evt_handler, + void * p_context) +{ + cli_libuarte_internal_t * p_internal = CONTAINER_OF(p_transport, + cli_libuarte_internal_t, + transport); + mp_internal = p_internal; + m_uart_busy = false; + + p_internal->p_cb->handler = evt_handler; + p_internal->p_cb->p_context = p_context; + p_internal->p_cb->blocking = false; + + cli_libuarte_config_t const * p_cli_libuarte_config = (cli_libuarte_config_t *)p_config; + nrf_libuarte_async_config_t uart_async_config = { + .tx_pin = p_cli_libuarte_config->tx_pin, + .rx_pin = p_cli_libuarte_config->rx_pin, + .baudrate = p_cli_libuarte_config->baudrate, + .parity = p_cli_libuarte_config->parity, + .hwfc = p_cli_libuarte_config->hwfc, + .timeout_us = 100, + }; + ret_code_t err_code = nrf_libuarte_async_init(&uart_async_config, uart_event_handler); + if (err_code == NRF_SUCCESS) + { + nrf_ringbuf_init(p_internal->p_rx_ringbuf); + nrf_ringbuf_init(p_internal->p_tx_ringbuf); + } + return err_code; +} + +static ret_code_t cli_libuarte_uninit(nrf_cli_transport_t const * p_transport) +{ + UNUSED_PARAMETER(p_transport); + nrf_libuarte_async_uninit(); + return NRF_SUCCESS; +} + +static ret_code_t cli_libuarte_enable(nrf_cli_transport_t const * p_transport, + bool blocking) +{ + UNUSED_PARAMETER(p_transport); + if (blocking) + { + return NRF_ERROR_NOT_SUPPORTED; + } + else + { + nrf_libuarte_async_enable(255); + } + return NRF_SUCCESS; +} + +static ret_code_t cli_libuarte_read(nrf_cli_transport_t const * p_transport, + void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + cli_libuarte_internal_t * p_instance = + CONTAINER_OF(p_transport, cli_libuarte_internal_t, transport); + + *p_cnt = length; + ret_code_t err_code = nrf_ringbuf_cpy_get(p_instance->p_rx_ringbuf, p_data, p_cnt); + + if (*p_cnt) + { + NRF_LOG_DEBUG("Read %d bytes (requested %d)", *p_cnt, length); + } + + return err_code; +} + +static ret_code_t cli_libuarte_write(nrf_cli_transport_t const * p_transport, + void const * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + cli_libuarte_internal_t * p_instance = CONTAINER_OF(p_transport, + cli_libuarte_internal_t, + transport); + *p_cnt = length; + ret_code_t err_code = nrf_ringbuf_cpy_put(p_instance->p_tx_ringbuf, p_data, p_cnt); + if (err_code == NRF_SUCCESS) + { + NRF_LOG_DEBUG("Requested write: %d, copied to ringbuf: %d.", length, *p_cnt); + + if (m_uart_busy) + { + return err_code; + } + + uint8_t * p_buf; + size_t len = 255; + if (nrf_ringbuf_get(p_instance->p_tx_ringbuf, &p_buf, &len, true) == NRF_SUCCESS) + { + NRF_LOG_DEBUG("Started TX (%d).", len); + + err_code = nrf_libuarte_async_tx(p_buf, len); + if (p_instance->p_cb->blocking && (err_code == NRF_SUCCESS)) + { + (void)nrf_ringbuf_free(p_instance->p_tx_ringbuf, len); + } + else + { + m_uart_busy = true; + } + } + } + return err_code; +} + +const nrf_cli_transport_api_t cli_libuarte_transport_api = { + .init = cli_libuarte_init, + .uninit = cli_libuarte_uninit, + .enable = cli_libuarte_enable, + .read = cli_libuarte_read, + .write = cli_libuarte_write, +}; + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.h new file mode 100644 index 0000000..e37fe64 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/libuarte/nrf_cli_libuarte.h @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2018 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_BACKEND_LIBUARTE_H__ +#define NRF_LOG_BACKEND_LIBUARTE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "nrf_cli.h" +#include "nrf_libuarte.h" +#include "nrf_ringbuf.h" +#include "app_timer.h" + +/**@file + * + * @defgroup nrf_cli_libuarte libUARTE command line interface transport layer. + * @ingroup nrf_cli + * + * @{ + * + */ + +/** + * @brief Command line interface transport. + * */ +extern const nrf_cli_transport_api_t cli_libuarte_transport_api; + +typedef struct cli_libuarte_internal_s cli_libuarte_internal_t; + +typedef struct { + nrf_cli_transport_handler_t handler; + void * p_context; + bool blocking; +} cli_libuarte_internal_cb_t; + +struct cli_libuarte_internal_s { + nrf_cli_transport_t transport; + cli_libuarte_internal_cb_t * p_cb; + nrf_ringbuf_t const * p_rx_ringbuf; + nrf_ringbuf_t const * p_tx_ringbuf; +}; + +typedef struct +{ + uint32_t tx_pin; + uint32_t rx_pin; + nrf_uarte_hwfc_t hwfc; ///< Flow control configuration. + nrf_uarte_parity_t parity; ///< Parity configuration. + nrf_uarte_baudrate_t baudrate; ///< Baudrate. +} cli_libuarte_config_t; + +/**@brief CLI libUARTE transport definition. + * + * @param _name Name of instance. + * @param _tx_buf_sz Size of TX ring buffer. + * @param _rx_buf_sz Size of RX ring buffer. + */ +#define NRF_CLI_LIBUARTE_DEF(_name, _tx_buf_sz, _rx_buf_sz) \ + NRF_RINGBUF_DEF(CONCAT_2(_name,_tx_ringbuf), _tx_buf_sz); \ + NRF_RINGBUF_DEF(CONCAT_2(_name,_rx_ringbuf), _rx_buf_sz); \ + static cli_libuarte_internal_cb_t CONCAT_2(_name, _cb); \ + static const cli_libuarte_internal_t _name = { \ + .transport = {.p_api = &cli_libuarte_transport_api}, \ + .p_cb = &CONCAT_2(_name, _cb), \ + .p_rx_ringbuf = &CONCAT_2(_name,_rx_ringbuf), \ + .p_tx_ringbuf = &CONCAT_2(_name,_tx_ringbuf), \ + } +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_LOG_BACKEND_LIBUARTE_H__ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.c new file mode 100644 index 0000000..1e4eba5 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.c @@ -0,0 +1,3500 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_CLI) +#include +#include +#include +#include "nrf_cli.h" +#include "nrf_cli_vt100.h" +#include "app_error.h" +#include "nrf_assert.h" +#include "nrf_delay.h" +#include "nrf_pwr_mgmt.h" +#include "nrf_atomic.h" + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) +#include "fnmatch.h" +#endif + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + #if NRF_CLI_HISTORY_ELEMENT_SIZE * NRF_CLI_HISTORY_ELEMENT_COUNT == 0 + #error Not proper memory size allocated for NRF_CLI_HISTORY + #endif +#endif + +/* 2 == 1 char for cmd + 1 char for '\0' */ +#if NRF_CLI_CMD_BUFF_SIZE < 2 + #error too small NRF_CLI_CMD_BUFF_SIZE +#endif + +#if NRF_CLI_PRINTF_BUFF_SIZE < 1 + #error too small NRF_CLI_PRINTF_BUFF_SIZE +#endif + +#define NRF_CLI_HELP_CLEAR "Clear screen." +#define NRF_CLI_HELP_COLORS "Toggle colored syntax." +#define NRF_CLI_HELP_COLORS_OFF "Disable colored syntax." +#define NRF_CLI_HELP_COLORS_ON "Enable colored syntax." +#define NRF_CLI_HELP_STATISTICS "CLI statistics." +#define NRF_CLI_HELP_STATISTICS_SHOW "Get CLI statistics for the Logger module." +#define NRF_CLI_HELP_STATISTICS_RESET "Reset CLI statistics for the Logger module." +#define NRF_CLI_HELP_RESIZE "Console gets terminal screen size or assumes 80 in case " \ + "the readout fails. It must be executed after each terminal " \ + "width change to ensure correct text display." +#define NRF_CLI_HELP_RESIZE_DEFAULT "Assume 80 chars screen width and send this setting " \ + "to the terminal." +#define NRF_CLI_HELP_HISTORY "Command history." +#define NRF_CLI_HELP_ECHO "Toggle CLI echo." +#define NRF_CLI_HELP_ECHO_ON "Enable CLI echo." +#define NRF_CLI_HELP_ECHO_OFF "Disable CLI echo. Arrows and buttons: Backspace, " \ + "Delete, End, Home, Insert are not handled." +#define NRF_CLI_HELP_CLI "Useful, not Unix-like CLI commands." + +#define NRF_CLI_MSG_SPECIFY_SUBCOMMAND "Please specify a subcommand.\r\n" +#define NRF_CLI_MSG_UNKNOWN_PARAMETER " unknown parameter: " +#define NRF_CLI_MSG_COMMAND_NOT_FOUND ": command not found" +#define NRF_CLI_MSG_TAB_OVERFLOWED "Tab function: commands counter overflowed.\r\n" + +/*lint -save -esym(526,cli_command*) -esym(526,cli_sorted_cmd_ptrs*)*/ +NRF_SECTION_DEF(cli_command, nrf_cli_cmd_entry_t); +#define CLI_DATA_SECTION_ITEM_GET(i) NRF_SECTION_ITEM_GET(cli_command, nrf_cli_cmd_entry_t, (i)) +#define CLI_DATA_SECTION_ITEM_COUNT NRF_SECTION_ITEM_COUNT(cli_command, nrf_cli_cmd_entry_t) + +NRF_SECTION_DEF(cli_sorted_cmd_ptrs, const char *); +/*lint -restore*/ +#define CLI_SORTED_CMD_PTRS_ITEM_GET(i) NRF_SECTION_ITEM_GET(cli_sorted_cmd_ptrs, const char *, (i)) +#define CLI_SORTED_CMD_PTRS_START_ADDR_GET NRF_SECTION_START_ADDR(cli_sorted_cmd_ptrs) + +#if defined(NRF_CLI_LOG_BACKEND) && NRF_CLI_LOG_BACKEND +#include "nrf_log_str_formatter.h" +#include "nrf_log_internal.h" +#endif + +#define NRF_CLI_INIT_OPTION_PRINTER (NULL) + +#define NRF_CLI_MAX_TERMINAL_SIZE (250u) +#define NRF_CLI_CURSOR_POSITION_BUFFER (10u) /* 10 == {esc, [, 2, 5, 0, ;, 2, 5, 0, '\0'} */ +#define NRF_CLI_DEFAULT_TERMINAL_WIDTH (80u) /* Default PuTTY width. */ +#define NRF_CLI_DEFAULT_TERMINAL_HEIGHT (24u) /* Default PuTTY height. */ +#define NRF_CLI_INITIAL_CURS_POS (1u) /* Initial cursor position is: (1, 1). */ + +#define NRF_CLI_CMD_ROOT_LVL (0u) + +/* Macro to send VT100 commands. */ +#define NRF_CLI_VT100_CMD(_p_cli_, _cmd_) { \ + ASSERT(_p_cli_); \ + ASSERT(_p_cli_->p_fprintf_ctx); \ + static char const cmd[] = _cmd_; \ + nrf_fprintf(_p_cli_->p_fprintf_ctx, "%s", cmd); \ +} + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) +typedef enum +{ + WILDCARD_CMD_ADDED, + WILDCARD_CMD_ADDED_MISSING_SPACE, + WILDCARD_CMD_NO_MATCH_FOUND +} wildcard_cmd_status_t; +#endif + + +static bool cli_log_entry_process(nrf_cli_t const * p_cli, bool skip); +static void cli_execute(nrf_cli_t const * p_cli); + + +static inline void transport_buffer_flush(nrf_cli_t const * p_cli) +{ + nrf_fprintf_buffer_flush(p_cli->p_fprintf_ctx); +} + +static inline void cli_flag_help_set(nrf_cli_t const * p_cli) +{ + p_cli->p_ctx->internal.flag.show_help = 1; +} +static inline void cli_flag_help_clear(nrf_cli_t const * p_cli) +{ + p_cli->p_ctx->internal.flag.show_help = 0; +} + +static inline void cli_flag_echo_set(nrf_cli_t const * p_cli) +{ + p_cli->p_ctx->internal.flag.echo = 1; +} + +static inline void cli_flag_echo_clear(nrf_cli_t const * p_cli) +{ + p_cli->p_ctx->internal.flag.echo = 0; +} + +static inline bool cli_flag_echo_is_set(nrf_cli_t const * p_cli) +{ + return p_cli->p_ctx->internal.flag.echo == 1 ? true : false; +} + +static inline bool cli_flag_processing_is_set(nrf_cli_t const * p_cli) +{ + return p_cli->p_ctx->internal.flag.processing == 1 ? true : false; +} + +static inline void recieve_state_change(nrf_cli_t const * p_cli, nrf_cli_receive_t state) +{ + p_cli->p_ctx->receive_state = state; +} + +static inline size_t cli_strlen(char const * str) +{ + return str == NULL ? 0 : strlen(str); +} + +/* Function returns true if cursor is at beginning of an empty line. */ +static inline bool cursor_in_empty_line(nrf_cli_t const * p_cli) +{ + return ( (p_cli->p_ctx->cmd_buff_pos + cli_strlen(p_cli->p_name)) % + p_cli->p_ctx->vt100_ctx.cons.terminal_wid == 0); +} + +/* Function returns true if command length is equal to multiplicity of terminal width. */ +static inline bool full_line_cmd(nrf_cli_t const * p_cli) +{ + return ((p_cli->p_ctx->cmd_buff_len + cli_strlen(p_cli->p_name)) % + p_cli->p_ctx->vt100_ctx.cons.terminal_wid == 0); +} + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) +/* Function returns true if string contains wildcard character: '?' or '*'. */ +static bool wildcard_character_exist(char * p_str) +{ + size_t str_len = cli_strlen(p_str); + for (size_t i = 0; i < str_len; i++) + { + if ((p_str[i] == '?') || (p_str[i] == '*')) + { + return true; + } + } + return false; +} +#endif + +/* Function sends data stream to the CLI instance. Each time before the cli_write function is called, + * it must be ensured that IO buffer of fprintf is flushed to avoid synchronization issues. + * For that purpose, use function transport_buffer_flush(p_cli) */ +static void cli_write(nrf_cli_t const * p_cli, + void const * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cli && p_data); + ASSERT(p_cli->p_iface->p_api); + size_t offset = 0; + size_t cnt; + while (length) + { + ret_code_t ret = p_cli->p_iface->p_api->write(p_cli->p_iface, + &((uint8_t const *)p_data)[offset], + length, + &cnt); + UNUSED_VARIABLE(ret); + ASSERT(ret == NRF_SUCCESS); + ASSERT(length >= cnt); + offset += cnt; + length -= cnt; + if (cnt == 0 && (p_cli->p_ctx->state != NRF_CLI_STATE_PANIC_MODE_ACTIVE)) + { +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + (void)task_events_wait(NRF_CLI_TRANSPORT_TX_RDY_TASK_EVT); +#else + while (p_cli->p_ctx->internal.flag.tx_rdy == 0) + { + ; + } + p_cli->p_ctx->internal.flag.tx_rdy = 0; +#endif + } + } + + if (p_cnt) + { + *p_cnt = cnt; + } +} + +/* Function sends 1 character to the CLI instance. */ +static inline void cli_putc(nrf_cli_t const * p_cli, char ch) +{ + nrf_fprintf(p_cli->p_fprintf_ctx, "%c", ch); +} + +/* Function reads data from the CLI instance. */ +static void cli_read(nrf_cli_t const * p_cli, + void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cli && p_data); + ASSERT(p_cli->p_iface); + + ret_code_t ret = p_cli->p_iface->p_api->read(p_cli->p_iface, p_data, length, p_cnt); + UNUSED_VARIABLE(ret); +} + +/* Function cmd_get shall be used to search commands. It moves the pointer pp_entry to command + * of static command structure. If the command cannot be found, the function will set pp_entry to NULL. + * p_command Pointer to command which will be processed (no matter the root command). + * lvl Level of the requested command. + * idx Index of the requested command. + * pp_entry Pointer which points to subcommand[idx] after function execution. + * p_st_entry Pointer to the structure where dynamic entry data can be stored. + */ +static void cmd_get(nrf_cli_cmd_entry_t const * p_command, + size_t lvl, + size_t idx, + nrf_cli_static_entry_t const ** pp_entry, + nrf_cli_static_entry_t * p_st_entry) +{ + ASSERT (pp_entry != NULL); + ASSERT (p_st_entry != NULL); + + if (lvl == NRF_CLI_CMD_ROOT_LVL) + { + if (idx < CLI_DATA_SECTION_ITEM_COUNT) + { + nrf_cli_cmd_entry_t const * p_cmd = NULL; + char const * * pp_sorted_cmds = (char const * *)CLI_SORTED_CMD_PTRS_START_ADDR_GET; + for (size_t i = 0; i < CLI_DATA_SECTION_ITEM_COUNT; i++) + { + p_cmd = CLI_DATA_SECTION_ITEM_GET(i); + if (!strcmp(pp_sorted_cmds[idx], p_cmd->u.p_static->p_syntax)) + { + *pp_entry = p_cmd->u.p_static; + return; + } + } + } + *pp_entry = NULL; + return; + } + + if (p_command == NULL) + { + *pp_entry = NULL; + return; + } + + if (p_command->is_dynamic) + { + p_command->u.p_dynamic_get(idx, p_st_entry); + if (p_st_entry->p_syntax == NULL) + { + *pp_entry = NULL; + } + else + { + *pp_entry = p_st_entry; + } + } + else + { + if (p_command->u.p_static[idx].p_syntax != NULL) + { + *pp_entry = &p_command->u.p_static[idx]; + } + else + { + *pp_entry = NULL; + } + } +} + +/* Function multiline_console_data_check checks the current cursor position (x, y) on terminal screen + * based on: command length, console name length, and terminal width. + * Example 1: + * || - cursor + * ---------------------------- + * |console_name $: || | + * ---------------------------- + * => coordinates are: cur_x = 17, cur_x_end = 17, + * cur_y = 1, cur_y_end = 1 + * Example 2: + * ---------------------------- + * |console_name $: test command| + * |showing |e|xample | + * ---------------------------- + * => coordinates are: cur_x = 9, cur_x_end = 18 (cursor can be one column after 'e') + * => cur_y = 2, cur_y_end = 2 + * Example 3: + * ---------------------------- + * |console_name $: test command| + * |showing e|x|ample with more | + * |parameters | + * ---------------------------- + * => coordinates are: cur_x = 10, cur_x_end = 11 (cursor can be one column after 's') + * => cur_y = 2, cur_y_end = 3 + */ +static nrf_cli_multiline_cons_t const * multiline_console_data_check(nrf_cli_t const * p_cli) +{ + nrf_cli_ctx_t * p_ctx = p_cli->p_ctx; + nrf_cli_multiline_cons_t * p_cons = &p_cli->p_ctx->vt100_ctx.cons; + + p_cons->name_len = cli_strlen(p_cli->p_name); + + /* Current cursor position in command. + * +1 -> because home position is (1, 1) */ + p_cons->cur_x = (p_ctx->cmd_buff_pos + p_cons->name_len) % p_cons->terminal_wid + 1; + p_cons->cur_y = (p_ctx->cmd_buff_pos + p_cons->name_len) / p_cons->terminal_wid + 1; + + /* Extreme position when cursor is at the end of command. */ + p_cons->cur_y_end = (p_ctx->cmd_buff_len + p_cons->name_len) / p_cons->terminal_wid + 1; + p_cons->cur_x_end = (p_ctx->cmd_buff_len + p_cons->name_len) % p_cons->terminal_wid + 1; + + return p_cons; +} + +/* Function sends VT100 command to clear the screen from cursor position to end of the screen. */ +static inline void cli_clear_eos(nrf_cli_t const * p_cli) +{ + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CLEAREOS); +} + +/* Function sends VT100 command to save cursor position. */ +static inline void cli_cursor_save(nrf_cli_t const * p_cli) +{ + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_SAVECURSOR); +} + +/* Function sends VT100 command to restore saved cursor position. */ +static inline void cli_cursor_restore(nrf_cli_t const * p_cli) +{ + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_RESTORECURSOR); +} + +/* Function forcing new line - cannot be replaced with function cursor_down_move. */ +static inline void cursor_next_line_move(nrf_cli_t const * p_cli) +{ + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_NEXTLINE); +} + +/* Function moves cursor left by n positions. */ +static inline void cursor_left_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n) +{ + if (n > 0) + { + nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dD", n); + } +} + +/* Function moves cursor right by n positions. */ +static inline void cursor_right_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n) +{ + if (n > 0) + { + nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dC", n); + } +} + +/* Function moves cursor up by n positions. */ +static inline void cursor_up_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n) +{ + if (n > 0) + { + nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dA", n); + } +} + +/* Function moves cursor down by n positions but it will bring no effect if cursor is in the last + * line of terminal screen. In such case, the cursor_next_line_move function shall be invoked. */ +static inline void cursor_down_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n) +{ + if (n > 0) + { + nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dB", n); + } +} + +/* Function increments cursor position (if possible) and moves cursor to new line if necessary. */ +static void cursor_position_increment(nrf_cli_t const * p_cli) +{ + if (p_cli->p_ctx->cmd_buff_pos >= p_cli->p_ctx->cmd_buff_len) + { + return; /* incrementation not possible */ + } + + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + nrf_cli_cmd_len_t cur_y = p_cons->cur_y; + ++p_cli->p_ctx->cmd_buff_pos; + p_cons = multiline_console_data_check(p_cli); + + if (cur_y == p_cons->cur_y) + { + cursor_right_move(p_cli, 1); + } + else + { + cursor_next_line_move(p_cli); + } +} + +/* Function will move cursor back to position == cmd_buff_pos. Example usage is when cursor needs + * to be moved back after printing some text. This function cannot be used to move cursor to new + * location by manual change of cmd_buff_pos.*/ +static void cursor_position_synchronize(nrf_cli_t const * p_cli) +{ + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false; + + /* In case cursor reaches the bottom line of a terminal, it will be moved to the next line. */ + if (cursor_in_empty_line(p_cli) || full_line_cmd(p_cli)) + { + cursor_next_line_move(p_cli); + } + + if (last_line) + { + cursor_left_move(p_cli, p_cons->cur_x_end - p_cons->cur_x); + } + else + { + cursor_up_move(p_cli, p_cons->cur_y_end - p_cons->cur_y); + if (p_cons->cur_x > p_cons->cur_x_end) + { + cursor_right_move(p_cli, p_cons->cur_x - p_cons->cur_x_end); + } + else + { + cursor_left_move(p_cli, p_cons->cur_x_end - p_cons->cur_x); + } + } +} + +/* Function moves cursor to begin of command position, just after console name. */ +static void cursor_home_position_move(nrf_cli_t const * p_cli) +{ + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + + if ((p_cons->cur_x == p_cons->name_len + NRF_CLI_INITIAL_CURS_POS) && + (p_cons->cur_y == NRF_CLI_INITIAL_CURS_POS)) + { + return; /* nothing to handle because cursor is in start position */ + } + + if (p_cons->cur_y > NRF_CLI_INITIAL_CURS_POS) + { + cursor_up_move(p_cli, p_cons->cur_y - NRF_CLI_INITIAL_CURS_POS); + } + + if (p_cons->cur_x > p_cons->name_len) + { + cursor_left_move(p_cli, p_cons->cur_x - NRF_CLI_INITIAL_CURS_POS - p_cons->name_len); + } + else + { + cursor_right_move(p_cli, p_cons->name_len + NRF_CLI_INITIAL_CURS_POS - p_cons->cur_x); + } + /* align data buffer pointer with cursor position */ + p_cli->p_ctx->cmd_buff_pos = 0; +} + +/* Function moves cursor to end of command. */ +static void cursor_end_position_move(nrf_cli_t const * p_cli) +{ + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + + if ((p_cons->cur_x == p_cons->cur_x_end) && (p_cons->cur_y == p_cons->cur_y_end)) + { + return; /* nothing to handle because cursor is in end position */ + } + + if (p_cons->cur_y_end > p_cons->cur_y) + { + cursor_down_move(p_cli, p_cons->cur_y_end - p_cons->cur_y); + } + + if (p_cons->cur_x > p_cons->cur_x_end) + { + cursor_left_move(p_cli, p_cons->cur_x - p_cons->cur_x_end); + } + else + { + cursor_right_move(p_cli, p_cons->cur_x_end - p_cons->cur_x); + } + /* align data buffer pointer with cursor position */ + p_cli->p_ctx->cmd_buff_pos = p_cli->p_ctx->cmd_buff_len; +} + +#if NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS) +/* Function reads cursor position from terminal. */ +static ret_code_t cursor_position_get(nrf_cli_t const * p_cli) +{ + size_t cnt; + uint16_t x = 0; /* horizontal position */ + uint16_t y = 0; /* vertical position */ + char c = 0; + + nrf_cli_cmd_len_t buff_idx = 0; + + /* clear temp buffer */ + memset(p_cli->p_ctx->temp_buff, 0, sizeof(p_cli->p_ctx->temp_buff)); + + /* escape code asking terminal about its size */ + static char const cmd_get_terminal_size[] = "\033[6n"; + + nrf_fprintf(p_cli->p_fprintf_ctx, cmd_get_terminal_size); + /* fprintf buffer needs to be flushed to start sending prepared escape code to the terminal */ + transport_buffer_flush(p_cli); + + /* timeout for terminal response = ~1s */ + for (uint16_t i = 0; i < 1000; i++) + { + do + { + cli_read(p_cli, &c, sizeof(c), &cnt); + if (cnt == 0) + { + nrf_delay_us(999); + continue; + } + if ((c != NRF_CLI_VT100_ASCII_ESC) && + (p_cli->p_ctx->temp_buff[0] != NRF_CLI_VT100_ASCII_ESC)) + { + continue; + } + + if (c == 'R') /* end of response from the terminal */ + { + p_cli->p_ctx->temp_buff[buff_idx] = '\0'; + if (p_cli->p_ctx->temp_buff[1] != '[') + { + p_cli->p_ctx->temp_buff[0] = 0; + return NRF_ERROR_INVALID_DATA; + } + buff_idx = 2; /* index start position in the buffer where 'y' is stored */ + while (p_cli->p_ctx->temp_buff[buff_idx] != ';') + { + y = y * 10 + (p_cli->p_ctx->temp_buff[buff_idx++] - '0'); + if (buff_idx >= NRF_CLI_CMD_BUFF_SIZE) + { + return NRF_ERROR_DATA_SIZE; + } + } + if (++buff_idx >= NRF_CLI_CMD_BUFF_SIZE) + { + return NRF_ERROR_DATA_SIZE; + } + while (p_cli->p_ctx->temp_buff[buff_idx] != '\0') + { + x = x * 10 + (p_cli->p_ctx->temp_buff[buff_idx++] - '0'); + if (buff_idx >= NRF_CLI_CMD_BUFF_SIZE) + { + return NRF_ERROR_DATA_SIZE; + } + } + /* horizontal cursor position */ + if (x > NRF_CLI_MAX_TERMINAL_SIZE) + { + p_cli->p_ctx->vt100_ctx.cons.cur_x = NRF_CLI_MAX_TERMINAL_SIZE; + } + else + { + p_cli->p_ctx->vt100_ctx.cons.cur_x = (nrf_cli_cmd_len_t)x; + } + /* vertical cursor position */ + if (y > NRF_CLI_MAX_TERMINAL_SIZE) + { + p_cli->p_ctx->vt100_ctx.cons.cur_y = NRF_CLI_MAX_TERMINAL_SIZE; + } + else + { + p_cli->p_ctx->vt100_ctx.cons.cur_y = (nrf_cli_cmd_len_t)y; + } + p_cli->p_ctx->temp_buff[0] = 0; + return NRF_SUCCESS; + } + else + { + p_cli->p_ctx->temp_buff[buff_idx] = c; + } + + if (++buff_idx > NRF_CLI_CURSOR_POSITION_BUFFER - 1) + { + p_cli->p_ctx->temp_buff[0] = 0; + /* data_buf[NRF_CLI_CURSOR_POSITION_BUFFER - 1] is reserved for '\0' */ + return NRF_ERROR_NO_MEM; + } + + } while (cnt > 0); + } + return NRF_ERROR_TIMEOUT; +} + +/* Function gets terminal width and height. */ +static ret_code_t terminal_size_get(nrf_cli_t const * p_cli, + nrf_cli_cmd_len_t * p_length, + nrf_cli_cmd_len_t * p_height) +{ + ASSERT(p_length); + ASSERT(p_height); + + uint16_t x; + uint16_t y; + + if (cursor_position_get(p_cli) == NRF_SUCCESS) + { + x = p_cli->p_ctx->vt100_ctx.cons.cur_x; + y = p_cli->p_ctx->vt100_ctx.cons.cur_y; + /* assumption: terminal widht and height < 999 */ + cursor_right_move(p_cli, NRF_CLI_MAX_TERMINAL_SIZE); /* move to last column */ + cursor_down_move(p_cli, NRF_CLI_MAX_TERMINAL_SIZE); /* move to last row */ + } + else + { + return NRF_ERROR_NOT_SUPPORTED; + } + + if (cursor_position_get(p_cli) == NRF_SUCCESS) + { + *p_length = p_cli->p_ctx->vt100_ctx.cons.cur_x; + *p_height = p_cli->p_ctx->vt100_ctx.cons.cur_y; + cursor_left_move(p_cli, *p_length - x); + cursor_up_move(p_cli, *p_height - y); + + return NRF_SUCCESS; + } + return NRF_ERROR_NOT_SUPPORTED; +} +#endif // NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS) + +#if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) +static void vt100_color_set(nrf_cli_t const * p_cli, nrf_cli_vt100_color_t color) +{ + if (color != NRF_CLI_DEFAULT) + { + if (p_cli->p_ctx->vt100_ctx.col.col == color) + { + return; + } + + uint8_t cmd[] = NRF_CLI_VT100_COLOR(color - 1); + + p_cli->p_ctx->vt100_ctx.col.col = color; + nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd); + } + else + { + static uint8_t const cmd[] = NRF_CLI_VT100_MODESOFF; + + p_cli->p_ctx->vt100_ctx.col.col = color; + nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd); + } +} + +static void vt100_bgcolor_set(nrf_cli_t const * p_cli, nrf_cli_vt100_color_t bgcolor) +{ + if (bgcolor != NRF_CLI_DEFAULT) + { + if (p_cli->p_ctx->vt100_ctx.col.bgcol == bgcolor) + { + return; + } + /* -1 because default value is first in enum */ + uint8_t cmd[] = NRF_CLI_VT100_BGCOLOR(bgcolor - 1); + + p_cli->p_ctx->vt100_ctx.col.bgcol = bgcolor; + nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd); + } +} + +static inline void vt100_colors_store(nrf_cli_t const * p_cli, + nrf_cli_vt100_colors_t * p_color) +{ + memcpy(p_color, &p_cli->p_ctx->vt100_ctx.col, sizeof(nrf_cli_vt100_colors_t)); +} + +static void vt100_colors_restore(nrf_cli_t const * p_cli, + nrf_cli_vt100_colors_t const * p_color) +{ + vt100_color_set(p_cli, p_color->col); + vt100_bgcolor_set(p_cli, p_color->bgcol); +} +#endif // NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) + +static void left_arrow_handle(nrf_cli_t const * p_cli) +{ + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + + if ((p_cons->cur_x == p_cons->name_len + NRF_CLI_INITIAL_CURS_POS) && + (p_cons->cur_y == NRF_CLI_INITIAL_CURS_POS)) + { + return; /* nothing to handle because cursor is in start position */ + } + + if (p_cons->cur_x == NRF_CLI_INITIAL_CURS_POS) + { /* go to previous line */ + cursor_up_move(p_cli, 1); + cursor_right_move(p_cli, p_cons->terminal_wid); + --p_cli->p_ctx->cmd_buff_pos; + } + else + { + cursor_left_move(p_cli, 1); + --p_cli->p_ctx->cmd_buff_pos; + } +} + +static void right_arrow_handle(nrf_cli_t const * p_cli) +{ + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + + if ((p_cons->cur_x == p_cons->cur_x_end) && + (p_cons->cur_y == p_cons->cur_y_end)) + { + return; /* nothing to handle because cursor is in start position */ + } + + if (p_cons->cur_x == p_cons->terminal_wid) /* go to next line */ + { + cursor_down_move(p_cli, 1); + cursor_left_move(p_cli, p_cons->terminal_wid); + ++p_cli->p_ctx->cmd_buff_pos; + } + else + { + cursor_right_move(p_cli, 1); + ++p_cli->p_ctx->cmd_buff_pos; + } +} + +static inline void char_insert_echo_off(nrf_cli_t const * p_cli, char data) +{ + if (p_cli->p_ctx->cmd_buff_len >= (NRF_CLI_CMD_BUFF_SIZE - 1)) + { + return; + } + + p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos++] = data; + p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos] = '\0'; + ++p_cli->p_ctx->cmd_buff_len; +} + +static void char_insert(nrf_cli_t const * p_cli, char data) +{ + nrf_cli_cmd_len_t diff; + bool ins_mode = (bool)p_cli->p_ctx->internal.flag.insert_mode; + + diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos; + + if (!ins_mode) + { + if (p_cli->p_ctx->cmd_buff_len >= (NRF_CLI_CMD_BUFF_SIZE - 1)) + { + return; + } + if (diff > 0) + { + memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos + 1], + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos], + diff); + } + } + else + { + if ((p_cli->p_ctx->cmd_buff_len >= (NRF_CLI_CMD_BUFF_SIZE - 1)) && + (diff == 0)) + { + /* If cmd buffer is full, it is possible to replace chars but adding new + is not allowed. */ + return; + } + } + + p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos] = data; + + if (!ins_mode) + { + p_cli->p_ctx->cmd_buff[++p_cli->p_ctx->cmd_buff_len] = '\0'; + } + + if (diff > 0) + { + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false; + + /* Below if-else statement is to minimize esc codes transmission. */ + if (last_line) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s", + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]); + /* Move cursor one position left less in case of insert mode. */ + cursor_left_move(p_cli, diff - ins_mode); + } + else + { + /* Save the current cursor position in order to get back after fprintf function. */ + cli_cursor_save(p_cli); + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s", + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]); + cli_cursor_restore(p_cli); + /* Move cursor right by one position to edit the next character. */ + cursor_right_move(p_cli, 1); + } + } + else + { + /* New char appended at the end of buffer. */ + if (ins_mode) + { + p_cli->p_ctx->cmd_buff[++p_cli->p_ctx->cmd_buff_len] = '\0'; + } + cli_putc(p_cli, data); + } + + /* Incrementation needs to be executed before invoking function: cursor_in_empty_line. */ + ++p_cli->p_ctx->cmd_buff_pos; + + /* Forcing terminal to switch to a new line if the command is too long. */ + if (cursor_in_empty_line(p_cli)) + { + cursor_next_line_move(p_cli); + return; + } + + if (full_line_cmd(p_cli)) + { + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + + /* The code below will force the terminal to scroll one line down when the currently entered command + * reaches lower right corner of the terminal screen. */ + cursor_down_move(p_cli, p_cons->cur_y_end - p_cons->cur_y - 1); + cursor_next_line_move(p_cli); + cursor_up_move(p_cli, p_cons->cur_y_end - p_cons->cur_y); + cursor_right_move(p_cli, p_cons->cur_x - 1); + return; + } +} + +static void char_backspace(nrf_cli_t const * p_cli) +{ + nrf_cli_cmd_len_t diff; + + if ((p_cli->p_ctx->cmd_buff_len == 0) || (p_cli->p_ctx->cmd_buff_pos == 0)) + { + return; + } + + diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos; + + memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos - 1], + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos], + diff + 1); + + --p_cli->p_ctx->cmd_buff_pos; + --p_cli->p_ctx->cmd_buff_len; + + if (diff > 0) + { + cli_putc(p_cli, NRF_CLI_VT100_ASCII_BSPACE); + + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false; + + if (last_line) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s", + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]); + cli_clear_eos(p_cli); + cursor_left_move(p_cli, diff); + } + else + { + /* If cursor is not in last cmd line, its position needs to be saved by + * VT100 command. */ + cli_cursor_save(p_cli); + cli_clear_eos(p_cli); + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s", + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]); + cli_cursor_restore(p_cli); + } + } + else + { + static char const cmd_bspace[] = { + NRF_CLI_VT100_ASCII_BSPACE, ' ', NRF_CLI_VT100_ASCII_BSPACE, '\0'}; + nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd_bspace); + } +} + +static void char_delete(nrf_cli_t const * p_cli) +{ + nrf_cli_cmd_len_t diff; + + diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos; + + if (diff == 0) + { + return; + } + + memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos], + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos + 1], + diff); + + --p_cli->p_ctx->cmd_buff_len; + + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false; + + /* If last line of command is edited, there is no need for saving cursor position. */ + if (last_line) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s", + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]); + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CLEAREOL); + cursor_left_move(p_cli, --diff); + } + else + { + cli_cursor_save(p_cli); + cli_clear_eos(p_cli); + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s", + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]); + cli_cursor_restore(p_cli); + } +} + +static char make_argv(size_t * p_argc, char ** pp_argv, char * p_cmd, uint8_t max_argc) +{ + char c; + char quote = 0; + + *p_argc = 0; + do + { + c = *p_cmd; + if (c == '\0') + { + break; + } + + if (isspace((int)c)) + { + *p_cmd++ = '\0'; + continue; + } + + pp_argv[(*p_argc)++] = p_cmd; + quote = 0; + + while (1) + { + c = *p_cmd; + + if (c == '\0') + { + break; + } + + if (!quote) + { + switch (c) + { + case '\\': + memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd)); + p_cmd += 1; + continue; + + case '\'': + case '\"': + memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd)); + quote = c; + continue; + default: + break; + } + } + + if (quote == c) + { + memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd)); + quote = 0; + continue; + } + + if (quote && c == '\\') + { + char t = *(p_cmd + 1); + + if (t == quote) + { + memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd)); + p_cmd += 1; + continue; + } + + if (t == '0') + { + uint8_t i; + uint8_t v = 0; + + for (i = 2; i < (2 + 3); i++) + { + t = *(p_cmd + i); + + if (t >= '0' && t <= '7') + { + v = (v << 3) | (t - '0'); + } + else + { + break; + } + } + + if (i > 2) + { + memmove(p_cmd, p_cmd + (i - 1), cli_strlen(p_cmd) - (i - 2)); + *p_cmd++ = v; + continue; + } + } + + if (t == 'x') + { + uint8_t i; + uint8_t v = 0; + + for (i = 2; i < (2 + 2); i++) + { + t = *(p_cmd + i); + + if (t >= '0' && t <= '9') + { + v = (v << 4) | (t - '0'); + } + else if (t >= 'a' && t <= 'f') + { + v = (v << 4) | (t - 'a' + 10); + } + else if (t >= 'A' && t <= 'F') + { + v = (v << 4) | (t - 'A' + 10); + } + else + { + break; + } + } + + if (i > 2) + { + memmove(p_cmd, p_cmd + (i - 1), cli_strlen(p_cmd) - (i - 2)); + *p_cmd++ = v; + continue; + } + } + } + if (!quote && isspace((int)c)) + { + break; + } + + p_cmd += 1; + } + } while (*p_argc < max_argc); + + ASSERT(*p_argc <= NRF_CLI_ARGC_MAX); + pp_argv[*p_argc] = 0; + + return quote; +} + +static void cli_state_set(nrf_cli_t const * p_cli, nrf_cli_state_t state) +{ + p_cli->p_ctx->state = state; + + if (state == NRF_CLI_STATE_ACTIVE) + { + p_cli->p_ctx->cmd_buff[0] = '\0'; /* clear command buffer */ + p_cli->p_ctx->cmd_buff_pos = 0; + p_cli->p_ctx->cmd_buff_len = 0; + nrf_cli_fprintf(p_cli, NRF_CLI_INFO, "%s", p_cli->p_name); + } +} + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) +static inline void history_mode_exit(nrf_cli_t const * p_cli) +{ + p_cli->p_ctx->p_cmd_list_element = NULL; +} + +static void history_handle(nrf_cli_t const * p_cli, bool up) +{ + nrf_cli_memobj_header_t header = { + .p_prev = NULL, + .p_next = NULL, + .cmd_len = 0 + }; + nrf_cli_cmd_len_t current_cmd_len; + bool skip = false; + + if (!up) /* button down */ + { + if (p_cli->p_ctx->p_cmd_list_element == NULL) + { + return; + } + cursor_home_position_move(p_cli); + + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + + p_cli->p_ctx->p_cmd_list_element = header.p_next; + current_cmd_len = p_cli->p_ctx->cmd_buff_len; + + if (p_cli->p_ctx->p_cmd_list_element == NULL) + { + if (cli_strlen(p_cli->p_ctx->temp_buff) > 0) + { + strcpy(p_cli->p_ctx->cmd_buff, p_cli->p_ctx->temp_buff); + } + else + { + p_cli->p_ctx->cmd_buff[0] = '\0'; + } + header.cmd_len = cli_strlen(p_cli->p_ctx->cmd_buff); + skip = true; + } + } + else /* button up */ + { + if ((p_cli->p_ctx->p_cmd_list_element == p_cli->p_ctx->p_cmd_list_tail) || + (p_cli->p_ctx->p_cmd_list_head == NULL)) + { + /* Nothing to display. */ + return; + } + cursor_home_position_move(p_cli); + + if (p_cli->p_ctx->p_cmd_list_element == NULL) + { + current_cmd_len = cli_strlen(p_cli->p_ctx->cmd_buff); + + p_cli->p_ctx->p_cmd_list_element = p_cli->p_ctx->p_cmd_list_head; + /* Save the currently entered and not executed command. */ + if (current_cmd_len > 0) + { + strcpy(p_cli->p_ctx->temp_buff, p_cli->p_ctx->cmd_buff); + } + else + { + p_cli->p_ctx->temp_buff[0] = '\0'; + } + } + else + { + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + current_cmd_len = header.cmd_len; + p_cli->p_ctx->p_cmd_list_element = header.p_prev; + } + } + if (!skip) + { + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element, + p_cli->p_ctx->cmd_buff, + header.cmd_len + 1, /* +1 for '\0' */ + (uint32_t)NRF_CLI_HISTORY_HEADER_SIZE); + } + + p_cli->p_ctx->cmd_buff_pos = header.cmd_len; + p_cli->p_ctx->cmd_buff_len = header.cmd_len; + + if (current_cmd_len > header.cmd_len) + { + cli_clear_eos(p_cli); + } + + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%s", p_cli->p_ctx->cmd_buff); + if (cursor_in_empty_line(p_cli) || full_line_cmd(p_cli)) + { + cursor_next_line_move(p_cli); + } +} + +static void history_list_element_add(nrf_cli_t const * p_cli, nrf_memobj_t * p_memobj) +{ + ASSERT(p_memobj != NULL); + + nrf_cli_memobj_header_t header; + + if (p_cli->p_ctx->p_cmd_list_head == NULL) + { + p_cli->p_ctx->p_cmd_list_head = p_memobj; + p_cli->p_ctx->p_cmd_list_tail = p_memobj; + header.p_prev = NULL; + header.p_next = NULL; + header.cmd_len = p_cli->p_ctx->cmd_buff_len; + } + else + { + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_head, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + + header.p_next = p_memobj; + + nrf_memobj_write(p_cli->p_ctx->p_cmd_list_head, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + + header.p_next = NULL; + header.p_prev = p_cli->p_ctx->p_cmd_list_head; + header.cmd_len = p_cli->p_ctx->cmd_buff_len; + + p_cli->p_ctx->p_cmd_list_head = p_memobj; + } + + nrf_memobj_write(p_memobj, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + + nrf_memobj_write(p_memobj, + p_cli->p_ctx->cmd_buff, + p_cli->p_ctx->cmd_buff_len + 1, /* +1 for '\0' */ + (uint32_t)NRF_CLI_HISTORY_HEADER_SIZE); +} + +static void history_list_element_oldest_remove(nrf_cli_t const * p_cli) +{ + if (p_cli->p_ctx->p_cmd_list_tail == NULL) + { + return; // nothing to do + } + + nrf_cli_memobj_header_t header; + nrf_memobj_t * p_memobj = p_cli->p_ctx->p_cmd_list_tail; + + nrf_memobj_read(p_memobj, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + + p_cli->p_ctx->p_cmd_list_tail = header.p_next; + memset(&header, 0, sizeof(nrf_cli_memobj_header_t)); + nrf_memobj_write(p_memobj, &header, NRF_CLI_HISTORY_HEADER_SIZE, 0); + nrf_memobj_free(p_memobj); + + /* Checking if memory objects list is empty. */ + if (p_cli->p_ctx->p_cmd_list_tail == NULL) + { + p_cli->p_ctx->p_cmd_list_head = NULL; + return; + } + + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_tail, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + + header.p_prev = NULL; + nrf_memobj_write(p_cli->p_ctx->p_cmd_list_tail, &header, NRF_CLI_HISTORY_HEADER_SIZE, 0); +} + +static void history_list_free_memory(nrf_cli_t const * p_cli) +{ + while (p_cli->p_ctx->p_cmd_list_tail != NULL) + { + history_list_element_oldest_remove(p_cli); + } +} + +static void history_save(nrf_cli_t const * p_cli) +{ + nrf_cli_cmd_len_t cmd_new_len = cli_strlen(p_cli->p_ctx->cmd_buff); + + history_mode_exit(p_cli); + + if (cmd_new_len == 0) + { + return; + } + + /* Checking if newly entered command is not duplicated with previous one. */ + if (p_cli->p_ctx->p_cmd_list_head != NULL) + { + nrf_cli_memobj_header_t header; + + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_head, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + if (cmd_new_len == header.cmd_len) + { + nrf_memobj_read(p_cli->p_ctx->p_cmd_list_head, + p_cli->p_ctx->temp_buff, + header.cmd_len + 1, /* +1 for '\0' */ + (uint32_t)NRF_CLI_HISTORY_HEADER_SIZE); + + if (strcmp(p_cli->p_ctx->cmd_buff, p_cli->p_ctx->temp_buff) == 0) + { + /* Duplicated command, nothing to save. */ + p_cli->p_ctx->temp_buff[0] = '\0'; + return; + } + p_cli->p_ctx->temp_buff[0] = '\0'; + } + } + + for (size_t idx = 0; idx < NRF_CLI_HISTORY_ELEMENT_COUNT; idx++) + { + nrf_memobj_t * p_memobj; + + p_memobj = nrf_memobj_alloc(p_cli->p_cmd_hist_mempool, + ((size_t)NRF_CLI_HISTORY_HEADER_SIZE + cmd_new_len + 1)); + if (p_memobj != NULL) + { + history_list_element_add(p_cli, p_memobj); + return; + } + else + { + history_list_element_oldest_remove(p_cli); + } + } + return; +} +#endif // NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + +/* Function checks how many identical characters have two strings starting from the first character. */ +static nrf_cli_cmd_len_t str_similarity_check(char const * str_a, char const * str_b) +{ + nrf_cli_cmd_len_t cnt = 0; + + while (str_a[cnt] != '\0') + { + if (str_a[cnt] != str_b[cnt]) + { + return cnt; + } + + if (++cnt == 0) + { + return --cnt; /* too long strings */ + } + } + return cnt; +} + +static void completion_insert(nrf_cli_t const * p_cli, + char const * p_compl, + nrf_cli_cmd_len_t compl_len) +{ + ASSERT (p_compl); + + nrf_cli_cmd_len_t diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos; + + if ((p_cli->p_ctx->cmd_buff_len + compl_len > NRF_CLI_CMD_BUFF_SIZE - 1) || + (compl_len == 0)) + { + return; + } + + /* Make space for completion. */ + memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos + compl_len], + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos], + diff + 1); /* + 1 for '\0' */ + + /* Insert completion. */ + memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos], + p_compl, + compl_len); + + p_cli->p_ctx->cmd_buff_len = cli_strlen(p_cli->p_ctx->cmd_buff); + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s", + &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]); + p_cli->p_ctx->cmd_buff_pos += compl_len; + + if (cursor_in_empty_line(p_cli) || full_line_cmd(p_cli)) + { + cursor_next_line_move(p_cli); + } + + if (diff > 0) + { + cursor_position_synchronize(p_cli); + } +} + +static void option_print(nrf_cli_t const * p_cli, + char const * p_option, + nrf_cli_cmd_len_t longest_option) +{ + static char const * tab = " "; + + /* Function initialization has been requested. */ + if (p_option == NULL) + { + p_cli->p_ctx->vt100_ctx.printed_cmd = 0; + return; + } + longest_option += cli_strlen(tab); + + nrf_cli_cmd_len_t columns = + (p_cli->p_ctx->vt100_ctx.cons.terminal_wid - cli_strlen(tab)) / longest_option; + nrf_cli_cmd_len_t diff = longest_option - cli_strlen(p_option); + + if (p_cli->p_ctx->vt100_ctx.printed_cmd++ % columns == 0) + { + nrf_cli_fprintf(p_cli, NRF_CLI_OPTION, "\r\n%s%s", tab, p_option); + } + else + { + nrf_cli_fprintf(p_cli, NRF_CLI_OPTION, "%s", p_option); + } + cursor_right_move(p_cli, diff); +} + +static void cli_tab_handle(nrf_cli_t const * p_cli) +{ + size_t cmd_idx; + size_t cmd_last = 0; + size_t cmd_first = 0; + + size_t argc; + char * argv[NRF_CLI_ARGC_MAX + 1]; /* +1 reserved for NULL in function make_argv */ + + nrf_cli_cmd_len_t cmd_lvl = NRF_CLI_CMD_ROOT_LVL; + nrf_cli_cmd_len_t cmd_longest = 0; /* longest matching command */ + + /* Calculating the longest possible completion length. -1 for '\0'. */ + nrf_cli_cmd_len_t compl_len = (NRF_CLI_CMD_BUFF_SIZE - 1) - p_cli->p_ctx->cmd_buff_len; + + if (compl_len == 0) + { + return; + } + + /* Copy command from its beginning to cursor position. */ + memcpy(p_cli->p_ctx->temp_buff, + p_cli->p_ctx->cmd_buff, + p_cli->p_ctx->cmd_buff_pos); + + p_cli->p_ctx->temp_buff[p_cli->p_ctx->cmd_buff_pos] = '\0'; + + /* Check if the current cursor position points to the 'space' character. */ + bool space = isspace((int)p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos - 1]); + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + /* If the Tab key is pressed, "history mode" must be terminated because tab and history handlers + are sharing the same array: temp_buff. */ + history_mode_exit(p_cli); +#endif + + /* Create argument list. */ + (void)make_argv(&argc, + &argv[0], + p_cli->p_ctx->temp_buff, + NRF_CLI_ARGC_MAX); + + nrf_cli_cmd_len_t arg_len = cli_strlen(argv[cmd_lvl]); + + /* Variable 'static_entry' is needed to handle dynamic commands. */ + nrf_cli_static_entry_t static_entry; + + nrf_cli_cmd_entry_t const * p_cmd = NULL; + nrf_cli_static_entry_t const * p_st_cmd = NULL; + nrf_cli_static_entry_t const * p_st_cmd_last = NULL; + + do + { + if ((argc == 0) || + (cmd_lvl >= argc - 1 + space)) + { + if (space) + { + arg_len = 0; + } + else + { + arg_len = cli_strlen(argv[cmd_lvl]); + } + + cmd_idx = 0; + + while (1) + { + cmd_get(p_cmd, cmd_lvl, cmd_idx++, &p_st_cmd, &static_entry); + + if (p_st_cmd == NULL) + { + /* No more commands available. */ + break; + } + + if (strncmp(argv[cmd_lvl], p_st_cmd->p_syntax, arg_len) != 0) + { + if (p_st_cmd_last != NULL) + { + /* No more matches will be found as commands are sorted alphabetically. */ + break; + } + continue; + } + if (p_st_cmd_last == NULL) + { + cmd_first = cmd_idx - 1; + cmd_longest = cli_strlen(p_st_cmd->p_syntax); + if (compl_len > (cmd_longest - arg_len)) + { + compl_len = cmd_longest - arg_len; + } + } + else + { + nrf_cli_cmd_len_t len = cli_strlen(p_st_cmd->p_syntax); + if (len > cmd_longest) + { + cmd_longest = len; + } + + if (compl_len > 0) /* Checking if partial completion is possible */ + { + nrf_cli_static_entry_t last_entry; + cmd_get(p_cmd, cmd_lvl, cmd_last, &p_st_cmd_last, &last_entry); + + len = str_similarity_check(p_st_cmd->p_syntax + arg_len, + p_st_cmd_last->p_syntax + arg_len); + if (compl_len > len) + { + /* Determining the longest possible completion. */ + compl_len = len; + } + } + } + cmd_last = cmd_idx - 1; + p_st_cmd_last = p_st_cmd; + + if (cmd_idx == 0) /* Too many possibilities */ + { + nrf_cli_fprintf(p_cli, NRF_CLI_WARNING, NRF_CLI_MSG_TAB_OVERFLOWED); + break; + } + } + } + else + { + cmd_idx = 0; + + while (1) + { + cmd_get(p_cmd, cmd_lvl, cmd_idx++, &p_st_cmd, &static_entry); + + if (cmd_idx == 0) + { + /* No match found and commands counter overflowed. */ + nrf_cli_fprintf(p_cli, NRF_CLI_WARNING, NRF_CLI_MSG_TAB_OVERFLOWED); + return; + } + + if (p_st_cmd == NULL) /* No more commands available */ + { + return; + } + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) + /* Ignore wildcard character arguments if they are "standalone". Example: + 1. log enable info b* -> "b*" is treated as a command so no match found + 2. log enable info b* -> "b* " is ignored, will prompt all available + commands. */ + if (wildcard_character_exist(argv[cmd_lvl])) + { + break; + } +#endif + /* Fuction "strcmp" is used because an exact match is required. */ + if (strcmp(argv[cmd_lvl], p_st_cmd->p_syntax) == 0) + { + p_cmd = p_st_cmd->p_subcmd; + break; + } + } + } + + if ((p_cmd == NULL) || (p_st_cmd == NULL)) + { + break; + } + + } while (++cmd_lvl < argc + space); + + if (p_st_cmd_last == NULL) + { + /* No match found. */ + return; + } + + if (cmd_first == cmd_last) /* only one match found */ + { + if (p_cmd->is_dynamic) + { + /* In case of dynamic entry, function cmd_get shall be called again for matching + * command index (cmd_last). It is because static_entry is most likely appended by + * not valid data. + */ + cmd_get(p_cmd, cmd_lvl, cmd_last, &p_st_cmd_last, &static_entry); + } + if (cli_strlen(p_st_cmd_last->p_syntax) != arg_len) /* no exact match found */ + { + completion_insert(p_cli, p_st_cmd_last->p_syntax + arg_len, compl_len); + } + + /* Next character in the buffer is not 'space'. */ + if (!isspace((int)p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos])) + { + if (p_cli->p_ctx->internal.flag.insert_mode) + { + p_cli->p_ctx->internal.flag.insert_mode = 0; + char_insert(p_cli, ' '); + p_cli->p_ctx->internal.flag.insert_mode = 1; + } + else + { + char_insert(p_cli, ' '); + } + } + else + { + /* case: + | | -> cursor + cons_name $: valid_cmd valid_sub_cmd| |argument + */ + cursor_position_increment(p_cli); + /* result: + cons_name $: valid_cmd valid_sub_cmd |a|rgument + */ + } + return; + } + + /* Printing all matching commands (options). */ + option_print(p_cli, NRF_CLI_INIT_OPTION_PRINTER, cmd_longest); + for (cmd_idx = cmd_first; cmd_idx <= cmd_last; cmd_idx++) + { + cmd_get(p_cmd, cmd_lvl, cmd_idx, &p_st_cmd, &static_entry); + option_print(p_cli, p_st_cmd->p_syntax, cmd_longest); + } + + nrf_cli_fprintf(p_cli, NRF_CLI_INFO, "\r\n%s", p_cli->p_name); + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%s", p_cli->p_ctx->cmd_buff); + + cursor_position_synchronize(p_cli); + completion_insert(p_cli, p_st_cmd_last->p_syntax + arg_len, compl_len); +} + +#define NRF_CLI_ASCII_MAX_CHAR (127u) +static inline ret_code_t ascii_filter(char const data) +{ + return (uint8_t)data > NRF_CLI_ASCII_MAX_CHAR ? NRF_ERROR_INVALID_DATA : NRF_SUCCESS; +} + +static void cli_state_collect(nrf_cli_t const * p_cli) +{ + size_t count = 0; + char data; + + while (1) + { + cli_read(p_cli, &data, sizeof(data), &count); + if (count == 0) + { + return; + } + + if (ascii_filter(data) != NRF_SUCCESS) + { + continue; + } + +#if NRF_MODULE_ENABLED(NRF_PWR_MGMT) + nrf_pwr_mgmt_feed(); +#endif + + switch (p_cli->p_ctx->receive_state) + { + case NRF_CLI_RECEIVE_DEFAULT: + if (data == p_cli->newline_char) + { + if (p_cli->p_ctx->cmd_buff_len == 0) + { + cursor_next_line_move(p_cli); + } + else + { + /* Command execution */ + cli_execute(p_cli); + } + cli_state_set(p_cli, NRF_CLI_STATE_ACTIVE); + return; + } + switch (data) + { + case NRF_CLI_VT100_ASCII_ESC: /* ESCAPE */ + recieve_state_change(p_cli, NRF_CLI_RECEIVE_ESC); + break; + case '\0': + break; + case '\t': /* TAB */ + if (cli_flag_echo_is_set(p_cli)) + { + cli_tab_handle(p_cli); + } + break; + case NRF_CLI_VT100_ASCII_BSPACE: /* BACKSPACE */ + if (cli_flag_echo_is_set(p_cli)) + { + char_backspace(p_cli); + } + break; + case NRF_CLI_VT100_ASCII_DEL: /* DELETE */ + if (cli_flag_echo_is_set(p_cli)) + { + char_delete(p_cli); + } + break; + default: + if (isprint((int)data)) + { + if (cli_flag_echo_is_set(p_cli)) + { + char_insert(p_cli, data); + } + else + { + char_insert_echo_off(p_cli, data); + } + } + break; + } + break; + case NRF_CLI_RECEIVE_ESC: + if (data == '[') + { + recieve_state_change(p_cli, NRF_CLI_RECEIVE_ESC_SEQ); + } + else + { + recieve_state_change(p_cli, NRF_CLI_RECEIVE_DEFAULT); + } + break; + case NRF_CLI_RECEIVE_ESC_SEQ: + recieve_state_change(p_cli, NRF_CLI_RECEIVE_DEFAULT); + + if (!cli_flag_echo_is_set(p_cli)) + { + return; + } + + switch (data) + { +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + case 'A': /* UP arrow */ + history_handle(p_cli, true); + break; + case 'B': /* DOWN arrow */ + history_handle(p_cli, false); + break; +#endif + case 'C': /* RIGHT arrow */ + right_arrow_handle(p_cli); + break; + case 'D': /* LEFT arrow */ + left_arrow_handle(p_cli); + break; + case 'F': /* END Button */ + cursor_end_position_move(p_cli); + break; + case 'H': /* HOME Button */ + cursor_home_position_move(p_cli); + break; + case 'L': /* INSERT Button */ + p_cli->p_ctx->internal.flag.insert_mode ^= 1; + break; + default: + break; + } + break; + default: + recieve_state_change(p_cli, NRF_CLI_RECEIVE_DEFAULT); + break; + } + } +} + +/* Function remove white chars from beginning and end of command buffer. */ +static void cmd_trim(nrf_cli_t const * p_cli) +{ + nrf_cli_cmd_len_t i = 0; + + if (p_cli->p_ctx->cmd_buff[0] == '\0') /* no command in the buffer */ + { + return; + } + + /* Counting white characters starting from beginning of the command. */ + while (isspace((int)p_cli->p_ctx->cmd_buff[i++])) + { + if (i == 0) + { + p_cli->p_ctx->cmd_buff[0] = '\0'; + return; + } + } + + /* Removing counted white characters. */ + if (--i > 0) + { + memmove(p_cli->p_ctx->cmd_buff, + p_cli->p_ctx->cmd_buff + i, + (p_cli->p_ctx->cmd_buff_len + 1) - i); /* +1 for '\0' */ + p_cli->p_ctx->cmd_buff_len = p_cli->p_ctx->cmd_buff_len - i; + p_cli->p_ctx->cmd_buff_pos = p_cli->p_ctx->cmd_buff_len; + } + + /* Counting white characters starting from end of command. */ + char * p_end = &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_len - 1]; + i = 0; + while (isspace((int)*p_end)) + { + ++i; + --p_end; + } + + /* Removing counted white characters. */ + if (p_end != &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_len - 1]) + { + p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_len - i] = '\0'; + p_cli->p_ctx->cmd_buff_len = p_cli->p_ctx->cmd_buff_len - i; + p_cli->p_ctx->cmd_buff_pos = p_cli->p_ctx->cmd_buff_len; + } +} + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) +static void spaces_trim(char * p_char) +{ + nrf_cli_cmd_len_t shift = 0; + nrf_cli_cmd_len_t len = cli_strlen(p_char); + + if (p_char == NULL) + { + return; + } + + for (nrf_cli_cmd_len_t i = 0; i < len - 1; i++) + { + if (isspace((int)p_char[i])) + { + for (nrf_cli_cmd_len_t j = i + 1; j < len; j++) + { + if (isspace((int)p_char[j])) + { + shift++; + continue; + } + if (shift > 0) + { + memmove(&p_char[i + 1], &p_char[j], len - shift + 1); // +1 for EOS + len -= shift; + shift = 0; + } + break; + } + } + } +} + +/* Adds new command and one space just before pattern */ +static bool command_to_tmp_buffer_add(nrf_cli_t const * p_cli, + char const * p_new_cmd, + char const * p_pattern) +{ + nrf_cli_cmd_len_t cmd_len = cli_strlen(p_new_cmd); + nrf_cli_cmd_len_t shift; + char * p_cmd_source_addr; + + /* +1 for space */ + if (((size_t)p_cli->p_ctx->cmd_tmp_buff_len + cmd_len + 1) > NRF_CLI_CMD_BUFF_SIZE) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_WARNING, + "Command buffer is too short to expand all commands matching " + "wildcard pattern\r\n"); + return false; + } + + p_cmd_source_addr = strstr(p_cli->p_ctx->temp_buff, p_pattern); + + if (p_cmd_source_addr == NULL) + { + return false; + } + + shift = cli_strlen(p_cmd_source_addr); + + /* make place for new command: + 1 for space + 1 for EOS */ + memmove(p_cmd_source_addr + cmd_len + 1, p_cmd_source_addr, shift + 1); + memcpy(p_cmd_source_addr, p_new_cmd, cmd_len); + p_cmd_source_addr[cmd_len] = ' '; + + p_cli->p_ctx->cmd_tmp_buff_len += cmd_len + 1; // + 1 for space + + return true; +} + +/* removes pattern and following space */ +static void pattern_from_tmp_buffer_remove(nrf_cli_t const * p_cli, + char const * p_pattern) +{ + size_t shift; + char * p_pattern_addr = strstr(p_cli->p_ctx->temp_buff, p_pattern); + + nrf_cli_cmd_len_t pattern_len = cli_strlen(p_pattern); + + if (p_pattern_addr == NULL) + { + return; + } + + if (p_pattern_addr > p_cli->p_ctx->temp_buff) + { + if (*(p_pattern_addr - 1) == ' ') + { + pattern_len++; /* space needs to be removed as well */ + p_pattern_addr--; /* set pointer to space */ + } + } + + shift = cli_strlen(p_pattern_addr) - pattern_len + 1; /* +1 for EOS */ + p_cli->p_ctx->cmd_tmp_buff_len -= pattern_len; + + memmove(p_pattern_addr, p_pattern_addr + pattern_len, shift); +} + +/** + * @internal @brief Function for searching and adding commands matching to wildcard pattern. + * + * This function is internal to nrf_cli module and shall be not called directly. + * + * @param[in/out] p_cli Pointer to the CLI instance. + * @param[in] p_cmd Pointer to command which will be processed + * @param[in] cmd_lvl Command level in the command tree. + * @param[in] p_pattern Pointer to wildcard pattern. + * @param[out] p_counter Number of found and added commands. + * + * @retval WILDCARD_CMD_ADDED All matching commands added to the buffer. + * @retval WILDCARD_CMD_ADDED_MISSING_SPACE Not all matching commands added because + * NRF_CLI_CMD_BUFF_SIZE is too small. + * @retval WILDCARD_CMD_NO_MATCH_FOUND No matching command found. + */ +static wildcard_cmd_status_t commands_expand(nrf_cli_t const * p_cli, + nrf_cli_cmd_entry_t const * p_cmd, + size_t cmd_lvl, + char * p_pattern, + size_t * p_counter) +{ + size_t cmd_idx = 0; + size_t counter = 0; + bool success = false; + + nrf_cli_static_entry_t static_entry; + nrf_cli_static_entry_t const * p_static_entry = NULL; + wildcard_cmd_status_t ret_val = WILDCARD_CMD_NO_MATCH_FOUND; + + do + { + cmd_get(p_cmd, + cmd_lvl, + cmd_idx++, + &p_static_entry, + &static_entry); + + if (p_static_entry == NULL) + { + break; + } + + if (0 == fnmatch(p_pattern, p_static_entry->p_syntax, 0)) + { + success = command_to_tmp_buffer_add(p_cli, + p_static_entry->p_syntax, + p_pattern); + if (!success) + { + break; + } + counter++; + } + + } while(cmd_idx != 0); + + if (counter > 0) + { + *p_counter = counter; + pattern_from_tmp_buffer_remove(p_cli, p_pattern); + + if (success) + { + ret_val = WILDCARD_CMD_ADDED; + } + else + { + ret_val = WILDCARD_CMD_ADDED_MISSING_SPACE; + } + } + + return ret_val; +} +#endif // NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) + +/* Function is analyzing the command buffer to find matching commands. Next, it invokes the last recognized + * command which has a handler and passes the rest of command buffer as arguments. */ +static void cli_execute(nrf_cli_t const * p_cli) +{ + char quote; + size_t argc; + char * argv[NRF_CLI_ARGC_MAX + 1]; /* +1 reserved for NULL added by function make_argv */ + + size_t cmd_idx; /* currently analyzed command in cmd_level */ + size_t cmd_lvl = NRF_CLI_CMD_ROOT_LVL; /* currently analyzed command level */ + size_t cmd_handler_lvl = 0; /* last command level for which a handler has been found */ + size_t cmd_handler_idx = 0; /* last command index for which a handler has been found */ + + nrf_cli_cmd_entry_t const * p_cmd = NULL; + + cmd_trim(p_cli); + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + history_save(p_cli); +#endif + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) +/* Wildcard can be correctly handled under following conditions: + - wildcard command does not have a handler + - wildcard command is on the deepest commands level + - other commands on the same level as wildcard command shall also not have a handler + + Algorithm: + 1. Command buffer is copied to Temp buffer. + 2. Algorithm goes through Command buffer to find handlers and subcommands. + 3. If algorithm will find a wildcard character it switches to Temp buffer. + 4. In the Temp buffer command with found wildcard character is changed into matching command(s). + 5. Algorithm switch back to Command buffer and analyzes next command. + 6. When all arguments are analyzed from Command buffer, Temp buffer is copied to Command buffer. + 7. Last found handler is executed with all arguments in the Command buffer. +*/ + size_t commands_expanded = 0; + + memset(p_cli->p_ctx->temp_buff, 0, sizeof(p_cli->p_ctx->temp_buff)); + memcpy(p_cli->p_ctx->temp_buff, + p_cli->p_ctx->cmd_buff, + p_cli->p_ctx->cmd_buff_len); + + /* Function spaces_trim must be used instead of make_argv. At this point it is important to keep + temp_buff as a one string. It will allow to find wildcard commands easly with strstr + function. */ + spaces_trim(p_cli->p_ctx->temp_buff); + p_cli->p_ctx->cmd_tmp_buff_len = cli_strlen(p_cli->p_ctx->temp_buff) + 1; // +1 for EOS +#endif + + cursor_end_position_move(p_cli); + if (!cursor_in_empty_line(p_cli)) + { + cursor_next_line_move(p_cli); + } + + /* create argument list */ + quote = make_argv(&argc, + &argv[0], + p_cli->p_ctx->cmd_buff, + NRF_CLI_ARGC_MAX); + + if (!argc) + { + cursor_next_line_move(p_cli); + return; + } + + if (quote != 0) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "not terminated: %c\r\n", quote); + return; + } + + /* Searching for a matching root command. */ + for (cmd_idx = 0; cmd_idx <= CLI_DATA_SECTION_ITEM_COUNT; ++cmd_idx) + { + if (cmd_idx >= CLI_DATA_SECTION_ITEM_COUNT) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_ERROR, + "%s%s\r\n", + argv[0], + NRF_CLI_MSG_COMMAND_NOT_FOUND); + return; + } + + p_cmd = CLI_DATA_SECTION_ITEM_GET(cmd_idx); + if (strcmp(argv[cmd_lvl], p_cmd->u.p_static->p_syntax) != 0) + { + continue; + } + break; + } + + /* Root command shall be always static. */ + ASSERT(p_cmd->is_dynamic == false); + + /* Pointer to the deepest command level with a handler. */ + nrf_cli_cmd_entry_t const * p_cmd_low_level_entry = NULL; + + /* Memory reserved for dynamic commands. */ + nrf_cli_static_entry_t static_entry; + nrf_cli_static_entry_t const * p_static_entry = NULL; + + nrf_cli_cmd_handler handler_cmd_lvl_0 = p_cmd->u.p_static->handler; + if (handler_cmd_lvl_0 != NULL) + { + p_cli->p_ctx->p_current_stcmd = p_cmd->u.p_static; + } + + p_cmd = p_cmd->u.p_static->p_subcmd; + cmd_lvl++; + cmd_idx = 0; + + while (1) + { + if (cmd_lvl >= argc) + { + break; + } + + if (!strcmp(argv[cmd_lvl], "-h") || !strcmp(argv[cmd_lvl], "--help")) + { + /* Command called with help option so it makes no sense to search deeper commands. */ + cli_flag_help_set(p_cli); + break; + } + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) + /* Wildcard character is found */ + if (wildcard_character_exist(argv[cmd_lvl])) + { + size_t counter = 0; + wildcard_cmd_status_t status; + + /* Function will search commands tree for commands matching wildcard pattern stored in + argv[cmd_lvl]. If match is found wildcard pattern will be replaced by matching + commands in temp_buffer. If there is no space to add all matching commands function + will add as many as possible. Next it will continue to search for next wildcard + pattern and it will try to add matching commands. */ + status = commands_expand(p_cli, p_cmd, cmd_lvl, argv[cmd_lvl], &counter); + if (WILDCARD_CMD_NO_MATCH_FOUND == status) + { + break; + } + commands_expanded += counter; + cmd_lvl++; + continue; + } +#endif + + cmd_get(p_cmd, + cmd_lvl, + cmd_idx++, + &p_static_entry, + &static_entry); + + if ((cmd_idx == 0) || (p_static_entry == NULL)) + { + break; + } + + if (strcmp(argv[cmd_lvl], p_static_entry->p_syntax) == 0) + { + /* checking if command has a handler */ + if (p_static_entry->handler != NULL) + { +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) + if (commands_expanded > 0) + { + cursor_end_position_move(p_cli); + if (!cursor_in_empty_line(p_cli)) + { + cursor_next_line_move(p_cli); + } + /* An error occured, fnmatch argument cannot be followed by argument + * with a handler to avoid multiple function calls. */ + nrf_cli_fprintf(p_cli, + NRF_CLI_ERROR, + "Error: requested multiple function executions\r\n"); + cli_flag_help_clear(p_cli); + return; + } +#endif + /* Storing p_st_cmd->handler is not feasible for dynamic commands. Data will be + * invalid with the next loop iteration. */ + cmd_handler_lvl = cmd_lvl; + cmd_handler_idx = cmd_idx - 1; + p_cmd_low_level_entry = p_cmd; + } + cmd_lvl++; + cmd_idx = 0; + p_cmd = p_static_entry->p_subcmd; + } + } +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) + if (commands_expanded > 0) + { + /* Copy temp_buff to cmd_buff */ + memcpy(p_cli->p_ctx->cmd_buff, + p_cli->p_ctx->temp_buff, + p_cli->p_ctx->cmd_tmp_buff_len); + p_cli->p_ctx->cmd_buff_len = p_cli->p_ctx->cmd_tmp_buff_len; + + /* calling make_arg function again because cmd_buffer has additional commads */ + (void)make_argv(&argc, + &argv[0], + p_cli->p_ctx->cmd_buff, + NRF_CLI_ARGC_MAX); + } + #endif + + /* Executing the deepest found handler. */ + if (p_cmd_low_level_entry != NULL) + { + cmd_get(p_cmd_low_level_entry, + cmd_handler_lvl, + cmd_handler_idx, + &p_static_entry, + &static_entry); + + p_cli->p_ctx->p_current_stcmd = p_static_entry; + + p_cli->p_ctx->p_current_stcmd->handler(p_cli, + argc - cmd_handler_lvl, + &argv[cmd_handler_lvl]); + } + else if (handler_cmd_lvl_0 != NULL) + { + handler_cmd_lvl_0(p_cli, argc, &argv[0]); + } + else + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, NRF_CLI_MSG_SPECIFY_SUBCOMMAND); + } + cli_flag_help_clear(p_cli); +} + +/* Function required by qsort. */ +static int string_cmp(void const * pp_a, void const * pp_b) +{ + ASSERT(pp_a); + ASSERT(pp_b); + + char const ** pp_str_a = (char const **)pp_a; + char const ** pp_str_b = (char const **)pp_b; + + return strcmp(*pp_str_a, *pp_str_b); +} + +static void cli_transport_evt_handler(nrf_cli_transport_evt_t evt_type, void * p_context) +{ + nrf_cli_t * p_cli = (nrf_cli_t *)p_context; + ASSERT(p_cli); +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + task_events_set(p_cli->p_ctx->task_id, evt_type == NRF_CLI_TRANSPORT_EVT_RX_RDY ? + NRF_CLI_TRANSPORT_RX_RDY_TASK_EVT : NRF_CLI_TRANSPORT_TX_RDY_TASK_EVT); +#else + + if (evt_type == NRF_CLI_TRANSPORT_EVT_RX_RDY) + { + + } + else + { + /* wr done evt */ + p_cli->p_ctx->internal.flag.tx_rdy = 1; + } +#endif +} + +static ret_code_t nrf_cli_instance_init(nrf_cli_t const * p_cli, + void const * p_config, + bool use_colors) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + ASSERT((p_cli->newline_char == '\n') || (p_cli->newline_char == '\r')); + +#if defined(NRF_CLI_LOG_BACKEND) && NRF_CLI_LOG_BACKEND + p_cli->p_log_backend->p_cli = p_cli; +#endif + ret_code_t ret = p_cli->p_iface->p_api->init(p_cli->p_iface, + p_config, + cli_transport_evt_handler, + (void *)p_cli); + if (ret != NRF_SUCCESS) + { + return ret; + } + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + ASSERT(p_cli->p_cmd_hist_mempool); + ret = nrf_memobj_pool_init(p_cli->p_cmd_hist_mempool); + if (ret != NRF_SUCCESS) + { + return ret; + } + p_cli->p_ctx->p_cmd_list_head = NULL; + p_cli->p_ctx->p_cmd_list_tail = NULL; +#endif + + memset(p_cli->p_ctx, 0, sizeof(nrf_cli_ctx_t)); + p_cli->p_ctx->internal.flag.tx_rdy = 1; + +#if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) + p_cli->p_ctx->internal.flag.use_colors = use_colors; +#endif + p_cli->p_ctx->internal.flag.echo = NRF_CLI_ECHO_STATUS; + p_cli->p_ctx->state = NRF_CLI_STATE_INITIALIZED; + p_cli->p_ctx->vt100_ctx.cons.terminal_wid = NRF_CLI_DEFAULT_TERMINAL_WIDTH; + p_cli->p_ctx->vt100_ctx.cons.terminal_hei = NRF_CLI_DEFAULT_TERMINAL_HEIGHT; + + const char * * pp_sorted_cmds = (const char * *)CLI_SORTED_CMD_PTRS_START_ADDR_GET; + for (size_t i = 0; i < CLI_DATA_SECTION_ITEM_COUNT; i++) + { + const nrf_cli_cmd_entry_t * cmd; + cmd = CLI_DATA_SECTION_ITEM_GET(i); + + /* NULL syntax commands not allowed. */ + ASSERT(cmd); + ASSERT(cmd->u.p_static->p_syntax); + + pp_sorted_cmds[i] = cmd->u.p_static->p_syntax; + } + + if (CLI_DATA_SECTION_ITEM_COUNT > 0) + { + qsort(pp_sorted_cmds, + CLI_DATA_SECTION_ITEM_COUNT, + sizeof (char *), + string_cmp); + } + + return NRF_SUCCESS; +} + +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) +static ret_code_t nrf_cli_instance_uninit(nrf_cli_t const * p_cli); +void console_task(void * p_context) +{ + nrf_cli_t * p_cli = (nrf_cli_t *)p_context; + + ret_code_t ret = nrf_cli_start(p_cli); + APP_ERROR_CHECK(ret); + + while (1) + { + uint32_t evts = task_events_wait(NRF_CLI_TASK_EVTS); + + if (evts & NRF_CLI_KILL_TASK_EVT) + { + (void)nrf_cli_instance_uninit(p_cli); + task_exit(); + } + else + { + nrf_cli_process(p_cli); + } + } +} +#endif + +ret_code_t nrf_cli_init(nrf_cli_t const * p_cli, + void const * p_config, + bool use_colors, + bool log_backend, + nrf_log_severity_t init_lvl) +{ + ASSERT(p_cli); + + ret_code_t err_code = nrf_cli_instance_init(p_cli, p_config, use_colors); + +#if NRF_CLI_LOG_BACKEND && NRF_MODULE_ENABLED(NRF_LOG) + if ((err_code == NRF_SUCCESS) && log_backend && NRF_CLI_LOG_BACKEND) + { + int32_t id = nrf_log_backend_add(&p_cli->p_log_backend->backend, init_lvl); + if (id < 0) + { + return NRF_ERROR_NO_MEM; + } + + nrf_log_backend_enable(&p_cli->p_log_backend->backend); + } +#endif + return err_code; +} + +ret_code_t nrf_cli_task_create(nrf_cli_t const * p_cli) +{ +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + p_cli->p_ctx->task_id = task_create(console_task, p_cli->p_name,(void *)p_cli); + if (p_cli->p_ctx->task_id == TASK_ID_INVALID) + { + return NRF_ERROR_NO_MEM; + } + else + { + return NRF_SUCCESS; + } +#else + return NRF_ERROR_NOT_SUPPORTED; +#endif +} + +static ret_code_t nrf_cli_instance_uninit(nrf_cli_t const * p_cli) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + if (cli_flag_processing_is_set(p_cli)) + { + return NRF_ERROR_BUSY; + } + +#if NRF_CLI_LOG_BACKEND && NRF_MODULE_ENABLED(NRF_LOG) + if (p_cli->p_log_backend != NULL) + { + nrf_log_backend_disable(&p_cli->p_log_backend->backend); + nrf_log_backend_remove(&p_cli->p_log_backend->backend); + } +#endif + + ret_code_t ret = p_cli->p_iface->p_api->uninit(p_cli->p_iface); + if (ret != NRF_SUCCESS) + { + return ret; + } + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + history_list_free_memory(p_cli); +#endif + + memset(p_cli->p_ctx, 0, sizeof(nrf_cli_ctx_t)); + p_cli->p_ctx->state = NRF_CLI_STATE_UNINITIALIZED; + + return NRF_SUCCESS; +} + +ret_code_t nrf_cli_uninit(nrf_cli_t const * p_cli) +{ +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + if (cli_flag_processing_is_set(p_cli)) + { + return NRF_ERROR_BUSY; + } + task_events_set(p_cli->p_ctx->task_id, NRF_CLI_KILL_TASK_EVT); + return NRF_SUCCESS; +#else + return nrf_cli_instance_uninit(p_cli); +#endif +} + +ret_code_t nrf_cli_start(nrf_cli_t const * p_cli) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + if (p_cli->p_ctx->state != NRF_CLI_STATE_INITIALIZED) + { + return NRF_ERROR_INVALID_STATE; + } + +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + void * p_context = (void *)((uint32_t)task_id_get()); + p_cli->p_log_backend->p_context = p_context; +#endif + + ret_code_t err_code = p_cli->p_iface->p_api->enable(p_cli->p_iface, false); + +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + task_events_set(task_id_get(), NRF_CLI_TRANSPORT_RX_RDY_TASK_EVT); +#endif + + if (err_code == NRF_SUCCESS) + { +#if NRF_CLI_VT100_COLORS_ENABLED + vt100_color_set(p_cli, NRF_CLI_NORMAL); + vt100_bgcolor_set(p_cli, NRF_CLI_VT100_COLOR_BLACK); +#endif + nrf_fprintf(p_cli->p_fprintf_ctx, "\r\n\n"); + cli_state_set(p_cli, NRF_CLI_STATE_ACTIVE); + } + + return err_code; +} + +ret_code_t nrf_cli_stop(nrf_cli_t const * p_cli) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + if (p_cli->p_ctx->state == NRF_CLI_STATE_INITIALIZED || + p_cli->p_ctx->state == NRF_CLI_STATE_UNINITIALIZED) + { + return NRF_ERROR_INVALID_STATE; + } + + cli_state_set(p_cli, NRF_CLI_STATE_INITIALIZED); + return NRF_SUCCESS; +} + +void nrf_cli_process(nrf_cli_t const * p_cli) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + nrf_cli_internal_t internal; + internal.value = 0; + internal.flag.processing = 1; + (void)nrf_atomic_u32_or((nrf_atomic_u32_t *)&p_cli->p_ctx->internal.value, + internal.value); + + switch (p_cli->p_ctx->state) + { + case NRF_CLI_STATE_UNINITIALIZED: + case NRF_CLI_STATE_INITIALIZED: + /* Console initialized but not started. */ + break; + case NRF_CLI_STATE_ACTIVE: + { + cli_state_collect(p_cli); + bool log_processed = cli_log_entry_process(p_cli, false); + if (log_processed) + { + nrf_cli_fprintf(p_cli, NRF_CLI_INFO, "%s", p_cli->p_name); + if (cli_flag_echo_is_set(p_cli)) + { + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%s", p_cli->p_ctx->cmd_buff); + cursor_position_synchronize(p_cli); + } + } + break; + } + default: + break; + } + transport_buffer_flush(p_cli); + internal.value = (uint32_t)0xFFFFFFFF; + internal.flag.processing = 0; + (void)nrf_atomic_u32_and((nrf_atomic_u32_t *)&p_cli->p_ctx->internal.value, + internal.value); +} + +/* Function shall be only used by the nrf_fprintf module. */ +void nrf_cli_print_stream(void const * p_user_ctx, char const * p_data, size_t data_len) +{ + cli_write((nrf_cli_t const *)p_user_ctx, + p_data, + data_len, + NULL); +} + +void nrf_cli_fprintf(nrf_cli_t const * p_cli, + nrf_cli_vt100_color_t color, + char const * p_fmt, + ...) +{ + ASSERT(p_fmt); + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + va_list args = {0}; + va_start(args, p_fmt); + +#if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) + if ((p_cli->p_ctx->internal.flag.use_colors) && + (color != p_cli->p_ctx->vt100_ctx.col.col)) + { + nrf_cli_vt100_colors_t col; + + vt100_colors_store(p_cli, &col); + vt100_color_set(p_cli, color); + + nrf_fprintf_fmt(p_cli->p_fprintf_ctx, p_fmt, &args); + + vt100_colors_restore(p_cli, &col); + } + else +#endif // NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) + { + nrf_fprintf_fmt(p_cli->p_fprintf_ctx, p_fmt, &args); + } + + va_end(args); +} + +/* Function prints a string on terminal screen with requested margin. + * It takes care to not divide words. + * p_cli Pointer to CLI instance. + * p_str Pointer to string to be printed. + * terminal_offset Requested left margin. + * offset_first_line Add margin to the first printed line. + */ +static void format_offset_string_print(nrf_cli_t const * p_cli, + char const * p_str, + size_t terminal_offset, + bool offset_first_line) +{ + if (p_str == NULL) + { + return; + } + + if (offset_first_line) + { + cursor_right_move(p_cli, terminal_offset); + } + + size_t length; + size_t offset = 0; + + /* Skipping whitespace. */ + while (isspace((int)*(p_str + offset))) + { + ++offset; + } + + while (1) + { + size_t idx = 0; + length = cli_strlen(p_str) - offset; + + if (length <= p_cli->p_ctx->vt100_ctx.cons.terminal_wid - terminal_offset) + { + for (idx = 0; idx < length; idx++) + { + if (*(p_str + offset + idx) == '\n') + { + transport_buffer_flush(p_cli); + cli_write(p_cli, p_str + offset, idx, NULL); + offset += idx + 1; + cursor_next_line_move(p_cli); + cursor_right_move(p_cli, terminal_offset); + break; + } + } + /* String will fit in one line. */ + nrf_fprintf(p_cli->p_fprintf_ctx, p_str + offset); + break; + } + else + { + /* String is longer than terminal line so text needs to divide in the way + to not divide words. */ + length = p_cli->p_ctx->vt100_ctx.cons.terminal_wid - terminal_offset; + + while (1) + { + /* Determining line break. */ + if (isspace((int)(*(p_str + offset + idx)))) + { + length = idx; + if (*(p_str + offset + idx) == '\n') + { + break; + } + } + if ((idx + terminal_offset) >= p_cli->p_ctx->vt100_ctx.cons.terminal_wid) + { + /* End of line reached. */ + break; + } + ++idx; + } + + /* Writing one line, fprintf IO buffer must be flushed before calling cli_write. */ + transport_buffer_flush(p_cli); + cli_write(p_cli, p_str + offset, length, NULL); + offset += length; + + /* Calculating text offset to ensure that next line will not begin with a space. */ + while (isspace((int)(*(p_str + offset)))) + { + ++offset; + } + cursor_next_line_move(p_cli); + cursor_right_move(p_cli, terminal_offset); + } + } + cursor_next_line_move(p_cli); +} + +void nrf_cli_help_print(nrf_cli_t const * p_cli, + nrf_cli_getopt_option_t const * p_opt, + size_t opt_len) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + static uint8_t const tab_len = 2; + static char const opt_sep[] =", "; /* options separator */ + static char const help[] = "-h, --help"; + static char const cmd_sep[] = " - "; /* command separator */ + uint16_t field_width = 0; + uint16_t longest_string = cli_strlen(help) - cli_strlen(opt_sep); + + /* Printing help string for command. */ + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "%s%s", + p_cli->p_ctx->p_current_stcmd->p_syntax, + cmd_sep); + + field_width = cli_strlen(p_cli->p_ctx->p_current_stcmd->p_syntax) + cli_strlen(cmd_sep); + format_offset_string_print(p_cli, p_cli->p_ctx->p_current_stcmd->p_help, field_width, false); + + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Options:\r\n"); + + /* Looking for the longest option string. */ + if ((opt_len > 0) && (p_opt != NULL)) + { + for (size_t i = 0; i < opt_len; ++i) + { + if (cli_strlen(p_opt[i].p_optname_short) + cli_strlen(p_opt[i].p_optname) + > longest_string) + { + longest_string = cli_strlen(p_opt[i].p_optname_short) + + cli_strlen(p_opt[i].p_optname); + } + } + } + longest_string += cli_strlen(opt_sep) + tab_len; + + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + " %-*s:", + longest_string, + help); + + /* Print help string for options (only -h and --help). */ + field_width = longest_string + tab_len + 1; /* tab_len + 1 == " " and ':' from: " %-*s:" */ + format_offset_string_print(p_cli, "Show command help.", field_width, false); + + /* Formating and printing all available options (except -h, --help). */ + if (p_opt != NULL) + { + for (size_t i = 0; i < opt_len; ++i) + { + if ((p_opt[i].p_optname_short != NULL) && (p_opt[i].p_optname != NULL)) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + " %s%s%s", + p_opt[i].p_optname_short, + opt_sep, + p_opt[i].p_optname); + field_width = longest_string + tab_len; + cursor_right_move(p_cli, + field_width - ( cli_strlen(p_opt[i].p_optname_short) + + cli_strlen(p_opt[i].p_optname) + + tab_len + + cli_strlen(opt_sep))); + cli_putc(p_cli, ':'); + ++field_width; /* incrementing because char ':' was already printed above */ + } + else if (p_opt[i].p_optname_short != NULL) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + " %-*s:", + longest_string, + p_opt[i].p_optname_short); + /* tab_len + 1 == " " and ':' from: " %-*s:" */ + field_width = longest_string + tab_len + 1; + } + else if (p_opt[i].p_optname != NULL) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + " %-*s:", + longest_string, + p_opt[i].p_optname); + /* tab_len + 1 == " " and ':' from: " %-*s:" */ + field_width = longest_string + tab_len + 1; + } + else + { + /* Do nothing. */ + } + + if (p_opt[i].p_optname_help != NULL) + { + format_offset_string_print(p_cli, p_opt[i].p_optname_help, field_width, false); + } + else + { + cursor_next_line_move(p_cli); + } + } + } + + /* Checking if there are any subcommands avilable. */ + if (p_cli->p_ctx->p_current_stcmd->p_subcmd == NULL) + { + return; + } + + /* Printing formatted help of one level deeper subcommands. */ + nrf_cli_static_entry_t static_entry; + nrf_cli_cmd_entry_t const * p_cmd = p_cli->p_ctx->p_current_stcmd->p_subcmd; + nrf_cli_static_entry_t const * p_st_cmd = NULL; + + field_width = 0; + longest_string = 0; + + size_t cmd_idx = 0; + + /* Searching for the longest subcommand to print. */ + while (1) + { + cmd_get(p_cmd, !NRF_CLI_CMD_ROOT_LVL, cmd_idx++, &p_st_cmd, &static_entry); + + if (p_st_cmd == NULL) + { + break; + } + if (cli_strlen(p_st_cmd->p_syntax) > longest_string) + { + longest_string = cli_strlen(p_st_cmd->p_syntax); + } + } + + /* Checking if there are dynamic subcommands. */ + if (cmd_idx == 1) + { + /* No dynamic subcommands available. */ + return; + } + + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Subcommands:\r\n"); + + /* Printing subcommands and help string (if exists). */ + cmd_idx = 0; + while (1) + { + cmd_get(p_cmd, !NRF_CLI_CMD_ROOT_LVL, cmd_idx++, &p_st_cmd, &static_entry); + + if (p_st_cmd == NULL) + { + break; + } + + field_width = longest_string + tab_len; + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL," %-*s:", field_width, p_st_cmd->p_syntax); + field_width += tab_len + 1; /* tab_len + 1 == " " and ':' from: " %-*s:" */ + + if (p_st_cmd->p_help != NULL) + { + format_offset_string_print(p_cli, p_st_cmd->p_help, field_width, false); + } + else + { + cursor_next_line_move(p_cli); + } + } +} + +#if NRF_CLI_LOG_BACKEND && NRF_MODULE_ENABLED(NRF_LOG) + +#define NRF_CLI_LOG_MSG_OVERFLOW_MSK ((uint32_t)7) +static bool cli_log_entry_process(nrf_cli_t const * p_cli, bool skip) +{ + nrf_log_entry_t entry; + +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) + bool print_msg = false; +#endif + + if (nrf_queue_pop(p_cli->p_log_backend->p_queue, &entry) != NRF_SUCCESS) + { + return false; + } + + if (skip) + { + nrf_memobj_put(entry); +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) + ++p_cli->p_ctx->statistics.log_lost_cnt; + if ((p_cli->p_ctx->statistics.log_lost_cnt & NRF_CLI_LOG_MSG_OVERFLOW_MSK) == 1) + { + /* Set flag to print a message after clearing the currently entered command. */ + print_msg = true; + } + else +#endif + { + return true; + } + } + { + /* Erasing the currently displayed command and console name. */ + nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli); + + if (p_cons->cur_y > NRF_CLI_INITIAL_CURS_POS) + { + cursor_up_move(p_cli, p_cons->cur_y - NRF_CLI_INITIAL_CURS_POS); + } + + if (p_cons->cur_x > NRF_CLI_INITIAL_CURS_POS) + { + cursor_left_move(p_cli, p_cons->cur_x - NRF_CLI_INITIAL_CURS_POS); + } + cli_clear_eos(p_cli); + } + +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) + if (print_msg) + { + /* Print the requested string and exit function. */ + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Lost logs - increase log backend queue size.\r\n"); + + return true; + } +#endif + + /* Printing logs from the queue. */ + do + { + nrf_log_header_t header; + uint32_t memobj_offset = 0; + nrf_log_str_formatter_entry_params_t params; + + nrf_memobj_read(entry, &header, HEADER_SIZE*sizeof(uint32_t), memobj_offset); + memobj_offset = HEADER_SIZE * sizeof(uint32_t); + + params.timestamp = header.timestamp; + params.module_id = header.module_id; + params.dropped = header.dropped; + params.use_colors = NRF_LOG_USES_COLORS; /* Color will be provided by the console application. */ + + if (header.base.generic.type == HEADER_TYPE_STD) + { + char const * p_log_str = (char const *)((uint32_t)header.base.std.addr); + params.severity = (nrf_log_severity_t)header.base.std.severity; + uint32_t nargs = header.base.std.nargs; + uint32_t args[6]; + + nrf_memobj_read(entry, args, nargs*sizeof(uint32_t), memobj_offset); + nrf_log_std_entry_process(p_log_str, + args, + nargs, + ¶ms, + p_cli->p_fprintf_ctx); + } + else if (header.base.generic.type == HEADER_TYPE_HEXDUMP) + { + uint32_t data_len; + uint8_t data_buf[8]; + uint32_t chunk_len; + + data_len = header.base.hexdump.len; + params.severity = (nrf_log_severity_t)header.base.hexdump.severity; + + do + { + chunk_len = sizeof(data_buf) > data_len ? data_len : sizeof(data_buf); + nrf_memobj_read(entry, data_buf, chunk_len, memobj_offset); + memobj_offset += chunk_len; + data_len -= chunk_len; + nrf_log_hexdump_entry_process(data_buf, chunk_len, ¶ms, p_cli->p_fprintf_ctx); + } while (data_len > 0); + } + + nrf_memobj_put(entry); + } while (nrf_queue_pop(p_cli->p_log_backend->p_queue, &entry) == NRF_SUCCESS); + return true; +} + +static void nrf_log_backend_cli_put(nrf_log_backend_t const * p_backend, nrf_log_entry_t * p_msg) +{ + nrf_cli_log_backend_t * p_backend_cli = CONTAINER_OF(p_backend, nrf_cli_log_backend_t, backend); + nrf_cli_t const * p_cli = p_backend_cli->p_cli; + + //If panic mode cannot be handled, stop handling new requests. + if (p_cli->p_ctx->state != NRF_CLI_STATE_PANIC_MODE_INACTIVE) + { + bool panic_mode = (p_cli->p_ctx->state == NRF_CLI_STATE_PANIC_MODE_ACTIVE); + //If there is no place for a new log entry, remove the oldest one. + ret_code_t err_code = nrf_queue_push(p_backend_cli->p_queue, &p_msg); + while (err_code != NRF_SUCCESS) + { + (void)cli_log_entry_process(p_cli, panic_mode ? false : true); + + err_code = nrf_queue_push(p_backend_cli->p_queue, &p_msg); + } + nrf_memobj_get(p_msg); + + if (panic_mode) + { + (void)cli_log_entry_process(p_cli, false); + } +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + else + { + task_events_set((task_id_t)((uint32_t)p_backend_cli->p_context & 0x000000FF), + NRF_CLI_LOG_PENDING_TASK_EVT); + } +#endif + } +} + +static void nrf_log_backend_cli_flush(nrf_log_backend_t const * p_backend) +{ + nrf_cli_log_backend_t * p_backend_cli; + nrf_cli_t const * p_cli; + nrf_log_entry_t * p_msg; + + p_backend_cli = CONTAINER_OF(p_backend, nrf_cli_log_backend_t, backend); + p_cli = p_backend_cli->p_cli; + + if (nrf_queue_pop(p_backend_cli->p_queue, &p_msg) == NRF_SUCCESS) + { + (void)cli_log_entry_process(p_cli, false); + } + UNUSED_PARAMETER(p_backend); +} + +static void nrf_log_backend_cli_panic_set(nrf_log_backend_t const * p_backend) +{ + nrf_cli_log_backend_t * p_backend_cli = CONTAINER_OF(p_backend, nrf_cli_log_backend_t, backend); + nrf_cli_t const * p_cli = p_backend_cli->p_cli; + + if (p_cli->p_iface->p_api->enable(p_cli->p_iface, true) == NRF_SUCCESS) + { + p_cli->p_ctx->state = NRF_CLI_STATE_PANIC_MODE_ACTIVE; + } + else + { + p_cli->p_ctx->state = NRF_CLI_STATE_PANIC_MODE_INACTIVE; + } +} + +const nrf_log_backend_api_t nrf_log_backend_cli_api = { + .put = nrf_log_backend_cli_put, + .flush = nrf_log_backend_cli_flush, + .panic_set = nrf_log_backend_cli_panic_set, +}; +#else +static bool cli_log_entry_process(nrf_cli_t const * p_cli, bool skip) +{ + UNUSED_PARAMETER(p_cli); + UNUSED_PARAMETER(skip); + return false; +} +#endif // NRF_CLI_LOG_BACKEND + +/* ============ built-in commands ============ */ +#if NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS) + +static bool nrf_cli_build_in_cmd_common_executed(nrf_cli_t const * p_cli, + bool arg_cnt_nok, + nrf_cli_getopt_option_t const * p_opt, + size_t opt_len) +{ + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, p_opt, opt_len); + return true; + } + + if (arg_cnt_nok) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_ERROR, + "%s: wrong parameter count\r\n", + p_cli->p_ctx->p_current_stcmd->p_syntax); + return true; + } + + return false; +} + +static void nrf_cli_cmd_clear(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + UNUSED_PARAMETER(argv); + + if ((argc == 2) && (nrf_cli_help_requested(p_cli))) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CURSORHOME); + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CLEARSCREEN); +} + +static void nrf_cli_cmd_cli(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + UNUSED_PARAMETER(argv); + + if ((argc == 1) || ((argc == 2) && nrf_cli_help_requested(p_cli)) ) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, NRF_CLI_MSG_SPECIFY_SUBCOMMAND); +} + +#if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) +static void nrf_cli_cmd_colors_off(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + p_cli->p_ctx->internal.flag.use_colors = 0; +} + +static void nrf_cli_cmd_colors_on(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + p_cli->p_ctx->internal.flag.use_colors = 1; +} + +static void nrf_cli_cmd_colors(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + if (argc == 1) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 2), NULL, 0)) + { + return; + } + + nrf_cli_fprintf(p_cli, + NRF_CLI_ERROR, + "%s:%s%s\r\n", + argv[0], + NRF_CLI_MSG_UNKNOWN_PARAMETER, + argv[1]); +} +#endif // NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) + +static void nrf_cli_cmd_echo(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc > 2), NULL, 0)) + { + return; + } + + if (argc == 2) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_ERROR, + "%s:%s%s\r\n", + argv[0], + NRF_CLI_MSG_UNKNOWN_PARAMETER, + argv[1]); + return; + } + + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "Echo status: %s\r\n", + cli_flag_echo_is_set(p_cli) ? "on" : "off"); +} + +static void nrf_cli_cmd_echo_off(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + + cli_flag_echo_clear(p_cli); +} + +static void nrf_cli_cmd_echo_on(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + + cli_flag_echo_set(p_cli); +} + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) +static void nrf_cli_cmd_history(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + + size_t i = 0; + nrf_memobj_t const * p_cmd_list = p_cli->p_ctx->p_cmd_list_tail; + nrf_cli_memobj_header_t header; + + while (1) + { + if ((p_cmd_list == NULL) || (i >= NRF_CLI_HISTORY_ELEMENT_COUNT)) + { + break; + } + nrf_memobj_read((nrf_memobj_t * )p_cmd_list, + &header, + NRF_CLI_HISTORY_HEADER_SIZE, + (uint32_t)0); + nrf_memobj_read((nrf_memobj_t * )p_cmd_list, + p_cli->p_ctx->temp_buff, + header.cmd_len + 1, + (uint32_t)NRF_CLI_HISTORY_HEADER_SIZE); + p_cmd_list = header.p_next; + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "[%3d] %s\r\n", i++, p_cli->p_ctx->temp_buff); + } + p_cli->p_ctx->temp_buff[0] = '\0'; +} +#endif // NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) +void nrf_cli_cmd_cli_stats(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (argc == 1) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + if (argc == 2) + { + nrf_cli_fprintf(p_cli, + NRF_CLI_ERROR, + "%s:%s%s\r\n", + argv[0], + NRF_CLI_MSG_UNKNOWN_PARAMETER, + argv[1]); + return; + } + + UNUSED_RETURN_VALUE(nrf_cli_build_in_cmd_common_executed(p_cli, (argc > 2), NULL, 0)); +} + +void nrf_cli_cmd_cli_stats_show(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + + uint8_t max_util = nrf_queue_max_utilization_get(p_cli->p_log_backend->p_queue); + uint8_t utilization = (uint8_t)(max_util * 100ul / p_cli->p_log_backend->p_queue->size); + + nrf_cli_fprintf(p_cli, + NRF_CLI_NORMAL, + "Lost logs: %u\r\n" + "Max log queue utilization: %u%% [%u/%u]\r\n", + p_cli->p_ctx->statistics.log_lost_cnt, + utilization, + max_util, + p_cli->p_log_backend->p_queue->size); +} + +void nrf_cli_cmd_cli_stats_reset(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + + p_cli->p_ctx->statistics.log_lost_cnt = 0; + nrf_queue_max_utilization_reset(p_cli->p_log_backend->p_queue); +} +#endif // NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) + +static void nrf_cli_cmd_resize_default(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0)) + { + return; + } + + NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_SETCOL_80); + p_cli->p_ctx->vt100_ctx.cons.terminal_wid = NRF_CLI_DEFAULT_TERMINAL_WIDTH; + p_cli->p_ctx->vt100_ctx.cons.terminal_hei = NRF_CLI_DEFAULT_TERMINAL_HEIGHT; +} + +static void nrf_cli_cmd_resize(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + ASSERT(p_cli); + ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name); + + if (argc == 1) + { + if (terminal_size_get(p_cli, + &p_cli->p_ctx->vt100_ctx.cons.terminal_wid, + &p_cli->p_ctx->vt100_ctx.cons.terminal_hei) != NRF_SUCCESS) + { + p_cli->p_ctx->vt100_ctx.cons.terminal_wid = NRF_CLI_DEFAULT_TERMINAL_WIDTH; + p_cli->p_ctx->vt100_ctx.cons.terminal_hei = NRF_CLI_DEFAULT_TERMINAL_HEIGHT; + nrf_cli_fprintf(p_cli, + NRF_CLI_WARNING, + "No response from the terminal, assumed 80x24 screen size\r\n"); + } + return; + } + + if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc > 2), NULL, 0)) + { + return; + } + + nrf_cli_fprintf(p_cli, + NRF_CLI_ERROR, + "%s:%s%s\r\n", + argv[0], + NRF_CLI_MSG_UNKNOWN_PARAMETER, + argv[1]); +} + +#if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_colors) +{ + NRF_CLI_CMD(off, NULL, NRF_CLI_HELP_COLORS_OFF, nrf_cli_cmd_colors_off), + NRF_CLI_CMD(on, NULL, NRF_CLI_HELP_COLORS_ON, nrf_cli_cmd_colors_on), + NRF_CLI_SUBCMD_SET_END +}; +#endif + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_echo) +{ + NRF_CLI_CMD(off, NULL, NRF_CLI_HELP_ECHO_OFF, nrf_cli_cmd_echo_off), + NRF_CLI_CMD(on, NULL, NRF_CLI_HELP_ECHO_ON, nrf_cli_cmd_echo_on), + NRF_CLI_SUBCMD_SET_END +}; + +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_cli_stats) +{ + NRF_CLI_CMD(reset, NULL, NRF_CLI_HELP_STATISTICS_RESET, nrf_cli_cmd_cli_stats_reset), + NRF_CLI_CMD(show, NULL, NRF_CLI_HELP_STATISTICS_SHOW, nrf_cli_cmd_cli_stats_show), + NRF_CLI_SUBCMD_SET_END +}; +#endif // NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_cli) +{ +#if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS) + NRF_CLI_CMD(colors, &m_sub_colors, NRF_CLI_HELP_COLORS, nrf_cli_cmd_colors), +#endif + NRF_CLI_CMD(echo, &m_sub_echo, NRF_CLI_HELP_ECHO, nrf_cli_cmd_echo), +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) + NRF_CLI_CMD(stats, &m_sub_cli_stats, NRF_CLI_HELP_STATISTICS, nrf_cli_cmd_cli_stats), +#endif + NRF_CLI_SUBCMD_SET_END +}; + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_resize) +{ + NRF_CLI_CMD(default, NULL, NRF_CLI_HELP_RESIZE_DEFAULT, nrf_cli_cmd_resize_default), + NRF_CLI_SUBCMD_SET_END +}; + +NRF_CLI_CMD_REGISTER(clear, NULL, NRF_CLI_HELP_CLEAR, nrf_cli_cmd_clear); +NRF_CLI_CMD_REGISTER(cli, &m_sub_cli, NRF_CLI_HELP_CLI, nrf_cli_cmd_cli); +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) +NRF_CLI_CMD_REGISTER(history, NULL, NRF_CLI_HELP_HISTORY, nrf_cli_cmd_history); +#endif +NRF_CLI_CMD_REGISTER(resize, &m_sub_resize, NRF_CLI_HELP_RESIZE, nrf_cli_cmd_resize); + +#endif // NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS) + +#endif // NRF_MODULE_ENABLED(NRF_CLI) + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.h new file mode 100644 index 0000000..480b453 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli.h @@ -0,0 +1,636 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_CLI_H__ +#define NRF_CLI_H__ + +#include "sdk_common.h" +#include "nrf_cli_types.h" +#include "nrf_section.h" +#include "nrf_log_backend_interface.h" +#include "nrf_queue.h" +#include "nrf_log_ctrl.h" +#include "app_util_platform.h" + +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) +#include "task_manager.h" +#endif + +#include "nrf_fprintf.h" +#include "nrf_fprintf_format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NRF_CLI_RX_BUFF_SIZE 16 + +/* CLI reserves top task manager flags, bits 0...18 are available for application. */ +#define NRF_CLI_TRANSPORT_TX_RDY_TASK_EVT (1UL << 19) +#define NRF_CLI_TRANSPORT_RX_RDY_TASK_EVT (1UL << 20) +#define NRF_CLI_LOG_PENDING_TASK_EVT (1UL << 21) +#define NRF_CLI_CMD_EXECUTE_EVT (1UL << 22) +#define NRF_CLI_KILL_TASK_EVT (1UL << 23) + +#define NRF_CLI_TASK_EVTS (NRF_CLI_TRANSPORT_TX_RDY_TASK_EVT | \ + NRF_CLI_TRANSPORT_RX_RDY_TASK_EVT | \ + NRF_CLI_LOG_PENDING_TASK_EVT | \ + NRF_CLI_CMD_EXECUTE_EVT | \ + NRF_CLI_KILL_TASK_EVT) +/** + * @defgroup nrf_cli Command Line Interface + * @ingroup app_common + * + * @brief Module for unified command line handling. + * + * @{ + */ + +/** + * @brief Aliases to: @ref nrf_cli, @ref nrf_cli_cmd_entry, and @ref nrf_cli_static_entry. + * Must be created here to satisfy module declaration order dependencies. + */ +typedef struct nrf_cli nrf_cli_t; +typedef struct nrf_cli_cmd_entry nrf_cli_cmd_entry_t; +typedef struct nrf_cli_static_entry nrf_cli_static_entry_t; + +/** + * @brief CLI dynamic command descriptor. + * + * @details Function shall fill the received @ref nrf_cli_static_entry structure with requested (idx) + * dynamic subcommand data. If there is more than one dynamic subcommand available, + * the function shall ensure that the returned commands: p_static->p_syntax are sorted in + * alphabetical order. If idx exceeds the available dynamic subcommands, the function must write + * to p_static->p_syntax NULL value. This will indicate to the CLI module that + * there are no more dynamic commands to read. + */ +typedef void (*nrf_cli_dynamic_get)(size_t idx, nrf_cli_static_entry_t * p_static); + +/** + * @brief CLI command descriptor. + */ +struct nrf_cli_cmd_entry +{ + bool is_dynamic; + union + { + nrf_cli_dynamic_get p_dynamic_get; //!< Pointer to function returning dynamic commands. + nrf_cli_static_entry_t const * p_static; //!< Pointer to array of static commands. + } u; +}; + +/** + * @brief CLI command handler prototype. + */ +typedef void (*nrf_cli_cmd_handler)(nrf_cli_t const * p_cli, size_t argc, char **argv); + +/** + * @brief CLI static command descriptor. + */ +struct nrf_cli_static_entry +{ + char const * p_syntax; //!< Command syntax strings. + char const * p_help; //!< Command help string. + + nrf_cli_cmd_entry_t const * p_subcmd; //!< Pointer to subcommand. + + nrf_cli_cmd_handler handler; //!< Command handler. +}; + +/** + * @brief Macro for defining and adding a root command (level 0). + * + * @note Each root command shall have unique syntax. + * + * @param[in] p_syntax Command syntax (for example: history). + * @param[in] p_subcmd Pointer to a subcommands array. + * @param[in] p_help Pointer to a command help string. + * @param[in] p_handler Pointer to a function handler. + */ +#define NRF_CLI_CMD_REGISTER(p_syntax, p_subcmd, p_help, p_handler) \ + nrf_cli_static_entry_t const CONCAT_3(nrf_cli_, p_syntax, _raw) = \ + NRF_CLI_CMD(p_syntax, p_subcmd, p_help, p_handler); \ + NRF_SECTION_ITEM_REGISTER(cli_command, \ + nrf_cli_cmd_entry_t const CONCAT_3(nrf_cli_, p_syntax, _const)) = { \ + .is_dynamic = false, \ + .u.p_static = &CONCAT_3(nrf_cli_, p_syntax, _raw) \ + }; \ + NRF_SECTION_ITEM_REGISTER(cli_sorted_cmd_ptrs, char const * CONCAT_2(p_syntax, _str_ptr)) + +/** + * @brief Macro for creating a subcommand set. It must be used outside of any function body. + * + * @param[in] name Name of the subcommand set. + */ +#define NRF_CLI_CREATE_STATIC_SUBCMD_SET(name) \ + /*lint -save -e85 -e31*/ \ + static nrf_cli_static_entry_t const CONCAT_2(name, _raw)[]; \ + static nrf_cli_cmd_entry_t const name = { \ + .is_dynamic = false, \ + .u.p_static = CONCAT_2(name, _raw) \ + }; \ + static nrf_cli_static_entry_t const CONCAT_2(name, _raw)[] = /*lint -restore*/ + +/** + * @brief Define ending subcommands set. + * + */ +#define NRF_CLI_SUBCMD_SET_END {NULL} + +/** + * @brief Macro for creating a dynamic entry. + * + * @param[in] name Name of the dynamic entry. + * @param[in] p_get Pointer to the function returning dynamic commands array @ref nrf_cli_dynamic_get. + */ +#define NRF_CLI_CREATE_DYNAMIC_CMD(name, p_get) \ + /*lint -save -e19*/ \ + static nrf_cli_cmd_entry_t const name = { \ + .is_dynamic = true, \ + .u.p_dynamic_get = p_get \ +}; /*lint -restore*/ + +/** + * @brief Initializes a CLI command (@ref nrf_cli_static_entry). + * + * @param[in] _p_syntax Command syntax (for example: history). + * @param[in] _p_subcmd Pointer to a subcommands array. + * @param[in] _p_help Pointer to a command help string. + * @param[in] _p_handler Pointer to a function handler. + */ +#define NRF_CLI_CMD(_p_syntax, _p_subcmd, _p_help, _p_handler) { \ + .p_syntax = (const char *) STRINGIFY(_p_syntax), \ + .p_subcmd = _p_subcmd, \ + .p_help = (const char *) _p_help, \ + .handler = _p_handler \ +} + +/** + * @internal @brief Internal CLI state in response to data received from the terminal. + */ +typedef enum +{ + NRF_CLI_RECEIVE_DEFAULT, + NRF_CLI_RECEIVE_ESC, + NRF_CLI_RECEIVE_ESC_SEQ +} nrf_cli_receive_t; + + +/** + * @internal @brief Internal CLI state. + */ +typedef enum +{ + NRF_CLI_STATE_UNINITIALIZED, //!< State uninitialized. + NRF_CLI_STATE_INITIALIZED, //!< State initialized but not active. + NRF_CLI_STATE_ACTIVE, //!< State active. + NRF_CLI_STATE_PANIC_MODE_ACTIVE, //!< State panic mode activated. + NRF_CLI_STATE_PANIC_MODE_INACTIVE //!< State panic mode requested but not supported. +} nrf_cli_state_t; + +/** + * @brief Event type from CLI transport. + */ +typedef enum +{ + NRF_CLI_TRANSPORT_EVT_RX_RDY, + NRF_CLI_TRANSPORT_EVT_TX_RDY +} nrf_cli_transport_evt_t; + +typedef void (*nrf_cli_transport_handler_t)(nrf_cli_transport_evt_t evt_type, void * p_context); + +typedef struct nrf_cli_transport_s nrf_cli_transport_t; + +/** + * @brief Unified CLI transport interface. + */ +typedef struct +{ + /** + * @brief Function for initializing the CLI transport interface. + * + * @param[in] p_transport Pointer to the transfer instance. + * @param[in] p_config Pointer to instance configuration. + * @param[in] evt_handler Event handler. + * @param[in] p_context Pointer to the context passed to event handler. + * + * @return Standard error code. + */ + ret_code_t (*init)(nrf_cli_transport_t const * p_transport, + void const * p_config, + nrf_cli_transport_handler_t evt_handler, + void * p_context); + + /** + * @brief Function for uninitializing the CLI transport interface. + * + * @param[in] p_transport Pointer to the transfer instance. + * + * @return Standard error code. + */ + ret_code_t (*uninit)(nrf_cli_transport_t const * p_transport); + + /** + * @brief Function for reconfiguring the transport to work in blocking mode. + * + * @param p_transport Pointer to the transfer instance. + * @param blocking If true, the transport is enabled in blocking mode. + * + * @return NRF_SUCCESS on successful enabling, error otherwise (also if not supported). + */ + ret_code_t (*enable)(nrf_cli_transport_t const * p_transport, + bool blocking); + + /** + * @brief Function for writing data to the transport interface. + * + * @param[in] p_transport Pointer to the transfer instance. + * @param[in] p_data Pointer to the source buffer. + * @param[in] length Source buffer length. + * @param[in] p_cnt Pointer to the sent bytes counter. + * + * @return Standard error code. + */ + ret_code_t (*write)(nrf_cli_transport_t const * p_transport, + const void * p_data, + size_t length, + size_t * p_cnt); + + /** + * @brief Function for reading data from the transport interface. + * + * @param[in] p_transport Pointer to the transfer instance. + * @param[in] p_data Pointer to the destination buffer. + * @param[in] length Destination buffer length. + * @param[in] p_cnt Pointer to the received bytes counter. + * + * @return Standard error code. + */ + ret_code_t (*read)(nrf_cli_transport_t const * p_transport, + void * p_data, + size_t length, + size_t * p_cnt); + +} nrf_cli_transport_api_t; + +struct nrf_cli_transport_s +{ + nrf_cli_transport_api_t const * p_api; +}; + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) +/** + * @brief CLI history object header. + */ +typedef PACKED_STRUCT +{ + nrf_memobj_t * p_prev; //!< Pointer to the next object. + nrf_memobj_t * p_next; //!< Pointer to the previous object. + nrf_cli_cmd_len_t cmd_len; //!< Command length. +} nrf_cli_memobj_header_t; +#endif + +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) +typedef struct +{ + uint32_t log_lost_cnt; //!< Lost log counter. +} nrf_cli_statistics_t; +#endif + +/** + * @internal @brief Flags for internal CLI usage. + */ +typedef struct +{ + uint32_t insert_mode : 1; //!< Enables or disables console insert mode for text introduction. + uint32_t show_help : 1; //!< Shows help if the command was called with -h or --help parameter. + uint32_t use_colors : 1; //!< Enables or disables colored syntax. + uint32_t echo : 1; //!< Enables or disables CLI echo. + uint32_t processing : 1; //!< CLI is executing process function. + uint32_t tx_rdy : 1; +} nrf_cli_flag_t; +STATIC_ASSERT(sizeof(nrf_cli_flag_t) == sizeof(uint32_t)); + +/** + * @internal @brief Union for internal CLI usage. + */ +typedef union +{ + uint32_t value; + nrf_cli_flag_t flag; +} nrf_cli_internal_t; + +/** + * @brief CLI instance context. + */ +typedef struct +{ + nrf_cli_state_t state; //!< Internal module state. + nrf_cli_receive_t receive_state; //!< Escape sequence indicator. + + nrf_cli_static_entry_t const * p_current_stcmd; //!< Currently executed command. + + nrf_cli_vt100_ctx_t vt100_ctx; //!< VT100 color and cursor position, terminal width. + + nrf_cli_cmd_len_t cmd_buff_len; //!< Command length. + nrf_cli_cmd_len_t cmd_buff_pos; //!< Command buffer cursor position. + +#if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD) + nrf_cli_cmd_len_t cmd_tmp_buff_len; //!< Command length in tmp buffer +#endif + + char cmd_buff[NRF_CLI_CMD_BUFF_SIZE]; //!< Command input buffer. + char temp_buff[NRF_CLI_CMD_BUFF_SIZE]; //!< Temporary buffer used by various functions. + char printf_buff[NRF_CLI_PRINTF_BUFF_SIZE]; //!< Printf buffer size. + +#if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS) + nrf_cli_statistics_t statistics; //!< CLI statistics. +#endif + +#if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER) + task_id_t task_id; +#endif + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) + nrf_memobj_t * p_cmd_list_head; //!< Pointer to the head of history list. + nrf_memobj_t * p_cmd_list_tail; //!< Pointer to the tail of history list. + nrf_memobj_t * p_cmd_list_element; //!< Pointer to an element of history list. +#endif + volatile nrf_cli_internal_t internal; //!< Internal CLI data +} nrf_cli_ctx_t; + +extern const nrf_log_backend_api_t nrf_log_backend_cli_api; + +typedef struct +{ + nrf_log_backend_t backend; + nrf_queue_t const * p_queue; + void * p_context; + nrf_cli_t const * p_cli; +} nrf_cli_log_backend_t; + +#if NRF_CLI_LOG_BACKEND && NRF_MODULE_ENABLED(NRF_LOG) +#define NRF_LOG_BACKEND_CLI_DEF(_name_, _queue_size_) \ + NRF_QUEUE_DEF(nrf_log_entry_t, \ + CONCAT_2(_name_, _queue),_queue_size_, NRF_QUEUE_MODE_NO_OVERFLOW); \ + static nrf_cli_log_backend_t _name_ = { \ + .backend = {.p_api = &nrf_log_backend_cli_api}, \ + .p_queue = &CONCAT_2(_name_, _queue), \ + } + +#define NRF_CLI_BACKEND_PTR(_name_) &CONCAT_2(_name_, _log_backend) +#else +#define NRF_LOG_BACKEND_CLI_DEF(_name_, _queue_sz_) +#define NRF_CLI_BACKEND_PTR(_name_) NULL +#endif + +#if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) +/* Header consists memory for cmd length and pointer to: prev and next element. */ +#define NRF_CLI_HISTORY_HEADER_SIZE (sizeof(nrf_cli_memobj_header_t)) + +#define NRF_CLI_HISTORY_MEM_OBJ(name) \ + NRF_MEMOBJ_POOL_DEF(CONCAT_2(name, _cmd_hist_memobj), \ + NRF_CLI_HISTORY_HEADER_SIZE + \ + NRF_CLI_HISTORY_ELEMENT_SIZE, \ + NRF_CLI_HISTORY_ELEMENT_COUNT) + +#define NRF_CLI_MEMOBJ_PTR(_name_) &CONCAT_2(_name_, _cmd_hist_memobj) +#else +#define NRF_CLI_MEMOBJ_PTR(_name_) NULL +#define NRF_CLI_HISTORY_MEM_OBJ(name) +#endif + +/** + * @brief CLI instance internals. + * + * @ref nrf_cli_t + */ +struct nrf_cli +{ + char const * const p_name; //!< Terminal name. + + nrf_cli_transport_t const * p_iface; //!< Transport interface. + nrf_cli_ctx_t * p_ctx; //!< Internal context. + nrf_cli_log_backend_t * p_log_backend; //!< Logger backend. + nrf_fprintf_ctx_t * p_fprintf_ctx; //!< fprintf context. + nrf_memobj_pool_t const * p_cmd_hist_mempool; //!< Memory reserved for commands history. + char const newline_char; //!< New line character, only allowed values: \\n and \\r. +}; + +/** + * @brief Macro for defining a command line interface instance. + * + * @param[in] name Instance name. + * @param[in] cli_prefix CLI prefix string. + * @param[in] p_transport_iface Pointer to the transport interface. + * @param[in] newline_ch New line character - only allowed values are '\\n' or '\\r'. + * @param[in] log_queue_size Logger processing queue size. + */ +#define NRF_CLI_DEF(name, cli_prefix, p_transport_iface, newline_ch, log_queue_size) \ + static nrf_cli_t const name; \ + static nrf_cli_ctx_t CONCAT_2(name, _ctx); \ + NRF_FPRINTF_DEF(CONCAT_2(name, _fprintf_ctx), \ + &name, \ + CONCAT_2(name, _ctx).printf_buff, \ + NRF_CLI_PRINTF_BUFF_SIZE, \ + false, \ + nrf_cli_print_stream); \ + NRF_LOG_BACKEND_CLI_DEF(CONCAT_2(name, _log_backend), log_queue_size); \ + NRF_CLI_HISTORY_MEM_OBJ(name); \ + /*lint -save -e31*/ \ + static nrf_cli_t const name = { \ + .p_name = cli_prefix, \ + .p_iface = p_transport_iface, \ + .p_ctx = &CONCAT_2(name, _ctx), \ + .p_log_backend = NRF_CLI_BACKEND_PTR(name), \ + .p_fprintf_ctx = &CONCAT_2(name, _fprintf_ctx), \ + .p_cmd_hist_mempool = NRF_CLI_MEMOBJ_PTR(name), \ + .newline_char = newline_ch \ + } /*lint -restore*/ + +/** + * @brief Function for initializing a transport layer and internal CLI state. + * + * @param[in] p_cli Pointer to CLI instance. + * @param[in] p_transport_config Configuration forwarded to the transport during initialization. + * @param[in] use_colors Enables colored prints. + * @param[in] log_backend If true, the console will be used as logger backend. + * @param[in] init_lvl Default severity level for the logger. + * + * @return Standard error code. + */ +ret_code_t nrf_cli_init(nrf_cli_t const * p_cli, + void const * p_transport_config, + bool use_colors, + bool log_backend, + nrf_log_severity_t init_lvl); + +ret_code_t nrf_cli_task_create(nrf_cli_t const * p_cli); + +/** + * @brief Function for uninitializing a transport layer and internal CLI state. + * If function returns NRF_ERROR_BUSY, you must call @ref nrf_cli_process before calling + * nrf_cli_uninit again. + * + * @param p_cli Pointer to CLI instance. + * + * @return Standard error code. + */ +ret_code_t nrf_cli_uninit(nrf_cli_t const * p_cli); + +/** + * @brief Function for starting CLI processing. + * + * @param p_cli Pointer to the CLI instance. + * + * @return Standard error code. + */ +ret_code_t nrf_cli_start(nrf_cli_t const * p_cli); + +/** + * @brief Function for stopping CLI processing. + * + * @param p_cli Pointer to CLI instance. + * + * @return Standard error code. + */ +ret_code_t nrf_cli_stop(nrf_cli_t const * p_cli); + +/** + * @brief CLI colors for @ref nrf_cli_fprintf function. + */ +#define NRF_CLI_DEFAULT NRF_CLI_VT100_COLOR_DEFAULT /**< Turn off character attributes. */ +#define NRF_CLI_NORMAL NRF_CLI_VT100_COLOR_WHITE /**< Normal color printf. */ +#define NRF_CLI_INFO NRF_CLI_VT100_COLOR_GREEN /**< Info color printf. */ +#define NRF_CLI_OPTION NRF_CLI_VT100_COLOR_CYAN /**< Option color printf. */ +#define NRF_CLI_WARNING NRF_CLI_VT100_COLOR_YELLOW /**< Warning color printf. */ +#define NRF_CLI_ERROR NRF_CLI_VT100_COLOR_RED /**< Error color printf. */ + +/** + * @brief Printf-like function which sends formatted data stream to the CLI. + * This function shall not be used outside of the CLI module or CLI command context. + * + * @param[in] p_cli Pointer to the CLI instance. + * @param[in] color Printf color. + * @param[in] p_fmt Format string. + * @param[in] ... List of parameters to print. + */ +void nrf_cli_fprintf(nrf_cli_t const * p_cli, + nrf_cli_vt100_color_t color, + char const * p_fmt, + ...); + +/** + * @brief Process function, which should be executed when data is ready in the transport interface. + * + * @param[in] p_cli Pointer to the CLI instance. + */ +void nrf_cli_process(nrf_cli_t const * p_cli); + + +/** + * @brief Option descriptor. + */ +typedef struct nrf_cli_getopt_option +{ + char const * p_optname; //!< Option long name. + char const * p_optname_short; //!< Option short name. + char const * p_optname_help; //!< Option help string. +} nrf_cli_getopt_option_t; + + +/** + * @brief Option structure initializer @ref nrf_cli_getopt_option. + * + * @param[in] _p_optname Option name long. + * @param[in] _p_shortname Option name short. + * @param[in] _p_help Option help string. + */ +#define NRF_CLI_OPT(_p_optname, _p_shortname, _p_help) { \ + .p_optname = _p_optname, \ + .p_optname_short = _p_shortname, \ + .p_optname_help = _p_help, \ +} + +/** + * @brief Informs that a command has been called with -h or --help option. + * + * @param[in] p_cli Pointer to the CLI instance. + * + * @return True if help has been requested. + */ +__STATIC_INLINE bool nrf_cli_help_requested(nrf_cli_t const * p_cli); + +#ifndef SUPPRESS_INLINE_IMPLEMENTATION +__STATIC_INLINE bool nrf_cli_help_requested(nrf_cli_t const * p_cli) +{ + return p_cli->p_ctx->internal.flag.show_help; +} +#endif + +/** + * @brief Prints the current command help. + * @details Function will print a help string with: the currently entered command, its options, + * and subcommands (if they exist). + * + * @param[in] p_cli Pointer to the CLI instance. + * @param[in] p_opt Pointer to the optional option array. + * @param[in] opt_len Option array size. + */ +void nrf_cli_help_print(nrf_cli_t const * p_cli, + nrf_cli_getopt_option_t const * p_opt, + size_t opt_len); + +/** + * @internal @brief This function shall not be used directly, it is required by the + * nrf_fprintf module. + * + * @param[in] p_user_ctx Pointer to the context for the CLI instance. + * @param[in] p_data Pointer to the data buffer. + * @param[in] data_len Data buffer size. + */ +void nrf_cli_print_stream(void const * p_user_ctx, char const * p_data, size_t data_len); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_CLI_H__ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_types.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_types.h new file mode 100644 index 0000000..ca4ca75 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_types.h @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_CLI_TYPES_H__ +#define NRF_CLI_TYPES_H__ + +#include +#include "sdk_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if (NRF_CLI_CMD_BUFF_SIZE > 65535) + typedef uint32_t nrf_cli_cmd_len_t; +#elif (NRF_CLI_CMD_BUFF_SIZE > 255) + typedef uint16_t nrf_cli_cmd_len_t; +#else + typedef uint8_t nrf_cli_cmd_len_t; +#endif + +typedef enum +{ + NRF_CLI_VT100_COLOR_DEFAULT, + NRF_CLI_VT100_COLOR_BLACK, + NRF_CLI_VT100_COLOR_RED, + NRF_CLI_VT100_COLOR_GREEN, + NRF_CLI_VT100_COLOR_YELLOW, + NRF_CLI_VT100_COLOR_BLUE, + NRF_CLI_VT100_COLOR_MAGENTA, + NRF_CLI_VT100_COLOR_CYAN, + NRF_CLI_VT100_COLOR_WHITE, + + VT100_COLOR_END +} nrf_cli_vt100_color_t; + +typedef struct +{ + nrf_cli_vt100_color_t col; // text color + nrf_cli_vt100_color_t bgcol; // background color +} nrf_cli_vt100_colors_t; + +typedef struct +{ + nrf_cli_cmd_len_t cur_x; // horizontal cursor position in edited command line + nrf_cli_cmd_len_t cur_x_end; // horizontal cursor position at the end of command + nrf_cli_cmd_len_t cur_y; // vertical cursor position in edited command + nrf_cli_cmd_len_t cur_y_end; // vertical cursor position at the end of command + nrf_cli_cmd_len_t terminal_hei; // terminal screen height + nrf_cli_cmd_len_t terminal_wid; // terminal screen width + uint8_t name_len; // console name length +} nrf_cli_multiline_cons_t; + +typedef struct +{ + nrf_cli_multiline_cons_t cons; + nrf_cli_vt100_colors_t col; + nrf_cli_cmd_len_t printed_cmd; // printed commands counter +} nrf_cli_vt100_ctx_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_CLI_TYPES_H__ */ + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_vt100.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_vt100.h new file mode 100644 index 0000000..d581423 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/nrf_cli_vt100.h @@ -0,0 +1,625 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_CLI_VT100_H__ +#define NRF_CLI_VT100_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NRF_CLI_VT100_ASCII_ESC (0x1b) +#define NRF_CLI_VT100_ASCII_DEL (0x7F) +#define NRF_CLI_VT100_ASCII_BSPACE (0x08) + +#define NRF_CLI_VT100_SETNL \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', '0', 'h', '\0' \ + } /* Set new line mode */ +#define NRF_CLI_VT100_SETAPPL \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '1', 'h', '\0' \ + } /* Set cursor key to application */ +#define NRF_CLI_VT100_SETCOL_132 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '3', 'h', '\0' \ + } /* Set number of columns to 132 */ +#define NRF_CLI_VT100_SETSMOOTH \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '4', 'h', '\0' \ + } /* Set smooth scrolling */ +#define NRF_CLI_VT100_SETREVSCRN \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '5', 'h', '\0' \ + } /* Set reverse video on screen */ +#define NRF_CLI_VT100_SETORGREL \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '6', 'h', '\0' \ + } /* Set origin to relative */ +#define NRF_CLI_VT100_SETWRAP_ON \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '7', 'h', '\0' \ + } /* Set auto-wrap mode */ +#define NRF_CLI_VT100_SETWRAP_OFF \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '7', 'l', '\0' \ + } /* Set auto-wrap mode */ + +#define NRF_CLI_VT100_SETREP \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '8', 'h', '\0' \ + } /* Set auto-repeat mode */ +#define NRF_CLI_VT100_SETINTER \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '9', 'h', '\0' \ + } /* Set interlacing mode */ + +#define NRF_CLI_VT100_SETLF \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', '0', 'l', '\0' \ + } /* Set line feed mode */ +#define NRF_CLI_VT100_SETCURSOR \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '1', 'l', '\0' \ + } /* Set cursor key to cursor */ +#define NRF_CLI_VT100_SETVT52 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '2', 'l', '\0' \ + } /* Set VT52 (versus ANSI) */ +#define NRF_CLI_VT100_SETCOL_80 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '3', 'l', '\0' \ + } /* Set number of columns to 80 */ +#define NRF_CLI_VT100_SETJUMP \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '4', 'l', '\0' \ + } /* Set jump scrolling */ +#define NRF_CLI_VT100_SETNORMSCRN \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '5', 'l', '\0' \ + } /* Set normal video on screen */ +#define NRF_CLI_VT100_SETORGABS \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '6', 'l', '\0' \ + } /* Set origin to absolute */ +#define NRF_CLI_VT100_RESETWRAP \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '7', 'l', '\0' \ + } /* Reset auto-wrap mode */ +#define NRF_CLI_VT100_RESETREP \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '8', 'l', '\0' \ + } /* Reset auto-repeat mode */ +#define NRF_CLI_VT100_RESETINTER \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '9', 'l', '\0' \ + } /* Reset interlacing mode */ + +#define NRF_CLI_VT100_ALTKEYPAD \ + { \ + NRF_CLI_VT100_ASCII_ESC, '=', '\0' \ + } /* Set alternate keypad mode */ +#define NRF_CLI_VT100_NUMKEYPAD \ + { \ + NRF_CLI_VT100_ASCII_ESC, '>', '\0' \ + } /* Set numeric keypad mode */ + +#define NRF_CLI_VT100_SETUKG0 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '(', 'A', '\0' \ + } /* Set United Kingdom G0 character set */ +#define NRF_CLI_VT100_SETUKG1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, ')', 'A', '\0' \ + } /* Set United Kingdom G1 character set */ +#define NRF_CLI_VT100_SETUSG0 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '(', 'B', '\0' \ + } /* Set United States G0 character set */ +#define NRF_CLI_VT100_SETUSG1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, ')', 'B', '\0' \ + } /* Set United States G1 character set */ +#define NRF_CLI_VT100_SETSPECG0 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '(', '0', '\0' \ + } /* Set G0 special chars. & line set */ +#define NRF_CLI_VT100_SETSPECG1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, ')', '0', '\0' \ + } /* Set G1 special chars. & line set */ +#define NRF_CLI_VT100_SETALTG0 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '(', '1', '\0' \ + } /* Set G0 alternate character ROM */ +#define NRF_CLI_VT100_SETALTG1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, ')', '1', '\0' \ + } /* Set G1 alternate character ROM */ +#define NRF_CLI_VT100_SETALTSPECG0 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '(', '2', '\0' \ + } /* Set G0 alt char ROM and spec. graphics */ +#define NRF_CLI_VT100_SETALTSPECG1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, ')', '2', '\0' \ + } /* Set G1 alt char ROM and spec. graphics */ + +#define NRF_CLI_VT100_SETSS2 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'N', '\0' \ + } /* Set single shift 2 */ +#define NRF_CLI_VT100_SETSS3 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', '\0' \ + } /* Set single shift 3 */ + +#define NRF_CLI_VT100_MODESOFF \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', 'm', '\0' \ + } /* Turn off character attributes */ +#define NRF_CLI_VT100_MODESOFF_ \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '0', 'm', '\0' \ + } /* Turn off character attributes */ +#define NRF_CLI_VT100_BOLD \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '1', 'm', '\0' \ + } /* Turn bold mode on */ +#define NRF_CLI_VT100_LOWINT \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', 'm', '\0' \ + } /* Turn low intensity mode on */ +#define NRF_CLI_VT100_UNDERLINE \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '4', 'm', '\0' \ + } /* Turn underline mode on */ +#define NRF_CLI_VT100_BLINK \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '5', 'm', '\0' \ + } /* Turn blinking mode on */ +#define NRF_CLI_VT100_REVERSE \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '7', 'm', '\0' \ + } /* Turn reverse video on */ +#define NRF_CLI_VT100_INVISIBLE \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '8', 'm', '\0' \ + } /* Turn invisible text mode on */ + +#define NRF_CLI_VT100_SETWIN(t, b) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', (t), ';', (b), 'r', '\0' \ + } /* Set top and bottom line#s of a window */ + +#define NRF_CLI_VT100_CURSORUP(n) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', (n), 'A', '\0' \ + } /* Move cursor up n lines */ +#define NRF_CLI_VT100_CURSORDN(n) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', (n), 'B', '\0' \ + } /* Move cursor down n lines */ +#define NRF_CLI_VT100_CURSORRT(n) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', (n), 'C', '\0' \ + } /* Move cursor right n lines */ +#define NRF_CLI_VT100_CURSORLF(n) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', (n), 'D', '\0' \ + } /* Move cursor left n lines */ +#define NRF_CLI_VT100_CURSORHOME \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', 'H', '\0' \ + } /* Move cursor to upper left corner */ +#define NRF_CLI_VT100_CURSORHOME_ \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', ';', 'H', '\0' \ + } /* Move cursor to upper left corner */ +#define NRF_CLI_VT100_CURSORPOS(v, h) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', (v), ';', (h), 'H', '\0' \ + } /* Move cursor to screen location v,h */ + +#define NRF_CLI_VT100_HVHOME \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', 'f', '\0' \ + } /* Move cursor to upper left corner */ +#define NRF_CLI_VT100_HVHOME_ \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', ';', 'f', '\0' \ + } /* Move cursor to upper left corner */ +#define NRF_CLI_VT100_HVPOS(v, h) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', (v), ';', (h), 'f', '\0' \ + } /* Move cursor to screen location v,h */ +#define NRF_CLI_VT100_INDEX \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'D', '\0' \ + } /* Move/scroll window up one line */ +#define NRF_CLI_VT100_REVINDEX \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'M', '\0' \ + } /* Move/scroll window down one line */ +#define NRF_CLI_VT100_NEXTLINE \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'E', '\0' \ + } /* Move to next line */ +#define NRF_CLI_VT100_SAVECURSOR \ + { \ + NRF_CLI_VT100_ASCII_ESC, '7', '\0' \ + } /* Save cursor position and attributes */ +#define NRF_CLI_VT100_RESTORECURSOR \ + { \ + NRF_CLI_VT100_ASCII_ESC, '8', '\0' \ + } /* Restore cursor position and attribute */ + +#define NRF_CLI_VT100_TABSET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'H', '\0' \ + } /* Set a tab at the current column */ +#define NRF_CLI_VT100_TABCLR \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', 'g', '\0' \ + } /* Clear a tab at the current column */ +#define NRF_CLI_VT100_TABCLR_ \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '0', 'g', '\0' \ + } /* Clear a tab at the current column */ +#define NRF_CLI_VT100_TABCLRALL \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '3', 'g', '\0' \ + } /* Clear all tabs */ + +#define NRF_CLI_VT100_DHTOP \ + { \ + NRF_CLI_VT100_ASCII_ESC, '#', '3', '\0' \ + } /* Double-height letters, top half */ +#define NRF_CLI_VT100_DHBOT \ + { \ + NRF_CLI_VT100_ASCII_ESC, '#', '4', '\0' \ + } /* Double-height letters, bottom hal */ +#define NRF_CLI_VT100_SWSH \ + { \ + NRF_CLI_VT100_ASCII_ESC, '#', '5', '\0' \ + } /* Single width, single height letters */ +#define NRF_CLI_VT100_DWSH \ + { \ + NRF_CLI_VT100_ASCII_ESC, '#', '6', '\0' \ + } /* Double width, single height letters */ + +#define NRF_CLI_VT100_CLEAREOL \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', 'K', '\0' \ + } /* Clear line from cursor right */ +#define NRF_CLI_VT100_CLEAREOL_ \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '0', 'K', '\0' \ + } /* Clear line from cursor right */ +#define NRF_CLI_VT100_CLEARBOL \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '1', 'K', '\0' \ + } /* Clear line from cursor left */ +#define NRF_CLI_VT100_CLEARLINE \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', 'K', '\0' \ + } /* Clear entire line */ + +#define NRF_CLI_VT100_CLEAREOS \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', 'J', '\0' \ + } /* Clear screen from cursor down */ +#define NRF_CLI_VT100_CLEAREOS_ \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '0', 'J', '\0' \ + } /* Clear screen from cursor down */ +#define NRF_CLI_VT100_CLEARBOS \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '1', 'J', '\0' \ + } /* Clear screen from cursor up */ +#define NRF_CLI_VT100_CLEARSCREEN \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', 'J', '\0' \ + } /* Clear entire screen */ + +#define NRF_CLI_VT100_DEVSTAT \ + { \ + NRF_CLI_VT100_ASCII_ESC, '5', 'n', '\0' \ + } /* Device status report */ +#define NRF_CLI_VT100_TERMOK \ + { \ + NRF_CLI_VT100_ASCII_ESC, '0', 'n', '\0' \ + } /* Response: terminal is OK */ +#define NRF_CLI_VT100_TERMNOK \ + { \ + NRF_CLI_VT100_ASCII_ESC, '3', 'n', '\0' \ + } /* Response: terminal is not OK */ + +#define NRF_CLI_VT100_GETCURSOR \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '6', 'n', '\0' \ + } /* Get cursor position */ +#define NRF_CLI_VT100_CURSORPOSAT \ + { \ + NRF_CLI_VT100_ASCII_ESC, (v), ';', (h), 'R', '\0' \ + } /* Response: cursor is at v,h */ + +#define NRF_CLI_VT100_IDENT \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', 'c', '\0' \ + } /* Identify what terminal type */ +#define NRF_CLI_VT100_IDENT_ \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '0', 'c', '\0' \ + } /* Identify what terminal type */ +#define NRF_CLI_VT100_GETTYPE \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '?', '1', ';', (n), '0', 'c', '\0'\ + } /* Response: terminal type code n */ + +#define NRF_CLI_VT100_RESET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'c', '\0' \ + } /* Reset terminal to initial state */ + +#define NRF_CLI_VT100_ALIGN \ + { \ + NRF_CLI_VT100_ASCII_ESC, '#', '8', '\0' \ + } /* Screen alignment display */ +#define NRF_CLI_VT100_TESTPU \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', ';', '1', 'y', '\0' \ + } /* Confidence power up test */ +#define NRF_CLI_VT100_TESTLB \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', ';', '2', 'y', '\0' \ + } /* Confidence loopback test */ +#define NRF_CLI_VT100_TESTPUREP \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', ';', '9', 'y', '\0' \ + } /* Repeat power up test */ +#define NRF_CLI_VT100_TESTLBREP \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', ';', '1', '0', 'y', '\0' \ + } /* Repeat loopback test */ + +#define NRF_CLI_VT100_LEDSOFF \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '0', 'q', '\0' \ + } /* Turn off all four leds */ +#define NRF_CLI_VT100_LED1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '1', 'q', '\0' \ + } /* Turn on LED #1 */ +#define NRF_CLI_VT100_LED2 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '2', 'q', '\0' \ + } /* Turn on LED #2 */ +#define NRF_CLI_VT100_LED3 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '3', 'q', '\0' \ + } /* Turn on LED #3 */ +#define NRF_CLI_VT100_LED4 \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '4', 'q', '\0' \ + } /* Turn on LED #4 */ + +/* Function Keys */ + +#define NRF_CLI_VT100_PF1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'P', '\0' \ + } +#define NRF_CLI_VT100_PF2 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'Q', '\0' \ + } +#define NRF_CLI_VT100_PF3 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'R', '\0' \ + } +#define NRF_CLI_VT100_PF4 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'S', '\0' \ + } + +/* Arrow keys */ + +#define NRF_CLI_VT100_UP_RESET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'A', '\0' \ + } +#define NRF_CLI_VT100_UP_SET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'A', '\0' \ + } +#define NRF_CLI_VT100_DOWN_RESET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'B', '\0' \ + } +#define NRF_CLI_VT100_DOWN_SET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'B', '\0' \ + } +#define NRF_CLI_VT100_RIGHT_RESET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'C', '\0' \ + } +#define NRF_CLI_VT100_RIGHT_SET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'C', '\0' \ + } +#define NRF_CLI_VT100_LEFT_RESET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'D', '\0' \ + } +#define NRF_CLI_VT100_LEFT_SET \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'D', '\0' \ + } + +/* Numeric Keypad Keys */ + +#define NRF_CLI_VT100_NUMERIC_0 \ + { \ + '0', '\0' \ + } +#define NRF_CLI_VT100_ALT_0 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'p', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_1 \ + { \ + '1', '\0' \ + } +#define NRF_CLI_VT100_ALT_1 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'q', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_2 \ + { \ + '2', '\0' \ + } +#define NRF_CLI_VT100_ALT_2 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'r', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_3 \ + { \ + '3', '\0' \ + } +#define NRF_CLI_VT100_ALT_3 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 's', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_4 \ + { \ + '4', '\0' \ + } +#define NRF_CLI_VT100_ALT_4 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 't', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_5 \ + { \ + '5', '\0' \ + } +#define NRF_CLI_VT100_ALT_5 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'u', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_6 \ + { \ + '6', '\0' \ + } +#define NRF_CLI_VT100_ALT_6 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'v', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_7 \ + { \ + '7', '\0' \ + } +#define NRF_CLI_VT100_ALT_7 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'w', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_8 \ + { \ + '8', '\0' \ + } +#define NRF_CLI_VT100_ALT_8 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'x', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_9 \ + { \ + '9', '\0' +#define NRF_CLI_VT100_ALT_9 \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'y' \ + } +#define NRF_CLI_VT100_NUMERIC_MINUS \ + { \ + '-', '\0' \ + } +#define NRF_CLI_VT100_ALT_MINUS \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'm', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_COMMA \ + { \ + ',', '\0' \ + } +#define NRF_CLI_VT100_ALT_COMMA \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'l', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_PERIOD \ + { \ + '.', '\0' \ + } +#define NRF_CLI_VT100_ALT_PERIOD \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'n', '\0' \ + } +#define NRF_CLI_VT100_NUMERIC_ENTER \ + { \ + ASCII_CR \ + } +#define NRF_CLI_VT100_ALT_ENTER \ + { \ + NRF_CLI_VT100_ASCII_ESC, 'O', 'M', '\0' \ + } + +#define NRF_CLI_VT100_COLOR(__col) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '1', ';', '3', '0' + (__col), 'm', '\0' \ + } +#define NRF_CLI_VT100_BGCOLOR(__col) \ + { \ + NRF_CLI_VT100_ASCII_ESC, '[', '4', '0' + (__col), 'm', '\0' \ + } + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_CLI_VT100_H__ */ + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.c new file mode 100644 index 0000000..badcd04 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.c @@ -0,0 +1,223 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_CLI_RTT) +#include +#include +#include "nrf_cli_rtt.h" +#include "nrf_assert.h" +#include "nrf_delay.h" + +#define RTT_RX_TIMEOUT 100 + +static bool m_host_present; + +static void timer_handler(void * p_context) +{ + nrf_cli_rtt_internal_t * p_internal = (nrf_cli_rtt_internal_t *)p_context; + if (SEGGER_RTT_HasData(0)) + { + p_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_RX_RDY, p_internal->p_cb->p_context); + } + p_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_TX_RDY, p_internal->p_cb->p_context); + + ret_code_t err_code = app_timer_start(*p_internal->p_timer, + APP_TIMER_TICKS(RTT_RX_TIMEOUT), + p_context); + ASSERT(err_code == NRF_SUCCESS); + UNUSED_VARIABLE(err_code); +} + +static ret_code_t cli_rtt_init(nrf_cli_transport_t const * p_transport, + void const * p_config, + nrf_cli_transport_handler_t evt_handler, + void * p_context) +{ + UNUSED_PARAMETER(p_config); + + nrf_cli_rtt_internal_t * p_internal = + CONTAINER_OF(p_transport, nrf_cli_rtt_internal_t, transport); + p_internal->p_cb->handler = evt_handler; + p_internal->p_cb->p_context = p_context; + p_internal->p_cb->timer_created = false; + + SEGGER_RTT_Init(); + + m_host_present = true; + + return NRF_SUCCESS; +} + +static ret_code_t cli_rtt_uninit(nrf_cli_transport_t const * p_transport) +{ + nrf_cli_rtt_internal_t * p_internal = + CONTAINER_OF(p_transport, nrf_cli_rtt_internal_t, transport); + + return app_timer_stop(*p_internal->p_timer); +} + +static ret_code_t cli_rtt_enable(nrf_cli_transport_t const * p_transport, + bool blocking) +{ + nrf_cli_rtt_internal_t * p_internal = + CONTAINER_OF(p_transport, nrf_cli_rtt_internal_t, transport); + ret_code_t err_code = NRF_SUCCESS; + + if (p_internal->p_cb->timer_created) + { + err_code = app_timer_stop(*p_internal->p_timer); //Timer may be running or inactive + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) + { + return err_code; + } + else + { + err_code = NRF_SUCCESS; + } + } + if (!blocking) + { + if (!p_internal->p_cb->timer_created) + { + err_code = app_timer_create(p_internal->p_timer, + APP_TIMER_MODE_SINGLE_SHOT, + timer_handler); + p_internal->p_cb->timer_created = true; + } + if (err_code == NRF_SUCCESS) + { + err_code = app_timer_start(*p_internal->p_timer, + APP_TIMER_TICKS(RTT_RX_TIMEOUT), + p_internal); + SEGGER_RTT_Init(); + } + } + return err_code; +} +static ret_code_t cli_rtt_read(nrf_cli_transport_t const * p_transport, + void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + UNUSED_PARAMETER(p_transport); + + size_t rcnt = SEGGER_RTT_Read(NRF_CLI_RTT_TERMINAL_ID, p_data, length); + *p_cnt = rcnt; + + return NRF_SUCCESS; +} + +static ret_code_t cli_rtt_write(nrf_cli_transport_t const * p_transport, + const void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + UNUSED_PARAMETER(p_transport); + + if (!(CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk)) + { + /* If an RTT session is not active, but the RTT console is processed, the program may hang. + * Workaround: If the debugger is not connected, always return NRF_SUCCESS. + */ + *p_cnt = length; + return NRF_SUCCESS; + } + + size_t idx = 0; + uint32_t processed; + uint32_t watchdog_counter = NRF_CLI_RTT_TX_RETRY_CNT; + const uint8_t * p_buffer = (const uint8_t *)p_data; + do { + processed = SEGGER_RTT_Write(NRF_CLI_RTT_TERMINAL_ID, &p_buffer[idx], length); + if (processed == 0) + { + /* There are two possible reasons for not writing any data to RTT: + * - The host is not connected and not reading the data. + * - The buffer got full and will be read by the host. + * These two situations are distinguished using the following algorithm. + * At the begining, the module assumes that the host is active, + * so when no data is read, it busy waits and retries. + * If, after retrying, the host reads the data, the module assumes that the host is active. + * If it fails, the module assumes that the host is inactive and stores that information. On next + * call, only one attempt takes place. The host is marked as active if the attempt is successful. + */ + if (!m_host_present) + { + break; + } + else + { + nrf_delay_ms(NRF_CLI_RTT_TX_RETRY_DELAY_MS); + watchdog_counter--; + if (watchdog_counter == 0) + { + m_host_present = false; + break; + } + } + } + m_host_present = true; + idx += processed; + length -= processed; + } while (length); + + if (idx > 0) + { + *p_cnt = idx; + } + else + { + *p_cnt = length; + } + return NRF_SUCCESS; +} + +const nrf_cli_transport_api_t nrf_cli_rtt_transport_api = { + .init = cli_rtt_init, + .uninit = cli_rtt_uninit, + .enable = cli_rtt_enable, + .read = cli_rtt_read, + .write = cli_rtt_write, +}; + +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.h new file mode 100644 index 0000000..4cb44e1 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/rtt/nrf_cli_rtt.h @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_CLI_RTT_H__ +#define NRF_CLI_RTT_H__ + +#include "nrf_cli.h" +#include "app_timer.h" +#include "nordic_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nrf_cli_rtt RTT command line interface transport layer + * @ingroup nrf_cli + * + * @{ + * + */ + +/** + * @brief Command line interface transport. + */ +extern const nrf_cli_transport_api_t nrf_cli_rtt_transport_api; + +/** + * @brief CLI RTT transport control block structure. + */ +typedef struct { + nrf_cli_transport_handler_t handler; //!< Event handler + void * p_context; //!< User context. + bool timer_created;//!< Flag indicating whether a timer is created. +} nrf_cli_rtt_internal_cb_t; + +/** + * @brief CLI RTT transport instance structure. + */ +typedef struct { + nrf_cli_transport_t transport; //!< Transport structure. + nrf_cli_rtt_internal_cb_t * p_cb; //!< Pointer to the instance control block. + app_timer_id_t const * p_timer; //!< Pointer to the app_timer instance. +} nrf_cli_rtt_internal_t; + +/**@brief CLI RTT transport definition */ +#define NRF_CLI_RTT_DEF(_name_) \ + APP_TIMER_DEF(CONCAT_2(_name_, _timer)); \ + static nrf_cli_rtt_internal_cb_t CONCAT_2(_name_, _cb); \ + static const nrf_cli_rtt_internal_t _name_ = { \ + .transport = {.p_api = &nrf_cli_rtt_transport_api}, \ + .p_cb = &CONCAT_2(_name_, _cb), \ + .p_timer = &CONCAT_2(_name_, _timer) \ + } + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_CLI_RTT_H__ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.c new file mode 100644 index 0000000..d38941a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.c @@ -0,0 +1,308 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_CLI_UART) +#include "nrf_cli_uart.h" +#include "nrf_drv_uart.h" +#include "nrf_assert.h" + +#define NRF_LOG_MODULE_NAME cli_uart + +#define NRF_LOG_LEVEL (NRF_CLI_UART_CONFIG_LOG_ENABLED ? NRF_CLI_UART_CONFIG_LOG_LEVEL : 0) +#define NRF_LOG_INFO_COLOR NRF_CLI_UART_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR NRF_CLI_UART_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define CLI_UART_RX_TIMEOUT 100 + +static ret_code_t rx_try(nrf_cli_uart_internal_t * p_internal) +{ + ret_code_t err_code; + size_t len = 255; + uint8_t * p_data; + + err_code = nrf_ringbuf_alloc(p_internal->p_rx_ringbuf, &p_data, &len, true); + ASSERT(err_code == NRF_SUCCESS); + + if ((err_code == NRF_SUCCESS) && len) + { + err_code = nrf_drv_uart_rx(p_internal->p_uart, p_data, len); + + if (err_code == NRF_SUCCESS) + { + err_code = app_timer_start(*p_internal->p_timer, + APP_TIMER_TICKS(CLI_UART_RX_TIMEOUT), + p_internal); + } + } + + return err_code; +} + +static void uart_event_handler(nrf_drv_uart_event_t * p_event, void * p_context) +{ + nrf_cli_uart_internal_t * p_internal = (nrf_cli_uart_internal_t *)p_context; + ret_code_t err_code = NRF_SUCCESS; + UNUSED_VARIABLE(err_code); + uint8_t * p_data; + size_t len = 255; + switch (p_event->type) + { + case NRF_DRV_UART_EVT_ERROR: + NRF_LOG_WARNING("id:%d, evt: ERROR:%d", + p_internal->p_uart->inst_idx, + p_event->data.error.error_mask); + err_code = nrf_ringbuf_put(p_internal->p_rx_ringbuf, p_event->data.error.rxtx.bytes); + ASSERT((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_NO_MEM)); + err_code = rx_try(p_internal); + ASSERT(err_code == NRF_SUCCESS); + + break; + + case NRF_DRV_UART_EVT_RX_DONE: + err_code = nrf_ringbuf_put(p_internal->p_rx_ringbuf, p_event->data.rxtx.bytes); + ASSERT((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_NO_MEM)); + + if (p_event->data.rxtx.bytes) + { + NRF_LOG_INFO("id:%d, evt: RXRDY len:%d", + p_internal->p_uart->inst_idx, + p_event->data.rxtx.bytes); + NRF_LOG_HEXDUMP_DEBUG(p_event->data.rxtx.p_data, p_event->data.rxtx.bytes); + p_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_RX_RDY, + p_internal->p_cb->p_context); + } + err_code = rx_try(p_internal); + ASSERT(err_code == NRF_SUCCESS); + + break; + + case NRF_DRV_UART_EVT_TX_DONE: + err_code = nrf_ringbuf_free(p_internal->p_tx_ringbuf, p_event->data.rxtx.bytes); + ASSERT(err_code == NRF_SUCCESS); + len = 255; + err_code = nrf_ringbuf_get(p_internal->p_tx_ringbuf, &p_data, &len, true); + ASSERT(err_code == NRF_SUCCESS); + if (len) + { + NRF_LOG_INFO("id:%d, evt uart_tx, len:%d", p_internal->p_uart->inst_idx, len); + err_code = nrf_drv_uart_tx(p_internal->p_uart, p_data, len); + ASSERT(err_code == NRF_SUCCESS); + } + p_internal->p_cb->handler(NRF_CLI_TRANSPORT_EVT_TX_RDY, p_internal->p_cb->p_context); + NRF_LOG_INFO("id:%d, evt: TXRDY, len:%d", + p_internal->p_uart->inst_idx, + p_event->data.rxtx.bytes); + break; + + default: + NRF_LOG_ERROR("Unknown event"); + ASSERT(false); + } +} + +static void timer_handler(void * p_context) +{ + nrf_cli_uart_internal_t * p_internal = (nrf_cli_uart_internal_t *)p_context; + NRF_LOG_DEBUG("id:%d, evt: Timeout", p_internal->p_uart->inst_idx); + nrf_drv_uart_rx_abort(p_internal->p_uart); +} + +static ret_code_t cli_uart_init(nrf_cli_transport_t const * p_transport, + void const * p_config, + nrf_cli_transport_handler_t evt_handler, + void * p_context) +{ + nrf_cli_uart_internal_t * p_internal = + CONTAINER_OF(p_transport, + nrf_cli_uart_internal_t, + transport); + p_internal->p_cb->handler = evt_handler; + p_internal->p_cb->p_context = p_context; + p_internal->p_cb->timer_created = false; + p_internal->p_cb->blocking = false; + + nrf_drv_uart_config_t * p_uart_config = (nrf_drv_uart_config_t *)p_config; + memcpy(&p_internal->p_cb->uart_config, p_uart_config, sizeof(nrf_drv_uart_config_t)); + p_uart_config->p_context = (void *)p_internal; + ret_code_t err_code = nrf_drv_uart_init(p_internal->p_uart, + p_uart_config, + uart_event_handler); + if (err_code == NRF_SUCCESS) + { + nrf_ringbuf_init(p_internal->p_rx_ringbuf); + nrf_ringbuf_init(p_internal->p_tx_ringbuf); + } + return err_code; +} + +static ret_code_t cli_uart_uninit(nrf_cli_transport_t const * p_transport) +{ + nrf_cli_uart_internal_t * p_internal = + CONTAINER_OF(p_transport, + nrf_cli_uart_internal_t, + transport); + + nrf_drv_uart_uninit(p_internal->p_uart); + + return app_timer_stop(*p_internal->p_timer); +} + +static ret_code_t cli_uart_enable(nrf_cli_transport_t const * p_transport, + bool blocking) +{ + nrf_cli_uart_internal_t * p_internal = + CONTAINER_OF(p_transport, + nrf_cli_uart_internal_t, + transport); + ret_code_t err_code = NRF_SUCCESS; + + if (p_internal->p_cb->timer_created) + { + err_code = app_timer_stop(*p_internal->p_timer); //Timer may be running or inactive + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) + { + return err_code; + } + else + { + err_code = NRF_SUCCESS; + } + } + + if (blocking) + { + nrf_drv_uart_uninit(p_internal->p_uart); + err_code = nrf_drv_uart_init(p_internal->p_uart, &p_internal->p_cb->uart_config, NULL); + if (err_code == NRF_SUCCESS) + { + p_internal->p_cb->blocking = true; + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_NOT_SUPPORTED; + } + } + else + { + if (!p_internal->p_cb->timer_created) + { + err_code = app_timer_create(p_internal->p_timer, + APP_TIMER_MODE_SINGLE_SHOT, + timer_handler); + p_internal->p_cb->timer_created = true; + } + if (err_code == NRF_SUCCESS) + { + err_code = rx_try(p_internal); + } + } + return err_code; +} + +static ret_code_t cli_uart_read(nrf_cli_transport_t const * p_transport, + void * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + nrf_cli_uart_internal_t * p_instance = + CONTAINER_OF(p_transport, nrf_cli_uart_internal_t, transport); + + *p_cnt = length; + ret_code_t err_code = nrf_ringbuf_cpy_get(p_instance->p_rx_ringbuf, p_data, p_cnt); + + if (*p_cnt) + { + NRF_LOG_INFO("id:%d, read:%d", p_instance->p_uart->inst_idx, *p_cnt); + } + + return err_code; +} + +static ret_code_t cli_uart_write(nrf_cli_transport_t const * p_transport, + void const * p_data, + size_t length, + size_t * p_cnt) +{ + ASSERT(p_cnt); + nrf_cli_uart_internal_t * p_instance = + CONTAINER_OF(p_transport, nrf_cli_uart_internal_t, transport); + ret_code_t err_code; + *p_cnt = length; + err_code = nrf_ringbuf_cpy_put(p_instance->p_tx_ringbuf, p_data, p_cnt); + if (err_code == NRF_SUCCESS) + { + NRF_LOG_INFO("id:%d, write, req:%d, done:%d", + p_instance->p_uart->inst_idx, + length, + *p_cnt); + + if (!nrf_drv_uart_tx_in_progress(p_instance->p_uart)) + { + uint8_t * p_buf; + size_t len = 255; + if (nrf_ringbuf_get(p_instance->p_tx_ringbuf, &p_buf, &len, true) == NRF_SUCCESS) + { + NRF_LOG_INFO("id:%d, uart_tx, len:%d", p_instance->p_uart->inst_idx, len); + + err_code = nrf_drv_uart_tx(p_instance->p_uart, p_buf, len); + if (p_instance->p_cb->blocking && (err_code == NRF_SUCCESS)) + { + (void)nrf_ringbuf_free(p_instance->p_tx_ringbuf, len); + } + } + } + } + return err_code; +} + +const nrf_cli_transport_api_t nrf_cli_uart_transport_api = { + .init = cli_uart_init, + .uninit = cli_uart_uninit, + .enable = cli_uart_enable, + .read = cli_uart_read, + .write = cli_uart_write, +}; + +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.h new file mode 100644 index 0000000..1a827a4 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/cli/uart/nrf_cli_uart.h @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_CLI_UART_H__ +#define NRF_CLI_UART_H__ + +#include "nrf_cli.h" +#include "nrf_drv_uart.h" +#include "nrf_ringbuf.h" +#include "app_timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nrf_cli_uart UART command line interface transport layer + * @ingroup nrf_cli + * + * @{ + * + */ + +/** + * @brief Command line interface transport. + */ +extern const nrf_cli_transport_api_t nrf_cli_uart_transport_api; + +typedef struct nrf_cli_uart_internal_s nrf_cli_uart_internal_t; + +typedef struct { + nrf_cli_transport_handler_t handler; + void * p_context; + nrf_drv_uart_config_t uart_config; + bool timer_created; + bool blocking; +} nrf_cli_uart_internal_cb_t; + +struct nrf_cli_uart_internal_s { + nrf_cli_transport_t transport; + nrf_cli_uart_internal_cb_t * p_cb; + app_timer_id_t const * p_timer; + nrf_ringbuf_t const * p_rx_ringbuf; + nrf_ringbuf_t const * p_tx_ringbuf; + nrf_drv_uart_t const * p_uart; +}; + +typedef nrf_drv_uart_config_t nrf_cli_uart_config_t; + +/**@brief CLI UART transport definition. + * + * @param _name Name of the instance. + * @param _uart_id UART instance ID. + * @param _tx_buf_sz Size of TX ring buffer. + * @param _rx_buf_sz Size of RX ring buffer. + */ +#define NRF_CLI_UART_DEF(_name, _uart_id, _tx_buf_sz, _rx_buf_sz) \ + APP_TIMER_DEF(CONCAT_2(_name, _timer)); \ + NRF_RINGBUF_DEF(CONCAT_2(_name,_tx_ringbuf), _tx_buf_sz); \ + NRF_RINGBUF_DEF(CONCAT_2(_name,_rx_ringbuf), _rx_buf_sz); \ + static const nrf_drv_uart_t CONCAT_2(_name,_uart) = \ + NRF_DRV_UART_INSTANCE(_uart_id); \ + static nrf_cli_uart_internal_cb_t CONCAT_2(_name, _cb); \ + static const nrf_cli_uart_internal_t _name = { \ + .transport = {.p_api = &nrf_cli_uart_transport_api}, \ + .p_cb = &CONCAT_2(_name, _cb), \ + .p_timer = &CONCAT_2(_name, _timer), \ + .p_rx_ringbuf = &CONCAT_2(_name,_rx_ringbuf), \ + .p_tx_ringbuf = &CONCAT_2(_name,_tx_ringbuf), \ + .p_uart = &CONCAT_2(_name,_uart), \ + } +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_CLI_UART_H__ */ -- cgit v1.2.3