1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Notification facilities.
9 */
10
11 #include <internal/fwk_core.h>
12 #include <internal/fwk_module.h>
13 #include <internal/fwk_notification.h>
14
15 #include <fwk_assert.h>
16 #include <fwk_attributes.h>
17 #include <fwk_dlist.h>
18 #include <fwk_event.h>
19 #include <fwk_id.h>
20 #include <fwk_interrupt.h>
21 #include <fwk_list.h>
22 #include <fwk_log.h>
23 #include <fwk_mm.h>
24 #include <fwk_module.h>
25 #include <fwk_notification.h>
26 #include <fwk_status.h>
27
28 #include <stdbool.h>
29 #include <stddef.h>
30
31 struct notification_ctx {
32 /*
33 * Queue of notification subscription structures that are free.
34 */
35 struct fwk_dlist free_subscription_dlist;
36 };
37
38 static struct notification_ctx ctx;
39
40 static const char err_msg_func[] = "[NOT] Error %d in %s";
41
42 /*
43 * Static functions
44 */
45
46 /*
47 * Get list of subscriptions for a given notification emitted by a given source
48 *
49 * \note The function assumes the validity of all its input parameters.
50 *
51 * \param notification_id Identifier of the notification.
52 * \param source_id Identifier of the emitter of the notification.
53 *
54 * \return A pointer to the doubly-linked list of subscriptions.
55 */
get_subscription_dlist(fwk_id_t notification_id,fwk_id_t source_id)56 static struct fwk_dlist *get_subscription_dlist(
57 fwk_id_t notification_id, fwk_id_t source_id)
58 {
59 struct fwk_dlist *subscription_dlist_table;
60
61 if (fwk_id_is_type(source_id, FWK_ID_TYPE_MODULE)) {
62 subscription_dlist_table =
63 fwk_module_get_ctx(source_id)->subscription_dlist_table;
64 } else {
65 subscription_dlist_table =
66 fwk_module_get_element_ctx(source_id)->subscription_dlist_table;
67 }
68
69 return &subscription_dlist_table[
70 fwk_id_get_notification_idx(notification_id)];
71 }
72
73 /*
74 * Search for a subscription with a given source and target identifier in a list
75 * of subscriptions.
76 *
77 * \note The function assumes the validity of all its input parameters.
78 *
79 * \param subscription_dlist Pointer to the doubly-linked list of subscriptions
80 * to search.
81 * \param source_id Identifier of the emitter of the notification.
82 * \param target_id Identifier of the target of the notification.
83 *
84 * \return A pointer to the subscription that has been found if any, NULL
85 * otherwise.
86 */
search_subscription(struct fwk_dlist * subscription_dlist,fwk_id_t source_id,fwk_id_t target_id)87 static struct __fwk_notification_subscription *search_subscription(
88 struct fwk_dlist *subscription_dlist,
89 fwk_id_t source_id, fwk_id_t target_id)
90 {
91 struct fwk_dlist_node *node;
92 struct __fwk_notification_subscription *subscription;
93
94 for (node = fwk_list_head(subscription_dlist); node != NULL;
95 node = fwk_list_next(subscription_dlist, node)) {
96 subscription = FWK_LIST_GET(node,
97 struct __fwk_notification_subscription, dlist_node);
98
99 if (fwk_id_is_equal(subscription->source_id, source_id) &&
100 fwk_id_is_equal(subscription->target_id, target_id)) {
101 return subscription;
102 }
103 }
104
105 return NULL;
106 }
107
108 /*
109 * Send all the notifications associated with a notification event.
110 *
111 * \note The function assumes the validity of all its input parameters. The
112 * function is a sub-routine of 'fwk_notification_notify'.
113 *
114 * \param notification_event Pointer to the notification event.
115 * \param[out] count The number of notifications being sent.
116 */
send_notifications(struct fwk_event * notification_event,unsigned int * count)117 static void send_notifications(struct fwk_event *notification_event,
118 unsigned int *count)
119 {
120 int status;
121 struct fwk_dlist *subscription_dlist;
122 struct fwk_dlist_node *node;
123 struct __fwk_notification_subscription *subscription;
124
125 subscription_dlist = get_subscription_dlist(notification_event->id,
126 notification_event->source_id);
127 notification_event->is_response = false;
128 notification_event->is_notification = true;
129
130 for (node = fwk_list_head(subscription_dlist); node != NULL;
131 node = fwk_list_next(subscription_dlist, node)) {
132 subscription = FWK_LIST_GET(node,
133 struct __fwk_notification_subscription, dlist_node);
134
135 if (!fwk_id_is_equal(
136 subscription->source_id, notification_event->source_id)) {
137 continue;
138 }
139
140 notification_event->target_id = subscription->target_id;
141
142 status = __fwk_put_notification(notification_event);
143 if (status == FWK_SUCCESS) {
144 (*count)++;
145 }
146 }
147 }
148
149 /*
150 * Private interface functions
151 */
152
fwk_notification_init(void)153 static FWK_CONSTRUCTOR void fwk_notification_init(void)
154 {
155 static struct __fwk_notification_subscription
156 subscriptions[FMW_NOTIFICATION_MAX];
157
158 unsigned int i;
159
160 /* All the subscription structures are free to be used */
161 fwk_list_init(&ctx.free_subscription_dlist);
162
163 for (i = 0; i < FMW_NOTIFICATION_MAX; i++) {
164 fwk_list_push_tail(
165 &ctx.free_subscription_dlist, &subscriptions[i].dlist_node);
166 }
167 }
168
__fwk_notification_reset(void)169 void __fwk_notification_reset(void)
170 {
171 fwk_notification_init();
172 }
173
174 /*
175 * Public interface functions
176 */
177
fwk_notification_subscribe(fwk_id_t notification_id,fwk_id_t source_id,fwk_id_t target_id)178 int fwk_notification_subscribe(fwk_id_t notification_id, fwk_id_t source_id,
179 fwk_id_t target_id)
180 {
181 int status;
182 unsigned int flags;
183 struct fwk_dlist *subscription_dlist;
184 struct __fwk_notification_subscription *subscription;
185
186 if (fwk_is_interrupt_context()) {
187 status = FWK_E_HANDLER;
188 goto error;
189 }
190
191 if (!fwk_module_is_valid_notification_id(notification_id) ||
192 !fwk_module_is_valid_entity_id(source_id) ||
193 !fwk_module_is_valid_entity_id(target_id) ||
194 !fwk_id_is_equal(fwk_id_build_module_id(notification_id),
195 fwk_id_build_module_id(source_id))) {
196 status = FWK_E_PARAM;
197 goto error;
198 }
199
200 subscription_dlist = get_subscription_dlist(notification_id, source_id);
201 if (search_subscription(subscription_dlist, source_id, target_id) != NULL) {
202 status = FWK_E_STATE;
203 goto error;
204 }
205
206 subscription = FWK_LIST_GET(
207 fwk_list_pop_head(&ctx.free_subscription_dlist),
208 struct __fwk_notification_subscription, dlist_node);
209
210 if (subscription == NULL) {
211 status = FWK_E_NOMEM;
212 fwk_unexpected();
213 goto error;
214 }
215
216 subscription->source_id = source_id;
217 subscription->target_id = target_id;
218
219 flags = fwk_interrupt_global_disable();
220 fwk_list_push_tail(subscription_dlist, &subscription->dlist_node);
221 (void)fwk_interrupt_global_enable(flags);
222
223 return FWK_SUCCESS;
224
225 error:
226 FWK_LOG_CRIT(err_msg_func, status, __func__);
227 return status;
228 }
229
fwk_notification_unsubscribe(fwk_id_t notification_id,fwk_id_t source_id,fwk_id_t target_id)230 int fwk_notification_unsubscribe(fwk_id_t notification_id, fwk_id_t source_id,
231 fwk_id_t target_id)
232 {
233 int status;
234 unsigned int flags;
235 struct fwk_dlist *subscription_dlist;
236 struct __fwk_notification_subscription *subscription;
237
238 if (fwk_is_interrupt_context()) {
239 status = FWK_E_HANDLER;
240 goto error;
241 }
242
243 if (!(fwk_module_is_valid_notification_id(notification_id)) ||
244 !(fwk_module_is_valid_entity_id(source_id)) ||
245 !(fwk_module_is_valid_entity_id(target_id)) ||
246 !fwk_id_is_equal(fwk_id_build_module_id(notification_id),
247 fwk_id_build_module_id(source_id))) {
248 status = FWK_E_PARAM;
249 goto error;
250 }
251
252 subscription_dlist = get_subscription_dlist(notification_id, source_id);
253 subscription = search_subscription(subscription_dlist,
254 source_id, target_id);
255 if (subscription == NULL) {
256 status = FWK_E_STATE;
257 goto error;
258 }
259
260 flags = fwk_interrupt_global_disable();
261 fwk_list_remove(subscription_dlist, &subscription->dlist_node);
262 (void)fwk_interrupt_global_enable(flags);
263 fwk_list_push_tail(&ctx.free_subscription_dlist, &subscription->dlist_node);
264
265 return FWK_SUCCESS;
266
267 error:
268 FWK_LOG_CRIT(err_msg_func, status, __func__);
269 return status;
270 }
271
fwk_notification_notify(struct fwk_event * notification_event,unsigned int * count)272 int fwk_notification_notify(struct fwk_event *notification_event,
273 unsigned int *count)
274 {
275 int status;
276 const struct fwk_event *current_event;
277
278 if ((notification_event == NULL) || (count == NULL)) {
279 return FWK_E_PARAM;
280 }
281
282 if (fwk_is_interrupt_context()) {
283 if (!fwk_module_is_valid_entity_id(notification_event->source_id)) {
284 status = FWK_E_PARAM;
285 goto error;
286 }
287 } else {
288 current_event = __fwk_get_current_event();
289
290 if ((current_event != NULL) &&
291 (!fwk_module_is_valid_entity_id(notification_event->source_id))) {
292 /*
293 * The source_id provided is not valid, use the identifier of the
294 * target for the current event.
295 */
296 notification_event->source_id = current_event->target_id;
297 }
298 }
299
300 if (!fwk_module_is_valid_notification_id(notification_event->id) ||
301 (fwk_id_get_module_idx(notification_event->id) !=
302 fwk_id_get_module_idx(notification_event->source_id))) {
303 status = FWK_E_PARAM;
304 goto error;
305 }
306
307 *count = 0;
308 send_notifications(notification_event, count);
309
310 return FWK_SUCCESS;
311
312 error:
313 FWK_LOG_CRIT(err_msg_func, status, __func__);
314 return status;
315 }
316