cc3dsfs/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_acquisition.cpp

234 lines
10 KiB
C++

#include "3dscapture_ftd3_libusb_acquisition.hpp"
#include "3dscapture_ftd3_libusb_comms.hpp"
#include "3dscapture_ftd3_compatibility.hpp"
#include "3dscapture_ftd3_shared_general.hpp"
#include "devicecapture.hpp"
#include <thread>
#include <libusb.h>
#include "usb_generic.hpp"
#define MAX_TIME_WAIT 0.5
// This was created to remove the dependency from the FTD3XX library
// under Linux and MacOS (as well as WinUSB).
// There were issues with said library which forced the use of an
// older release (double free when a device is disconnected, for example).
// There were problems with automatic downloads being blocked as well.
// This is something which could not go on indefinitively.
// Optimize data capturing for libusb with async transfers and callbacks,
// instead of waiting for the usb transfer to be done...
struct FTD3LibusbCaptureReceivedData {
bool in_use;
uint32_t index;
CaptureData* capture_data;
CaptureReceived buffer;
uint32_t* last_index;
std::chrono::time_point<std::chrono::high_resolution_clock> *clock_start;
SharedConsumerMutex *is_buffer_free_shared_mutex;
int* status;
ftd3_async_callback_data cb_data;
};
static void ftd3_libusb_read_frame_cb(void* user_data, int transfer_length, int transfer_status);
static void ftd3_device_usb_thread_function(bool* usb_thread_run) {
if(!usb_is_initialized())
return;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 300000;
while(*usb_thread_run)
libusb_handle_events_timeout_completed(get_usb_ctx(), &tv, NULL);
}
static void ftd3_libusb_start_thread(std::thread* thread_ptr, bool* usb_thread_run) {
if(!usb_is_initialized())
return;
*usb_thread_run = true;
*thread_ptr = std::thread(ftd3_device_usb_thread_function, usb_thread_run);
}
static void ftd3_libusb_close_thread(std::thread* thread_ptr, bool* usb_thread_run) {
if(!usb_is_initialized())
return;
*usb_thread_run = false;
thread_ptr->join();
}
static int get_ftd3_libusb_status(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
return *ftd3_libusb_capture_recv_data[0].status;
}
static void error_ftd3_libusb_status(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data, int error_val) {
if((error_val == 0) || (get_ftd3_libusb_status(ftd3_libusb_capture_recv_data) == 0))
*ftd3_libusb_capture_recv_data[0].status = error_val;
}
static void reset_ftd3_libusb_status(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
error_ftd3_libusb_status(ftd3_libusb_capture_recv_data, 0);
}
static void ftd3_libusb_read_frame_request(CaptureData* capture_data, FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data, uint32_t index, int pipe) {
if(ftd3_libusb_capture_recv_data == NULL)
return;
if((*ftd3_libusb_capture_recv_data->status) < 0)
return;
ftd3_libusb_capture_recv_data->index = index;
ftd3_libusb_capture_recv_data->cb_data.function = ftd3_libusb_read_frame_cb;
ftd3_libusb_async_in_start((ftd3_device_device_handlers*)capture_data->handle, pipe, MAX_TIME_WAIT * 1000, (uint8_t*)&ftd3_libusb_capture_recv_data->buffer, ftd3_get_capture_size(capture_data), &ftd3_libusb_capture_recv_data->cb_data);
}
static void end_ftd3_libusb_read_frame_cb(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
ftd3_libusb_capture_recv_data->in_use = false;
ftd3_libusb_capture_recv_data->is_buffer_free_shared_mutex->specific_unlock(ftd3_libusb_capture_recv_data->cb_data.internal_index);
}
static void ftd3_libusb_read_frame_cb(void* user_data, int transfer_length, int transfer_status) {
FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data = (FTD3LibusbCaptureReceivedData*)user_data;
if((*ftd3_libusb_capture_recv_data->status) < 0)
return end_ftd3_libusb_read_frame_cb(ftd3_libusb_capture_recv_data);
if(transfer_status != LIBUSB_TRANSFER_COMPLETED) {
*ftd3_libusb_capture_recv_data->status = LIBUSB_ERROR_OTHER;
return end_ftd3_libusb_read_frame_cb(ftd3_libusb_capture_recv_data);
}
if(((int32_t)(ftd3_libusb_capture_recv_data->index - (*ftd3_libusb_capture_recv_data->last_index))) <= 0) {
//*ftd3_libusb_capture_recv_data->status = LIBUSB_ERROR_INTERRUPTED;
return end_ftd3_libusb_read_frame_cb(ftd3_libusb_capture_recv_data);
}
*ftd3_libusb_capture_recv_data->last_index = ftd3_libusb_capture_recv_data->index;
data_output_update(&ftd3_libusb_capture_recv_data->buffer, transfer_length, ftd3_libusb_capture_recv_data->capture_data, *ftd3_libusb_capture_recv_data->clock_start);
end_ftd3_libusb_read_frame_cb(ftd3_libusb_capture_recv_data);
}
static int ftd3_libusb_get_num_free_buffers(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
int num_free = 0;
for(int i = 0; i < FTD3_CONCURRENT_BUFFERS; i++)
if(!ftd3_libusb_capture_recv_data[i].in_use)
num_free += 1;
return num_free;
}
static void close_all_reads_error(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data, bool &async_read_closed) {
if(get_ftd3_libusb_status(ftd3_libusb_capture_recv_data) >= 0)
return;
if(!async_read_closed) {
for (int i = 0; i < FTD3_CONCURRENT_BUFFERS; i++)
ftd3_libusb_cancell_callback(&ftd3_libusb_capture_recv_data[i].cb_data);
async_read_closed = true;
}
}
static bool has_too_much_time_passed(const std::chrono::time_point<std::chrono::high_resolution_clock> &start_time) {
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - start_time;
return diff.count() > MAX_TIME_WAIT;
}
static void error_too_much_time_passed(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data, bool &async_read_closed, const std::chrono::time_point<std::chrono::high_resolution_clock> &start_time) {
if(has_too_much_time_passed(start_time)) {
error_ftd3_libusb_status(ftd3_libusb_capture_recv_data, -1);
close_all_reads_error(ftd3_libusb_capture_recv_data, async_read_closed);
}
}
static void wait_all_ftd3_libusb_buffers_free(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
if(ftd3_libusb_capture_recv_data == NULL)
return;
bool async_read_closed = false;
close_all_reads_error(ftd3_libusb_capture_recv_data, async_read_closed);
const auto start_time = std::chrono::high_resolution_clock::now();
for(int i = 0; i < FTD3_CONCURRENT_BUFFERS; i++)
while(ftd3_libusb_capture_recv_data[i].in_use) {
error_too_much_time_passed(ftd3_libusb_capture_recv_data, async_read_closed, start_time);
ftd3_libusb_capture_recv_data[i].is_buffer_free_shared_mutex->specific_timed_lock(i);
}
}
static void wait_one_ftd3_libusb_buffer_free(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
bool done = false;
const auto start_time = std::chrono::high_resolution_clock::now();
while(!done) {
for(int i = 0; i < FTD3_CONCURRENT_BUFFERS; i++) {
if(!ftd3_libusb_capture_recv_data[i].in_use)
done = true;
}
if(!done) {
if(has_too_much_time_passed(start_time))
return;
if(get_ftd3_libusb_status(ftd3_libusb_capture_recv_data) < 0)
return;
int dummy = 0;
ftd3_libusb_capture_recv_data[0].is_buffer_free_shared_mutex->general_timed_lock(&dummy);
}
}
}
static bool ftd3_libusb_are_buffers_all_free(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
return ftd3_libusb_get_num_free_buffers(ftd3_libusb_capture_recv_data) == FTD3_CONCURRENT_BUFFERS;
}
static FTD3LibusbCaptureReceivedData* ftd3_libusb_get_free_buffer(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data) {
wait_one_ftd3_libusb_buffer_free(ftd3_libusb_capture_recv_data);
if(get_ftd3_libusb_status(ftd3_libusb_capture_recv_data) < 0)
return NULL;
for(int i = 0; i < FTD3_CONCURRENT_BUFFERS; i++)
if(!ftd3_libusb_capture_recv_data[i].in_use) {
ftd3_libusb_capture_recv_data[i].is_buffer_free_shared_mutex->specific_try_lock(i);
ftd3_libusb_capture_recv_data[i].in_use = true;
return &ftd3_libusb_capture_recv_data[i];
}
return NULL;
}
static void ftd3_libusb_capture_main_loop_processing(FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data, int pipe) {
int index = 0;
CaptureData* capture_data = ftd3_libusb_capture_recv_data[0].capture_data;
for(int i = 0; i < FTD3_CONCURRENT_BUFFERS; i++)
ftd3_libusb_read_frame_request(capture_data, ftd3_libusb_get_free_buffer(ftd3_libusb_capture_recv_data), index++, pipe);
while(capture_data->status.connected && capture_data->status.running) {
if(get_ftd3_libusb_status(ftd3_libusb_capture_recv_data) < 0) {
capture_error_print(true, capture_data, "Disconnected: Read failed");
return;
}
FTD3LibusbCaptureReceivedData* free_buffer = ftd3_libusb_get_free_buffer(ftd3_libusb_capture_recv_data);
ftd3_libusb_read_frame_request(capture_data, free_buffer, index++, pipe);
}
}
void ftd3_libusb_capture_main_loop(CaptureData* capture_data, int pipe) {
if(!usb_is_initialized())
return;
bool is_done_thread;
std::thread async_processing_thread;
uint32_t last_index = -1;
int status = 0;
SharedConsumerMutex is_buffer_free_shared_mutex(FTD3_CONCURRENT_BUFFERS);
std::chrono::time_point<std::chrono::high_resolution_clock> clock_start = std::chrono::high_resolution_clock::now();
FTD3LibusbCaptureReceivedData* ftd3_libusb_capture_recv_data = new FTD3LibusbCaptureReceivedData[FTD3_CONCURRENT_BUFFERS];
for(int i = 0; i < FTD3_CONCURRENT_BUFFERS; i++) {
ftd3_libusb_capture_recv_data[i].in_use = false;
ftd3_libusb_capture_recv_data[i].index = i;
ftd3_libusb_capture_recv_data[i].capture_data = capture_data;
ftd3_libusb_capture_recv_data[i].last_index = &last_index;
ftd3_libusb_capture_recv_data[i].clock_start = &clock_start;
ftd3_libusb_capture_recv_data[i].is_buffer_free_shared_mutex = &is_buffer_free_shared_mutex;
ftd3_libusb_capture_recv_data[i].status = &status;
ftd3_libusb_capture_recv_data[i].cb_data.actual_user_data = &ftd3_libusb_capture_recv_data[i];
ftd3_libusb_capture_recv_data[i].cb_data.transfer_data = NULL;
ftd3_libusb_capture_recv_data[i].cb_data.internal_index = i;
}
ftd3_libusb_start_thread(&async_processing_thread, &is_done_thread);
ftd3_libusb_capture_main_loop_processing(ftd3_libusb_capture_recv_data, pipe);
wait_all_ftd3_libusb_buffers_free(ftd3_libusb_capture_recv_data);
ftd3_libusb_close_thread(&async_processing_thread, &is_done_thread);
delete []ftd3_libusb_capture_recv_data;
}