1 /*
2 * Copyright (c) 2020 - 2021 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/ipc/ipc_service.h>
9 #include <zephyr/device.h>
10 #include <zephyr/logging/log.h>
11
12 #if defined(CONFIG_SOC_NRF5340_CPUAPP)
13 #include <nrf53_cpunet_mgmt.h>
14 #include <hal/nrf_spu.h>
15 #endif
16
17 #include "nrf_802154.h"
18 #include "nrf_802154_spinel_backend_callouts.h"
19 #include "nrf_802154_serialization_error.h"
20 #include "../../spinel_base/spinel.h"
21 #include "../../src/include/nrf_802154_spinel.h"
22
23 #if defined(CONFIG_SOC_NRF5340_CPUAPP)
24 #include <nrf53_cpunet_mgmt.h>
25 #endif
26
27 #define LOG_LEVEL LOG_LEVEL_INFO
28 #define LOG_MODULE_NAME spinel_ipc_backend
29 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
30
31 #define IPC_BOUND_TIMEOUT_IN_MS K_MSEC(1000)
32
33 static K_SEM_DEFINE(edp_bound_sem, 0, 1);
34 static struct ipc_ept ept;
35
endpoint_bound(void * priv)36 static void endpoint_bound(void *priv)
37 {
38 k_sem_give(&edp_bound_sem);
39 }
40
endpoint_received(const void * data,size_t len,void * priv)41 static void endpoint_received(const void *data, size_t len, void *priv)
42 {
43 LOG_DBG("Received message of %u bytes.", len);
44
45 nrf_802154_spinel_encoded_packet_received(data, len);
46 }
47
48 static struct ipc_ept_cfg ept_cfg = {
49 .name = "nrf_802154_spinel",
50 .cb = {
51 .bound = endpoint_bound,
52 .received = endpoint_received
53 },
54 };
55
nrf_802154_backend_init(void)56 nrf_802154_ser_err_t nrf_802154_backend_init(void)
57 {
58 const struct device *const ipc_instance =
59 DEVICE_DT_GET(DT_CHOSEN(nordic_802154_spinel_ipc));
60 int err;
61
62 #if defined(CONFIG_SOC_NRF5340_CPUAPP)
63
64 #if !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)
65 /* Retain nRF5340 Network MCU in Secure domain (bus
66 * accesses by Network MCU will have Secure attribute set).
67 */
68 nrf_spu_extdomain_set((NRF_SPU_Type *)DT_REG_ADDR(DT_NODELABEL(spu)), 0, true, false);
69 #endif /* !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) */
70
71 nrf53_cpunet_enable(true);
72 #endif
73
74 err = ipc_service_open_instance(ipc_instance);
75 if (err < 0 && err != -EALREADY) {
76 LOG_ERR("Failed to open IPC instance: %d", err);
77 return NRF_802154_SERIALIZATION_ERROR_INIT_FAILED;
78 }
79
80 err = ipc_service_register_endpoint(ipc_instance, &ept, &ept_cfg);
81 if (err < 0) {
82 LOG_ERR("Failed to register IPC endpoint: %d", err);
83 return NRF_802154_SERIALIZATION_ERROR_INIT_FAILED;
84 }
85
86 err = k_sem_take(&edp_bound_sem, IPC_BOUND_TIMEOUT_IN_MS);
87 if (err < 0) {
88 LOG_ERR("IPC endpoint bind timed out");
89 return NRF_802154_SERIALIZATION_ERROR_INIT_FAILED;
90 }
91
92 return NRF_802154_SERIALIZATION_ERROR_OK;
93 }
94
95 /* Send packet thread details */
96 #define SEND_THREAD_STACK_SIZE 1024
97
98 /* Make the ring buffer long enough to hold all notifications that the driver can produce */
99 #define RING_BUFFER_LEN (NRF_802154_MAX_PENDING_NOTIFICATIONS + 1)
100
101 static K_SEM_DEFINE(send_sem, 0, RING_BUFFER_LEN);
102 K_THREAD_STACK_DEFINE(send_thread_stack, SEND_THREAD_STACK_SIZE);
103 struct k_thread send_thread_data;
104
105 struct ringbuffer {
106 uint32_t len;
107 uint8_t data[NRF_802154_SPINEL_FRAME_MAX_SIZE];
108 };
109
110 static struct ringbuffer ring_buffer[RING_BUFFER_LEN];
111 static uint8_t rd_idx;
112 static uint8_t wr_idx;
113
get_rb_idx_plus_1(uint8_t i)114 static uint8_t get_rb_idx_plus_1(uint8_t i)
115 {
116 return (i + 1) % RING_BUFFER_LEN;
117 }
118
spinel_packet_from_thread_send(const uint8_t * data,uint32_t len)119 static nrf_802154_ser_err_t spinel_packet_from_thread_send(const uint8_t *data, uint32_t len)
120 {
121 if (get_rb_idx_plus_1(wr_idx) == rd_idx) {
122 LOG_ERR("No spinel buffer available to send a new packet");
123 return NRF_802154_SERIALIZATION_ERROR_BACKEND_FAILURE;
124 }
125
126 LOG_DBG("Scheduling %u bytes for send thread", len);
127
128 struct ringbuffer *buf = &ring_buffer[wr_idx];
129
130 wr_idx = get_rb_idx_plus_1(wr_idx);
131 buf->len = len;
132 memcpy(buf->data, data, len);
133
134 k_sem_give(&send_sem);
135 return (nrf_802154_ser_err_t)len;
136 }
137
spinel_packet_send_thread_fn(void * arg1,void * arg2,void * arg3)138 static void spinel_packet_send_thread_fn(void *arg1, void *arg2, void *arg3)
139 {
140 LOG_DBG("Spinel backend send thread started");
141 while (true) {
142 k_sem_take(&send_sem, K_FOREVER);
143 struct ringbuffer *buf = &ring_buffer[rd_idx];
144 uint32_t expected_ret = buf->len;
145
146 LOG_DBG("Sending %u bytes from send thread", buf->len);
147 int ret = ipc_service_send(&ept, buf->data, buf->len);
148
149 rd_idx = get_rb_idx_plus_1(rd_idx);
150
151 if (ret != expected_ret) {
152 nrf_802154_ser_err_data_t err = {
153 .reason = NRF_802154_SERIALIZATION_ERROR_BACKEND_FAILURE,
154 };
155
156 nrf_802154_serialization_error(&err);
157 }
158 }
159 }
160
161 K_THREAD_DEFINE(spinel_packet_send_thread, SEND_THREAD_STACK_SIZE,
162 spinel_packet_send_thread_fn, NULL, NULL, NULL, K_PRIO_COOP(0), 0, 0);
163
nrf_802154_spinel_encoded_packet_send(const void * p_data,size_t data_len)164 nrf_802154_ser_err_t nrf_802154_spinel_encoded_packet_send(const void *p_data,
165 size_t data_len)
166 {
167 if (k_is_in_isr()) {
168 return spinel_packet_from_thread_send(p_data, data_len);
169 }
170
171 LOG_DBG("Sending %u bytes directly", data_len);
172 int ret = ipc_service_send(&ept, p_data, data_len);
173
174 return ((ret < 0) ? NRF_802154_SERIALIZATION_ERROR_BACKEND_FAILURE
175 : (nrf_802154_ser_err_t) ret);
176 }
177