diff --git a/firmware/common/LPC4320_M4_memory.ld b/firmware/common/LPC4320_M4_memory.ld index a5997f321..48be94da2 100644 --- a/firmware/common/LPC4320_M4_memory.ld +++ b/firmware/common/LPC4320_M4_memory.ld @@ -26,7 +26,8 @@ MEMORY { /* rom is really the shadow region that points to SPI flash or elsewhere */ rom (rx) : ORIGIN = 0x00000000, LENGTH = 1M - ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K + ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 64K + ram_usb (rw) : ORIGIN = 0x10010000, LENGTH = 32K ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 32K ram_sleep (rwx) : ORIGIN = 0x10088000, LENGTH = 8K } diff --git a/firmware/common/LPC4330_M4_memory.ld b/firmware/common/LPC4330_M4_memory.ld index 1e886d3a9..1d42ebee9 100644 --- a/firmware/common/LPC4330_M4_memory.ld +++ b/firmware/common/LPC4330_M4_memory.ld @@ -26,7 +26,8 @@ MEMORY { /* rom is really the shadow region that points to SPI flash or elsewhere */ rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K - ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 128K + ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K + ram_usb(rw) : ORIGIN = 0x10018000, LENGTH = 32K ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 64K ram_sleep (rwx) : ORIGIN = 0x10090000, LENGTH = 8K } diff --git a/firmware/common/LPC43xx_M4_memory.ld b/firmware/common/LPC43xx_M4_memory.ld index 933fa854e..fc7dcee7f 100644 --- a/firmware/common/LPC43xx_M4_memory.ld +++ b/firmware/common/LPC43xx_M4_memory.ld @@ -35,11 +35,12 @@ MEMORY * to get performance benefit of having two USB buffers addressable * simultaneously (on two different buses of the AHB multilayer matrix) */ - ram_usb (rwx) : ORIGIN = 0x20008000, LENGTH = 32K + ram_samp (rwx) : ORIGIN = 0x20008000, LENGTH = 32K } -usb_bulk_buffer = ORIGIN(ram_usb); lz4_in_buf = ORIGIN(ram_lz4_in); lz4_out_buf = ORIGIN(ram_lz4_out); +usb_samp_buffer = ORIGIN(ram_samp); +usb_bulk_buffer = ORIGIN(ram_usb); m0_state = ORIGIN(ram_shared); PROVIDE(__ram_m0_start__ = ORIGIN(ram_m0)); diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index 30e70ea27..b23be5255 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -37,8 +37,8 @@ #include "streaming.h" // USB buffer used during selftests. -#define USB_BULK_BUFFER_SIZE 0x8000 -extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE]; +#define USB_SAMP_BUFFER_SIZE 0x8000 +extern uint8_t usb_samp_buffer[USB_SAMP_BUFFER_SIZE]; static int rx_samples(const unsigned int num_samples, uint32_t max_cycles) { @@ -115,10 +115,10 @@ bool fpga_sgpio_selftest(void) fpga_set_prbs_enable(&fpga, false); // Generate sequence from first value and compare. - bool seq_in_sync = (usb_bulk_buffer[0] != 0); - uint8_t seq = lfsr_advance(usb_bulk_buffer[0]); + bool seq_in_sync = (usb_samp_buffer[0] != 0); + uint8_t seq = lfsr_advance(usb_samp_buffer[0]); for (int i = 1; i < 512; ++i) { - if (usb_bulk_buffer[i] != seq) { + if (usb_samp_buffer[i] != seq) { seq_in_sync = false; break; } @@ -189,7 +189,7 @@ bool fpga_if_xcvr_selftest(void) return false; } - const size_t num_samples = USB_BULK_BUFFER_SIZE / 2; + const size_t num_samples = USB_SAMP_BUFFER_SIZE / 2; // Set common RX path and gateware settings for the measurements. fpga_set_tx_nco_pstep(&fpga, 64); // NCO phase increment @@ -206,7 +206,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[0]); @@ -217,7 +217,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[1]); @@ -230,7 +230,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[2]); @@ -241,7 +241,7 @@ bool fpga_if_xcvr_selftest(void) timeout = true; } measure_tone( - (int8_t*) usb_bulk_buffer, + (int8_t*) usb_samp_buffer, num_samples, &selftest.xcvr_measurements[3]); diff --git a/firmware/common/usb.h b/firmware/common/usb.h index 9445728a3..2b8c6c8ce 100644 --- a/firmware/common/usb.h +++ b/firmware/common/usb.h @@ -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); diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 54431ff3c..cabf0d114 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -183,6 +183,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_test_rtc_osc, usb_vendor_request_write_radio_reg, usb_vendor_request_read_radio_reg, + usb_vendor_request_get_buffer_size, }; static const uint32_t vendor_request_handler_count = diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index ae3b399d1..eee742282 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -40,7 +40,7 @@ #include "usb_api_sweep.h" #include "usb_api_transceiver.h" -#include "usb_bulk_buffer.h" +#include "usb_buffer.h" #include "usb_endpoint.h" #define MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -207,7 +207,7 @@ void sweep_mode(uint32_t seq) m0_state.next_mode = M0_MODE_RX; // Write metadata to buffer. - buffer = &usb_bulk_buffer[phase * 0x4000]; + buffer = &usb_samp_buffer[phase * 0x4000]; *buffer = 0x7f; *(buffer + 1) = 0x7f; *(buffer + 2) = sweep_freq & 0xff; diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index b6c6782be..fe2b54057 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -26,9 +26,14 @@ #include #include +#include +#include +#include + #include #include #include +#include #include #include #include @@ -41,10 +46,20 @@ #include #include -#include "usb_bulk_buffer.h" +#include "usb_buffer.h" #include "usb_endpoint.h" #define USB_TRANSFER_SIZE 0x4000 +#define DMA_TRANSFER_SIZE 0x2000 + +#define BUF_HALF_MASK (USB_SAMP_BUFFER_SIZE >> 1) + +// Unless we know the host knows our buffer size, we'll avoid leaving TX +// until we've transmitted all bytes sent by the host. This flag is cleared +// when the host requests our buffer size. +bool auto_tx_flush = true; + +volatile uint32_t dma_started, dma_pending, usb_started, usb_completed; typedef struct { uint32_t freq_mhz; @@ -68,6 +83,8 @@ typedef struct { set_sample_r_params_t set_sample_r_params; +void transceiver_dma_setup(void); + usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) @@ -314,6 +331,17 @@ volatile transceiver_request_t transceiver_request = { .seq = 0, }; +void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint) +{ + if (transceiver_request.mode == TRANSCEIVER_MODE_TX && + endpoint->setup.request == 1 && auto_tx_flush) { + // This is a request to leave TX mode. Do so but NAK for now. + request_transceiver_mode(endpoint->setup.value); + } else { + usb_setup_complete(endpoint); + } +} + // Must be called from an atomic context (normally USB ISR) void request_transceiver_mode(transceiver_mode_t mode) { @@ -340,6 +368,13 @@ void transceiver_shutdown(void) void transceiver_startup(const transceiver_mode_t mode) { + dma_started = 0; + dma_pending = 0; + usb_started = 0; + usb_completed = 0; + + transceiver_dma_setup(); + radio_switch_opmode(&radio, mode); switch (mode) { @@ -424,30 +459,190 @@ usb_request_status_t usb_vendor_request_set_rx_overrun_limit( return USB_REQUEST_STATUS_OK; } +usb_request_status_t usb_vendor_request_get_buffer_size( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint32_t value = USB_SAMP_BUFFER_SIZE + USB_BULK_BUFFER_SIZE; + endpoint->buffer[0] = value & 0xff; + endpoint->buffer[1] = (value & 0xff00) >> 8; + endpoint->buffer[2] = (value & 0xff0000) >> 16; + endpoint->buffer[3] = (value & 0xff000000) >> 24; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 4, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + + // We now know the host is aware of our buffer size, so it + // can make its own decisions about flushing the buffer. + auto_tx_flush = false; + + return USB_REQUEST_STATUS_OK; + } + return USB_REQUEST_STATUS_OK; +} + +/* clang-format off */ + +// Which GPDMA channel to use. +const uint32_t DMA_CHANNEL = 1; + +// GPDMA CCONFIG register setting. +const uint32_t DMA_CONFIG = + GPDMA_CCONFIG_FLOWCNTRL(0) // memory-to-memory + | GPDMA_CCONFIG_IE(0) // no error interrupt + | GPDMA_CCONFIG_ITC(1) // terminal count interrupt + | GPDMA_CCONFIG_L(0) // do not lock + | GPDMA_CCONFIG_H(0); // do not halt + +// GPDMA CCONTROL register setting (excluding TRANSFERSIZE field). +const uint32_t DMA_CONTROL = + GPDMA_CCONTROL_SBSIZE(7) // 256-transfer src bursts + | GPDMA_CCONTROL_DBSIZE(7) // 256-transfer dst bursts + | GPDMA_CCONTROL_SWIDTH(2) // 32-bit src transfers + | GPDMA_CCONTROL_DWIDTH(2) // 32-bit dst transfers + | GPDMA_CCONTROL_S(0) // AHB Master 0 + | GPDMA_CCONTROL_D(1) // AHB Master 1 + | GPDMA_CCONTROL_SI(1) // increment source + | GPDMA_CCONTROL_DI(1) // increment destination + | GPDMA_CCONTROL_PROT1(0) // user mode + | GPDMA_CCONTROL_PROT2(0) // not bufferable + | GPDMA_CCONTROL_PROT3(0) // not cacheable + | GPDMA_CCONTROL_I(1); // interrupt enabled + +/* clang-format on */ + +// Called before any sequence of DMA transfers. +void transceiver_dma_setup(void) +{ + gpdma_controller_enable(); + GPDMA_CCONFIG(DMA_CHANNEL) = DMA_CONFIG; + GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL; + GPDMA_CLLI(DMA_CHANNEL) = 0; + GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL); + nvic_enable_irq(NVIC_DMA_IRQ); +} + +// Called to start each DMA transfer. +void transceiver_start_dma(void* src, void* dest, size_t size) +{ + uint32_t num_transfers = size >> 2; + GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL | num_transfers; + GPDMA_CSRCADDR(DMA_CHANNEL) = (uint32_t) src; + GPDMA_CDESTADDR(DMA_CHANNEL) = (uint32_t) dest; + dma_pending = size; + gpdma_channel_enable(DMA_CHANNEL); +} + +// Called when a DMA transfer completes. +void dma_isr(void) +{ + gpdma_channel_disable(DMA_CHANNEL); + GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL); + m0_state.m4_count += dma_pending; + dma_pending = 0; +} + void transceiver_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) { (void) user_data; - m0_state.m4_count += bytes_transferred; + usb_completed += bytes_transferred; } -void rx_mode(uint32_t seq) +typedef enum { + DIRECTION_RX, + DIRECTION_TX, +} direction_t; + +void start_dma_if_possible(direction_t direction, size_t size) +{ + if (dma_pending) { + return; + } + + uint32_t sampling_completed = m0_state.m0_count; + uint32_t dma_completed = m0_state.m4_count; + uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK; + uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK; + uint32_t data_available, space_in_use, space_available, samp_buf_margin; + uint8_t *dest, *src; + + if (direction == DIRECTION_RX) { + data_available = sampling_completed - dma_started; + space_in_use = usb_completed - dma_completed; + space_available = USB_BULK_BUFFER_SIZE - space_in_use; + samp_buf_margin = USB_SAMP_BUFFER_SIZE - data_available; + src = &usb_samp_buffer[samp_offset]; + dest = &usb_bulk_buffer[bulk_offset]; + } else { + data_available = usb_completed - dma_started; + space_in_use = dma_completed - sampling_completed; + space_available = USB_SAMP_BUFFER_SIZE - space_in_use; + samp_buf_margin = space_in_use; + src = &usb_bulk_buffer[bulk_offset]; + dest = &usb_samp_buffer[samp_offset]; + } + + if (data_available < size || size > space_available) { + return; + } + + uint32_t m0_buf_half = sampling_completed & BUF_HALF_MASK; + uint32_t dma_buf_half = dma_started & BUF_HALF_MASK; + bool same_buf_half = m0_buf_half == dma_buf_half; + + if (same_buf_half && samp_buf_margin >= (USB_SAMP_BUFFER_SIZE / 2)) { + return; + } + + transceiver_start_dma(src, dest, size); + + dma_started += size; +} + +void start_usb_if_possible(direction_t direction) { - uint32_t usb_count = 0; + uint32_t bulk_offset = usb_started & USB_BULK_BUFFER_MASK; + uint32_t dma_completed = m0_state.m4_count; + uint32_t bytes_available; + usb_endpoint_t* usb_endpoint; + + if (direction == DIRECTION_RX) { + bytes_available = dma_completed - usb_started; + usb_endpoint = &usb_endpoint_bulk_in; + } else { + uint32_t space_used = usb_started - dma_completed; + bytes_available = USB_BULK_BUFFER_SIZE - space_used; + usb_endpoint = &usb_endpoint_bulk_out; + } + + if (bytes_available < USB_TRANSFER_SIZE) { + return; + } + + usb_transfer_schedule_block( + usb_endpoint, + &usb_bulk_buffer[bulk_offset], + USB_TRANSFER_SIZE, + transceiver_bulk_transfer_complete, + NULL); + usb_started += USB_TRANSFER_SIZE; +} + +void rx_mode(uint32_t seq) +{ transceiver_startup(TRANSCEIVER_MODE_RX); baseband_streaming_enable(&sgpio_config); while (transceiver_request.seq == seq) { - if ((m0_state.m0_count - usb_count) >= USB_TRANSFER_SIZE) { - usb_transfer_schedule_block( - &usb_endpoint_bulk_in, - &usb_bulk_buffer[usb_count & USB_BULK_BUFFER_MASK], - USB_TRANSFER_SIZE, - transceiver_bulk_transfer_complete, - NULL); - usb_count += USB_TRANSFER_SIZE; - } + start_dma_if_possible(DIRECTION_RX, DMA_TRANSFER_SIZE); + start_usb_if_possible(DIRECTION_RX); radio_update(&radio); } @@ -456,38 +651,112 @@ void rx_mode(uint32_t seq) void tx_mode(uint32_t seq) { - unsigned int usb_count = 0; - bool started = false; - transceiver_startup(TRANSCEIVER_MODE_TX); - // Set up OUT transfer of buffer 0. - usb_transfer_schedule_block( - &usb_endpoint_bulk_out, - &usb_bulk_buffer[0x0000], - USB_TRANSFER_SIZE, - transceiver_bulk_transfer_complete, - NULL); - usb_count += USB_TRANSFER_SIZE; + // First, make transfers directly into the sample buffer to fill it. + for (int i = 0; i < (USB_SAMP_BUFFER_SIZE / USB_TRANSFER_SIZE); i++) { + // Set up transfer. + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + &usb_samp_buffer[usb_started], + USB_TRANSFER_SIZE, + transceiver_bulk_transfer_complete, + NULL); + usb_started += USB_TRANSFER_SIZE; - while (transceiver_request.seq == seq) { - if (!started && (m0_state.m4_count == USB_BULK_BUFFER_SIZE)) { - // Buffer is now full, start streaming. - baseband_streaming_enable(&sgpio_config); - started = true; + // Wait for the transfer to complete. + while (usb_completed < usb_started) { + // Handle the host switching modes before filling the buffer. + if (transceiver_request.seq != seq) { + transceiver_shutdown(); + return; + } + + radio_update(&radio); } - if ((usb_count - m0_state.m0_count) <= USB_TRANSFER_SIZE) { - usb_transfer_schedule_block( - &usb_endpoint_bulk_out, - &usb_bulk_buffer[usb_count & USB_BULK_BUFFER_MASK], - USB_TRANSFER_SIZE, - transceiver_bulk_transfer_complete, - NULL); - usb_count += USB_TRANSFER_SIZE; + } + + // Sample buffer is now full. Update DMA counters accordingly. + dma_started = USB_SAMP_BUFFER_SIZE; + m0_state.m4_count = USB_SAMP_BUFFER_SIZE; + + // Start transmitting samples. + baseband_streaming_enable(&sgpio_config); + + // Continue feeding samples to the sample buffer. + while (transceiver_request.seq == seq) { + start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE); + start_usb_if_possible(DIRECTION_TX); + radio_update(&radio); + } + + // Host has now requested to stop TX. If we're not auto-flushing, we + // should now stop TX immediately. + + if (!auto_tx_flush) { + transceiver_shutdown(); + return; + } + + // Otherwise, we should now ensure all bytes sent by the host are + // transmitted before we leave TX. First, 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. + // Now we can ACK the control request that took us out of TX mode. + usb_transfer_schedule_ack(usb_endpoint_control_in.in); + transceiver_shutdown(); } diff --git a/firmware/hackrf_usb/usb_api_transceiver.h b/firmware/hackrf_usb/usb_api_transceiver.h index df981c1ea..a29dec118 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.h +++ b/firmware/hackrf_usb/usb_api_transceiver.h @@ -36,6 +36,8 @@ typedef struct { extern volatile transceiver_request_t transceiver_request; +void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint); + usb_request_status_t usb_vendor_request_set_transceiver_mode( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); @@ -75,6 +77,9 @@ usb_request_status_t usb_vendor_request_set_tx_underrun_limit( usb_request_status_t usb_vendor_request_set_rx_overrun_limit( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_get_buffer_size( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); void request_transceiver_mode(transceiver_mode_t mode); void transceiver_startup(transceiver_mode_t mode); diff --git a/firmware/hackrf_usb/usb_bulk_buffer.h b/firmware/hackrf_usb/usb_buffer.h similarity index 74% rename from firmware/hackrf_usb/usb_bulk_buffer.h rename to firmware/hackrf_usb/usb_buffer.h index e21a84ffb..075430253 100644 --- a/firmware/hackrf_usb/usb_bulk_buffer.h +++ b/firmware/hackrf_usb/usb_buffer.h @@ -25,11 +25,15 @@ #include +#define USB_SAMP_BUFFER_SIZE 0x8000 +#define USB_SAMP_BUFFER_MASK 0x7FFF + #define USB_BULK_BUFFER_SIZE 0x8000 #define USB_BULK_BUFFER_MASK 0x7FFF -/* Address of usb_bulk_buffer is set in ldscripts. If you change the name of this - * variable, it won't be where it needs to be in the processor's address space, - * unless you also adjust the ldscripts. +/* Addresses of usb_samp_buffer and usb_bulk_buffer are set in ldscripts. If + * you change the name of these variables, they won't be where they need to + * be in the processor's address space, unless you also adjust the ldscripts. */ +extern uint8_t usb_samp_buffer[USB_SAMP_BUFFER_SIZE]; extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE]; diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 36531ebcb..067317b31 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -30,7 +30,7 @@ #define USB_VENDOR_ID (0x1D50) -#define USB_API_VERSION (0x0111) +#define USB_API_VERSION (0x0112) #define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) diff --git a/firmware/hackrf_usb/usb_endpoint.c b/firmware/hackrf_usb/usb_endpoint.c index a8a35f0d9..81251f1ec 100644 --- a/firmware/hackrf_usb/usb_endpoint.c +++ b/firmware/hackrf_usb/usb_endpoint.c @@ -28,13 +28,14 @@ #include #include "usb_device.h" +#include "usb_api_transceiver.h" usb_endpoint_t usb_endpoint_control_out = { .address = 0x00, .device = &usb_device, .in = &usb_endpoint_control_in, .out = &usb_endpoint_control_out, - .setup_complete = usb_setup_complete, + .setup_complete = transceiver_usb_setup_complete, .transfer_complete = usb_control_out_complete, }; USB_DEFINE_QUEUE(usb_endpoint_control_out, 4); diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 48a25868a..da3c0f152 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -115,6 +115,7 @@ typedef enum { HACKRF_VENDOR_REQUEST_TEST_RTC_OSC = 58, HACKRF_VENDOR_REQUEST_RADIO_WRITE_REG = 59, HACKRF_VENDOR_REQUEST_RADIO_READ_REG = 60, + HACKRF_VENDOR_REQUEST_GET_BUFFER_SIZE = 61, } hackrf_vendor_request; #define USB_CONFIG_STANDARD 0x1 @@ -138,11 +139,11 @@ typedef enum { #define TRANSFER_COUNT 4 #define TRANSFER_BUFFER_SIZE 262144 -#define DEVICE_BUFFER_SIZE 32768 #define USB_MAX_SERIAL_LENGTH 32 struct hackrf_device { libusb_device_handle* usb_device; + uint16_t usb_api_version; struct libusb_transfer** transfers; hackrf_sample_block_cb_fn callback; volatile bool @@ -162,6 +163,7 @@ struct hackrf_device { hackrf_flush_cb_fn flush_callback; hackrf_tx_block_complete_cb_fn tx_completion_callback; void* flush_ctx; + uint32_t buffer_size; }; typedef struct { @@ -187,10 +189,8 @@ static const max2837_ft_t max2837_ft[] = { {28000000}, {0}}; -#define USB_API_REQUIRED(device, version) \ - uint16_t usb_version = 0; \ - hackrf_usb_api_version_read(device, &usb_version); \ - if (usb_version < version) \ +#define USB_API_REQUIRED(device, version) \ + if (device->usb_api_version < version) \ return HACKRF_ERROR_USB_API_VERSION; static const uint16_t hackrf_usb_vid = 0x1d50; @@ -713,6 +713,14 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d { int result; hackrf_device* lib_device; + uint32_t buffer_size; + struct libusb_device_descriptor device_descriptor; + libusb_device* dev = libusb_get_device(usb_device); + result = libusb_get_device_descriptor(dev, &device_descriptor); + if (result < 0) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } //int speed = libusb_get_device_speed(usb_device); // TODO: Error or warning if not high speed USB? @@ -746,6 +754,7 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d #endif lib_device->usb_device = usb_device; + lib_device->usb_api_version = device_descriptor.bcdDevice; lib_device->transfers = NULL; lib_device->callback = NULL; lib_device->transfer_thread_started = false; @@ -758,6 +767,30 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d lib_device->flush_ctx = NULL; lib_device->tx_completion_callback = NULL; + if (lib_device->usb_api_version >= 0x0112) { + // Fetch buffer size from device so we know how many bytes to flush TX with. + result = libusb_control_transfer( + lib_device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_GET_BUFFER_SIZE, + 0, + 0, + (unsigned char*) &buffer_size, + 4, + 0); + + if (result < 4) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } + + lib_device->buffer_size = FROM_LE32(buffer_size); + } else { + // All older firmware uses a fixed 32KB buffer size. + lib_device->buffer_size = 32768; + } + result = pthread_mutex_init(&lib_device->transfer_lock, NULL); if (result != 0) { free(lib_device); @@ -1700,17 +1733,7 @@ extern ADDAPI int ADDCALL hackrf_usb_api_version_read( hackrf_device* device, uint16_t* version) { - int result; - libusb_device* dev; - struct libusb_device_descriptor desc; - dev = libusb_get_device(device->usb_device); - result = libusb_get_device_descriptor(dev, &desc); - if (result < 0) { - last_libusb_error = result; - return HACKRF_ERROR_LIBUSB; - } - - *version = desc.bcdDevice; + *version = device->usb_api_version; return HACKRF_SUCCESS; } @@ -2374,8 +2397,8 @@ ADDAPI int ADDCALL hackrf_enable_tx_flush( device->flush_transfer, device->usb_device, TX_ENDPOINT_ADDRESS, - calloc(1, DEVICE_BUFFER_SIZE), - DEVICE_BUFFER_SIZE, + calloc(1, device->buffer_size), + device->buffer_size, hackrf_libusb_flush_callback, device, 0);