From b872647e97fe5401a653ae871e535c0064336d01 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 20 Sep 2022 13:26:05 +0100 Subject: [PATCH 01/15] Provide a callback for TX flush, rather than a wait function. --- host/hackrf-tools/src/hackrf_transfer.c | 15 +++--- host/libhackrf/src/hackrf.c | 66 +++++++++++++------------ host/libhackrf/src/hackrf.h | 7 ++- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index c030e24f..091f72ae 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -499,7 +499,6 @@ int tx_callback(hackrf_transfer* transfer) /* If the last data was already buffered, stop. */ if (tx_complete) { - stop_main_loop(); return -1; } @@ -558,6 +557,11 @@ int tx_callback(hackrf_transfer* transfer) return 0; } +static void flush_callback(void* flush_ctx) +{ + stop_main_loop(); +} + static int update_stats(hackrf_device* device, hackrf_m0_state* state, stats_t* stats) { int result = hackrf_get_m0_state(device, state); @@ -1252,7 +1256,7 @@ int main(int argc, char** argv) preload_bytes = hackrf_get_transfer_queue_depth(device) * hackrf_get_transfer_buffer_size(device); result = hackrf_set_txvga_gain(device, txvga_gain); - result |= hackrf_enable_tx_flush(device, 1); + result |= hackrf_enable_tx_flush(device, flush_callback, NULL); result |= hackrf_start_tx(device, tx_callback, NULL); } @@ -1281,7 +1285,7 @@ int main(int argc, char** argv) .it_value = {.tv_sec = 1, .tv_usec = 0}}; setitimer(ITIMER_REAL, &interval_timer, NULL); #endif - while ((hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false)) { + while (!do_exit) { uint64_t byte_count_now; struct timeval time_now; float time_difference, rate; @@ -1406,11 +1410,6 @@ int main(int argc, char** argv) interval_timer.it_value.tv_sec = 0; setitimer(ITIMER_REAL, &interval_timer, NULL); #endif - if ((transmit || signalsource) && !interrupted) { - // Wait for TX to finish. - hackrf_await_tx_flush(device); - } - result = hackrf_is_streaming(device); if (do_exit) { fprintf(stderr, "\nExiting...\n"); diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 1be670ae..d088f376 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -147,6 +147,8 @@ struct hackrf_device { pthread_mutex_t all_finished_lock; /* used to protect all_finished */ bool flush; struct libusb_transfer* flush_transfer; + hackrf_flush_cb_fn flush_callback; + void* flush_ctx; }; typedef struct { @@ -724,6 +726,8 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d lib_device->active_transfers = 0; lib_device->flush = false; lib_device->flush_transfer = NULL; + lib_device->flush_callback = NULL; + lib_device->flush_ctx = NULL; result = pthread_mutex_init(&lib_device->transfer_lock, NULL); if (result != 0) { @@ -1788,6 +1792,8 @@ static void LIBUSB_CALL hackrf_libusb_flush_callback(struct libusb_transfer* usb device->active_transfers = 0; pthread_cond_broadcast(&device->all_finished_cv); pthread_mutex_unlock(&device->all_finished_lock); + if (device->flush_callback) + device->flush_callback(device->flush_ctx); } static void LIBUSB_CALL @@ -2004,45 +2010,43 @@ int ADDCALL hackrf_start_tx( return result; } -int ADDCALL hackrf_enable_tx_flush(hackrf_device* device, int enable) +ADDAPI int ADDCALL hackrf_enable_tx_flush( + hackrf_device* device, + hackrf_flush_cb_fn callback, + void* flush_ctx) { - if (enable) { - if (device->flush_transfer) { - return HACKRF_SUCCESS; - } + device->flush_callback = callback; + device->flush_ctx = flush_ctx; - 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; + 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; + return HACKRF_SUCCESS; } -int ADDCALL hackrf_await_tx_flush(hackrf_device* device) +ADDAPI int ADDCALL hackrf_disable_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); + libusb_free_transfer(device->flush_transfer); + device->flush_transfer = NULL; + device->flush_callback = NULL; + device->flush_ctx = NULL; return HACKRF_SUCCESS; } diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 8bb9c1ba..f9e4dde0 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -228,6 +228,7 @@ struct hackrf_device_list { typedef struct hackrf_device_list hackrf_device_list_t; typedef int (*hackrf_sample_block_cb_fn)(hackrf_transfer* transfer); +typedef void (*hackrf_flush_cb_fn)(void* flush_ctx); #ifdef __cplusplus extern "C" { @@ -270,8 +271,10 @@ 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_enable_tx_flush( + hackrf_device* device, + hackrf_flush_cb_fn callback, + void* flush_ctx); extern ADDAPI int ADDCALL hackrf_stop_tx(hackrf_device* device); From 98faa5429f965e8896c4a52a7f3b9d757642a006 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 21 Sep 2022 10:05:17 +0100 Subject: [PATCH 02/15] Fix inaccurate statistics in hackrf_transfer. --- host/hackrf-tools/src/hackrf_transfer.c | 102 ++++++++++++++---------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index 091f72ae..e6ac8fca 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -324,12 +324,15 @@ char* u64toa(uint64_t val, t_u64toa* str) static volatile bool do_exit = false; static volatile bool interrupted = false; static volatile bool tx_complete = false; +static volatile bool flush_complete = false; #ifdef _WIN32 static HANDLE interrupt_handle; #endif FILE* file = NULL; -volatile uint32_t byte_count = 0; +volatile uint32_t preload_transfers = 0; +volatile uint32_t completed_byte_count = 0; +volatile uint32_t submitted_byte_count = 0; bool signalsource = false; uint32_t amplitude = 0; @@ -419,8 +422,9 @@ int rx_callback(hackrf_transfer* transfer) sum += value * value; } - /* Update both running totals at approximately the same time. */ - byte_count += transfer->valid_length; + /* Update all running totals at approximately the same time. */ + submitted_byte_count += transfer->valid_length; + completed_byte_count += transfer->valid_length; stream_power += sum; if (limit_num_samples) { @@ -486,19 +490,16 @@ int tx_callback(hackrf_transfer* transfer) return -1; } - /* Accumulate power (magnitude squared). */ - uint64_t sum = 0; - for (i = 0; i < transfer->valid_length; i++) { - int8_t value = transfer->buffer[i]; - sum += value * value; + /* The first few TX callbacks are filling empty transfer buffers, not + * reusing existing ones that have completed. */ + if (preload_transfers > 0) { + transfer->valid_length = 0; + preload_transfers--; } - /* Update both running totals at approximately the same time. */ - byte_count += transfer->valid_length; - stream_power += sum; - /* If the last data was already buffered, stop. */ if (tx_complete) { + completed_byte_count += transfer->valid_length; return -1; } @@ -523,6 +524,20 @@ int tx_callback(hackrf_transfer* transfer) /* Read samples from file. */ bytes_read = fread(transfer->buffer, 1, bytes_to_read, file); } + + /* Accumulate power (magnitude squared). */ + uint64_t sum = 0; + for (i = 0; i < bytes_read; i++) { + int8_t value = transfer->buffer[i]; + sum += value * value; + } + + /* Update all running totals at approximately the same time. */ + completed_byte_count += transfer->valid_length; + submitted_byte_count += bytes_read; + stream_power += sum; + + /* Now set the valid length to the bytes we put in the buffer. */ transfer->valid_length = bytes_read; /* If the sample limit has been reached, this is the last data. */ @@ -544,13 +559,28 @@ int tx_callback(hackrf_transfer* transfer) /* If we get to here, we need to repeat the file until we fill the buffer. */ while (bytes_read < bytes_to_read) { + size_t extra_bytes_read; + + /* Rewind and read more samples. */ rewind(file); - bytes_read += + extra_bytes_read = fread(transfer->buffer + bytes_read, 1, bytes_to_read - bytes_read, file); - transfer->valid_length = bytes_read; + + /* Accumulate power for the additional samples. */ + sum = 0; + for (i = 0; i < extra_bytes_read; i++) { + int8_t value = transfer->buffer[bytes_read + i]; + sum += value * value; + } + bytes_read += extra_bytes_read; + transfer->valid_length += extra_bytes_read; + + /* Update running totals together. */ + submitted_byte_count += extra_bytes_read; + stream_power += sum; } /* Then return normally. */ @@ -559,6 +589,7 @@ int tx_callback(hackrf_transfer* transfer) static void flush_callback(void* flush_ctx) { + flush_complete = true; stop_main_loop(); } @@ -694,7 +725,6 @@ int main(int argc, char** argv) unsigned int lna_gain = 8, vga_gain = 20, txvga_gain = 0; hackrf_m0_state state; stats_t stats = {0, 0}; - static int32_t preload_bytes = 0; while ((opt = getopt(argc, argv, "Hwr:t:f:i:o:m:a:p:s:Fn:b:l:g:x:c:d:C:RS:Bh?")) != EOF) { @@ -1253,8 +1283,7 @@ int main(int argc, char** argv) result |= hackrf_set_lna_gain(device, lna_gain); result |= hackrf_start_rx(device, rx_callback, NULL); } else { - preload_bytes = hackrf_get_transfer_queue_depth(device) * - hackrf_get_transfer_buffer_size(device); + preload_transfers = hackrf_get_transfer_queue_depth(device); result = hackrf_set_txvga_gain(device, txvga_gain); result |= hackrf_enable_tx_flush(device, flush_callback, NULL); result |= hackrf_start_tx(device, tx_callback, NULL); @@ -1286,7 +1315,6 @@ int main(int argc, char** argv) setitimer(ITIMER_REAL, &interval_timer, NULL); #endif while (!do_exit) { - uint64_t byte_count_now; struct timeval time_now; float time_difference, rate; if (stream_size > 0) { @@ -1319,6 +1347,8 @@ int main(int argc, char** argv) } #endif } else { + uint64_t submitted_count_now; + uint64_t completed_count_now; uint64_t stream_power_now; #ifdef _WIN32 // Wait for interval timer event, or interrupt event. @@ -1330,40 +1360,30 @@ int main(int argc, char** argv) #endif gettimeofday(&time_now, NULL); - /* Read and reset both totals at approximately the same time. */ - byte_count_now = byte_count; + /* Read and reset all totals at approximately the same time. */ + submitted_count_now = submitted_byte_count; + completed_count_now = completed_byte_count; stream_power_now = stream_power; - byte_count = 0; + + submitted_byte_count = 0; + completed_byte_count = 0; stream_power = 0; - /* - * The TX callback is called to preload the USB - * transfer buffers at the start of TX. This results in - * invalid statistics collected about the empty buffers - * before any USB transfer is completed. We skip these - * statistics and do not report them to the user. - */ - if (preload_bytes > 0) { - if (preload_bytes > byte_count_now) { - preload_bytes -= byte_count_now; - byte_count_now = 0; - } else { - byte_count_now -= preload_bytes; - preload_bytes = 0; - } + if (flush_complete) { + completed_count_now = submitted_count_now; } time_difference = TimevalDiff(&time_now, &time_start); - rate = (float) byte_count_now / time_difference; - if ((byte_count_now == 0) && (hw_sync)) { + rate = (float) completed_count_now / time_difference; + if ((completed_count_now == 0) && (hw_sync)) { fprintf(stderr, "Waiting for trigger...\n"); } else { double full_scale_ratio = (double) stream_power_now / - (byte_count_now * 127 * 127); + (submitted_count_now * 127 * 127); double dB_full_scale = 10 * log10(full_scale_ratio) + 3.0; fprintf(stderr, "%4.1f MiB / %5.3f sec = %4.1f MiB/second, average power %3.1f dBfs", - (byte_count_now / 1e6f), + (completed_count_now / 1e6f), time_difference, (rate / 1e6f), dB_full_scale); @@ -1393,7 +1413,7 @@ int main(int argc, char** argv) time_start = time_now; - if ((byte_count_now == 0) && (!hw_sync)) { + if ((completed_count_now == 0) && (!hw_sync)) { exit_code = EXIT_FAILURE; fprintf(stderr, "\nCouldn't transfer any bytes for one second.\n"); From cb8fc333aa9b60b095539761ea831fd90ccba1ea Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 21 Sep 2022 11:42:21 +0100 Subject: [PATCH 03/15] Fix inaccurate error message when TX completes near a 1 second boundary. --- 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 e6ac8fca..d665b0a7 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -1413,7 +1413,7 @@ int main(int argc, char** argv) time_start = time_now; - if ((completed_count_now == 0) && (!hw_sync)) { + if ((completed_count_now == 0) && (!hw_sync) && (!tx_complete)) { exit_code = EXIT_FAILURE; fprintf(stderr, "\nCouldn't transfer any bytes for one second.\n"); From 2448796e35a3aec2227829278964c1dc8b21411f Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 00:20:36 +0100 Subject: [PATCH 04/15] Handle failures in fread from TX callback. --- host/hackrf-tools/src/hackrf_transfer.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index d665b0a7..dd8aa9fa 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -523,6 +523,13 @@ int tx_callback(hackrf_transfer* transfer) } else { /* Read samples from file. */ bytes_read = fread(transfer->buffer, 1, bytes_to_read, file); + + /* If no more bytes, error or file empty, treat as end. */ + if (bytes_read == 0) { + completed_byte_count += transfer->valid_length; + tx_complete = true; + return 0; + } } /* Accumulate power (magnitude squared). */ @@ -569,6 +576,12 @@ int tx_callback(hackrf_transfer* transfer) bytes_to_read - bytes_read, file); + /* If no more bytes, error or file empty, use what we have. */ + if (extra_bytes_read == 0) { + tx_complete = true; + return 0; + } + /* Accumulate power for the additional samples. */ sum = 0; for (i = 0; i < extra_bytes_read; i++) { From 14093fe73b6be44de0a0fdbc4d17a358dcb7f48d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 01:53:49 +0100 Subject: [PATCH 05/15] Provide a callback for completion of a TX transfer. --- host/hackrf-tools/src/hackrf_transfer.c | 71 +++++++++---------------- host/libhackrf/src/hackrf.c | 34 ++++++++---- host/libhackrf/src/hackrf.h | 5 ++ 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index dd8aa9fa..4a54db15 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -324,15 +324,12 @@ char* u64toa(uint64_t val, t_u64toa* str) static volatile bool do_exit = false; static volatile bool interrupted = false; static volatile bool tx_complete = false; -static volatile bool flush_complete = false; #ifdef _WIN32 static HANDLE interrupt_handle; #endif FILE* file = NULL; -volatile uint32_t preload_transfers = 0; volatile uint32_t completed_byte_count = 0; -volatile uint32_t submitted_byte_count = 0; bool signalsource = false; uint32_t amplitude = 0; @@ -423,7 +420,6 @@ int rx_callback(hackrf_transfer* transfer) } /* Update all running totals at approximately the same time. */ - submitted_byte_count += transfer->valid_length; completed_byte_count += transfer->valid_length; stream_power += sum; @@ -490,16 +486,8 @@ int tx_callback(hackrf_transfer* transfer) return -1; } - /* The first few TX callbacks are filling empty transfer buffers, not - * reusing existing ones that have completed. */ - if (preload_transfers > 0) { - transfer->valid_length = 0; - preload_transfers--; - } - /* If the last data was already buffered, stop. */ if (tx_complete) { - completed_byte_count += transfer->valid_length; return -1; } @@ -526,24 +514,11 @@ int tx_callback(hackrf_transfer* transfer) /* If no more bytes, error or file empty, treat as end. */ if (bytes_read == 0) { - completed_byte_count += transfer->valid_length; tx_complete = true; return 0; } } - /* Accumulate power (magnitude squared). */ - uint64_t sum = 0; - for (i = 0; i < bytes_read; i++) { - int8_t value = transfer->buffer[i]; - sum += value * value; - } - - /* Update all running totals at approximately the same time. */ - completed_byte_count += transfer->valid_length; - submitted_byte_count += bytes_read; - stream_power += sum; - /* Now set the valid length to the bytes we put in the buffer. */ transfer->valid_length = bytes_read; @@ -582,27 +557,37 @@ int tx_callback(hackrf_transfer* transfer) return 0; } - /* Accumulate power for the additional samples. */ - sum = 0; - for (i = 0; i < extra_bytes_read; i++) { - int8_t value = transfer->buffer[bytes_read + i]; - sum += value * value; - } bytes_read += extra_bytes_read; transfer->valid_length += extra_bytes_read; - - /* Update running totals together. */ - submitted_byte_count += extra_bytes_read; - stream_power += sum; } /* Then return normally. */ return 0; } +static void tx_complete_callback(hackrf_transfer* transfer, int success) +{ + // If a transfer failed to complete, stop the main loop. + if (!success) { + stop_main_loop(); + return; + } + + /* Accumulate power (magnitude squared). */ + uint32_t i; + uint64_t sum = 0; + for (i = 0; i < transfer->valid_length; i++) { + int8_t value = transfer->buffer[i]; + sum += value * value; + } + + /* Update all running totals at approximately the same time. */ + completed_byte_count += transfer->valid_length; + stream_power += sum; +} + static void flush_callback(void* flush_ctx) { - flush_complete = true; stop_main_loop(); } @@ -1296,9 +1281,11 @@ int main(int argc, char** argv) result |= hackrf_set_lna_gain(device, lna_gain); result |= hackrf_start_rx(device, rx_callback, NULL); } else { - preload_transfers = hackrf_get_transfer_queue_depth(device); result = hackrf_set_txvga_gain(device, txvga_gain); result |= hackrf_enable_tx_flush(device, flush_callback, NULL); + result |= hackrf_set_tx_block_complete_callback( + device, + tx_complete_callback); result |= hackrf_start_tx(device, tx_callback, NULL); } @@ -1360,7 +1347,6 @@ int main(int argc, char** argv) } #endif } else { - uint64_t submitted_count_now; uint64_t completed_count_now; uint64_t stream_power_now; #ifdef _WIN32 @@ -1374,25 +1360,18 @@ int main(int argc, char** argv) gettimeofday(&time_now, NULL); /* Read and reset all totals at approximately the same time. */ - submitted_count_now = submitted_byte_count; completed_count_now = completed_byte_count; stream_power_now = stream_power; - - submitted_byte_count = 0; completed_byte_count = 0; stream_power = 0; - if (flush_complete) { - completed_count_now = submitted_count_now; - } - time_difference = TimevalDiff(&time_now, &time_start); rate = (float) completed_count_now / time_difference; if ((completed_count_now == 0) && (hw_sync)) { fprintf(stderr, "Waiting for trigger...\n"); } else { double full_scale_ratio = (double) stream_power_now / - (submitted_count_now * 127 * 127); + (completed_count_now * 127 * 127); double dB_full_scale = 10 * log10(full_scale_ratio) + 3.0; fprintf(stderr, "%4.1f MiB / %5.3f sec = %4.1f MiB/second, average power %3.1f dBfs", diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index d088f376..cc2dd88b 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -148,6 +148,7 @@ struct hackrf_device { bool flush; struct libusb_transfer* flush_transfer; hackrf_flush_cb_fn flush_callback; + hackrf_tx_block_complete_cb_fn tx_completion_callback; void* flush_ctx; }; @@ -728,6 +729,7 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d lib_device->flush_transfer = NULL; lib_device->flush_callback = NULL; lib_device->flush_ctx = NULL; + lib_device->tx_completion_callback = NULL; result = pthread_mutex_init(&lib_device->transfer_lock, NULL); if (result != 0) { @@ -1800,18 +1802,24 @@ static void LIBUSB_CALL hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) { hackrf_device* device = (hackrf_device*) usb_transfer->user_data; - bool resubmit; + bool success, resubmit; int result; - if (usb_transfer->status == LIBUSB_TRANSFER_COMPLETED) { - hackrf_transfer transfer = { - .device = device, - .buffer = usb_transfer->buffer, - .buffer_length = TRANSFER_BUFFER_SIZE, - .valid_length = usb_transfer->actual_length, - .rx_ctx = device->rx_ctx, - .tx_ctx = device->tx_ctx}; + hackrf_transfer transfer = { + .device = device, + .buffer = usb_transfer->buffer, + .buffer_length = TRANSFER_BUFFER_SIZE, + .valid_length = usb_transfer->actual_length, + .rx_ctx = device->rx_ctx, + .tx_ctx = device->tx_ctx}; + success = usb_transfer->status == LIBUSB_TRANSFER_COMPLETED; + + if (device->tx_completion_callback != NULL) { + device->tx_completion_callback(&transfer, success); + } + + if (success) { if (device->streaming && device->callback(&transfer) == 0) { // Take lock to make sure that we don't restart a // transfer whilst cancel_transfers() is in the middle @@ -2010,6 +2018,14 @@ int ADDCALL hackrf_start_tx( return result; } +ADDAPI int ADDCALL hackrf_set_tx_block_complete_callback( + hackrf_device* device, + hackrf_tx_block_complete_cb_fn callback) +{ + device->tx_completion_callback = callback; + return HACKRF_SUCCESS; +} + ADDAPI int ADDCALL hackrf_enable_tx_flush( hackrf_device* device, hackrf_flush_cb_fn callback, diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index f9e4dde0..73d3bcff 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -228,6 +228,7 @@ struct hackrf_device_list { typedef struct hackrf_device_list hackrf_device_list_t; typedef int (*hackrf_sample_block_cb_fn)(hackrf_transfer* transfer); +typedef void (*hackrf_tx_block_complete_cb_fn)(hackrf_transfer* transfer, int); typedef void (*hackrf_flush_cb_fn)(void* flush_ctx); #ifdef __cplusplus @@ -271,6 +272,10 @@ extern ADDAPI int ADDCALL hackrf_start_tx( hackrf_sample_block_cb_fn callback, void* tx_ctx); +extern ADDAPI int ADDCALL hackrf_set_tx_block_complete_callback( + hackrf_device* device, + hackrf_tx_block_complete_cb_fn callback); + extern ADDAPI int ADDCALL hackrf_enable_tx_flush( hackrf_device* device, hackrf_flush_cb_fn callback, From eae7049284924d85b0ca7ccd016d1ea24060b919 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 02:08:28 +0100 Subject: [PATCH 06/15] Fix empty report when TX ends soon after the last report. --- host/hackrf-tools/src/hackrf_transfer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index 4a54db15..489f7dfa 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -324,6 +324,7 @@ char* u64toa(uint64_t val, t_u64toa* str) static volatile bool do_exit = false; static volatile bool interrupted = false; static volatile bool tx_complete = false; +static volatile bool flush_complete = false; #ifdef _WIN32 static HANDLE interrupt_handle; #endif @@ -588,6 +589,7 @@ static void tx_complete_callback(hackrf_transfer* transfer, int success) static void flush_callback(void* flush_ctx) { + flush_complete = true; stop_main_loop(); } @@ -1369,7 +1371,7 @@ int main(int argc, char** argv) rate = (float) completed_count_now / time_difference; if ((completed_count_now == 0) && (hw_sync)) { fprintf(stderr, "Waiting for trigger...\n"); - } else { + } else if (!((completed_count_now == 0) && (flush_complete))) { double full_scale_ratio = (double) stream_power_now / (completed_count_now * 127 * 127); double dB_full_scale = 10 * log10(full_scale_ratio) + 3.0; @@ -1405,7 +1407,8 @@ int main(int argc, char** argv) time_start = time_now; - if ((completed_count_now == 0) && (!hw_sync) && (!tx_complete)) { + if ((completed_count_now == 0) && (!hw_sync) && + (!flush_complete)) { exit_code = EXIT_FAILURE; fprintf(stderr, "\nCouldn't transfer any bytes for one second.\n"); From c089bb0b888e6fb1489173e98366dd1a759e2290 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 10:14:36 +0100 Subject: [PATCH 07/15] Fix handling of EOF and error conditions after fread. --- host/hackrf-tools/src/hackrf_transfer.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index 489f7dfa..c3680263 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -513,8 +513,15 @@ int tx_callback(hackrf_transfer* transfer) /* Read samples from file. */ bytes_read = fread(transfer->buffer, 1, bytes_to_read, file); - /* If no more bytes, error or file empty, treat as end. */ - if (bytes_read == 0) { + /* Terminate immediately on error. */ + if (ferror(file)) { + fprintf(stderr, "Could not read input file.\n"); + stop_main_loop(); + return -1; + } + + /* Finish TX if no more data can be read. */ + if ((bytes_read == 0) && (ftell(file) < 1)) { tx_complete = true; return 0; } From 770416f122d95ba9a5d69e19bb306db2c49cddb2 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 12:41:54 +0100 Subject: [PATCH 08/15] Cancel flush transfer if shutting down. --- host/libhackrf/src/hackrf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index cc2dd88b..ec9ac084 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -239,6 +239,9 @@ static int cancel_transfers(hackrf_device* device) } } + if (device->flush_transfer != NULL) + libusb_cancel_transfer(device->flush_transfer); + device->transfers_setup = false; device->flush = false; From 1484b78c05898ba3621338fcd1857ff417c5bc26 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 12:40:15 +0100 Subject: [PATCH 09/15] Expand lock scope in libusb transfer callback. This is required since cancel_transfers will also cancel the flush. --- host/libhackrf/src/hackrf.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index ec9ac084..acbd4d1c 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -1805,7 +1805,7 @@ static void LIBUSB_CALL hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) { hackrf_device* device = (hackrf_device*) usb_transfer->user_data; - bool success, resubmit; + bool success, resubmit = false; int result; hackrf_transfer transfer = { @@ -1822,13 +1822,12 @@ hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) device->tx_completion_callback(&transfer, success); } + // Take lock to make sure that we don't restart a + // transfer whilst cancel_transfers() is in the middle + // of stopping them. + pthread_mutex_lock(&device->transfer_lock); if (success) { if (device->streaming && device->callback(&transfer) == 0) { - // Take lock to make sure that we don't restart a - // transfer whilst cancel_transfers() is in the middle - // of stopping them. - pthread_mutex_lock(&device->transfer_lock); - if ((resubmit = device->transfers_setup)) { if (usb_transfer->endpoint == TX_ENDPOINT_ADDRESS) { usb_transfer->length = transfer.valid_length; @@ -1839,13 +1838,6 @@ hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) } result = libusb_submit_transfer(usb_transfer); } - - // Now we can release the lock. Our transfer was either - // cancelled or restarted, not both. - pthread_mutex_unlock(&device->transfer_lock); - - if (resubmit && result == LIBUSB_SUCCESS) - return; } else if (device->flush) { result = libusb_submit_transfer(device->flush_transfer); if (result != LIBUSB_SUCCESS) { @@ -1857,9 +1849,15 @@ hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) device->streaming = false; device->flush = false; } + // Now we can release the lock. Our transfer was either + // cancelled or restarted, not both. + pthread_mutex_unlock(&device->transfer_lock); - // Unless we resubmitted this transfer and returned above, - // it's now finished. + // If a data transfer was resubmitted successfully, we're done. + if (resubmit && result == LIBUSB_SUCCESS) + return; + + // Otherwise, a transfer has now finished. transfer_finished(device, usb_transfer); } From 4810d1f9922a88778f1afaf5b8136125034b3f0d Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 12:46:42 +0100 Subject: [PATCH 10/15] Fold transfer_finished function into libusb transfer callback. --- host/libhackrf/src/hackrf.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index acbd4d1c..9dbb338c 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -1768,26 +1768,6 @@ static void* transfer_threadproc(void* arg) return NULL; } -static void transfer_finished( - struct hackrf_device* device, - struct libusb_transfer* finished_transfer) -{ - // 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) { - if (!device->flush) { - 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. @@ -1857,8 +1837,20 @@ hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) if (resubmit && result == LIBUSB_SUCCESS) return; - // Otherwise, a transfer has now finished. - transfer_finished(device, usb_transfer); + // Otherwise, no further calls should be made to the TX callback. + 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) { + if (!device->flush) { + device->active_transfers = 0; + pthread_cond_broadcast(&device->all_finished_cv); + } + } else { + device->active_transfers--; + } + pthread_mutex_unlock(&device->all_finished_lock); } static int kill_transfer_thread(hackrf_device* device) From 0becbc6b4ae1493b51becd63e34b2fb96256ed9e Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 12:54:23 +0100 Subject: [PATCH 11/15] Indicate success or failure to flush callback. --- host/hackrf-tools/src/hackrf_transfer.c | 6 ++++-- host/libhackrf/src/hackrf.c | 7 +++++-- host/libhackrf/src/hackrf.h | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index c3680263..ec74b95b 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -594,9 +594,11 @@ static void tx_complete_callback(hackrf_transfer* transfer, int success) stream_power += sum; } -static void flush_callback(void* flush_ctx) +static void flush_callback(void* flush_ctx, int success) { - flush_complete = true; + if (success) { + flush_complete = true; + } stop_main_loop(); } diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 9dbb338c..0c44b75c 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -1770,15 +1770,18 @@ static void* transfer_threadproc(void* arg) static void LIBUSB_CALL hackrf_libusb_flush_callback(struct libusb_transfer* usb_transfer) { - // TX buffer is now flushed, so proceed with signalling completion. + bool success = usb_transfer->status == LIBUSB_TRANSFER_COMPLETED; + + // All transfers have now ended, 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); + if (device->flush_callback) - device->flush_callback(device->flush_ctx); + device->flush_callback(device->flush_ctx, success); } static void LIBUSB_CALL diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 73d3bcff..79f140f7 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -229,7 +229,7 @@ typedef struct hackrf_device_list hackrf_device_list_t; typedef int (*hackrf_sample_block_cb_fn)(hackrf_transfer* transfer); typedef void (*hackrf_tx_block_complete_cb_fn)(hackrf_transfer* transfer, int); -typedef void (*hackrf_flush_cb_fn)(void* flush_ctx); +typedef void (*hackrf_flush_cb_fn)(void* flush_ctx, int); #ifdef __cplusplus extern "C" { From db33e1bdafe28c11db5e15170f44aa5c6631d3c5 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 22 Sep 2022 13:08:04 +0100 Subject: [PATCH 12/15] Revert rename of byte_count now that we only have one count. --- host/hackrf-tools/src/hackrf_transfer.c | 31 ++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index ec74b95b..151b2493 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -330,7 +330,7 @@ static HANDLE interrupt_handle; #endif FILE* file = NULL; -volatile uint32_t completed_byte_count = 0; +volatile uint32_t byte_count = 0; bool signalsource = false; uint32_t amplitude = 0; @@ -420,8 +420,8 @@ int rx_callback(hackrf_transfer* transfer) sum += value * value; } - /* Update all running totals at approximately the same time. */ - completed_byte_count += transfer->valid_length; + /* Update both running totals at approximately the same time. */ + byte_count += transfer->valid_length; stream_power += sum; if (limit_num_samples) { @@ -589,8 +589,8 @@ static void tx_complete_callback(hackrf_transfer* transfer, int success) sum += value * value; } - /* Update all running totals at approximately the same time. */ - completed_byte_count += transfer->valid_length; + /* Update both running totals at approximately the same time. */ + byte_count += transfer->valid_length; stream_power += sum; } @@ -1358,7 +1358,7 @@ int main(int argc, char** argv) } #endif } else { - uint64_t completed_count_now; + uint64_t byte_count_now; uint64_t stream_power_now; #ifdef _WIN32 // Wait for interval timer event, or interrupt event. @@ -1370,23 +1370,23 @@ int main(int argc, char** argv) #endif gettimeofday(&time_now, NULL); - /* Read and reset all totals at approximately the same time. */ - completed_count_now = completed_byte_count; + /* Read and reset both totals at approximately the same time. */ + byte_count_now = byte_count; stream_power_now = stream_power; - completed_byte_count = 0; + byte_count = 0; stream_power = 0; time_difference = TimevalDiff(&time_now, &time_start); - rate = (float) completed_count_now / time_difference; - if ((completed_count_now == 0) && (hw_sync)) { + rate = (float) byte_count_now / time_difference; + if ((byte_count_now == 0) && (hw_sync)) { fprintf(stderr, "Waiting for trigger...\n"); - } else if (!((completed_count_now == 0) && (flush_complete))) { + } else if (!((byte_count_now == 0) && (flush_complete))) { double full_scale_ratio = (double) stream_power_now / - (completed_count_now * 127 * 127); + (byte_count_now * 127 * 127); double dB_full_scale = 10 * log10(full_scale_ratio) + 3.0; fprintf(stderr, "%4.1f MiB / %5.3f sec = %4.1f MiB/second, average power %3.1f dBfs", - (completed_count_now / 1e6f), + (byte_count_now / 1e6f), time_difference, (rate / 1e6f), dB_full_scale); @@ -1416,8 +1416,7 @@ int main(int argc, char** argv) time_start = time_now; - if ((completed_count_now == 0) && (!hw_sync) && - (!flush_complete)) { + if ((byte_count_now == 0) && (!hw_sync) && (!flush_complete)) { exit_code = EXIT_FAILURE; fprintf(stderr, "\nCouldn't transfer any bytes for one second.\n"); From 8fbaf1336c389aa67af1a781dcb5a45e99c94a34 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 23 Sep 2022 05:17:55 -0400 Subject: [PATCH 13/15] Improve handling of file read results. --- host/hackrf-tools/src/hackrf_transfer.c | 35 ++++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index 151b2493..0b15b627 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -513,17 +513,18 @@ int tx_callback(hackrf_transfer* transfer) /* Read samples from file. */ bytes_read = fread(transfer->buffer, 1, bytes_to_read, file); - /* Terminate immediately on error. */ - if (ferror(file)) { - fprintf(stderr, "Could not read input file.\n"); - stop_main_loop(); - return -1; - } - - /* Finish TX if no more data can be read. */ - if ((bytes_read == 0) && (ftell(file) < 1)) { - tx_complete = true; - return 0; + /* If no more bytes, error or file empty, terminate. */ + if (bytes_read == 0) { + /* Report any error. */ + if (ferror(file)) { + fprintf(stderr, "Could not read input file.\n"); + stop_main_loop(); + return -1; + } + if (ftell(file) < 1) { + stop_main_loop(); + return -1; + } } } @@ -561,8 +562,16 @@ int tx_callback(hackrf_transfer* transfer) /* If no more bytes, error or file empty, use what we have. */ if (extra_bytes_read == 0) { - tx_complete = true; - return 0; + /* Report any error. */ + if (ferror(file)) { + fprintf(stderr, "Could not read input file.\n"); + tx_complete = true; + return 0; + } + if (ftell(file) < 1) { + tx_complete = true; + return 0; + } } bytes_read += extra_bytes_read; From 45504ff397103751df3f51799374d8d4555d5c7e Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 23 Sep 2022 06:40:14 -0400 Subject: [PATCH 14/15] Don't repeat file that can't advance its position. This avoids a confusing condition when hackrf_transfer is reading from stdin with -R in which it does not repeat previous data but repeatedly restarts reading new data after the user types Ctrl-D to indicate EOF. The -R option should repeat previous data or should do nothing if the input file cannot be rewound. --- 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 0b15b627..ec4e67a2 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -543,7 +543,7 @@ int tx_callback(hackrf_transfer* transfer) } /* Otherwise, the file ran short. If not repeating, this is the last data. */ - if (!repeat) { + if ((!repeat) || (ftell(file) < 1)) { tx_complete = true; return 0; } From 2e73a4f2dbfcd1de07613124f7d2ea68fd8a7f97 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 23 Sep 2022 12:46:49 +0100 Subject: [PATCH 15/15] If TX callback sets valid_length to 0, treat as end of data. --- host/libhackrf/src/hackrf.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 0c44b75c..46385ebf 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -367,7 +367,8 @@ static int prepare_transfers( .rx_ctx = device->rx_ctx, .tx_ctx = device->tx_ctx, }; - if (device->callback(&transfer) == 0) { + if ((device->callback(&transfer) == 0) && + (transfer.valid_length > 0)) { device->transfers[transfer_index]->length = transfer.valid_length; ready_transfers++; @@ -1810,7 +1811,8 @@ hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) // of stopping them. pthread_mutex_lock(&device->transfer_lock); if (success) { - if (device->streaming && device->callback(&transfer) == 0) { + if (device->streaming && (device->callback(&transfer) == 0) && + (transfer.valid_length > 0)) { if ((resubmit = device->transfers_setup)) { if (usb_transfer->endpoint == TX_ENDPOINT_ADDRESS) { usb_transfer->length = transfer.valid_length;