1 /*
2  * Copyright (c) 2020 - 2021 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "nrf_802154_spinel_response_notifier.h"
8 
9 #include <assert.h>
10 #include <string.h>
11 
12 #include <zephyr/logging/log.h>
13 #include <zephyr/kernel.h>
14 
15 #include "../spinel_base/spinel.h"
16 #include "nrf_802154_spinel_log.h"
17 
18 #define LOG_LEVEL LOG_LEVEL_INFO
19 #define LOG_MODULE_NAME spinel_ipc_backend_rsp_ntf
20 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
21 
22 /* TODO: Current implementation doesn't support reentrancy or multiple awaits. */
23 
24 /**
25  * Valid property IDs are from 0 to SPINEL_MAX_UINT_PACKED (2097151).
26  * We use UINT32_MAX which is out of this range to indicate that we are
27  * not waiting for any property.
28  */
29 #define AWAITED_PROPERTY_NONE UINT32_MAX
30 
31 struct spinel_notify_buff_internal {
32 	nrf_802154_spinel_notify_buff_t buff;
33 	bool                            free;
34 };
35 
36 static K_SEM_DEFINE(notify_sem, 0, 1);
37 static struct k_mutex await_mutex;
38 
39 static struct spinel_notify_buff_internal notify_buff;
40 
41 static spinel_prop_key_t awaited_property = AWAITED_PROPERTY_NONE;
42 
nrf_802154_spinel_response_notifier_init(void)43 void nrf_802154_spinel_response_notifier_init(void)
44 {
45 	notify_buff.free = true;
46 	k_mutex_init(&await_mutex);
47 }
48 
nrf_802154_spinel_response_notifier_lock_before_request(spinel_prop_key_t property)49 void nrf_802154_spinel_response_notifier_lock_before_request(spinel_prop_key_t property)
50 {
51 	/*
52 	 * Only one thread can await response.
53 	 * TODO: Implement matching responses to requests and allow multiple threads
54 	 *       to await simultaneously
55 	 */
56 
57 	LOG_DBG("Locking response notifier");
58 	int ret = k_mutex_lock(&await_mutex, K_FOREVER);
59 
60 	assert(ret == 0);
61 	(void)ret;
62 
63 	assert(awaited_property == AWAITED_PROPERTY_NONE);
64 	awaited_property = property;
65 }
66 
nrf_802154_spinel_response_notifier_property_await(uint32_t timeout)67 nrf_802154_spinel_notify_buff_t *nrf_802154_spinel_response_notifier_property_await(
68 	uint32_t timeout)
69 {
70 	nrf_802154_spinel_notify_buff_t *result = NULL;
71 
72 	k_timeout_t k_timeout;
73 
74 	if (timeout == 0) {
75 		k_timeout = K_NO_WAIT;
76 	} else if (timeout == SPINEL_RESPONSE_NOTIFIER_INF_TIMEOUT) {
77 		k_timeout = K_FOREVER;
78 	} else {
79 		k_timeout = K_MSEC(timeout);
80 	}
81 
82 	if (k_sem_take(&notify_sem, k_timeout) == 0) {
83 		result = &notify_buff.buff;
84 	} else {
85 		LOG_ERR("No response within timeout %u", timeout);
86 	}
87 
88 	return result;
89 }
90 
nrf_802154_spinel_response_notifier_free(nrf_802154_spinel_notify_buff_t * p_notify)91 void nrf_802154_spinel_response_notifier_free(nrf_802154_spinel_notify_buff_t *p_notify)
92 {
93 	struct spinel_notify_buff_internal *p_notify_buff_free;
94 
95 	int ret;
96 	p_notify_buff_free = CONTAINER_OF(p_notify,
97 					  struct spinel_notify_buff_internal,
98 					  buff);
99 
100 	assert(p_notify_buff_free == &notify_buff);
101 
102 	LOG_DBG("Unlocking response notifier");
103 
104 	p_notify_buff_free->free = true;
105 
106 	ret = k_mutex_unlock(&await_mutex);
107 	assert(ret == 0);
108 	(void)ret;
109 }
110 
nrf_802154_spinel_response_notifier_property_notify(spinel_prop_key_t property,const void * p_data,size_t data_len)111 void nrf_802154_spinel_response_notifier_property_notify(spinel_prop_key_t property,
112 					      const void       *p_data,
113 					      size_t            data_len)
114 {
115 	if (property == awaited_property) {
116 		assert(notify_buff.free);
117 
118 		notify_buff.free = false;
119 		awaited_property = AWAITED_PROPERTY_NONE;
120 
121 		assert(data_len <= sizeof(notify_buff.buff.data));
122 
123 		memcpy(notify_buff.buff.data, p_data, data_len);
124 		notify_buff.buff.data_len = data_len;
125 
126 		k_sem_give(&notify_sem);
127 	} else {
128 		/* TODO: Determine if this is an error condition. */
129 		NRF_802154_SPINEL_LOG_RAW("Received property that noone is waiting for\n");
130 	}
131 }
132