Software APIs
dif_usbdev.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
6
7#include <assert.h>
8
11
12#include "usbdev_regs.h" // Generated.
13
14/**
15 * Definition in the header file (and probably other places) must be updated if
16 * there is a hardware change.
17 */
18static_assert(USBDEV_NUM_ENDPOINTS == USBDEV_PARAM_N_ENDPOINTS,
19 "Mismatch in number of endpoints");
20
21/**
22 * Max packet size is equal to the size of device buffers.
23 */
24#define USBDEV_BUFFER_ENTRY_SIZE_BYTES USBDEV_MAX_PACKET_SIZE
25
26/**
27 * Constants used to indicate that a buffer pool is full or empty.
28 */
29#define BUFFER_POOL_FULL (USBDEV_NUM_BUFFERS - 1)
30#define BUFFER_POOL_EMPTY -1
31
32/**
33 * Hardware information for endpoints.
34 */
35typedef struct endpoint_hw_info {
36 uint32_t config_in_reg_offset;
37 uint8_t bit_index;
38} endpoint_hw_info_t;
39
40/**
41 * Helper macro to define an `endpoint_hw_info_t` entry for endpoint N.
42 *
43 * Note: This uses the bit indices of `USBDEV_IN_SENT` register for the sake
44 * of conciseness because other endpoint registers use the same layout.
45 */
46#define ENDPOINT_HW_INFO_ENTRY(N) \
47 [N] = {.config_in_reg_offset = USBDEV_CONFIGIN_##N##_REG_OFFSET, \
48 .bit_index = USBDEV_IN_SENT_SENT_##N##_BIT}
49
50static const endpoint_hw_info_t kEndpointHwInfos[USBDEV_NUM_ENDPOINTS] = {
51 ENDPOINT_HW_INFO_ENTRY(0), ENDPOINT_HW_INFO_ENTRY(1),
52 ENDPOINT_HW_INFO_ENTRY(2), ENDPOINT_HW_INFO_ENTRY(3),
53 ENDPOINT_HW_INFO_ENTRY(4), ENDPOINT_HW_INFO_ENTRY(5),
54 ENDPOINT_HW_INFO_ENTRY(6), ENDPOINT_HW_INFO_ENTRY(7),
55 ENDPOINT_HW_INFO_ENTRY(8), ENDPOINT_HW_INFO_ENTRY(9),
56 ENDPOINT_HW_INFO_ENTRY(10), ENDPOINT_HW_INFO_ENTRY(11),
57};
58
59#undef ENDPOINT_HW_INFO_ENTRY
60
61/**
62 * Static functions for the free buffer pool.
63 */
64
65/**
66 * Checks if a buffer pool is full.
67 *
68 * A buffer pool is full if it contains `USBDEV_NUM_BUFFERS` buffers.
69 *
70 * @param pool A buffer pool.
71 * @return `true` if the buffer pool if full, `false` otherwise.
72 */
74static bool buffer_pool_is_full(dif_usbdev_buffer_pool_t *pool) {
75 return pool->top == BUFFER_POOL_FULL;
76}
77
78/**
79 * Checks if a buffer pool is empty.
80 *
81 * @param pool A buffer pool.
82 * @return `true` if the buffer pool is empty, `false` otherwise.
83 */
85static bool buffer_pool_is_empty(dif_usbdev_buffer_pool_t *pool) {
86 return pool->top == BUFFER_POOL_EMPTY;
87}
88
89/**
90 * Checks if a buffer id is valid.
91 *
92 * A buffer id is valid if it is less than `USBDEV_NUM_BUFFERS`.
93 *
94 * @param buffer_id A buffer id.
95 * @return `true` if `buffer_id` is valid, `false` otherwise.
96 */
98static bool buffer_pool_is_valid_buffer_id(uint8_t buffer_id) {
99 return buffer_id < USBDEV_NUM_BUFFERS;
100}
101
102/**
103 * Adds a buffer to a buffer pool.
104 *
105 * @param pool A buffer pool.
106 * @param buffer_id A buffer id.
107 * @return `true` if the operation was successful, `false` otherwise.
108 */
110static bool buffer_pool_add(dif_usbdev_buffer_pool_t *pool, uint8_t buffer_id) {
111 if (buffer_pool_is_full(pool) || !buffer_pool_is_valid_buffer_id(buffer_id)) {
112 return false;
113 }
114
115 ++pool->top;
116 pool->buffers[pool->top] = buffer_id;
117
118 return true;
119}
120
121/**
122 * Removes a buffer from a buffer pool.
123 *
124 * @param pool A buffer pool.
125 * @param buffer_id A buffer id.
126 * @return `true` if the operation was successful, `false` otherwise.
127 */
129static bool buffer_pool_remove(dif_usbdev_buffer_pool_t *pool,
130 uint8_t *buffer_id) {
131 if (buffer_pool_is_empty(pool) || buffer_id == NULL) {
132 return false;
133 }
134
135 *buffer_id = pool->buffers[pool->top];
136 --pool->top;
137
138 return true;
139}
140
141/**
142 * Initializes the buffer pool.
143 *
144 * At the end of this operation, the buffer pool contains `USBDEV_NUM_BUFFERS`
145 * buffers.
146 *
147 * @param pool A buffer pool.
148 * @return `true` if the operation was successful, `false` otherwise.
149 */
151static bool buffer_pool_init(dif_usbdev_buffer_pool_t *pool) {
152 // Start with an empty pool
153 pool->top = -1;
154
155 // Add all buffers
156 for (uint8_t i = 0; i < USBDEV_NUM_BUFFERS; ++i) {
157 if (!buffer_pool_add(pool, i)) {
158 return false;
159 }
160 }
161
162 return true;
163}
164
165/**
166 * Utility functions
167 */
168
169/**
170 * Checks if the given value is a valid endpoint number.
171 */
173static bool is_valid_endpoint(uint8_t endpoint_number) {
174 return endpoint_number < USBDEV_NUM_ENDPOINTS;
175}
176
177/**
178 * Enables/disables the functionality controlled by the register at `reg_offset`
179 * for an endpoint.
180 */
182static dif_result_t endpoint_functionality_enable(const dif_usbdev_t *usbdev,
183 uint32_t reg_offset,
184 uint8_t endpoint,
185 dif_toggle_t new_state) {
186 if (usbdev == NULL || !is_valid_endpoint(endpoint) ||
187 !dif_is_valid_toggle(new_state)) {
188 return kDifBadArg;
189 }
190
191 uint32_t reg_val =
192 mmio_region_read32(usbdev->base_addr, (ptrdiff_t)reg_offset);
193 reg_val = bitfield_bit32_write(reg_val, kEndpointHwInfos[endpoint].bit_index,
194 dif_toggle_to_bool(new_state));
195 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)reg_offset, reg_val);
196 return kDifOk;
197}
198
199/**
200 * Returns the address that corresponds to the given buffer and offset
201 * into that buffer.
202 */
204static uint32_t get_buffer_addr(uint8_t buffer_id, size_t offset) {
205 return USBDEV_BUFFER_REG_OFFSET +
206 (buffer_id * USBDEV_BUFFER_ENTRY_SIZE_BYTES) + offset;
207}
208
209/**
210 * USBDEV DIF library functions.
211 */
212
213dif_result_t dif_usbdev_configure(const dif_usbdev_t *usbdev,
214 dif_usbdev_buffer_pool_t *buffer_pool,
215 dif_usbdev_config_t config) {
216 if (usbdev == NULL || buffer_pool == NULL) {
217 return kDifBadArg;
218 }
219
220 // Configure the free buffer pool.
221 if (!buffer_pool_init(buffer_pool)) {
222 return kDifError;
223 }
224
225 // Check enum fields.
226 if (!dif_is_valid_toggle(config.have_differential_receiver) ||
227 !dif_is_valid_toggle(config.use_tx_d_se0) ||
228 !dif_is_valid_toggle(config.single_bit_eop) ||
229 !dif_is_valid_toggle(config.pin_flip) ||
230 !dif_is_valid_toggle(config.clock_sync_signals)) {
231 return kDifBadArg;
232 }
233
234 // Determine the value of the PHY_CONFIG register.
235 uint32_t phy_config_val = 0;
236 phy_config_val = bitfield_bit32_write(
237 phy_config_val, USBDEV_PHY_CONFIG_USE_DIFF_RCVR_BIT,
238 dif_toggle_to_bool(config.have_differential_receiver));
239 phy_config_val =
240 bitfield_bit32_write(phy_config_val, USBDEV_PHY_CONFIG_TX_USE_D_SE0_BIT,
241 dif_toggle_to_bool(config.use_tx_d_se0));
242 phy_config_val =
243 bitfield_bit32_write(phy_config_val, USBDEV_PHY_CONFIG_EOP_SINGLE_BIT_BIT,
244 dif_toggle_to_bool(config.single_bit_eop));
245 phy_config_val =
246 bitfield_bit32_write(phy_config_val, USBDEV_PHY_CONFIG_PINFLIP_BIT,
247 dif_toggle_to_bool(config.pin_flip));
248 phy_config_val = bitfield_bit32_write(
249 phy_config_val, USBDEV_PHY_CONFIG_USB_REF_DISABLE_BIT,
250 !dif_toggle_to_bool(config.clock_sync_signals));
251
252 // Write configuration to PHY_CONFIG register
253 mmio_region_write32(usbdev->base_addr, USBDEV_PHY_CONFIG_REG_OFFSET,
254 phy_config_val);
255
256 return kDifOk;
257}
258
259dif_result_t dif_usbdev_fill_available_fifos(
260 const dif_usbdev_t *usbdev, dif_usbdev_buffer_pool_t *buffer_pool) {
261 if (usbdev == NULL || buffer_pool == NULL) {
262 return kDifBadArg;
263 }
264
265 // Remove buffers from the pool and write as many as possible into the FIFOs
266 while (!buffer_pool_is_empty(buffer_pool)) {
267 uint32_t status =
268 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
269 // Prioritize available SETUP buffers
270 uint32_t av_setup_depth =
271 bitfield_field32_read(status, USBDEV_USBSTAT_AV_SETUP_DEPTH_FIELD);
272 if (av_setup_depth >= 2) {
273 // Available SETUP Buffer FIFO is okay, what about the OUT buffers?
274 bool av_out_full =
275 bitfield_bit32_read(status, USBDEV_USBSTAT_AV_OUT_FULL_BIT);
276 if (av_out_full) {
277 break;
278 }
279 }
280 uint8_t buffer_id;
281 if (!buffer_pool_remove(buffer_pool, &buffer_id)) {
282 return kDifError;
283 }
284 if (av_setup_depth >= 2) {
285 // Supply Available OUT Buffer
286 uint32_t reg_val =
287 bitfield_field32_write(0, USBDEV_AVOUTBUFFER_BUFFER_FIELD, buffer_id);
288 mmio_region_write32(usbdev->base_addr, USBDEV_AVOUTBUFFER_REG_OFFSET,
289 reg_val);
290 } else {
291 // Supply Available SETUP Buffer
292 uint32_t reg_val = bitfield_field32_write(
293 0, USBDEV_AVSETUPBUFFER_BUFFER_FIELD, buffer_id);
294 mmio_region_write32(usbdev->base_addr, USBDEV_AVSETUPBUFFER_REG_OFFSET,
295 reg_val);
296 }
297 }
298
299 return kDifOk;
300}
301
302dif_result_t dif_usbdev_endpoint_setup_enable(const dif_usbdev_t *usbdev,
303 uint8_t endpoint,
304 dif_toggle_t new_state) {
305 return endpoint_functionality_enable(usbdev, USBDEV_RXENABLE_SETUP_REG_OFFSET,
306 endpoint, new_state);
307}
308
309dif_result_t dif_usbdev_endpoint_out_enable(const dif_usbdev_t *usbdev,
310 uint8_t endpoint,
311 dif_toggle_t new_state) {
312 if (usbdev == NULL || !is_valid_endpoint(endpoint) ||
313 !dif_is_valid_toggle(new_state)) {
314 return kDifBadArg;
315 }
316
317 // For compatibility with earlier hardware, we must read back the state of the
318 // other OUT enables because they will _all_ be updated by any write.
319 uint32_t reg_val =
320 mmio_region_read32(usbdev->base_addr, USBDEV_RXENABLE_OUT_REG_OFFSET);
321
322 reg_val = bitfield_bit32_write(reg_val, kEndpointHwInfos[endpoint].bit_index,
323 dif_toggle_to_bool(new_state));
324
325 // More recent hardware supports conditional updating of the OUT enables.
326 //
327 // Update only the specified endpoint by setting 'preserve' for all other OUT
328 // endpoints. This avoids a race between firmware and the USB device in the
329 // event of `set_nak_out` functionality being used.
330 bitfield_field32_t preserve_field = {
331 .mask = USBDEV_RXENABLE_OUT_PRESERVE_MASK,
332 .index = USBDEV_RXENABLE_OUT_PRESERVE_OFFSET};
333
334 uint32_t preserved_endpoints = USBDEV_RXENABLE_OUT_PRESERVE_MASK &
335 ~(1u << kEndpointHwInfos[endpoint].bit_index);
336
337 reg_val =
338 bitfield_field32_write(reg_val, preserve_field, preserved_endpoints);
339 mmio_region_write32(usbdev->base_addr, USBDEV_RXENABLE_OUT_REG_OFFSET,
340 reg_val);
341 return kDifOk;
342}
343
344dif_result_t dif_usbdev_endpoint_set_nak_out_enable(const dif_usbdev_t *usbdev,
345 uint8_t endpoint,
346 dif_toggle_t new_state) {
347 return endpoint_functionality_enable(usbdev, USBDEV_SET_NAK_OUT_REG_OFFSET,
348 endpoint, new_state);
349}
350
351dif_result_t dif_usbdev_endpoint_stall_enable(const dif_usbdev_t *usbdev,
352 dif_usbdev_endpoint_id_t endpoint,
353 dif_toggle_t new_state) {
354 if (endpoint.direction == USBDEV_ENDPOINT_DIR_IN) {
355 return endpoint_functionality_enable(usbdev, USBDEV_IN_STALL_REG_OFFSET,
356 endpoint.number, new_state);
357 } else {
358 return endpoint_functionality_enable(usbdev, USBDEV_OUT_STALL_REG_OFFSET,
359 endpoint.number, new_state);
360 }
361}
362
363dif_result_t dif_usbdev_endpoint_stall_get(const dif_usbdev_t *usbdev,
364 dif_usbdev_endpoint_id_t endpoint,
365 bool *state) {
366 if (usbdev == NULL || state == NULL || !is_valid_endpoint(endpoint.number)) {
367 return kDifBadArg;
368 }
369
370 ptrdiff_t reg_offset = endpoint.direction == USBDEV_ENDPOINT_DIR_IN
371 ? USBDEV_IN_STALL_REG_OFFSET
372 : USBDEV_OUT_STALL_REG_OFFSET;
373 uint32_t reg_val = mmio_region_read32(usbdev->base_addr, reg_offset);
374 *state =
375 bitfield_bit32_read(reg_val, kEndpointHwInfos[endpoint.number].bit_index);
376
377 return kDifOk;
378}
379
380dif_result_t dif_usbdev_endpoint_iso_enable(const dif_usbdev_t *usbdev,
381 dif_usbdev_endpoint_id_t endpoint,
382 dif_toggle_t new_state) {
383 if (endpoint.direction == USBDEV_ENDPOINT_DIR_IN) {
384 return endpoint_functionality_enable(usbdev, USBDEV_IN_ISO_REG_OFFSET,
385 endpoint.number, new_state);
386 } else {
387 return endpoint_functionality_enable(usbdev, USBDEV_OUT_ISO_REG_OFFSET,
388 endpoint.number, new_state);
389 }
390}
391
392dif_result_t dif_usbdev_endpoint_enable(const dif_usbdev_t *usbdev,
393 dif_usbdev_endpoint_id_t endpoint,
394 dif_toggle_t new_state) {
395 if (endpoint.direction == USBDEV_ENDPOINT_DIR_IN) {
396 return endpoint_functionality_enable(usbdev, USBDEV_EP_IN_ENABLE_REG_OFFSET,
397 endpoint.number, new_state);
398 } else {
399 return endpoint_functionality_enable(
400 usbdev, USBDEV_EP_OUT_ENABLE_REG_OFFSET, endpoint.number, new_state);
401 }
402 return kDifOk;
403}
404
405dif_result_t dif_usbdev_interface_enable(const dif_usbdev_t *usbdev,
406 dif_toggle_t new_state) {
407 if (usbdev == NULL || !dif_is_valid_toggle(new_state)) {
408 return kDifBadArg;
409 }
410
411 uint32_t reg_val =
412 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
413 reg_val = bitfield_bit32_write(reg_val, USBDEV_USBCTRL_ENABLE_BIT,
414 dif_toggle_to_bool(new_state));
415 mmio_region_write32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET, reg_val);
416
417 return kDifOk;
418}
419
420dif_result_t dif_usbdev_recv(const dif_usbdev_t *usbdev,
422 dif_usbdev_buffer_t *buffer) {
423 if (usbdev == NULL || info == NULL || buffer == NULL) {
424 return kDifBadArg;
425 }
426
427 // Check if the RX FIFO is empty
428 uint32_t fifo_status =
429 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
430 if (bitfield_bit32_read(fifo_status, USBDEV_USBSTAT_RX_EMPTY_BIT)) {
431 return kDifUnavailable;
432 }
433
434 // Read fifo entry
435 const uint32_t fifo_entry =
436 mmio_region_read32(usbdev->base_addr, USBDEV_RXFIFO_REG_OFFSET);
437 // Init packet info
439 .endpoint =
440 (uint8_t)bitfield_field32_read(fifo_entry, USBDEV_RXFIFO_EP_FIELD),
441 .is_setup = bitfield_bit32_read(fifo_entry, USBDEV_RXFIFO_SETUP_BIT),
442 .length =
443 (uint8_t)bitfield_field32_read(fifo_entry, USBDEV_RXFIFO_SIZE_FIELD),
444 };
445 // Init buffer struct
446 *buffer = (dif_usbdev_buffer_t){
447 .id = (uint8_t)bitfield_field32_read(fifo_entry,
448 USBDEV_RXFIFO_BUFFER_FIELD),
449 .offset = 0,
450 .remaining_bytes = info->length,
452 };
453
454 return kDifOk;
455}
456
457dif_result_t dif_usbdev_buffer_request(const dif_usbdev_t *usbdev,
458 dif_usbdev_buffer_pool_t *buffer_pool,
459 dif_usbdev_buffer_t *buffer) {
460 if (usbdev == NULL || buffer_pool == NULL || buffer == NULL) {
461 return kDifBadArg;
462 }
463
464 if (buffer_pool_is_empty(buffer_pool)) {
465 return kDifUnavailable;
466 }
467
468 uint8_t buffer_id;
469 if (!buffer_pool_remove(buffer_pool, &buffer_id)) {
470 return kDifError;
471 }
472
473 *buffer = (dif_usbdev_buffer_t){
474 .id = buffer_id,
475 .offset = 0,
476 .remaining_bytes = USBDEV_BUFFER_ENTRY_SIZE_BYTES,
478 };
479
480 return kDifOk;
481}
482
483dif_result_t dif_usbdev_buffer_return(const dif_usbdev_t *usbdev,
484 dif_usbdev_buffer_pool_t *buffer_pool,
485 dif_usbdev_buffer_t *buffer) {
486 if (usbdev == NULL || buffer_pool == NULL || buffer == NULL) {
487 return kDifBadArg;
488 }
489
490 switch (buffer->type) {
493 // Return the buffer to the free buffer pool
494 if (!buffer_pool_add(buffer_pool, buffer->id)) {
495 return kDifError;
496 }
497 // Mark the buffer as stale
499 return kDifOk;
500 default:
501 return kDifBadArg;
502 }
503}
504
505dif_result_t dif_usbdev_buffer_read(const dif_usbdev_t *usbdev,
506 dif_usbdev_buffer_pool_t *buffer_pool,
507 dif_usbdev_buffer_t *buffer, uint8_t *dst,
508 size_t dst_len, size_t *bytes_written) {
509 if (usbdev == NULL || buffer_pool == NULL || buffer == NULL ||
510 buffer->type != kDifUsbdevBufferTypeRead || dst == NULL) {
511 return kDifBadArg;
512 }
513
514 // bytes_to_copy is the minimum of remaining_bytes and dst_len
515 size_t bytes_to_copy = buffer->remaining_bytes;
516 if (bytes_to_copy > dst_len) {
517 bytes_to_copy = dst_len;
518 }
519 // Copy from buffer to dst
520 const uint32_t buffer_addr = get_buffer_addr(buffer->id, buffer->offset);
521 mmio_region_memcpy_from_mmio32(usbdev->base_addr, buffer_addr, dst,
522 bytes_to_copy);
523 // Update buffer state
524 buffer->offset += bytes_to_copy;
525 buffer->remaining_bytes -= bytes_to_copy;
526
527 if (bytes_written != NULL) {
528 *bytes_written = bytes_to_copy;
529 }
530
531 // Check if there are any remaining bytes
532 if (buffer->remaining_bytes > 0) {
533 return kDifOk;
534 }
535
536 // Return the buffer to the free buffer pool
537 if (!buffer_pool_add(buffer_pool, buffer->id)) {
538 return kDifError;
539 }
540
541 // Mark the buffer as stale
543 return kDifOk;
544}
545
546dif_result_t dif_usbdev_buffer_write(const dif_usbdev_t *usbdev,
547 dif_usbdev_buffer_t *buffer,
548 const uint8_t *src, size_t src_len,
549 size_t *bytes_written) {
550 if (usbdev == NULL || buffer == NULL ||
551 buffer->type != kDifUsbdevBufferTypeWrite || src == NULL) {
552 return kDifBadArg;
553 }
554
555 // bytes_to_copy is the minimum of remaining_bytes and src_len.
556 size_t bytes_to_copy = buffer->remaining_bytes;
557 if (bytes_to_copy > src_len) {
558 bytes_to_copy = src_len;
559 }
560
561 // Write bytes to the buffer
562 uint32_t buffer_addr = get_buffer_addr(buffer->id, buffer->offset);
563 mmio_region_memcpy_to_mmio32(usbdev->base_addr, buffer_addr, src,
564 bytes_to_copy);
565
566 buffer->offset += bytes_to_copy;
567 buffer->remaining_bytes -= bytes_to_copy;
568
569 if (bytes_written) {
570 *bytes_written = bytes_to_copy;
571 }
572
573 if (buffer->remaining_bytes == 0 && bytes_to_copy < src_len) {
574 return kDifError;
575 }
576
577 return kDifOk;
578}
579
580dif_result_t dif_usbdev_send(const dif_usbdev_t *usbdev, uint8_t endpoint,
581 dif_usbdev_buffer_t *buffer) {
582 if (usbdev == NULL || !is_valid_endpoint(endpoint) || buffer == NULL ||
583 buffer->type != kDifUsbdevBufferTypeWrite) {
584 return kDifBadArg;
585 }
586
587 // Get the configin register offset of the endpoint.
588 const uint32_t config_in_reg_offset =
589 kEndpointHwInfos[endpoint].config_in_reg_offset;
590
591 // Configure USBDEV_CONFIGINX register.
592 // Note: Using mask and offset values for the USBDEV_CONFIGIN0 register
593 // for all endpoints because all USBDEV_CONFIGINX registers have the same
594 // layout.
595 uint32_t config_in_val = 0;
596 config_in_val = bitfield_field32_write(
597 config_in_val, USBDEV_CONFIGIN_0_BUFFER_0_FIELD, buffer->id);
598 config_in_val = bitfield_field32_write(
599 config_in_val, USBDEV_CONFIGIN_0_SIZE_0_FIELD, buffer->offset);
600 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset,
601 config_in_val);
602
603 // Mark the packet as ready for transmission
604 config_in_val =
605 bitfield_bit32_write(config_in_val, USBDEV_CONFIGIN_0_RDY_0_BIT, true);
606 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset,
607 config_in_val);
608
609 // Mark the buffer as stale. It will be returned to the free buffer pool
610 // in dif_usbdev_get_tx_status once transmission is complete.
612
613 return kDifOk;
614}
615
616dif_result_t dif_usbdev_get_tx_sent(const dif_usbdev_t *usbdev,
617 uint16_t *sent) {
618 if (usbdev == NULL || sent == NULL) {
619 return kDifBadArg;
620 }
621 *sent = (uint16_t)mmio_region_read32(usbdev->base_addr,
622 USBDEV_IN_SENT_REG_OFFSET);
623 return kDifOk;
624}
625
626dif_result_t dif_usbdev_clear_tx_status(const dif_usbdev_t *usbdev,
627 dif_usbdev_buffer_pool_t *buffer_pool,
628 uint8_t endpoint) {
629 if (usbdev == NULL || buffer_pool == NULL || !is_valid_endpoint(endpoint)) {
630 return kDifBadArg;
631 }
632 // Get the configin register offset and bit index of the endpoint.
633 uint32_t config_in_reg_offset =
634 kEndpointHwInfos[endpoint].config_in_reg_offset;
635 uint32_t config_in_reg_val =
636 mmio_region_read32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset);
637 uint8_t buffer = (uint8_t)bitfield_field32_read(
638 config_in_reg_val, USBDEV_CONFIGIN_0_BUFFER_0_FIELD);
639
640 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset,
641 1u << USBDEV_CONFIGIN_0_PEND_0_BIT);
642 // Clear IN_SENT bit (rw1c).
643 mmio_region_write32(usbdev->base_addr, USBDEV_IN_SENT_REG_OFFSET,
644 1u << endpoint);
645 // Return the buffer back to the free buffer pool.
646 if (!buffer_pool_add(buffer_pool, buffer)) {
647 return kDifError;
648 }
649 return kDifOk;
650}
651
652dif_result_t dif_usbdev_get_tx_status(const dif_usbdev_t *usbdev,
653 uint8_t endpoint,
655 if (usbdev == NULL || status == NULL || !is_valid_endpoint(endpoint)) {
656 return kDifBadArg;
657 }
658
659 // Get the configin register offset and bit index of the endpoint.
660 uint32_t config_in_reg_offset =
661 kEndpointHwInfos[endpoint].config_in_reg_offset;
662 uint8_t endpoint_bit_index = kEndpointHwInfos[endpoint].bit_index;
663
664 // Read the configin register.
665 uint32_t config_in_val =
666 mmio_region_read32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset);
667
668 // Check the status of the packet.
669 if (bitfield_bit32_read(config_in_val, USBDEV_CONFIGIN_0_RDY_0_BIT)) {
670 // Packet is marked as ready to be sent and pending transmission.
672 } else if (bitfield_bit32_read(mmio_region_read32(usbdev->base_addr,
673 USBDEV_IN_SENT_REG_OFFSET),
674 endpoint_bit_index)) {
675 // Packet was sent successfully.
677 } else if (bitfield_bit32_read(config_in_val, USBDEV_CONFIGIN_0_PEND_0_BIT)) {
678 // Canceled due to an IN SETUP packet or link reset.
680 } else {
681 // No packet has been queued for this endpoint.
683 }
684
685 return kDifOk;
686}
687
688dif_result_t dif_usbdev_address_set(const dif_usbdev_t *usbdev, uint8_t addr) {
689 if (usbdev == NULL) {
690 return kDifBadArg;
691 }
692
693 uint32_t reg_val =
694 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
695 reg_val = bitfield_field32_write(reg_val, USBDEV_USBCTRL_DEVICE_ADDRESS_FIELD,
696 addr);
697 mmio_region_write32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET, reg_val);
698
699 return kDifOk;
700}
701
702dif_result_t dif_usbdev_address_get(const dif_usbdev_t *usbdev, uint8_t *addr) {
703 if (usbdev == NULL || addr == NULL) {
704 return kDifBadArg;
705 }
706
707 uint32_t reg_val =
708 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
709 // Note: Size of address is 7 bits.
710 *addr = (uint8_t)bitfield_field32_read(reg_val,
711 USBDEV_USBCTRL_DEVICE_ADDRESS_FIELD);
712
713 return kDifOk;
714}
715
716dif_result_t dif_usbdev_data_toggle_out_read(const dif_usbdev_t *usbdev,
717 uint16_t *toggles) {
718 if (usbdev == NULL || toggles == NULL) {
719 return kDifBadArg;
720 }
721
722 uint32_t reg_val =
723 mmio_region_read32(usbdev->base_addr, USBDEV_OUT_DATA_TOGGLE_REG_OFFSET);
724 // Note: only 12 OUT endpoints defined.
725 *toggles = (uint16_t)reg_val;
726
727 return kDifOk;
728}
729
730dif_result_t dif_usbdev_data_toggle_in_read(const dif_usbdev_t *usbdev,
731 uint16_t *toggles) {
732 if (usbdev == NULL || toggles == NULL) {
733 return kDifBadArg;
734 }
735
736 uint32_t reg_val =
737 mmio_region_read32(usbdev->base_addr, USBDEV_IN_DATA_TOGGLE_REG_OFFSET);
738 // Note: only 12 OUT endpoints defined.
739 *toggles = (uint16_t)reg_val;
740
741 return kDifOk;
742}
743
744dif_result_t dif_usbdev_data_toggle_out_write(const dif_usbdev_t *usbdev,
745 uint16_t mask, uint16_t state) {
746 if (usbdev == NULL) {
747 return kDifBadArg;
748 }
749
750 // Note: only 12 OUT endpoints defined.
751 mmio_region_write32(usbdev->base_addr, USBDEV_OUT_DATA_TOGGLE_REG_OFFSET,
752 ((uint32_t)mask << 16) | state);
753
754 return kDifOk;
755}
756
757dif_result_t dif_usbdev_data_toggle_in_write(const dif_usbdev_t *usbdev,
758 uint16_t mask, uint16_t state) {
759 if (usbdev == NULL) {
760 return kDifBadArg;
761 }
762
763 // Note: only 12 OUT endpoints defined.
764 mmio_region_write32(usbdev->base_addr, USBDEV_IN_DATA_TOGGLE_REG_OFFSET,
765 ((uint32_t)mask << 16) | state);
766
767 return kDifOk;
768}
769
770dif_result_t dif_usbdev_clear_data_toggle(const dif_usbdev_t *usbdev,
771 uint8_t endpoint) {
772 if (usbdev == NULL) {
773 return kDifBadArg;
774 }
775
776 uint32_t reg_val = (uint32_t)1u << (endpoint + 16u);
777 mmio_region_write32(usbdev->base_addr, USBDEV_OUT_DATA_TOGGLE_REG_OFFSET,
778 reg_val);
779 mmio_region_write32(usbdev->base_addr, USBDEV_IN_DATA_TOGGLE_REG_OFFSET,
780 reg_val);
781
782 return kDifOk;
783}
784
785dif_result_t dif_usbdev_status_get_frame(const dif_usbdev_t *usbdev,
786 uint16_t *frame_index) {
787 if (usbdev == NULL || frame_index == NULL) {
788 return kDifBadArg;
789 }
790
791 uint32_t reg_val =
792 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
793 // Note: size of frame index is 11 bits.
794 *frame_index =
795 (uint8_t)bitfield_field32_read(reg_val, USBDEV_USBSTAT_FRAME_FIELD);
796
797 return kDifOk;
798}
799
800dif_result_t dif_usbdev_status_get_host_lost(const dif_usbdev_t *usbdev,
801 bool *host_lost) {
802 if (usbdev == NULL || host_lost == NULL) {
803 return kDifBadArg;
804 }
805
806 *host_lost =
807 mmio_region_get_bit32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET,
808 USBDEV_USBSTAT_HOST_LOST_BIT);
809
810 return kDifOk;
811}
812
813dif_result_t dif_usbdev_status_get_link_state(
814 const dif_usbdev_t *usbdev, dif_usbdev_link_state_t *link_state) {
815 if (usbdev == NULL || link_state == NULL) {
816 return kDifBadArg;
817 }
818
819 uint32_t val =
820 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
821 val = bitfield_field32_read(val, USBDEV_USBSTAT_LINK_STATE_FIELD);
822
823 switch (val) {
824 case USBDEV_USBSTAT_LINK_STATE_VALUE_DISCONNECTED:
825 *link_state = kDifUsbdevLinkStateDisconnected;
826 break;
827 case USBDEV_USBSTAT_LINK_STATE_VALUE_POWERED:
828 *link_state = kDifUsbdevLinkStatePowered;
829 break;
830 case USBDEV_USBSTAT_LINK_STATE_VALUE_POWERED_SUSPENDED:
831 *link_state = kDifUsbdevLinkStatePoweredSuspended;
832 break;
833 case USBDEV_USBSTAT_LINK_STATE_VALUE_ACTIVE:
834 *link_state = kDifUsbdevLinkStateActive;
835 break;
836 case USBDEV_USBSTAT_LINK_STATE_VALUE_SUSPENDED:
837 *link_state = kDifUsbdevLinkStateSuspended;
838 break;
839 case USBDEV_USBSTAT_LINK_STATE_VALUE_ACTIVE_NOSOF:
840 *link_state = kDifUsbdevLinkStateActiveNoSof;
841 break;
842 case USBDEV_USBSTAT_LINK_STATE_VALUE_RESUMING:
843 *link_state = kDifUsbdevLinkStateResuming;
844 break;
845 default:
846 return kDifError;
847 }
848
849 return kDifOk;
850}
851
852dif_result_t dif_usbdev_status_get_sense(const dif_usbdev_t *usbdev,
853 bool *sense) {
854 if (usbdev == NULL || sense == NULL) {
855 return kDifBadArg;
856 }
857
858 *sense = mmio_region_get_bit32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET,
859 USBDEV_USBSTAT_SENSE_BIT);
860
861 return kDifOk;
862}
863
864dif_result_t dif_usbdev_status_get_available_fifo_depths(
865 const dif_usbdev_t *usbdev, uint8_t *setup_depth, uint8_t *out_depth) {
866 if (usbdev == NULL || setup_depth == NULL || out_depth == NULL) {
867 return kDifBadArg;
868 }
869
870 uint32_t reg_val =
871 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
872 // Note: Size of Available SETUP FIFO depth is 3 bits.
873 *setup_depth = (uint8_t)bitfield_field32_read(
874 reg_val, USBDEV_USBSTAT_AV_SETUP_DEPTH_FIELD);
875 // Note: Size of Available OUT FIFO depth is 4 bits.
876 *out_depth = (uint8_t)bitfield_field32_read(
877 reg_val, USBDEV_USBSTAT_AV_OUT_DEPTH_FIELD);
878
879 return kDifOk;
880}
881
882dif_result_t dif_usbdev_status_get_available_fifo_full(
883 const dif_usbdev_t *usbdev, bool *setup_is_full, bool *out_is_full) {
884 if (usbdev == NULL || setup_is_full == NULL || out_is_full == NULL) {
885 return kDifBadArg;
886 }
887
888 uint32_t reg_val =
889 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
890 *setup_is_full =
891 bitfield_bit32_read(reg_val, USBDEV_USBSTAT_AV_SETUP_FULL_BIT);
892 *out_is_full = bitfield_bit32_read(reg_val, USBDEV_USBSTAT_AV_OUT_FULL_BIT);
893
894 return kDifOk;
895}
896
897dif_result_t dif_usbdev_status_get_rx_fifo_depth(const dif_usbdev_t *usbdev,
898 uint8_t *depth) {
899 if (usbdev == NULL || depth == NULL) {
900 return kDifBadArg;
901 }
902
903 uint32_t reg_val =
904 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
905 // Note: Size of RX FIFO depth is 4 bits.
906 *depth =
907 (uint8_t)bitfield_field32_read(reg_val, USBDEV_USBSTAT_RX_DEPTH_FIELD);
908
909 return kDifOk;
910}
911
912dif_result_t dif_usbdev_status_get_rx_fifo_empty(const dif_usbdev_t *usbdev,
913 bool *is_empty) {
914 if (usbdev == NULL || is_empty == NULL) {
915 return kDifBadArg;
916 }
917
918 uint32_t reg_val =
919 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
920 *is_empty = bitfield_bit32_read(reg_val, USBDEV_USBSTAT_RX_EMPTY_BIT);
921
922 return kDifOk;
923}
924
925dif_result_t dif_usbdev_set_osc_test_mode(const dif_usbdev_t *usbdev,
926 dif_toggle_t enable) {
927 if (usbdev == NULL || !dif_is_valid_toggle(enable)) {
928 return kDifBadArg;
929 }
930 bool set_tx_osc_mode = dif_toggle_to_bool(enable);
931 uint32_t reg_val =
932 mmio_region_read32(usbdev->base_addr, USBDEV_PHY_CONFIG_REG_OFFSET);
933 reg_val = bitfield_bit32_write(
934 reg_val, USBDEV_PHY_CONFIG_TX_OSC_TEST_MODE_BIT, set_tx_osc_mode);
935 mmio_region_write32(usbdev->base_addr, USBDEV_PHY_CONFIG_REG_OFFSET, reg_val);
936 return kDifOk;
937}
938
939dif_result_t dif_usbdev_set_wake_enable(const dif_usbdev_t *usbdev,
940 dif_toggle_t enable) {
941 if (usbdev == NULL || !dif_is_valid_toggle(enable)) {
942 return kDifBadArg;
943 }
944 uint32_t reg_val;
945 if (dif_toggle_to_bool(enable)) {
946 reg_val =
947 bitfield_bit32_write(0, USBDEV_WAKE_CONTROL_SUSPEND_REQ_BIT, true);
948 } else {
949 reg_val = bitfield_bit32_write(0, USBDEV_WAKE_CONTROL_WAKE_ACK_BIT, true);
950 }
951 mmio_region_write32(usbdev->base_addr, USBDEV_WAKE_CONTROL_REG_OFFSET,
952 reg_val);
953 return kDifOk;
954}
955
956dif_result_t dif_usbdev_get_wake_status(const dif_usbdev_t *usbdev,
957 dif_usbdev_wake_status_t *status) {
958 if (usbdev == NULL || status == NULL) {
959 return kDifBadArg;
960 }
961 uint32_t reg_val =
962 mmio_region_read32(usbdev->base_addr, USBDEV_WAKE_EVENTS_REG_OFFSET);
963 status->active =
964 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_MODULE_ACTIVE_BIT);
965 status->disconnected =
966 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_DISCONNECTED_BIT);
967 status->bus_reset =
968 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_BUS_RESET_BIT);
969 status->bus_not_idle =
970 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_BUS_NOT_IDLE_BIT);
971 return kDifOk;
972}
973
974dif_result_t dif_usbdev_resume_link_to_active(const dif_usbdev_t *usbdev) {
975 if (usbdev == NULL) {
976 return kDifBadArg;
977 }
978 uint32_t reg_val =
979 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
980 reg_val = bitfield_bit32_write(reg_val, USBDEV_USBCTRL_RESUME_LINK_ACTIVE_BIT,
981 true);
982 mmio_region_write32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET, reg_val);
983 return kDifOk;
984}
985
986dif_result_t dif_usbdev_get_phy_pins_status(
987 const dif_usbdev_t *usbdev, dif_usbdev_phy_pins_sense_t *status) {
988 if (usbdev == NULL || status == NULL) {
989 return kDifBadArg;
990 }
991 uint32_t reg_val =
992 mmio_region_read32(usbdev->base_addr, USBDEV_PHY_PINS_SENSE_REG_OFFSET);
993 status->rx_dp =
994 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_RX_DP_I_BIT);
995 status->rx_dn =
996 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_RX_DN_I_BIT);
997 status->rx_d = bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_RX_D_I_BIT);
998 status->tx_dp =
999 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_DP_O_BIT);
1000 status->tx_dn =
1001 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_DN_O_BIT);
1002 status->tx_d = bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_D_O_BIT);
1003 status->tx_se0 =
1004 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_SE0_O_BIT);
1005 status->output_enable =
1006 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_OE_O_BIT);
1007 status->vbus_sense =
1008 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_PWR_SENSE_BIT);
1009 return kDifOk;
1010}
1011
1012dif_result_t dif_usbdev_set_phy_pins_state(
1013 const dif_usbdev_t *usbdev, dif_toggle_t override_enable,
1014 dif_usbdev_phy_pins_drive_t overrides) {
1015 if (usbdev == NULL || !dif_is_valid_toggle(override_enable)) {
1016 return kDifBadArg;
1017 }
1018 bool drive_en = dif_toggle_to_bool(override_enable);
1019 uint32_t reg_val =
1020 bitfield_bit32_write(0, USBDEV_PHY_PINS_DRIVE_EN_BIT, drive_en);
1021 if (drive_en) {
1022 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DP_O_BIT,
1023 overrides.dp);
1024 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DN_O_BIT,
1025 overrides.dn);
1026 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_D_O_BIT,
1027 overrides.data);
1028 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_SE0_O_BIT,
1029 overrides.se0);
1030 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_OE_O_BIT,
1031 overrides.output_enable);
1032 reg_val =
1033 bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_RX_ENABLE_O_BIT,
1034 overrides.diff_receiver_enable);
1035 reg_val =
1036 bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DP_PULLUP_EN_O_BIT,
1037 overrides.dp_pullup_en);
1038 reg_val =
1039 bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DN_PULLUP_EN_O_BIT,
1040 overrides.dn_pullup_en);
1041 }
1042 mmio_region_write32(usbdev->base_addr, USBDEV_PHY_PINS_DRIVE_REG_OFFSET,
1043 reg_val);
1044 return kDifOk;
1045}
1046
1047dif_result_t dif_usbdev_buffer_raw_write(const dif_usbdev_t *usbdev, uint8_t id,
1048 const uint8_t *src, size_t src_len) {
1049 if (usbdev == NULL || src == NULL || misalignment32_of((uintptr_t)src) ||
1050 src_len > USBDEV_BUFFER_ENTRY_SIZE_BYTES) {
1051 return kDifBadArg;
1052 }
1053
1054 // We're writing to the start of the buffer.
1055 ptrdiff_t buffer_offset = (ptrdiff_t)get_buffer_addr(id, 0U);
1056 const uint32_t *restrict ews = (uint32_t *)(src + (src_len & ~15u));
1057 const uint32_t *restrict ws = (uint32_t *)src;
1058
1059 // Transfer blocks of 4 x 32-bit words at a time; use the mmio_ routines for
1060 // compliance and to operate correctly with the DIF mocks, although this
1061 // results in transfers taking 50% longer because of the additional addressing
1062 // arithmetic and increased loop overheads.
1063 while (ws < ews) {
1064 mmio_region_write32(usbdev->base_addr, buffer_offset, ws[0]);
1065 mmio_region_write32(usbdev->base_addr, buffer_offset + 4, ws[1]);
1066 mmio_region_write32(usbdev->base_addr, buffer_offset + 8, ws[2]);
1067 mmio_region_write32(usbdev->base_addr, buffer_offset + 12, ws[3]);
1068 buffer_offset += 16;
1069 ws += 4;
1070 }
1071 src_len &= 15u;
1072
1073 if (src_len) {
1074 // Remaining whole words
1075 ews = ws + (src_len >> 2);
1076 while (ws < ews) {
1077 mmio_region_write32(usbdev->base_addr, buffer_offset, *ws++);
1078 buffer_offset += 4;
1079 }
1080 src_len &= 3u;
1081 if (src_len) {
1082 // Remaining individual bytes
1083 const uint8_t *restrict bs = (uint8_t *)ws;
1084 uint32_t d = bs[0];
1085 if (src_len > 1) {
1086 d |= ((uint32_t)bs[1] << 8);
1087 if (src_len > 2) {
1088 d |= ((uint32_t)bs[2] << 16);
1089 }
1090 }
1091 // Note: we can only perform full 32-bit writes to the packet buffer but
1092 // any additional byte(s) will be ignored. Attempting byte-level writes
1093 // would raise exceptions.
1094 mmio_region_write32(usbdev->base_addr, buffer_offset, d);
1095 }
1096 }
1097
1098 return kDifOk;
1099}
1100
1101dif_result_t dif_usbdev_buffer_raw_read(const dif_usbdev_t *usbdev, uint8_t id,
1102 uint8_t *dst, size_t dst_len) {
1103 if (usbdev == NULL || dst == NULL || misalignment32_of((uintptr_t)dst) ||
1104 dst_len > USBDEV_BUFFER_ENTRY_SIZE_BYTES) {
1105 return kDifBadArg;
1106 }
1107
1108 // We're reading from the start of the packet buffer.
1109 ptrdiff_t buffer_offset = (ptrdiff_t)get_buffer_addr(id, 0U);
1110 const uint32_t *restrict ewd = (uint32_t *)(dst + (dst_len & ~15u));
1111 uint32_t *restrict wd = (uint32_t *)dst;
1112
1113 // Transfer blocks of 4 x 32-bit words at a time; use the mmio_ routines for
1114 // compliance and to operate correctly with the DIF mocks, although this
1115 // results in transfers taking 50% longer because of the additional addressing
1116 // arithmetic and increased loop overheads.
1117 while (wd < ewd) {
1118 wd[0] = mmio_region_read32(usbdev->base_addr, buffer_offset);
1119 wd[1] = mmio_region_read32(usbdev->base_addr, buffer_offset + 4);
1120 wd[2] = mmio_region_read32(usbdev->base_addr, buffer_offset + 8);
1121 wd[3] = mmio_region_read32(usbdev->base_addr, buffer_offset + 12);
1122 buffer_offset += 16;
1123 wd += 4;
1124 }
1125 dst_len &= 15u;
1126
1127 if (dst_len) {
1128 // Remaining whole words
1129 ewd = wd + (dst_len >> 2);
1130 while (wd < ewd) {
1131 *wd++ = mmio_region_read32(usbdev->base_addr, buffer_offset);
1132 buffer_offset += 4;
1133 }
1134 dst_len &= 3u;
1135 if (dst_len) {
1136 // Remaining individual bytes
1137 // Note: we can only perform full 32-bit reads from the packet buffer.
1138 uint8_t *restrict bd = (uint8_t *)wd;
1139 uint32_t d = mmio_region_read32(usbdev->base_addr, buffer_offset);
1140 bd[0] = (uint8_t)d;
1141 if (dst_len > 1) {
1142 bd[1] = (uint8_t)(d >> 8);
1143 if (dst_len > 2) {
1144 bd[2] = (uint8_t)(d >> 16);
1145 }
1146 }
1147 }
1148 }
1149
1150 return kDifOk;
1151}