1535 lines
42 KiB
C
1535 lines
42 KiB
C
/* Copyright 2019 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 Type-C VPD and CTVPD module.
|
||
*/
|
||
#include "common.h"
|
||
#include "crc.h"
|
||
#include "task.h"
|
||
#include "test_util.h"
|
||
#include "timer.h"
|
||
#include "usb_pd.h"
|
||
#include "usb_sm.h"
|
||
#include "usb_tc_sm.h"
|
||
#include "util.h"
|
||
#include "usb_pd_tcpm.h"
|
||
#include "usb_pd_test_util.h"
|
||
#include "usb_sm_checks.h"
|
||
#include "vpd_api.h"
|
||
|
||
#define PORT0 0
|
||
|
||
enum cc_type {CC1, CC2};
|
||
enum vbus_type {VBUS_0 = 0, VBUS_5 = 5000};
|
||
enum vconn_type {VCONN_0 = 0, VCONN_3 = 3000, VCONN_5 = 5000};
|
||
enum snk_con_voltage_type {SRC_CON_DEF, SRC_CON_1_5, SRC_CON_3_0};
|
||
|
||
/*
|
||
* These enum definitions are declared in usb_tc_*_sm and are private to that
|
||
* file. If those definitions are re-ordered, then we need to update these
|
||
* definitions (should be very rare).
|
||
*/
|
||
enum usb_tc_state {
|
||
/* Normal States */
|
||
TC_DISABLED,
|
||
TC_UNATTACHED_SNK,
|
||
TC_ATTACH_WAIT_SNK,
|
||
TC_ATTACHED_SNK,
|
||
TC_ERROR_RECOVERY,
|
||
TC_TRY_SNK,
|
||
TC_UNATTACHED_SRC,
|
||
TC_ATTACH_WAIT_SRC,
|
||
TC_TRY_WAIT_SRC,
|
||
TC_ATTACHED_SRC,
|
||
TC_CT_TRY_SNK,
|
||
TC_CT_ATTACH_WAIT_UNSUPPORTED,
|
||
TC_CT_ATTACHED_UNSUPPORTED,
|
||
TC_CT_UNATTACHED_UNSUPPORTED,
|
||
TC_CT_UNATTACHED_VPD,
|
||
TC_CT_DISABLED_VPD,
|
||
TC_CT_ATTACHED_VPD,
|
||
TC_CT_ATTACH_WAIT_VPD,
|
||
};
|
||
|
||
/* Defined in implementation */
|
||
enum usb_tc_state get_state_tc(const int port);
|
||
|
||
struct pd_port_t {
|
||
int host_mode;
|
||
int has_vbus;
|
||
int msg_tx_id;
|
||
int msg_rx_id;
|
||
int polarity;
|
||
int partner_role; /* -1 for none */
|
||
int partner_polarity;
|
||
int rev;
|
||
} pd_port[CONFIG_USB_PD_PORT_MAX_COUNT];
|
||
|
||
uint64_t wait_for_state_change(int port, uint64_t timeout)
|
||
{
|
||
uint64_t start;
|
||
uint64_t wait;
|
||
enum usb_tc_state state = get_state_tc(port);
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
|
||
wait = get_time().val + timeout;
|
||
start = get_time().val;
|
||
while (get_state_tc(port) == state && get_time().val < wait) {
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(1 * MSEC);
|
||
}
|
||
|
||
return get_time().val - start;
|
||
}
|
||
|
||
#if defined(TEST_USB_TYPEC_CTVPD)
|
||
static int ct_connect_sink(enum cc_type cc, enum snk_con_voltage_type v)
|
||
{
|
||
int ret;
|
||
|
||
switch (v) {
|
||
case SRC_CON_DEF:
|
||
ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_DEF_RD_THRESH_MV) :
|
||
mock_set_cc1_rp3a0_rd_l(PD_SRC_DEF_RD_THRESH_MV);
|
||
break;
|
||
case SRC_CON_1_5:
|
||
ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_1_5_RD_THRESH_MV) :
|
||
mock_set_cc1_rp3a0_rd_l(PD_SRC_1_5_RD_THRESH_MV);
|
||
break;
|
||
case SRC_CON_3_0:
|
||
ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_3_0_RD_THRESH_MV) :
|
||
mock_set_cc1_rp3a0_rd_l(PD_SRC_3_0_RD_THRESH_MV);
|
||
break;
|
||
default:
|
||
ret = 0;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
static int ct_disconnect_sink(void)
|
||
{
|
||
int r1;
|
||
int r2;
|
||
|
||
r1 = mock_set_cc1_rp3a0_rd_l(PD_SRC_DEF_VNC_MV);
|
||
r2 = mock_set_cc2_rp3a0_rd_l(PD_SRC_DEF_VNC_MV);
|
||
|
||
return r1 & r2;
|
||
}
|
||
|
||
static int ct_connect_source(enum cc_type cc, enum vbus_type vbus)
|
||
{
|
||
mock_set_ct_vbus(vbus);
|
||
return (cc) ? mock_set_cc2_rpusb_odh(PD_SNK_VA_MV) :
|
||
mock_set_cc1_rpusb_odh(PD_SNK_VA_MV);
|
||
}
|
||
|
||
static int ct_disconnect_source(void)
|
||
{
|
||
int r1;
|
||
int r2;
|
||
|
||
mock_set_ct_vbus(VBUS_0);
|
||
r1 = mock_set_cc1_rpusb_odh(0);
|
||
r2 = mock_set_cc2_rpusb_odh(0);
|
||
|
||
return r1 & r2;
|
||
}
|
||
#endif
|
||
|
||
static void host_disconnect_source(void)
|
||
{
|
||
mock_set_host_vbus(VBUS_0);
|
||
mock_set_host_cc_source_voltage(0);
|
||
mock_set_host_cc_sink_voltage(0);
|
||
}
|
||
|
||
static void host_connect_source(enum vbus_type vbus)
|
||
{
|
||
mock_set_host_vbus(vbus);
|
||
mock_set_host_cc_source_voltage(PD_SNK_VA_MV);
|
||
}
|
||
|
||
#if defined(TEST_USB_TYPEC_CTVPD)
|
||
static void host_connect_sink(enum snk_con_voltage_type v)
|
||
{
|
||
switch (v) {
|
||
case SRC_CON_DEF:
|
||
mock_set_host_cc_sink_voltage(PD_SRC_DEF_RD_THRESH_MV);
|
||
break;
|
||
case SRC_CON_1_5:
|
||
mock_set_host_cc_sink_voltage(PD_SRC_1_5_RD_THRESH_MV);
|
||
break;
|
||
case SRC_CON_3_0:
|
||
mock_set_host_cc_sink_voltage(PD_SRC_3_0_RD_THRESH_MV);
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static void init_port(int port)
|
||
{
|
||
pd_port[port].polarity = 0;
|
||
pd_port[port].rev = PD_REV30;
|
||
pd_port[port].msg_tx_id = 0;
|
||
pd_port[port].msg_rx_id = 0;
|
||
}
|
||
|
||
static int check_host_ra_rd(void)
|
||
{
|
||
/* Make sure CC_RP3A0_RD_L is configured as GPO */
|
||
if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_GPO)
|
||
return 0;
|
||
|
||
/* Make sure CC_RP3A0_RD_L is asserted low */
|
||
if (mock_get_cc_rp3a0_rd_l() != 0)
|
||
return 0;
|
||
|
||
/* Make sure VPDMCU_CC_EN is enabled */
|
||
if (mock_get_mcu_cc_en() != 1)
|
||
return 0;
|
||
|
||
/* Make sure CC_VPDMCU is configured as ADC */
|
||
if (mock_get_cfg_cc_vpdmcu() != PIN_ADC)
|
||
return 0;
|
||
|
||
/* Make sure CC_DB_EN_OD is HZ */
|
||
if (mock_get_cc_db_en_od() != GPO_HZ)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int check_host_rd(void)
|
||
{
|
||
/* Make sure CC_RP3A0_RD_L is configured as GPO */
|
||
if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_GPO)
|
||
return 0;
|
||
|
||
/* Make sure CC_RP3A0_RD_L is asserted low */
|
||
if (mock_get_cc_rp3a0_rd_l() != 0)
|
||
return 0;
|
||
|
||
/* Make sure VPDMCU_CC_EN is enabled */
|
||
if (mock_get_mcu_cc_en() != 1)
|
||
return 0;
|
||
|
||
/* Make sure CC_VPDMCU is configured as ADC */
|
||
if (mock_get_cfg_cc_vpdmcu() != PIN_ADC)
|
||
return 0;
|
||
|
||
/* Make sure CC_DB_EN_OD is LOW */
|
||
if (mock_get_cc_db_en_od() != GPO_LOW)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
#if defined(TEST_USB_TYPEC_CTVPD)
|
||
static int check_host_rp3a0(void)
|
||
{
|
||
/* Make sure CC_RP3A0_RD_L is asserted high */
|
||
if (mock_get_cc_rp3a0_rd_l() != 1)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int check_host_rpusb(void)
|
||
{
|
||
/* Make sure CC_RPUSB_ODH is asserted high */
|
||
if (mock_get_cc_rpusb_odh() != 1)
|
||
return 0;
|
||
|
||
/* Make sure CC_RP3A0_RD_L is configured as comparator */
|
||
if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_CMP)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int check_host_cc_open(void)
|
||
{
|
||
/* Make sure CC_RPUSB_ODH is hi-z */
|
||
if (mock_get_cc_rpusb_odh() != GPO_HZ)
|
||
return 0;
|
||
|
||
/* Make sure CC_RP3A0_RD_L is set to comparitor */
|
||
if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_CMP)
|
||
return 0;
|
||
|
||
/* Make sure cc_db_en_od is set low */
|
||
if (mock_get_cc_db_en_od() != GPO_LOW)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int check_ct_ccs_hz(void)
|
||
{
|
||
return (mock_get_ct_rd() == GPO_HIGH);
|
||
}
|
||
|
||
static int check_ct_ccs_rd(void)
|
||
{
|
||
return (mock_get_ct_rd() == GPO_LOW);
|
||
}
|
||
|
||
static int check_ct_ccs_cc1_rpusb(void)
|
||
{
|
||
return (mock_get_ct_cc1_rpusb() == 1);
|
||
}
|
||
#endif
|
||
|
||
void inc_tx_id(int port)
|
||
{
|
||
pd_port[port].msg_tx_id = (pd_port[port].msg_tx_id + 1) % 7;
|
||
}
|
||
|
||
void inc_rx_id(int port)
|
||
{
|
||
pd_port[port].msg_rx_id = (pd_port[port].msg_rx_id + 1) % 7;
|
||
}
|
||
|
||
static int verify_goodcrc(int port, int role, int id)
|
||
{
|
||
return pd_test_tx_msg_verify_sop_prime(port) &&
|
||
pd_test_tx_msg_verify_short(port, PD_HEADER(PD_CTRL_GOOD_CRC,
|
||
role, role, id, 0, 0, 0)) &&
|
||
pd_test_tx_msg_verify_crc(port) &&
|
||
pd_test_tx_msg_verify_eop(port);
|
||
}
|
||
|
||
static void simulate_rx_msg(int port, uint16_t header, int cnt,
|
||
const uint32_t *data)
|
||
{
|
||
int i;
|
||
|
||
pd_test_rx_set_preamble(port, 1);
|
||
pd_test_rx_msg_append_sop_prime(port);
|
||
pd_test_rx_msg_append_short(port, header);
|
||
|
||
crc32_init();
|
||
crc32_hash16(header);
|
||
|
||
for (i = 0; i < cnt; ++i) {
|
||
pd_test_rx_msg_append_word(port, data[i]);
|
||
crc32_hash32(data[i]);
|
||
}
|
||
|
||
pd_test_rx_msg_append_word(port, crc32_result());
|
||
|
||
pd_test_rx_msg_append_eop(port);
|
||
pd_test_rx_msg_append_last_edge(port);
|
||
|
||
pd_simulate_rx(port);
|
||
}
|
||
|
||
static void simulate_goodcrc(int port, int role, int id)
|
||
{
|
||
simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0,
|
||
pd_port[port].rev, 0), 0, NULL);
|
||
}
|
||
|
||
static void simulate_discovery_identity(int port)
|
||
{
|
||
uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF, PD_ROLE_SOURCE,
|
||
0, pd_port[port].msg_rx_id,
|
||
1, pd_port[port].rev, 0);
|
||
uint32_t msg = VDO(USB_SID_PD,
|
||
1, /* Structured VDM */
|
||
VDO_SVDM_VERS(1) |
|
||
VDO_CMDT(CMDT_INIT) |
|
||
CMD_DISCOVER_IDENT);
|
||
|
||
simulate_rx_msg(port, header, 1, (const uint32_t *)&msg);
|
||
}
|
||
|
||
static int test_vpd_host_src_detection(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host is configured properly and start state is UNATTACHED_SNK
|
||
*/
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host PORT Source Connection Detected
|
||
*/
|
||
|
||
host_connect_source(VBUS_0);
|
||
mock_set_vconn(VCONN_0);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host CC debounce in ATTACH_WAIT_SNK state
|
||
*/
|
||
|
||
host_disconnect_source();
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(5 * MSEC);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host CC debounce in ATTACH_WAIT_SNK state
|
||
*/
|
||
|
||
host_connect_source(VBUS_0);
|
||
mock_set_vconn(VCONN_0);
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(50 * MSEC);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Port Connection Removed
|
||
*/
|
||
host_disconnect_source();
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_vpd_host_src_detection_vbus(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host is configured properly and start state is UNATTACHED_SNK
|
||
*/
|
||
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Port Source Connection Detected
|
||
*/
|
||
|
||
host_connect_source(VBUS_0);
|
||
mock_set_vconn(VCONN_0);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Port Source Detected for tCCDebounce and Host Port VBUS
|
||
* Detected.
|
||
*/
|
||
|
||
host_connect_source(VBUS_5);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SNK);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Port VBUS Removed
|
||
*/
|
||
|
||
host_connect_source(VBUS_0);
|
||
|
||
/*
|
||
* The state changes from UNATTACHED_SNK to ATTACH_WAIT_SNK immediately
|
||
* if Rp is detected.
|
||
*/
|
||
wait_for_state_change(port, 10 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_vpd_host_src_detection_vconn(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host is configured properly and start state is UNATTACHED_SNK
|
||
*/
|
||
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Source Connection Detected
|
||
*/
|
||
|
||
host_connect_source(VBUS_0);
|
||
mock_set_vconn(VCONN_0);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Port Source Detected for tCCDebounce and VCONN Detected
|
||
*/
|
||
|
||
host_connect_source(VBUS_0);
|
||
mock_set_vconn(VCONN_3);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SNK);
|
||
|
||
/* VCONN was detected. Make sure RA is removed */
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
TEST_ASSERT(check_host_rd());
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Port VCONN Removed
|
||
*/
|
||
|
||
mock_set_host_cc_source_voltage(0);
|
||
mock_set_vconn(VCONN_0);
|
||
|
||
wait_for_state_change(port, 10 * MSEC);
|
||
|
||
TEST_EQ(get_state_tc(port), TC_UNATTACHED_SNK, "%d");
|
||
|
||
host_disconnect_source();
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_vpd_host_src_detection_message_reception(void)
|
||
{
|
||
int port = PORT0;
|
||
uint32_t expected_vdm_header = VDO(USB_VID_GOOGLE,
|
||
1, /* Structured VDM */
|
||
VDO_SVDM_VERS(1) |
|
||
VDO_CMDT(CMDT_RSP_ACK) |
|
||
CMD_DISCOVER_IDENT);
|
||
uint32_t expected_vdo_id_header = VDO_IDH(
|
||
0, /* Not a USB Host */
|
||
1, /* Capable of being enumerated as USB Device */
|
||
IDH_PTYPE_VPD,
|
||
0, /* Modal Operation Not Supported */
|
||
USB_VID_GOOGLE);
|
||
uint32_t expected_vdo_cert = 0;
|
||
uint32_t expected_vdo_product = VDO_PRODUCT(
|
||
CONFIG_USB_PID,
|
||
USB_BCD_DEVICE);
|
||
uint32_t expected_vdo_vpd = VDO_VPD(
|
||
VPD_HW_VERSION,
|
||
VPD_FW_VERSION,
|
||
VPD_MAX_VBUS_20V,
|
||
VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE),
|
||
VPD_GND_IMP(VPD_GND_IMPEDANCE),
|
||
#ifdef CONFIG_USB_CTVPD
|
||
VPD_CTS_SUPPORTED
|
||
#else
|
||
VPD_CTS_NOT_SUPPORTED
|
||
#endif
|
||
);
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host is configured properly and start state is UNATTACHED_SNK
|
||
*/
|
||
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
/*
|
||
* Transition to ATTACHED_SNK
|
||
*/
|
||
|
||
host_connect_source(VBUS_5);
|
||
|
||
wait_for_state_change(port, 10 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 20 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SNK);
|
||
|
||
/* Run state machines to enable rx monitoring */
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(30 * MSEC);
|
||
|
||
/*
|
||
* TEST:
|
||
* Reception of Discovery Identity message
|
||
*/
|
||
|
||
simulate_discovery_identity(port);
|
||
task_wait_event(30 * MSEC);
|
||
|
||
TEST_ASSERT(verify_goodcrc(port,
|
||
PD_ROLE_SINK, pd_port[port].msg_rx_id));
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(30 * MSEC);
|
||
inc_rx_id(port);
|
||
|
||
/* Test Discover Identity Ack */
|
||
TEST_ASSERT(pd_test_tx_msg_verify_sop_prime(port));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||
PD_HEADER(PD_DATA_VENDOR_DEF, PD_PLUG_FROM_CABLE, 0,
|
||
pd_port[port].msg_tx_id, 5, pd_port[port].rev, 0)));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdm_header));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_id_header));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_cert));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_product));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_vpd));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(30 * MSEC);
|
||
|
||
/* Ack was good. Send GoodCRC */
|
||
simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(30 * MSEC);
|
||
inc_tx_id(port);
|
||
|
||
/*
|
||
* TEST:
|
||
* Host Port VBUS Removed
|
||
*/
|
||
|
||
host_disconnect_source();
|
||
|
||
wait_for_state_change(port, 100 * MSEC);
|
||
|
||
TEST_EQ(get_state_tc(port), TC_UNATTACHED_SNK, "%d");
|
||
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
#if defined(TEST_USB_TYPEC_CTVPD)
|
||
static int test_ctvpd_behavior_case1(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
TEST_ASSERT(ct_disconnect_sink());
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* CASE 1: The following tests the behavior when a DRP is connected to a
|
||
* Charge-Through VCONN-Powered USB Device (abbreviated CTVPD),
|
||
* with no Power Source attached to the ChargeThrough port on
|
||
* the CTVPD.
|
||
*/
|
||
|
||
/* 1. DRP and CTVPD are both in the unattached state */
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
/*
|
||
* a. DRP alternates between Unattached.SRC and Unattached.SNK
|
||
*
|
||
* b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
|
||
* pins and Rd on the Host-side port’s CC pin
|
||
*/
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
|
||
/*
|
||
* 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
|
||
* Attached.SRC
|
||
*
|
||
* a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
|
||
* is in Unattached.SNK and DRP enters AttachWait.SRC
|
||
* b. DRP in AttachWait.SRC detects that pull down on CC persists for
|
||
* tCCDebounce, enters Attached.SRC and turns on VBUS and VCONN
|
||
*/
|
||
host_connect_source(VBUS_5);
|
||
mock_set_vconn(VCONN_3);
|
||
|
||
/*
|
||
* 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
|
||
* AttachWait.SNK.
|
||
*
|
||
* a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
|
||
* enters AttachWait.SNK
|
||
* b. CTVPD in AttachWait.SNK detects that pull up on the Host-side
|
||
* port’s CC persists for tCCDebounce, VCONN present and enters
|
||
* Attached.SNK
|
||
* c. CTVPD present a high-impedance to ground (above zOPEN) on its
|
||
* Charge-Through port’s CC1 and CC2 pins
|
||
*/
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SNK);
|
||
TEST_ASSERT(check_ct_ccs_hz());
|
||
|
||
/*
|
||
* 4. While DRP and CTVPD are in their respective attached states, DRP
|
||
* discovers the ChargeThrough CTVPD and transitions to
|
||
* CTUnattached.SNK
|
||
*
|
||
* a. DRP (as Source) queries the device identity via USB PD
|
||
* (Device Identity Command) on SOP’.
|
||
* b. CTVPD responds on SOP’, advertising that it is a
|
||
* Charge-Through VCONN-Powered USB Device
|
||
* c. DRP (as Source) removes VBUS
|
||
* d. DRP (as Source) changes its Rp to a Rd
|
||
* e. DRP (as Sink) continues to provide VCONN and enters
|
||
* CTUnattached.SNK
|
||
*/
|
||
host_disconnect_source();
|
||
|
||
/*
|
||
* 5. CTVPD transitions to CTUnattached.VPD
|
||
*
|
||
* a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
|
||
* CC pin and enters CTUnattached.VPD
|
||
* b. CTVPD changes its host-side Rd to a Rp advertising 3.0 A
|
||
* c. CTVPD isolates itself from VBUS
|
||
* d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
|
||
*/
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_VPD);
|
||
|
||
/*
|
||
* 6. While the CTVPD in CTUnattached.VPD state and the DRP in
|
||
* CTUnattached.SNK state:
|
||
*
|
||
* a. CTVPD monitors Charge-Though CC pins for a source or sink;
|
||
* when a Power Source attach is detected, enters
|
||
* CTAttachWait.VPD; when a sink is detected, enters
|
||
* CTAttachWait.Unsupported
|
||
* b. CTVPD monitors VCONN for Host detach and when detected, enters
|
||
* Unattached.SNK
|
||
* c. DRP monitors VBUS and CC for CTVPD detach for tVPDDetach and
|
||
* when detected, enters Unattached.SNK
|
||
* d. DRP monitors VBUS for Power Source attach and when detected,
|
||
* enters CTAttached.SNK
|
||
*/
|
||
/* Attach Power Source */
|
||
TEST_ASSERT(ct_connect_source(CC2, VBUS_0));
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
TEST_EQ(get_state_tc(port), TC_CT_ATTACH_WAIT_VPD, "%d");
|
||
|
||
/* Remove Power Source */
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_EQ(get_state_tc(port), TC_CT_UNATTACHED_VPD, "%d");
|
||
|
||
/* Attach Sink */
|
||
TEST_ASSERT(ct_connect_sink(CC1, SRC_CON_DEF));
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_EQ(get_state_tc(port), TC_CT_ATTACH_WAIT_UNSUPPORTED, "%d");
|
||
|
||
/* Remove VCONN (Host detach) */
|
||
mock_set_vconn(VCONN_0);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_EQ(get_state_tc(port), TC_UNATTACHED_SNK, "%d");
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_ctvpd_behavior_case2(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
TEST_ASSERT(ct_disconnect_sink());
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* CASE 2: The following tests the behavior when a Power Source is
|
||
* connected to a Charge-Through VCONN-Powered USB Device
|
||
* (abbreviated CTVPD), with a Host already attached to the
|
||
* Host-Side port on the CTVPD.
|
||
*/
|
||
|
||
/*
|
||
* 1. DRP is in CTUnattached.SNK state, CTVPD in CTUnattached.VPD, and
|
||
* Power Source in the unattached state
|
||
*
|
||
* a. CTVPD has applied Rd on the Charge-Through port’s CC1 and CC2
|
||
* pins and Rp termination advertising 3.0 A on the Host-side
|
||
* port’s CC pin
|
||
*/
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
host_connect_source(VBUS_5);
|
||
mock_set_vconn(VCONN_3);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SNK);
|
||
|
||
/* Remove Host CC */
|
||
mock_set_host_cc_source_voltage(0);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_VPD);
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
TEST_ASSERT(check_host_rp3a0());
|
||
|
||
/*
|
||
* 2. Power Source transitions from Unattached.SRC to Attached.SRC
|
||
* through AttachWait.SRC.
|
||
*
|
||
* a. Power Source detects the CC pull-down of the CTVPD and enters
|
||
* AttachWait.SRC
|
||
* b. Power Source in AttachWait.SRC detects that pull down on CC
|
||
* persists for tCCDebounce, enters Attached.SRC and turns on
|
||
* VBUS
|
||
*/
|
||
TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
|
||
|
||
/*
|
||
* 3. CTVPD transitions from CTUnattached.VPD through CTAttachWait.VPD
|
||
* to CTAttached.VPD
|
||
*
|
||
* a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
|
||
* pins, and transitions to CTAttachWait.VPD
|
||
* b. CTVPD finishes any active USB PD communication on SOP’ and
|
||
* ceases to respond to SOP’ queries
|
||
* c. CTVPD in CTAttachWait.VPD detects that the pull up on
|
||
* Charge-Through CC pin persists for tCCDebounce, detects VBUS
|
||
* and enters CTAttached.VPD
|
||
* d. CTVPD connects the active Charge-Through CC pin to the
|
||
* Host-side port’s CC pin
|
||
* e. CTVPD disables its Rp termination advertising 3.0 A on the
|
||
* Host-side port’s CC pin
|
||
* f. CTVPD disables its Rd on the Charge-Through CC pins
|
||
* g. CTVPD connects VBUS from the Charge-Through side to the Host
|
||
* side
|
||
*/
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_ATTACH_WAIT_VPD);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_ATTACHED_VPD);
|
||
TEST_ASSERT(moch_get_ct_cl_sel() == CT_CC2);
|
||
TEST_ASSERT(check_host_cc_open());
|
||
TEST_ASSERT(check_ct_ccs_hz());
|
||
TEST_ASSERT(mock_get_vbus_pass_en());
|
||
|
||
/*
|
||
* 4. DRP (as Sink) transitions to CTAttached.SNK
|
||
* a. DRP (as Sink) detects VBUS, monitors vRd for available current
|
||
* and enter CTAttached.SNK
|
||
*/
|
||
|
||
/*
|
||
* 5. While the devices are all in their respective attached states:
|
||
* a. CTVPD monitors VCONN for DRP detach and when detected,
|
||
* enters CTDisabled.VPD
|
||
* b. CTVPD monitors VBUS and CC for Power Source detach and when
|
||
* detected, enters CTUnattached.VPD within tVPDCTDD
|
||
* c. DRP (as Sink) monitors VBUS for Charge-Through Power Source
|
||
* detach and when detected, enters CTUnattached.SNK
|
||
* d. DRP (as Sink) monitors VBUS and CC for CTVPD detach and when
|
||
* detected, enters Unattached.SNK (and resumes toggling between
|
||
* Unattached.SNK and Unattached.SRC)
|
||
* e. Power Source monitors CC for CTVPD detach and when detected,
|
||
* enters Unattached.SRC
|
||
*/
|
||
mock_set_vconn(VCONN_0);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_DISABLED_VPD);
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_ctvpd_behavior_case3(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
TEST_ASSERT(ct_disconnect_sink());
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* CASE 3: The following describes the behavior when a Power Source is
|
||
* connected to a ChargeThrough VCONN-Powered USB Device
|
||
* (abbreviated CTVPD), with no Host attached to the Host-side
|
||
* port on the CTVPD.
|
||
*/
|
||
|
||
/*
|
||
* 1. CTVPD and Power Source are both in the unattached state
|
||
* a. CTVPD has applied Rd on the Charge-Through port’s CC1 and CC2
|
||
* pins and Rd on the Host-side port’s CC pin
|
||
*/
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
|
||
|
||
/*
|
||
* 2. Power Source transitions from Unattached.SRC to Attached.SRC
|
||
* through AttachWait.SRC.
|
||
*
|
||
* a. Power Source detects the CC pull-down of the CTVPD and enters
|
||
* AttachWait.SRC
|
||
* b. Power Source in AttachWait.SRC detects that pull down on CC
|
||
* persists for tCCDebounce, enters Attached.SRC and turns on
|
||
* VBUS
|
||
*/
|
||
|
||
/* 3. CTVPD alternates between Unattached.SNk and Unattached.SRC
|
||
*
|
||
* a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
|
||
* pins, detects VBUS for tCCDebounce and starts alternating
|
||
* between Unattached.SRC and Unattached.SNK
|
||
*/
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SRC);
|
||
|
||
/*
|
||
* 4. While the CTVPD alternates between Unattached.SRC and
|
||
* Unattached.SNK state and the Power Source in Attached.SRC state:
|
||
*
|
||
* a. CTVPD monitors the Host-side port’s CC pin for device attach
|
||
* and when detected, enters AttachWait.SRC
|
||
* b. CTVPD monitors VBUS for Power Source detach and when detected,
|
||
* enters Unattached.SNK
|
||
* c. Power Source monitors CC for CTVPD detach and when detected,
|
||
* enters Unattached.SRC
|
||
*/
|
||
|
||
/* Attached host side device */
|
||
host_connect_sink(SRC_CON_DEF);
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SRC);
|
||
|
||
/* Remove VBUS */
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_ctvpd_behavior_case4(void)
|
||
{
|
||
int port = PORT0;
|
||
uint32_t expected_vdm_header = VDO(USB_VID_GOOGLE,
|
||
1, /* Structured VDM */
|
||
VDO_SVDM_VERS(1) |
|
||
VDO_CMDT(CMDT_RSP_ACK) |
|
||
CMD_DISCOVER_IDENT);
|
||
uint32_t expected_vdo_id_header = VDO_IDH(
|
||
0, /* Not a USB Host */
|
||
1, /* Capable of being enumerated as USB Device */
|
||
IDH_PTYPE_VPD,
|
||
0, /* Modal Operation Not Supported */
|
||
USB_VID_GOOGLE);
|
||
uint32_t expected_vdo_cert = 0;
|
||
uint32_t expected_vdo_product = VDO_PRODUCT(
|
||
CONFIG_USB_PID,
|
||
USB_BCD_DEVICE);
|
||
uint32_t expected_vdo_vpd = VDO_VPD(
|
||
VPD_HW_VERSION,
|
||
VPD_FW_VERSION,
|
||
VPD_MAX_VBUS_20V,
|
||
VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE),
|
||
VPD_GND_IMP(VPD_GND_IMPEDANCE),
|
||
VPD_CTS_SUPPORTED
|
||
);
|
||
|
||
init_port(port);
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
TEST_ASSERT(ct_disconnect_sink());
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* CASE 4: The following describes the behavior when a DRP is connected
|
||
* to a Charge-Through VCONN-Powered USB Device
|
||
* (abbreviated CTVPD), with a Power Source already attached to
|
||
* the Charge-Through side on the CTVPD.
|
||
*/
|
||
|
||
/*
|
||
* 1. DRP, CTVPD and Sink are all in the unattached state
|
||
*
|
||
* a. DRP alternates between Unattached.SRC and Unattached.SNK
|
||
* b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
|
||
* pins and Rd on the Host-side port’s CC pin
|
||
*/
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
|
||
/*
|
||
* 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
|
||
* Attached.SRC
|
||
*
|
||
* a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
|
||
* is in Unattached.SNK and DRP enters AttachWait.SRC
|
||
* b. DRP in AttachWait.SRC detects that pull down on CC persists
|
||
* for tCCDebounce, enters Attached.SRC and turns on VBUS and
|
||
* VCONN
|
||
*/
|
||
|
||
host_connect_source(VBUS_5);
|
||
mock_set_vconn(VCONN_3);
|
||
|
||
/*
|
||
* 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
|
||
* AttachWait.SNK.
|
||
*
|
||
* a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
|
||
* enters AttachWait.SNK
|
||
* b. CTVPD in AttachWait.SNK detects that pull up on the
|
||
* Host-side port’s CC persists for tCCDebounce, VCONN present
|
||
* and enters Attached.SNK
|
||
* c. CTVPD present a high-impedance to ground (above zOPEN) on its
|
||
* Charge-Through port’s CC1 and CC2 pins
|
||
*/
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SNK);
|
||
TEST_ASSERT(check_ct_ccs_hz());
|
||
|
||
/*
|
||
* 4. While DRP and CTVPD are in their respective attached states, DRP
|
||
* discovers the ChargeThrough CTVPD and transitions to
|
||
* CTUnattached.SNK
|
||
*
|
||
* a. DRP (as Source) queries the device identity via USB PD
|
||
* (Discover Identity Command) on SOP’.
|
||
* b. CTVPD responds on SOP’, advertising that it is a
|
||
* Charge-Through VCONN-Powered USB Device
|
||
* c. DRP (as Source) removes VBUS
|
||
* d. DRP (as Source) changes its Rp to a Rd
|
||
* e. DRP (as Sink) continues to provide VCONN and enters
|
||
* CTUnattached.SNK
|
||
*/
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
simulate_discovery_identity(port);
|
||
task_wait_event(40 * MSEC);
|
||
|
||
TEST_ASSERT(verify_goodcrc(port,
|
||
PD_ROLE_SINK, pd_port[port].msg_rx_id));
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
inc_rx_id(port);
|
||
|
||
/* Test Discover Identity Ack */
|
||
TEST_ASSERT(pd_test_tx_msg_verify_sop_prime(port));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_short(port,
|
||
PD_HEADER(PD_DATA_VENDOR_DEF, PD_PLUG_FROM_CABLE, 0,
|
||
pd_port[port].msg_tx_id, 5, pd_port[port].rev, 0)));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdm_header));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_id_header));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_cert));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_product));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_vpd));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
|
||
TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/* Ack was good. Send GoodCRC */
|
||
simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
inc_tx_id(port);
|
||
|
||
/*
|
||
* 5. CTVPD transitions to CTUnattached.VPD
|
||
*
|
||
* a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
|
||
* CC pin and enters CTUnattached.VPD
|
||
* b. CTVPD changes its host-side Rd to a Rp termination advertising
|
||
* 3.0 A
|
||
* c. CTVPD isolates itself from VBUS
|
||
* d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
|
||
*/
|
||
host_disconnect_source();
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_VPD);
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
TEST_ASSERT(check_host_rp3a0());
|
||
|
||
/*
|
||
* 6. CTVPD alternates between CTUnattached.VPD and
|
||
* CTUnattached.Unsupported
|
||
*/
|
||
wait_for_state_change(port, PD_T_DRP_SRC + 10 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_UNSUPPORTED);
|
||
|
||
wait_for_state_change(port, PD_T_DRP_SRC + 10 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_VPD);
|
||
TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_ATTACH_WAIT_VPD);
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_ctvpd_behavior_case5(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
TEST_ASSERT(ct_disconnect_sink());
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* CASE 5: The following describes the behavior when a Power Source is
|
||
* connected to a ChargeThrough VCONN-Powered USB Device
|
||
* (abbreviated CTVPD), with a DRP (with dead battery) attached
|
||
* to the Host-side port on the CTVPD.
|
||
*/
|
||
|
||
/*
|
||
* 1. DRP, CTVPD and Power Source are all in the unattached state
|
||
*
|
||
* a. DRP apply dead battery Rd
|
||
* b. CTVPD apply Rd on the Charge-Through port’s CC1 and CC2 pins
|
||
* and Rd on the Host-side port’s CC pin
|
||
*/
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
|
||
/*
|
||
* 2. Power Source transitions from Unattached.SRC to Attached.SRC
|
||
* through AttachWait.SRC.
|
||
*
|
||
* a. Power Source detects the CC pull-down of the CTVPD and enters
|
||
* AttachWait.SRC
|
||
* b. Power Source in AttachWait.SRC detects that pull down on CC
|
||
* persists for tCCDebounce, enters Attached.SRC and enable VBUS
|
||
*/
|
||
TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
|
||
|
||
/*
|
||
* 3. CTVPD alternates between Unattached.SNK and Unattached.SRC
|
||
*
|
||
* a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
|
||
* pins, detects VBUS for tCCDebounce and starts alternating
|
||
* between Unattached.SRC and Unattached.SNK
|
||
*/
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SRC);
|
||
|
||
/* Connect Host With Dead Battery */
|
||
host_connect_sink(SRC_CON_DEF);
|
||
|
||
/*
|
||
* 4. CTVPD transitions from Unattached.SRC to Try.SNK through
|
||
* AttachWait.SRC
|
||
*
|
||
* a. CTVPD in Unattached.SRC detects the CC pull-down of DRP which
|
||
* is in Unattached.SNK and CTVPD enters AttachWait.SRC
|
||
* b. CTVPD in AttachWait.SRC detects that pull down on CC persists
|
||
* for tCCDebounce and enters Try.SNK
|
||
* c. CTVPD disables Rp termination advertising Default USB Power on
|
||
* the Host-side port’s CC
|
||
* d. CTVPD enables Rd on the Host-side port’s CC
|
||
*/
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SRC);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_TRY_SNK);
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
|
||
/* 5. DRP in dead battery condition remains in Unattached.SNK */
|
||
|
||
/*
|
||
* 6. CTVPD transitions from Try.SNK to Attached.SRC through
|
||
* TryWait.SRC
|
||
*
|
||
* a. CTVPD didn’t detect the CC pull-up of the DRP for
|
||
* tTryDebounce after tDRPTry and enters TryWait.SRC
|
||
* b. CTVPD disables Rd on the Host-side port’s CC
|
||
* c. CTVPD enables Rp termination advertising Default USB Power on
|
||
* the Host-side port’s CC
|
||
* d. CTVPD detects the CC pull-down of the DRP for tTryCCDebounce
|
||
* and enters Attached.SRC
|
||
* e. CTVPD connects VBUS from the Charge-Through side to the Host
|
||
* side
|
||
*/
|
||
wait_for_state_change(port, PD_T_TRY_CC_DEBOUNCE +
|
||
PD_T_DRP_TRY + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_TRY_WAIT_SRC);
|
||
TEST_ASSERT(check_host_rpusb());
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SRC);
|
||
TEST_ASSERT(mock_get_vbus_pass_en());
|
||
|
||
/*
|
||
* 7. DRP transitions from Unattached.SNK to Attached.SNK through
|
||
* AttachWait.SNK
|
||
*
|
||
* a. DRP in Unattached.SNK detects the CC pull-up of CTVPD which is
|
||
* in Attached.SRC and DRP enters AttachWait.SNK
|
||
* b. DRP in AttachWait.SNK detects that pull up on CC persists for
|
||
* tCCDebounce, VBUS present and enters Attached.SNK
|
||
*/
|
||
|
||
/*
|
||
* 8. While the devices are all in their respective attached states:
|
||
* a. CTVPD monitors the Host-side port’s CC pin for device attach
|
||
* and when detected, enters Unattached.SNK
|
||
* b. CTVPD monitors VBUS for Power Source detach and when detected,
|
||
* enters Unattached.SNK
|
||
* c. Power Source monitors CC for CTVPD detach and when detected,
|
||
* enters Unattached.SRC
|
||
* d. DRP monitors VBUS for CTVPD detach and when detected, enters
|
||
* Unattached.SNK
|
||
* e. Additionally, the DRP may query the identity of the cable via
|
||
* USB PD on SOP’ when it has sufficient battery power and when
|
||
* a Charge-Through VPD is identified enters TryWait.SRC if
|
||
* implemented, or enters Unattached.SRC if TryWait.SRC is not
|
||
* supported
|
||
*/
|
||
TEST_ASSERT(ct_connect_source(CC2, VBUS_0));
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
|
||
static int test_ctvpd_behavior_case6(void)
|
||
{
|
||
int port = PORT0;
|
||
|
||
mock_set_vconn(VCONN_0);
|
||
host_disconnect_source();
|
||
TEST_ASSERT(ct_disconnect_source());
|
||
TEST_ASSERT(ct_disconnect_sink());
|
||
|
||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||
task_wait_event(40 * MSEC);
|
||
|
||
/*
|
||
* CASE 6: The following describes the behavior when a DRP is connected
|
||
* to a Charge-Through VCONN-Powered USB Device
|
||
* (abbreviated CTVPD) and a Sink is attached to the
|
||
* Charge-Through port on the CTVPD.
|
||
*/
|
||
|
||
/*
|
||
* 1. DRP, CTVPD and Sink are all in the unattached state
|
||
*
|
||
* a. DRP alternates between Unattached.SRC and Unattached.SNK
|
||
* b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
|
||
* pins and Rd on the Host-side port’s CC pin
|
||
*/
|
||
TEST_ASSERT(get_state_tc(port) == TC_UNATTACHED_SNK);
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
TEST_ASSERT(check_host_ra_rd());
|
||
|
||
/*
|
||
* 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
|
||
* Attached.SRC
|
||
*
|
||
* a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
|
||
* is in Unattached.SNK and DRP enters AttachWait.SRC
|
||
* b. DRP in AttachWait.SRC detects that pull down on CC persists
|
||
* for tCCDebounce, enters Attached.SRC and turns on VBUS and
|
||
* VCONN
|
||
*/
|
||
host_connect_source(VBUS_5);
|
||
mock_set_vconn(VCONN_3);
|
||
|
||
/*
|
||
* 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
|
||
* AttachWait.SNK.
|
||
*
|
||
* a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
|
||
* enters AttachWait.SNK
|
||
* b. CTVPD in AttachWait.SNK detects that pull up on the Host-side
|
||
* port’s CC persists for tCCDebounce, VCONN present and enters
|
||
* Attached.SNK
|
||
* c. CTVPD present a high-impedance to ground (above zOPEN) on its
|
||
* Charge-Through port’s CC1 and CC2 pins
|
||
*/
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACH_WAIT_SNK);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_ATTACHED_SNK);
|
||
TEST_ASSERT(check_ct_ccs_hz());
|
||
|
||
/*
|
||
* 4. While DRP and CTVPD are in their respective attached states, DRP
|
||
* discovers the ChargeThrough CTVPD and transitions to
|
||
* CTUnattached.SNK
|
||
*
|
||
* a. DRP (as Source) queries the device identity via USB PD
|
||
* (Discover Identity Command) on SOP’.
|
||
* b. CTVPD responds on SOP’, advertising that it is a
|
||
* Charge-Through VCONN-Powered USB Device
|
||
* c. DRP (as Source) removes VBUS
|
||
* d. DRP (as Source) changes its Rp to a Rd
|
||
* e. DRP (as Sink) continues to provide VCONN and enters
|
||
* CTUnattached.SNK
|
||
*/
|
||
|
||
host_disconnect_source();
|
||
host_connect_sink(SRC_CON_DEF);
|
||
|
||
/*
|
||
* 5. CTVPD transitions to CTUnattached.VPD
|
||
*
|
||
* a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
|
||
* CC pin and enters CTUnattached.VPD
|
||
* b. CTVPD changes its host-side Rd to a Rp termination advertising
|
||
* 3.0 A
|
||
* c. CTVPD isolates itself from VBUS
|
||
* d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
|
||
*/
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_VPD);
|
||
TEST_ASSERT(check_host_rp3a0());
|
||
TEST_ASSERT(mock_get_vbus_pass_en() == 0);
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
|
||
/*
|
||
* 6. CTVPD alternates between CTUnattached.VPD and
|
||
* CTUnattached.Unsupported
|
||
*
|
||
* a. CTVPD detects SRC.open on its Charge-Through CC pins and
|
||
* starts alternating between CTUnattached.VPD and
|
||
* CTUnattached.Unsupported
|
||
*/
|
||
wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_UNSUPPORTED);
|
||
|
||
wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_VPD);
|
||
|
||
wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_UNSUPPORTED);
|
||
|
||
/*
|
||
* 7. CTVPD transitions from CTUnattached.Unsupported to CTTry.SNK
|
||
* through CTAttachWait.Unsupported
|
||
*
|
||
* a. CTVPD in CTUnattached.Unsupported detects the CC pull-down of
|
||
* the Sink which is in Unattached.SNK and CTVPD enters
|
||
* CTAttachWait.Unsupported
|
||
* b. CTVPD in CTAttachWait.Unsupported detects that pull down on CC
|
||
* persists for tCCDebounce and enters CTTry.SNK
|
||
* c. CTVPD disables Rp termination advertising Default USB Power on
|
||
* the ChargeThrough port’s CC pins
|
||
* d. CTVPD enables Rd on the Charge-Through port’s CC pins
|
||
*/
|
||
TEST_ASSERT(ct_connect_sink(CC1, SRC_CON_DEF));
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_ATTACH_WAIT_UNSUPPORTED);
|
||
|
||
wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_TRY_SNK);
|
||
TEST_ASSERT(check_ct_ccs_rd());
|
||
|
||
/*
|
||
* 8. CTVPD transitions from CTTry.SNK to CTAttached.Unsupported
|
||
*
|
||
* a. CTVPD didn’t detect the CC pull-up of the potential Source
|
||
* for tDRPTryWait after tDRPTry and enters
|
||
* CTAttached.Unsupported
|
||
*/
|
||
|
||
wait_for_state_change(port, PD_T_DRP_TRY + PD_T_TRY_WAIT + 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_ATTACHED_UNSUPPORTED);
|
||
|
||
/*
|
||
* 9. While the CTVPD in CTAttached.Unsupported state, the DRP in
|
||
* CTUnattached.SNK state and the Sink in Unattached.SNK state:
|
||
*
|
||
* a. CTVPD disables the Rd termination on the Charge-Through
|
||
* port’s CC pins and applies Rp termination advertising
|
||
* Default USB Power
|
||
* b. CTVPD exposes a USB Billboard Device Class to the DRP
|
||
* indicating that it is connected to an unsupported device on
|
||
* its Charge Through port
|
||
* c. CTVPD monitors Charge-Though CC pins for Sink detach and when
|
||
* detected, enters CTUnattached.VPD
|
||
* d. CTVPD monitors VCONN for Host detach and when detected, enters
|
||
* Unattached.SNK
|
||
* e. DRP monitors CC for CTVPD detach for tVPDDetach and when
|
||
* detected, enters Unattached.SNK
|
||
* f. DRP monitors VBUS for CTVPD Charge-Through source attach and,
|
||
* when detected, enters CTAttached.SNK
|
||
*/
|
||
|
||
TEST_ASSERT(check_ct_ccs_cc1_rpusb());
|
||
TEST_ASSERT(mock_get_present_billboard() == BB_SNK);
|
||
|
||
TEST_ASSERT(ct_disconnect_sink());
|
||
|
||
wait_for_state_change(port, 40 * MSEC);
|
||
|
||
TEST_ASSERT(get_state_tc(port) == TC_CT_UNATTACHED_VPD);
|
||
|
||
return EC_SUCCESS;
|
||
}
|
||
#endif
|
||
|
||
void run_test(int argc, char **argv)
|
||
{
|
||
test_reset();
|
||
|
||
init_port(PORT0);
|
||
|
||
/* VPD and CTVPD tests */
|
||
RUN_TEST(test_vpd_host_src_detection);
|
||
RUN_TEST(test_vpd_host_src_detection_vbus);
|
||
RUN_TEST(test_vpd_host_src_detection_vconn);
|
||
RUN_TEST(test_vpd_host_src_detection_message_reception);
|
||
|
||
/* CTVPD only tests */
|
||
#if defined(TEST_USB_TYPEC_CTVPD)
|
||
/* DRP to VCONN-Powered USB Device (CTVPD) Behavior Tests */
|
||
RUN_TEST(test_ctvpd_behavior_case1);
|
||
RUN_TEST(test_ctvpd_behavior_case2);
|
||
RUN_TEST(test_ctvpd_behavior_case3);
|
||
RUN_TEST(test_ctvpd_behavior_case4);
|
||
RUN_TEST(test_ctvpd_behavior_case5);
|
||
RUN_TEST(test_ctvpd_behavior_case6);
|
||
#endif
|
||
|
||
/* Do basic state machine validity checks last. */
|
||
RUN_TEST(test_tc_no_parent_cycles);
|
||
RUN_TEST(test_tc_all_states_named);
|
||
|
||
/*
|
||
* Since you have to include TypeC layer when adding PE layer, the
|
||
* PE test would have the same build dependencies, so go ahead and test
|
||
* te PE statemachine here so we don't have to create another test exe
|
||
*/
|
||
RUN_TEST(test_pe_no_parent_cycles);
|
||
RUN_TEST(test_pe_all_states_named);
|
||
|
||
test_print_result();
|
||
}
|
||
|