/* * Copyright (c) 2019 Peter Bigot Consulting, LLC * Copyright (c) 2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include static struct onoff_client onoff_cli; struct onoff_transitions transitions; static struct onoff_manager onoff_srv; static struct onoff_monitor onoff_mon; struct transition_record { uint32_t state; int res; }; static struct transition_record trans[32]; static size_t ntrans; static void trans_callback(struct onoff_manager *mgr, struct onoff_monitor *mon, uint32_t state, int res) { if (ntrans < ARRAY_SIZE(trans)) { trans[ntrans++] = (struct transition_record){ .state = state, .res = res, }; } } static void check_trans(uint32_t idx, uint32_t state, int res, const char *tag) { zassert_true(idx < ntrans, "trans idx %u high: %s", idx, tag); const struct transition_record *xp = trans + idx; zassert_equal(xp->state, state, "trans[%u] state %x != %x: %s", idx, xp->state, state, tag); zassert_equal(xp->res, res, "trans[%u] res %d != %d: %s", idx, xp->res, res, tag); } static struct onoff_manager *callback_srv; static struct onoff_client *callback_cli; static uint32_t callback_state; static int callback_res; static onoff_client_callback callback_fn; static void callback(struct onoff_manager *srv, struct onoff_client *cli, uint32_t state, int res) { onoff_client_callback cb = callback_fn; callback_srv = srv; callback_cli = cli; callback_state = state; callback_res = res; callback_fn = NULL; if (cb != NULL) { cb(srv, cli, state, res); } } static void check_callback(uint32_t state, int res, const char *tag) { zassert_equal(callback_state, state, "callback state %x != %x: %s", callback_state, state, tag); zassert_equal(callback_res, res, "callback res %d != %d: %s", callback_res, res, tag); } static inline int cli_result(const struct onoff_client *cp) { int result; int rc = sys_notify_fetch_result(&cp->notify, &result); if (rc == 0) { rc = result; } return rc; } static void check_result(int res, const char *tag) { zassert_equal(cli_result(&onoff_cli), res, "cli res %d != %d: %s", cli_result(&onoff_cli), res, tag); } struct transit_state { const char *tag; bool async; int retval; onoff_notify_fn notify; struct onoff_manager *srv; }; static void reset_transit_state(struct transit_state *tsp) { tsp->async = false; tsp->retval = 0; tsp->notify = NULL; tsp->srv = NULL; } static void run_transit(struct onoff_manager *srv, onoff_notify_fn notify, struct transit_state *tsp) { if (tsp->async) { TC_PRINT("%s async\n", tsp->tag); tsp->notify = notify; tsp->srv = srv; } else { TC_PRINT("%s notify %d\n", tsp->tag, tsp->retval); notify(srv, tsp->retval); } } static void notify(struct transit_state *tsp) { TC_PRINT("%s settle %d %p\n", tsp->tag, tsp->retval, tsp->notify); tsp->notify(tsp->srv, tsp->retval); tsp->notify = NULL; tsp->srv = NULL; } static struct transit_state start_state = { .tag = "start", }; static void start(struct onoff_manager *srv, onoff_notify_fn notify_fn) { run_transit(srv, notify_fn, &start_state); } static struct transit_state stop_state = { .tag = "stop", }; static void stop(struct onoff_manager *srv, onoff_notify_fn notify_fn) { run_transit(srv, notify_fn, &stop_state); } static struct transit_state reset_state = { .tag = "reset", }; static void reset(struct onoff_manager *srv, onoff_notify_fn notify_fn) { run_transit(srv, notify_fn, &reset_state); } static struct k_sem isr_sync; static struct k_timer isr_timer; static void isr_notify(struct k_timer *timer) { struct transit_state *tsp = k_timer_user_data_get(timer); TC_PRINT("ISR NOTIFY %s %d\n", tsp->tag, k_is_in_isr()); notify(tsp); k_sem_give(&isr_sync); } struct isr_call_state { struct onoff_manager *srv; struct onoff_client *cli; int result; }; static void isr_request(struct k_timer *timer) { struct isr_call_state *rsp = k_timer_user_data_get(timer); rsp->result = onoff_request(rsp->srv, rsp->cli); k_sem_give(&isr_sync); } static void isr_release(struct k_timer *timer) { struct isr_call_state *rsp = k_timer_user_data_get(timer); rsp->result = onoff_release(rsp->srv); k_sem_give(&isr_sync); } static void isr_reset(struct k_timer *timer) { struct isr_call_state *rsp = k_timer_user_data_get(timer); rsp->result = onoff_reset(rsp->srv, rsp->cli); k_sem_give(&isr_sync); } static void reset_cli(void) { onoff_cli = (struct onoff_client){}; sys_notify_init_callback(&onoff_cli.notify, callback); } static void reset_callback(void) { callback_state = -1; callback_res = 0; callback_fn = NULL; } static void setup_test(void) { int rc; reset_callback(); reset_transit_state(&start_state); reset_transit_state(&stop_state); reset_transit_state(&reset_state); ntrans = 0; transitions = (struct onoff_transitions) ONOFF_TRANSITIONS_INITIALIZER(start, stop, reset); rc = onoff_manager_init(&onoff_srv, &transitions); zassert_equal(rc, 0, "service init"); onoff_mon = (struct onoff_monitor){ .callback = trans_callback, }; rc = onoff_monitor_register(&onoff_srv, &onoff_mon); zassert_equal(rc, 0, "mon reg"); reset_cli(); } static void setup_error(void) { int rc; setup_test(); start_state.retval = -1; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req 0 0"); check_result(start_state.retval, "err req"); zassert_true(onoff_has_error(&onoff_srv), "has_err"); reset_cli(); } ZTEST(onoff_api, test_manager_init) { int rc; struct onoff_transitions xit = {}; setup_test(); rc = onoff_manager_init(NULL, NULL); zassert_equal(rc, -EINVAL, "init 0 0"); rc = onoff_manager_init(&onoff_srv, NULL); zassert_equal(rc, -EINVAL, "init srv 0"); rc = onoff_manager_init(NULL, &transitions); zassert_equal(rc, -EINVAL, "init 0 xit"); rc = onoff_manager_init(&onoff_srv, &xit); zassert_equal(rc, -EINVAL, "init 0 xit-start"); xit.start = start; rc = onoff_manager_init(&onoff_srv, &xit); zassert_equal(rc, -EINVAL, "init srv xit-stop"); xit.stop = stop; rc = onoff_manager_init(&onoff_srv, &xit); zassert_equal(rc, 0, "init srv xit ok"); } ZTEST(onoff_api, test_mon_reg) { static struct onoff_monitor mon = {}; setup_test(); /* Verify parameter validation */ zassert_equal(onoff_monitor_register(NULL, NULL), -EINVAL, "mon reg 0 0"); zassert_equal(onoff_monitor_register(&onoff_srv, NULL), -EINVAL, "mon reg srv 0"); zassert_equal(onoff_monitor_register(NULL, &mon), -EINVAL, "mon reg 0 mon"); zassert_equal(onoff_monitor_register(&onoff_srv, &mon), -EINVAL, "mon reg srv mon(!cb)"); } ZTEST(onoff_api, test_mon_unreg) { setup_test(); /* Verify parameter validation */ zassert_equal(onoff_monitor_unregister(NULL, NULL), -EINVAL, "mon unreg 0 0"); zassert_equal(onoff_monitor_unregister(&onoff_srv, NULL), -EINVAL, "mon unreg srv 0"); zassert_equal(onoff_monitor_unregister(NULL, &onoff_mon), -EINVAL, "mon unreg 0 mon"); zassert_equal(onoff_monitor_unregister(&onoff_srv, &onoff_mon), 0, "mon unreg 0 mon"); zassert_equal(onoff_monitor_unregister(&onoff_srv, &onoff_mon), -EINVAL, "mon unreg 0 mon"); } ZTEST(onoff_api, test_request) { struct onoff_client cli2 = {}; int rc; setup_test(); rc = onoff_request(NULL, NULL); zassert_equal(rc, -EINVAL, "req 0 0"); rc = onoff_request(&onoff_srv, NULL); zassert_equal(rc, -EINVAL, "req srv 0"); rc = onoff_request(NULL, &onoff_cli); zassert_equal(rc, -EINVAL, "req 0 cli"); rc = onoff_request(&onoff_srv, &cli2); zassert_equal(rc, -EINVAL, "req srv cli-uninit"); onoff_cli.notify.flags |= BIT(ONOFF_CLIENT_EXTENSION_POS); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, -EINVAL, "req srv cli-flags"); onoff_cli.notify.flags &= ~BIT(ONOFF_CLIENT_EXTENSION_POS); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, 0, "req srv cli ok"); reset_cli(); onoff_srv.refs = -1; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, -EAGAIN, "req srv cli ofl"); } ZTEST(onoff_api, test_basic_sync) { int rc; /* Verify synchronous request and release behavior. */ setup_test(); start_state.retval = 16; stop_state.retval = 23; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req: %d", rc); zassert_equal(onoff_srv.refs, 1U, "req refs: %u", onoff_srv.refs); check_result(start_state.retval, "req"); zassert_equal(callback_srv, &onoff_srv, "callback wrong srv"); zassert_equal(callback_cli, &onoff_cli, "callback wrong cli"); check_callback(ONOFF_STATE_ON, start_state.retval, "req"); zassert_equal(ntrans, 2U, "req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); rc = onoff_release(&onoff_srv); zassert_equal(rc, ONOFF_STATE_ON, "rel: %d", rc); zassert_equal(onoff_srv.refs, 0U, "rel refs: %u", onoff_srv.refs); zassert_equal(ntrans, 4U, "rel trans"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); rc = onoff_release(&onoff_srv); zassert_equal(rc, -ENOTSUP, "re-rel: %d", rc); } ZTEST(onoff_api, test_basic_async) { int rc; /* Verify asynchronous request and release behavior. */ setup_test(); start_state.async = true; start_state.retval = 51; stop_state.async = true; stop_state.retval = 17; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "async req: %d", rc); zassert_equal(onoff_srv.refs, 0U, "to-on refs: %u", onoff_srv.refs); check_result(-EAGAIN, "async req"); zassert_equal(ntrans, 1U, "async req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); notify(&start_state); zassert_equal(onoff_srv.refs, 1U, "on refs: %u", onoff_srv.refs); check_result(start_state.retval, "async req"); zassert_equal(ntrans, 2U, "async req trans"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); rc = onoff_release(&onoff_srv); zassert_true(rc >= 0, "rel: %d", rc); zassert_equal(onoff_srv.refs, 0U, "on refs: %u", onoff_srv.refs); zassert_equal(ntrans, 3U, "async rel trans"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); notify(&stop_state); zassert_equal(onoff_srv.refs, 0U, "rel refs: %u", onoff_srv.refs); zassert_equal(ntrans, 4U, "rel trans"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); } ZTEST(onoff_api, test_reset) { struct onoff_client cli2 = {}; int rc; setup_error(); reset_cli(); rc = onoff_reset(NULL, NULL); zassert_equal(rc, -EINVAL, "rst 0 0"); rc = onoff_reset(&onoff_srv, NULL); zassert_equal(rc, -EINVAL, "rst srv 0"); rc = onoff_reset(NULL, &onoff_cli); zassert_equal(rc, -EINVAL, "rst 0 cli"); rc = onoff_reset(&onoff_srv, &cli2); zassert_equal(rc, -EINVAL, "rst srv cli-cfg"); transitions.reset = NULL; rc = onoff_reset(&onoff_srv, &onoff_cli); zassert_equal(rc, -ENOTSUP, "rst srv cli-cfg"); transitions.reset = reset; rc = onoff_reset(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_ERROR, "rst srv cli"); reset_cli(); rc = onoff_reset(&onoff_srv, &onoff_cli); zassert_equal(rc, -EALREADY, "re-rst srv cli"); } ZTEST(onoff_api, test_basic_reset) { int rc; /* Verify that reset works. */ setup_error(); zassert_equal(ntrans, 2U, "err trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(1, ONOFF_STATE_ERROR, start_state.retval, "trans on"); reset_cli(); reset_state.retval = 12; rc = onoff_reset(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_ERROR, "rst"); check_result(reset_state.retval, "rst"); zassert_equal(ntrans, 4U, "err trans"); check_trans(2, ONOFF_STATE_RESETTING, 0, "trans resetting"); check_trans(3, ONOFF_STATE_OFF, reset_state.retval, "trans off"); } ZTEST(onoff_api, test_multi_start) { int rc; struct onoff_client cli2 = {}; /* Verify multiple requests are satisfied when start * transition completes. */ setup_test(); start_state.async = true; start_state.retval = 16; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req: %d", rc); zassert_equal(onoff_srv.refs, 0U, "req refs: %u", onoff_srv.refs); check_result(-EAGAIN, "req"); zassert_equal(ntrans, 1U, "req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); sys_notify_init_spinwait(&cli2.notify); rc = onoff_request(&onoff_srv, &cli2); zassert_equal(rc, ONOFF_STATE_TO_ON, "req2: %d", rc); zassert_equal(cli_result(&cli2), -EAGAIN, "req2 result"); notify(&start_state); zassert_equal(ntrans, 2U, "async req trans"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); check_result(start_state.retval, "req"); zassert_equal(cli_result(&cli2), start_state.retval, "req2"); } ZTEST(onoff_api, test_indep_req) { int rc; struct onoff_client cli0 = {}; /* Verify that requests and releases while on behave as * expected. */ setup_test(); sys_notify_init_spinwait(&cli0.notify); start_state.retval = 62; rc = onoff_request(&onoff_srv, &cli0); zassert_equal(rc, ONOFF_STATE_OFF, "req0: %d", rc); zassert_equal(onoff_srv.refs, 1U, "req0 refs: %u", onoff_srv.refs); zassert_equal(cli_result(&cli0), start_state.retval, "req0 result"); zassert_equal(ntrans, 2U, "req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); ++start_state.retval; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_ON, "req: %d", rc); check_result(0, "req"); zassert_equal(ntrans, 2U, "async req trans"); zassert_equal(onoff_srv.refs, 2U, "srv refs: %u", onoff_srv.refs); rc = onoff_release(&onoff_srv); /* pair with cli0 */ zassert_equal(rc, ONOFF_STATE_ON, "rel: %d", rc); zassert_equal(onoff_srv.refs, 1U, "srv refs"); zassert_equal(ntrans, 2U, "async req trans"); rc = onoff_release(&onoff_srv); /* pair with cli */ zassert_equal(rc, ONOFF_STATE_ON, "rel: %d", rc); zassert_equal(onoff_srv.refs, 0U, "srv refs"); zassert_equal(ntrans, 4U, "async req trans"); } ZTEST(onoff_api, test_delayed_req) { int rc; setup_test(); start_state.retval = 16; /* Verify that a request received while turning off is * processed on completion of the transition to off. */ rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req: %d", rc); check_result(start_state.retval, "req"); zassert_equal(ntrans, 2U, "req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); start_state.retval += 1; stop_state.async = true; stop_state.retval = 14; rc = onoff_release(&onoff_srv); zassert_true(rc >= 0, "rel: %d", rc); zassert_equal(onoff_srv.refs, 0U, "on refs: %u", onoff_srv.refs); zassert_equal(ntrans, 3U, "async rel trans"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); reset_cli(); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_TO_OFF, "del req: %d", rc); zassert_equal(ntrans, 3U, "async rel trans"); check_result(-EAGAIN, "del req"); notify(&stop_state); check_result(start_state.retval, "del req"); zassert_equal(ntrans, 6U, "req trans"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); check_trans(4, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(5, ONOFF_STATE_ON, start_state.retval, "trans on"); } ZTEST(onoff_api, test_recheck_start) { int rc; /* Verify fast-path recheck when entering ON with no clients. * * This removes the monitor which bypasses the unlock region * in process_events() when there is no client and no * transition. */ setup_test(); rc = onoff_monitor_unregister(&onoff_srv, &onoff_mon); zassert_equal(rc, 0, "mon unreg"); start_state.async = true; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req"); rc = onoff_cancel(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_TO_ON, "cancel"); notify(&start_state); zassert_equal(onoff_srv.flags, ONOFF_STATE_OFF, "completed"); } ZTEST(onoff_api, test_recheck_stop) { int rc; /* Verify fast-path recheck when entering OFF with clients. * * This removes the monitor which bypasses the unlock region * in process_events() when there is no client and no * transition. */ setup_test(); rc = onoff_monitor_unregister(&onoff_srv, &onoff_mon); zassert_equal(rc, 0, "mon unreg"); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req"); check_result(start_state.retval, "req"); stop_state.async = true; rc = onoff_release(&onoff_srv); zassert_equal(rc, ONOFF_STATE_ON, "rel"); reset_cli(); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_TO_OFF, "delayed req"); check_result(-EAGAIN, "delayed req"); notify(&stop_state); zassert_equal(onoff_srv.flags, ONOFF_STATE_ON, "completed"); } static void rel_in_req_cb(struct onoff_manager *srv, struct onoff_client *cli, uint32_t state, int res) { int rc = onoff_release(srv); zassert_equal(rc, ONOFF_STATE_ON, "rel-in-req"); } ZTEST(onoff_api, test_rel_in_req_cb) { int rc; /* Verify that a release invoked during the request completion * callback is processed to final state. */ setup_test(); callback_fn = rel_in_req_cb; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req"); zassert_equal(callback_fn, NULL, "invoke"); zassert_equal(ntrans, 4U, "req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); } ZTEST(onoff_api, test_multi_reset) { int rc; struct onoff_client cli2 = {}; /* Verify multiple reset requests are satisfied when reset * transition completes. */ setup_test(); start_state.retval = -23; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req err"); check_result(start_state.retval, "req err"); zassert_true(onoff_has_error(&onoff_srv), "has_error"); zassert_equal(ntrans, 2U, "err trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(1, ONOFF_STATE_ERROR, start_state.retval, "trans on"); reset_state.async = true; reset_state.retval = 21; sys_notify_init_spinwait(&cli2.notify); rc = onoff_reset(&onoff_srv, &cli2); zassert_equal(rc, ONOFF_STATE_ERROR, "rst2"); zassert_equal(cli_result(&cli2), -EAGAIN, "rst2 result"); zassert_equal(ntrans, 3U, "rst trans"); check_trans(2, ONOFF_STATE_RESETTING, 0, "trans resetting"); reset_cli(); rc = onoff_reset(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_RESETTING, "rst"); zassert_equal(ntrans, 3U, "rst trans"); notify(&reset_state); zassert_equal(cli_result(&cli2), reset_state.retval, "rst2 result"); check_result(reset_state.retval, "rst"); zassert_equal(ntrans, 4U, "rst trans"); check_trans(3, ONOFF_STATE_OFF, reset_state.retval, "trans off"); } ZTEST(onoff_api, test_error) { struct onoff_client cli2 = {}; int rc; /* Verify rejected operations when error present. */ setup_error(); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, -EIO, "req in err"); rc = onoff_release(&onoff_srv); zassert_equal(rc, -EIO, "rel in err"); reset_state.async = true; sys_notify_init_spinwait(&cli2.notify); rc = onoff_reset(&onoff_srv, &cli2); zassert_equal(rc, ONOFF_STATE_ERROR, "rst"); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, -ENOTSUP, "req in err"); rc = onoff_release(&onoff_srv); zassert_equal(rc, -ENOTSUP, "rel in err"); } ZTEST(onoff_api, test_cancel_req) { int rc; setup_test(); start_state.async = true; start_state.retval = 14; rc = onoff_cancel(NULL, NULL); zassert_equal(rc, -EINVAL, "can 0 0"); rc = onoff_cancel(&onoff_srv, NULL); zassert_equal(rc, -EINVAL, "can srv 0"); rc = onoff_cancel(NULL, &onoff_cli); zassert_equal(rc, -EINVAL, "can 0 cli"); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "async req: %d", rc); check_result(-EAGAIN, "async req"); zassert_equal(ntrans, 1U, "req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); rc = onoff_cancel(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_TO_ON, "cancel req: %d", rc); rc = onoff_cancel(&onoff_srv, &onoff_cli); zassert_equal(rc, -EALREADY, "re-cancel req: %d", rc); zassert_equal(ntrans, 1U, "req trans"); notify(&start_state); zassert_equal(ntrans, 4U, "req trans"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); } ZTEST(onoff_api, test_cancel_delayed_req) { int rc; setup_test(); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req: %d", rc); check_result(start_state.retval, "req"); zassert_equal(ntrans, 2U, "req trans"); check_trans(0, ONOFF_STATE_TO_ON, 0, "trans to-on"); check_trans(1, ONOFF_STATE_ON, start_state.retval, "trans on"); stop_state.async = true; stop_state.retval = 14; rc = onoff_release(&onoff_srv); zassert_true(rc >= 0, "rel: %d", rc); zassert_equal(onoff_srv.refs, 0U, "on refs: %u", onoff_srv.refs); zassert_equal(ntrans, 3U, "async rel trans"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); reset_cli(); rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_TO_OFF, "del req: %d", rc); zassert_equal(ntrans, 3U, "async rel trans"); check_result(-EAGAIN, "del req"); rc = onoff_cancel(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_TO_OFF, "can del req: %d", rc); notify(&stop_state); zassert_equal(ntrans, 4U, "req trans"); check_trans(2, ONOFF_STATE_TO_OFF, 0, "trans to-off"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); } ZTEST(onoff_api, test_cancel_or_release) { int rc; /* First, verify that the cancel-or-release idiom works when * invoked in state TO-ON. */ setup_test(); start_state.async = true; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req"); rc = onoff_cancel_or_release(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_TO_ON, "c|r to-on"); notify(&start_state); zassert_equal(ntrans, 4U, "req trans"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); /* Now verify that the cancel-or-release idiom works when * invoked in state ON. */ setup_test(); start_state.async = false; rc = onoff_request(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_OFF, "req"); zassert_equal(ntrans, 2U, "req trans"); rc = onoff_cancel_or_release(&onoff_srv, &onoff_cli); zassert_equal(rc, ONOFF_STATE_ON, "c|r to-on"); zassert_equal(ntrans, 4U, "req trans"); check_trans(3, ONOFF_STATE_OFF, stop_state.retval, "trans off"); } ZTEST(onoff_api, test_sync_basic) { static struct onoff_sync_service srv = {}; k_spinlock_key_t key; int res = 5; int rc; reset_cli(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, 0, "init req"); rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true); zassert_equal(rc, 1, "req count"); zassert_equal(callback_srv, NULL, "sync cb srv"); zassert_equal(callback_cli, &onoff_cli, "sync cb cli"); check_callback(ONOFF_STATE_ON, res, "sync req"); reset_cli(); reset_callback(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, 1, "init rel"); ++res; rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true); zassert_equal(rc, 2, "req2 count"); check_callback(ONOFF_STATE_ON, res, "sync req2"); reset_cli(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, 2, "init rel"); rc = onoff_sync_finalize(&srv, key, NULL, res, false); zassert_equal(rc, 1, "rel count"); reset_cli(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, 1, "init rel2"); rc = onoff_sync_finalize(&srv, key, NULL, res, false); zassert_equal(rc, 0, "rel2 count"); /* Extra release is caught and diagnosed. May not happen with * onoff manager, but we can/should do it for sync. */ reset_cli(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, 0, "init rel2"); rc = onoff_sync_finalize(&srv, key, NULL, res, false); zassert_equal(rc, -1, "rel-1 count"); /* Error state is visible to next lock. */ reset_cli(); reset_callback(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, -1, "init req"); } ZTEST(onoff_api, test_sync_error) { static struct onoff_sync_service srv = {}; k_spinlock_key_t key; int res = -EPERM; int rc; reset_cli(); reset_callback(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, 0, "init req"); rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true); zassert_equal(rc, res, "err final"); zassert_equal(srv.count, res, "srv err count"); zassert_equal(callback_srv, NULL, "sync cb srv"); zassert_equal(callback_cli, &onoff_cli, "sync cb cli"); check_callback(ONOFF_STATE_ERROR, res, "err final"); /* Error is visible to next operation (the value is the * negative count) */ reset_cli(); reset_callback(); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, -1, "init req"); /* Error is cleared by non-negative finalize result */ res = 3; rc = onoff_sync_finalize(&srv, key, &onoff_cli, res, true); zassert_equal(rc, 1, "req count %d", rc); check_callback(ONOFF_STATE_ON, res, "sync req"); rc = onoff_sync_lock(&srv, &key); zassert_equal(rc, 1, "init rel"); } void *test_init(void) { k_sem_init(&isr_sync, 0, 1); k_timer_init(&isr_timer, isr_notify, NULL); (void)isr_reset; (void)isr_release; (void)isr_request; return NULL; } ZTEST_SUITE(onoff_api, NULL, test_init, NULL, NULL, NULL);