chrome-ec/test/usb_pe_drp.c

286 lines
8.2 KiB
C

/* Copyright 2020 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.
*
* Test USB PE module.
*/
#include "common.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"
#include "mock/usb_tc_sm_mock.h"
#include "mock/tcpc_mock.h"
#include "mock/usb_mux_mock.h"
#include "mock/usb_pd_dpm_mock.h"
#include "mock/dp_alt_mode_mock.h"
#include "mock/usb_prl_mock.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 usb_muxes[CONFIG_USB_PD_PORT_MAX_COUNT] = {
{
.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();
/* 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 pd_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.
*/
test_static int finish_src_discovery(void)
{
int i;
/* Expect GET_SOURCE_CAP, reply NOT_SUPPORTED. */
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP,
PD_CTRL_GET_SOURCE_CAP, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
rx_message(PD_MSG_SOP, PD_CTRL_NOT_SUPPORTED, 0,
PD_ROLE_SINK, PD_ROLE_UFP, 0);
/*
* Expect GET_SINK_CAP, reply with a simple Sink Cap since sink partners
* must support this message.
*/
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP,
PD_CTRL_GET_SINK_CAP, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
rx_message(PD_MSG_SOP, 0, PD_DATA_SINK_CAP,
PD_ROLE_SINK, PD_ROLE_UFP,
PDO_FIXED(5000, 500, PDO_FIXED_COMM_CAP));
/*
* Cable identity discovery is attempted 6 times total. 1 was done
* above, so expect 5 more now.
*/
for (i = 0; i < 5; i++) {
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP_PRIME,
0, PD_DATA_VENDOR_DEF,
60 * MSEC),
EC_SUCCESS, "%d");
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPC_TX_SOP_PRIME);
}
/* Expect VENDOR_DEF for partner identity, reply NOT_SUPPORTED. */
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP,
0, PD_DATA_VENDOR_DEF, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(10 * MSEC);
rx_message(PD_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, TCPC_TX_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, TCPC_TX_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, TCPC_TX_SOP_PRIME,
0, PD_DATA_VENDOR_DEF, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPC_TX_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, TCPC_TX_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(PD_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, TCPC_TX_SOP,
PD_CTRL_ACCEPT, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP,
PD_CTRL_PS_RDY, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(finish_src_discovery(), 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, TCPC_TX_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(PD_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, TCPC_TX_SOP,
PD_CTRL_ACCEPT, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP,
PD_CTRL_PS_RDY, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
/*
* Expect VENDOR_DEF for cable identity, simulate no cable (so no
* GoodCRC, so ERR_TCH_XMIT). Don't reply NOT_SUPPORTED, since the spec
* says a cable never does that.
* TODO: Add tests for cable replying to identity, and replying
* NOT_SUPPORTED (since we should be robust to cables doing the wrong
* thing).
*/
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP_PRIME,
0, PD_DATA_VENDOR_DEF, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPC_TX_SOP_PRIME);
TEST_EQ(finish_src_discovery(), 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(PD_MSG_SOP, PD_CTRL_GET_SOURCE_CAP, 0,
PD_ROLE_SINK, PD_ROLE_UFP, 0);
TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP,
0, PD_DATA_SOURCE_CAP, 10 * MSEC),
EC_SUCCESS, "%d");
/* Simulate error sending SOURCE_CAP. */
mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPC_TX_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, TCPC_TX_SOP,
PD_CTRL_SOFT_RESET, 0, 10 * MSEC),
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
task_wait_event(5 * SECOND);
return EC_SUCCESS;
}
void run_test(int argc, char **argv)
{
test_reset();
RUN_TEST(test_send_caps_error_before_connected);
RUN_TEST(test_send_caps_error_when_connected);
/* Do basic state machine validity checks last. */
RUN_TEST(test_pe_no_parent_cycles);
test_print_result();
}