Software APIs
usb_testutils_controlep.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 #include "sw/device/lib/testing/usb_testutils_controlep.h"
6 
10 #include "sw/device/lib/testing/test_framework/check.h"
11 #include "sw/device/lib/testing/usb_testutils.h"
12 
13 #define MODULE_ID MAKE_MODULE_ID('u', 't', 'c')
14 
15 // Device descriptor
16 static const uint8_t kDevDscr[] = {
17  18, // bLength
18  1, // bDescriptorType
19  0x00, // bcdUSB[0]
20  0x02, // bcdUSB[1]
21  0x00, // bDeviceClass (defined at interface level)
22  0x00, // bDeviceSubClass
23  0x00, // bDeviceProtocol
24  64, // bMaxPacketSize0
25 
26  0xd1, // idVendor[0] 0x18d1 Google Inc.
27  0x18, // idVendor[1]
28  0x3a, // idProduct[0] lowRISC generic FS USB
29  0x50, // idProduct[1] (allocated by Google)
30 
31  0, // bcdDevice[0]
32  0x1, // bcdDevice[1]
33  0, // iManufacturer
34  0, // iProduct
35  0, // iSerialNumber
36  1 // bNumConfigurations
37 };
38 
39 // SETUP requests
40 typedef enum usb_setup_req {
41  kUsbSetupReqGetStatus = 0,
42  kUsbSetupReqClearFeature = 1,
43  kUsbSetupReqSetFeature = 3,
44  kUsbSetupReqSetAddress = 5,
45  kUsbSetupReqGetDescriptor = 6,
46  kUsbSetupReqSetDescriptor = 7,
47  kUsbSetupReqGetConfiguration = 8,
48  kUsbSetupReqSetConfiguration = 9,
49  kUsbSetupReqGetInterface = 10,
50  kUsbSetupReqSetInterface = 11,
51  kUsbSetupReqSynchFrame = 12
52 } usb_setup_req_t;
53 
54 // Vendor-specific requests defined by our device/test framework
55 typedef enum vendor_setup_req {
56  kVendorSetupReqTestConfig = 0x7C,
57  kVendorSetupReqTestStatus = 0x7E
58 } vendor_setup_req_t;
59 
60 typedef enum usb_req_type { // bmRequestType
61  kUsbReqTypeRecipientMask = 0x1f,
62  kUsbReqTypeDevice = 0,
63  kUsbReqTypeInterface = 1,
64  kUsbReqTypeEndpoint = 2,
65  kUsbReqTypeOther = 3,
66  kUsbReqTypeTypeMask = 0x60,
67  kUsbReqTypeStandard = 0,
68  kUsbReqTypeClass = 0x20,
69  kUsbReqTypeVendor = 0x40,
70  kUsbReqTypeReserved = 0x60,
71  kUsbReqTypeDirMask = 0x80,
72  kUsbReqTypeDirH2D = 0x00,
73  kUsbReqTypeDirD2H = 0x80,
74 } usb_req_type_t;
75 
76 typedef enum usb_desc_type { // Descriptor type (wValue hi)
77  kUsbDescTypeDevice = 1,
78  kUsbDescTypeConfiguration,
79  kUsbDescTypeString,
80  kUsbDescTypeInterface,
81  kUsbDescTypeEndpoint,
82  kUsbDescTypeDeviceQualifier,
83  kUsbDescTypeOtherSpeedConfiguration,
84  kUsbDescTypeInterfacePower,
85 } usb_desc_type_t;
86 
87 typedef enum usb_feature_req {
88  kUsbFeatureEndpointHalt = 0, // recipient is endpoint
89  kUsbFeatureDeviceRemoteWakeup = 1, // recipient is device
90  kUsbFeatureTestMode = 2, // recipient is device
91  kUsbFeatureBHnpEnable = 3, // recipient is device only if OTG
92  kUsbFeatureAHnpSupport = 4, // recipient is device only if OTG
93  kUsbFeatureAAltHnpSupport = 5 // recipient is device only if OTG
94 } usb_feature_req_t;
95 
96 typedef enum usb_status {
97  kUsbStatusSelfPowered = 1, // Device status request
98  kUsbStatusRemWake = 2, // Device status request
99  kUsbStatusHalted = 1 // Endpoint status request
100 } usb_status_t;
101 
102 static usb_testutils_ctstate_t setup_req(usb_testutils_controlep_ctx_t *ctctx,
103  usb_testutils_ctx_t *ctx,
104  uint8_t bmRequestType,
105  uint8_t bRequest, uint16_t wValue,
106  uint16_t wIndex, uint16_t wLength) {
107  size_t len;
108  uint32_t stat;
109  int zero, type;
110  size_t bytes_written;
111  // Endpoint for SetFeature/ClearFeature/GetStatus requests
112  dif_usbdev_endpoint_id_t endpoint = {
113  .number = (uint8_t)wIndex,
114  .direction = ((bmRequestType & 0x80U) != 0U),
115  };
116  dif_usbdev_buffer_t buffer;
117  CHECK_DIF_OK(dif_usbdev_buffer_request(ctx->dev, ctx->buffer_pool, &buffer));
118  switch (bRequest) {
119  case kUsbSetupReqGetDescriptor:
120  if ((wValue & 0xff00) == 0x100) {
121  // Device descriptor
122  len = sizeof(kDevDscr);
123  if (wLength < len) {
124  len = wLength;
125  }
126  CHECK_DIF_OK(dif_usbdev_buffer_write(ctx->dev, &buffer, kDevDscr, len,
127  &bytes_written));
128  CHECK(bytes_written == len);
129  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
130  return kUsbTestutilsCtWaitIn;
131  } else if ((wValue & 0xff00) == 0x200) {
132  usb_testutils_xfr_flags_t flags = kUsbTestutilsXfrDoubleBuffered;
133 
134  // Configuration descriptor
135  len = ctctx->cfg_dscr_len;
136  if (wLength < len) {
137  len = wLength;
138  } else if (wLength > len) {
139  // Since we're not sending as much as requested, we may need to use
140  // a Zero Length Packet to mark the end of the data stage
141  flags |= kUsbTestutilsXfrEmployZLP;
142  }
143 
144  if (len >= USBDEV_MAX_PACKET_SIZE) {
145  CHECK_DIF_OK(
146  dif_usbdev_buffer_return(ctx->dev, ctx->buffer_pool, &buffer));
147 
148  if (UNWRAP(usb_testutils_transfer_send(ctx, 0U, ctctx->cfg_dscr, len,
149  flags)) == false) {
150  return kUsbTestutilsCtError;
151  }
152  } else {
153  CHECK_DIF_OK(dif_usbdev_buffer_write(
154  ctx->dev, &buffer, ctctx->cfg_dscr, len, &bytes_written));
155  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
156  }
157  return kUsbTestutilsCtWaitIn;
158  }
159  return kUsbTestutilsCtError; // unknown
160 
161  case kUsbSetupReqSetAddress:
162  TRC_S("SA");
163  ctctx->new_dev = (uint8_t)(wValue & 0x7fU);
164  // send zero length packet for status phase
165  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
166  return kUsbTestutilsCtAddrStatIn;
167 
168  case kUsbSetupReqSetConfiguration:
169  TRC_S("SC");
170  // only ever expect this to be 1 since there is one config descriptor
171  ctctx->new_config = (uint8_t)wValue;
172  // send zero length packet for status phase
173  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
174  return kUsbTestutilsCtCfgStatIn;
175 
176  case kUsbSetupReqGetConfiguration:
177  len = sizeof(ctctx->usb_config);
178  if (wLength < len) {
179  len = wLength;
180  }
181  // return the value that was set
182  CHECK_DIF_OK(dif_usbdev_buffer_write(
183  ctx->dev, &buffer, &ctctx->usb_config, len, &bytes_written));
184  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
185  return kUsbTestutilsCtWaitIn;
186 
187  case kUsbSetupReqSetFeature:
188  if (wValue == kUsbFeatureEndpointHalt) {
189  CHECK_DIF_OK(dif_usbdev_endpoint_stall_enable(ctx->dev, endpoint,
191  // send zero length packet for status phase
192  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
193  return kUsbTestutilsCtStatIn;
194  }
195  return kUsbTestutilsCtError; // unknown
196 
197  case kUsbSetupReqClearFeature:
198  if (wValue == kUsbFeatureEndpointHalt) {
199  CHECK_DIF_OK(dif_usbdev_endpoint_stall_enable(ctx->dev, endpoint,
201  // Clearing the Halt feature on an endpoint that is using Data Toggling
202  // also requires us to clear the Data Toggle for that endpoint
203  CHECK_DIF_OK(dif_usbdev_clear_data_toggle(ctx->dev, endpoint.number));
204  // send zero length packet for status phase
205  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
206  return kUsbTestutilsCtStatIn;
207  }
208  // We must return a Request Error (STALL in response to Status stage)
209  return kUsbTestutilsCtError; // unknown
210 
211  case kUsbSetupReqGetStatus:
212  len = 2;
213  type = bmRequestType & kUsbReqTypeRecipientMask;
214  if (type == kUsbReqTypeDevice) {
215  stat = kUsbStatusSelfPowered;
216  } else if (type == kUsbReqTypeEndpoint) {
217  bool halted;
218  CHECK_DIF_OK(
219  dif_usbdev_endpoint_stall_get(ctx->dev, endpoint, &halted));
220  stat = halted ? kUsbStatusHalted : 0;
221  } else {
222  stat = 0;
223  }
224  if (wLength < len) {
225  len = wLength;
226  }
227  // return the value that was set
228  CHECK_DIF_OK(dif_usbdev_buffer_write(ctx->dev, &buffer, (uint8_t *)&stat,
229  len, &bytes_written));
230  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
231  return kUsbTestutilsCtWaitIn;
232 
233  case kUsbSetupReqSetInterface:
234  // Don't support alternate interfaces, so just ignore
235  // send zero length packet for status phase
236  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
237  return kUsbTestutilsCtStatIn;
238 
239  case kUsbSetupReqGetInterface:
240  zero = 0;
241  len = 1;
242  if (wLength < len) {
243  len = wLength;
244  }
245  // Don't support interface, so return zero
246  CHECK_DIF_OK(dif_usbdev_buffer_write(ctx->dev, &buffer, (uint8_t *)&zero,
247  len, &bytes_written));
248  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
249  return kUsbTestutilsCtWaitIn;
250 
251  case kUsbSetupReqSynchFrame:
252  zero = 0;
253  len = 2;
254  if (wLength < len) {
255  len = wLength;
256  }
257  // Don't support synch_frame so return zero
258  CHECK_DIF_OK(dif_usbdev_buffer_write(ctx->dev, &buffer, (uint8_t *)&zero,
259  len, &bytes_written));
260  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
261  return kUsbTestutilsCtWaitIn;
262 
263  default:
264  // We implement a couple of bespoke, vendor-defined Setup requests to
265  // allow the DPI model to access the test configuration (Control Read) and
266  // to report the test status (Control Write)
267  if ((bmRequestType & kUsbReqTypeTypeMask) == kUsbReqTypeVendor &&
268  ctctx->test_dscr) {
269  switch ((vendor_setup_req_t)bRequest) {
270  case kVendorSetupReqTestConfig: {
271  TRC_S("TC");
272  // Test config descriptor
273  len = ctctx->test_dscr_len;
274  if (wLength < len) {
275  len = wLength;
276  }
277  CHECK_DIF_OK(dif_usbdev_buffer_write(
278  ctx->dev, &buffer, ctctx->test_dscr, len, &bytes_written));
279  CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer));
280  return kUsbTestutilsCtWaitIn;
281  } break;
282  case kVendorSetupReqTestStatus: {
283  // TODO - pass the received test status to the OTTF directly?
284  } break;
285  }
286  }
287  return kUsbTestutilsCtError;
288  }
289  return kUsbTestutilsCtError;
290 }
291 
292 static status_t ctrl_tx_done(void *ctctx_v, usb_testutils_xfr_result_t result) {
295  usb_testutils_ctx_t *ctx = ctctx->ctx;
296  TRC_C('A' + ctctx->ctrlstate);
297  switch (ctctx->ctrlstate) {
298  case kUsbTestutilsCtAddrStatIn:
299  // Now the Status was sent on Endpoint Zero, the device can switch to new
300  // Device Address
301  TRY(dif_usbdev_address_set(ctx->dev, ctctx->new_dev));
302  TRC_I(ctctx->new_dev, 8);
303  ctctx->ctrlstate = kUsbTestutilsCtIdle;
304  // We now have a device address on the USB
305  ctctx->device_state = kUsbTestutilsDeviceAddressed;
306  return OK_STATUS();
307  case kUsbTestutilsCtCfgStatIn:
308  // Now the Status was sent on Endpoint Zero, the new configuration has
309  // been (de)selected.
310  ctctx->usb_config = ctctx->new_config;
311  ctctx->ctrlstate = kUsbTestutilsCtIdle;
312  if (ctctx->new_config) {
313  ctctx->device_state = kUsbTestutilsDeviceConfigured;
314  } else {
315  // Device deconfigured
316  ctctx->device_state = kUsbTestutilsDeviceAddressed;
317  }
318  return OK_STATUS();
319  case kUsbTestutilsCtStatIn:
320  ctctx->ctrlstate = kUsbTestutilsCtIdle;
321  return OK_STATUS();
322  case kUsbTestutilsCtWaitIn:
323  ctctx->ctrlstate = kUsbTestutilsCtStatOut;
324  return OK_STATUS();
325 
326  default:
327  break;
328  }
329  TRC_S("USB: unexpected IN ");
330  TRC_I((ctctx->ctrlstate << 24), 32);
331  return OK_STATUS();
332 }
333 
334 static status_t ctrl_rx(void *ctctx_v, dif_usbdev_rx_packet_info_t packet_info,
335  dif_usbdev_buffer_t buffer) {
338  usb_testutils_ctx_t *ctx = ctctx->ctx;
339  TRY(dif_usbdev_endpoint_out_enable(ctx->dev, /*endpoint=*/0,
341 
342  TRC_C('0' + ctctx->ctrlstate);
343  size_t bytes_written;
344  // TODO: Should check for canceled IN transactions due to receiving a SETUP
345  // packet.
346  switch (ctctx->ctrlstate) {
347  case kUsbTestutilsCtIdle:
348  // Waiting to be set up
349  if (packet_info.is_setup && (packet_info.length == 8)) {
350  alignas(uint32_t) uint8_t bp[8];
351  TRY(dif_usbdev_buffer_read(ctx->dev, ctx->buffer_pool, &buffer, bp,
352  sizeof(bp), &bytes_written));
353  uint8_t bmRequestType = bp[0];
354  uint8_t bRequest = bp[1];
355  uint16_t wValue = (uint16_t)((bp[3] << 8) | bp[2]);
356  uint16_t wIndex = (uint16_t)((bp[5] << 8) | bp[4]);
357  uint16_t wLength = (uint16_t)((bp[7] << 8) | bp[6]);
358  TRC_C('0' + bRequest);
359 
360  ctctx->ctrlstate = setup_req(ctctx, ctx, bmRequestType, bRequest,
361  wValue, wIndex, wLength);
362  if (ctctx->ctrlstate != kUsbTestutilsCtError) {
363  return OK_STATUS();
364  }
365 
366  TRC_C(':');
367  for (int i = 0; i < packet_info.length; i++) {
368  TRC_I(bp[i], 8);
369  }
370  }
371  break;
372 
373  case kUsbTestutilsCtStatOut:
374  // Have sent some data, waiting STATUS stage
375  if (!packet_info.is_setup && (packet_info.length == 0)) {
376  TRY(dif_usbdev_buffer_return(ctx->dev, ctx->buffer_pool, &buffer));
377  ctctx->ctrlstate = kUsbTestutilsCtIdle;
378  return OK_STATUS();
379  }
380  // anything else is unexpected
381  break;
382 
383  default:
384  // Error
385  break;
386  }
387  dif_usbdev_endpoint_id_t endpoint = {
388  .number = 0,
389  .direction = USBDEV_ENDPOINT_DIR_IN,
390  };
391  // Enable responding with STALL. Will be cleared by the HW upon next SETUP.
392  TRY(dif_usbdev_endpoint_stall_enable(ctx->dev, endpoint, kDifToggleEnabled));
393  endpoint.direction = USBDEV_ENDPOINT_DIR_OUT;
394  TRY(dif_usbdev_endpoint_stall_enable(ctx->dev, endpoint, kDifToggleEnabled));
395 
396  TRC_S("USB: unCT ");
397  TRC_I((ctctx->ctrlstate << 24) | ((uint32_t)packet_info.is_setup << 16) |
398  packet_info.length,
399  32);
400  if (buffer.type != kDifUsbdevBufferTypeStale) {
401  // Return the unused buffer.
402  TRY(dif_usbdev_buffer_return(ctx->dev, ctx->buffer_pool, &buffer));
403  }
404  ctctx->ctrlstate = kUsbTestutilsCtIdle;
405  return OK_STATUS();
406 }
407 
408 // Callback for the USB link reset
409 static status_t ctrl_reset(void *ctctx_v) {
412  ctctx->ctrlstate = kUsbTestutilsCtIdle;
413  // We have lost any device address that we were assigned; the device has
414  // cleared its own copy of the device address automatically.
415  ctctx->device_state = kUsbTestutilsDeviceDefault;
416  return OK_STATUS();
417 }
418 
419 status_t usb_testutils_controlep_init(usb_testutils_controlep_ctx_t *ctctx,
420  usb_testutils_ctx_t *ctx, uint8_t ep,
421  const uint8_t *cfg_dscr,
422  size_t cfg_dscr_len,
423  const uint8_t *test_dscr,
424  size_t test_dscr_len) {
425  ctctx->ctx = ctx;
426  TRY(usb_testutils_endpoint_setup(
427  ctx, ep, kUsbTransferTypeControl, kUsbTransferTypeControl,
428  kUsbdevOutMessage, ctctx, ctrl_tx_done, ctrl_rx, NULL, ctrl_reset));
429  ctctx->ep = ep;
430  ctctx->ctrlstate = kUsbTestutilsCtIdle;
431  ctctx->cfg_dscr = cfg_dscr;
432  ctctx->cfg_dscr_len = cfg_dscr_len;
433  ctctx->test_dscr = test_dscr;
434  ctctx->test_dscr_len = test_dscr_len;
435  ctctx->device_state = kUsbTestutilsDeviceDefault;
436 
437  // Indicate the device presence, at which point we can expect to start
438  // receiving control transfers from the host
440 
441  return OK_STATUS();
442 }
443 
444 // Proceed only when the device has been configured; this allows host-side
445 // software to establish communication.
446 status_t usb_testutils_controlep_config_wait(
448  // In simulation the DPI (host) is very responsive, and it will take only
449  // a handful of bus frames to set the configuration; importantly we want
450  // regression simulations to terminate sooner rather than later if there
451  // is a gross connectivity failure.
452  uint32_t timeout_usecs = 8 * 1000; // 8ms = 8 x 1ms bus frames
453  switch (kDeviceType) {
454  case kDeviceSimDV:
455  break;
456  case kDeviceSimVerilator: {
457  // The Verilator simulation runs the CPU and the USB DPI model on the same
458  // clock, and the USB bus frame is 1ms (= 48,000 clock cycles), so we
459  // simply want to set the timeout in terms of clock cycles.
460  uint64_t clk_cycles = 48 * timeout_usecs;
461  timeout_usecs =
462  (uint32_t)udiv64_slow(clk_cycles * 1000000, kClockFreqCpuHz, NULL);
463  } break;
464  default:
465  // With an FGPA build the host software will respond more slowly and there
466  // may even be a requirement for user intervention such as cabling.
467  timeout_usecs = 30 * 1000000;
468  break;
469  }
470  ibex_timeout_t timeout = ibex_timeout_init(timeout_usecs);
471  while (ctctx->device_state != kUsbTestutilsDeviceConfigured &&
472  !ibex_timeout_check(&timeout)) {
473  TRY(usb_testutils_poll(ctx));
474  }
475  if (ctctx->device_state != kUsbTestutilsDeviceConfigured) {
476  // Don't wait indefinitely because there may be no usable connection.
477  return UNAVAILABLE();
478  }
479  return OK_STATUS();
480 }