Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 : : * Copyright (c) 2020 Nordic Semiconductor ASA
4 : : *
5 : : * SPDX-License-Identifier: Apache-2.0
6 : : */
7 : :
8 : : #include <kernel.h>
9 : : #include <sys/onoff.h>
10 : : #include <stdio.h>
11 : :
12 : : #define SERVICE_REFS_MAX UINT16_MAX
13 : :
14 : : /* Confirm consistency of public flags with private flags */
15 : : BUILD_ASSERT((ONOFF_FLAG_ERROR | ONOFF_FLAG_ONOFF | ONOFF_FLAG_TRANSITION)
16 : : < BIT(3));
17 : :
18 : : #define ONOFF_FLAG_PROCESSING BIT(3)
19 : : #define ONOFF_FLAG_COMPLETE BIT(4)
20 : : #define ONOFF_FLAG_RECHECK BIT(5)
21 : :
22 : : /* These symbols in the ONOFF_FLAGS namespace identify bits in
23 : : * onoff_manager::flags that indicate the state of the machine. The
24 : : * bits are manipulated by process_event() under lock, and actions
25 : : * cued by bit values are executed outside of lock within
26 : : * process_event().
27 : : *
28 : : * * ERROR indicates that the machine is in an error state. When
29 : : * this bit is set ONOFF will be cleared.
30 : : * * ONOFF indicates whether the target/current state is off (clear)
31 : : * or on (set).
32 : : * * TRANSITION indicates whether a service transition function is in
33 : : * progress. It combines with ONOFF to identify start and stop
34 : : * transitions, and with ERROR to identify a reset transition.
35 : : * * PROCESSING indicates that the process_event() loop is active. It
36 : : * is used to defer initiation of transitions and other complex
37 : : * state changes while invoking notifications associated with a
38 : : * state transition. This bounds the depth by limiting
39 : : * active process_event() call stacks to two instances. State changes
40 : : * initiated by a nested call will be executed when control returns
41 : : * to the parent call.
42 : : * * COMPLETE indicates that a transition completion notification has
43 : : * been received. This flag is set in the notification, and cleared
44 : : * by process_events() which is invoked from the notification. In
45 : : * the case of nested process_events() the processing is deferred to
46 : : * the top invocation.
47 : : * * RECHECK indicates that a state transition has completed but
48 : : * process_events() must re-check the overall state to confirm no
49 : : * additional transitions are required. This is used to simplify the
50 : : * logic when, for example, a request is received during a
51 : : * transition to off, which means that when the transition completes
52 : : * a transition to on must be initiated if the request is still
53 : : * present. Transition to ON with no remaining requests similarly
54 : : * triggers a recheck.
55 : : */
56 : :
57 : : /* Identify the events that can trigger state changes, as well as an
58 : : * internal state used when processing deferred actions.
59 : : */
60 : : enum event_type {
61 : : /* No-op event: used to process deferred changes.
62 : : *
63 : : * This event is local to the process loop.
64 : : */
65 : : EVT_NOP,
66 : :
67 : : /* Completion of a service transition.
68 : : *
69 : : * This event is triggered by the transition notify callback.
70 : : * It can be received only when the machine is in a transition
71 : : * state (TO-ON, TO-OFF, or RESETTING).
72 : : */
73 : : EVT_COMPLETE,
74 : :
75 : : /* Reassess whether a transition from a stable state is needed.
76 : : *
77 : : * This event causes:
78 : : * * a start from OFF when there are clients;
79 : : * * a stop from ON when there are no clients;
80 : : * * a reset from ERROR when there are clients.
81 : : *
82 : : * The client list can change while the manager lock is
83 : : * released (e.g. during client and monitor notifications and
84 : : * transition initiations), so this event records the
85 : : * potential for these state changes, and process_event() ...
86 : : *
87 : : */
88 : : EVT_RECHECK,
89 : :
90 : : /* Transition to on.
91 : : *
92 : : * This is synthesized from EVT_RECHECK in a non-nested
93 : : * process_event() when state OFF is confirmed with a
94 : : * non-empty client (request) list.
95 : : */
96 : : EVT_START,
97 : :
98 : : /* Transition to off.
99 : : *
100 : : * This is synthesized from EVT_RECHECK in a non-nested
101 : : * process_event() when state ON is confirmed with a
102 : : * zero reference count.
103 : : */
104 : : EVT_STOP,
105 : :
106 : : /* Transition to resetting.
107 : : *
108 : : * This is synthesized from EVT_RECHECK in a non-nested
109 : : * process_event() when state ERROR is confirmed with a
110 : : * non-empty client (reset) list.
111 : : */
112 : : EVT_RESET,
113 : : };
114 : :
115 : 2 : static void set_state(struct onoff_manager *mgr,
116 : : uint32_t state)
117 : : {
118 : 2 : mgr->flags = (state & ONOFF_STATE_MASK)
119 : 2 : | (mgr->flags & ~ONOFF_STATE_MASK);
120 : 2 : }
121 : :
122 : 1 : static int validate_args(const struct onoff_manager *mgr,
123 : : struct onoff_client *cli)
124 : : {
125 [ + - - + ]: 1 : if ((mgr == NULL) || (cli == NULL)) {
126 : 0 : return -EINVAL;
127 : : }
128 : :
129 : 1 : int rv = sys_notify_validate(&cli->notify);
130 : :
131 [ + - ]: 1 : if ((rv == 0)
132 : 1 : && ((cli->notify.flags
133 [ - + ]: 1 : & ~BIT_MASK(ONOFF_CLIENT_EXTENSION_POS)) != 0)) {
134 : 0 : rv = -EINVAL;
135 : : }
136 : :
137 : 1 : return rv;
138 : : }
139 : :
140 : 4 : int onoff_manager_init(struct onoff_manager *mgr,
141 : : const struct onoff_transitions *transitions)
142 : : {
143 [ + - ]: 4 : if ((mgr == NULL)
144 [ + - ]: 4 : || (transitions == NULL)
145 [ + - ]: 4 : || (transitions->start == NULL)
146 [ - + ]: 4 : || (transitions->stop == NULL)) {
147 : 0 : return -EINVAL;
148 : : }
149 : :
150 : 4 : *mgr = (struct onoff_manager)ONOFF_MANAGER_INITIALIZER(transitions);
151 : :
152 : 4 : return 0;
153 : : }
154 : :
155 : 0 : static void notify_monitors(struct onoff_manager *mgr,
156 : : uint32_t state,
157 : : int res)
158 : : {
159 : 0 : sys_slist_t *mlist = &mgr->monitors;
160 : : struct onoff_monitor *mon;
161 : : struct onoff_monitor *tmp;
162 : :
163 [ # # # # : 0 : SYS_SLIST_FOR_EACH_CONTAINER_SAFE(mlist, mon, tmp, node) {
# # # # #
# # # ]
164 : 0 : mon->callback(mgr, mon, state, res);
165 : : }
166 : 0 : }
167 : :
168 : 1 : static void notify_one(struct onoff_manager *mgr,
169 : : struct onoff_client *cli,
170 : : uint32_t state,
171 : : int res)
172 : : {
173 : : onoff_client_callback cb =
174 : 1 : (onoff_client_callback)sys_notify_finalize(&cli->notify, res);
175 : :
176 [ - + ]: 1 : if (cb) {
177 : 0 : cb(mgr, cli, state, res);
178 : : }
179 : 1 : }
180 : :
181 : 1 : static void notify_all(struct onoff_manager *mgr,
182 : : sys_slist_t *list,
183 : : uint32_t state,
184 : : int res)
185 : : {
186 [ + + ]: 2 : while (!sys_slist_is_empty(list)) {
187 : 1 : sys_snode_t *node = sys_slist_get_not_empty(list);
188 : 1 : struct onoff_client *cli =
189 : : CONTAINER_OF(node,
190 : : struct onoff_client,
191 : : node);
192 : :
193 : 1 : notify_one(mgr, cli, state, res);
194 : : }
195 : 1 : }
196 : :
197 : : static void process_event(struct onoff_manager *mgr,
198 : : int evt,
199 : : k_spinlock_key_t key);
200 : :
201 : 1 : static void transition_complete(struct onoff_manager *mgr,
202 : : int res)
203 : : {
204 : 1 : k_spinlock_key_t key = k_spin_lock(&mgr->lock);
205 : :
206 : 1 : mgr->last_res = res;
207 : 1 : process_event(mgr, EVT_COMPLETE, key);
208 : 1 : }
209 : :
210 : : /* Detect whether static state requires a transition. */
211 : 2 : static int process_recheck(struct onoff_manager *mgr)
212 : : {
213 : 2 : int evt = EVT_NOP;
214 : 2 : uint32_t state = mgr->flags & ONOFF_STATE_MASK;
215 : :
216 [ + + ]: 2 : if ((state == ONOFF_STATE_OFF)
217 [ + - ]: 1 : && !sys_slist_is_empty(&mgr->clients)) {
218 : 1 : evt = EVT_START;
219 [ + - ]: 1 : } else if ((state == ONOFF_STATE_ON)
220 [ - + ]: 1 : && (mgr->refs == 0U)) {
221 : 0 : evt = EVT_STOP;
222 [ - + ]: 1 : } else if ((state == ONOFF_STATE_ERROR)
223 [ # # ]: 0 : && !sys_slist_is_empty(&mgr->clients)) {
224 : 0 : evt = EVT_RESET;
225 : : } else {
226 : : ;
227 : : }
228 : :
229 : 2 : return evt;
230 : : }
231 : :
232 : : /* Process a transition completion.
233 : : *
234 : : * If the completion requires notifying clients, the clients are moved
235 : : * from the manager to the output list for notification.
236 : : */
237 : 1 : static void process_complete(struct onoff_manager *mgr,
238 : : sys_slist_t *clients,
239 : : int res)
240 : : {
241 : 1 : uint32_t state = mgr->flags & ONOFF_STATE_MASK;
242 : :
243 [ - + ]: 1 : if (res < 0) {
244 : : /* Enter ERROR state and notify all clients. */
245 : 0 : *clients = mgr->clients;
246 : 0 : sys_slist_init(&mgr->clients);
247 : 0 : set_state(mgr, ONOFF_STATE_ERROR);
248 [ - + ]: 1 : } else if ((state == ONOFF_STATE_TO_ON)
249 [ # # ]: 0 : || (state == ONOFF_STATE_RESETTING)) {
250 : 1 : *clients = mgr->clients;
251 : 1 : sys_slist_init(&mgr->clients);
252 : :
253 [ + - ]: 1 : if (state == ONOFF_STATE_TO_ON) {
254 : : struct onoff_client *cp;
255 : :
256 : : /* Increment reference count for all remaining
257 : : * clients and enter ON state.
258 : : */
259 [ + - - + : 2 : SYS_SLIST_FOR_EACH_CONTAINER(clients, cp, node) {
+ + ]
260 [ + - ]: 1 : mgr->refs += 1U;
261 : : }
262 : :
263 : 1 : set_state(mgr, ONOFF_STATE_ON);
264 : : } else {
265 [ # # ]: 0 : __ASSERT_NO_MSG(state == ONOFF_STATE_RESETTING);
266 : :
267 : 0 : set_state(mgr, ONOFF_STATE_OFF);
268 : : }
269 [ - + ]: 1 : if (process_recheck(mgr) != EVT_NOP) {
270 : 0 : mgr->flags |= ONOFF_FLAG_RECHECK;
271 : : }
272 [ # # ]: 0 : } else if (state == ONOFF_STATE_TO_OFF) {
273 : : /* Any active clients are requests waiting for this
274 : : * transition to complete. Queue a RECHECK event to
275 : : * ensure we don't miss them if we don't unlock to
276 : : * tell anybody about the completion.
277 : : */
278 : 0 : set_state(mgr, ONOFF_STATE_OFF);
279 [ # # ]: 0 : if (process_recheck(mgr) != EVT_NOP) {
280 : 0 : mgr->flags |= ONOFF_FLAG_RECHECK;
281 : : }
282 : : } else {
283 : 0 : __ASSERT_NO_MSG(false);
284 : : }
285 : 1 : }
286 : :
287 : : /* There are two points in the state machine where the machine is
288 : : * unlocked to perform some external action:
289 : : * * Initiation of an transition due to some event;
290 : : * * Invocation of the user-specified callback when a stable state is
291 : : * reached or an error detected.
292 : : *
293 : : * Events received during these unlocked periods are recorded in the
294 : : * state, but processing is deferred to the top-level invocation which
295 : : * will loop to handle any events that occurred during the unlocked
296 : : * regions.
297 : : */
298 : 2 : static void process_event(struct onoff_manager *mgr,
299 : : int evt,
300 : : k_spinlock_key_t key)
301 : : {
302 : : sys_slist_t clients;
303 : 2 : uint32_t state = mgr->flags & ONOFF_STATE_MASK;
304 : 2 : int res = 0;
305 : 2 : bool processing = ((mgr->flags & ONOFF_FLAG_PROCESSING) != 0);
306 : :
307 [ - + ]: 2 : __ASSERT_NO_MSG(evt != EVT_NOP);
308 : :
309 : : /* If this is a nested call record the event for processing in
310 : : * the top invocation.
311 : : */
312 [ - + ]: 2 : if (processing) {
313 [ # # ]: 0 : if (evt == EVT_COMPLETE) {
314 : 0 : mgr->flags |= ONOFF_FLAG_COMPLETE;
315 : : } else {
316 [ # # ]: 0 : __ASSERT_NO_MSG(evt == EVT_RECHECK);
317 : :
318 : 0 : mgr->flags |= ONOFF_FLAG_RECHECK;
319 : : }
320 : :
321 : 0 : goto out;
322 : : }
323 : :
324 : 2 : sys_slist_init(&clients);
325 : : do {
326 : 2 : onoff_transition_fn transit = NULL;
327 : :
328 [ + + ]: 2 : if (evt == EVT_RECHECK) {
329 : 1 : evt = process_recheck(mgr);
330 : : }
331 : :
332 [ - + ]: 2 : if (evt == EVT_NOP) {
333 : 0 : break;
334 : : }
335 : :
336 : 2 : res = 0;
337 [ + + ]: 2 : if (evt == EVT_COMPLETE) {
338 : 1 : res = mgr->last_res;
339 : 1 : process_complete(mgr, &clients, res);
340 : : /* NB: This can trigger a RECHECK */
341 [ + - ]: 1 : } else if (evt == EVT_START) {
342 [ - + ]: 1 : __ASSERT_NO_MSG(state == ONOFF_STATE_OFF);
343 [ - + ]: 1 : __ASSERT_NO_MSG(!sys_slist_is_empty(&mgr->clients));
344 : :
345 : 1 : transit = mgr->transitions->start;
346 [ - + ]: 1 : __ASSERT_NO_MSG(transit != NULL);
347 : 1 : set_state(mgr, ONOFF_STATE_TO_ON);
348 [ # # ]: 0 : } else if (evt == EVT_STOP) {
349 [ # # ]: 0 : __ASSERT_NO_MSG(state == ONOFF_STATE_ON);
350 [ # # ]: 0 : __ASSERT_NO_MSG(mgr->refs == 0);
351 : :
352 : 0 : transit = mgr->transitions->stop;
353 [ # # ]: 0 : __ASSERT_NO_MSG(transit != NULL);
354 : 0 : set_state(mgr, ONOFF_STATE_TO_OFF);
355 [ # # ]: 0 : } else if (evt == EVT_RESET) {
356 [ # # ]: 0 : __ASSERT_NO_MSG(state == ONOFF_STATE_ERROR);
357 [ # # ]: 0 : __ASSERT_NO_MSG(!sys_slist_is_empty(&mgr->clients));
358 : :
359 : 0 : transit = mgr->transitions->reset;
360 [ # # ]: 0 : __ASSERT_NO_MSG(transit != NULL);
361 : 0 : set_state(mgr, ONOFF_STATE_RESETTING);
362 : : } else {
363 : 0 : __ASSERT_NO_MSG(false);
364 : : }
365 : :
366 : : /* Have to unlock and do something if any of:
367 : : * * We changed state and there are monitors;
368 : : * * We completed a transition and there are clients to notify;
369 : : * * We need to initiate a transition.
370 : : */
371 : 4 : bool do_monitors = (state != (mgr->flags & ONOFF_STATE_MASK))
372 [ + - - + ]: 2 : && !sys_slist_is_empty(&mgr->monitors);
373 : :
374 : 2 : evt = EVT_NOP;
375 [ + - ]: 2 : if (do_monitors
376 [ + + ]: 2 : || !sys_slist_is_empty(&clients)
377 [ + - ]: 1 : || (transit != NULL)) {
378 : 2 : uint32_t flags = mgr->flags | ONOFF_FLAG_PROCESSING;
379 : :
380 : 2 : mgr->flags = flags;
381 : 2 : state = flags & ONOFF_STATE_MASK;
382 : :
383 : 2 : k_spin_unlock(&mgr->lock, key);
384 : :
385 [ - + ]: 2 : if (do_monitors) {
386 : 0 : notify_monitors(mgr, state, res);
387 : : }
388 : :
389 [ + + ]: 2 : if (!sys_slist_is_empty(&clients)) {
390 : 1 : notify_all(mgr, &clients, state, res);
391 : : }
392 : :
393 [ + + ]: 2 : if (transit != NULL) {
394 : 1 : transit(mgr, transition_complete);
395 : : }
396 : :
397 : 2 : key = k_spin_lock(&mgr->lock);
398 : 2 : mgr->flags &= ~ONOFF_FLAG_PROCESSING;
399 : 2 : state = mgr->flags & ONOFF_STATE_MASK;
400 : : }
401 : :
402 : : /* Process deferred events. Completion takes priority
403 : : * over recheck.
404 : : */
405 [ - + ]: 2 : if ((mgr->flags & ONOFF_FLAG_COMPLETE) != 0) {
406 : 0 : mgr->flags &= ~ONOFF_FLAG_COMPLETE;
407 : 0 : evt = EVT_COMPLETE;
408 [ - + ]: 2 : } else if ((mgr->flags & ONOFF_FLAG_RECHECK) != 0) {
409 : 0 : mgr->flags &= ~ONOFF_FLAG_RECHECK;
410 : 0 : evt = EVT_RECHECK;
411 : : } else {
412 : : ;
413 : : }
414 : :
415 : 2 : state = mgr->flags & ONOFF_STATE_MASK;
416 [ - + ]: 2 : } while (evt != EVT_NOP);
417 : :
418 : 2 : out:
419 : 2 : k_spin_unlock(&mgr->lock, key);
420 : 2 : }
421 : :
422 : 1 : int onoff_request(struct onoff_manager *mgr,
423 : : struct onoff_client *cli)
424 : : {
425 : 1 : bool add_client = false; /* add client to pending list */
426 : 1 : bool start = false; /* trigger a start transition */
427 : 1 : bool notify = false; /* do client notification */
428 : 1 : int rv = validate_args(mgr, cli);
429 : :
430 [ - + ]: 1 : if (rv < 0) {
431 : 0 : return rv;
432 : : }
433 : :
434 : 1 : k_spinlock_key_t key = k_spin_lock(&mgr->lock);
435 : 1 : uint32_t state = mgr->flags & ONOFF_STATE_MASK;
436 : :
437 : : /* Reject if this would overflow the reference count. */
438 [ - + ]: 1 : if (mgr->refs == SERVICE_REFS_MAX) {
439 : 0 : rv = -EAGAIN;
440 : 0 : goto out;
441 : : }
442 : :
443 : 1 : rv = state;
444 [ - + ]: 1 : if (state == ONOFF_STATE_ON) {
445 : : /* Increment reference count, notify in exit */
446 : 0 : notify = true;
447 : 0 : mgr->refs += 1U;
448 [ - + ]: 1 : } else if ((state == ONOFF_STATE_OFF)
449 [ # # ]: 0 : || (state == ONOFF_STATE_TO_OFF)
450 [ # # ]: 0 : || (state == ONOFF_STATE_TO_ON)) {
451 : : /* Start if OFF, queue client */
452 : 1 : start = (state == ONOFF_STATE_OFF);
453 : 1 : add_client = true;
454 [ # # ]: 0 : } else if (state == ONOFF_STATE_RESETTING) {
455 : 0 : rv = -ENOTSUP;
456 : : } else {
457 [ # # ]: 0 : __ASSERT_NO_MSG(state == ONOFF_STATE_ERROR);
458 : 0 : rv = -EIO;
459 : : }
460 : :
461 : 1 : out:
462 [ + - ]: 1 : if (add_client) {
463 : 1 : sys_slist_append(&mgr->clients, &cli->node);
464 : : }
465 : :
466 [ + - ]: 1 : if (start) {
467 : 1 : process_event(mgr, EVT_RECHECK, key);
468 : : } else {
469 : 0 : k_spin_unlock(&mgr->lock, key);
470 : :
471 [ # # ]: 0 : if (notify) {
472 : 0 : notify_one(mgr, cli, state, 0);
473 : : }
474 : : }
475 : :
476 : 1 : return rv;
477 : : }
478 : :
479 : 0 : int onoff_release(struct onoff_manager *mgr)
480 : : {
481 : 0 : bool stop = false; /* trigger a stop transition */
482 : :
483 : 0 : k_spinlock_key_t key = k_spin_lock(&mgr->lock);
484 : 0 : uint32_t state = mgr->flags & ONOFF_STATE_MASK;
485 : 0 : int rv = state;
486 : :
487 [ # # ]: 0 : if (state != ONOFF_STATE_ON) {
488 [ # # ]: 0 : if (state == ONOFF_STATE_ERROR) {
489 : 0 : rv = -EIO;
490 : : } else {
491 : 0 : rv = -ENOTSUP;
492 : : }
493 : 0 : goto out;
494 : : }
495 : :
496 [ # # ]: 0 : __ASSERT_NO_MSG(mgr->refs > 0);
497 : 0 : mgr->refs -= 1U;
498 : 0 : stop = (mgr->refs == 0);
499 : :
500 : 0 : out:
501 [ # # ]: 0 : if (stop) {
502 : 0 : process_event(mgr, EVT_RECHECK, key);
503 : : } else {
504 : 0 : k_spin_unlock(&mgr->lock, key);
505 : : }
506 : :
507 : 0 : return rv;
508 : : }
509 : :
510 : 0 : int onoff_reset(struct onoff_manager *mgr,
511 : : struct onoff_client *cli)
512 : : {
513 : 0 : bool reset = false;
514 : 0 : int rv = validate_args(mgr, cli);
515 : :
516 [ # # ]: 0 : if ((rv >= 0)
517 [ # # ]: 0 : && (mgr->transitions->reset == NULL)) {
518 : 0 : rv = -ENOTSUP;
519 : : }
520 : :
521 [ # # ]: 0 : if (rv < 0) {
522 : 0 : return rv;
523 : : }
524 : :
525 : 0 : k_spinlock_key_t key = k_spin_lock(&mgr->lock);
526 : 0 : uint32_t state = mgr->flags & ONOFF_STATE_MASK;
527 : :
528 : 0 : rv = state;
529 : :
530 [ # # ]: 0 : if ((state & ONOFF_FLAG_ERROR) == 0) {
531 : 0 : rv = -EALREADY;
532 : : } else {
533 : 0 : reset = (state != ONOFF_STATE_RESETTING);
534 : 0 : sys_slist_append(&mgr->clients, &cli->node);
535 : : }
536 : :
537 [ # # ]: 0 : if (reset) {
538 : 0 : process_event(mgr, EVT_RECHECK, key);
539 : : } else {
540 : 0 : k_spin_unlock(&mgr->lock, key);
541 : : }
542 : :
543 : 0 : return rv;
544 : : }
545 : :
546 : 0 : int onoff_cancel(struct onoff_manager *mgr,
547 : : struct onoff_client *cli)
548 : : {
549 [ # # # # ]: 0 : if ((mgr == NULL) || (cli == NULL)) {
550 : 0 : return -EINVAL;
551 : : }
552 : :
553 : 0 : int rv = -EALREADY;
554 : 0 : k_spinlock_key_t key = k_spin_lock(&mgr->lock);
555 : 0 : uint32_t state = mgr->flags & ONOFF_STATE_MASK;
556 : :
557 [ # # ]: 0 : if (sys_slist_find_and_remove(&mgr->clients, &cli->node)) {
558 [ # # # # : 0 : __ASSERT_NO_MSG((state == ONOFF_STATE_TO_ON)
# # ]
559 : : || (state == ONOFF_STATE_TO_OFF)
560 : : || (state == ONOFF_STATE_RESETTING));
561 : 0 : rv = state;
562 : : }
563 : :
564 : 0 : k_spin_unlock(&mgr->lock, key);
565 : :
566 : 0 : return rv;
567 : : }
568 : :
569 : 0 : int onoff_monitor_register(struct onoff_manager *mgr,
570 : : struct onoff_monitor *mon)
571 : : {
572 [ # # ]: 0 : if ((mgr == NULL)
573 [ # # ]: 0 : || (mon == NULL)
574 [ # # ]: 0 : || (mon->callback == NULL)) {
575 : 0 : return -EINVAL;
576 : : }
577 : :
578 : 0 : k_spinlock_key_t key = k_spin_lock(&mgr->lock);
579 : :
580 : 0 : sys_slist_append(&mgr->monitors, &mon->node);
581 : :
582 : 0 : k_spin_unlock(&mgr->lock, key);
583 : :
584 : 0 : return 0;
585 : : }
586 : :
587 : 0 : int onoff_monitor_unregister(struct onoff_manager *mgr,
588 : : struct onoff_monitor *mon)
589 : : {
590 : 0 : int rv = -EINVAL;
591 : :
592 [ # # ]: 0 : if ((mgr == NULL)
593 [ # # ]: 0 : || (mon == NULL)) {
594 : 0 : return rv;
595 : : }
596 : :
597 : 0 : k_spinlock_key_t key = k_spin_lock(&mgr->lock);
598 : :
599 [ # # ]: 0 : if (sys_slist_find_and_remove(&mgr->monitors, &mon->node)) {
600 : 0 : rv = 0;
601 : : }
602 : :
603 : 0 : k_spin_unlock(&mgr->lock, key);
604 : :
605 : 0 : return rv;
606 : : }
607 : :
608 : 0 : int onoff_sync_lock(struct onoff_sync_service *srv,
609 : : k_spinlock_key_t *keyp)
610 : : {
611 : 0 : *keyp = k_spin_lock(&srv->lock);
612 : 0 : return srv->count;
613 : : }
614 : :
615 : 0 : int onoff_sync_finalize(struct onoff_sync_service *srv,
616 : : k_spinlock_key_t key,
617 : : struct onoff_client *cli,
618 : : int res,
619 : : bool on)
620 : : {
621 : 0 : uint32_t state = ONOFF_STATE_ON;
622 : :
623 : : /* Clear errors visible when locked. If they are to be
624 : : * preserved the caller must finalize with the previous
625 : : * error code.
626 : : */
627 [ # # ]: 0 : if (srv->count < 0) {
628 : 0 : srv->count = 0;
629 : : }
630 [ # # ]: 0 : if (res < 0) {
631 : 0 : srv->count = res;
632 : 0 : state = ONOFF_STATE_ERROR;
633 [ # # ]: 0 : } else if (on) {
634 : 0 : srv->count += 1;
635 : : } else {
636 : 0 : srv->count -= 1;
637 : : /* state would be either off or on, but since
638 : : * callbacks are used only when turning on don't
639 : : * bother changing it.
640 : : */
641 : : }
642 : :
643 : 0 : int rv = srv->count;
644 : :
645 : 0 : k_spin_unlock(&srv->lock, key);
646 : :
647 [ # # ]: 0 : if (cli) {
648 : : /* Detect service mis-use: onoff does not callback on transition
649 : : * to off, so no client should have been passed.
650 : : */
651 [ # # ]: 0 : __ASSERT_NO_MSG(on);
652 : 0 : notify_one(NULL, cli, state, res);
653 : : }
654 : :
655 : 0 : return rv;
656 : : }
|