Software APIs
usbdev_setuprx_test.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 // USB SETUP RX test
6 //
7 // Test basic reception of SETUP token packet and associated DATA packet from
8 // the USB host.
9 //
10 // 1. Check for the presence of VBUS.
11 // 2. Assert the DP pull up, indicating the presence of a Full Speed device.
12 // 3. Check the DP line is high.
13 // 4. Wait until we receive an indication of SETUP packet reception or a timeout
14 // occurs.
15 // 5. Check the properties of the SETUP packet but do NOT attempt a thorough
16 // check of the packet content because it is not guaranteed what Control
17 // Transfer will be performed first by the host.
18 
19 #include <stdbool.h>
20 #include <stdint.h>
21 
22 #include "sw/device/lib/base/status.h"
29 #include "sw/device/lib/testing/pinmux_testutils.h"
30 #include "sw/device/lib/testing/test_framework/check.h"
32 
33 #define USBDEV_BASE_ADDR TOP_EARLGREY_USBDEV_BASE_ADDR
34 
35 /**
36  * USB device handle
37  */
38 static dif_usbdev_t usbdev;
39 static dif_usbdev_buffer_pool_t buffer_pool;
40 
41 /**
42  * Pinmux handle
43  */
44 static dif_pinmux_t pinmux;
45 
46 // Set the usbdev configuration according to whether or not pin flipping is
47 // desired.
48 static status_t config_set(bool pinflip) {
49  dif_usbdev_config_t config = {
51  .use_tx_d_se0 = kDifToggleDisabled,
52  .single_bit_eop = kDifToggleDisabled,
53  .pin_flip = dif_bool_to_toggle(pinflip),
54  .clock_sync_signals = kDifToggleEnabled,
55  };
56 
57  TRY(dif_usbdev_configure(&usbdev, &buffer_pool, config));
58 
59  return OK_STATUS();
60 }
61 
62 // Wait with timeout until the VBUS/SENSE signal is in the expected state.
63 static status_t vbus_wait(bool expected, uint32_t timeout_micros) {
64  ibex_timeout_t timeout = ibex_timeout_init(timeout_micros);
65  do {
66  // Read the current state of VBUS.
67  bool vbus;
68  TRY(dif_usbdev_status_get_sense(&usbdev, &vbus));
69  if (vbus == expected) {
70  return OK_STATUS();
71  }
72  } while (!ibex_timeout_check(&timeout));
73 
74  return DEADLINE_EXCEEDED();
75 }
76 
77 // Wait with timeout for the specified USB data line to be in the expected
78 // state.
79 static status_t line_wait(bool dp, bool expected, uint32_t timeout_micros) {
80  ibex_timeout_t timeout = ibex_timeout_init(timeout_micros);
81  do {
82  // Sense the current state of the pins.
84  TRY(dif_usbdev_get_phy_pins_status(&usbdev, &status));
85 
86  if ((dp && status.rx_dp == expected) || (!dp && status.rx_dn == expected)) {
87  return OK_STATUS();
88  }
89  } while (!ibex_timeout_check(&timeout));
90 
91  return DEADLINE_EXCEEDED();
92 }
93 
94 // Wait with timeout for the receipt of packet from the host controller; this
95 // is expected to be the DATA packet indicating the start of a Control Transfer.
96 static status_t packet_wait(dif_usbdev_rx_packet_info_t *info,
97  dif_usbdev_buffer_t *buf, uint32_t timeout_micros) {
98  ibex_timeout_t timeout = ibex_timeout_init(timeout_micros);
99  do {
100  // Attempt to collect a packet from the Rx FIFO.
101  uint8_t depth;
102  TRY(dif_usbdev_status_get_rx_fifo_depth(&usbdev, &depth));
103 
104  if (depth > 0u) {
105  TRY(dif_usbdev_recv(&usbdev, info, buf));
106 
107  return OK_STATUS();
108  }
109  } while (!ibex_timeout_check(&timeout));
110 
111  return DEADLINE_EXCEEDED();
112 }
113 
114 // Delay for the specified number of microseconds, with user reporting for
115 // appropriate targets.
116 static status_t delay(bool prompt, uint32_t timeout_micros) {
117  if (prompt) {
118  LOG_INFO("Delaying...");
119  }
120  busy_spin_micros(timeout_micros);
121 
122  return OK_STATUS();
123 }
124 
125 OTTF_DEFINE_TEST_CONFIG();
126 
127 bool test_main(void) {
128  // In simulation the DPI model connects VBUS shortly after reset and
129  // prolonged delays when asserting or deasserting pull ups are wasteful.
130  // It spends a little over a bus frame (1ms) in the Bus Reset state.
131  uint32_t timeout_micros = 1500u;
132  uint32_t delay_micros = 1u;
133  bool prompt = false;
134 
136  // FPGA or Silicon platform, where user intervention may be required.
137  timeout_micros = 30 * 1000 * 1000u;
138  // A short delay here permits the activity of the host controller to be
139  // observed (eg. dmesg -w on a Linux host).
140  delay_micros = 2 * 1000 * 1000u;
141  // Report instructions/progress to user, when driven manually.
142  prompt = true;
143  LOG_INFO("Running USBDEV_SETUPRX test");
144  }
145 
146  // Ensure that the VBUS/SENSE signal is routed through to the usbdev.
147  CHECK_DIF_OK(dif_pinmux_init(
149  pinmux_testutils_init(&pinmux);
150  CHECK_DIF_OK(dif_pinmux_input_select(
153 
154  // Initialize and configure the usbdev with pin flipping set appropriately.
155  CHECK_DIF_OK(
156  dif_usbdev_init(mmio_region_from_addr(USBDEV_BASE_ADDR), &usbdev));
157  CHECK_STATUS_OK(config_set(false));
158 
159  // Make some buffers available for received packets.
160  CHECK_DIF_OK(dif_usbdev_fill_available_fifos(&usbdev, &buffer_pool));
161 
162  // Initially, if VBUS is low then prompt the user to establish the connection.
163  if (prompt) {
164  bool vbus;
165  CHECK_DIF_OK(dif_usbdev_status_get_sense(&usbdev, &vbus));
166  if (!vbus) {
167  LOG_INFO("Connect or power up the USB");
168  }
169  }
170 
171  // Check for VBUS present/risen.
172  CHECK_STATUS_OK(vbus_wait(true, timeout_micros));
173 
174  // Delay a little, mostly to slow things on user-driven FPGA for
175  // observation.
176  CHECK_STATUS_OK(delay(prompt, delay_micros));
177 
178  if (prompt) {
179  LOG_INFO("Connecting");
180  }
181 
182  // We need to setup the appropriate configuration for Endpoint Zero,
183  // but in this test we are concerned only with OUT traffic from the host.
184  const uint8_t endpoint = 0u;
185  CHECK_DIF_OK(
187  CHECK_DIF_OK(
189  CHECK_DIF_OK(dif_usbdev_endpoint_set_nak_out_enable(&usbdev, endpoint,
191  dif_usbdev_endpoint_id_t ep_id = {0};
192  ep_id.number = endpoint;
193  ep_id.direction = USBDEV_ENDPOINT_DIR_OUT;
194  CHECK_DIF_OK(dif_usbdev_endpoint_enable(&usbdev, ep_id, kDifToggleEnabled));
195 
196  // Assert the Dx pull up, indicating the presence of a Full Speed device.
197  CHECK_DIF_OK(dif_usbdev_interface_enable(&usbdev, kDifToggleEnabled));
198 
199  // Check the Dx line has risen.
200  CHECK_STATUS_OK(line_wait(true, true, 1000u));
201 
202  if (prompt) {
203  LOG_INFO("Awaiting SETUP");
204  }
205 
206  // Wait until we receive the first SETUP and DATA packets from the host.
208  dif_usbdev_buffer_t buffer;
209  CHECK_STATUS_OK(packet_wait(&info, &buffer, timeout_micros));
210 
211  // Check the properties of the received packet; do not perform a complete
212  // check of the packet contents because different hosts may initiate the
213  // communication using different types of Control Transfer.
214  CHECK(info.is_setup && !info.endpoint && info.length == 8u,
215  "Received packet does not have the expected properties; expecting the "
216  "start of a Control Transfer from the host.");
217 
218  uint8_t packet[USBDEV_MAX_PACKET_SIZE];
219  size_t written;
220  CHECK_DIF_OK(dif_usbdev_buffer_read(&usbdev, &buffer_pool, &buffer, packet,
221  sizeof(packet), &written));
222  CHECK(written == info.length, "Packet data is not of the expected length.");
223  CHECK(packet[0] == 0x80u, "bmRequestType field not as expected");
224 
225  if (prompt) {
226  // Display the packet contents just for additional confirmation.
227  LOG_INFO("Setup stage:");
228 
229  base_hexdump_fmt_t fmt;
230  fmt.bytes_per_word = 1u;
231  fmt.words_per_line = 0x10u;
233  base_hexdump_with(fmt, (char *)packet, written);
234 
235  LOG_INFO("Disconnecting");
236  }
237 
238  // Deassert the pull up, disconnect us from the bus.
239  CHECK_DIF_OK(dif_usbdev_interface_enable(&usbdev, kDifToggleDisabled));
240 
241  // Dx line should drop in response.
242  CHECK_STATUS_OK(line_wait(true, false, 1000u));
243 
244  return true;
245 }