1 /*
2  * Copyright (c) 2022 Rodrigo Peixoto <rodrigopex@gmail.com>
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include <zephyr/kernel.h>
6 #include <zephyr/logging/log.h>
7 #include <zephyr/zbus/zbus.h>
8 
9 LOG_MODULE_DECLARE(zbus, CONFIG_ZBUS_LOG_LEVEL);
10 
zbus_chan_add_obs(const struct zbus_channel * chan,const struct zbus_observer * obs,k_timeout_t timeout)11 int zbus_chan_add_obs(const struct zbus_channel *chan, const struct zbus_observer *obs,
12 		      k_timeout_t timeout)
13 {
14 	int err;
15 	struct zbus_observer_node *obs_nd, *tmp;
16 	struct zbus_channel_observation *observation;
17 
18 	_ZBUS_ASSERT(!k_is_in_isr(), "ISR blocked");
19 	_ZBUS_ASSERT(chan != NULL, "chan is required");
20 	_ZBUS_ASSERT(obs != NULL, "obs is required");
21 
22 	err = k_sem_take(&chan->data->sem, timeout);
23 	if (err) {
24 		return err;
25 	}
26 
27 	for (int16_t i = chan->data->observers_start_idx, limit = chan->data->observers_end_idx;
28 	     i < limit; ++i) {
29 		STRUCT_SECTION_GET(zbus_channel_observation, i, &observation);
30 
31 		__ASSERT(observation != NULL, "observation must be not NULL");
32 
33 		if (observation->obs == obs) {
34 			k_sem_give(&chan->data->sem);
35 
36 			return -EEXIST;
37 		}
38 	}
39 
40 	/* Check if the observer is already a runtime observer */
41 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&chan->data->observers, obs_nd, tmp, node) {
42 		if (obs_nd->obs == obs) {
43 			k_sem_give(&chan->data->sem);
44 
45 			return -EALREADY;
46 		}
47 	}
48 
49 	struct zbus_observer_node *new_obs_nd = k_malloc(sizeof(struct zbus_observer_node));
50 
51 	if (new_obs_nd == NULL) {
52 		LOG_ERR("Could not allocate observer node the heap is full!");
53 
54 		k_sem_give(&chan->data->sem);
55 
56 		return -ENOMEM;
57 	}
58 
59 	new_obs_nd->obs = obs;
60 
61 	sys_slist_append(&chan->data->observers, &new_obs_nd->node);
62 
63 	k_sem_give(&chan->data->sem);
64 
65 	return 0;
66 }
67 
zbus_chan_rm_obs(const struct zbus_channel * chan,const struct zbus_observer * obs,k_timeout_t timeout)68 int zbus_chan_rm_obs(const struct zbus_channel *chan, const struct zbus_observer *obs,
69 		     k_timeout_t timeout)
70 {
71 	int err;
72 	struct zbus_observer_node *obs_nd, *tmp;
73 	struct zbus_observer_node *prev_obs_nd = NULL;
74 
75 	_ZBUS_ASSERT(!k_is_in_isr(), "ISR blocked");
76 	_ZBUS_ASSERT(chan != NULL, "chan is required");
77 	_ZBUS_ASSERT(obs != NULL, "obs is required");
78 
79 	err = k_sem_take(&chan->data->sem, timeout);
80 	if (err) {
81 		return err;
82 	}
83 
84 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&chan->data->observers, obs_nd, tmp, node) {
85 		if (obs_nd->obs == obs) {
86 			sys_slist_remove(&chan->data->observers, &prev_obs_nd->node, &obs_nd->node);
87 
88 			k_free(obs_nd);
89 
90 			k_sem_give(&chan->data->sem);
91 
92 			return 0;
93 		}
94 
95 		prev_obs_nd = obs_nd;
96 	}
97 
98 	k_sem_give(&chan->data->sem);
99 
100 	return -ENODATA;
101 }
102