blob: c4d2c924e469ae8f0292f0054b51bae01a70cee3 [file] [log] [blame]
/* Copyright 2020 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Test USB PE module.
*/
#include "common.h"
#include "mock/charge_manager_mock.h"
#include "mock/dp_alt_mode_mock.h"
#include "mock/tcpc_mock.h"
#include "mock/usb_mux_mock.h"
#include "mock/usb_pd_dpm_mock.h"
#include "mock/usb_prl_mock.h"
#include "mock/usb_tc_sm_mock.h"
#include "task.h"
#include "test_util.h"
#include "timer.h"
#include "usb_emsg.h"
#include "usb_mux.h"
#include "usb_pe.h"
#include "usb_pe_sm.h"
#include "usb_sm_checks.h"
/* Install Mock TCPC and MUX drivers */
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_MAX_COUNT] = {
{
.drv = &mock_tcpc_driver,
},
};
const struct usb_mux_chain usb_muxes[CONFIG_USB_PD_PORT_MAX_COUNT] = { {
.mux =
&(const struct usb_mux){
.driver = &mock_usb_mux_driver,
},
} };
void before_test(void)
{
mock_tc_port_reset();
mock_tcpc_reset();
mock_usb_mux_reset();
mock_dpm_reset();
mock_dp_alt_mode_reset();
mock_prl_reset();
pe_clear_port_data(PORT0);
/* Restart the PD task and let it settle */
task_set_event(TASK_ID_PD_C0, TASK_EVENT_RESET_DONE);
task_wait_event(SECOND);
}
/*
* This assumes data messages only contain a single data object (uint32_t data).
* TODO: Add support for multiple data objects (when a test is added here that
* needs it).
*/
test_static void rx_message(enum tcpci_msg_type sop,
enum pd_ctrl_msg_type ctrl_msg,
enum pd_data_msg_type data_msg,
enum pd_power_role prole, enum pd_data_role drole,
uint32_t data)
{
int type, cnt;
if (ctrl_msg != 0) {
type = ctrl_msg;
cnt = 0;
} else {
type = data_msg;
cnt = 1;
}
rx_emsg[PORT0].header =
(PD_HEADER_SOP(sop) |
PD_HEADER(type, prole, drole, 0, cnt, PD_REV30, 0));
rx_emsg[PORT0].len = cnt * 4;
*(uint32_t *)rx_emsg[PORT0].buf = data;
mock_prl_message_received(PORT0);
}
/*
* This sequence is used by multiple tests, so pull out into a function to
* avoid duplication.
*
* Send in how many SOP' DiscoverIdentity requests have been processed so far,
* as this may vary depending on startup sequencing as a source.
*/
test_static int finish_src_discovery(int startup_cable_probes)
{
int i;
/* Expect GET_SOURCE_CAP, reply NOT_SUPPORTED. */
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP,
PD_CTRL_GET_SOURCE_CAP, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
rx_message(TCPCI_MSG_SOP, PD_CTRL_NOT_SUPPORTED, 0, PD_ROLE_SINK,
PD_ROLE_UFP, 0);
/* Expect GET_REVISION, reply NOT_SUPPORTED. */
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP,
PD_CTRL_GET_REVISION, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
rx_message(TCPCI_MSG_SOP, PD_CTRL_NOT_SUPPORTED, 0, PD_ROLE_SINK,
PD_ROLE_UFP, 0);
/*
* Cable identity discovery is attempted 6 times total. 1 was done
* above, so expect 5 more now.
*/
for (i = startup_cable_probes; i < 6; i++) {
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP_PRIME, 0,
PD_DATA_VENDOR_DEF, 60 * MSEC),
EC_SUCCESS, "%d");
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPCI_MSG_SOP_PRIME);
}
/* Expect VENDOR_DEF for partner identity, reply NOT_SUPPORTED. */
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, 0,
PD_DATA_VENDOR_DEF, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
rx_message(TCPCI_MSG_SOP, PD_CTRL_NOT_SUPPORTED, 0, PD_ROLE_SINK,
PD_ROLE_UFP, 0);
return EC_SUCCESS;
}
/*
* Verify that, before connection, PE_SRC_Send_Capabilities goes to
* PE_SRC_Discovery on send error, not PE_Send_Soft_Reset.
*/
test_static int test_send_caps_error_before_connected(void)
{
/* Enable PE as source, expect SOURCE_CAP. */
mock_tc_port[PORT0].power_role = PD_ROLE_SOURCE;
mock_tc_port[PORT0].pd_enable = 1;
mock_tc_port[PORT0].vconn_src = true;
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, 0,
PD_DATA_SOURCE_CAP, 10 * MSEC),
EC_SUCCESS, "%d");
/*
* Simulate error sending SOURCE_CAP, to test that before connection,
* PE_SRC_Send_Capabilities goes to PE_SRC_Discovery on send error (and
* does not send soft reset).
*/
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPCI_MSG_SOP);
/*
* We should have gone to PE_SRC_Discovery on above error, so expect
* VENDOR_DEF for cable identity, simulate no cable.
*/
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP_PRIME, 0,
PD_DATA_VENDOR_DEF, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPCI_MSG_SOP_PRIME);
/*
* Expect SOURCE_CAP again. This is a retry since the first one above
* got ERR_TCH_XMIT. Now simulate success (ie GoodCRC).
*/
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, 0,
PD_DATA_SOURCE_CAP, 110 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
/*
* From here, the sequence is very similar between
* test_send_caps_error_before_connected and
* test_send_caps_error_when_connected. We could end the test now, but
* keep going just to check that the slightly different ordering of
* cable identity discovery doesn't cause any issue below.
*/
/* REQUEST 5V, expect ACCEPT, PS_RDY. */
rx_message(TCPCI_MSG_SOP, 0, PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
RDO_FIXED(1, 500, 500, 0));
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, PD_CTRL_ACCEPT,
0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, PD_CTRL_PS_RDY,
0, 35 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(finish_src_discovery(1), EC_SUCCESS, "%d");
task_wait_event(5 * SECOND);
return EC_SUCCESS;
}
/*
* Verify that, after connection, PE_SRC_Send_Capabilities goes to
* PE_Send_Soft_Reset on send error, not PE_SRC_Discovery.
*/
test_static int test_send_caps_error_when_connected(void)
{
/* Enable PE as source, expect SOURCE_CAP. */
mock_tc_port[PORT0].power_role = PD_ROLE_SOURCE;
mock_tc_port[PORT0].pd_enable = 1;
mock_tc_port[PORT0].vconn_src = true;
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, 0,
PD_DATA_SOURCE_CAP, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
/* REQUEST 5V, expect ACCEPT, PS_RDY. */
rx_message(TCPCI_MSG_SOP, 0, PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
RDO_FIXED(1, 500, 500, 0));
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, PD_CTRL_ACCEPT,
0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, PD_CTRL_PS_RDY,
0, 35 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(finish_src_discovery(0), EC_SUCCESS, "%d");
task_wait_event(5 * SECOND);
/*
* Now connected. Send GET_SOURCE_CAP, to check how error sending
* SOURCE_CAP is handled.
*/
rx_message(TCPCI_MSG_SOP, PD_CTRL_GET_SOURCE_CAP, 0, PD_ROLE_SINK,
PD_ROLE_UFP, 0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, 0,
PD_DATA_SOURCE_CAP, 10 * MSEC),
EC_SUCCESS, "%d");
/* Simulate error sending SOURCE_CAP. */
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPCI_MSG_SOP);
/*
* Expect SOFT_RESET.
* See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State and section
* 8.3.3.2.3 PE_SRC_Send_Capabilities State.
* "The PE_SRC_Send_Soft_Reset state Shall be entered from any state
* when ... A Message has not been sent after retries to the Sink"
*/
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP,
PD_CTRL_SOFT_RESET, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(5 * SECOND);
return EC_SUCCESS;
}
/*
* Verify that when PR swap is interrupted during power transitiong, hard
* reset is sent
*/
test_static int test_interrupting_pr_swap(void)
{
/* Enable PE as source, expect SOURCE_CAP. */
mock_tc_port[PORT0].power_role = PD_ROLE_SOURCE;
mock_tc_port[PORT0].pd_enable = 1;
mock_tc_port[PORT0].vconn_src = true;
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, 0,
PD_DATA_SOURCE_CAP, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
/* REQUEST 5V, expect ACCEPT, PS_RDY. */
rx_message(TCPCI_MSG_SOP, 0, PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP,
RDO_FIXED(1, 500, 500, 0));
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, PD_CTRL_ACCEPT,
0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, PD_CTRL_PS_RDY,
0, 35 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(finish_src_discovery(0), EC_SUCCESS, "%d");
task_wait_event(5 * SECOND);
/*
* Now connected. Initiate a PR swap and then interrupt it after the
* Accept, when power is transitioning to off.
*/
rx_message(TCPCI_MSG_SOP, PD_CTRL_PR_SWAP, 0, PD_ROLE_SINK, PD_ROLE_UFP,
0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_SOP, PD_CTRL_ACCEPT,
0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(5 * SECOND);
/* Interrupt the non-interruptible AMS */
rx_message(TCPCI_MSG_SOP, PD_CTRL_PR_SWAP, 0, PD_ROLE_SINK, PD_ROLE_UFP,
0);
/*
* Expect a hard reset since power was transitioning during this
* interruption
*/
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPCI_MSG_TX_HARD_RESET, 0, 0,
10 * MSEC),
EC_SUCCESS, "%d");
return EC_SUCCESS;
}
void run_test(int argc, const char **argv)
{
test_reset();
RUN_TEST(test_send_caps_error_before_connected);
RUN_TEST(test_send_caps_error_when_connected);
RUN_TEST(test_interrupting_pr_swap);
/* Do basic state machine validity checks last. */
RUN_TEST(test_pe_no_parent_cycles);
test_print_result();
}