Merge pull request #1139 from martinling/tx-flush

Support flushing the device TX buffer before ending transmission
This commit is contained in:
Michael Ossmann
2022-09-14 05:45:40 -04:00
committed by GitHub
3 changed files with 91 additions and 2 deletions

View File

@ -322,6 +322,7 @@ char* u64toa(uint64_t val, t_u64toa* str)
} }
static volatile bool do_exit = false; static volatile bool do_exit = false;
static volatile bool interrupted = false;
#ifdef _WIN32 #ifdef _WIN32
static HANDLE interrupt_handle; static HANDLE interrupt_handle;
#endif #endif
@ -642,6 +643,7 @@ static hackrf_device* device = NULL;
BOOL WINAPI sighandler(int signum) BOOL WINAPI sighandler(int signum)
{ {
if (CTRL_C_EVENT == signum) { if (CTRL_C_EVENT == signum) {
interrupted = true;
fprintf(stderr, "Caught signal %d\n", signum); fprintf(stderr, "Caught signal %d\n", signum);
stop_main_loop(); stop_main_loop();
return TRUE; return TRUE;
@ -651,6 +653,7 @@ BOOL WINAPI sighandler(int signum)
#else #else
void sigint_callback_handler(int signum) void sigint_callback_handler(int signum)
{ {
interrupted = true;
fprintf(stderr, "Caught signal %d\n", signum); fprintf(stderr, "Caught signal %d\n", signum);
do_exit = true; do_exit = true;
} }
@ -1176,6 +1179,7 @@ int main(int argc, char** argv)
result |= hackrf_start_rx(device, rx_callback, NULL); result |= hackrf_start_rx(device, rx_callback, NULL);
} else { } else {
result = hackrf_set_txvga_gain(device, txvga_gain); result = hackrf_set_txvga_gain(device, txvga_gain);
result |= hackrf_enable_tx_flush(device, 1);
result |= hackrf_start_tx(device, tx_callback, NULL); result |= hackrf_start_tx(device, tx_callback, NULL);
} }
if (result != HACKRF_SUCCESS) { if (result != HACKRF_SUCCESS) {
@ -1403,6 +1407,10 @@ int main(int argc, char** argv)
interval_timer.it_value.tv_sec = 0; interval_timer.it_value.tv_sec = 0;
setitimer(ITIMER_REAL, &interval_timer, NULL); setitimer(ITIMER_REAL, &interval_timer, NULL);
#endif #endif
if ((transmit || signalsource) && !interrupted) {
// Wait for TX to finish.
hackrf_await_tx_flush(device);
}
result = hackrf_is_streaming(device); result = hackrf_is_streaming(device);
if (do_exit) { if (do_exit) {

View File

@ -123,6 +123,7 @@ typedef enum {
#define TRANSFER_COUNT 4 #define TRANSFER_COUNT 4
#define TRANSFER_BUFFER_SIZE 262144 #define TRANSFER_BUFFER_SIZE 262144
#define DEVICE_BUFFER_SIZE 32768
#define USB_MAX_SERIAL_LENGTH 32 #define USB_MAX_SERIAL_LENGTH 32
struct hackrf_device { struct hackrf_device {
@ -142,6 +143,8 @@ struct hackrf_device {
volatile int active_transfers; /* number of active transfers */ volatile int active_transfers; /* number of active transfers */
pthread_cond_t all_finished_cv; /* signalled when all transfers have finished */ pthread_cond_t all_finished_cv; /* signalled when all transfers have finished */
pthread_mutex_t all_finished_lock; /* used to protect all_finished */ pthread_mutex_t all_finished_lock; /* used to protect all_finished */
bool flush;
struct libusb_transfer* flush_transfer;
}; };
typedef struct { typedef struct {
@ -272,6 +275,9 @@ static int free_transfers(hackrf_device* device)
free(device->transfers); free(device->transfers);
device->transfers = NULL; device->transfers = NULL;
} }
libusb_free_transfer(device->flush_transfer);
return HACKRF_SUCCESS; return HACKRF_SUCCESS;
} }
@ -695,6 +701,8 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d
lib_device->streaming = false; lib_device->streaming = false;
lib_device->do_exit = false; lib_device->do_exit = false;
lib_device->active_transfers = 0; lib_device->active_transfers = 0;
lib_device->flush = false;
lib_device->flush_transfer = NULL;
result = pthread_mutex_init(&lib_device->transfer_lock, NULL); result = pthread_mutex_init(&lib_device->transfer_lock, NULL);
if (result != 0) { if (result != 0) {
@ -1734,20 +1742,44 @@ static void transfer_finished(
struct hackrf_device* device, struct hackrf_device* device,
struct libusb_transfer* finished_transfer) struct libusb_transfer* finished_transfer)
{ {
int result;
// If a transfer finished for any reason, we're shutting down. // If a transfer finished for any reason, we're shutting down.
device->streaming = false; device->streaming = false;
// If this is the last transfer, signal that all are now finished. // If this is the last transfer, signal that all are now finished.
pthread_mutex_lock(&device->all_finished_lock); pthread_mutex_lock(&device->all_finished_lock);
if (device->active_transfers == 1) { if (device->active_transfers == 1) {
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; device->active_transfers = 0;
pthread_cond_signal(&device->all_finished_cv); pthread_cond_broadcast(&device->all_finished_cv);
}
} else {
device->active_transfers = 0;
pthread_cond_broadcast(&device->all_finished_cv);
}
} else { } else {
device->active_transfers--; device->active_transfers--;
} }
pthread_mutex_unlock(&device->all_finished_lock); 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 static void LIBUSB_CALL
hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer)
{ {
@ -1935,6 +1967,9 @@ int ADDCALL hackrf_start_tx(
{ {
int result; int result;
const uint8_t endpoint_address = TX_ENDPOINT_ADDRESS; 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); result = hackrf_set_transceiver_mode(device, HACKRF_TRANSCEIVER_MODE_TRANSMIT);
if (result == HACKRF_SUCCESS) { if (result == HACKRF_SUCCESS) {
device->tx_ctx = tx_ctx; device->tx_ctx = tx_ctx;
@ -1943,6 +1978,49 @@ int ADDCALL hackrf_start_tx(
return result; 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. * Stop any pending transmit.
* *

View File

@ -238,6 +238,9 @@ extern ADDAPI int ADDCALL hackrf_start_tx(
hackrf_sample_block_cb_fn callback, hackrf_sample_block_cb_fn callback,
void* tx_ctx); 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_stop_tx(hackrf_device* device);
extern ADDAPI int ADDCALL hackrf_get_m0_state( extern ADDAPI int ADDCALL hackrf_get_m0_state(