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 <zephyr/ztest.h>
9 #include <zephyr/sys/onoff.h>
10 
11 static struct onoff_client onoff_cli;
12 struct onoff_transitions transitions;
13 static struct onoff_manager onoff_srv;
14 static struct onoff_monitor onoff_mon;
15 
16 struct transition_record {
17 	uint32_t state;
18 	int res;
19 };
20 static struct transition_record trans[32];
21 static size_t ntrans;
22 
trans_callback(struct onoff_manager * mgr,struct onoff_monitor * mon,uint32_t state,int res)23 static void trans_callback(struct onoff_manager *mgr,
24 			  struct onoff_monitor *mon,
25 			  uint32_t state,
26 			  int res)
27 {
28 	if (ntrans < ARRAY_SIZE(trans)) {
29 		trans[ntrans++] = (struct transition_record){
30 			.state = state,
31 			.res = res,
32 		};
33 	}
34 }
35 
check_trans(uint32_t idx,uint32_t state,int res,const char * tag)36 static void check_trans(uint32_t idx,
37 		       uint32_t state,
38 		       int res,
39 		       const char *tag)
40 {
41 	zassert_true(idx < ntrans,
42 		     "trans idx %u high: %s", idx, tag);
43 
44 	const struct transition_record *xp = trans + idx;
45 
46 	zassert_equal(xp->state, state,
47 		      "trans[%u] state %x != %x: %s",
48 		      idx, xp->state, state, tag);
49 	zassert_equal(xp->res, res,
50 		      "trans[%u] res %d != %d: %s",
51 		      idx, xp->res, res, tag);
52 }
53 
54 static struct onoff_manager *callback_srv;
55 static struct onoff_client *callback_cli;
56 static uint32_t callback_state;
57 static int callback_res;
58 static onoff_client_callback callback_fn;
59 
callback(struct onoff_manager * srv,struct onoff_client * cli,uint32_t state,int res)60 static void callback(struct onoff_manager *srv,
61 		     struct onoff_client *cli,
62 		     uint32_t state,
63 		     int res)
64 {
65 	onoff_client_callback cb = callback_fn;
66 
67 	callback_srv = srv;
68 	callback_cli = cli;
69 	callback_state = state;
70 	callback_res = res;
71 	callback_fn = NULL;
72 
73 	if (cb != NULL) {
74 		cb(srv, cli, state, res);
75 	}
76 }
77 
check_callback(uint32_t state,int res,const char * tag)78 static void check_callback(uint32_t state,
79 			   int res,
80 			   const char *tag)
81 {
82 	zassert_equal(callback_state, state,
83 		      "callback state %x != %x: %s",
84 		      callback_state, state, tag);
85 	zassert_equal(callback_res, res,
86 		      "callback res %d != %d: %s",
87 		      callback_res, res, tag);
88 }
89 
cli_result(const struct onoff_client * cp)90 static inline int cli_result(const struct onoff_client *cp)
91 {
92 	int result;
93 	int rc = sys_notify_fetch_result(&cp->notify, &result);
94 
95 	if (rc == 0) {
96 		rc = result;
97 	}
98 	return rc;
99 }
100 
check_result(int res,const char * tag)101 static void check_result(int res,
102 			 const char *tag)
103 {
104 	zassert_equal(cli_result(&onoff_cli), res,
105 		      "cli res %d != %d: %s",
106 		      cli_result(&onoff_cli), res, tag);
107 }
108 
109 struct transit_state {
110 	const char *tag;
111 	bool async;
112 	int retval;
113 	onoff_notify_fn notify;
114 	struct onoff_manager *srv;
115 };
116 
reset_transit_state(struct transit_state * tsp)117 static void reset_transit_state(struct transit_state *tsp)
118 {
119 	tsp->async = false;
120 	tsp->retval = 0;
121 	tsp->notify = NULL;
122 	tsp->srv = NULL;
123 }
124 
run_transit(struct onoff_manager * srv,onoff_notify_fn notify,struct transit_state * tsp)125 static void run_transit(struct onoff_manager *srv,
126 			onoff_notify_fn notify,
127 			struct transit_state *tsp)
128 {
129 	if (tsp->async) {
130 		TC_PRINT("%s async\n", tsp->tag);
131 		tsp->notify = notify;
132 		tsp->srv = srv;
133 	} else {
134 		TC_PRINT("%s notify %d\n", tsp->tag, tsp->retval);
135 		notify(srv, tsp->retval);
136 	}
137 }
138 
notify(struct transit_state * tsp)139 static void notify(struct transit_state *tsp)
140 {
141 	TC_PRINT("%s settle %d %p\n", tsp->tag, tsp->retval, tsp->notify);
142 	tsp->notify(tsp->srv, tsp->retval);
143 	tsp->notify = NULL;
144 	tsp->srv = NULL;
145 }
146 
147 static struct transit_state start_state = {
148 	.tag = "start",
149 };
start(struct onoff_manager * srv,onoff_notify_fn notify_fn)150 static void start(struct onoff_manager *srv,
151 		  onoff_notify_fn notify_fn)
152 {
153 	run_transit(srv, notify_fn, &start_state);
154 }
155 
156 static struct transit_state stop_state = {
157 	.tag = "stop",
158 };
stop(struct onoff_manager * srv,onoff_notify_fn notify_fn)159 static void stop(struct onoff_manager *srv,
160 		 onoff_notify_fn notify_fn)
161 {
162 	run_transit(srv, notify_fn, &stop_state);
163 }
164 
165 static struct transit_state reset_state = {
166 	.tag = "reset",
167 };
reset(struct onoff_manager * srv,onoff_notify_fn notify_fn)168 static void reset(struct onoff_manager *srv,
169 		  onoff_notify_fn notify_fn)
170 {
171 	run_transit(srv, notify_fn, &reset_state);
172 }
173 
174 static struct k_sem isr_sync;
175 static struct k_timer isr_timer;
176 
isr_notify(struct k_timer * timer)177 static void isr_notify(struct k_timer *timer)
178 {
179 	struct transit_state *tsp = k_timer_user_data_get(timer);
180 
181 	TC_PRINT("ISR NOTIFY %s %d\n", tsp->tag, k_is_in_isr());
182 	notify(tsp);
183 	k_sem_give(&isr_sync);
184 }
185 
186 struct isr_call_state {
187 	struct onoff_manager *srv;
188 	struct onoff_client *cli;
189 	int result;
190 };
191 
isr_request(struct k_timer * timer)192 static void isr_request(struct k_timer *timer)
193 {
194 	struct isr_call_state *rsp = k_timer_user_data_get(timer);
195 
196 	rsp->result = onoff_request(rsp->srv, rsp->cli);
197 	k_sem_give(&isr_sync);
198 }
199 
isr_release(struct k_timer * timer)200 static void isr_release(struct k_timer *timer)
201 {
202 	struct isr_call_state *rsp = k_timer_user_data_get(timer);
203 
204 	rsp->result = onoff_release(rsp->srv);
205 	k_sem_give(&isr_sync);
206 }
207 
isr_reset(struct k_timer * timer)208 static void isr_reset(struct k_timer *timer)
209 {
210 	struct isr_call_state *rsp = k_timer_user_data_get(timer);
211 
212 	rsp->result = onoff_reset(rsp->srv, rsp->cli);
213 	k_sem_give(&isr_sync);
214 }
215 
reset_cli(void)216 static void reset_cli(void)
217 {
218 	onoff_cli = (struct onoff_client){};
219 	sys_notify_init_callback(&onoff_cli.notify, callback);
220 }
221 
reset_callback(void)222 static void reset_callback(void)
223 {
224 	callback_state = -1;
225 	callback_res = 0;
226 	callback_fn = NULL;
227 }
228 
setup_test(void)229 static void setup_test(void)
230 {
231 	int rc;
232 
233 	reset_callback();
234 	reset_transit_state(&start_state);
235 	reset_transit_state(&stop_state);
236 	reset_transit_state(&reset_state);
237 	ntrans = 0;
238 
239 	transitions = (struct onoff_transitions)
240 		      ONOFF_TRANSITIONS_INITIALIZER(start, stop, reset);
241 	rc = onoff_manager_init(&onoff_srv, &transitions);
242 	zassert_equal(rc, 0,
243 		      "service init");
244 
245 	onoff_mon = (struct onoff_monitor){
246 		.callback = trans_callback,
247 	};
248 	rc = onoff_monitor_register(&onoff_srv, &onoff_mon);
249 	zassert_equal(rc, 0,
250 		      "mon reg");
251 
252 	reset_cli();
253 }
254 
setup_error(void)255 static void setup_error(void)
256 {
257 	int rc;
258 
259 	setup_test();
260 	start_state.retval = -1;
261 
262 	rc = onoff_request(&onoff_srv, &onoff_cli);
263 	zassert_equal(rc, ONOFF_STATE_OFF,
264 		      "req 0 0");
265 	check_result(start_state.retval,
266 		     "err req");
267 	zassert_true(onoff_has_error(&onoff_srv),
268 		     "has_err");
269 
270 	reset_cli();
271 }
272 
ZTEST(onoff_api,test_manager_init)273 ZTEST(onoff_api, test_manager_init)
274 {
275 	int rc;
276 	struct onoff_transitions xit = {};
277 
278 	setup_test();
279 
280 	rc = onoff_manager_init(NULL, NULL);
281 	zassert_equal(rc, -EINVAL,
282 		      "init 0 0");
283 	rc = onoff_manager_init(&onoff_srv, NULL);
284 	zassert_equal(rc, -EINVAL,
285 		      "init srv 0");
286 	rc = onoff_manager_init(NULL, &transitions);
287 	zassert_equal(rc, -EINVAL,
288 		      "init 0 xit");
289 	rc = onoff_manager_init(&onoff_srv, &xit);
290 	zassert_equal(rc, -EINVAL,
291 		      "init 0 xit-start");
292 
293 	xit.start = start;
294 	rc = onoff_manager_init(&onoff_srv, &xit);
295 	zassert_equal(rc, -EINVAL,
296 		      "init srv xit-stop");
297 
298 	xit.stop = stop;
299 	rc = onoff_manager_init(&onoff_srv, &xit);
300 	zassert_equal(rc, 0,
301 		      "init srv xit ok");
302 }
303 
ZTEST(onoff_api,test_mon_reg)304 ZTEST(onoff_api, test_mon_reg)
305 {
306 	static struct onoff_monitor mon = {};
307 
308 	setup_test();
309 
310 	/* Verify parameter validation */
311 
312 	zassert_equal(onoff_monitor_register(NULL, NULL), -EINVAL,
313 		      "mon reg 0 0");
314 	zassert_equal(onoff_monitor_register(&onoff_srv, NULL), -EINVAL,
315 		      "mon reg srv 0");
316 	zassert_equal(onoff_monitor_register(NULL, &mon), -EINVAL,
317 		      "mon reg 0 mon");
318 	zassert_equal(onoff_monitor_register(&onoff_srv, &mon), -EINVAL,
319 		      "mon reg srv mon(!cb)");
320 }
321 
ZTEST(onoff_api,test_mon_unreg)322 ZTEST(onoff_api, test_mon_unreg)
323 {
324 	setup_test();
325 
326 	/* Verify parameter validation */
327 
328 	zassert_equal(onoff_monitor_unregister(NULL, NULL), -EINVAL,
329 		      "mon unreg 0 0");
330 	zassert_equal(onoff_monitor_unregister(&onoff_srv, NULL), -EINVAL,
331 		      "mon unreg srv 0");
332 	zassert_equal(onoff_monitor_unregister(NULL, &onoff_mon), -EINVAL,
333 		      "mon unreg 0 mon");
334 	zassert_equal(onoff_monitor_unregister(&onoff_srv, &onoff_mon), 0,
335 		      "mon unreg 0 mon");
336 	zassert_equal(onoff_monitor_unregister(&onoff_srv, &onoff_mon), -EINVAL,
337 		      "mon unreg 0 mon");
338 }
339 
ZTEST(onoff_api,test_request)340 ZTEST(onoff_api, test_request)
341 {
342 	struct onoff_client cli2 = {};
343 	int rc;
344 
345 	setup_test();
346 
347 	rc = onoff_request(NULL, NULL);
348 	zassert_equal(rc, -EINVAL,
349 		      "req 0 0");
350 	rc = onoff_request(&onoff_srv, NULL);
351 	zassert_equal(rc, -EINVAL,
352 		      "req srv 0");
353 	rc = onoff_request(NULL, &onoff_cli);
354 	zassert_equal(rc, -EINVAL,
355 		      "req 0 cli");
356 
357 	rc = onoff_request(&onoff_srv, &cli2);
358 	zassert_equal(rc, -EINVAL,
359 		      "req srv cli-uninit");
360 
361 	onoff_cli.notify.flags |= BIT(ONOFF_CLIENT_EXTENSION_POS);
362 	rc = onoff_request(&onoff_srv, &onoff_cli);
363 	zassert_equal(rc, -EINVAL,
364 		      "req srv cli-flags");
365 
366 	onoff_cli.notify.flags &= ~BIT(ONOFF_CLIENT_EXTENSION_POS);
367 	rc = onoff_request(&onoff_srv, &onoff_cli);
368 	zassert_equal(rc, 0,
369 		      "req srv cli ok");
370 
371 	reset_cli();
372 	onoff_srv.refs = -1;
373 	rc = onoff_request(&onoff_srv, &onoff_cli);
374 	zassert_equal(rc, -EAGAIN,
375 		      "req srv cli ofl");
376 
377 }
378 
ZTEST(onoff_api,test_basic_sync)379 ZTEST(onoff_api, test_basic_sync)
380 {
381 	int rc;
382 
383 	/* Verify synchronous request and release behavior. */
384 
385 	setup_test();
386 	start_state.retval = 16;
387 	stop_state.retval = 23;
388 
389 	rc = onoff_request(&onoff_srv, &onoff_cli);
390 	zassert_equal(rc, ONOFF_STATE_OFF,
391 		      "req: %d", rc);
392 	zassert_equal(onoff_srv.refs, 1U,
393 		      "req refs: %u", onoff_srv.refs);
394 	check_result(start_state.retval, "req");
395 	zassert_equal(callback_srv, &onoff_srv,
396 		      "callback wrong srv");
397 	zassert_equal(callback_cli, &onoff_cli,
398 		      "callback wrong cli");
399 	check_callback(ONOFF_STATE_ON, start_state.retval,
400 		       "req");
401 	zassert_equal(ntrans, 2U,
402 		      "req trans");
403 	check_trans(0, ONOFF_STATE_TO_ON, 0,
404 		   "trans to-on");
405 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
406 		   "trans on");
407 
408 	rc = onoff_release(&onoff_srv);
409 	zassert_equal(rc, ONOFF_STATE_ON,
410 		      "rel: %d", rc);
411 	zassert_equal(onoff_srv.refs, 0U,
412 		      "rel refs: %u", onoff_srv.refs);
413 	zassert_equal(ntrans, 4U,
414 		      "rel trans");
415 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
416 		   "trans to-off");
417 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
418 		   "trans off");
419 
420 	rc = onoff_release(&onoff_srv);
421 	zassert_equal(rc, -ENOTSUP,
422 		      "re-rel: %d", rc);
423 }
424 
ZTEST(onoff_api,test_basic_async)425 ZTEST(onoff_api, test_basic_async)
426 {
427 	int rc;
428 
429 	/* Verify asynchronous request and release behavior. */
430 
431 	setup_test();
432 	start_state.async = true;
433 	start_state.retval = 51;
434 	stop_state.async = true;
435 	stop_state.retval = 17;
436 
437 	rc = onoff_request(&onoff_srv, &onoff_cli);
438 	zassert_equal(rc, ONOFF_STATE_OFF,
439 		      "async req: %d", rc);
440 	zassert_equal(onoff_srv.refs, 0U,
441 		      "to-on refs: %u", onoff_srv.refs);
442 	check_result(-EAGAIN, "async req");
443 	zassert_equal(ntrans, 1U,
444 		      "async req trans");
445 	check_trans(0, ONOFF_STATE_TO_ON, 0,
446 		   "trans to-on");
447 
448 	notify(&start_state);
449 	zassert_equal(onoff_srv.refs, 1U,
450 		      "on refs: %u", onoff_srv.refs);
451 	check_result(start_state.retval, "async req");
452 	zassert_equal(ntrans, 2U,
453 		      "async req trans");
454 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
455 		   "trans on");
456 
457 	rc = onoff_release(&onoff_srv);
458 	zassert_true(rc >= 0,
459 		     "rel: %d", rc);
460 	zassert_equal(onoff_srv.refs, 0U,
461 		      "on refs: %u", onoff_srv.refs);
462 	zassert_equal(ntrans, 3U,
463 		      "async rel trans");
464 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
465 		   "trans to-off");
466 
467 	notify(&stop_state);
468 	zassert_equal(onoff_srv.refs, 0U,
469 		      "rel refs: %u", onoff_srv.refs);
470 	zassert_equal(ntrans, 4U,
471 		      "rel trans");
472 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
473 		   "trans off");
474 }
475 
ZTEST(onoff_api,test_reset)476 ZTEST(onoff_api, test_reset)
477 {
478 	struct onoff_client cli2 = {};
479 	int rc;
480 
481 	setup_error();
482 
483 	reset_cli();
484 	rc = onoff_reset(NULL, NULL);
485 	zassert_equal(rc, -EINVAL,
486 		      "rst 0 0");
487 	rc = onoff_reset(&onoff_srv, NULL);
488 	zassert_equal(rc, -EINVAL,
489 		      "rst srv 0");
490 	rc = onoff_reset(NULL, &onoff_cli);
491 	zassert_equal(rc, -EINVAL,
492 		      "rst 0 cli");
493 	rc = onoff_reset(&onoff_srv, &cli2);
494 	zassert_equal(rc, -EINVAL,
495 		      "rst srv cli-cfg");
496 
497 	transitions.reset = NULL;
498 	rc = onoff_reset(&onoff_srv, &onoff_cli);
499 	zassert_equal(rc, -ENOTSUP,
500 		      "rst srv cli-cfg");
501 
502 	transitions.reset = reset;
503 	rc = onoff_reset(&onoff_srv, &onoff_cli);
504 	zassert_equal(rc, ONOFF_STATE_ERROR,
505 		      "rst srv cli");
506 
507 	reset_cli();
508 	rc = onoff_reset(&onoff_srv, &onoff_cli);
509 	zassert_equal(rc, -EALREADY,
510 		      "re-rst srv cli");
511 }
512 
ZTEST(onoff_api,test_basic_reset)513 ZTEST(onoff_api, test_basic_reset)
514 {
515 	int rc;
516 
517 	/* Verify that reset works. */
518 
519 	setup_error();
520 
521 	zassert_equal(ntrans, 2U,
522 		      "err trans");
523 	check_trans(0, ONOFF_STATE_TO_ON, 0,
524 		   "trans to-on");
525 	check_trans(1, ONOFF_STATE_ERROR, start_state.retval,
526 		   "trans on");
527 
528 	reset_cli();
529 	reset_state.retval = 12;
530 
531 	rc = onoff_reset(&onoff_srv, &onoff_cli);
532 	zassert_equal(rc, ONOFF_STATE_ERROR,
533 		      "rst");
534 	check_result(reset_state.retval,
535 		     "rst");
536 	zassert_equal(ntrans, 4U,
537 		      "err trans");
538 	check_trans(2, ONOFF_STATE_RESETTING, 0,
539 		   "trans resetting");
540 	check_trans(3, ONOFF_STATE_OFF, reset_state.retval,
541 		   "trans off");
542 }
543 
ZTEST(onoff_api,test_multi_start)544 ZTEST(onoff_api, test_multi_start)
545 {
546 	int rc;
547 	struct onoff_client cli2 = {};
548 
549 	/* Verify multiple requests are satisfied when start
550 	 * transition completes.
551 	 */
552 
553 	setup_test();
554 
555 	start_state.async = true;
556 	start_state.retval = 16;
557 
558 	rc = onoff_request(&onoff_srv, &onoff_cli);
559 	zassert_equal(rc, ONOFF_STATE_OFF,
560 		      "req: %d", rc);
561 	zassert_equal(onoff_srv.refs, 0U,
562 		      "req refs: %u", onoff_srv.refs);
563 	check_result(-EAGAIN, "req");
564 	zassert_equal(ntrans, 1U,
565 		      "req trans");
566 	check_trans(0, ONOFF_STATE_TO_ON, 0,
567 		   "trans to-on");
568 
569 	sys_notify_init_spinwait(&cli2.notify);
570 
571 	rc = onoff_request(&onoff_srv, &cli2);
572 	zassert_equal(rc, ONOFF_STATE_TO_ON,
573 		      "req2: %d", rc);
574 	zassert_equal(cli_result(&cli2), -EAGAIN,
575 		      "req2 result");
576 
577 	notify(&start_state);
578 
579 	zassert_equal(ntrans, 2U,
580 		      "async req trans");
581 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
582 		   "trans on");
583 	check_result(start_state.retval, "req");
584 	zassert_equal(cli_result(&cli2), start_state.retval,
585 		      "req2");
586 }
587 
ZTEST(onoff_api,test_indep_req)588 ZTEST(onoff_api, test_indep_req)
589 {
590 	int rc;
591 	struct onoff_client cli0 = {};
592 
593 	/* Verify that requests and releases while on behave as
594 	 * expected.
595 	 */
596 
597 	setup_test();
598 	sys_notify_init_spinwait(&cli0.notify);
599 	start_state.retval = 62;
600 
601 	rc = onoff_request(&onoff_srv, &cli0);
602 	zassert_equal(rc, ONOFF_STATE_OFF,
603 		      "req0: %d", rc);
604 	zassert_equal(onoff_srv.refs, 1U,
605 		      "req0 refs: %u", onoff_srv.refs);
606 	zassert_equal(cli_result(&cli0), start_state.retval,
607 		      "req0 result");
608 	zassert_equal(ntrans, 2U,
609 		      "req trans");
610 	check_trans(0, ONOFF_STATE_TO_ON, 0,
611 		   "trans to-on");
612 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
613 		   "trans on");
614 
615 	++start_state.retval;
616 
617 	rc = onoff_request(&onoff_srv, &onoff_cli);
618 	zassert_equal(rc, ONOFF_STATE_ON,
619 		      "req: %d", rc);
620 	check_result(0,
621 		     "req");
622 
623 	zassert_equal(ntrans, 2U,
624 		      "async req trans");
625 	zassert_equal(onoff_srv.refs, 2U,
626 		      "srv refs: %u", onoff_srv.refs);
627 
628 	rc = onoff_release(&onoff_srv); /* pair with cli0 */
629 	zassert_equal(rc, ONOFF_STATE_ON,
630 		      "rel: %d", rc);
631 	zassert_equal(onoff_srv.refs, 1U,
632 		      "srv refs");
633 	zassert_equal(ntrans, 2U,
634 		      "async req trans");
635 
636 	rc = onoff_release(&onoff_srv); /* pair with cli */
637 	zassert_equal(rc, ONOFF_STATE_ON,
638 		      "rel: %d", rc);
639 	zassert_equal(onoff_srv.refs, 0U,
640 		      "srv refs");
641 	zassert_equal(ntrans, 4U,
642 		      "async req trans");
643 }
644 
ZTEST(onoff_api,test_delayed_req)645 ZTEST(onoff_api, test_delayed_req)
646 {
647 	int rc;
648 
649 	setup_test();
650 	start_state.retval = 16;
651 
652 	/* Verify that a request received while turning off is
653 	 * processed on completion of the transition to off.
654 	 */
655 
656 	rc = onoff_request(&onoff_srv, &onoff_cli);
657 	zassert_equal(rc, ONOFF_STATE_OFF,
658 		      "req: %d", rc);
659 	check_result(start_state.retval, "req");
660 	zassert_equal(ntrans, 2U,
661 		      "req trans");
662 	check_trans(0, ONOFF_STATE_TO_ON, 0,
663 		   "trans to-on");
664 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
665 		   "trans on");
666 
667 	start_state.retval += 1;
668 	stop_state.async = true;
669 	stop_state.retval = 14;
670 
671 	rc = onoff_release(&onoff_srv);
672 	zassert_true(rc >= 0,
673 		     "rel: %d", rc);
674 	zassert_equal(onoff_srv.refs, 0U,
675 		      "on refs: %u", onoff_srv.refs);
676 	zassert_equal(ntrans, 3U,
677 		      "async rel trans");
678 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
679 		   "trans to-off");
680 
681 	reset_cli();
682 
683 	rc = onoff_request(&onoff_srv, &onoff_cli);
684 	zassert_equal(rc, ONOFF_STATE_TO_OFF,
685 		      "del req: %d", rc);
686 	zassert_equal(ntrans, 3U,
687 		      "async rel trans");
688 	check_result(-EAGAIN, "del req");
689 
690 	notify(&stop_state);
691 
692 	check_result(start_state.retval, "del req");
693 	zassert_equal(ntrans, 6U,
694 		      "req trans");
695 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
696 		   "trans to-off");
697 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
698 		   "trans off");
699 	check_trans(4, ONOFF_STATE_TO_ON, 0,
700 		   "trans to-on");
701 	check_trans(5, ONOFF_STATE_ON, start_state.retval,
702 		   "trans on");
703 }
704 
705 
ZTEST(onoff_api,test_recheck_start)706 ZTEST(onoff_api, test_recheck_start)
707 {
708 	int rc;
709 
710 	/* Verify fast-path recheck when entering ON with no clients.
711 	 *
712 	 * This removes the monitor which bypasses the unlock region
713 	 * in process_events() when there is no client and no
714 	 * transition.
715 	 */
716 
717 	setup_test();
718 	rc = onoff_monitor_unregister(&onoff_srv, &onoff_mon);
719 	zassert_equal(rc, 0,
720 		      "mon unreg");
721 
722 	start_state.async = true;
723 
724 	rc = onoff_request(&onoff_srv, &onoff_cli);
725 	zassert_equal(rc, ONOFF_STATE_OFF,
726 		      "req");
727 	rc = onoff_cancel(&onoff_srv, &onoff_cli);
728 	zassert_equal(rc, ONOFF_STATE_TO_ON,
729 		      "cancel");
730 
731 	notify(&start_state);
732 	zassert_equal(onoff_srv.flags, ONOFF_STATE_OFF,
733 		      "completed");
734 }
735 
ZTEST(onoff_api,test_recheck_stop)736 ZTEST(onoff_api, test_recheck_stop)
737 {
738 	int rc;
739 
740 	/* Verify fast-path recheck when entering OFF with clients.
741 	 *
742 	 * This removes the monitor which bypasses the unlock region
743 	 * in process_events() when there is no client and no
744 	 * transition.
745 	 */
746 
747 	setup_test();
748 	rc = onoff_monitor_unregister(&onoff_srv, &onoff_mon);
749 	zassert_equal(rc, 0,
750 		      "mon unreg");
751 
752 	rc = onoff_request(&onoff_srv, &onoff_cli);
753 	zassert_equal(rc, ONOFF_STATE_OFF,
754 		      "req");
755 	check_result(start_state.retval,
756 		     "req");
757 
758 	stop_state.async = true;
759 	rc = onoff_release(&onoff_srv);
760 	zassert_equal(rc, ONOFF_STATE_ON,
761 		      "rel");
762 
763 	reset_cli();
764 	rc = onoff_request(&onoff_srv, &onoff_cli);
765 	zassert_equal(rc, ONOFF_STATE_TO_OFF,
766 		      "delayed req");
767 	check_result(-EAGAIN,
768 		     "delayed req");
769 
770 	notify(&stop_state);
771 	zassert_equal(onoff_srv.flags, ONOFF_STATE_ON,
772 		      "completed");
773 }
774 
rel_in_req_cb(struct onoff_manager * srv,struct onoff_client * cli,uint32_t state,int res)775 static void rel_in_req_cb(struct onoff_manager *srv,
776 			  struct onoff_client *cli,
777 			  uint32_t state,
778 			  int res)
779 {
780 	int rc = onoff_release(srv);
781 
782 	zassert_equal(rc, ONOFF_STATE_ON,
783 		      "rel-in-req");
784 }
785 
ZTEST(onoff_api,test_rel_in_req_cb)786 ZTEST(onoff_api, test_rel_in_req_cb)
787 {
788 	int rc;
789 
790 	/* Verify that a release invoked during the request completion
791 	 * callback is processed to final state.
792 	 */
793 
794 	setup_test();
795 	callback_fn = rel_in_req_cb;
796 
797 	rc = onoff_request(&onoff_srv, &onoff_cli);
798 	zassert_equal(rc, ONOFF_STATE_OFF,
799 		      "req");
800 
801 	zassert_equal(callback_fn, NULL,
802 		      "invoke");
803 
804 	zassert_equal(ntrans, 4U,
805 		      "req trans");
806 	check_trans(0, ONOFF_STATE_TO_ON, 0,
807 		   "trans to-on");
808 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
809 		   "trans on");
810 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
811 		   "trans to-off");
812 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
813 		   "trans off");
814 }
815 
ZTEST(onoff_api,test_multi_reset)816 ZTEST(onoff_api, test_multi_reset)
817 {
818 	int rc;
819 	struct onoff_client cli2 = {};
820 
821 	/* Verify multiple reset requests are satisfied when reset
822 	 * transition completes.
823 	 */
824 	setup_test();
825 	start_state.retval = -23;
826 
827 	rc = onoff_request(&onoff_srv, &onoff_cli);
828 	zassert_equal(rc, ONOFF_STATE_OFF,
829 		      "req err");
830 	check_result(start_state.retval,
831 		     "req err");
832 	zassert_true(onoff_has_error(&onoff_srv),
833 		     "has_error");
834 	zassert_equal(ntrans, 2U,
835 		      "err trans");
836 	check_trans(0, ONOFF_STATE_TO_ON, 0,
837 		   "trans to-on");
838 	check_trans(1, ONOFF_STATE_ERROR, start_state.retval,
839 		   "trans on");
840 
841 	reset_state.async = true;
842 	reset_state.retval = 21;
843 
844 	sys_notify_init_spinwait(&cli2.notify);
845 	rc = onoff_reset(&onoff_srv, &cli2);
846 	zassert_equal(rc, ONOFF_STATE_ERROR,
847 		      "rst2");
848 	zassert_equal(cli_result(&cli2), -EAGAIN,
849 		      "rst2 result");
850 	zassert_equal(ntrans, 3U,
851 		      "rst trans");
852 	check_trans(2, ONOFF_STATE_RESETTING, 0,
853 		   "trans resetting");
854 
855 	reset_cli();
856 	rc = onoff_reset(&onoff_srv, &onoff_cli);
857 	zassert_equal(rc, ONOFF_STATE_RESETTING,
858 		      "rst");
859 	zassert_equal(ntrans, 3U,
860 		      "rst trans");
861 
862 	notify(&reset_state);
863 
864 	zassert_equal(cli_result(&cli2), reset_state.retval,
865 		      "rst2 result");
866 	check_result(reset_state.retval,
867 		     "rst");
868 	zassert_equal(ntrans, 4U,
869 		      "rst trans");
870 	check_trans(3, ONOFF_STATE_OFF, reset_state.retval,
871 		   "trans off");
872 }
873 
ZTEST(onoff_api,test_error)874 ZTEST(onoff_api, test_error)
875 {
876 	struct onoff_client cli2 = {};
877 	int rc;
878 
879 	/* Verify rejected operations when error present. */
880 
881 	setup_error();
882 
883 	rc = onoff_request(&onoff_srv, &onoff_cli);
884 	zassert_equal(rc, -EIO,
885 		      "req in err");
886 
887 	rc = onoff_release(&onoff_srv);
888 	zassert_equal(rc, -EIO,
889 		      "rel in err");
890 
891 	reset_state.async = true;
892 	sys_notify_init_spinwait(&cli2.notify);
893 
894 	rc = onoff_reset(&onoff_srv, &cli2);
895 	zassert_equal(rc, ONOFF_STATE_ERROR,
896 		      "rst");
897 
898 	rc = onoff_request(&onoff_srv, &onoff_cli);
899 	zassert_equal(rc, -ENOTSUP,
900 		      "req in err");
901 
902 	rc = onoff_release(&onoff_srv);
903 	zassert_equal(rc, -ENOTSUP,
904 		      "rel in err");
905 }
906 
ZTEST(onoff_api,test_cancel_req)907 ZTEST(onoff_api, test_cancel_req)
908 {
909 	int rc;
910 
911 	setup_test();
912 	start_state.async = true;
913 	start_state.retval = 14;
914 
915 	rc = onoff_cancel(NULL, NULL);
916 	zassert_equal(rc, -EINVAL,
917 		      "can 0 0");
918 	rc = onoff_cancel(&onoff_srv, NULL);
919 	zassert_equal(rc, -EINVAL,
920 		      "can srv 0");
921 	rc = onoff_cancel(NULL, &onoff_cli);
922 	zassert_equal(rc, -EINVAL,
923 		      "can 0 cli");
924 
925 	rc = onoff_request(&onoff_srv, &onoff_cli);
926 	zassert_equal(rc, ONOFF_STATE_OFF,
927 		      "async req: %d", rc);
928 	check_result(-EAGAIN, "async req");
929 	zassert_equal(ntrans, 1U,
930 		      "req trans");
931 	check_trans(0, ONOFF_STATE_TO_ON, 0,
932 		   "trans to-on");
933 
934 	rc = onoff_cancel(&onoff_srv, &onoff_cli);
935 	zassert_equal(rc, ONOFF_STATE_TO_ON,
936 		      "cancel req: %d", rc);
937 
938 	rc = onoff_cancel(&onoff_srv, &onoff_cli);
939 	zassert_equal(rc, -EALREADY,
940 		      "re-cancel req: %d", rc);
941 
942 	zassert_equal(ntrans, 1U,
943 		      "req trans");
944 	notify(&start_state);
945 
946 	zassert_equal(ntrans, 4U,
947 		      "req trans");
948 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
949 		   "trans on");
950 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
951 		   "trans to-off");
952 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
953 		   "trans off");
954 }
955 
ZTEST(onoff_api,test_cancel_delayed_req)956 ZTEST(onoff_api, test_cancel_delayed_req)
957 {
958 	int rc;
959 
960 	setup_test();
961 
962 	rc = onoff_request(&onoff_srv, &onoff_cli);
963 	zassert_equal(rc, ONOFF_STATE_OFF,
964 		      "req: %d", rc);
965 	check_result(start_state.retval, "req");
966 	zassert_equal(ntrans, 2U,
967 		      "req trans");
968 	check_trans(0, ONOFF_STATE_TO_ON, 0,
969 		   "trans to-on");
970 	check_trans(1, ONOFF_STATE_ON, start_state.retval,
971 		   "trans on");
972 
973 	stop_state.async = true;
974 	stop_state.retval = 14;
975 
976 	rc = onoff_release(&onoff_srv);
977 	zassert_true(rc >= 0,
978 		     "rel: %d", rc);
979 	zassert_equal(onoff_srv.refs, 0U,
980 		      "on refs: %u", onoff_srv.refs);
981 	zassert_equal(ntrans, 3U,
982 		      "async rel trans");
983 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
984 		   "trans to-off");
985 
986 	reset_cli();
987 
988 	rc = onoff_request(&onoff_srv, &onoff_cli);
989 	zassert_equal(rc, ONOFF_STATE_TO_OFF,
990 		      "del req: %d", rc);
991 	zassert_equal(ntrans, 3U,
992 		      "async rel trans");
993 	check_result(-EAGAIN, "del req");
994 
995 	rc = onoff_cancel(&onoff_srv, &onoff_cli);
996 	zassert_equal(rc, ONOFF_STATE_TO_OFF,
997 		      "can del req: %d", rc);
998 
999 	notify(&stop_state);
1000 
1001 	zassert_equal(ntrans, 4U,
1002 		      "req trans");
1003 	check_trans(2, ONOFF_STATE_TO_OFF, 0,
1004 		   "trans to-off");
1005 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
1006 		   "trans off");
1007 }
1008 
ZTEST(onoff_api,test_cancel_or_release)1009 ZTEST(onoff_api, test_cancel_or_release)
1010 {
1011 	int rc;
1012 
1013 	/* First, verify that the cancel-or-release idiom works when
1014 	 * invoked in state TO-ON.
1015 	 */
1016 
1017 	setup_test();
1018 	start_state.async = true;
1019 
1020 	rc = onoff_request(&onoff_srv, &onoff_cli);
1021 	zassert_equal(rc, ONOFF_STATE_OFF,
1022 		      "req");
1023 	rc = onoff_cancel_or_release(&onoff_srv, &onoff_cli);
1024 	zassert_equal(rc, ONOFF_STATE_TO_ON,
1025 		      "c|r to-on");
1026 	notify(&start_state);
1027 
1028 	zassert_equal(ntrans, 4U,
1029 		      "req trans");
1030 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
1031 		   "trans off");
1032 
1033 	/* Now verify that the cancel-or-release idiom works when
1034 	 * invoked in state ON.
1035 	 */
1036 
1037 	setup_test();
1038 	start_state.async = false;
1039 
1040 	rc = onoff_request(&onoff_srv, &onoff_cli);
1041 	zassert_equal(rc, ONOFF_STATE_OFF,
1042 		      "req");
1043 	zassert_equal(ntrans, 2U,
1044 		      "req trans");
1045 
1046 	rc = onoff_cancel_or_release(&onoff_srv, &onoff_cli);
1047 	zassert_equal(rc, ONOFF_STATE_ON,
1048 		      "c|r to-on");
1049 	zassert_equal(ntrans, 4U,
1050 		      "req trans");
1051 	check_trans(3, ONOFF_STATE_OFF, stop_state.retval,
1052 		   "trans off");
1053 }
1054 
ZTEST(onoff_api,test_sync_basic)1055 ZTEST(onoff_api, test_sync_basic)
1056 {
1057 	static struct onoff_sync_service srv = {};
1058 	k_spinlock_key_t key;
1059 	int res = 5;
1060 	int rc;
1061 
1062 	reset_cli();
1063 
1064 	rc = onoff_sync_lock(&srv, &key);
1065 	zassert_equal(rc, 0,
1066 		      "init req");
1067 
1068 	rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true);
1069 	zassert_equal(rc, 1,
1070 		      "req count");
1071 	zassert_equal(callback_srv, NULL,
1072 		      "sync cb srv");
1073 	zassert_equal(callback_cli, &onoff_cli,
1074 		      "sync cb cli");
1075 	check_callback(ONOFF_STATE_ON, res, "sync req");
1076 
1077 	reset_cli();
1078 	reset_callback();
1079 
1080 	rc = onoff_sync_lock(&srv, &key);
1081 	zassert_equal(rc, 1,
1082 		      "init rel");
1083 
1084 	++res;
1085 	rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true);
1086 	zassert_equal(rc, 2,
1087 		      "req2 count");
1088 	check_callback(ONOFF_STATE_ON, res, "sync req2");
1089 
1090 	reset_cli();
1091 
1092 	rc = onoff_sync_lock(&srv, &key);
1093 	zassert_equal(rc, 2,
1094 		      "init rel");
1095 
1096 	rc = onoff_sync_finalize(&srv, key, NULL, res, false);
1097 	zassert_equal(rc, 1,
1098 		      "rel count");
1099 
1100 	reset_cli();
1101 
1102 	rc = onoff_sync_lock(&srv, &key);
1103 	zassert_equal(rc, 1,
1104 		      "init rel2");
1105 
1106 	rc = onoff_sync_finalize(&srv, key, NULL, res, false);
1107 	zassert_equal(rc, 0,
1108 		      "rel2 count");
1109 
1110 	/* Extra release is caught and diagnosed.  May not happen with
1111 	 * onoff manager, but we can/should do it for sync.
1112 	 */
1113 	reset_cli();
1114 
1115 	rc = onoff_sync_lock(&srv, &key);
1116 	zassert_equal(rc, 0,
1117 		      "init rel2");
1118 
1119 	rc = onoff_sync_finalize(&srv, key, NULL, res, false);
1120 	zassert_equal(rc, -1,
1121 		      "rel-1 count");
1122 
1123 	/* Error state is visible to next lock. */
1124 	reset_cli();
1125 	reset_callback();
1126 
1127 	rc = onoff_sync_lock(&srv, &key);
1128 	zassert_equal(rc, -1,
1129 		      "init req");
1130 }
1131 
ZTEST(onoff_api,test_sync_error)1132 ZTEST(onoff_api, test_sync_error)
1133 {
1134 	static struct onoff_sync_service srv = {};
1135 	k_spinlock_key_t key;
1136 	int res = -EPERM;
1137 	int rc;
1138 
1139 	reset_cli();
1140 	reset_callback();
1141 
1142 	rc = onoff_sync_lock(&srv, &key);
1143 	zassert_equal(rc, 0,
1144 		      "init req");
1145 
1146 	rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true);
1147 
1148 	zassert_equal(rc, res,
1149 		      "err final");
1150 	zassert_equal(srv.count, res,
1151 		      "srv err count");
1152 	zassert_equal(callback_srv, NULL,
1153 		      "sync cb srv");
1154 	zassert_equal(callback_cli, &onoff_cli,
1155 		      "sync cb cli");
1156 	check_callback(ONOFF_STATE_ERROR, res, "err final");
1157 
1158 	/* Error is visible to next operation (the value is the
1159 	 * negative count)
1160 	 */
1161 
1162 	reset_cli();
1163 	reset_callback();
1164 
1165 	rc = onoff_sync_lock(&srv, &key);
1166 	zassert_equal(rc, -1,
1167 		      "init req");
1168 
1169 	/* Error is cleared by non-negative finalize result */
1170 	res = 3;
1171 	rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true);
1172 
1173 	zassert_equal(rc, 1,
1174 		      "req count %d", rc);
1175 	check_callback(ONOFF_STATE_ON, res, "sync req");
1176 
1177 	rc = onoff_sync_lock(&srv, &key);
1178 	zassert_equal(rc, 1,
1179 		      "init rel");
1180 }
1181 
1182 
test_init(void)1183 void *test_init(void)
1184 {
1185 	k_sem_init(&isr_sync, 0, 1);
1186 	k_timer_init(&isr_timer, isr_notify, NULL);
1187 
1188 	(void)isr_reset;
1189 	(void)isr_release;
1190 	(void)isr_request;
1191 	return NULL;
1192 }
1193 ZTEST_SUITE(onoff_api, NULL, test_init, NULL, NULL, NULL);
1194