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(¬ify_sem, k_timeout) == 0) {
83 result = ¬ify_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 == ¬ify_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(¬ify_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