Flush both buffers before leaving TX mode.

This commit is contained in:
Martin Ling 2025-12-01 23:24:34 +00:00
parent 0fb7b1c8c0
commit cebf0c7ce1
2 changed files with 60 additions and 0 deletions

View file

@ -31,6 +31,8 @@
#include "usb_type.h"
usb_queue_head_t* usb_queue_head(const uint_fast8_t endpoint_address);
void usb_peripheral_reset(void);
void usb_phy_enable(void);

View file

@ -27,6 +27,8 @@
#include <stddef.h>
#include <string.h>
#include <libopencm3/lpc43xx/usb.h>
#include <clock_gen.h>
#include <drivers.h>
#include <fixed_point.h>
@ -593,6 +595,62 @@ void tx_mode(uint32_t seq)
radio_update(&radio);
}
// Host has now requested to stop TX. We won't initiate any further USB
// transfers into the bulk buffer. However, we should make sure all
// data currently in the USB bulk buffer reaches the sample buffer.
if ((usb_started - usb_completed) > 0) {
// We were part way through a 16KB firmware-side transfer when
// the transceiver mode change request to stop TX was received.
//
// We want to include the contents of that partial transfer in
// the data we move to the sample buffer.
//
// The transfer was already stopped by usb_endpoint_flush(),
// which was called from request_transceiver_mode().
//
// We will not have had a callback, and the transfer descriptor
// (dTD) will not have been updated, since the transfer did not
// complete.
//
// However, as long as we haven't started a new transfer, we
// can retrieve the partial byte count from the transfer
// overlay in the endpoint queue head (dQH) (UM10503 25.9.1).
usb_queue_head_t* const qh =
usb_queue_head(usb_endpoint_bulk_out.address);
unsigned int bytes_remaining =
(qh->total_bytes & USB_TD_DTD_TOKEN_TOTAL_BYTES_MASK) >>
USB_TD_DTD_TOKEN_TOTAL_BYTES_SHIFT;
unsigned int bytes_transferred = USB_TRANSFER_SIZE - bytes_remaining;
usb_completed += bytes_transferred;
}
// Feed the remaining data from the bulk buffer to the sample buffer.
// At this point, we also need to handle the case where there is less data
// to be transferred to the sample buffer than a full-sized DMA transfer.
// Any remainder of less than 4 bytes will be ignored; this is the chunk
// size of our DMA transfers.
while ((usb_completed - m0_state.m4_count) >= 4) {
uint32_t data_available = usb_completed - dma_started;
if (data_available > DMA_TRANSFER_SIZE) {
start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE);
} else {
start_dma_if_possible(DIRECTION_TX, data_available);
}
radio_update(&radio);
}
// Wait for the data in the sample buffer to be transmitted.
// Any remainder of less than 32 bytes will be ignored; this is
// the chunk size used by the M0 core to transfer samples to SGPIO.
while ((m0_state.m4_count - m0_state.m0_count) >= 32) {
radio_update(&radio);
}
// All data received from the host has now been transmitted.
transceiver_shutdown();
}