| /* |
| * Copyright (c) 2020 - 2021 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/zephyr.h> |
| #include <zephyr/ipc/ipc_service.h> |
| #include <zephyr/device.h> |
| #include <zephyr/logging/log.h> |
| |
| #include "nrf_802154_spinel_backend_callouts.h" |
| #include "nrf_802154_serialization_error.h" |
| #include "../../spinel_base/spinel.h" |
| #include "../../src/include/nrf_802154_spinel.h" |
| |
| #define LOG_LEVEL LOG_LEVEL_INFO |
| #define LOG_MODULE_NAME spinel_ipc_backend |
| LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
| |
| #define IPC_BOUND_TIMEOUT_IN_MS K_MSEC(1000) |
| |
| static K_SEM_DEFINE(edp_bound_sem, 0, 1); |
| static struct ipc_ept ept; |
| |
| static void endpoint_bound(void *priv) |
| { |
| k_sem_give(&edp_bound_sem); |
| } |
| |
| static void endpoint_received(const void *data, size_t len, void *priv) |
| { |
| LOG_DBG("Received message of %u bytes.", len); |
| |
| nrf_802154_spinel_encoded_packet_received(data, len); |
| } |
| |
| static struct ipc_ept_cfg ept_cfg = { |
| .name = "nrf_802154_spinel", |
| .cb = { |
| .bound = endpoint_bound, |
| .received = endpoint_received |
| }, |
| }; |
| |
| nrf_802154_ser_err_t nrf_802154_backend_init(void) |
| { |
| const struct device *ipc_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0)); |
| int err; |
| |
| err = ipc_service_open_instance(ipc_instance); |
| if (err < 0 && err != -EALREADY) { |
| LOG_ERR("Failed to open IPC instance: %d", err); |
| return NRF_802154_SERIALIZATION_ERROR_INIT_FAILED; |
| } |
| |
| err = ipc_service_register_endpoint(ipc_instance, &ept, &ept_cfg); |
| if (err < 0) { |
| LOG_ERR("Failed to register IPC endpoint: %d", err); |
| return NRF_802154_SERIALIZATION_ERROR_INIT_FAILED; |
| } |
| |
| err = k_sem_take(&edp_bound_sem, IPC_BOUND_TIMEOUT_IN_MS); |
| if (err < 0) { |
| LOG_ERR("IPC endpoint bind timed out"); |
| return NRF_802154_SERIALIZATION_ERROR_INIT_FAILED; |
| } |
| |
| return NRF_802154_SERIALIZATION_ERROR_OK; |
| } |
| |
| /* Send packet thread details */ |
| #define RING_BUFFER_LEN 16 |
| #define SEND_THREAD_STACK_SIZE 1024 |
| |
| static K_SEM_DEFINE(send_sem, 0, RING_BUFFER_LEN); |
| K_THREAD_STACK_DEFINE(send_thread_stack, SEND_THREAD_STACK_SIZE); |
| struct k_thread send_thread_data; |
| |
| struct ringbuffer { |
| uint32_t len; |
| uint8_t data[NRF_802154_SPINEL_FRAME_MAX_SIZE]; |
| }; |
| |
| static struct ringbuffer ring_buffer[RING_BUFFER_LEN]; |
| static uint8_t rd_idx; |
| static uint8_t wr_idx; |
| |
| static uint8_t get_rb_idx_plus_1(uint8_t i) |
| { |
| return (i + 1) % RING_BUFFER_LEN; |
| } |
| |
| static nrf_802154_ser_err_t spinel_packet_from_thread_send(const uint8_t *data, uint32_t len) |
| { |
| if (get_rb_idx_plus_1(wr_idx) == rd_idx) { |
| LOG_ERR("No spinel buffer available to send a new packet"); |
| return NRF_802154_SERIALIZATION_ERROR_BACKEND_FAILURE; |
| } |
| |
| LOG_DBG("Scheduling %u bytes for send thread", len); |
| |
| struct ringbuffer *buf = &ring_buffer[wr_idx]; |
| |
| wr_idx = get_rb_idx_plus_1(wr_idx); |
| buf->len = len; |
| memcpy(buf->data, data, len); |
| |
| k_sem_give(&send_sem); |
| return (nrf_802154_ser_err_t)len; |
| } |
| |
| static void spinel_packet_send_thread_fn(void *arg1, void *arg2, void *arg3) |
| { |
| LOG_DBG("Spinel backend send thread started"); |
| while (true) { |
| k_sem_take(&send_sem, K_FOREVER); |
| struct ringbuffer *buf = &ring_buffer[rd_idx]; |
| uint32_t expected_ret = buf->len; |
| |
| LOG_DBG("Sending %u bytes from send thread", buf->len); |
| int ret = ipc_service_send(&ept, buf->data, buf->len); |
| |
| rd_idx = get_rb_idx_plus_1(rd_idx); |
| |
| if (ret != expected_ret) { |
| nrf_802154_ser_err_data_t err = { |
| .reason = NRF_802154_SERIALIZATION_ERROR_BACKEND_FAILURE, |
| }; |
| |
| nrf_802154_serialization_error(&err); |
| } |
| } |
| } |
| |
| K_THREAD_DEFINE(spinel_packet_send_thread, SEND_THREAD_STACK_SIZE, |
| spinel_packet_send_thread_fn, NULL, NULL, NULL, K_PRIO_COOP(0), 0, 0); |
| |
| nrf_802154_ser_err_t nrf_802154_spinel_encoded_packet_send(const void *p_data, |
| size_t data_len) |
| { |
| if (k_is_in_isr()) { |
| return spinel_packet_from_thread_send(p_data, data_len); |
| } |
| |
| LOG_DBG("Sending %u bytes directly", data_len); |
| int ret = ipc_service_send(&ept, p_data, data_len); |
| |
| return ((ret < 0) ? NRF_802154_SERIALIZATION_ERROR_BACKEND_FAILURE |
| : (nrf_802154_ser_err_t) ret); |
| } |