1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "nrfs_backend_ipc_service.h"
8
9 #include <internal/nrfs_backend.h>
10 #include <internal/nrfs_dispatcher.h>
11
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/reboot.h>
14 #include <zephyr/sys_clock.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/init.h>
17
18 LOG_MODULE_REGISTER(NRFS_BACKEND, CONFIG_NRFS_BACKEND_LOG_LEVEL);
19
20 #define MAX_PACKET_DATA_SIZE (CONFIG_NRFS_MAX_BACKEND_PACKET_SIZE)
21
22 K_MSGQ_DEFINE(ipc_transmit_msgq, sizeof(struct ipc_data_packet),
23 CONFIG_NRFS_BACKEND_TX_MSG_QUEUE_SIZE, 4);
24
25 static struct k_work backend_send_work;
26
27 static void ipc_sysctrl_ept_bound(void *priv);
28 static void ipc_sysctrl_ept_recv(const void *data, size_t size, void *priv);
29
30 static K_EVENT_DEFINE(ipc_connected_event);
31
32 #define IPC_INIT_DONE_EVENT (0x01)
33
34 struct ipc_channel_config {
35 const struct device *ipc_instance;
36 struct ipc_ept_cfg *endpoint_config;
37 struct ipc_ept ipc_ept;
38 atomic_t status;
39 bool enabled;
40 };
41
42 static struct ipc_ept_cfg ipc_sysctrl_ept_cfg = {
43 .name = "ipc_to_sysctrl",
44 .cb = {
45 .bound = ipc_sysctrl_ept_bound,
46 .received = ipc_sysctrl_ept_recv,
47 },
48 };
49
50 static struct ipc_channel_config ipc_cpusys_channel_config = {
51 .ipc_instance = DEVICE_DT_GET(DT_ALIAS(ipc_to_cpusys)),
52 .endpoint_config = &ipc_sysctrl_ept_cfg,
53 .status = ATOMIC_INIT(NOT_CONNECTED),
54 .enabled = true
55 };
56
57 /**
58 * @brief nrfs backend error handler
59 *
60 * @param error_id The id of an error to handle.
61 * @param error additional error code if needed, if not needed use 0.
62 * @param fatal true if fatal error and needs special handling
63 */
nrfs_backend_error_handler(enum nrfs_backend_error error_id,int error,bool fatal)64 __weak void nrfs_backend_error_handler(enum nrfs_backend_error error_id, int error, bool fatal)
65 {
66 switch (error_id) {
67 case NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG:
68 LOG_ERR("Received data is too long. Config error.");
69 break;
70
71 case NRFS_ERROR_NO_DATA_RECEIVED:
72 LOG_ERR("No data in received message!");
73 break;
74
75 case NRFS_ERROR_IPC_OPEN_INSTANCE:
76 LOG_ERR("IPC open instance failure with error: %d", error);
77 break;
78
79 case NRFS_ERROR_IPC_REGISTER_ENDPOINT:
80 LOG_ERR("IPC register endpoint failure with error: %d", error);
81 break;
82
83 default:
84 LOG_ERR("Undefined error id: %d, error cause: %d", error_id, error);
85 break;
86 }
87
88 if (fatal) {
89 nrfs_backend_fatal_error_handler(error_id);
90 }
91 }
92
ipc_sysctrl_ept_bound(void * priv)93 static void ipc_sysctrl_ept_bound(void *priv)
94 {
95 LOG_DBG("Bound to sysctrl.");
96 k_event_post(&ipc_connected_event, IPC_INIT_DONE_EVENT);
97 atomic_set(&ipc_cpusys_channel_config.status, CONNECTED);
98
99 if (k_msgq_num_used_get(&ipc_transmit_msgq) > 0) {
100 k_work_submit(&backend_send_work);
101 }
102 }
103
ipc_sysctrl_ept_recv(const void * data,size_t size,void * priv)104 static void ipc_sysctrl_ept_recv(const void *data, size_t size, void *priv)
105 {
106 struct ipc_data_packet rx_data;
107
108 __ASSERT(size <= MAX_PACKET_DATA_SIZE, "Received data is too long. Config error.");
109 if (size <= MAX_PACKET_DATA_SIZE) {
110 rx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
111 rx_data.size = size;
112 if (data) {
113 memcpy(rx_data.data, (uint8_t *)data, size);
114 nrfs_dispatcher_notify(&rx_data.data, rx_data.size);
115 } else {
116 nrfs_backend_error_handler(NRFS_ERROR_NO_DATA_RECEIVED, 0, false);
117 }
118 } else {
119 nrfs_backend_error_handler(NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG, 0, true);
120 }
121 }
122
nrfs_backend_send_work(struct k_work * item)123 static void nrfs_backend_send_work(struct k_work *item)
124 {
125 struct ipc_data_packet data_to_send;
126
127 LOG_DBG("Sending data from workqueue");
128 while (k_msgq_get(&ipc_transmit_msgq, &data_to_send, K_NO_WAIT) == 0) {
129 ipc_service_send(&ipc_cpusys_channel_config.ipc_ept, &data_to_send.data,
130 data_to_send.size);
131 }
132 }
133
134 /**
135 * @brief Initialize ipc channel
136 *
137 * @return -EINVAL when instance configuration is invalid.
138 * @return -EIO when no backend is registered.
139 * @return -EALREADY when the instance is already opened (or being opened).
140 * @return -EBUSY when the instance is busy.
141 * @return 0 on success
142 */
ipc_channel_init(void)143 static int ipc_channel_init(void)
144 {
145 struct ipc_channel_config *ch_cfg;
146 int ret = 0;
147
148 k_work_init(&backend_send_work, nrfs_backend_send_work);
149 ch_cfg = &ipc_cpusys_channel_config;
150
151 ret = ipc_service_open_instance(ch_cfg->ipc_instance);
152 if ((ret < 0) && (ret != -EALREADY)) {
153 nrfs_backend_error_handler(NRFS_ERROR_IPC_OPEN_INSTANCE, ret, false);
154 return ret;
155 }
156
157 LOG_DBG("ipc_service_open_instance() done.");
158
159 ret = ipc_service_register_endpoint(ch_cfg->ipc_instance,
160 &ch_cfg->ipc_ept,
161 ch_cfg->endpoint_config);
162 if (ret < 0) {
163 nrfs_backend_error_handler(NRFS_ERROR_IPC_REGISTER_ENDPOINT, ret, false);
164 return ret;
165 }
166
167 LOG_DBG("ipc_service_register_endpoint() done.");
168
169 return ret;
170 }
171
nrfs_backend_send(void * message,size_t size)172 nrfs_err_t nrfs_backend_send(void *message, size_t size)
173 {
174 return nrfs_backend_send_ex(message, size, K_NO_WAIT, false);
175 }
176
nrfs_backend_send_ex(void * message,size_t size,k_timeout_t timeout,bool high_prio)177 nrfs_err_t nrfs_backend_send_ex(void *message, size_t size, k_timeout_t timeout, bool high_prio)
178 {
179 if (!k_is_in_isr() && nrfs_backend_connected()) {
180 return ipc_service_send(&ipc_cpusys_channel_config.ipc_ept, message, size) ?
181 NRFS_SUCCESS : NRFS_ERR_IPC;
182 } else if (size <= MAX_PACKET_DATA_SIZE) {
183 int err;
184 struct ipc_data_packet tx_data;
185
186 tx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
187 tx_data.size = size;
188 memcpy(tx_data.data, (uint8_t *)message, size);
189
190 err = k_msgq_put(&ipc_transmit_msgq, &tx_data, timeout);
191 if (err) {
192 return NRFS_ERR_IPC;
193 }
194
195 if (nrfs_backend_connected()) {
196 err = k_work_submit(&backend_send_work);
197 }
198
199 return err >= 0 ? 0 : NRFS_ERR_IPC;
200 }
201
202 LOG_ERR("Trying to send %d bytes where max is %d.", size, MAX_PACKET_DATA_SIZE);
203
204 return NRFS_ERR_IPC;
205 }
206
nrfs_backend_connected(void)207 bool nrfs_backend_connected(void)
208 {
209 return atomic_get(&ipc_cpusys_channel_config.status) == CONNECTED;
210 }
211
nrfs_backend_wait_for_connection(k_timeout_t timeout)212 int nrfs_backend_wait_for_connection(k_timeout_t timeout)
213 {
214 uint32_t events;
215
216 if (nrfs_backend_connected()) {
217 return 0;
218 }
219
220 events = k_event_wait(&ipc_connected_event, IPC_INIT_DONE_EVENT, false, timeout);
221
222 return (events == IPC_INIT_DONE_EVENT ? 0 : (-EAGAIN));
223 }
224
nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id)225 __weak void nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id)
226 {
227 LOG_ERR("Fatal error: %d rebooting...", error_id);
228 sys_reboot(SYS_REBOOT_WARM);
229 }
230
231 SYS_INIT(ipc_channel_init, POST_KERNEL, CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO);
232