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 b49e8cb94..77ef0eaea 100644 --- a/firmware/common/LPC43xx_M4_memory.ld +++ b/firmware/common/LPC43xx_M4_memory.ld @@ -26,13 +26,14 @@ MEMORY rom_flash (rx) : ORIGIN = 0x80000000, LENGTH = 1M ram_m0 (rwx) : ORIGIN = 0x20000000, LENGTH = 28K ram_shared (rwx) : ORIGIN = 0x20007000, LENGTH = 4K - ram_usb (rwx) : ORIGIN = 0x20008000, LENGTH = 32K - /* ram_usb: USB buffer. Straddles two blocks of RAM - * to get performance benefit of having two USB buffers addressable + ram_samp (rwx) : ORIGIN = 0x20008000, LENGTH = 32K + /* ram_samp: Sample buffer. Straddles two blocks of RAM to get + * performance benefit of having two halves addressable * simultaneously (on two different buses of the AHB multilayer matrix) */ } +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/usb.h b/firmware/common/usb.h index 3d0898163..e064d196c 100644 --- a/firmware/common/usb.h +++ b/firmware/common/usb.h @@ -29,6 +29,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 860ff18fc..5ecb2707d 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -53,7 +53,6 @@ #include "usb_api_sweep.h" #include "usb_api_transceiver.h" #include "usb_api_ui.h" -#include "usb_bulk_buffer.h" #include "usb_api_m0_state.h" #include "cpld_xc2c.h" #include "portapack.h" @@ -151,6 +150,7 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_read_selftest, usb_vendor_request_adc_read, usb_vendor_request_test_rtc_osc, + 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 ae4e74b46..090961134 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -25,7 +25,7 @@ #include #include #include "usb_api_transceiver.h" -#include "usb_bulk_buffer.h" +#include "usb_buffer.h" #include "usb_api_m0_state.h" #include "tuning.h" #include "usb_endpoint.h" @@ -166,7 +166,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 cbc8e2939..ce699deca 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -27,7 +27,8 @@ #include "operacake_sctimer.h" #include -#include "usb_bulk_buffer.h" +#include +#include "usb_buffer.h" #include "usb_api_m0_state.h" #include "usb_api_cpld.h" // Remove when CPLD update is handled elsewhere @@ -40,6 +41,7 @@ #include "usb.h" #include "usb_queue.h" #include "platform_detect.h" +#include "gpdma.h" #include #include @@ -48,6 +50,18 @@ #include "usb_api_sweep.h" #define USB_TRANSFER_SIZE 0x4000 +#define DMA_TRANSFER_SIZE 0x2000 + +#define DMA_CHANNEL 1 + +#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; @@ -172,6 +186,11 @@ usb_request_status_t usb_vendor_request_set_sample_rate_frac( NULL); return USB_REQUEST_STATUS_OK; } else if (stage == USB_TRANSFER_STAGE_DATA) { + float hz = (float) (set_sample_r_params.freq_hz) / + (float) set_sample_r_params.divider; + if (hz > 21800000.0) { + return USB_REQUEST_STATUS_STALL; + } radio_error_t result = radio_set_sample_rate( &radio, RADIO_CHANNEL0, @@ -328,6 +347,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) { @@ -354,7 +384,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; + radio_switch_mode(&radio, RADIO_CHANNEL0, mode); + hackrf_ui()->set_transceiver_mode(mode); switch (mode) { @@ -444,29 +480,110 @@ 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; +} + +void transceiver_start_dma(void* src, void* dest, size_t size) +{ + uint32_t num_transfers = size >> 2; + gpdma_controller_enable(); + GPDMA_CSRCADDR(DMA_CHANNEL) = (uint32_t) src; + GPDMA_CDESTADDR(DMA_CHANNEL) = (uint32_t) dest; + GPDMA_CLLI(DMA_CHANNEL) = 0; + GPDMA_CCONTROL(DMA_CHANNEL) = GPDMA_CCONTROL_TRANSFERSIZE(num_transfers) | + 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 + GPDMA_CCONFIG(DMA_CHANNEL) = 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_INTTCCLEAR = (1 << DMA_CHANNEL); + nvic_enable_irq(NVIC_DMA_IRQ); + gpdma_channel_enable(DMA_CHANNEL); + dma_pending = size; +} + +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) { - uint32_t usb_count = 0; - 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) { + uint32_t data_gathered = m0_state.m0_count; + uint32_t dma_completed = m0_state.m4_count; + uint32_t data_available = data_gathered - dma_started; + uint32_t space_in_use = usb_completed - dma_completed; + uint32_t space_available = USB_BULK_BUFFER_SIZE - space_in_use; + bool ahb_busy = !((data_gathered ^ dma_started) & BUF_HALF_MASK); + if (!dma_pending && !ahb_busy && (data_available >= DMA_TRANSFER_SIZE) && + (space_available >= DMA_TRANSFER_SIZE)) { + uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK; + uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK; + transceiver_start_dma( + &usb_samp_buffer[samp_offset], + &usb_bulk_buffer[bulk_offset], + DMA_TRANSFER_SIZE); + dma_started += DMA_TRANSFER_SIZE; + } + if ((dma_completed - usb_started) >= USB_TRANSFER_SIZE) { + uint32_t bulk_offset = usb_started & USB_BULK_BUFFER_MASK; usb_transfer_schedule_block( &usb_endpoint_bulk_in, - &usb_bulk_buffer[usb_count & USB_BULK_BUFFER_MASK], + &usb_bulk_buffer[bulk_offset], USB_TRANSFER_SIZE, transceiver_bulk_transfer_complete, NULL); - usb_count += USB_TRANSFER_SIZE; + usb_started += USB_TRANSFER_SIZE; } } @@ -475,37 +592,142 @@ 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; + + // 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; + } + } + } + + // 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) { - if (!started && (m0_state.m4_count == USB_BULK_BUFFER_SIZE)) { - // Buffer is now full, start streaming. - baseband_streaming_enable(&sgpio_config); - started = true; + uint32_t data_used = m0_state.m0_count; + uint32_t dma_completed = m0_state.m4_count; + uint32_t data_available = usb_completed - dma_started; + uint32_t space_in_use = dma_started - data_used; + uint32_t space_available = USB_SAMP_BUFFER_SIZE - space_in_use; + bool ahb_busy = !((data_used ^ dma_started) & BUF_HALF_MASK); + if (!dma_pending && !ahb_busy && (data_available >= DMA_TRANSFER_SIZE) && + (space_available >= DMA_TRANSFER_SIZE)) { + uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK; + uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK; + transceiver_start_dma( + &usb_bulk_buffer[bulk_offset], + &usb_samp_buffer[samp_offset], + DMA_TRANSFER_SIZE); + dma_started += DMA_TRANSFER_SIZE; } - if ((usb_count - m0_state.m0_count) <= USB_TRANSFER_SIZE) { + if ((usb_started - dma_completed) <= USB_TRANSFER_SIZE) { + uint32_t bulk_offset = usb_started & USB_BULK_BUFFER_MASK; usb_transfer_schedule_block( &usb_endpoint_bulk_out, - &usb_bulk_buffer[usb_count & USB_BULK_BUFFER_MASK], + &usb_bulk_buffer[bulk_offset], USB_TRANSFER_SIZE, transceiver_bulk_transfer_complete, NULL); - usb_count += USB_TRANSFER_SIZE; + usb_started += USB_TRANSFER_SIZE; + } + } + + // 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_used = m0_state.m0_count; + uint32_t data_available = usb_completed - dma_started; + uint32_t space_in_use = dma_started - data_used; + uint32_t space_available = USB_SAMP_BUFFER_SIZE - space_in_use; + uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK; + uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK; + bool ahb_busy = !((data_used ^ dma_started) & BUF_HALF_MASK); + size_t size = data_available >= DMA_TRANSFER_SIZE ? DMA_TRANSFER_SIZE : + data_available; + if (dma_pending || ahb_busy || size > space_available) { + continue; } + transceiver_start_dma( + &usb_bulk_buffer[bulk_offset], + &usb_samp_buffer[samp_offset], + size); + dma_started += size; } + // 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) + ; + + // 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 e06d6c16d..1be3c4b58 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.h +++ b/firmware/hackrf_usb/usb_api_transceiver.h @@ -35,6 +35,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); @@ -74,6 +76,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 71% rename from firmware/hackrf_usb/usb_bulk_buffer.h rename to firmware/hackrf_usb/usb_buffer.h index 3e2841432..2727b3106 100644 --- a/firmware/hackrf_usb/usb_bulk_buffer.h +++ b/firmware/hackrf_usb/usb_buffer.h @@ -21,19 +21,23 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __USB_BULK_BUFFER_H__ -#define __USB_BULK_BUFFER_H__ +#ifndef __USB_BUFFER_H__ +#define __USB_BUFFER_H__ #include #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]; -#endif /*__USB_BULK_BUFFER_H__*/ +#endif /*__USB_BUFFER_H__*/ diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 70c4daf14..68b2ea81e 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -37,7 +37,7 @@ #define USB_PRODUCT_ID (0xFFFF) #endif -#define USB_API_VERSION (0x0109) +#define USB_API_VERSION (0x0110) #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 028327087..04ef0b95d 100644 --- a/firmware/hackrf_usb/usb_endpoint.c +++ b/firmware/hackrf_usb/usb_endpoint.c @@ -26,13 +26,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 5aab19917..3864da3af 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -111,6 +111,7 @@ typedef enum { HACKRF_VENDOR_REQUEST_READ_SELFTEST = 56, HACKRF_VENDOR_REQUEST_READ_ADC = 57, HACKRF_VENDOR_REQUEST_TEST_RTC_OSC = 58, + HACKRF_VENDOR_REQUEST_GET_BUFFER_SIZE = 59, } hackrf_vendor_request; #define USB_CONFIG_STANDARD 0x1 @@ -134,11 +135,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 @@ -158,6 +159,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 { @@ -183,10 +185,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; @@ -709,6 +709,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? @@ -742,6 +750,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; @@ -754,6 +763,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 >= 0x0110) { + // 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); @@ -1696,17 +1729,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; } @@ -2370,8 +2393,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);