1 /*------------------------------------------------------------------------------------------------*/
2 /* UNICENS V2.1.0-3491 */
3 /* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
5 /* This program is free software: you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation, either version 2 of the License, or */
8 /* (at your option) any later version. */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU General Public License for more details. */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* You may also obtain this software under a propriety license from Microchip. */
19 /* Please contact Microchip for further information. */
20 /*------------------------------------------------------------------------------------------------*/
24 * \brief Implementation of the observer library module. The module consists of the two classes
25 * CSubject and CObserver.
27 * \cond UCS_INTERNAL_DOC
32 /*------------------------------------------------------------------------------------------------*/
34 /*------------------------------------------------------------------------------------------------*/
37 #include "ucs_trace.h"
39 /*------------------------------------------------------------------------------------------------*/
40 /* Internal Prototypes */
41 /*------------------------------------------------------------------------------------------------*/
42 static void Sub_UpdateList(CSubject *self);
43 static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr);
45 /*------------------------------------------------------------------------------------------------*/
46 /* Implementation of class CSubject */
47 /*------------------------------------------------------------------------------------------------*/
48 /*! \brief Constructor of the subject class. Initializes a subject which distributes its data to
49 * a list of observers.
50 * \param self Instance pointer
51 * \param ucs_user_ptr User reference that needs to be passed in every callback function
53 void Sub_Ctor(CSubject *self, void *ucs_user_ptr)
55 MISC_MEM_SET(self, 0, sizeof(*self));
56 self->ucs_user_ptr = ucs_user_ptr;
57 Dl_Ctor(&self->list, self->ucs_user_ptr);
58 Dl_Ctor(&self->add_list, self->ucs_user_ptr);
61 /*! \brief Adds an observer to a subjects list.
62 * \param self Instance pointer
63 * \param obs_ptr Pointer to observer instance
64 * \return \c SUB_OK: No error
65 * \return \c SUB_ALREADY_ADDED: Observer is already added
66 * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
68 Sub_Ret_t Sub_AddObserver(CSubject *self, CObserver *obs_ptr)
73 ret_val = SUB_UNKNOWN_OBSERVER;
75 else if(obs_ptr->valid != false)
77 ret_val = SUB_ALREADY_ADDED;
79 else if((self->notify != false) &&
80 (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false) &&
81 (Dl_IsNodeInList(&self->add_list, &obs_ptr->node) == false))
83 TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers < 0xFFU));
84 Dl_InsertTail(&self->add_list, &obs_ptr->node);
85 obs_ptr->valid = true;
87 ret_val = SUB_DELAYED;
89 else if((self->notify == false) && (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false))
91 TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers < 0xFFU));
93 Dl_InsertTail(&self->list, &obs_ptr->node);
94 obs_ptr->valid = true;
95 self->num_observers++;
99 ret_val = SUB_UNKNOWN_OBSERVER;
104 /*! \brief Removes an observer from a subjects list.
105 * \param self Instance pointer
106 * \param obs_ptr Pointer to observer instance
107 * \return \c SUB_OK: No error
108 * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given
109 * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
111 Sub_Ret_t Sub_RemoveObserver(CSubject *self, CObserver *obs_ptr)
116 ret_val = SUB_UNKNOWN_OBSERVER;
118 else if(obs_ptr->valid == false)
120 ret_val = SUB_UNKNOWN_OBSERVER;
122 else if((self->notify != false) &&
123 (Dl_IsNodeInList(&self->list, &obs_ptr->node) != false))
125 TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers > 0U));
126 obs_ptr->valid = false;
127 self->changed = true;
128 self->num_observers--;
129 ret_val = SUB_DELAYED;
131 else if((self->notify == false) &&
132 (Dl_Remove(&self->list, &obs_ptr->node) == DL_OK))
134 TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers > 0U));
135 self->num_observers--;
140 ret_val = SUB_UNKNOWN_OBSERVER;
145 /*! \brief Notifies all registered observers of a subject.
146 * \param self Instance pointer
147 * \param data_ptr Reference to value to distribute (optional)
149 void Sub_Notify(CSubject *self, void *data_ptr)
153 CDlNode *n_tmp = self->list.head;
155 self->changed = false;
158 CObserver *o_tmp = (CObserver *)n_tmp->data_ptr;
159 if((o_tmp->update_fptr != NULL) && (o_tmp->valid != false))
161 (o_tmp->update_fptr)(o_tmp->inst_ptr, data_ptr);
165 if(self->changed != false)
167 Sub_UpdateList(self);
169 self->notify = false;
173 /*! \brief Updates the list of observers. Delayed remove- and add-operations are processed.
174 * \param self Instance pointer
176 static void Sub_UpdateList(CSubject *self)
178 (void)Dl_Foreach(&self->list, &Sub_CheckObserver, self);
179 Dl_AppendList(&self->list, &self->add_list);
182 /*! \brief Checks if the given observer is still valid. If the observer is invalid it will be
183 * removed from the list. This function is used by the foreach loop in Sub_UpdateList().
184 * \param current_obs_ptr Reference to the current observer object
185 * \param subject_ptr Reference to the subject object
186 * \return Returns always \c false. Force to process the whole list.
188 static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr)
190 CObserver *current_obs_ptr_ = (CObserver *)current_obs_ptr;
191 CSubject *subject_ptr_ = (CSubject *)subject_ptr;
193 if(current_obs_ptr_->valid == false)
195 (void)Dl_Remove(&subject_ptr_->list, ¤t_obs_ptr_->node);
200 /*! \brief Returns the number of registered observers of a subject.
201 * \param self Instance pointer
202 * \return The number of registered observers
204 uint8_t Sub_GetNumObservers(CSubject *self)
206 return self->num_observers;
209 /*! \brief Switches all observers of the source-subject to the target-subject.
210 * \param sub_target Target subject
211 * \param sub_source Source subject
212 * \return \c SUB_OK: No error
213 * \return \c SUB_INVALID_OPERATION: Target and source must be different objects
215 Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source)
219 if(sub_target == sub_source)
221 ret_val = SUB_INVALID_OPERATION;
225 Dl_AppendList(&sub_target->list, &sub_source->list);
226 sub_target->num_observers += sub_source->num_observers;
227 sub_source->num_observers = 0U;
233 /*------------------------------------------------------------------------------------------------*/
234 /* Implementation of class CObserver */
235 /*------------------------------------------------------------------------------------------------*/
236 /*! \brief Constructor of the observer class. Initializes an observer which is notified
237 * by a corresponding subject.
238 * \param self Instance pointer
239 * \param inst_ptr Instance pointer used by update_fptr()
240 * \param update_fptr Callback function to update the observer
242 void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr)
244 MISC_MEM_SET(self, 0, sizeof(*self));
245 self->inst_ptr = inst_ptr;
246 self->update_fptr = update_fptr;
247 Dln_Ctor(&self->node, self);
250 /*------------------------------------------------------------------------------------------------*/
251 /* Implementation of class CSingleSubject */
252 /*------------------------------------------------------------------------------------------------*/
253 /*! \brief Constructor of the single-subject class. Initializes a single-subject which distributes
254 * its data to the registered single-observer.
255 * \param self Instance pointer
256 * \param ucs_user_ptr User reference that needs to be passed in every callback function
258 void Ssub_Ctor(CSingleSubject *self, void *ucs_user_ptr)
260 self->observer_ptr = NULL;
261 self->ucs_user_ptr = ucs_user_ptr;
262 self->user_mask = 0U;
265 /*! \brief Adds a single-observer to a single-subject.
266 * \param self Instance pointer
267 * \param obs_ptr Pointer to single-observer instance
268 * \return \c SSUB_OK: No error
269 * \return \c SSUB_ALREADY_ADDED: Observer is already added
270 * \return \c SSUB_UNKNOWN_OBSERVER: Given observer is not valid
272 Ssub_Ret_t Ssub_AddObserver(CSingleSubject *self, CSingleObserver *obs_ptr)
277 ret_val = SSUB_UNKNOWN_OBSERVER;
279 else if(self->observer_ptr != obs_ptr)
282 if(self->observer_ptr != NULL)
284 TR_INFO((self->ucs_user_ptr, "[SSUB]", "Observer callback has been overwritten", 0U));
288 self->observer_ptr = obs_ptr;
292 ret_val = SSUB_ALREADY_ADDED;
298 /*! \brief Removes an single-observer from a single-subject.
299 * \param self Instance pointer
301 void Ssub_RemoveObserver(CSingleSubject *self)
303 self->observer_ptr = NULL;
306 /*! \brief Notifies the registered single-observer of the given single-subject.
307 * \param self Instance pointer
308 * \param data_ptr Reference to value to distribute (optional)
309 * \param auto_remove If true the observer will be removed
311 void Ssub_Notify(CSingleSubject *self, void *data_ptr, bool auto_remove)
313 void *inst_ptr = NULL;
314 Obs_UpdateCb_t update_fptr = NULL;
315 if(self->observer_ptr != NULL)
317 inst_ptr = self->observer_ptr->inst_ptr;
318 update_fptr = self->observer_ptr->update_fptr;
319 if(auto_remove != false)
321 self->observer_ptr = NULL;
324 if(update_fptr != NULL)
326 update_fptr(inst_ptr, data_ptr);
330 /*------------------------------------------------------------------------------------------------*/
331 /* Implementation of class CSingleObserver */
332 /*------------------------------------------------------------------------------------------------*/
333 /*! \brief Constructor of the single-observer class. Initializes an single-observer which is
334 * notified by a corresponding single-subject.
335 * \param self Instance pointer
336 * \param inst_ptr Instance pointer used by update_fptr()
337 * \param update_fptr Callback function to update the observer
339 void Sobs_Ctor(CSingleObserver *self, void *inst_ptr, Sobs_UpdateCb_t update_fptr)
341 self->inst_ptr = inst_ptr;
342 self->update_fptr = update_fptr;
345 /*------------------------------------------------------------------------------------------------*/
346 /* Implementation of class CMaskedObserver */
347 /*------------------------------------------------------------------------------------------------*/
348 /*! \brief Constructor of the masked-observer class. Initializes an observer which is notified
349 * by a corresponding subject.
350 * \param self Instance pointer
351 * \param inst_ptr Instance pointer used by update_fptr()
352 * \param notification_mask Notification bitmask
353 * \param update_fptr Callback function to update the observer
355 void Mobs_Ctor(CMaskedObserver *self,
357 uint32_t notification_mask,
358 Obs_UpdateCb_t update_fptr)
360 MISC_MEM_SET(self, 0, sizeof(*self));
361 Obs_Ctor(&self->parent, inst_ptr, update_fptr);
362 self->notification_mask = notification_mask;
365 /*! \brief Sets the notification mask of a masked-observer.
366 * \param self Instance pointer
367 * \param mask Bitmask to set
369 void Mobs_SetNotificationMask(CMaskedObserver *self, uint32_t mask)
371 self->notification_mask = mask;
374 /*! \brief Retrieves the notification mask of a masked-observer.
375 * \param self Instance pointer
376 * \return Returns the current notification bitmask of the given observer
378 uint32_t Mobs_GetNotificationMask(CMaskedObserver *self)
380 return self->notification_mask;
383 /*------------------------------------------------------------------------------------------------*/
384 /* Additional methods of class CSubject used in combination with CMaskedObserver */
385 /*------------------------------------------------------------------------------------------------*/
386 /*! \brief Adds an masked-observer to a masked-subjects list.
387 * \param self Instance pointer
388 * \param obs_ptr Pointer to observer instance
389 * \return \c SUB_OK: No error
390 * \return \c SUB_ALREADY_ADDED: Observer is already added
391 * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
393 Sub_Ret_t Msub_AddObserver(CSubject *self, CMaskedObserver *obs_ptr)
395 return Sub_AddObserver(self, &obs_ptr->parent);
398 /*! \brief Removes an masked-observer from a subjects list.
399 * \param self Instance pointer
400 * \param obs_ptr Pointer to observer instance
401 * \return \c SUB_OK: No error
402 * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given
403 * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
405 Sub_Ret_t Msub_RemoveObserver(CSubject *self, CMaskedObserver *obs_ptr)
407 return Sub_RemoveObserver(self, &obs_ptr->parent);
410 /*! \brief Notifies all registered masked-observers of a masked-subject.
411 * \param self Instance pointer
412 * \param data_ptr Reference to value to distribute (optional)
413 * \param notification_mask Bitmask indicates notified observers
415 void Msub_Notify(CSubject *self, void *data_ptr, uint32_t notification_mask)
419 CDlNode *n_tmp = self->list.head;
421 self->changed = false;
424 CMaskedObserver *o_tmp = (CMaskedObserver *)n_tmp->data_ptr;
425 if( (o_tmp->parent.update_fptr != NULL) &&
426 (o_tmp->parent.valid != false) &&
427 ((o_tmp->notification_mask & notification_mask) != 0U) )
429 (o_tmp->parent.update_fptr)(o_tmp->parent.inst_ptr, data_ptr);
433 if(self->changed != false)
435 Sub_UpdateList(self);
437 self->notify = false;
446 /*------------------------------------------------------------------------------------------------*/
448 /*------------------------------------------------------------------------------------------------*/