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 static sys_slist_t nrfs_backend_info_cb_slist = SYS_SLIST_STATIC_INIT(&nrfs_backend_info_cb_slist);
58 
59 /**
60  * @brief nrfs backend error handler
61  *
62  * @param error_id The id of an error to handle.
63  * @param error additional error code if needed, if not needed use 0.
64  * @param fatal true if fatal error and needs special handling
65  */
nrfs_backend_error_handler(enum nrfs_backend_error error_id,int error,bool fatal)66 __weak void nrfs_backend_error_handler(enum nrfs_backend_error error_id, int error, bool fatal)
67 {
68 	switch (error_id) {
69 	case NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG:
70 		LOG_ERR("Received data is too long. Config error.");
71 		break;
72 
73 	case NRFS_ERROR_NO_DATA_RECEIVED:
74 		LOG_ERR("No data in received message!");
75 		break;
76 
77 	case NRFS_ERROR_IPC_OPEN_INSTANCE:
78 		LOG_ERR("IPC open instance failure with error: %d", error);
79 		break;
80 
81 	case NRFS_ERROR_IPC_REGISTER_ENDPOINT:
82 		LOG_ERR("IPC register endpoint failure with error: %d", error);
83 		break;
84 
85 	case NRFS_ERROR_SEND_DATA_FROM_QUEUE:
86 		LOG_ERR("IPC backend sent data failed.");
87 		break;
88 
89 	default:
90 		LOG_ERR("Undefined error id: %d, error cause: %d", error_id, error);
91 		break;
92 	}
93 
94 	if (fatal) {
95 		nrfs_backend_fatal_error_handler(error_id);
96 	}
97 }
98 
ipc_sysctrl_ept_bound(void * priv)99 static void ipc_sysctrl_ept_bound(void *priv)
100 {
101 	LOG_DBG("Bound to sysctrl.");
102 	k_event_post(&ipc_connected_event, IPC_INIT_DONE_EVENT);
103 	atomic_set(&ipc_cpusys_channel_config.status, CONNECTED);
104 
105 	if (k_msgq_num_used_get(&ipc_transmit_msgq) > 0) {
106 		k_work_submit(&backend_send_work);
107 	}
108 
109 	struct nrfs_backend_bound_info_subs *subs;
110 
111 	SYS_SLIST_FOR_EACH_CONTAINER(&nrfs_backend_info_cb_slist, subs, node) {
112 		subs->cb();
113 	}
114 }
115 
ipc_sysctrl_ept_recv(const void * data,size_t size,void * priv)116 static void ipc_sysctrl_ept_recv(const void *data, size_t size, void *priv)
117 {
118 	struct ipc_data_packet rx_data;
119 
120 	__ASSERT(size <= MAX_PACKET_DATA_SIZE, "Received data is too long. Config error.");
121 	if (size <= MAX_PACKET_DATA_SIZE) {
122 		rx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
123 		rx_data.size	   = size;
124 		if (data) {
125 			memcpy(rx_data.data, (uint8_t *)data, size);
126 			nrfs_dispatcher_notify(&rx_data.data, rx_data.size);
127 		} else {
128 			nrfs_backend_error_handler(NRFS_ERROR_NO_DATA_RECEIVED, 0, false);
129 		}
130 	} else {
131 		nrfs_backend_error_handler(NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG, 0, true);
132 	}
133 }
134 
135 /**
136  * @brief This function will try to send data directly using ipc service
137  *        In case of errors it will retry if configured.
138  *
139  * @param message Pointer to the buffer to send.
140  * @param size Number of bytes to send.
141  * @retval NRFS_SUCCESS Message sent successfully.
142  * @retval NRFS_ERR_IPC Backend returned error during message sending.
143  */
nrfs_backend_try_send_directly_over_ipc_service(void * message,size_t size)144 static nrfs_err_t nrfs_backend_try_send_directly_over_ipc_service(void *message, size_t size)
145 {
146 	size_t retry_count = CONFIG_NRFS_SEND_RETRY_MAX_COUNT + 1;
147 	int ret = 0;
148 
149 	do {
150 		ret = ipc_service_send(&ipc_cpusys_channel_config.ipc_ept, message, size);
151 		if (ret < (int)size) {
152 			k_usleep(CONFIG_NRFS_SEND_RETRY_DELAY);
153 		} else {
154 			return NRFS_SUCCESS;
155 		}
156 	} while (--retry_count);
157 
158 	return NRFS_ERR_IPC;
159 }
160 
nrfs_backend_send_work(struct k_work * item)161 static void nrfs_backend_send_work(struct k_work *item)
162 {
163 	struct ipc_data_packet data_to_send;
164 
165 	LOG_DBG("Sending data from workqueue");
166 	while (k_msgq_get(&ipc_transmit_msgq, &data_to_send, K_NO_WAIT) == 0) {
167 
168 		nrfs_err_t ret = nrfs_backend_try_send_directly_over_ipc_service(&data_to_send.data,
169 										 data_to_send.size);
170 
171 		if (ret != NRFS_SUCCESS) {
172 			nrfs_backend_error_handler(NRFS_ERROR_SEND_DATA_FROM_QUEUE, 0, true);
173 		}
174 	}
175 }
176 
177 /**
178  *  @brief Initialize ipc channel
179  *
180  *  @return -EINVAL when instance configuration is invalid.
181  *  @return -EIO when no backend is registered.
182  *  @return -EALREADY when the instance is already opened (or being opened).
183  *  @return -EBUSY when the instance is busy.
184  *  @return 0 on success
185  */
ipc_channel_init(void)186 static int ipc_channel_init(void)
187 {
188 	struct ipc_channel_config *ch_cfg;
189 	int ret = 0;
190 
191 	k_work_init(&backend_send_work, nrfs_backend_send_work);
192 	ch_cfg = &ipc_cpusys_channel_config;
193 
194 	ret = ipc_service_open_instance(ch_cfg->ipc_instance);
195 	if ((ret < 0) && (ret != -EALREADY)) {
196 		nrfs_backend_error_handler(NRFS_ERROR_IPC_OPEN_INSTANCE, ret, false);
197 		return ret;
198 	}
199 
200 	LOG_DBG("ipc_service_open_instance() done.");
201 
202 	ret = ipc_service_register_endpoint(ch_cfg->ipc_instance,
203 					    &ch_cfg->ipc_ept,
204 					    ch_cfg->endpoint_config);
205 	if (ret < 0) {
206 		nrfs_backend_error_handler(NRFS_ERROR_IPC_REGISTER_ENDPOINT, ret, false);
207 		return ret;
208 	}
209 
210 	LOG_DBG("ipc_service_register_endpoint() done.");
211 
212 	return ret;
213 }
214 
nrfs_backend_send(void * message,size_t size)215 nrfs_err_t nrfs_backend_send(void *message, size_t size)
216 {
217 	return nrfs_backend_send_ex(message, size, K_NO_WAIT, false);
218 }
219 
nrfs_backend_send_ex(void * message,size_t size,k_timeout_t timeout,bool high_prio)220 nrfs_err_t nrfs_backend_send_ex(void *message, size_t size, k_timeout_t timeout, bool high_prio)
221 {
222 	if (!k_is_in_isr() && nrfs_backend_connected()) {
223 		return nrfs_backend_try_send_directly_over_ipc_service(message, size);
224 
225 	} else if (size <= MAX_PACKET_DATA_SIZE) {
226 		int err;
227 		struct ipc_data_packet tx_data;
228 
229 		tx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
230 		tx_data.size	   = size;
231 		memcpy(tx_data.data, (uint8_t *)message, size);
232 
233 		err = k_msgq_put(&ipc_transmit_msgq, &tx_data, timeout);
234 		if (err) {
235 			return NRFS_ERR_IPC;
236 		}
237 
238 		if (nrfs_backend_connected()) {
239 			err = k_work_submit(&backend_send_work);
240 		}
241 
242 		return err >= 0 ? 0 : NRFS_ERR_IPC;
243 	}
244 
245 	LOG_ERR("Trying to send %d bytes where max is %d.", size, MAX_PACKET_DATA_SIZE);
246 
247 	return NRFS_ERR_IPC;
248 }
249 
nrfs_backend_connected(void)250 bool nrfs_backend_connected(void)
251 {
252 	return atomic_get(&ipc_cpusys_channel_config.status) == CONNECTED;
253 }
254 
nrfs_backend_wait_for_connection(k_timeout_t timeout)255 int nrfs_backend_wait_for_connection(k_timeout_t timeout)
256 {
257 	uint32_t events;
258 
259 	if (nrfs_backend_connected()) {
260 		return 0;
261 	}
262 
263 	events = k_event_wait(&ipc_connected_event, IPC_INIT_DONE_EVENT, false, timeout);
264 
265 	return (events == IPC_INIT_DONE_EVENT ? 0 : (-EAGAIN));
266 }
267 
nrfs_backend_register_bound_subscribe(struct nrfs_backend_bound_info_subs * subs,nrfs_backend_bound_info_cb_t cb)268 void nrfs_backend_register_bound_subscribe(struct nrfs_backend_bound_info_subs *subs,
269 					   nrfs_backend_bound_info_cb_t cb)
270 {
271 	if (cb) {
272 		subs->cb = cb;
273 		sys_slist_append(&nrfs_backend_info_cb_slist, &subs->node);
274 	}
275 }
276 
nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id)277 __weak void nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id)
278 {
279 	LOG_ERR("Fatal error: %d rebooting...", error_id);
280 	sys_reboot(SYS_REBOOT_WARM);
281 }
282 
283 SYS_INIT(ipc_channel_init, POST_KERNEL, CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO);
284