/* Copyright 2014 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test USB PD module. */ #include "common.h" #include "crc.h" #include "task.h" #include "test_util.h" #include "timer.h" #include "usb_pd.h" #include "usb_pd_test_util.h" #include "util.h" #define PORT0 0 #define PORT1 1 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; } pd_port[CONFIG_USB_PD_PORT_COUNT]; static int give_back_called; /* Mock functions */ int pd_adc_read(int port, int cc) { if (pd_port[port].host_mode && pd_port[port].partner_role == PD_ROLE_SINK) /* we are source connected to sink, return Rd/Open */ return (pd_port[port].partner_polarity == cc) ? 400 : 3000; else if (!pd_port[port].host_mode && pd_port[port].partner_role == PD_ROLE_SOURCE) /* we are sink connected to source, return Rp/Open */ return (pd_port[port].partner_polarity == cc) ? 1700 : 0; else if (pd_port[port].host_mode) /* no sink on the other side, both CC are opened */ return 3000; else if (!pd_port[port].host_mode) /* no source on the other side, both CC are opened */ return 0; /* should never get here */ return 0; } int pd_snk_is_vbus_provided(int port) { return pd_port[port].has_vbus; } void pd_set_host_mode(int port, int enable) { pd_port[port].host_mode = enable; } void pd_select_polarity(int port, int polarity) { pd_port[port].polarity = polarity; } int pd_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) { return 0; } /* Tests */ 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 void init_ports(void) { int i; for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; ++i) { pd_port[i].host_mode = 0; pd_port[i].partner_role = -1; pd_port[i].has_vbus = 0; } } 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_wait(int port) { uint16_t header = PD_HEADER(PD_CTRL_WAIT, PD_ROLE_SOURCE, PD_ROLE_DFP, pd_port[port].msg_rx_id, 0); simulate_rx_msg(port, header, 0, NULL); } static void simulate_accept(int port) { uint16_t header = PD_HEADER(PD_CTRL_ACCEPT, PD_ROLE_SOURCE, PD_ROLE_DFP, pd_port[port].msg_rx_id, 0); simulate_rx_msg(port, header, 0, NULL); } static void simulate_reject(int port) { uint16_t header = PD_HEADER(PD_CTRL_REJECT, PD_ROLE_SOURCE, PD_ROLE_DFP, pd_port[port].msg_rx_id, 0); simulate_rx_msg(port, header, 0, NULL); } static void simulate_source_cap(int port, uint32_t cnt) { uint32_t src_pdo_cnt = (cnt == 0) ? 1 : pd_src_pdo_cnt; uint16_t header = PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE, PD_ROLE_DFP, pd_port[port].msg_rx_id, src_pdo_cnt); simulate_rx_msg(port, header, src_pdo_cnt, pd_src_pdo); } static void simulate_goodcrc(int port, int role, int id) { simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0), 0, NULL); } 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)) && pd_test_tx_msg_verify_crc(port) && pd_test_tx_msg_verify_eop(port); } static void plug_in_source(int port, int polarity) { pd_port[port].has_vbus = 1; pd_port[port].partner_role = PD_ROLE_SOURCE; pd_port[port].partner_polarity = polarity; } static void plug_in_sink(int port, int polarity) { pd_port[port].has_vbus = 0; pd_port[port].partner_role = PD_ROLE_SINK; pd_port[port].partner_polarity = polarity; } static void unplug(int port) { pd_port[port].msg_tx_id = 0; pd_port[port].msg_rx_id = 0; pd_port[port].has_vbus = 0; pd_port[port].partner_role = -1; task_wake(PD_PORT_TO_TASK_ID(port)); usleep(30 * MSEC); } void pd_snk_give_back(int port, uint32_t * const ma, uint32_t * const mv) { if (*ma == 3000) give_back_called = 1; } static void simulate_ps_rdy(int port) { uint16_t header = PD_HEADER(PD_CTRL_PS_RDY, PD_ROLE_SOURCE, PD_ROLE_DFP, pd_port[port].msg_rx_id, 0); simulate_rx_msg(port, header, 0, NULL); } static void simulate_goto_min(int port) { uint16_t header = PD_HEADER(PD_CTRL_GOTO_MIN, PD_ROLE_SOURCE, PD_ROLE_DFP, pd_port[port].msg_rx_id, 0); simulate_rx_msg(port, header, 0, NULL); } static int test_request_with_wait_and_contract(void) { #ifdef CONFIG_USB_PD_GIVE_BACK uint32_t expected_rdo = RDO_FIXED(2, 3000, PD_MIN_CURRENT_MA, RDO_GIVE_BACK); #else uint32_t expected_rdo = RDO_FIXED(2, 3000, 3000, 0); #endif uint8_t port = PORT0; plug_in_source(port, 0); task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC); TEST_ASSERT(pd_port[port].polarity == 0); /* We're in SNK_DISCOVERY now. Let's send the source cap. */ simulate_source_cap(port, 1); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_rx_id)); /* Wait for the power request */ task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */ inc_rx_id(port); /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); 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); /* Request 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); /* We're in SNK_REQUESTED. Send accept */ simulate_accept(port); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(0, 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); /* * We're in SNK_TRANSITION. * And we have an explicit power contract. */ simulate_source_cap(port, 1); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_rx_id)); /* Wait for the power request */ task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */ inc_rx_id(port); /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); 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); /* Request 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); /* We're in SNK_REQUESTED. Send wait */ simulate_wait(port); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[port].msg_rx_id)); task_wake(PD_PORT_TO_TASK_ID(port)); /* PD_T_SINK_REQUEST. Request is sent again after 100 ms */ task_wait_event(100 * MSEC); inc_rx_id(port); task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(30 * MSEC); /* We had an explicit contract. So request should have been resent. */ /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); 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); /* Request 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); /* We're in SNK_REQUESTED. Send accept */ simulate_accept(port); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(0, 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); /* We're in SNK_TRANSITION. Send ps_rdy */ simulate_ps_rdy(port); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(0, 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); /* We're in SNK_READY. Send goto_min */ simulate_goto_min(port); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(0, 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); #ifdef CONFIG_USB_PD_GIVE_BACK TEST_ASSERT(give_back_called); #else TEST_ASSERT(!give_back_called); #endif /* We're done */ unplug(port); return EC_SUCCESS; } static int test_request_with_wait(void) { #ifdef CONFIG_USB_PD_GIVE_BACK uint32_t expected_rdo = RDO_FIXED(1, 900, PD_MIN_CURRENT_MA, RDO_CAP_MISMATCH | RDO_GIVE_BACK); #else uint32_t expected_rdo = RDO_FIXED(1, 900, 900, RDO_CAP_MISMATCH); #endif uint8_t port = PORT0; plug_in_source(port, 0); task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC); TEST_ASSERT(pd_port[port].polarity == 0); /* We're in SNK_DISCOVERY now. Let's send the source cap. */ simulate_source_cap(port, 0); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_rx_id)); /* Wait for the power request */ task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */ inc_rx_id(port); /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); 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); /* Request is good. Send GoodCRC */ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id); task_wake(PD_PORT_TO_TASK_ID(0)); task_wait_event(30 * MSEC); inc_tx_id(port); /* We're in SNK_REQUESTED. Send wait */ simulate_wait(port); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(0, 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); /* We didn't have an explicit contract. So we're in SNK_DISCOVERY. */ /* Resend Source Cap. */ simulate_source_cap(port, 0); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_rx_id)); /* Wait for the power request */ task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */ inc_rx_id(port); /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); 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); /* Request 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); /* We're done */ unplug(port); return EC_SUCCESS; } static int test_request_with_reject(void) { #ifdef CONFIG_USB_PD_GIVE_BACK uint32_t expected_rdo = RDO_FIXED(1, 900, PD_MIN_CURRENT_MA, RDO_CAP_MISMATCH | RDO_GIVE_BACK); #else uint32_t expected_rdo = RDO_FIXED(1, 900, 900, RDO_CAP_MISMATCH); #endif uint8_t port = PORT0; plug_in_source(port, 0); task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC); TEST_ASSERT(pd_port[port].polarity == 0); /* We're in SNK_DISCOVERY now. Let's send the source cap. */ simulate_source_cap(port, 0); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_rx_id)); /* Wait for the power request */ task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */ inc_rx_id(port); /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); 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); /* Request is good. Send GoodCRC */ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id); task_wake(PD_PORT_TO_TASK_ID(0)); task_wait_event(30 * MSEC); inc_tx_id(port); /* We're in SNK_REQUESTED. Send reject */ simulate_reject(port); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(0, 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); /* We're in SNK_READY. Send source cap. again. */ simulate_source_cap(port, 0); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_rx_id)); /* Wait for the power request */ task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */ inc_rx_id(port); /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); TEST_ASSERT(pd_test_tx_msg_verify_crc(port)); TEST_ASSERT(pd_test_tx_msg_verify_eop(port)); /* We're done */ unplug(port); return EC_SUCCESS; } static int test_request(void) { #ifdef CONFIG_USB_PD_GIVE_BACK uint32_t expected_rdo = RDO_FIXED(1, 900, PD_MIN_CURRENT_MA, RDO_CAP_MISMATCH | RDO_GIVE_BACK); #else uint32_t expected_rdo = RDO_FIXED(1, 900, 900, RDO_CAP_MISMATCH); #endif uint8_t port = PORT0; plug_in_source(port, 0); task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC); TEST_ASSERT(pd_port[port].polarity == 0); /* We're in SNK_DISCOVERY now. Let's send the source cap. */ simulate_source_cap(port, 0); task_wait_event(30 * MSEC); TEST_ASSERT(verify_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_rx_id)); /* Wait for the power request */ task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */ inc_rx_id(port); /* Process the request */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, PD_ROLE_UFP, pd_port[port].msg_tx_id, 1))); TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_rdo)); 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); /* Request 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); /* We're done */ unplug(port); return EC_SUCCESS; } static int test_sink(void) { int i; uint8_t port = PORT1; plug_in_sink(port, 1); task_wake(PD_PORT_TO_TASK_ID(port)); task_wait_event(250 * MSEC); /* tTypeCSinkWaitCap: 210~250 ms */ TEST_ASSERT(pd_port[port].polarity == 1); /* The source cap should be sent */ TEST_ASSERT(pd_test_tx_msg_verify_sop(port)); TEST_ASSERT(pd_test_tx_msg_verify_short(port, PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE, PD_ROLE_DFP, pd_port[port].msg_tx_id, pd_src_pdo_cnt))); for (i = 0; i < pd_src_pdo_cnt; ++i) TEST_ASSERT(pd_test_tx_msg_verify_word(port, pd_src_pdo[i])); TEST_ASSERT(pd_test_tx_msg_verify_crc(port)); TEST_ASSERT(pd_test_tx_msg_verify_eop(port)); /* Looks good. Ack the source cap. */ simulate_goodcrc(port, PD_ROLE_SINK, pd_port[port].msg_tx_id); task_wake(PD_PORT_TO_TASK_ID(port)); usleep(30 * MSEC); inc_tx_id(port); /* We're done */ unplug(port); return EC_SUCCESS; } void run_test(void) { test_reset(); init_ports(); pd_set_dual_role(PD_DRP_TOGGLE_ON); RUN_TEST(test_request); RUN_TEST(test_sink); RUN_TEST(test_request_with_wait); RUN_TEST(test_request_with_wait_and_contract); RUN_TEST(test_request_with_reject); test_print_result(); }