summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2018-09-13 09:27:08 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-04-08 22:30:19 -0700
commit0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26 (patch)
tree05d4509bcfe68a248ec3fa58168f3de2536c2d9c /test
parente097feb8b2db20cd2435a483517356defa222db1 (diff)
downloadchrome-ec-0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26.tar.gz
chocodile_vpdmcu: Firmware for chocodile mcu
Implement Chocodile Charge-Through Vconn Powered firmware for mcu using new Type-C/PD State machine stack. BUG=b:115626873 BRANCH=none TEST=manual Charge-Through was tested on an Atlas running a DRP USB-C/PD state machine with CTUnattached.SNK and CTAttached.SNK states. Signed-off-by: Sam Hurst <shurst@chromium.org> Change-Id: I847f1bcd2fc3ce41e66edd133a10c943d5e8c819 Reviewed-on: https://chromium-review.googlesource.com/1225250 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Sam Hurst <shurst@google.com> Reviewed-by: Stefan Reinauer <reinauer@google.com>
Diffstat (limited to 'test')
-rw-r--r--test/build.mk14
-rw-r--r--test/test_config.h87
-rw-r--r--test/usb_pd_test_util.h2
-rw-r--r--test/usb_prl.c1306
-rw-r--r--test/usb_prl.tasklist19
l---------test/usb_sm_framework_h0.tasklist1
l---------test/usb_sm_framework_h1.tasklist1
l---------test/usb_sm_framework_h2.tasklist1
-rw-r--r--test/usb_sm_framework_h3.c1219
-rw-r--r--test/usb_sm_framework_h3.tasklist18
-rw-r--r--test/usb_typec_ctvpd.c1488
-rw-r--r--test/usb_typec_ctvpd.tasklist18
l---------test/usb_typec_vpd.tasklist1
-rw-r--r--test/vpd_api.c586
-rw-r--r--test/vpd_api.h333
15 files changed, 5094 insertions, 0 deletions
diff --git a/test/build.mk b/test/build.mk
index 6900c97e3d..b5b6e8bbea 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -65,6 +65,13 @@ test-list-host += timer_dos
test-list-host += usb_pd
test-list-host += usb_pd_giveback
test-list-host += usb_pd_rev30
+test-list-host += usb_sm_framework_h3
+test-list-host += usb_sm_framework_h2
+test-list-host += usb_sm_framework_h1
+test-list-host += usb_sm_framework_h0
+test-list-host += usb_typec_vpd
+test-list-host += usb_typec_ctvpd
+test-list-host += usb_prl
test-list-host += utils
test-list-host += utils_str
test-list-host += vboot
@@ -127,6 +134,13 @@ timer_dos-y=timer_dos.o
usb_pd-y=usb_pd.o
usb_pd_giveback-y=usb_pd.o
usb_pd_rev30-y=usb_pd.o
+usb_sm_framework_h3-y=usb_sm_framework_h3.o
+usb_sm_framework_h2-y=usb_sm_framework_h3.o
+usb_sm_framework_h1-y=usb_sm_framework_h3.o
+usb_sm_framework_h0-y=usb_sm_framework_h3.o
+usb_typec_vpd-y=usb_typec_ctvpd.o vpd_api.o
+usb_typec_ctvpd-y=usb_typec_ctvpd.o vpd_api.o
+usb_prl-y=usb_prl.o
utils-y=utils.o
utils_str-y=utils_str.o
vboot-y=vboot.o
diff --git a/test/test_config.h b/test/test_config.h
index 17e7d6f44d..afb2350681 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -217,6 +217,93 @@ int ncp15wb_calculate_temp(uint16_t adc);
#define CONFIG_ALS_LIGHTBAR_DIMMING 0
#endif
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 3
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H2)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 2
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H1)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 1
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H0)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 0
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_PRL)
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 3
+#define CONFIG_USB_PD_PORT_COUNT 2
+#define CONFIG_USB_SM_FRAMEWORK
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#define CONFIG_USB_PRL_SM
+#define CONFIG_USB_PD_TCPC
+#define CONFIG_USB_PD_TCPM_STUB
+#define CONFIG_USB_POWER_DELIVERY
+#define CONFIG_SHA256
+#define CONFIG_SW_CRC
+#endif
+
+#if defined(TEST_USB_TYPEC_VPD) || defined(TEST_USB_TYPEC_CTVPD)
+#if defined(TEST_USB_TYPEC_VPD)
+#define CONFIG_USB_TYPEC_VPD
+#else
+#define CONFIG_USB_TYPEC_CTVPD
+#endif
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 3
+
+#define CONFIG_USB_PID 0x5036
+#define VPD_HW_VERSION 0x0001
+#define VPD_FW_VERSION 0x0001
+#define USB_BCD_DEVICE 0
+
+/* Vbus impedance in milliohms */
+#define VPD_VBUS_IMPEDANCE 65
+
+/* GND impedance in milliohms */
+#define VPD_GND_IMPEDANCE 33
+
+#define CONFIG_USB_PD_PORT_COUNT 1
+#define CONFIG_USB_SM_FRAMEWORK
+#define CONFIG_USB_PE_SM
+#define CONFIG_USB_PRL_SM
+#define CONFIG_USB_TYPEC_SM
+#define CONFIG_USB_PD_TCPC
+#define CONFIG_USB_PD_TCPM_STUB
+#define CONFIG_USB_POWER_DELIVERY
+#define CONFIG_SHA256
+#define CONFIG_SW_CRC
+#endif /* TEST_USB_TYPEC_VPD or TEST_USB_TYPEC_CTVPD */
+
#if defined(TEST_USB_PD) || defined(TEST_USB_PD_GIVEBACK) || \
defined(TEST_USB_PD_REV30)
#define CONFIG_USB_POWER_DELIVERY
diff --git a/test/usb_pd_test_util.h b/test/usb_pd_test_util.h
index 561033d8fa..02fae22b41 100644
--- a/test/usb_pd_test_util.h
+++ b/test/usb_pd_test_util.h
@@ -13,6 +13,7 @@ void pd_test_rx_set_preamble(int port, int has_preamble);
void pd_test_rx_msg_append_bits(int port, uint32_t bits, int nb);
void pd_test_rx_msg_append_kcode(int port, uint8_t kcode);
void pd_test_rx_msg_append_sop(int port);
+void pd_test_rx_msg_append_sop_prime(int port);
void pd_test_rx_msg_append_eop(int port);
void pd_test_rx_msg_append_last_edge(int port);
void pd_test_rx_msg_append_4b(int port, uint8_t val);
@@ -23,6 +24,7 @@ void pd_simulate_rx(int port);
/* Verify Tx message */
int pd_test_tx_msg_verify_kcode(int port, uint8_t kcode);
int pd_test_tx_msg_verify_sop(int port);
+int pd_test_tx_msg_verify_sop_prime(int port);
int pd_test_tx_msg_verify_eop(int port);
int pd_test_tx_msg_verify_4b5b(int port, uint8_t b4);
int pd_test_tx_msg_verify_short(int port, uint16_t val);
diff --git a/test/usb_prl.c b/test/usb_prl.c
new file mode 100644
index 0000000000..ee15143986
--- /dev/null
+++ b/test/usb_prl.c
@@ -0,0 +1,1306 @@
+/* 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 Protocol Layer module.
+ */
+#include "common.h"
+#include "crc.h"
+#include "task.h"
+#include "test_util.h"
+#include "timer.h"
+#include "tcpm.h"
+#include "usb_emsg.h"
+#include "usb_pe_sm.h"
+#include "usb_pd.h"
+#include "usb_pd_test_util.h"
+#include "usb_prl_sm.h"
+#include "util.h"
+
+#define PORT0 0
+#define PORT1 1
+
+static uint32_t test_data[] = {
+ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f,
+ 0x10111213, 0x14151617, 0x1819a0b0, 0xc0d0e0f0,
+ 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f,
+ 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f,
+ 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f,
+ 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f,
+ 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f,
+ 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f,
+ 0x80818283, 0x84858687, 0x88898a8b, 0x8c8d8e8f,
+ 0x90919293, 0x94959697, 0x98999a9b, 0x9c9d9e9f,
+ 0xa0a1a2a3, 0xa4a5a6a7, 0xa8a9aaab, 0xacadaeaf,
+ 0xb0b1b2b3, 0xb4b5b6b7, 0xb8b9babb, 0xbcbdbebf,
+ 0xc0c1c2c3, 0xc4c5c6c7, 0xc8c9cacb, 0xcccdcecf,
+ 0xd0d1d2d3, 0xd4d5d6d7, 0xd8d9dadb, 0xdcdddedf,
+ 0xe0e1e2e3, 0xe4e5e6e7, 0xe8e9eaeb, 0xecedeeef,
+ 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, 0xfcfdfeff,
+ 0x11223344
+};
+
+static struct pd_prl {
+ int rev;
+ int pd_enable;
+ int power_role;
+ int data_role;
+ int msg_tx_id;
+ int msg_rx_id;
+
+ int mock_pe_message_sent;
+ int mock_pe_error;
+ int mock_pe_hard_reset_sent;
+ int mock_pe_got_hard_reset;
+ int mock_pe_pass_up_message;
+ int mock_got_soft_reset;
+} pd_port[CONFIG_USB_PD_PORT_COUNT];
+
+static void init_port(int port, int rev)
+{
+ pd_port[port].rev = rev;
+ pd_port[port].pd_enable = 0;
+ pd_port[port].power_role = PD_ROLE_SINK;
+ pd_port[port].data_role = PD_ROLE_UFP;
+ pd_port[port].msg_tx_id = 0;
+ pd_port[port].msg_rx_id = 0;
+ tcpm_init(port);
+ tcpm_set_polarity(port, 0);
+ tcpm_set_rx_enable(port, 0);
+}
+
+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(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(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 cycle_through_state_machine(int port, uint32_t num, uint32_t time)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(time);
+ }
+}
+
+static int simulate_request_chunk(int port, enum pd_data_msg_type msg_type,
+ int chunk_num, int len)
+{
+ uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_rx_id,
+ 1, pd_port[port].rev, 1);
+ uint32_t msg = PD_EXT_HEADER(chunk_num, 1, len);
+
+ simulate_rx_msg(port, header, 1, (const uint32_t *)&msg);
+ task_wait_event(30 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ return 1;
+}
+
+static int simulate_receive_ctrl_msg(int port, enum pd_ctrl_msg_type msg_type)
+{
+ uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, 0, NULL);
+ task_wait_event(30 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ return 1;
+}
+
+static int verify_data_reception(int port, uint16_t header, int len)
+{
+ int i;
+ int cnt = (len + 3) & ~3;
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ if (pd_port[port].mock_pe_error >= 0)
+ return 0;
+
+ if (!pd_port[port].mock_pe_pass_up_message)
+ return 0;
+
+ if (emsg[port].header != header)
+ return 0;
+
+ if (emsg[port].len != cnt)
+ return 0;
+
+ for (i = 0; i < cnt; i++) {
+ if (i < len) {
+ if (emsg[port].buf[i] !=
+ *((unsigned char *)test_data + i))
+ return 0;
+ } else {
+ if (emsg[port].buf[i] != 0)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int verify_chunk_data_reception(int port, uint16_t header, int len)
+{
+ int i;
+ uint8_t *td = (uint8_t *)test_data;
+
+ if (pd_port[port].mock_got_soft_reset)
+ return 0;
+
+ if (!pd_port[port].mock_pe_pass_up_message)
+ return 0;
+
+ if (pd_port[port].mock_pe_error >= 0)
+ return 0;
+
+ if (emsg[port].len != len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if (emsg[port].buf[i] != td[i])
+ return 0;
+
+ return 1;
+}
+
+static int simulate_receive_data(int port, enum pd_data_msg_type msg_type,
+ int len)
+{
+ int i;
+ int nw = (len + 3) >> 2;
+ uint8_t td[28];
+ uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_rx_id,
+ nw, pd_port[port].rev, 0);
+
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+ emsg[port].header = 0;
+ emsg[port].len = 0;
+ memset(emsg[port].buf, 0, 260);
+
+ for (i = 0; i < 28; i++) {
+ if (i < len)
+ td[i] = *((uint8_t *)test_data + i);
+ else
+ td[i] = 0;
+ }
+
+ simulate_rx_msg(port, header, nw, (uint32_t *)td);
+ task_wait_event(30 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ inc_rx_id(port);
+
+ return verify_data_reception(port, header, len);
+}
+
+static int simulate_receive_extended_data(int port,
+ enum pd_data_msg_type msg_type, int len)
+{
+ int i;
+ int j;
+ int byte_len;
+ int nw;
+ int dsize;
+ uint8_t td[28];
+ int chunk_num = 0;
+ int data_offset = 0;
+ uint8_t *expected_data = (uint8_t *)test_data;
+ uint16_t header;
+ int req_timeout;
+
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+ emsg[port].header = 0;
+ emsg[port].len = 0;
+ memset(emsg[port].buf, 0, 260);
+
+ dsize = len;
+
+ cycle_through_state_machine(port, 2, 40 * MSEC);
+
+ for (j = 0; j < 10; j++) {
+ byte_len = len;
+ if (byte_len > 26)
+ byte_len = 26;
+
+ len -= 26;
+
+ memset(td, 0, 28);
+ *(uint16_t *)td = PD_EXT_HEADER(chunk_num, 0, dsize);
+
+ for (i = 0; i < byte_len; i++)
+ td[i + 2] = *(expected_data + data_offset++);
+
+ nw = (byte_len + 2 + 3) >> 2;
+ header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_rx_id,
+ nw, pd_port[port].rev, 1);
+
+ cycle_through_state_machine(port, 2, 40 * MSEC);
+
+ if (pd_port[port].mock_pe_error >= 0)
+ return 0;
+
+ if (pd_port[port].mock_pe_pass_up_message)
+ return 0;
+
+ if (emsg[port].len != 0)
+ return 0;
+
+ simulate_rx_msg(port, header, nw, (uint32_t *)td);
+ task_wait_event(40 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40);
+ inc_rx_id(port);
+
+ /*
+ * If no more data, do expected to get a chunk request
+ */
+ if (len <= 0)
+ break;
+
+ /*
+ * Wait for request chunk message
+ */
+ req_timeout = 0;
+ while (get_rch_state_id(port) != RCH_REQUESTING_CHUNK &&
+ req_timeout < 5) {
+ req_timeout++;
+ msleep(2);
+ }
+
+ chunk_num++;
+
+ /* Test Request next chunk packet */
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type,
+ pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_tx_id,
+ 1, pd_port[port].rev, 1)))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_word(port,
+ PD_EXT_HEADER(chunk_num, 1, 0)))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request next chunk packet was good. Send GoodCRC */
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ inc_tx_id(port);
+ }
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(20 * MSEC);
+
+ return verify_chunk_data_reception(port, header, dsize);
+}
+
+static int verify_ctrl_msg_transmission(int port,
+ enum pd_ctrl_msg_type msg_type)
+{
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_tx_id, 0,
+ pd_port[port].rev, 0)))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ return 1;
+}
+
+static int simulate_send_ctrl_msg_request_from_pe(int port,
+ enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type)
+{
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_message_sent = 0;
+ prl_send_ctrl_msg(port, type, msg_type);
+ task_wait_event(40 * MSEC);
+
+ if (msg_type == PD_CTRL_SOFT_RESET)
+ cycle_through_state_machine(port, 1, 20 * MSEC);
+
+ return verify_ctrl_msg_transmission(port, msg_type);
+}
+
+static int verify_data_msg_transmission(int port,
+ enum pd_data_msg_type msg_type, int len)
+{
+ int i;
+ int num_words = (len + 3) >> 2;
+ int data_obj_in_bytes;
+ uint32_t td;
+
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_tx_id,
+ num_words, pd_port[port].rev, 0)))
+ return 0;
+
+ for (i = 0; i < num_words; i++) {
+ td = test_data[i];
+ data_obj_in_bytes = (i + 1) * 4;
+ if (data_obj_in_bytes > len) {
+ switch (data_obj_in_bytes - len) {
+ case 1:
+ td &= 0x00ffffff;
+ break;
+ case 2:
+ td &= 0x0000ffff;
+ break;
+ case 3:
+ td &= 0x000000ff;
+ break;
+ }
+ }
+
+ if (!pd_test_tx_msg_verify_word(port, td))
+ return 0;
+ }
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ return 1;
+}
+
+static int simulate_send_data_msg_request_from_pe(int port,
+ enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type, int len)
+{
+ int i;
+ uint8_t *buf = emsg[port].buf;
+ uint8_t *td = (uint8_t *)test_data;
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_message_sent = 0;
+
+ for (i = 0; i < len; i++)
+ buf[i] = td[i];
+
+ emsg[port].len = len;
+
+ prl_send_data_msg(port, type, msg_type);
+ task_wait_event(30 * MSEC);
+
+ return verify_data_msg_transmission(port, msg_type, len);
+}
+
+static int verify_extended_data_msg_transmission(int port,
+ enum pd_data_msg_type msg_type, int len)
+{
+ int i;
+ int j;
+ int nw;
+ int byte_len;
+ int dsize;
+ uint32_t td;
+ uint8_t *expected_data = (uint8_t *)&test_data;
+ int data_offset = 0;
+ int chunk_number_to_send = 0;
+
+ dsize = len;
+
+ for (j = 0; j < 10; j++) {
+ byte_len = len;
+ if (byte_len > 26)
+ byte_len = 26;
+
+ nw = (byte_len + 2 + 3) >> 2;
+
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_tx_id,
+ nw, pd_port[port].rev, 1)))
+ return 0;
+ td = PD_EXT_HEADER(chunk_number_to_send, 0, dsize);
+ td |= *(expected_data + data_offset++) << 16;
+ td |= *(expected_data + data_offset++) << 24;
+
+ if (byte_len == 1)
+ td &= 0x00ffffff;
+
+ if (!pd_test_tx_msg_verify_word(port, td))
+ return 0;
+
+ byte_len -= 2;
+
+ if (byte_len > 0) {
+ nw = (byte_len + 3) >> 2;
+ for (i = 0; i < nw; i++) {
+ td = *(expected_data + data_offset++) << 0;
+ td |= *(expected_data + data_offset++) << 8;
+ td |= *(expected_data + data_offset++) << 16;
+ td |= *(expected_data + data_offset++) << 24;
+
+ switch (byte_len) {
+ case 3:
+ td &= 0x00ffffff;
+ break;
+ case 2:
+ td &= 0x0000ffff;
+ break;
+ case 1:
+ td &= 0x000000ff;
+ break;
+ }
+
+ if (!pd_test_tx_msg_verify_word(port, td))
+ return 0;
+ byte_len -= 4;
+ }
+ }
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(10 * MSEC);
+
+ /* Send GoodCRC */
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ len -= 26;
+ if (len <= 0)
+ break;
+
+ chunk_number_to_send++;
+ cycle_through_state_machine(port, 4, 10 * MSEC);
+ if (!simulate_request_chunk(port, msg_type,
+ chunk_number_to_send, dsize))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+ }
+
+ return 1;
+}
+
+static int simulate_send_extended_data_msg(int port,
+ enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type,
+ int len)
+{
+ int i;
+ uint8_t *buf = emsg[port].buf;
+ uint8_t *td = (uint8_t *)test_data;
+
+ memset(buf, 0, 260);
+ emsg[port].len = len;
+
+ /* don't overflow buffer */
+ if (len > 260)
+ len = 260;
+
+ for (i = 0; i < len; i++)
+ buf[i] = td[i];
+
+ prl_send_ext_data_msg(port, type, msg_type);
+ task_wait_event(30 * MSEC);
+
+ return verify_extended_data_msg_transmission(port, msg_type,
+ len);
+}
+
+static void enable_prl(int port, int en)
+{
+ tcpm_set_rx_enable(port, en);
+
+ pd_port[port].pd_enable = en;
+ pd_port[port].msg_tx_id = 0;
+ pd_port[port].msg_rx_id = 0;
+
+ /* Init PRL */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(10 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(10 * MSEC);
+
+ prl_set_rev(port, pd_port[port].rev);
+}
+
+int tc_get_power_role(int port)
+{
+ return pd_port[port].power_role;
+}
+
+int tc_get_data_role(int port)
+{
+ return pd_port[port].data_role;
+}
+
+void pe_report_error(int port, enum pe_error e)
+{
+ pd_port[port].mock_pe_error = e;
+}
+
+void pe_got_hard_reset(int port)
+{
+ pd_port[port].mock_pe_got_hard_reset = 1;
+}
+
+void pe_pass_up_message(int port)
+{
+ pd_port[port].mock_pe_pass_up_message = 1;
+}
+
+void pe_message_sent(int port)
+{
+ pd_port[port].mock_pe_message_sent = 1;
+}
+
+void pe_hard_reset_sent(int port)
+{
+ pd_port[port].mock_pe_hard_reset_sent = 1;
+}
+
+void pe_got_soft_reset(int port)
+{
+ pd_port[port].mock_got_soft_reset = 1;
+}
+
+static int test_initial_states(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+ TEST_ASSERT(get_tch_state_id(port) ==
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE);
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_REQUEST);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_ctrl_msg(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Control message transmission and tx_id increment
+ */
+ for (i = 0; i < 10; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_CTRL_ACCEPT));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ inc_tx_id(port);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_ctrl_msg_with_retry_and_fail(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Control message transmission fail with retry
+ */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_CTRL_ACCEPT));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+
+ /* Do not increment tx_id so phy layer will not transmit message */
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ pd_port[port].mock_pe_message_sent = 0;
+ prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+ task_wait_event(30 * MSEC);
+
+ for (i = 0; i < N_RETRY_COUNT + 1; i++) {
+ cycle_through_state_machine(port, 10, 10 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(PD_T_TCPC_TX_TIMEOUT);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent == 0);
+ if (i == N_RETRY_COUNT)
+ TEST_ASSERT(pd_port[port].mock_pe_error ==
+ ERR_TCH_XMIT);
+ else
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_ctrl_msg_with_retry_and_success(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Control message transmission fail with retry
+ */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_message_sent = 0;
+
+ prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+ task_wait_event(40 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+
+ /* Do not increment tx_id. */
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ pd_port[port].mock_pe_message_sent = 0;
+ prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+ task_wait_event(30 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ for (i = 0; i < N_RETRY_COUNT + 1; i++) {
+ if (i == N_RETRY_COUNT)
+ inc_tx_id(port);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+
+ cycle_through_state_machine(port, 8, 10 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(PD_T_TCPC_TX_TIMEOUT);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ if (i == N_RETRY_COUNT)
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ else
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent == 0);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_data_msg(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Sending data message with 1 to 28 bytes
+ */
+ for (i = 1; i <= 28; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_data_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_DATA_SOURCE_CAP, i));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ inc_tx_id(port);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_data_msg_to_much_data(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Send data message with more than 28-bytes, should fail
+ */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ /* Try to send 29-bytes */
+ TEST_ASSERT(!simulate_send_data_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_DATA_SOURCE_CAP, 29));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(!pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error = ERR_TCH_XMIT);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_extended_data_msg(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Sending extended data message with 29 to 260 bytes
+ */
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+
+ for (i = 29; i <= 260; i++) {
+ pd_port[port].mock_pe_message_sent = 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_extended_data_msg(
+ port, TCPC_TX_SOP, PD_EXT_MANUFACTURER_INFO, i));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_soft_reset_msg(void)
+{
+ int port = PORT0;
+ int expected_header = PD_HEADER(PD_CTRL_SOFT_RESET,
+ pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving Soft Reset
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+
+ TEST_ASSERT(simulate_receive_ctrl_msg(port, PD_CTRL_SOFT_RESET));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ TEST_ASSERT(pd_port[port].mock_pe_pass_up_message);
+ TEST_ASSERT(expected_header == emsg[port].header);
+ TEST_ASSERT(emsg[port].len == 0);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_control_msg(void)
+{
+ int port = PORT0;
+ int expected_header = PD_HEADER(PD_CTRL_DR_SWAP,
+ pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving a control message
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+
+ TEST_ASSERT(simulate_receive_ctrl_msg(port, PD_CTRL_DR_SWAP));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ TEST_ASSERT(pd_port[port].mock_pe_pass_up_message);
+ TEST_ASSERT(expected_header == emsg[port].header);
+ TEST_ASSERT(emsg[port].len == 0);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_data_msg(void)
+{
+ int port = PORT0;
+ int i;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving data message with 1 to 28 bytes
+ */
+
+ for (i = 1; i <= 28; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+ TEST_ASSERT(simulate_receive_data(port,
+ PD_DATA_BATTERY_STATUS, i));
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_extended_data_msg(void)
+{
+ int len;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving extended data message with 29 to 260 bytes
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+
+ for (len = 29; len <= 260; len++)
+ TEST_ASSERT(simulate_receive_extended_data(port,
+ PD_DATA_BATTERY_STATUS, len));
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_soft_reset_msg(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Send soft reset
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_CTRL_SOFT_RESET));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ inc_tx_id(port);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_LAYER_RESET_FOR_TRANSMIT);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_pe_execute_hard_reset_msg(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ pd_port[port].mock_pe_hard_reset_sent = 0;
+
+ /*
+ * TEST: Policy Engine initiated hard reset
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ /* Simulate receiving hard reset from policy engine */
+ prl_execute_hard_reset(port);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_RESET_LAYER);
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE);
+
+ cycle_through_state_machine(port, 2, PD_T_PS_HARD_RESET);
+ TEST_ASSERT(pd_port[port].mock_pe_hard_reset_sent);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE);
+
+ /* Simulate policy engine indicating that it is done hard reset */
+ prl_hard_reset_complete(port);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_phy_execute_hard_reset_msg(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Port partner initiated hard reset
+ */
+
+ pd_port[port].mock_pe_got_hard_reset = 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ /* Simulate receiving hard reset from port partner */
+ pd_execute_hard_reset(port);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_RESET_LAYER);
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE);
+
+ cycle_through_state_machine(port, 2, PD_T_PS_HARD_RESET);
+ TEST_ASSERT(pd_port[port].mock_pe_got_hard_reset);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE);
+
+ /* Simulate policy engine indicating that it is done hard reset */
+ prl_hard_reset_complete(port);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+int pd_task(void *u)
+{
+ int port = PORT0;
+ int evt;
+
+ while (1) {
+ evt = task_wait_event(-1);
+
+ tcpc_run(port, evt);
+ protocol_layer(port, evt, pd_port[port].pd_enable);
+ }
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ /* Test PD 2.0 Protocol */
+ init_port(PORT0, PD_REV20);
+ RUN_TEST(test_initial_states);
+ RUN_TEST(test_send_ctrl_msg);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_fail);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_success);
+ RUN_TEST(test_send_data_msg);
+ RUN_TEST(test_send_data_msg_to_much_data);
+ RUN_TEST(test_receive_control_msg);
+ RUN_TEST(test_receive_data_msg);
+ RUN_TEST(test_receive_soft_reset_msg);
+ RUN_TEST(test_send_soft_reset_msg);
+ RUN_TEST(test_pe_execute_hard_reset_msg);
+ RUN_TEST(test_phy_execute_hard_reset_msg);
+
+ /* TODO(shurst): More PD 2.0 Tests */
+
+ /* Test PD 3.0 Protocol */
+ init_port(PORT0, PD_REV30);
+ RUN_TEST(test_initial_states);
+ RUN_TEST(test_send_ctrl_msg);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_fail);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_success);
+ RUN_TEST(test_send_data_msg);
+ RUN_TEST(test_send_data_msg_to_much_data);
+ RUN_TEST(test_send_extended_data_msg);
+ RUN_TEST(test_receive_control_msg);
+ RUN_TEST(test_receive_data_msg);
+ RUN_TEST(test_receive_extended_data_msg);
+ RUN_TEST(test_receive_soft_reset_msg);
+ RUN_TEST(test_send_soft_reset_msg);
+ RUN_TEST(test_pe_execute_hard_reset_msg);
+ RUN_TEST(test_phy_execute_hard_reset_msg);
+
+ /* TODO(shurst): More PD 3.0 Tests */
+
+ test_print_result();
+}
+
diff --git a/test/usb_prl.tasklist b/test/usb_prl.tasklist
new file mode 100644
index 0000000000..88eca7d9b7
--- /dev/null
+++ b/test/usb_prl.tasklist
@@ -0,0 +1,19 @@
+/* Copyright (c) 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST \
+ TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_TEST(PD_C1, pd_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/test/usb_sm_framework_h0.tasklist b/test/usb_sm_framework_h0.tasklist
new file mode 120000
index 0000000000..b55922b1ee
--- /dev/null
+++ b/test/usb_sm_framework_h0.tasklist
@@ -0,0 +1 @@
+usb_sm_framework_h3.tasklist \ No newline at end of file
diff --git a/test/usb_sm_framework_h1.tasklist b/test/usb_sm_framework_h1.tasklist
new file mode 120000
index 0000000000..b55922b1ee
--- /dev/null
+++ b/test/usb_sm_framework_h1.tasklist
@@ -0,0 +1 @@
+usb_sm_framework_h3.tasklist \ No newline at end of file
diff --git a/test/usb_sm_framework_h2.tasklist b/test/usb_sm_framework_h2.tasklist
new file mode 120000
index 0000000000..b55922b1ee
--- /dev/null
+++ b/test/usb_sm_framework_h2.tasklist
@@ -0,0 +1 @@
+usb_sm_framework_h3.tasklist \ No newline at end of file
diff --git a/test/usb_sm_framework_h3.c b/test/usb_sm_framework_h3.c
new file mode 100644
index 0000000000..db88338387
--- /dev/null
+++ b/test/usb_sm_framework_h3.c
@@ -0,0 +1,1219 @@
+/* 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 "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_test_util.h"
+#include "vpd_api.h"
+
+/*
+ * Test State Hierarchy
+ * SM_TEST_A4 transitions to SM_TEST_B4
+ * SM_TEST_B4 transitions to SM_TEST_B5
+ * SM_TEST_B5 transitions to SM_TEST_B6
+ * SM_TEST_B6 transitions to SM_TEST_C
+ * SM_TEST_C transitions to SM_TEST_A7
+ * SM_TEST_A7 transitions to SM_TEST_A6
+ * SM_TEST_A6 transitions to SM_TEST_A5
+ * SM_TEST_A5 transitions to SM_TEST_A4
+ *
+ * --------------------------- ---------------------------
+ * | SM_TEST_SUPER_A1 | | SM_TEST_SUPER_B1 |
+ * | ----------------------- | | ----------------------- |
+ * | | SM_TEST_SUPER_A2 | | | | SM_TEST_SUPER_B2 | |
+ * | | ------------------- | | | | ------------------- | |
+ * | | |SM_TEST_SUPER_A3 | | | | | |SM_TEST_SUPER_B3 | | |
+ * | | | | | | | | | | | |
+ * | | | ------------- | | | | | | ------------- | | |
+ * | | | | SM_TEST_A4|------------------>| SM_TEST_B4| | | |
+ * | | | ------------- | | | | | | ------------- | | |
+ * | | | ^ | | | | | |--------|--------| | |
+ * | | | | | | | | | | | |
+ * | | | -------------- | | | | | \/ | |
+ * | | | | SM_TEST_A5 | | | | | | -------------- | |
+ * | | | -------------- | | | | | | SM_TEST_B5 | | |
+ * | | |--------^--------| | | | | -------------- | |
+ * | | | | | | | | | |
+ * | | -------------- | | | -----------|----------- |
+ * | | | SM_TEST_A6 | | | | \/ |
+ * | | -------------- | | | -------------- |
+ * | |----------^----------| | | | SM_TEST_B6 | |
+ * | | | | -------------- |
+ * | -------------- | |--------/----------------|
+ * | | SM_TEST_A7 | | /
+ * | -------------- | /
+ * |------------------^------| /
+ * \ /
+ * \ \/
+ * -------------
+ * | SM_TEST_C |
+ * -------------
+ *
+ * test_hierarchy_0: Tests a flat state machine without super states
+ * test_hierarchy_1: Tests a hierarchical state machine with 1 super state
+ * test_hierarchy_2: Tests a hierarchical state machine with 2 super states
+ * test_hierarchy_3: Tests a hierarchical state machine with 3 super states
+ *
+ */
+
+#define SEQUENCE_SIZE 55
+
+enum state_id {
+ ENTER_A1 = 1,
+ RUN_A1,
+ EXIT_A1,
+ ENTER_A2,
+ RUN_A2,
+ EXIT_A2,
+ ENTER_A3,
+ RUN_A3,
+ EXIT_A3,
+ ENTER_A4,
+ RUN_A4,
+ EXIT_A4,
+ ENTER_A5,
+ RUN_A5,
+ EXIT_A5,
+ ENTER_A6,
+ RUN_A6,
+ EXIT_A6,
+ ENTER_A7,
+ RUN_A7,
+ EXIT_A7,
+ ENTER_B1,
+ RUN_B1,
+ EXIT_B1,
+ ENTER_B2,
+ RUN_B2,
+ EXIT_B2,
+ ENTER_B3,
+ RUN_B3,
+ EXIT_B3,
+ ENTER_B4,
+ RUN_B4,
+ EXIT_B4,
+ ENTER_B5,
+ RUN_B5,
+ EXIT_B5,
+ ENTER_B6,
+ RUN_B6,
+ EXIT_B6,
+ ENTER_C,
+ RUN_C,
+ EXIT_C,
+};
+
+#define PORT0 0
+#define TSM_OBJ(port) (SM_OBJ(sm[port]))
+
+struct sm_ {
+ /* struct sm_obj must be first */
+ struct sm_obj obj;
+ int sv_tmp;
+ int idx;
+ int seq[55];
+} sm[1];
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static unsigned int sm_test_super_A1(int port, enum signal sig);
+static unsigned int sm_test_super_A1_entry(int port);
+static unsigned int sm_test_super_A1_run(int port);
+static unsigned int sm_test_super_A1_exit(int port);
+
+static unsigned int sm_test_super_B1(int port, enum signal sig);
+static unsigned int sm_test_super_B1_entry(int port);
+static unsigned int sm_test_super_B1_run(int port);
+static unsigned int sm_test_super_B1_exit(int port);
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+static unsigned int sm_test_super_A2(int port, enum signal sig);
+static unsigned int sm_test_super_A2_entry(int port);
+static unsigned int sm_test_super_A2_run(int port);
+static unsigned int sm_test_super_A2_exit(int port);
+
+static unsigned int sm_test_super_B2(int port, enum signal sig);
+static unsigned int sm_test_super_B2_entry(int port);
+static unsigned int sm_test_super_B2_run(int port);
+static unsigned int sm_test_super_B2_exit(int port);
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+static unsigned int sm_test_super_A3(int port, enum signal sig);
+static unsigned int sm_test_super_A3_entry(int port);
+static unsigned int sm_test_super_A3_run(int port);
+static unsigned int sm_test_super_A3_exit(int port);
+
+static unsigned int sm_test_super_B3(int port, enum signal sig);
+static unsigned int sm_test_super_B3_entry(int port);
+static unsigned int sm_test_super_B3_run(int port);
+static unsigned int sm_test_super_B3_exit(int port);
+#endif
+
+static unsigned int sm_test_A4(int port, enum signal sig);
+static unsigned int sm_test_A4_entry(int port);
+static unsigned int sm_test_A4_run(int port);
+static unsigned int sm_test_A4_exit(int port);
+
+static unsigned int sm_test_A5(int port, enum signal sig);
+static unsigned int sm_test_A5_entry(int port);
+static unsigned int sm_test_A5_run(int port);
+static unsigned int sm_test_A5_exit(int port);
+
+static unsigned int sm_test_A6(int port, enum signal sig);
+static unsigned int sm_test_A6_entry(int port);
+static unsigned int sm_test_A6_run(int port);
+static unsigned int sm_test_A6_exit(int port);
+
+static unsigned int sm_test_A7(int port, enum signal sig);
+static unsigned int sm_test_A7_entry(int port);
+static unsigned int sm_test_A7_run(int port);
+static unsigned int sm_test_A7_exit(int port);
+
+static unsigned int sm_test_B4(int port, enum signal sig);
+static unsigned int sm_test_B4_entry(int port);
+static unsigned int sm_test_B4_run(int port);
+static unsigned int sm_test_B4_exit(int port);
+
+static unsigned int sm_test_B5(int port, enum signal sig);
+static unsigned int sm_test_B5_entry(int port);
+static unsigned int sm_test_B5_run(int port);
+static unsigned int sm_test_B5_exit(int port);
+
+static unsigned int sm_test_B6(int port, enum signal sig);
+static unsigned int sm_test_B6_entry(int port);
+static unsigned int sm_test_B6_run(int port);
+static unsigned int sm_test_B6_exit(int port);
+
+static unsigned int sm_test_C(int port, enum signal sig);
+static unsigned int sm_test_C_entry(int port);
+static unsigned int sm_test_C_run(int port);
+static unsigned int sm_test_C_exit(int port);
+
+static unsigned int get_super_state(int port);
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static const state_sig sm_test_super_A1_sig[] = {
+ sm_test_super_A1_entry,
+ sm_test_super_A1_run,
+ sm_test_super_A1_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_super_B1_sig[] = {
+ sm_test_super_B1_entry,
+ sm_test_super_B1_run,
+ sm_test_super_B1_exit,
+ get_super_state
+};
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+static const state_sig sm_test_super_A2_sig[] = {
+ sm_test_super_A2_entry,
+ sm_test_super_A2_run,
+ sm_test_super_A2_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_super_B2_sig[] = {
+ sm_test_super_B2_entry,
+ sm_test_super_B2_run,
+ sm_test_super_B2_exit,
+ get_super_state
+};
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+static const state_sig sm_test_super_A3_sig[] = {
+ sm_test_super_A3_entry,
+ sm_test_super_A3_run,
+ sm_test_super_A3_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_super_B3_sig[] = {
+ sm_test_super_B3_entry,
+ sm_test_super_B3_run,
+ sm_test_super_B3_exit,
+ get_super_state
+};
+#endif
+
+static const state_sig sm_test_A4_sig[] = {
+ sm_test_A4_entry,
+ sm_test_A4_run,
+ sm_test_A4_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_A5_sig[] = {
+ sm_test_A5_entry,
+ sm_test_A5_run,
+ sm_test_A5_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_A6_sig[] = {
+ sm_test_A6_entry,
+ sm_test_A6_run,
+ sm_test_A6_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_A7_sig[] = {
+ sm_test_A7_entry,
+ sm_test_A7_run,
+ sm_test_A7_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_B4_sig[] = {
+ sm_test_B4_entry,
+ sm_test_B4_run,
+ sm_test_B4_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_B5_sig[] = {
+ sm_test_B5_entry,
+ sm_test_B5_run,
+ sm_test_B5_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_B6_sig[] = {
+ sm_test_B6_entry,
+ sm_test_B6_run,
+ sm_test_B6_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_C_sig[] = {
+ sm_test_C_entry,
+ sm_test_C_run,
+ sm_test_C_exit,
+ get_super_state
+};
+
+static void clear_seq(int port)
+{
+ int i;
+
+ sm[port].idx = 0;
+
+ for (i = 0; i < 8; i++)
+ sm[port].seq[i] = 0;
+}
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static unsigned int sm_test_super_A1(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_A1_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int sm_test_super_A1_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_A1;
+ return 0;
+}
+
+static unsigned int sm_test_super_A1_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_A1;
+ return 0;
+}
+
+static unsigned int sm_test_super_A1_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A1;
+ return 0;
+}
+
+static unsigned int sm_test_super_B1(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_B1_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int sm_test_super_B1_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_B1;
+ return 0;
+}
+
+static unsigned int sm_test_super_B1_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_B1;
+ return 0;
+}
+
+static unsigned int sm_test_super_B1_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B1;
+ return 0;
+}
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+static unsigned int sm_test_super_A2(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_A2_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_A1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_A2_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_A2;
+ return 0;
+}
+
+static unsigned int sm_test_super_A2_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_A2;
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_super_A2_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A2;
+ return 0;
+}
+
+static unsigned int sm_test_super_B2(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_B2_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_B1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_B2_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_B2;
+ return 0;
+}
+
+static unsigned int sm_test_super_B2_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_B2;
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_super_B2_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B2;
+ return 0;
+}
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+static unsigned int sm_test_super_A3(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_A3_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_A2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_A3_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_A3;
+ return 0;
+}
+
+static unsigned int sm_test_super_A3_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_A3;
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return RUN_SUPER;
+#else
+ return 0;
+#endif
+}
+
+static unsigned int sm_test_super_A3_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A3;
+ return 0;
+}
+
+static unsigned int sm_test_super_B3(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_B3_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_B2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_B3_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_B3;
+ return 0;
+}
+
+static unsigned int sm_test_super_B3_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_B3;
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return RUN_SUPER;
+#else
+ return 0;
+#endif
+}
+
+static unsigned int sm_test_super_B3_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B3;
+ return 0;
+}
+#endif
+
+
+static unsigned int sm_test_A4(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A4_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+ return SUPER(ret, sig, sm_test_super_A3);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A4_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A4;
+ return 0;
+}
+
+static unsigned int sm_test_A4_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A4;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_B4);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A4_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A4;
+ return 0;
+}
+
+static unsigned int sm_test_A5(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A5_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+ return SUPER(ret, sig, sm_test_super_A3);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A5_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A5;
+ return 0;
+}
+
+static unsigned int sm_test_A5_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A5;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A4);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A5_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A5;
+ return 0;
+}
+
+static unsigned int sm_test_A6(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A6_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_A2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A6_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A6;
+ return 0;
+}
+
+static unsigned int sm_test_A6_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A6;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A5);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A6_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A6;
+ return 0;
+}
+
+static unsigned int sm_test_A7(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A7_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_A1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A7_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A7;
+ return 0;
+}
+
+static unsigned int sm_test_A7_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A7;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A6);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A7_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A7;
+ return 0;
+}
+
+static unsigned int sm_test_B4(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_B4_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+ return SUPER(ret, sig, sm_test_super_B3);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_B4_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_B4;
+ return 0;
+}
+
+static unsigned int sm_test_B4_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].seq[sm[port].idx++] = RUN_B4;
+ sm[port].sv_tmp = 1;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_B5);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_B4_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B4;
+ return 0;
+}
+
+static unsigned int sm_test_B5(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_B5_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_B2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_B5_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_B5;
+ return 0;
+}
+
+static unsigned int sm_test_B5_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_B5;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_B6);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_B5_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B5;
+ return 0;
+}
+
+static unsigned int sm_test_B6(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_B6_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_B1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_B6_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_B6;
+ return 0;
+}
+
+static unsigned int sm_test_B6_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_B6;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_C);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_B6_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B6;
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_C(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_C_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int sm_test_C_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_C;
+ return 0;
+}
+
+static unsigned int sm_test_C_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].seq[sm[port].idx++] = RUN_C;
+ sm[port].sv_tmp = 1;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A7);
+ }
+
+ return 0;
+}
+
+static unsigned int sm_test_C_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_C;
+ return 0;
+}
+
+#if defined(TEST_USB_SM_FRAMEWORK_H0)
+static int test_hierarchy_0(void)
+{
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[1] == RUN_A4);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[2] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[3] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[4] == RUN_B4);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[5] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[6] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[7] == RUN_B5);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[8] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[9] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[10] == RUN_B6);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[11] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[12] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[13] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[14] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[15] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[16] == RUN_A7);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[17] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[18] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[19] == RUN_A6);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[21] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[22] == RUN_A5);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[23] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[24] == ENTER_A4);
+
+ for (i = 25; i < SEQUENCE_SIZE; i++)
+ TEST_ASSERT(sm[port].seq[i] == 0);
+
+ return EC_SUCCESS;
+}
+#endif
+
+
+#if defined(TEST_USB_SM_FRAMEWORK_H1)
+static int test_hierarchy_1(void)
+{
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[1] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[2] == RUN_A4);
+ TEST_ASSERT(sm[port].seq[3] == RUN_A3);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[4] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[5] == EXIT_A3);
+ TEST_ASSERT(sm[port].seq[6] == ENTER_B3);
+ TEST_ASSERT(sm[port].seq[7] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[8] == RUN_B4);
+ TEST_ASSERT(sm[port].seq[9] == RUN_B3);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[10] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[11] == EXIT_B3);
+ TEST_ASSERT(sm[port].seq[12] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[13] == RUN_B5);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[14] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[15] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[16] == RUN_B6);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[17] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[18] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[19] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[21] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[22] == RUN_A7);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[23] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[24] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[25] == RUN_A6);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[26] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[27] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[28] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[29] == RUN_A5);
+ TEST_ASSERT(sm[port].seq[30] == RUN_A3);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[31] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[32] == ENTER_A4);
+
+ for (i = 33; i < SEQUENCE_SIZE; i++)
+ TEST_ASSERT(sm[port].seq[i] == 0);
+
+ return EC_SUCCESS;
+}
+#endif
+
+
+#if defined(TEST_USB_SM_FRAMEWORK_H2)
+static int test_hierarchy_2(void)
+{
+
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[1] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[2] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[3] == RUN_A4);
+ TEST_ASSERT(sm[port].seq[4] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[5] == RUN_A2);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[6] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[7] == EXIT_A3);
+ TEST_ASSERT(sm[port].seq[8] == EXIT_A2);
+ TEST_ASSERT(sm[port].seq[9] == ENTER_B2);
+ TEST_ASSERT(sm[port].seq[10] == ENTER_B3);
+ TEST_ASSERT(sm[port].seq[11] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[12] == RUN_B4);
+ TEST_ASSERT(sm[port].seq[13] == RUN_B3);
+ TEST_ASSERT(sm[port].seq[14] == RUN_B2);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[15] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[16] == EXIT_B3);
+ TEST_ASSERT(sm[port].seq[17] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[18] == RUN_B5);
+ TEST_ASSERT(sm[port].seq[19] == RUN_B2);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[21] == EXIT_B2);
+ TEST_ASSERT(sm[port].seq[22] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[23] == RUN_B6);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[24] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[25] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[26] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[27] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[28] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[29] == RUN_A7);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[30] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[31] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[32] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[33] == RUN_A6);
+ TEST_ASSERT(sm[port].seq[34] == RUN_A2);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[35] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[36] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[37] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[38] == RUN_A5);
+ TEST_ASSERT(sm[port].seq[39] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[40] == RUN_A2);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[41] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[42] == ENTER_A4);
+
+ for (i = 43; i < SEQUENCE_SIZE; i++)
+ TEST_ASSERT(sm[port].seq[i] == 0);
+
+ return EC_SUCCESS;
+}
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static int test_hierarchy_3(void)
+{
+
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A1);
+ TEST_ASSERT(sm[port].seq[1] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[2] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[3] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[4] == RUN_A4);
+ TEST_ASSERT(sm[port].seq[5] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[6] == RUN_A2);
+ TEST_ASSERT(sm[port].seq[7] == RUN_A1);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[8] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[9] == EXIT_A3);
+ TEST_ASSERT(sm[port].seq[10] == EXIT_A2);
+ TEST_ASSERT(sm[port].seq[11] == EXIT_A1);
+ TEST_ASSERT(sm[port].seq[12] == ENTER_B1);
+ TEST_ASSERT(sm[port].seq[13] == ENTER_B2);
+ TEST_ASSERT(sm[port].seq[14] == ENTER_B3);
+ TEST_ASSERT(sm[port].seq[15] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[16] == RUN_B4);
+ TEST_ASSERT(sm[port].seq[17] == RUN_B3);
+ TEST_ASSERT(sm[port].seq[18] == RUN_B2);
+ TEST_ASSERT(sm[port].seq[19] == RUN_B1);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[21] == EXIT_B3);
+ TEST_ASSERT(sm[port].seq[22] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[23] == RUN_B5);
+ TEST_ASSERT(sm[port].seq[24] == RUN_B2);
+ TEST_ASSERT(sm[port].seq[25] == RUN_B1);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[26] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[27] == EXIT_B2);
+ TEST_ASSERT(sm[port].seq[28] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[29] == RUN_B6);
+ TEST_ASSERT(sm[port].seq[30] == RUN_B1);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[31] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[32] == EXIT_B1);
+ TEST_ASSERT(sm[port].seq[33] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[34] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[35] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[36] == ENTER_A1);
+ TEST_ASSERT(sm[port].seq[37] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[38] == RUN_A7);
+ TEST_ASSERT(sm[port].seq[39] == RUN_A1);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[40] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[41] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[42] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[43] == RUN_A6);
+ TEST_ASSERT(sm[port].seq[44] == RUN_A2);
+ TEST_ASSERT(sm[port].seq[45] == RUN_A1);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[46] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[47] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[48] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[49] == RUN_A5);
+ TEST_ASSERT(sm[port].seq[50] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[51] == RUN_A2);
+ TEST_ASSERT(sm[port].seq[52] == RUN_A1);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[53] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[54] == ENTER_A4);
+
+ return EC_SUCCESS;
+}
+#endif
+
+int test_task(void *u)
+{
+ int port = PORT0;
+
+ while (1) {
+ /* wait for next event/packet or timeout expiration */
+ task_wait_event(-1);
+ /* run state machine */
+ exe_state(port, TSM_OBJ(port), RUN_SIG);
+ }
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ RUN_TEST(test_hierarchy_3);
+#elif defined(TEST_USB_SM_FRAMEWORK_H2)
+ RUN_TEST(test_hierarchy_2);
+#elif defined(TEST_USB_SM_FRAMEWORK_H1)
+ RUN_TEST(test_hierarchy_1);
+#else
+ RUN_TEST(test_hierarchy_0);
+#endif
+ test_print_result();
+}
diff --git a/test/usb_sm_framework_h3.tasklist b/test/usb_sm_framework_h3.tasklist
new file mode 100644
index 0000000000..d1b4e6e2ca
--- /dev/null
+++ b/test/usb_sm_framework_h3.tasklist
@@ -0,0 +1,18 @@
+/* Copyright (c) 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST \
+ TASK_TEST(TEST, test_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/test/usb_typec_ctvpd.c b/test/usb_typec_ctvpd.c
new file mode 100644
index 0000000000..19fbfa1b8a
--- /dev/null
+++ b/test/usb_typec_ctvpd.c
@@ -0,0 +1,1488 @@
+/* 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 "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};
+
+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_COUNT];
+
+uint64_t wait_for_state_change(int port, uint64_t timeout)
+{
+ uint64_t start;
+ uint64_t wait;
+ uint32_t state = get_typec_state_id(port);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+
+ wait = get_time().val + timeout;
+ start = get_time().val;
+ while (get_typec_state_id(port) == state && get_time().val < wait)
+ task_wait_event(5 * 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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == ATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host Port VBUS Removed
+ */
+
+ host_connect_source(VBUS_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == 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_vconn(VCONN_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ 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_TYPEC_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_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * Transition to ATTACHED_SNK
+ */
+
+ host_connect_source(VBUS_5);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 20 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == 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_CABLE_VPD, 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_connect_source(VBUS_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_disconnect_source();
+
+ 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_typec_state_id(port) == 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_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == 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_typec_state_id(port) == CTUNATTACHED_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_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ /* Remove Power Source */
+ TEST_ASSERT(ct_disconnect_source());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ /* Attach Sink */
+ TEST_ASSERT(ct_connect_sink(CC1, SRC_CON_DEF));
+
+ wait_for_state_change(port, PD_T_DRP_SNK);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_UNSUPPORTED);
+
+ /* Remove VCONN (Host detach) */
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ 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_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /* Remove Host CC */
+ mock_set_host_cc_source_voltage(0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_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_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACHED_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_typec_state_id(port) == CTDISABLED_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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == ATTACH_WAIT_SRC);
+
+ /* Remove VBUS */
+ TEST_ASSERT(ct_disconnect_source());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == 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_CABLE_VPD, 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_typec_state_id(port) == CTUNATTACHED_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_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_DRP_SRC + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == ATTACH_WAIT_SRC);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == 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_typec_state_id(port) == TRY_WAIT_SRC);
+ TEST_ASSERT(check_host_rpusb());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == 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_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == 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_typec_state_id(port) == CTUNATTACHED_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_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_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_typec_state_id(port) == CTATTACH_WAIT_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTTRY_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_typec_state_id(port) == CTATTACHED_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_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ return EC_SUCCESS;
+}
+#endif
+
+void run_test(void)
+{
+ 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
+ test_print_result();
+}
+
diff --git a/test/usb_typec_ctvpd.tasklist b/test/usb_typec_ctvpd.tasklist
new file mode 100644
index 0000000000..96ce0f08eb
--- /dev/null
+++ b/test/usb_typec_ctvpd.tasklist
@@ -0,0 +1,18 @@
+/* Copyright (c) 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST \
+ TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/test/usb_typec_vpd.tasklist b/test/usb_typec_vpd.tasklist
new file mode 120000
index 0000000000..3e39415ded
--- /dev/null
+++ b/test/usb_typec_vpd.tasklist
@@ -0,0 +1 @@
+usb_typec_ctvpd.tasklist \ No newline at end of file
diff --git a/test/vpd_api.c b/test/vpd_api.c
new file mode 100644
index 0000000000..960c0c664b
--- /dev/null
+++ b/test/vpd_api.c
@@ -0,0 +1,586 @@
+/* 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.
+ */
+
+#include "registers.h"
+#include "vpd_api.h"
+#include "driver/tcpm/tcpm.h"
+#include "console.h"
+/*
+ * Polarity based on 'DFP Perspective' (see table USB Type-C Cable and Connector
+ * Specification)
+ *
+ * CC1 CC2 STATE POSITION
+ * ----------------------------------------
+ * open open NC N/A
+ * Rd open UFP attached 1
+ * open Rd UFP attached 2
+ * open Ra pwr cable no UFP N/A
+ * Ra open pwr cable no UFP N/A
+ * Rd Ra pwr cable & UFP 1
+ * Ra Rd pwr cable & UFP 2
+ * Rd Rd dbg accessory N/A
+ * Ra Ra audio accessory N/A
+ *
+ * Note, V(Rd) > V(Ra)
+ */
+#ifndef PD_SRC_RD_THRESHOLD
+#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV
+#endif
+#ifndef PD_SRC_VNC
+#define PD_SRC_VNC PD_SRC_DEF_VNC_MV
+#endif
+
+#ifndef CC_RA
+#define CC_RA(port, cc, sel) (cc < pd_src_rd_threshold[ct_cc_rp_value])
+#endif
+#define CC_RD(cc) ((cc >= PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC))
+#ifndef CC_NC
+#define CC_NC(port, cc, sel) (cc >= PD_SRC_VNC)
+#endif
+
+/*
+ * Polarity based on 'UFP Perspective'.
+ *
+ * CC1 CC2 STATE POSITION
+ * ----------------------------------------
+ * open open NC N/A
+ * Rp open DFP attached 1
+ * open Rp DFP attached 2
+ * Rp Rp Accessory attached N/A
+ */
+#ifndef PD_SNK_VA
+#define PD_SNK_VA PD_SNK_VA_MV
+#endif
+
+#define CC_RP(cc) (cc >= PD_SNK_VA)
+
+/* Mock Board State */
+static enum vpd_pwr mock_vconn_pwr_sel_odl;
+static enum vpd_gpo mock_cc1_cc2_rd_l;
+static enum vpd_gpo mock_cc_db_en_od;
+static enum vpd_gpo mock_cc_rpusb_odh;
+static enum vpd_cc mock_ct_cl_sel;
+static int mock_mcu_cc_en;
+static enum vpd_billboard mock_present_billboard;
+static int mock_red_led;
+static int mock_green_led;
+static int mock_vbus_pass_en;
+
+static int mock_read_host_vbus;
+static int mock_read_ct_vbus;
+static int mock_read_vconn;
+
+static struct mock_pin mock_cc2_rpusb_odh;
+static struct mock_pin mock_cc2_rp3a0_rd_l;
+static struct mock_pin mock_cc1_rpusb_odh;
+static struct mock_pin mock_cc1_rp3a0_rd_l;
+static struct mock_pin mock_cc_vpdmcu;
+static struct mock_pin mock_cc_rp3a0_rd_l;
+
+/* Charge-Through pull up/down enabled */
+static int ct_cc_pull;
+/* Charge-Through pull up value */
+static int ct_cc_rp_value;
+
+/* Charge-Through pull up/down enabled */
+static int host_cc_pull;
+/* Charge-Through pull up value */
+static int host_cc_rp_value;
+
+/* Voltage thresholds for Ra attach in normal SRC mode */
+static int pd_src_rd_threshold[TYPEC_RP_RESERVED] = {
+ PD_SRC_DEF_RD_THRESH_MV,
+ PD_SRC_1_5_RD_THRESH_MV,
+ PD_SRC_3_0_RD_THRESH_MV,
+};
+
+enum vpd_pwr mock_get_vconn_pwr_source(void)
+{
+ return mock_vconn_pwr_sel_odl;
+}
+
+int mock_get_ct_cc1_rpusb(void)
+{
+ return mock_cc1_rpusb_odh.value;
+}
+
+int mock_get_ct_cc2_rpusb(void)
+{
+ return mock_cc2_rpusb_odh.value;
+}
+
+enum vpd_gpo mock_get_ct_rd(void)
+{
+ return mock_cc1_cc2_rd_l;
+}
+
+enum vpd_gpo mock_get_cc_rpusb_odh(void)
+{
+ return mock_cc_rpusb_odh;
+}
+
+enum vpd_gpo mock_get_cc_db_en_od(void)
+{
+ return mock_cc_db_en_od;
+}
+
+enum vpd_cc moch_get_ct_cl_sel(void)
+{
+ return mock_ct_cl_sel;
+}
+
+int mock_get_mcu_cc_en(void)
+{
+ return mock_mcu_cc_en;
+}
+
+enum vpd_billboard mock_get_present_billboard(void)
+{
+ return mock_present_billboard;
+}
+
+int mock_get_red_led(void)
+{
+ return mock_red_led;
+}
+
+int mock_get_green_led(void)
+{
+ return mock_green_led;
+}
+
+int mock_get_vbus_pass_en(void)
+{
+ return mock_vbus_pass_en;
+}
+
+void mock_set_host_cc_sink_voltage(int v)
+{
+ mock_cc_vpdmcu.value = v;
+}
+
+void mock_set_host_cc_source_voltage(int v)
+{
+ mock_cc_vpdmcu.value2 = v;
+}
+
+void mock_set_host_vbus(int v)
+{
+ mock_read_host_vbus = v;
+}
+
+void mock_set_ct_vbus(int v)
+{
+ mock_read_ct_vbus = v;
+}
+
+void mock_set_vconn(int v)
+{
+ mock_read_vconn = v;
+}
+
+int mock_get_cfg_cc2_rpusb_odh(void)
+{
+ return mock_cc2_rpusb_odh.cfg;
+}
+
+int mock_set_cc2_rpusb_odh(int v)
+{
+ if (mock_cc2_rpusb_odh.cfg == PIN_ADC) {
+ mock_cc2_rpusb_odh.value = v;
+ return 1;
+ }
+ return 0;
+}
+
+int mock_get_cfg_cc2_rp3a0_rd_l(void)
+{
+ return mock_cc2_rp3a0_rd_l.cfg;
+}
+
+int mock_set_cc2_rp3a0_rd_l(int v)
+{
+ if (mock_cc2_rp3a0_rd_l.cfg == PIN_ADC) {
+ mock_cc2_rp3a0_rd_l.value = v;
+ return 1;
+ }
+
+ return 0;
+}
+
+int mock_get_cc1_rpusb_odh(void)
+{
+ return mock_cc1_rpusb_odh.cfg;
+}
+
+int mock_set_cc1_rpusb_odh(int v)
+{
+ if (mock_cc1_rpusb_odh.cfg == PIN_ADC) {
+ mock_cc1_rpusb_odh.value = v;
+ return 1;
+ }
+
+ return 0;
+}
+
+int mock_get_cfg_cc_vpdmcu(void)
+{
+ return mock_cc_vpdmcu.cfg;
+}
+
+enum vpd_pin mock_get_cfg_cc_rp3a0_rd_l(void)
+{
+ return mock_cc_rp3a0_rd_l.cfg;
+}
+
+int mock_get_cc_rp3a0_rd_l(void)
+{
+ return mock_cc_rp3a0_rd_l.value;
+}
+
+int mock_get_cfg_cc1_rp3a0_rd_l(void)
+{
+ return mock_cc1_rp3a0_rd_l.cfg;
+}
+
+int mock_set_cc1_rp3a0_rd_l(int v)
+{
+ if (mock_cc1_rp3a0_rd_l.cfg == PIN_ADC) {
+ mock_cc1_rp3a0_rd_l.value = v;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Convert CC voltage to CC status */
+static int vpd_cc_voltage_to_status(int cc_volt, int cc_pull)
+{
+ /* If we have a pull-up, then we are source, check for Rd. */
+ if (cc_pull == TYPEC_CC_RP) {
+ if (CC_NC(0, cc_volt, 0))
+ return TYPEC_CC_VOLT_OPEN;
+ else if (CC_RA(0, cc_volt, 0))
+ return TYPEC_CC_VOLT_RA;
+ else
+ return TYPEC_CC_VOLT_RD;
+ /* If we have a pull-down, then we are sink, check for Rp. */
+ } else if (cc_pull == TYPEC_CC_RD || cc_pull == TYPEC_CC_RA_RD) {
+ if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD)
+ return TYPEC_CC_VOLT_RP_3_0;
+ else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD)
+ return TYPEC_CC_VOLT_RP_1_5;
+ else if (CC_RP(cc_volt))
+ return TYPEC_CC_VOLT_RP_DEF;
+ else
+ return TYPEC_CC_VOLT_OPEN;
+ } else {
+ /* If we are open, then always return 0 */
+ return 0;
+ }
+}
+
+void vpd_ct_set_pull(int pull, int rp_value)
+{
+ ct_cc_pull = pull;
+
+ switch (pull) {
+ case TYPEC_CC_RP:
+ ct_cc_rp_value = rp_value;
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ switch (rp_value) {
+ case TYPEC_RP_USB:
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc1_rpusb_odh(PIN_GPO, 1);
+ vpd_config_cc2_rpusb_odh(PIN_GPO, 1);
+ break;
+ case TYPEC_RP_3A0:
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_GPO, 1);
+ vpd_config_cc2_rp3a0_rd_l(PIN_GPO, 1);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_cc1_cc2_db_en_l(GPO_LOW);
+ break;
+ case TYPEC_CC_OPEN:
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ break;
+ }
+}
+
+void vpd_ct_get_cc(int *cc1, int *cc2)
+{
+ int cc1_v;
+ int cc2_v;
+
+ switch (ct_cc_pull) {
+ case TYPEC_CC_RP:
+ switch (ct_cc_rp_value) {
+ case TYPEC_RP_USB:
+ cc1_v = mock_cc1_rp3a0_rd_l.value;
+ cc2_v = mock_cc2_rp3a0_rd_l.value;
+ break;
+ case TYPEC_RP_3A0:
+ cc1_v = mock_cc1_rpusb_odh.value;
+ cc2_v = mock_cc2_rpusb_odh.value;
+ break;
+ }
+
+ if (!cc1_v && !cc2_v) {
+ cc1_v = PD_SRC_VNC;
+ cc2_v = PD_SRC_VNC;
+ }
+ break;
+ case TYPEC_CC_RD:
+ cc1_v = mock_cc1_rpusb_odh.value;
+ cc2_v = mock_cc2_rpusb_odh.value;
+ break;
+ case TYPEC_CC_OPEN:
+ *cc1 = 0;
+ *cc2 = 0;
+ return;
+ }
+
+ *cc1 = vpd_cc_voltage_to_status(cc1_v, ct_cc_pull);
+ *cc2 = vpd_cc_voltage_to_status(cc2_v, ct_cc_pull);
+}
+
+void vpd_host_set_pull(int pull, int rp_value)
+{
+ host_cc_pull = pull;
+
+ switch (pull) {
+ case TYPEC_CC_RP:
+ vpd_cc_db_en_od(GPO_LOW);
+ host_cc_rp_value = rp_value;
+ switch (rp_value) {
+ case TYPEC_RP_USB:
+ vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0);
+ vpd_cc_rpusb_odh(GPO_HIGH);
+ break;
+ case TYPEC_RP_3A0:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 1);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_cc_db_en_od(GPO_LOW);
+
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0);
+ break;
+ case TYPEC_CC_RA_RD:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0);
+
+ /*
+ * RA is connected to VCONN
+ * RD is connected to CC
+ */
+ vpd_cc_db_en_od(GPO_HZ);
+ break;
+ case TYPEC_CC_OPEN:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0);
+ vpd_cc_db_en_od(GPO_LOW);
+
+ /*
+ * Do nothing. CC is open on entry to this function
+ */
+ break;
+ }
+}
+
+void vpd_host_get_cc(int *cc)
+{
+ int v;
+
+ if (host_cc_pull == TYPEC_CC_OPEN) {
+ *cc = 0;
+ return;
+ } else if (host_cc_pull == TYPEC_CC_RP) {
+ v = mock_cc_vpdmcu.value;
+ } else {
+ v = mock_cc_vpdmcu.value2;
+ }
+
+ *cc = vpd_cc_voltage_to_status(v, host_cc_pull);
+}
+
+void vpd_rx_enable(int en)
+{
+ if (en) {
+ mock_ct_cl_sel = 0;
+ mock_mcu_cc_en = 1;
+ }
+
+ tcpm_set_polarity(0, 0);
+ tcpm_set_rx_enable(0, en);
+}
+
+/*
+ * PA1: Configure as ADC, CMP, or GPO
+ */
+void vpd_config_cc_vpdmcu(enum vpd_pin cfg, int en)
+{
+ mock_cc_vpdmcu.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc_vpdmcu.value = en ? 1 : 0;
+}
+
+/*
+ * PA2: Configure as COMP2_INM6 or GPO
+ */
+void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ mock_cc_rp3a0_rd_l.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc_rp3a0_rd_l.value = en ? 1 : 0;
+}
+
+/*
+ * PA4: Configure as ADC, CMP, or GPO
+ */
+void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ mock_cc1_rp3a0_rd_l.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc1_rp3a0_rd_l.value = en ? 1 : 0;
+}
+
+/*
+ * PA5: Configure as ADC, COMP, or GPO
+ */
+void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ mock_cc2_rp3a0_rd_l.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc2_rp3a0_rd_l.value = en ? 1 : 0;
+}
+
+/*
+ * PB0: Configure as ADC or GPO
+ */
+void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ mock_cc1_rpusb_odh.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc1_rpusb_odh.value = en ? 1 : 0;
+}
+
+/*
+ * PB1: Configure as ADC or GPO
+ */
+void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ mock_cc2_rpusb_odh.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc2_rpusb_odh.value = en ? 1 : 0;
+}
+
+int vpd_read_host_vbus(void)
+{
+ return mock_read_host_vbus;
+}
+
+int vpd_read_ct_vbus(void)
+{
+ return mock_read_ct_vbus;
+}
+
+int vpd_read_vconn(void)
+{
+ return mock_read_vconn;
+}
+
+int vpd_is_host_vbus_present(void)
+{
+ return (vpd_read_host_vbus() >= PD_SNK_VA);
+}
+
+int vpd_is_ct_vbus_present(void)
+{
+ return (vpd_read_ct_vbus() >= PD_SNK_VA);
+}
+
+int vpd_is_vconn_present(void)
+{
+ return (vpd_read_vconn() >= PD_SNK_VA);
+}
+
+int vpd_read_rdconnect_ref(void)
+{
+ return 200; /* 200 mV */
+}
+
+void vpd_red_led(int on)
+{
+ mock_red_led = on ? 0 : 1;
+}
+
+void vpd_green_led(int on)
+{
+ mock_green_led = on ? 0 : 1;
+}
+
+void vpd_vbus_pass_en(int en)
+{
+ mock_vbus_pass_en = en ? 1 : 0;
+}
+
+void vpd_present_billboard(enum vpd_billboard bb)
+{
+ mock_present_billboard = bb;
+}
+
+void vpd_mcu_cc_en(int en)
+{
+ mock_mcu_cc_en = en ? 1 : 0;
+}
+
+void vpd_ct_cc_sel(enum vpd_cc sel)
+{
+ mock_ct_cl_sel = sel;
+}
+
+/* Set as GPO High, GPO Low, or High-Z */
+void vpd_cc_db_en_od(enum vpd_gpo val)
+{
+ mock_cc_db_en_od = val;
+}
+
+void vpd_cc_rpusb_odh(enum vpd_gpo val)
+{
+ mock_cc_rpusb_odh = val;
+}
+
+void vpd_cc1_cc2_db_en_l(enum vpd_gpo val)
+{
+ mock_cc1_cc2_rd_l = val;
+}
+
+void vpd_vconn_pwr_sel_odl(enum vpd_pwr en)
+{
+ mock_vconn_pwr_sel_odl = en;
+}
diff --git a/test/vpd_api.h b/test/vpd_api.h
new file mode 100644
index 0000000000..3db4803288
--- /dev/null
+++ b/test/vpd_api.h
@@ -0,0 +1,333 @@
+/* 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.
+ */
+
+/* Vconn Power Device API module */
+
+#ifndef __CROS_EC_VPD_API_H
+#define __CROS_EC_VPD_API_H
+
+#include "adc.h"
+#include "gpio.h"
+#include "usb_pd.h"
+
+/*
+ * Type C power source charge current limits are identified by their cc
+ * voltage (set by selecting the proper Rd resistor). Any voltage below
+ * TYPE_C_SRC_DEFAULT_THRESHOLD will not be identified as a type C charger.
+ */
+#define TYPE_C_SRC_DEFAULT_THRESHOLD 200 /* mV */
+#define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */
+#define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */
+
+
+enum vpd_pin {
+ PIN_ADC,
+ PIN_CMP,
+ PIN_GPO
+};
+
+enum vpd_gpo {
+ GPO_HZ,
+ GPO_HIGH,
+ GPO_LOW
+};
+
+enum vpd_pwr {
+ PWR_VCONN,
+ PWR_VBUS,
+};
+
+enum vpd_cc {
+ CT_OPEN,
+ CT_CC1,
+ CT_CC2
+};
+
+enum vpd_billboard {
+ BB_NONE,
+ BB_SRC,
+ BB_SNK
+};
+
+struct mock_pin {
+ enum vpd_pin cfg;
+ int value;
+ int value2;
+};
+
+enum vpd_pwr mock_get_vconn_pwr_source(void);
+enum vpd_gpo mock_get_ct_rd(void);
+enum vpd_gpo mock_get_cc_rp1a5_odh(void);
+enum vpd_gpo mock_get_cc_rpusb_odh(void);
+enum vpd_gpo mock_get_cc_db_en_od(void);
+enum vpd_cc moch_get_ct_cl_sel(void);
+int mock_get_mcu_cc_en(void);
+enum vpd_billboard mock_get_present_billboard(void);
+int mock_get_red_led(void);
+int mock_get_green_led(void);
+int mock_get_vbus_pass_en(void);
+int mock_set_cc_vpdmcu(int v);
+void mock_set_host_vbus(int v);
+void mock_set_ct_vbus(int v);
+void mock_set_vconn(int v);
+int mock_get_cfg_cc2_rpusb_odh(void);
+int mock_set_cc2_rpusb_odh(int v);
+int mock_get_cfg_cc2_rp3a0_rd_l(void);
+int mock_set_cc2_rp3a0_rd_l(int v);
+int mock_get_cfg_cc1_rpusb_odh(void);
+int mock_set_cc1_rpusb_odh(int v);
+int mock_get_cfg_cc_vpdmcu(void);
+int mock_get_cc_vpdmcu(int v);
+enum vpd_pin mock_get_cfg_cc_rp3a0_rd_l(void);
+int mock_get_cc_rp3a0_rd_l(void);
+int mock_get_cfg_cc1_rp3a0_rd_l(void);
+int mock_set_cc1_rp3a0_rd_l(int v);
+void mock_set_host_cc_sink_voltage(int v);
+void mock_set_host_cc_source_voltage(int v);
+int mock_get_ct_cc1_rpusb(void);
+int mock_get_ct_cc2_rpusb(void);
+
+/**
+ * Set Charge-Through Rp or Rd on CC lines
+ *
+ * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD
+ * @param rp_value When pull is RP, set this to
+ * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored
+ * for TYPEC_CC_RD
+ */
+void vpd_ct_set_pull(int pull, int rp_value);
+
+/**
+ * Get the status of the Charge-Through CC lines
+ *
+ * @param cc1 Either TYPEC_CC_VOLT_OPEN,
+ * TYPEC_CC_VOLT_RA,
+ * TYPEC_CC_VOLT_RD,
+ * any other value is considered RP
+ * @param cc2 Either TYPEC_CC_VOLT_OPEN,
+ * TYPEC_CC_VOLT_RA,
+ * TYPEC_CC_VOLT_RD,
+ * any other value is considered RP
+ */
+void vpd_ct_get_cc(int *cc1, int *cc2);
+
+/**
+ * Set Host Rp or Rd on CC lines
+ *
+ * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD
+ * @param rp_value When pull is RP, set this to
+ * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored
+ * for TYPEC_CC_RD
+ */
+void vpd_host_set_pull(int pull, int rp_value);
+
+/**
+ * Get the status of the Host CC line
+ *
+ * @param cc Either TYPEC_CC_VOLT_SNK_DEF, TYPEC_CC_VOLT_SNK_1_5,
+ * TYPEC_CC_VOLT_SNK_3_0, or TYPEC_CC_RD
+ */
+void vpd_host_get_cc(int *cc);
+
+/**
+ * Set RX Enable flag
+ *
+ * @param en 1 for enable, 0 for disable
+ */
+void vpd_rx_enable(int en);
+
+/**
+ * Configure the cc_vpdmcu pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc_vpdmcu(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc1_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc2_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc1_rpusb_odh pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc2_rpusb_odh pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc_db_en_od pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_db_en_od(enum vpd_gpo val);
+
+/**
+ * Configure the cc_rpusb_odh pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_rpusb_odh(enum vpd_gpo val);
+
+/**
+ * Configure the cc_rp1a5_odh pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_rp1a5_odh(enum vpd_gpo val);
+
+/**
+ * Configure the cc1_cc2_db_en_l pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc1_cc2_db_en_l(enum vpd_gpo val);
+
+/**
+ * Get status of host vbus
+ *
+ * @return 1 if host vbus is present, else 0
+ */
+int vpd_is_host_vbus_present(void);
+
+/**
+ * Get status of charge-through vbus
+ *
+ * @return 1 if charge-through vbus is present, else 0
+ */
+int vpd_is_ct_vbus_present(void);
+
+/**
+ * Get status of vconn
+ *
+ * @return 1 if vconn is present, else 0
+ */
+int vpd_is_vconn_present(void);
+
+/**
+ * Read Host VBUS voltage. Range from 22000mV to 3000mV
+ *
+ * @return vbus voltage
+ */
+int vpd_read_host_vbus(void);
+
+/**
+ * Read Host CC voltage.
+ *
+ * @return cc voltage
+ */
+int vpd_read_cc_host(void);
+
+/**
+ * Read voltage on cc_vpdmcu pin
+ *
+ * @return cc_vpdmcu voltage
+ */
+int vpd_read_cc_vpdmcu(void);
+
+/**
+ * Read charge-through VBUS voltage. Range from 22000mV to 3000mV
+ *
+ * @return charge-through vbus voltage
+ */
+int vpd_read_ct_vbus(void);
+
+/**
+ * Read VCONN Voltage. Range from 5500mV to 3000mV
+ *
+ * @return vconn voltage
+ */
+int vpd_read_vconn(void);
+
+/**
+ * Turn ON/OFF Red LED. Should be off when performing power
+ * measurements.
+ *
+ * @param on 0 turns LED off, any other value turns it ON
+ */
+void vpd_red_led(int on);
+
+/**
+ * Turn ON/OFF Green LED. Should be off when performing power
+ * measurements.
+ *
+ * @param on 0 turns LED off, any other value turns it ON
+ */
+void vpd_green_led(int on);
+
+/**
+ * Connects/Disconnects the Host VBUS to the Charge-Through VBUS.
+ *
+ * @param en 0 disconnectes the VBUS, any other value connects VBUS.
+ */
+void vpd_vbus_pass_en(int en);
+
+/**
+ * Preset Billboard device
+ *
+ * @param bb BB_NONE no billboard presented,
+ * BB_SRC source connected but not in charge-through
+ * BB_SNK sink connected
+ */
+void vpd_present_billboard(enum vpd_billboard bb);
+
+/**
+ * Enables the MCU to host cc communication
+ *
+ * @param en 1 enabled, 0 disabled
+ */
+void vpd_mcu_cc_en(int en);
+
+/**
+ * Selects which supply to power the VPD from
+ *
+ * @param en PWR_VCONN or PWR_VBUS
+ */
+void vpd_vconn_pwr_sel_odl(enum vpd_pwr en);
+
+/**
+ * Controls if the Charge-Through's CC1, CC2, or neither is
+ * connected to Host CC
+ *
+ * @param sel CT_OPEN neither, CT_CC1 cc1, CT_CC2 cc2
+ */
+void vpd_ct_cc_sel(enum vpd_cc sel);
+
+#endif /* __CROS_EC_VPD_API_H */