From d556f0abab999f8d218e4d037145738be5d762f7 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 24 Aug 2022 23:20:13 +0100 Subject: [PATCH 1/4] Add API to support flushing the device buffer when TX finishes. --- host/libhackrf/src/hackrf.c | 82 ++++++++++++++++++++++++++++++++++++- host/libhackrf/src/hackrf.h | 3 ++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index a16556db..cb2b1eff 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -123,6 +123,7 @@ 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 { @@ -142,6 +143,8 @@ struct hackrf_device { volatile int active_transfers; /* number of active transfers */ pthread_cond_t all_finished_cv; /* signalled when all transfers have finished */ pthread_mutex_t all_finished_lock; /* used to protect all_finished */ + bool flush; + struct libusb_transfer* flush_transfer; }; typedef struct { @@ -272,6 +275,9 @@ static int free_transfers(hackrf_device* device) free(device->transfers); device->transfers = NULL; } + + libusb_free_transfer(device->flush_transfer); + return HACKRF_SUCCESS; } @@ -653,6 +659,8 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d lib_device->streaming = false; lib_device->do_exit = false; lib_device->active_transfers = 0; + lib_device->flush = false; + lib_device->flush_transfer = NULL; result = pthread_mutex_init(&lib_device->transfer_lock, NULL); if (result != 0) { @@ -1692,20 +1700,44 @@ static void transfer_finished( struct hackrf_device* device, struct libusb_transfer* finished_transfer) { + int result; + // If a transfer finished for any reason, we're shutting down. device->streaming = false; // If this is the last transfer, signal that all are now finished. pthread_mutex_lock(&device->all_finished_lock); if (device->active_transfers == 1) { - device->active_transfers = 0; - pthread_cond_signal(&device->all_finished_cv); + if (device->flush) { + // Don't finish yet - flush the TX buffer first. + result = libusb_submit_transfer(device->flush_transfer); + // If that fails, just shut down. + if (result != LIBUSB_SUCCESS) { + device->flush = false; + device->active_transfers = 0; + pthread_cond_broadcast(&device->all_finished_cv); + } + } else { + device->active_transfers = 0; + pthread_cond_broadcast(&device->all_finished_cv); + } } else { device->active_transfers--; } pthread_mutex_unlock(&device->all_finished_lock); } +static void LIBUSB_CALL hackrf_libusb_flush_callback(struct libusb_transfer* usb_transfer) +{ + // TX buffer is now flushed, so proceed with signalling completion. + hackrf_device* device = (hackrf_device*) usb_transfer->user_data; + pthread_mutex_lock(&device->all_finished_lock); + device->flush = false; + device->active_transfers = 0; + pthread_cond_broadcast(&device->all_finished_cv); + pthread_mutex_unlock(&device->all_finished_lock); +} + static void LIBUSB_CALL hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) { @@ -1893,6 +1925,9 @@ int ADDCALL hackrf_start_tx( { int result; const uint8_t endpoint_address = TX_ENDPOINT_ADDRESS; + if (device->flush_transfer != NULL) { + device->flush = true; + } result = hackrf_set_transceiver_mode(device, HACKRF_TRANSCEIVER_MODE_TRANSMIT); if (result == HACKRF_SUCCESS) { device->tx_ctx = tx_ctx; @@ -1901,6 +1936,49 @@ int ADDCALL hackrf_start_tx( return result; } +int ADDCALL hackrf_enable_tx_flush(hackrf_device* device, int enable) +{ + if (enable) { + if (device->flush_transfer) { + return HACKRF_SUCCESS; + } + + if ((device->flush_transfer = libusb_alloc_transfer(0)) == NULL) { + return HACKRF_ERROR_LIBUSB; + } + + libusb_fill_bulk_transfer( + device->flush_transfer, + device->usb_device, + TX_ENDPOINT_ADDRESS, + calloc(1, DEVICE_BUFFER_SIZE), + DEVICE_BUFFER_SIZE, + hackrf_libusb_flush_callback, + device, + 0); + + device->flush_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; + } else { + libusb_free_transfer(device->flush_transfer); + device->flush_transfer = NULL; + } + + return HACKRF_SUCCESS; +} + +int ADDCALL hackrf_await_tx_flush(hackrf_device* device) +{ + // Wait for the transfer thread to signal that all transfers + // have finished. + pthread_mutex_lock(&device->all_finished_lock); + while (device->active_transfers > 0) { + pthread_cond_wait(&device->all_finished_cv, &device->all_finished_lock); + } + pthread_mutex_unlock(&device->all_finished_lock); + + return HACKRF_SUCCESS; +} + /* * Stop any pending transmit. * diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 0d3efc8f..2a0abf28 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -237,6 +237,9 @@ extern ADDAPI int ADDCALL hackrf_start_tx( hackrf_sample_block_cb_fn callback, void* tx_ctx); +extern ADDAPI int ADDCALL hackrf_enable_tx_flush(hackrf_device* device, int enable); +extern ADDAPI int ADDCALL hackrf_await_tx_flush(hackrf_device* device); + extern ADDAPI int ADDCALL hackrf_stop_tx(hackrf_device* device); extern ADDAPI int ADDCALL hackrf_get_m0_state( From 8f60e6b66f527f961015d54d1f99a7485d5422ec Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 24 Aug 2022 23:21:52 +0100 Subject: [PATCH 2/4] Wait for TX buffer flush at end of hackrf_transfer. --- host/hackrf-tools/src/hackrf_transfer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index 619b6724..27c94e51 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -1171,6 +1171,7 @@ int main(int argc, char** argv) result |= hackrf_start_rx(device, rx_callback, NULL); } else { result = hackrf_set_txvga_gain(device, txvga_gain); + result |= hackrf_enable_tx_flush(device, 1); result |= hackrf_start_tx(device, tx_callback, NULL); } if (result != HACKRF_SUCCESS) { @@ -1387,6 +1388,10 @@ int main(int argc, char** argv) interval_timer.it_value.tv_sec = 0; setitimer(ITIMER_REAL, &interval_timer, NULL); #endif + if (transmit) { + // Wait for TX to finish. + hackrf_await_tx_flush(device); + } result = hackrf_is_streaming(device); if (do_exit) { From 0142ae6da9ee680f352f267bbd3177eeaee9fc6f Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 12 Sep 2022 16:32:11 +0100 Subject: [PATCH 3/4] Don't await buffer flush if interrupted by Ctrl-C. --- host/hackrf-tools/src/hackrf_transfer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index 27c94e51..b9005cf2 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -322,6 +322,7 @@ char* u64toa(uint64_t val, t_u64toa* str) } static volatile bool do_exit = false; +static volatile bool interrupted = false; #ifdef _WIN32 static HANDLE interrupt_handle; #endif @@ -638,6 +639,7 @@ static hackrf_device* device = NULL; BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { + interrupted = true; fprintf(stderr, "Caught signal %d\n", signum); stop_main_loop(); return TRUE; @@ -647,6 +649,7 @@ BOOL WINAPI sighandler(int signum) #else void sigint_callback_handler(int signum) { + interrupted = true; fprintf(stderr, "Caught signal %d\n", signum); do_exit = true; } @@ -1388,7 +1391,7 @@ int main(int argc, char** argv) interval_timer.it_value.tv_sec = 0; setitimer(ITIMER_REAL, &interval_timer, NULL); #endif - if (transmit) { + if (transmit && !interrupted) { // Wait for TX to finish. hackrf_await_tx_flush(device); } From 765023683921aeb833e28131c44d88a811b97ece Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Mon, 12 Sep 2022 16:50:53 +0100 Subject: [PATCH 4/4] Flush buffer when CW mode is used with -n. --- host/hackrf-tools/src/hackrf_transfer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index b9005cf2..8a822ebf 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -1391,7 +1391,7 @@ int main(int argc, char** argv) interval_timer.it_value.tv_sec = 0; setitimer(ITIMER_REAL, &interval_timer, NULL); #endif - if (transmit && !interrupted) { + if ((transmit || signalsource) && !interrupted) { // Wait for TX to finish. hackrf_await_tx_flush(device); }