diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index f9394914..30226162 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -144,7 +144,6 @@ struct hackrf_device { pthread_mutex_t transfer_lock; /* must be held to cancel or restart transfers */ 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; hackrf_flush_cb_fn flush_callback; @@ -245,22 +244,14 @@ static int cancel_transfers(hackrf_device* device) device->transfers_setup = false; device->flush = false; - // Now release the lock. It's possible that some transfers were - // already complete when we called libusb_cancel_transfer() on - // them, and they may still get a callback. But the callback - // won't restart a transfer now that the transfers_setup flag - // is set to false. - pthread_mutex_unlock(&device->transfer_lock); - // Now wait for the transfer thread to signal that all transfers // have finished, either by completing or being fully cancelled. - pthread_mutex_lock(&device->all_finished_lock); while (device->active_transfers > 0) { pthread_cond_wait( &device->all_finished_cv, - &device->all_finished_lock); + &device->transfer_lock); } - pthread_mutex_unlock(&device->all_finished_lock); + pthread_mutex_unlock(&device->transfer_lock); return HACKRF_SUCCESS; } else { @@ -382,7 +373,11 @@ static int prepare_transfers( ready_transfers = TRANSFER_COUNT; } - // Now everything is ready, go ahead and submit the ready transfers. + // Now everything is ready, go ahead and submit the ready transfers. We must hold + // the transfer lock whilst doing this, so that completion callbacks cannot resubmit + // any transfers until all transfers have been initially submitted. + pthread_mutex_lock(&device->transfer_lock); + for (transfer_index = 0; transfer_index < ready_transfers; transfer_index++) { struct libusb_transfer* transfer = device->transfers[transfer_index]; transfer->endpoint = endpoint_address; @@ -397,27 +392,35 @@ static int prepare_transfers( error = libusb_submit_transfer(transfer); if (error != 0) { last_libusb_error = error; - return HACKRF_ERROR_LIBUSB; + break; } device->active_transfers++; } - // We should only continue streaming if all transfers were made ready - // and submitted above. Otherwise, set streaming to false so that the - // libusb completion callback won't submit further transfers. - device->streaming = (ready_transfers == TRANSFER_COUNT); - device->transfers_setup = true; + if (error == 0) { + // We should only continue streaming if all transfers were made ready + // and submitted above. Otherwise, set streaming to false so that the + // libusb completion callback won't submit further transfers. + device->streaming = (ready_transfers == TRANSFER_COUNT); + device->transfers_setup = true; - // If we're not continuing streaming, follow up with a flush if needed. - if (!device->streaming && device->flush) { - error = libusb_submit_transfer(device->flush_transfer); - if (error != 0) { - last_libusb_error = error; - return HACKRF_ERROR_LIBUSB; + // If we're not continuing streaming, follow up with a flush if needed. + if (!device->streaming && device->flush) { + error = libusb_submit_transfer(device->flush_transfer); + if (error != 0) { + last_libusb_error = error; + } } } - return HACKRF_SUCCESS; + // Now we can release the transfer lock. + pthread_mutex_unlock(&device->transfer_lock); + + if (error == 0) { + return HACKRF_SUCCESS; + } else { + return HACKRF_ERROR_LIBUSB; + } } static int detach_kernel_drivers(libusb_device_handle* usb_device_handle) @@ -743,14 +746,6 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d return HACKRF_ERROR_THREAD; } - result = pthread_mutex_init(&lib_device->all_finished_lock, NULL); - if (result != 0) { - free(lib_device); - libusb_release_interface(usb_device, 0); - libusb_close(usb_device); - return HACKRF_ERROR_THREAD; - } - result = pthread_cond_init(&lib_device->all_finished_cv, NULL); if (result != 0) { free(lib_device); @@ -1775,11 +1770,11 @@ static void LIBUSB_CALL hackrf_libusb_flush_callback(struct libusb_transfer* usb // 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); + pthread_mutex_lock(&device->transfer_lock); device->flush = false; device->active_transfers = 0; pthread_cond_broadcast(&device->all_finished_cv); - pthread_mutex_unlock(&device->all_finished_lock); + pthread_mutex_unlock(&device->transfer_lock); if (device->flush_callback) device->flush_callback(device->flush_ctx, success); @@ -1834,28 +1829,26 @@ hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) device->streaming = false; device->flush = false; } + + // If a data transfer was resubmitted successfully, we're done. + if (!resubmit || result != LIBUSB_SUCCESS) { + // 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. + if (device->active_transfers == 1) { + if (!device->flush) { + device->active_transfers = 0; + pthread_cond_broadcast(&device->all_finished_cv); + } + } else { + device->active_transfers--; + } + } + // Now we can release the lock. Our transfer was either // cancelled or restarted, not both. pthread_mutex_unlock(&device->transfer_lock); - - // If a data transfer was resubmitted successfully, we're done. - if (resubmit && result == LIBUSB_SUCCESS) - return; - - // 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) @@ -2109,7 +2102,6 @@ int ADDCALL hackrf_close(hackrf_device* device) pthread_mutex_destroy(&device->transfer_lock); pthread_cond_destroy(&device->all_finished_cv); - pthread_mutex_destroy(&device->all_finished_lock); free(device); }