1368 lines
30 KiB
C
1368 lines
30 KiB
C
|
/* Copyright 2019 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*
|
||
|
* Test USB 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 "usb_sm_checks.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
#define PORT0 0
|
||
|
#define PORT1 1
|
||
|
|
||
|
/*
|
||
|
* These enum definitions are declared in usb_prl_sm and are private to that
|
||
|
* file. If those definitions are re-ordered, then we need to update these
|
||
|
* definitions (should be very rare).
|
||
|
*/
|
||
|
enum usb_prl_tx_state {
|
||
|
PRL_TX_PHY_LAYER_RESET,
|
||
|
PRL_TX_WAIT_FOR_MESSAGE_REQUEST,
|
||
|
PRL_TX_LAYER_RESET_FOR_TRANSMIT,
|
||
|
PRL_TX_WAIT_FOR_PHY_RESPONSE,
|
||
|
PRL_TX_SRC_SOURCE_TX,
|
||
|
PRL_TX_SNK_START_AMS,
|
||
|
PRL_TX_SRC_PENDING,
|
||
|
PRL_TX_SNK_PENDING,
|
||
|
PRL_TX_DISCARD_MESSAGE,
|
||
|
};
|
||
|
|
||
|
enum usb_prl_hr_state {
|
||
|
PRL_HR_WAIT_FOR_REQUEST,
|
||
|
PRL_HR_RESET_LAYER,
|
||
|
PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE,
|
||
|
PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE,
|
||
|
};
|
||
|
|
||
|
enum usb_rch_state {
|
||
|
RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER,
|
||
|
RCH_PASS_UP_MESSAGE,
|
||
|
RCH_PROCESSING_EXTENDED_MESSAGE,
|
||
|
RCH_REQUESTING_CHUNK,
|
||
|
RCH_WAITING_CHUNK,
|
||
|
RCH_REPORT_ERROR,
|
||
|
};
|
||
|
|
||
|
enum usb_tch_state {
|
||
|
TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE,
|
||
|
TCH_WAIT_FOR_TRANSMISSION_COMPLETE,
|
||
|
TCH_CONSTRUCT_CHUNKED_MESSAGE,
|
||
|
TCH_SENDING_CHUNKED_MESSAGE,
|
||
|
TCH_WAIT_CHUNK_REQUEST,
|
||
|
TCH_MESSAGE_RECEIVED,
|
||
|
TCH_MESSAGE_SENT,
|
||
|
TCH_REPORT_ERROR,
|
||
|
};
|
||
|
|
||
|
/* Defined in implementation */
|
||
|
enum usb_prl_tx_state prl_tx_get_state(const int port);
|
||
|
enum usb_prl_hr_state prl_hr_get_state(const int port);
|
||
|
enum usb_rch_state rch_get_state(const int port);
|
||
|
enum usb_tch_state tch_get_state(const int port);
|
||
|
|
||
|
|
||
|
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_message_received;
|
||
|
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_message_received)
|
||
|
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_message_received)
|
||
|
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_message_received = 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_message_received = 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_message_received)
|
||
|
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 (rch_get_state(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_message_received(int port)
|
||
|
{
|
||
|
pd_port[port].mock_pe_message_received = 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_prl_reset(void)
|
||
|
{
|
||
|
int port = PORT0;
|
||
|
|
||
|
enable_prl(port, 1);
|
||
|
|
||
|
prl_reset(port);
|
||
|
|
||
|
TEST_ASSERT(prl_tx_get_state(port) ==
|
||
|
PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
|
||
|
TEST_ASSERT(rch_get_state(port) ==
|
||
|
RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
|
||
|
TEST_ASSERT(tch_get_state(port) ==
|
||
|
TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE);
|
||
|
TEST_ASSERT(prl_hr_get_state(port) ==
|
||
|
PRL_HR_WAIT_FOR_REQUEST);
|
||
|
enable_prl(port, 0);
|
||
|
|
||
|
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(prl_tx_get_state(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(prl_tx_get_state(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(prl_tx_get_state(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(prl_tx_get_state(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(prl_tx_get_state(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(prl_tx_get_state(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(prl_tx_get_state(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(prl_tx_get_state(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(rch_get_state(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_message_received = 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_message_received);
|
||
|
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(rch_get_state(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_message_received = 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_message_received);
|
||
|
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(rch_get_state(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(rch_get_state(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(prl_tx_get_state(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(prl_tx_get_state(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(prl_hr_get_state(port) == PRL_HR_WAIT_FOR_REQUEST);
|
||
|
|
||
|
/* Simulate receiving hard reset from policy engine */
|
||
|
prl_execute_hard_reset(port);
|
||
|
|
||
|
TEST_ASSERT(prl_hr_get_state(port) == PRL_HR_RESET_LAYER);
|
||
|
TEST_ASSERT(prl_tx_get_state(port) ==
|
||
|
PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
|
||
|
|
||
|
cycle_through_state_machine(port, 1, 10 * MSEC);
|
||
|
|
||
|
TEST_ASSERT(prl_hr_get_state(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(prl_hr_get_state(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(prl_hr_get_state(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(prl_hr_get_state(port) == PRL_HR_WAIT_FOR_REQUEST);
|
||
|
|
||
|
/* Simulate receiving hard reset from port partner */
|
||
|
pd_execute_hard_reset(port);
|
||
|
|
||
|
TEST_ASSERT(prl_hr_get_state(port) == PRL_HR_RESET_LAYER);
|
||
|
TEST_ASSERT(prl_tx_get_state(port) ==
|
||
|
PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
|
||
|
|
||
|
cycle_through_state_machine(port, 1, 10 * MSEC);
|
||
|
|
||
|
TEST_ASSERT(prl_hr_get_state(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(prl_hr_get_state(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(prl_hr_get_state(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);
|
||
|
prl_run(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_prl_reset);
|
||
|
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_prl_reset);
|
||
|
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 */
|
||
|
|
||
|
/* Do basic state machine sanity checks last. */
|
||
|
RUN_TEST(test_prl_no_parent_cycles);
|
||
|
RUN_TEST(test_prl_no_empty_state);
|
||
|
RUN_TEST(test_prl_all_states_named);
|
||
|
|
||
|
test_print_result();
|
||
|
}
|
||
|
|