diff options
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/examples/802_15_4/wireless_uart/fsm.c')
-rw-r--r-- | thirdparty/nRF5_SDK_15.0.0_a53641a/examples/802_15_4/wireless_uart/fsm.c | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/examples/802_15_4/wireless_uart/fsm.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/examples/802_15_4/wireless_uart/fsm.c new file mode 100644 index 0000000..843cd26 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/examples/802_15_4/wireless_uart/fsm.c @@ -0,0 +1,601 @@ +/** + * Copyright (c) 2016 - 2018 Nordic Semiconductor ASA and Luxoft Global Operations Gmbh. + * + * 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 "config.h" + +#include "fsm.h" +#include "uart.h" +#if (CONFIG_SECURE == 1) +#include "security.h" +#endif +#include "sys_fsm.h" +#include "sys_utils.h" +#include "sys_memory_manager.h" +#include "sys_debug.h" +#include "sys_task_scheduler.h" +#include "boards.h" +#include "nrf_gpio.h" +#include <stdint.h> +#include <stddef.h> + +typedef struct +{ + mac_payload_descriptor_t payload_descr; + uint8_t length; +} fsm_frame_item_t; + +typedef enum +{ + S_INITIAL, + S_SETTING_CH, + S_SETTING_SHORT_ADDR, + S_SETTING_EXT_ADDR, + S_SETTING_PAN_ID, + S_SETTING_RX_ON_IDLE, + S_SETTING_SECURITY, + S_WORKING, +} fsm_states_t; + +typedef enum +{ + GU_SET_SUCCESS, + GU_SEND_SUCCESS, + GU_UART_TX_DATA_AVAILABLE, + GU_RADIO_TX_IDLE, + GU_UART_TX_IDLE, +} fsm_guards_t; + +typedef enum +{ + A_CHANNEL_SET, + A_SHORT_ADDR_SET, + A_EXT_ADDR_SET, + A_PAN_ID_SET, + A_RX_ON_IDLE_SET, + A_CONFIG_COMPLETE, + A_RADIO_TX_START, + A_RADIO_TX_RESTART, + A_UART_TX_START, + A_UART_TX_IDLE_SET, +#if (CONFIG_SECURE == 1) + A_SECURITY_SET +#endif +} fsm_actions_t; + +typedef void (* fsm_action_t)(void *); +typedef bool (* fsm_guard_t)(void *); +typedef void (* app_action_t)(void); + +typedef void (* store_data_handler_t)(fsm_event_data_t *); + +// prototypes +static void fsm_action(sys_fsm_action_id_t action_id, void * p_data); +static bool fsm_guard(sys_fsm_guard_id_t guard_id, void * p_data); + +static void a_channel_set(void * p_data); +static void a_short_addr_set(void * p_data); +static void a_ext_addr_set(void * p_data); +static void a_pan_id_set(void * p_data); +static void a_rx_on_idle_set(void * p_data); +static void a_config_complete(void * p_data); +static void a_radio_tx_start(void * p_data); +static void a_radio_tx_restart(void * p_data); +static void a_uart_tx_start(void * p_data); +static void a_uart_tx_idle_set(void * p_data); +#if (CONFIG_SECURE == 1) +static void a_security_set(void * p_data); +#endif + +static bool gu_set_success(void * p_data); +static bool gu_send_success(void * p_data); +static bool gu_uart_tx_data_available(void * p_data); +static bool gu_radio_tx_idle(void * p_data); +static bool gu_uart_tx_idle(void * p_data); + +static bool frames_available(void); + +static void mlme_set_conf_store(fsm_event_data_t * p_data); +static void mcps_data_ind_store(fsm_event_data_t * p_data); +static void mcps_data_conf_store(fsm_event_data_t * p_data); + +static void mcps_data_conf(mcps_data_conf_t * conf); +static void mlme_set_conf(mlme_set_conf_t * conf); + +static const sys_fsm_transition_t m_transition_table[] = +{ + SYS_FSM_STATE ( S_WORKING ), + SYS_FSM_TRANSITION( E_SET_DONE, SYS_FSM_ALWAYS, A_CONFIG_COMPLETE, SYS_FSM_SAME_STATE ), + SYS_FSM_TRANSITION( E_UART_TX_DONE, GU_UART_TX_DATA_AVAILABLE, A_UART_TX_START, SYS_FSM_SAME_STATE ), + SYS_FSM_TRANSITION( E_UART_TX_DONE, SYS_FSM_OTHERWISE, A_UART_TX_IDLE_SET, SYS_FSM_SAME_STATE ), + SYS_FSM_TRANSITION( E_UART_RX_DONE, GU_RADIO_TX_IDLE, A_RADIO_TX_START, SYS_FSM_SAME_STATE ), + SYS_FSM_TRANSITION( E_RADIO_RX_DONE, GU_UART_TX_IDLE, A_UART_TX_START, SYS_FSM_SAME_STATE ), + SYS_FSM_TRANSITION( E_RADIO_TX_DONE, GU_SEND_SUCCESS, A_RADIO_TX_START, SYS_FSM_SAME_STATE ), + SYS_FSM_TRANSITION( E_RADIO_TX_DONE, SYS_FSM_OTHERWISE, A_RADIO_TX_RESTART, SYS_FSM_SAME_STATE ), + + SYS_FSM_STATE ( S_INITIAL ), + SYS_FSM_TRANSITION( E_START_CONFIG, SYS_FSM_ALWAYS, A_CHANNEL_SET, S_SETTING_CH ), + + SYS_FSM_STATE ( S_SETTING_CH ), + SYS_FSM_TRANSITION( E_SET_DONE, GU_SET_SUCCESS, A_SHORT_ADDR_SET, S_SETTING_SHORT_ADDR ), + + SYS_FSM_STATE ( S_SETTING_SHORT_ADDR ), + SYS_FSM_TRANSITION( E_SET_DONE, GU_SET_SUCCESS, A_EXT_ADDR_SET, S_SETTING_EXT_ADDR ), + + SYS_FSM_STATE ( S_SETTING_EXT_ADDR ), + SYS_FSM_TRANSITION( E_SET_DONE, GU_SET_SUCCESS, A_PAN_ID_SET, S_SETTING_PAN_ID ), +#if (CONFIG_SECURE == 1) + SYS_FSM_STATE ( S_SETTING_PAN_ID ), + SYS_FSM_TRANSITION( E_SET_DONE, GU_SET_SUCCESS, A_RX_ON_IDLE_SET, S_SETTING_RX_ON_IDLE ), + + SYS_FSM_STATE ( S_SETTING_RX_ON_IDLE ), + SYS_FSM_TRANSITION( E_SET_DONE, GU_SET_SUCCESS, A_SECURITY_SET, S_WORKING ), +#else + SYS_FSM_STATE ( S_SETTING_PAN_ID ), + SYS_FSM_TRANSITION( E_SET_DONE, GU_SET_SUCCESS, A_RX_ON_IDLE_SET, S_WORKING ), +#endif +}; + +static const sys_fsm_const_descriptor_t m_fsm_descriptor = +{ + FSM_DEBUG_NAME("wi-art fsm") + + /* Pointer to the transition table.*/ + .transition_table = m_transition_table, + + /* Number of transitions in the transition table. */ + .transitions_amount = ARRAY_SIZE(m_transition_table), + + /* Initial state ID. */ + .initial_state = S_INITIAL, + + /* Pointer to the guard condition function */ + .guard = fsm_guard, + + /* Pointer to the action function */ + .action = fsm_action +}; + +static sys_fsm_t m_fsm; +static mlme_set_req_t * mp_set_req; +static mlme_set_conf_t m_set_conf; +static mcps_data_req_t m_data_req; +static mcps_data_conf_t m_data_conf; +static bool m_uart_tx_idle = true; +static bool m_radio_tx_idle = true; +static uint8_t m_radio_tx_buffer[PHY_MAX_PACKET_SIZE + MAC_MAX_MHR_SIZE]; +static sequence_number_t tx_sequence_number = 0; +#if (CONFIG_SECURE == 1) +static uint8_t m_radio_tx_buffer_shadow[MAX_MSDU_SIZE + MAX_APP_SEQUENCE_NUMBER_SIZE]; +#endif +static uint8_t m_events = 0; +static store_data_handler_t event_data_store[EVENTS_AMOUNT] = +{ + NULL, // E_START_CONFIG + mlme_set_conf_store, // E_SET_DONE + mcps_data_ind_store, // E_RADIO_RX_DONE + mcps_data_conf_store, // E_RADIO_TX_DONE + NULL, // E_UART_RX_DONE + NULL, // E_UART_TX_DONE + NULL, // E_SECURITY_SET +}; +static fsm_frame_item_t m_radio_rx_array[CONFIG_POOL_SIZE / (PHY_MAX_PACKET_SIZE + PHY_MAX_HEADER_SIZE) + 1U]; +static size_t m_radio_rx_array_read_index = 0; +static size_t m_radio_rx_array_write_index = 0; + +static const fsm_guard_t m_fsm_guards[] = +{ + gu_set_success, + gu_send_success, + gu_uart_tx_data_available, + gu_radio_tx_idle, + gu_uart_tx_idle, +}; + +static const fsm_action_t m_fsm_actions[] = +{ + a_channel_set, + a_short_addr_set, + a_ext_addr_set, + a_pan_id_set, + a_rx_on_idle_set, + a_config_complete, + a_radio_tx_start, + a_radio_tx_restart, + a_uart_tx_start, + a_uart_tx_idle_set, +#if (CONFIG_SECURE == 1) + a_security_set, +#endif +}; + +static void fsm_action(sys_fsm_action_id_t action_id, void * p_data) +{ + m_fsm_actions[action_id](p_data); +} + +static bool fsm_guard(sys_fsm_guard_id_t guard_id, void * p_data) +{ + return m_fsm_guards[guard_id](p_data); +} + +/* guards */ + +static bool gu_set_success(void * p_data) +{ + if (m_set_conf.status != MAC_SUCCESS) + { + ASSERT_INFO(0, "Failed to set attribute %02x\n", m_set_conf.pib_attribute.mlme_id); + return false; + } + + return true; +} + +static bool gu_send_success(void * p_data) +{ + return m_data_conf.status == MAC_SUCCESS; +} + +static bool gu_uart_tx_data_available(void * p_data) +{ + return frames_available(); +} + +static bool gu_radio_tx_idle(void * p_data) +{ + return m_radio_tx_idle; +} + +static bool gu_uart_tx_idle(void * p_data) +{ + return m_uart_tx_idle; +} + + +/* actions */ + +static void a_channel_set(void * p_data) +{ + const pib_id_t pib_id = + { + .plme_id = PHY_CURRENT_CHANNEL_ID, + }; + mp_set_req = (mlme_set_req_t *)sys_mm_alloc(sizeof(mlme_set_req_t)); + ASSERT(mp_set_req != NULL); + mp_set_req->value = (uint8_t *)sys_mm_alloc(mlme_pib_attr_size_calc(pib_id, 0)); + ASSERT(mp_set_req->value != NULL); + + uint8_t channel = CONFIG_TEST_CHANNEL; + mp_set_req->pib_attribute.plme_id = PHY_CURRENT_CHANNEL_ID; + mp_set_req->pib_attribute_idx = 0; + memcpy(mp_set_req->value, &channel, sizeof(channel)); + mlme_set_req(mp_set_req, mlme_set_conf); +} + +static void a_short_addr_set(void * p_data) +{ + const pib_id_t pib_id = + { + .mlme_id = MAC_SHORT_ADDRESS, + }; + mp_set_req = (mlme_set_req_t *)sys_mm_alloc(sizeof(mlme_set_req_t)); + ASSERT(mp_set_req != NULL); + mp_set_req->value = (uint8_t *)sys_mm_alloc(mlme_pib_attr_size_calc(pib_id, 0)); + ASSERT(mp_set_req->value != NULL); + + mp_set_req->pib_attribute.mlme_id = MAC_SHORT_ADDRESS; + uint16_t address = CONFIG_DEVICE_SHORT_ADDRESS; + memcpy(mp_set_req->value, &address, sizeof(uint16_t)); + mp_set_req->pib_attribute_idx = 0; + mlme_set_req(mp_set_req, mlme_set_conf); +} + +static void a_ext_addr_set(void * p_data) +{ + const pib_id_t pib_id = + { + .mlme_id = MAC_EXTENDED_ADDRESS, + }; + mp_set_req = (mlme_set_req_t *)sys_mm_alloc(sizeof(mlme_set_req_t)); + ASSERT(mp_set_req != NULL); + mp_set_req->value = (uint8_t *)sys_mm_alloc(mlme_pib_attr_size_calc(pib_id, 0)); + ASSERT(mp_set_req->value != NULL); + + mp_set_req->pib_attribute.mlme_id = MAC_EXTENDED_ADDRESS; + + // sum is for device address uniqueness + uint64_t address = CONFIG_IEEE_ADDRESS + CONFIG_DEVICE_SHORT_ADDRESS; + memcpy(mp_set_req->value, &address, sizeof(uint64_t)); + mp_set_req->pib_attribute_idx = 0; + mlme_set_req(mp_set_req, mlme_set_conf); +} + +static void a_pan_id_set(void * p_data) +{ + const pib_id_t pib_id = + { + .mlme_id = MAC_PAN_ID, + }; + mp_set_req = (mlme_set_req_t *)sys_mm_alloc(sizeof(mlme_set_req_t)); + ASSERT(mp_set_req != NULL); + mp_set_req->value = (uint8_t *)sys_mm_alloc(mlme_pib_attr_size_calc(pib_id, 0)); + ASSERT(mp_set_req->value != NULL); + + mp_set_req->pib_attribute.mlme_id = MAC_PAN_ID; + + uint16_t panid = CONFIG_PAN_ID; + memcpy(mp_set_req->value, &panid, sizeof(uint16_t)); + mp_set_req->pib_attribute_idx = 0; + mlme_set_req(mp_set_req, mlme_set_conf); +} + +static void a_rx_on_idle_set(void * p_data) +{ + const pib_id_t pib_id = + { + .mlme_id = MAC_RX_ON_WHEN_IDLE, + }; + mp_set_req = (mlme_set_req_t *)sys_mm_alloc(sizeof(mlme_set_req_t)); + ASSERT(mp_set_req != NULL); + mp_set_req->value = (uint8_t *)sys_mm_alloc(mlme_pib_attr_size_calc(pib_id, 0)); + ASSERT(mp_set_req->value != NULL); + + mp_set_req->pib_attribute.mlme_id = MAC_RX_ON_WHEN_IDLE; + uint8_t rx_on = 1; + memcpy(mp_set_req->value, &rx_on, sizeof(uint8_t)); + mlme_set_req(mp_set_req, mlme_set_conf); +} + +static void a_radio_tx_start(void * p_data) +{ + m_radio_tx_idle = false; + + const hal_uart_descriptor_t * p_descr = uart_descr_get(); + size_t sz = hal_uart_read_buffer_size_get(p_descr); + + LEDS_OFF(BIT(CONFIG_UPSTREAM_PIN)); + + if (sz > MAX_MSDU_SIZE) + { + sz = MAX_MSDU_SIZE; + } + + if (sz > 0) + { + hal_uart_read(p_descr, &m_radio_tx_buffer[PAYLOAD_START_POSITION], sz); + memcpy(&m_radio_tx_buffer[COUNTER_POSITION], &tx_sequence_number, MAX_APP_SEQUENCE_NUMBER_SIZE); +#if (CONFIG_SECURE == 1) + memcpy(m_radio_tx_buffer_shadow, &m_radio_tx_buffer[COUNTER_POSITION], + sz + MAX_APP_SEQUENCE_NUMBER_SIZE); +#endif + + m_data_req.dst_addr_mode = MAC_ADDR_SHORT; + m_data_req.dst_addr.short_address = CONFIG_OTHER_ADDRESS; + m_data_req.dst_pan_id = CONFIG_PAN_ID; + m_data_req.src_addr_mode = MAC_ADDR_SHORT; + m_data_req.msdu = (uint8_t *)&m_radio_tx_buffer[MAC_MAX_MHR_SIZE]; + m_data_req.msdu_length = sz + MAX_APP_SEQUENCE_NUMBER_SIZE; + m_data_req.msdu_handle++; + m_data_req.tx_options.ack = true; + m_data_req.tx_options.gts = false; + m_data_req.tx_options.indirect = false; +#if (CONFIG_SECURE == 1) + m_data_req.security_level = CONFIG_DATA_SECURITY_LEVEL; + m_data_req.key_id_mode = 0; +#endif + mcps_data_req(&m_data_req, mcps_data_conf); + + LEDS_ON(BIT(CONFIG_UPSTREAM_PIN)); + } + else + { + m_radio_tx_idle = true; + } +} + +static void a_config_complete(void * p_data) +{ + LEDS_ON(BIT(CONFIG_INIT_DONE_PIN)); +} + +static void a_radio_tx_restart(void * p_data) +{ +#if (CONFIG_SECURE == 1) + /** This is necessary because data in m_radio_tx_buffer had been already ciphered. */ + memcpy(&m_radio_tx_buffer[COUNTER_POSITION], m_radio_tx_buffer_shadow, m_data_req.msdu_length); +#endif + m_data_req.msdu_handle++; + mcps_data_req(&m_data_req, mcps_data_conf); + LEDS_ON(BIT(CONFIG_UPSTREAM_PIN)); +} + +static bool frames_available(void) +{ + return m_radio_rx_array_read_index != m_radio_rx_array_write_index; +} + +static void frame_store(fsm_frame_item_t * p_frame) +{ + m_radio_rx_array[m_radio_rx_array_write_index++] = *p_frame; + if (m_radio_rx_array_write_index == ARRAY_SIZE(m_radio_rx_array)) + { + m_radio_rx_array_write_index = 0; + } + ASSERT(frames_available()); +} + +static fsm_frame_item_t * next_frame_get(void) +{ + ASSERT(frames_available()); + fsm_frame_item_t * result = &m_radio_rx_array[m_radio_rx_array_read_index++]; + if (m_radio_rx_array_read_index == ARRAY_SIZE(m_radio_rx_array)) + { + m_radio_rx_array_read_index = 0; + } + return result; +} + +static void a_uart_tx_start(void * p_data) +{ + ASSERT(frames_available()); + + fsm_frame_item_t * p_fsm_frame = next_frame_get(); + const hal_uart_descriptor_t * p_uart_descr = uart_descr_get(); + + hal_uart_write(p_uart_descr, + p_fsm_frame->payload_descr.p_payload + MAX_APP_SEQUENCE_NUMBER_SIZE, + p_fsm_frame->length - MAX_APP_SEQUENCE_NUMBER_SIZE); + + mac_mem_msdu_free(&p_fsm_frame->payload_descr); + + m_uart_tx_idle = false; + + LEDS_ON(BIT(CONFIG_DOWNSTREAM_PIN)); + + (void)p_data; +} + +static void a_uart_tx_idle_set(void * p_data) +{ + m_uart_tx_idle = true; +} + +#if (CONFIG_SECURE == 1) +static void a_security_set(void * p_data) +{ + security_tables_init(); + fsm_event_post(E_SECURITY_SET, NULL); +} +#endif + + +/* callback functions */ + +static void mlme_set_conf(mlme_set_conf_t * conf) +{ + fsm_event_data_t data = + { + .mlme_set = conf, + }; + fsm_event_post(E_SET_DONE, &data); + + sys_mm_free(mp_set_req->value); + sys_mm_free(mp_set_req); +} + +static void mcps_data_conf(mcps_data_conf_t * conf) +{ + fsm_event_data_t data = + { + .mcps_data_conf = conf + }; + if (conf->status == MAC_SUCCESS) + { + tx_sequence_number++; + } + fsm_event_post(E_RADIO_TX_DONE, &data); + LEDS_OFF(BIT(CONFIG_UPSTREAM_PIN)); +} + + +/* storing functions */ + +static void mlme_set_conf_store(fsm_event_data_t * p_data) +{ + if (p_data != NULL) + { + m_set_conf = *p_data->mlme_set; + } +} + +static void mcps_data_ind_store(fsm_event_data_t * p_data) +{ + mcps_data_ind_t * p_frame = p_data->mcps_data_ind; + fsm_frame_item_t fsm_frame; + + fsm_frame.payload_descr = p_frame->msdu; + fsm_frame.length = p_frame->msdu_length; + frame_store(&fsm_frame); +} + +static void mcps_data_conf_store(fsm_event_data_t * p_data) +{ + m_data_conf = *p_data->mcps_data_conf; +} + +/* interface functions */ + +void fsm_init(void) +{ + sys_fsm_init(&m_fsm, &m_fsm_descriptor); + m_radio_rx_array_read_index = 0; + m_radio_rx_array_write_index = 0; +} + +void fsm_event_post(fsm_events_t event, fsm_event_data_t * p_data) +{ + m_events |= BIT(event); + if (event_data_store[event] != NULL) + { + event_data_store[event](p_data); + } + + sys_task_post(APP_TASK_ID); +} + +void fsm_event_scheduler_run(void) +{ + for (uint8_t event_id = 0; event_id < EVENTS_AMOUNT; event_id++) + { + uint8_t mask = BIT(event_id); + if (m_events & mask) + { + m_events ^= mask; + sys_fsm_event_post(&m_fsm, (fsm_events_t)event_id, NULL); + } + } + + if (m_events) + { + sys_task_post(APP_TASK_ID); + } +} + |