/* Copyright 2014 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "console.h" #include "ec_commands.h" #include "lightbar.h" #include "host_command.h" #include "test_util.h" #include "timer.h" #include "util.h" static int get_seq(void) { int rv; struct ec_params_lightbar params; struct ec_response_lightbar resp; /* Get the state */ memset(&resp, 0, sizeof(resp)); params.cmd = LIGHTBAR_CMD_GET_SEQ; rv = test_send_host_command(EC_CMD_LIGHTBAR_CMD, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); if (rv != EC_RES_SUCCESS) { ccprintf("%s:%s(): rv = %d\n", __FILE__, __func__, rv); return -1; } return resp.get_seq.num; } static int set_seq(int s) { int rv; struct ec_params_lightbar params; struct ec_response_lightbar resp; /* Get the state */ memset(&resp, 0, sizeof(resp)); params.cmd = LIGHTBAR_CMD_SEQ; params.seq.num = s; rv = test_send_host_command(EC_CMD_LIGHTBAR_CMD, 0, ¶ms, sizeof(params), &resp, sizeof(resp)); if (rv != EC_RES_SUCCESS) { ccprintf("%s:%s(): rv = %d\n", __FILE__, __func__, rv); return -1; } return EC_RES_SUCCESS; } static int test_double_oneshots(void) { /* Start in S0 */ TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); /* Invoke the oneshot */ TEST_ASSERT(set_seq(LIGHTBAR_TEST) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_TEST); /* Switch to a different oneshot while that one's running */ TEST_ASSERT(set_seq(LIGHTBAR_KONAMI) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_KONAMI); /* Afterwards, it should go back to the original normal state */ msleep(30 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); /* Same test, but with a bunch more oneshots. */ TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); TEST_ASSERT(set_seq(LIGHTBAR_TEST) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_TEST); TEST_ASSERT(set_seq(LIGHTBAR_KONAMI) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_KONAMI); TEST_ASSERT(set_seq(LIGHTBAR_TAP) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_TAP); TEST_ASSERT(set_seq(LIGHTBAR_PULSE) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_PULSE); TEST_ASSERT(set_seq(LIGHTBAR_KONAMI) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_KONAMI); /* It should still go back to the original normal state */ msleep(30 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); /* But if the interruption is a normal state, that should stick. */ TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); TEST_ASSERT(set_seq(LIGHTBAR_TEST) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_TEST); TEST_ASSERT(set_seq(LIGHTBAR_KONAMI) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_KONAMI); /* Here's a normal sequence */ TEST_ASSERT(set_seq(LIGHTBAR_S3) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S3); /* And another one-shot */ TEST_ASSERT(set_seq(LIGHTBAR_TAP) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_TAP); TEST_ASSERT(set_seq(LIGHTBAR_PULSE) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_PULSE); TEST_ASSERT(set_seq(LIGHTBAR_KONAMI) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_KONAMI); /* It should go back to the new normal sequence */ msleep(30 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S3); return EC_SUCCESS; } static int test_oneshots_norm_msg(void) { /* Revert to the next state when interrupted with a normal message. */ enum lightbar_sequence seqs[] = { LIGHTBAR_PULSE, LIGHTBAR_TEST, LIGHTBAR_KONAMI, LIGHTBAR_TAP, }; int i; for (i = 0; i < ARRAY_SIZE(seqs); i++) { /* Start in S0 */ TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); msleep(MSEC); /* Invoke the oneshot */ TEST_ASSERT(set_seq(seqs[i]) == EC_RES_SUCCESS); msleep(MSEC); /* Interrupt with S0S3 */ TEST_ASSERT(set_seq(LIGHTBAR_S0S3) == EC_RES_SUCCESS); msleep(MSEC); /* It should be back right away */ TEST_ASSERT(get_seq() == LIGHTBAR_S0S3); /* And transition on to the correct value */ msleep(30 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S3); } return EC_SUCCESS; } static int test_stop_timeout(void) { int i; for (i = 0; i < LIGHTBAR_NUM_SEQUENCES; i++) { /* Start in S0 */ TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); msleep(MSEC); /* Tell it to stop */ TEST_ASSERT(set_seq(LIGHTBAR_STOP) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_STOP); /* Try to interrupt it */ TEST_ASSERT(set_seq(i) == EC_RES_SUCCESS); msleep(MSEC); /* What happened? */ if (i == LIGHTBAR_RUN || i == LIGHTBAR_S0S3 || i == LIGHTBAR_S3 || i == LIGHTBAR_S3S5 || i == LIGHTBAR_S5) /* RUN or shutdown sequences should stop it */ TEST_ASSERT(get_seq() == LIGHTBAR_S0); else /* All other sequences should be ignored */ TEST_ASSERT(get_seq() == LIGHTBAR_STOP); /* Let it RUN again for the next iteration */ TEST_ASSERT(set_seq(LIGHTBAR_RUN) == EC_RES_SUCCESS); msleep(MSEC); } TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); return EC_SUCCESS; } static int test_oneshots_timeout(void) { /* These should revert to the previous state after running */ enum lightbar_sequence seqs[] = { LIGHTBAR_RUN, LIGHTBAR_TEST, LIGHTBAR_KONAMI, LIGHTBAR_TAP, }; int i; for (i = 0; i < ARRAY_SIZE(seqs); i++) { TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); msleep(MSEC); TEST_ASSERT(set_seq(seqs[i]) == EC_RES_SUCCESS); /* Assume the oneshot sequence takes at least a second (except * for LIGHTBAR_RUN, which returns immediately) */ if (seqs[i] != LIGHTBAR_RUN) { msleep(MSEC); TEST_ASSERT(get_seq() == seqs[i]); } msleep(30 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); } return EC_SUCCESS; } static int test_transition_states(void) { /* S5S3 */ TEST_ASSERT(set_seq(LIGHTBAR_S5S3) == EC_RES_SUCCESS); msleep(10 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S3); /* S3S0 */ TEST_ASSERT(set_seq(LIGHTBAR_S3S0) == EC_RES_SUCCESS); msleep(10 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); /* S0S3 */ TEST_ASSERT(set_seq(LIGHTBAR_S0S3) == EC_RES_SUCCESS); msleep(10 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S3); /* S3S5 */ TEST_ASSERT(set_seq(LIGHTBAR_S3S5) == EC_RES_SUCCESS); msleep(10 * MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S5); return EC_SUCCESS; } static int test_stable_states(void) { int i; /* Wait for the lightbar task to initialize */ msleep(500); /* It should come up in S5 */ TEST_ASSERT(get_seq() == LIGHTBAR_S5); /* It should stay there */ for (i = 0; i < 30; i++) { msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S5); } /* S3 is sticky, too */ TEST_ASSERT(set_seq(LIGHTBAR_S3) == EC_RES_SUCCESS); for (i = 0; i < 30; i++) { msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S3); } /* And S0 */ TEST_ASSERT(set_seq(LIGHTBAR_S0) == EC_RES_SUCCESS); for (i = 0; i < 30; i++) { msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_S0); } /* PULSE is stable too, but nobody should care. Test it anyway. */ TEST_ASSERT(set_seq(LIGHTBAR_PULSE) == EC_RES_SUCCESS); for (i = 0; i < 30; i++) { msleep(MSEC); TEST_ASSERT(get_seq() == LIGHTBAR_PULSE); } return EC_SUCCESS; } void run_test(void) { RUN_TEST(test_stable_states); RUN_TEST(test_transition_states); RUN_TEST(test_oneshots_timeout); RUN_TEST(test_stop_timeout); RUN_TEST(test_oneshots_norm_msg); RUN_TEST(test_double_oneshots); test_print_result(); }