summaryrefslogtreecommitdiff
path: root/test/usb_pd.c
blob: fd10d44840ee452d7ec2ed254249114aeb975148 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/* 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_config.h"
#include "usb_pd_test_util.h"
#include "util.h"

struct pd_port_t {
	int host_mode;
	int cc_volt[2]; /* -1 for Hi-Z */
	int has_vbus;
	int msg_tx_id;
	int msg_rx_id;
	int polarity;
} pd_port[PD_PORT_COUNT];

/* Mock functions */

int pd_adc_read(int port, int cc)
{
	int val = pd_port[port].cc_volt[cc];
	if (val == -1)
		return pd_port[port].host_mode ? 3000 : 0;
	return val;
}

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;
}

/* 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 < PD_PORT_COUNT; ++i) {
		pd_port[i].host_mode = 0;
		pd_port[i].cc_volt[0] = pd_port[i].cc_volt[1] = -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_simulate_rx(port);
}

static void simulate_source_cap(int port)
{
	uint16_t header = PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
				    pd_port[port].msg_rx_id, pd_src_pdo_cnt);
	simulate_rx_msg(port, header, pd_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, id, 0),
			0, NULL);
}

static int verify_goodcrc(int port, int role, int id)
{
	return pd_test_tx_msg_verify_sop(0) &&
	       pd_test_tx_msg_verify_short(0, PD_HEADER(PD_CTRL_GOOD_CRC,
							role, id, 0)) &&
	       pd_test_tx_msg_verify_crc(0) &&
	       pd_test_tx_msg_verify_eop(0);
}

static void plug_in_source(int port, int polarity)
{
	pd_port[port].has_vbus = 1;
	pd_port[port].cc_volt[polarity] = 3000;
}

static void plug_in_sink(int port, int polarity)
{
	pd_port[port].has_vbus = 0;
	pd_port[port].cc_volt[polarity] = 0;
}

static void unplug(int port)
{
	pd_port[port].has_vbus = 0;
	pd_port[port].cc_volt[0] = -1;
	pd_port[port].cc_volt[1] = -1;
	task_wake(PORT_TO_TASK_ID(port));
	usleep(30 * MSEC);
}

static int test_request(void)
{
	plug_in_source(0, 0);
	task_wake(PORT_TO_TASK_ID(0));
	usleep(30 * MSEC);
	TEST_ASSERT(pd_port[0].polarity == 0);

	simulate_source_cap(0);
	task_wait_event(3 * MSEC);
	TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[0].msg_rx_id));
	task_wake(PORT_TO_TASK_ID(0));
	task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
	inc_rx_id(0);

	/* Process the request */
	TEST_ASSERT(pd_test_tx_msg_verify_sop(0));
	TEST_ASSERT(pd_test_tx_msg_verify_short(0,
			PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK,
				  pd_port[0].msg_tx_id, 1)));
	TEST_ASSERT(pd_test_tx_msg_verify_word(0, RDO_FIXED(2, 450, 900, 0)));
	TEST_ASSERT(pd_test_tx_msg_verify_crc(0));
	TEST_ASSERT(pd_test_tx_msg_verify_eop(0));
	inc_tx_id(0);
	unplug(0);
	return EC_SUCCESS;
}

static int test_sink(void)
{
	int i;

	plug_in_sink(1, 1);
	task_wake(PORT_TO_TASK_ID(1));
	task_wait_event(250 * MSEC); /* tTypeCSinkWaitCap: 210~250 ms */
	TEST_ASSERT(pd_port[1].polarity == 1);

	TEST_ASSERT(pd_test_tx_msg_verify_sop(1));
	TEST_ASSERT(pd_test_tx_msg_verify_short(1,
			PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
				  pd_port[1].msg_tx_id, pd_src_pdo_cnt)));
	for (i = 0; i < pd_src_pdo_cnt; ++i)
		TEST_ASSERT(pd_test_tx_msg_verify_word(1, pd_src_pdo[i]));
	TEST_ASSERT(pd_test_tx_msg_verify_crc(1));
	TEST_ASSERT(pd_test_tx_msg_verify_eop(1));

	simulate_goodcrc(1, PD_ROLE_SINK, pd_port[1].msg_tx_id);
	task_wake(PORT_TO_TASK_ID(1));
	usleep(30 * MSEC);
	inc_tx_id(1);

	unplug(1);
	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);

	test_print_result();
}